<commit_before>var predator = require('predator');

var types = {
        'button': ['button', 'a'],
        'label': ['label', 'span', 'div'],
        'image': ['img', 'svg'],
        'field': ['input']
    },
    noelementOfType = 'no elements of type ';

function executeKeyPress(key, element, done) {
    element.value += key;

    var keydownEvent = new window.KeyboardEvent('keydown'),
        keyupEvent = new window.KeyboardEvent('keyup'),
        keypressEvent = new window.KeyboardEvent('keypress');

    var method = 'initKeyboardEvent' in keydownEvent ? 'initKeyboardEvent' : 'initKeyEvent';

    keydownEvent[method]('keydown', true, true, window, key, 3, true, false, true, false, false);
    keyupEvent[method]('keyup', true, true, window, key, 3, true, false, true, false, false);
    keypressEvent[method]('keypress', true, true, window, key, 3, true, false, true, false, false);

    element.dispatchEvent(keydownEvent);
    element.dispatchEvent(keyupEvent);
    element.dispatchEvent(keypressEvent);

    done(null, element);
}

function findUi(selectors) {
    return document.querySelectorAll(selectors);
}

function executeNavigate(location, previousElement, done) {
    var callbackTimer;

    function handleWindowError(error) {
        clearTimeout(callbackTimer);

        done(error);
        window.removeEventListener('error', handleWindowError);
    }

    window.addEventListener('error', handleWindowError);
    window.location = location;

    callbackTimer = setTimeout(done, 150);
}

function _findUi(value, type, previousElement, done) {
    var elementTypes = types[type];

    if(!elementTypes) {
        return done(new Error(type + ' is not a valid ui type'));
    }

    var elements = findUi(elementTypes);

    if(!elements.length) {
        return done(new Error(noelementOfType + type));
    } else {
        var element;

        for(var i = 0; i < elements.length; i++) {
            var currentElement = elements[i];

            if((currentElement.textContent.toLowerCase() === value.toLowerCase() || currentElement.title.toLowerCase() === value.toLowerCase()) && !predator(currentElement).hidden) {
                element = currentElement;
                break;
            }
        }

        if(!element) {
            return done(new Error(noelementOfType + type + ' with value of ' + value));
        }

        done(null, element);
    }
}

function executeSetValue(value, element, done) {
    element.value = value;

    done(null, element);
}

function executeWait(time, previousElement, done) {
    setTimeout(done.bind(this, null, previousElement), time || 0);
}

function _click(value, type, previousElement, done) {
    var rect = previousElement.getBoundingClientRect();

    if(!previousElement.click) {
        return done(new Error('no clickable element with type' + type + ' and value of ' + value));
    }

    previousElement.click();
    done(null, previousElement);
}

function _focus(value, type, previousElement, done) {
    previousElement.focus();

    done(null, previousElement);
}

function _blur(value, type, previousElement, done) {
    previousElement.blur();

    done(null, previousElement);
}

function execute(task, value, type, previousElement, done) {
    if(!value && previousElement) {
        return task(value, type, previousElement, done);
    }

    _findUi(value, type, previousElement, function(error, element) {
        if(error) {
            return done(error);
        }

        task(value, type, element, done);
    });
}

function runTasks(tasks, previousElement, callback) {
    if(tasks.length) {
        tasks.shift()(previousElement, function(error, result) {
            if(error) {
                return callback(error);
            } else {
                if(tasks.length === 0) {
                    callback(null, result);
                } else {
                    runTasks(tasks, result, callback);
                }
            }
        });
    }
}

function driveUi(){
    var tasks = [];

    var driverFunctions = {
        navigate: function(location){
            tasks.push(executeNavigate.bind(driverFunctions, location));
            return driverFunctions;
        },
        findUi: function(value, type){
            tasks.push(execute.bind(driverFunctions, _findUi, value, type));

            return driverFunctions;
        },
        focus: function(value, type) {
            tasks.push(execute.bind(driverFunctions, _focus, value, type));

            return driverFunctions;
        },
        blur: function(value, type) {
            tasks.push(execute.bind(driverFunctions, _blur, value, type));

            return driverFunctions;
        },
        click: function(value, type){
            tasks.push(execute.bind(driverFunctions, _click, value, type));

            return driverFunctions;
        },
        keyPress: function(value) {
            tasks.push(executeKeyPress.bind(driverFunctions, value));

            return driverFunctions;
        },
        setValue: function(value) {
            tasks.push(executeSetValue.bind(driverFunctions, value));

            return driverFunctions;
        },
        wait: function(time) {
            tasks.push(executeWait.bind(driverFunctions, time));

            return driverFunctions;
        },
        go: function(callback) {
            if(tasks.length) {
                runTasks(tasks, null, callback);
            } else {
                callback(new Error('No tasks defined'));
            }
        },
        do: function(driver){
            tasks.push(driver.go);
            return driverFunctions;
        }
    };

    return driverFunctions;
}

module.exports = driveUi;
<commit_msg>check click element is clickable<commit_after>var predator = require('predator');

var types = {
        'button': ['button', 'a'],
        'label': ['label', 'span', 'div'],
        'heading': ['h1', 'h2', 'h3', 'h4'],
        'image': ['img', 'svg'],
        'field': ['input']
    },
    noelementOfType = 'no elements of type ';

function executeKeyPress(key, element, done) {
    element.value += key;

    var keydownEvent = new window.KeyboardEvent('keydown'),
        keyupEvent = new window.KeyboardEvent('keyup'),
        keypressEvent = new window.KeyboardEvent('keypress');

    var method = 'initKeyboardEvent' in keydownEvent ? 'initKeyboardEvent' : 'initKeyEvent';

    keydownEvent[method]('keydown', true, true, window, key, 3, true, false, true, false, false);
    keyupEvent[method]('keyup', true, true, window, key, 3, true, false, true, false, false);
    keypressEvent[method]('keypress', true, true, window, key, 3, true, false, true, false, false);

    element.dispatchEvent(keydownEvent);
    element.dispatchEvent(keyupEvent);
    element.dispatchEvent(keypressEvent);

    done(null, element);
}

function findUi(selectors) {
    return document.querySelectorAll(selectors);
}

function executeNavigate(location, previousElement, done) {
    var callbackTimer;

    function handleWindowError(error) {
        clearTimeout(callbackTimer);

        done(error);
        window.removeEventListener('error', handleWindowError);
    }

    window.addEventListener('error', handleWindowError);
    window.location = location;

    callbackTimer = setTimeout(done, 150);
}

function _findUi(value, type, previousElement, done) {
    var elementTypes = types[type];

    if(!elementTypes) {
        return done(new Error(type + ' is not a valid ui type'));
    }

    var elements = findUi(elementTypes);

    if(!elements.length) {
        return done(new Error(noelementOfType + type));
    } else {
        var element;

        for(var i = 0; i < elements.length; i++) {
            var currentElement = elements[i];

            if((currentElement.textContent.toLowerCase() === value.toLowerCase() || currentElement.title.toLowerCase() === value.toLowerCase()) && !predator(currentElement).hidden) {
                element = currentElement;
                break;
            }
        }

        if(!element) {
            return done(new Error(noelementOfType + type + ' with value of ' + value));
        }

        done(null, element);
    }
}

function executeSetValue(value, element, done) {
    element.value = value;

    done(null, element);
}

function executeWait(time, previousElement, done) {
    setTimeout(done.bind(this, null, previousElement), time || 0);
}

function _click(value, type, previousElement, done) {
    var rect = previousElement.getBoundingClientRect(),
        clickElement = document.elementFromPoint(rect.left, rect.top),
        elementInClickElement = ~Array.prototype.indexOf.call(clickElement.children, previousElement);

    if
    (!elementInClickElement && (clickElement !== previousElement)) {
        return done(new Error('no clickable element with type ' + type + ' and value of ' + value));
    }

    previousElement.click();
    done(null, previousElement);
}

function _focus(value, type, previousElement, done) {
    previousElement.focus();

    done(null, previousElement);
}

function _blur(value, type, previousElement, done) {
    previousElement.blur();

    done(null, previousElement);
}

function execute(task, value, type, previousElement, done) {
    if(!value && previousElement) {
        return task(value, type, previousElement, done);
    }

    _findUi(value, type, previousElement, function(error, element) {
        if(error) {
            return done(error);
        }

        task(value, type, element, done);
    });
}

function runTasks(tasks, previousElement, callback) {
    if(tasks.length) {
        tasks.shift()(previousElement, function(error, result) {
            if(error) {
                return callback(error);
            } else {
                if(tasks.length === 0) {
                    callback(null, result);
                } else {
                    runTasks(tasks, result, callback);
                }
            }
        });
    }
}

function driveUi(){
    var tasks = [];

    var driverFunctions = {
        navigate: function(location){
            tasks.push(executeNavigate.bind(driverFunctions, location));
            return driverFunctions;
        },
        findUi: function(value, type){
            tasks.push(execute.bind(driverFunctions, _findUi, value, type));

            return driverFunctions;
        },
        focus: function(value, type) {
            tasks.push(execute.bind(driverFunctions, _focus, value, type));

            return driverFunctions;
        },
        blur: function(value, type) {
            tasks.push(execute.bind(driverFunctions, _blur, value, type));

            return driverFunctions;
        },
        click: function(value, type){
            tasks.push(execute.bind(driverFunctions, _click, value, type));

            return driverFunctions;
        },
        keyPress: function(value) {
            tasks.push(executeKeyPress.bind(driverFunctions, value));

            return driverFunctions;
        },
        setValue: function(value) {
            tasks.push(executeSetValue.bind(driverFunctions, value));

            return driverFunctions;
        },
        wait: function(time) {
            tasks.push(executeWait.bind(driverFunctions, time));

            return driverFunctions;
        },
        go: function(callback) {
            if(tasks.length) {
                runTasks(tasks, null, callback);
            } else {
                callback(new Error('No tasks defined'));
            }
        },
        do: function(driver){
            tasks.push(driver.go);
            return driverFunctions;
        }
    };

    return driverFunctions;
}

module.exports = driveUi;
<|endoftext|>


<commit_before>const electron = require("electron");

const app = electron.app;

if(app == null){
    throw "electron is not initiated.";
    process.exit();
}

const BrowserWindow = electron.BrowserWindow;

const path = require("path");
const url = require("url");
const fs = require("fs");

var oldFile = "pso2Tools.db";
var newFile = "pso2db.db";

function upgradeDatabase(dbFile){
    var db = require("sqlite-sync");
    var offset = (new Date().getTimezoneOffset())*60;
    var timeahead = offset<0;
    offset = Math.abs(offset);
    db.connect(dbFile)
    var commands = [
        "CREATE TABLE temp_table AS SELECT * FROM characters",
        "DROP TABLE characters",
        "CREATE TABLE IF NOT EXISTS `characters`(\
            `cid` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,\
            `CName` TEXT,\
            `lastTATime` TIMESTAMP,\
            `classLv` TEXT,\
            `memo` TEXT\
        );",
        "INSERT INTO characters(CName, lastTATime, classLv, memo) SELECT CName, strftime('%s',lastTATime)"+ (timeahead?"-":"+") + offset.toString() +", classLv, memo FROM temp_table",
        "DROP TABLE temp_table",
        "CREATE TABLE IF NOT EXISTS `installed_modules`(`module_name` TEXT PRIMARY KEY);",
        "INSERT INTO installed_modules(module_name) VALUES ('ClassLevel')",
        "INSERT INTO installed_modules(module_name) VALUES ('Memo')",
        "INSERT INTO installed_modules(module_name) VALUES ('TaCounter')",
    ]
    for(var cmd in commands){
        console.log(db.run(commands[cmd]));
    }
}

if(!path.isAbsolute(oldFile)){
    oldFile = path.join(process.cwd(),oldFile);
}

if(!path.isAbsolute(newFile)){
    newFile = path.join(process.cwd(),newFile);
}

if(fs.existsSync(oldFile)){
    fs.rename(oldFile, newFile, ()=>{upgradeDatabase(newFile);})
}

let mainWindow;

function CreateWindow(){
    mainWindow = new BrowserWindow({
        width: 1024,
        height: 600,
        minHeight: 400,
        minWidth: 500,
        frame: false,
        transparent: true,
        webPreferences: {
            //experimentalFeatures: true
        }
    });

    // To open DevTools, uncomment the line below
    //mainWindow.webContents.openDevTools();

    mainWindow.setMenu(null);

    mainWindow.loadURL(url.format({
        pathname: path.join(__dirname, "index.html"),
        protocol: "file:",
        slashes: true
    }));

    mainWindow.on("closed", function(){
        mainWindow = null;
    })
}

app.on("ready",CreateWindow);

app.on("window-all-closed", function(){
    if(process.platform !== 'darwin'){
        app.quit();
    }
})

app.on("activate", function(){
    if(mainWindow == null){
        CreateWindow();
    }
})<commit_msg>The window behavior bacome strange when  being transparent<commit_after>const electron = require("electron");

const app = electron.app;

if(app == null){
    throw "electron is not initiated.";
    process.exit();
}

const BrowserWindow = electron.BrowserWindow;

const path = require("path");
const url = require("url");
const fs = require("fs");

var oldFile = "pso2Tools.db";
var newFile = "pso2db.db";

function upgradeDatabase(dbFile){
    var db = require("sqlite-sync");
    var offset = (new Date().getTimezoneOffset())*60;
    var timeahead = offset<0;
    offset = Math.abs(offset);
    db.connect(dbFile)
    var commands = [
        "CREATE TABLE temp_table AS SELECT * FROM characters",
        "DROP TABLE characters",
        "CREATE TABLE IF NOT EXISTS `characters`(\
            `cid` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,\
            `CName` TEXT,\
            `lastTATime` TIMESTAMP,\
            `classLv` TEXT,\
            `memo` TEXT\
        );",
        "INSERT INTO characters(CName, lastTATime, classLv, memo) SELECT CName, strftime('%s',lastTATime)"+ (timeahead?"-":"+") + offset.toString() +", classLv, memo FROM temp_table",
        "DROP TABLE temp_table",
        "CREATE TABLE IF NOT EXISTS `installed_modules`(`module_name` TEXT PRIMARY KEY);",
        "INSERT INTO installed_modules(module_name) VALUES ('ClassLevel')",
        "INSERT INTO installed_modules(module_name) VALUES ('Memo')",
        "INSERT INTO installed_modules(module_name) VALUES ('TaCounter')",
    ]
    for(var cmd in commands){
        console.log(db.run(commands[cmd]));
    }
}

if(!path.isAbsolute(oldFile)){
    oldFile = path.join(process.cwd(),oldFile);
}

if(!path.isAbsolute(newFile)){
    newFile = path.join(process.cwd(),newFile);
}

if(fs.existsSync(oldFile)){
    fs.rename(oldFile, newFile, ()=>{upgradeDatabase(newFile);})
}

let mainWindow;

function CreateWindow(){
    mainWindow = new BrowserWindow({
        width: 1024,
        height: 600,
        minHeight: 400,
        minWidth: 500,
        frame: false,
        //transparent: true,
        webPreferences: {
            //experimentalFeatures: true
        }
    });

    // To open DevTools, uncomment the line below
    //mainWindow.webContents.openDevTools();

    mainWindow.setMenu(null);

    mainWindow.loadURL(url.format({
        pathname: path.join(__dirname, "index.html"),
        protocol: "file:",
        slashes: true
    }));

    mainWindow.on("closed", function(){
        mainWindow = null;
    })
}

app.on("ready",CreateWindow);

app.on("window-all-closed", function(){
    if(process.platform !== 'darwin'){
        app.quit();
    }
})

app.on("activate", function(){
    if(mainWindow == null){
        CreateWindow();
    }
})<|endoftext|>


<commit_before>#!/usr/bin/env node
var fs = require('fs')
var path = require('path')
var debug = require('debug')('tlapse')
var mkdirp = require('mkdirp')
var execa = require('execa')
var ms = require('ms')
var dateFormat = require('dateformat')
var pageresPath = path.join(__dirname, './node_modules/.bin/pageres')

var yargs = require('yargs')
  .usage('Usage: $0 [options] -- <pageres-cli-options>')
  .option('every', {
    alias: 'e',
    describe: 'screenshots interval',
    default: '1m',
    type: 'string'
  })
  .option('directory', {
    alias: 'd',
    describe: 'screenshots directory',
    default: './tlapse',
    type: 'string'
  })
  .help()
  .version()

var argv = yargs.argv

if (argv._.length === 0) {
  yargs.showHelp()
  process.exit(1)
}

if (argv.directory) {
  mkdirp.sync(argv.directory)
  process.chdir(argv.directory)
}

console.log('Screenshots directory:', argv.directory)
console.log('Interval:', argv.every)

function run () {
  var pageresOptions = argv._.join(' ')
  var cmd = [
    pageresPath,
    pageresOptions,
    '--filename=\'' + Date.now() + '\''
  ].join(' ')

  debug(cmd)
  const result = execa.shellSync(cmd)

  if (result.error) {
    console.log('Error')
    console.log('stdout:', result.stdout)
    console.log('stderr:', result.stderr)
  } else {
    var recentFiles = fs
      .readdirSync(process.cwd())
      .reverse()

    var latestFile = recentFiles[0]
    var previousFile = recentFiles[1]
    var now = dateFormat(Date.now(), 'hh:MM:ss')

    if (latestFile && latestFile.indexOf('.png.') !== -1) {
      console.log(now, '○ Can\'t connect, Skipping')
      return fs.unlinkSync(latestFile)
    }

    if (
      latestFile &&
      previousFile &&
      fs.readFileSync(latestFile, 'utf-8') === fs.readFileSync(previousFile, 'utf-8')
    ) {
      console.log(now, '○ Duplicate screenshot, skipping')
      return fs.unlinkSync(latestFile)
    }

    console.log(now, result.stdout.trim())
  }
}

run()
setInterval(run, ms(argv.every))
<commit_msg>Check for the child process errors the proper way<commit_after>#!/usr/bin/env node
var fs = require('fs')
var path = require('path')
var debug = require('debug')('tlapse')
var mkdirp = require('mkdirp')
var execa = require('execa')
var ms = require('ms')
var dateFormat = require('dateformat')
var pageresPath = path.join(__dirname, './node_modules/.bin/pageres')

var yargs = require('yargs')
  .usage('Usage: $0 [options] -- <pageres-cli-options>')
  .option('every', {
    alias: 'e',
    describe: 'screenshots interval',
    default: '1m',
    type: 'string'
  })
  .option('directory', {
    alias: 'd',
    describe: 'screenshots directory',
    default: './tlapse',
    type: 'string'
  })
  .help()
  .version()

var argv = yargs.argv

if (argv._.length === 0) {
  yargs.showHelp()
  process.exit(1)
}

if (argv.directory) {
  mkdirp.sync(argv.directory)
  process.chdir(argv.directory)
}

console.log('Screenshots directory:', argv.directory)
console.log('Interval:', argv.every)

function run () {
  var pageresOptions = argv._.join(' ')
  var cmd = [
    pageresPath,
    pageresOptions,
    '--filename=\'' + Date.now() + '\''
  ].join(' ')

  debug(cmd)
  const result = execa.shellSync(cmd)

  if (result.status !== 0) {
    console.log('Error')
    console.log('stdout:', result.stdout)
    console.log('stderr:', result.stderr)
  } else {
    var recentFiles = fs
      .readdirSync(process.cwd())
      .reverse()

    var latestFile = recentFiles[0]
    var previousFile = recentFiles[1]
    var now = dateFormat(Date.now(), 'hh:MM:ss')

    if (latestFile && latestFile.indexOf('.png.') !== -1) {
      console.log(now, '○ Can\'t connect, Skipping')
      return fs.unlinkSync(latestFile)
    }

    if (
      latestFile &&
      previousFile &&
      fs.readFileSync(latestFile, 'utf-8') === fs.readFileSync(previousFile, 'utf-8')
    ) {
      console.log(now, '○ Duplicate screenshot, skipping')
      return fs.unlinkSync(latestFile)
    }

    console.log(now, result.stdout.trim())
  }
}

run()
setInterval(run, ms(argv.every))
<|endoftext|>


<commit_before>var GitHubApi = require('github');
var github = new GitHubApi({
    version: '3.0.0'
});
// Authenticate to github
var github_token = require('./github_token.json');
github.authenticate(github_token);
var https = require('https');
var fs = require('fs');
var StreamZip = require('node-stream-zip');
var mime = require('mime');
var aws = require('aws-sdk');
var S3 = new aws.S3({apiVersion: '2006-03-01'});

var boolIssue = boolPusher = false;
var file;
var requestDataString = '';
var firstEntry = '';
var extractedTotal = uploadedCount = 0;

function getSNSMessageObject(msgString) {
    var x = msgString.replace(/\\/g,'');
    var y = x.substring(1,x.length-1);
    var z = JSON.parse(y);
    return z;
}

exports.handler = function(event, context) {
    console.log('Version: ','2.0.6');    //DEBUG
    var githubEventString = JSON.stringify(event.Records[0].Sns.Message);
    var githubEventObject = getSNSMessageObject(githubEventString);

    function getDeployJSON(err, user, repo, ref, callback) {
      console.log("getDeployJSON::user " + user); //DEBUG
      console.log("getDeployJSON::repo " + repo); //DEBUG
      console.log("getDeployJSON::ref " + ref); //DEBUG
      if(err) {
        context.fail(err);
        return;
      }
      if(!user) {
        //console.log("getDeployTarget::field user is required");
        callback( new Error('Parameter user is required') );
        return;
      }
      if(!repo) {
        //console.log("getDeployTarget::field repo is required");
        callback( new Error('Parameter repo is required') );
        return;
      }
      if(!ref) {
        ref="master";
      }
      var apiMsg = {
        user: user,
        repo: repo,
        path: "deploy.json",
        ref: ref
      };
      github.repos.getContent( apiMsg , function(err, data) {
        if (err) {
          console.log("getContent failed: "+err);
          return false;
        } else {
          dataContent = JSON.parse(new Buffer(data.content, 'base64'));
          console.log("getDeployJSON::Type " + dataContent.deploy.type); //DEBUG
          console.log("getDeployJSON::Target " + dataContent.deploy.target); //DEBUG
          callback(null, dataContent);
        }
      });
    }

    function getDeployType(err, data) {
      if(err) {
        console.log(err);
        return;
      } else {
        if(data.deploy.type == 'S3') {
          console.log("This type is S3");	//DEBUG
          console.log("Target is " + data.deploy.target);
          deployS3(err, data.deploy.target);
        } else if(data.deploy.type == 'EB') {
          console.log("This type is EB");	//DEBUG
          console.log("Target is " + data.deploy.target);
        } else {
          console.log('deployType must be S3 or EB');
          return;
        }
      }
    }

    function getArchive(user, repo, ref, callback) {
      var apiMsg = {
        user: user,
        repo: repo,
        ref: ref,
        archive_format: 'zipball'
      };
      github.repos.getArchiveLink( apiMsg , function(err, data) {
        if(err) {
          console.log("getArchiveLink failed");
          context.fail(err);
          return;
        } else {
          var archiveLink = JSON.stringify(data.meta.location);
          archiveLink=archiveLink.substring(1,archiveLink.length-1);  //Slay the frakking ""s!!!
          file = fs.createWriteStream('/tmp/github.zip');
          var request = https.get(archiveLink, function(response) {
            response.on('data', function(chunkBuffer) {
              // var data is a chunk of data from response body
              requestDataString += chunkBuffer.toString();
            });
            response.on('end', function() {
              //console.log("All data received...");  //DEBUG
            });
            response.pipe(file);
            file.on('uncaughtException', function(err) {
              //console.log("File failed: "+err);
              context.fail("File failed: "+err);
            });
            file.on('finish', function() {
              file.close();
              callback(null, user, repo, ref, getDeployType);
            });
          });
          request.on('error', function(e) {
            console.log("Request error"); //DEBUG
            console.error(e);
            context.fail();
          });
          request.end( function() {
          });
        } // End else
      }); // End getArchiveLink
    } // End getArchive

    function deployS3(err, target) {
      if(err) {
        console.log("deployS3 error");
      } else {
        var zip = new StreamZip({
          file: '/tmp/github.zip',
          storeEntries: true
        });
        zip.on('error', function(err) {
          context.fail("Zip failed: "+err);
        });
        zip.on('ready', function() {
          //console.log("Entries read: "+zip.entriesCount); //DEBUG
          // The first entry should be the subdirectory added by github.
          // We don't want to upload that to S3 so capture it here
          // so it can be pruned from the S3 key during upload.
          firstEntry = Object.keys(zip.entries())[0];
          // extract to /tmp (remember the 500mb limit, this is cumulative)
          zip.extract(null, '/tmp', function(err, count) {
            if (err) {
              context.fail("zip failed: "+err);
            } else {
              extractedTotal = count;
            }
          });
        });
        zip.on('extract', function(entry, file) {
          fs.readFile(file, function(err, data) {
            if (err) {
              console.log("readFile Failed: ",err); //DEBUG
              context.fail(err);
            } else {
              var key = entry.name;
              // Pruning the subdirectory mentioned above.
              key = key.replace(firstEntry,'');
              // S3 defaults to binary ContentType if not specified.
              var mimeType=mime.lookup(file);
              var params = {
                Bucket: target,
                Key: key,
                ContentType: mimeType,
                Body: data
              };
              S3.upload(params, function(err, data) {
                if(err) {
                  context.fail("S3Upload failed: "+err);
                } else {
                  // Keep track of uploaded files to avoid race condition.
                  // once it equals the number of extracted files context.succeed
                  uploadedCount++;
                  if(uploadedCount==extractedTotal) {
                    console.log(uploadedCount+" files deployed to "+target);
                    context.succeed();  //Commented out for DEBUG
                  }
                }
              }); // End S3.upload()
            } // End readFile else
          }); // End readFile()
        }); // End zip.on('extract')
        zip.on('entry', function(entry) {
          console.log("Read entry ", entry.name);
        });
      }
    } // End deployS3

    // If deploy.type is EB
    function deployEB(err, target) {
      console.log("Deploy to Elastic Beanstalk functionality not currently supported.");
    } // End deployEB

    // Responds to new issues opened in github, no use for this really, just shows the use of the github API.
    if (githubEventObject.hasOwnProperty('issue') && githubEventObject.action == 'opened') {
        // An event for opening an issue
        boolIssue=true;

        // Get user name of the comment poster
        var poster = githubEventObject.issue.user.login;

        github.issues.createComment({
            user: githubEventObject.repository.owner.login,
            repo: githubEventObject.repository.name,
            number: githubEventObject.issue.nummber,
            body: "Hi @" + poster + "!\n" +
                  "\n" +
                  "Thank you for your interest in this project! Unfortunately, we're " +
                  "really busy at the moment, but we'll get to your issue as soon as " +
                  "possible. Have a great day!"
        }, context.done);
    } // End Issue-Opened

    // Checks if a push has been made to the master branch, if so, deploy to S3
    if (githubEventObject.hasOwnProperty('pusher') && githubEventObject.ref == 'refs/heads/master') {
      boolPusher=true;

      // Get the archive url
      getArchive(
        githubEventObject.repository.owner.name,
        githubEventObject.repository.name,
        githubEventObject.ref,
        getDeployJSON
      );
    }  // End If(Pusher)

  // Nothing, just close it. Honestly this should never happen if the
  // GitHub webhooks are configured properly.
  if(!boolIssue&&!boolPusher) {
    context.done();
  }
};  // End exports.handler
<commit_msg>Changed error message for getContent<commit_after>var GitHubApi = require('github');
var github = new GitHubApi({
    version: '3.0.0'
});
// Authenticate to github
var github_token = require('./github_token.json');
github.authenticate(github_token);
var https = require('https');
var fs = require('fs');
var StreamZip = require('node-stream-zip');
var mime = require('mime');
var aws = require('aws-sdk');
var S3 = new aws.S3({apiVersion: '2006-03-01'});

var boolIssue = boolPusher = false;
var file;
var requestDataString = '';
var firstEntry = '';
var extractedTotal = uploadedCount = 0;

function getSNSMessageObject(msgString) {
    var x = msgString.replace(/\\/g,'');
    var y = x.substring(1,x.length-1);
    var z = JSON.parse(y);
    return z;
}

exports.handler = function(event, context) {
    console.log('Version: ','2.0.6');    //DEBUG
    var githubEventString = JSON.stringify(event.Records[0].Sns.Message);
    var githubEventObject = getSNSMessageObject(githubEventString);

    function getDeployJSON(err, user, repo, ref, callback) {
      console.log("getDeployJSON::user " + user); //DEBUG
      console.log("getDeployJSON::repo " + repo); //DEBUG
      console.log("getDeployJSON::ref " + ref); //DEBUG
      if(err) {
        context.fail(err);
        return;
      }
      if(!user) {
        //console.log("getDeployTarget::field user is required");
        callback( new Error('Parameter user is required') );
        return;
      }
      if(!repo) {
        //console.log("getDeployTarget::field repo is required");
        callback( new Error('Parameter repo is required') );
        return;
      }
      if(!ref) {
        ref="master";
      }
      var apiMsg = {
        user: user,
        repo: repo,
        path: "deploy.json",
        ref: ref
      };
      github.repos.getContent( apiMsg , function(err, data) {
        if (err) {
          console.log("deploy.json is missing from this repo: "+err);
          return false;
        } else {
          dataContent = JSON.parse(new Buffer(data.content, 'base64'));
          console.log("getDeployJSON::Type " + dataContent.deploy.type); //DEBUG
          console.log("getDeployJSON::Target " + dataContent.deploy.target); //DEBUG
          callback(null, dataContent);
        }
      });
    }

    function getDeployType(err, data) {
      if(err) {
        console.log(err);
        return;
      } else {
        if(data.deploy.type == 'S3') {
          console.log("This type is S3");	//DEBUG
          console.log("Target is " + data.deploy.target);
          deployS3(err, data.deploy.target);
        } else if(data.deploy.type == 'EB') {
          console.log("This type is EB");	//DEBUG
          console.log("Target is " + data.deploy.target);
        } else {
          console.log('deployType must be S3 or EB');
          return;
        }
      }
    }

    function getArchive(user, repo, ref, callback) {
      var apiMsg = {
        user: user,
        repo: repo,
        ref: ref,
        archive_format: 'zipball'
      };
      github.repos.getArchiveLink( apiMsg , function(err, data) {
        if(err) {
          console.log("getArchiveLink failed");
          context.fail(err);
          return;
        } else {
          var archiveLink = JSON.stringify(data.meta.location);
          archiveLink=archiveLink.substring(1,archiveLink.length-1);  //Slay the frakking ""s!!!
          file = fs.createWriteStream('/tmp/github.zip');
          var request = https.get(archiveLink, function(response) {
            response.on('data', function(chunkBuffer) {
              // var data is a chunk of data from response body
              requestDataString += chunkBuffer.toString();
            });
            response.on('end', function() {
              //console.log("All data received...");  //DEBUG
            });
            response.pipe(file);
            file.on('uncaughtException', function(err) {
              //console.log("File failed: "+err);
              context.fail("File failed: "+err);
            });
            file.on('finish', function() {
              file.close();
              callback(null, user, repo, ref, getDeployType);
            });
          });
          request.on('error', function(e) {
            console.log("Request error"); //DEBUG
            console.error(e);
            context.fail();
          });
          request.end( function() {
          });
        } // End else
      }); // End getArchiveLink
    } // End getArchive

    function deployS3(err, target) {
      if(err) {
        console.log("deployS3 error");
      } else {
        var zip = new StreamZip({
          file: '/tmp/github.zip',
          storeEntries: true
        });
        zip.on('error', function(err) {
          context.fail("Zip failed: "+err);
        });
        zip.on('ready', function() {
          //console.log("Entries read: "+zip.entriesCount); //DEBUG
          // The first entry should be the subdirectory added by github.
          // We don't want to upload that to S3 so capture it here
          // so it can be pruned from the S3 key during upload.
          firstEntry = Object.keys(zip.entries())[0];
          // extract to /tmp (remember the 500mb limit, this is cumulative)
          zip.extract(null, '/tmp', function(err, count) {
            if (err) {
              context.fail("zip failed: "+err);
            } else {
              extractedTotal = count;
            }
          });
        });
        zip.on('extract', function(entry, file) {
          fs.readFile(file, function(err, data) {
            if (err) {
              console.log("readFile Failed: ",err); //DEBUG
              context.fail(err);
            } else {
              var key = entry.name;
              // Pruning the subdirectory mentioned above.
              key = key.replace(firstEntry,'');
              // S3 defaults to binary ContentType if not specified.
              var mimeType=mime.lookup(file);
              var params = {
                Bucket: target,
                Key: key,
                ContentType: mimeType,
                Body: data
              };
              S3.upload(params, function(err, data) {
                if(err) {
                  context.fail("S3Upload failed: "+err);
                } else {
                  // Keep track of uploaded files to avoid race condition.
                  // once it equals the number of extracted files context.succeed
                  uploadedCount++;
                  if(uploadedCount==extractedTotal) {
                    console.log(uploadedCount+" files deployed to "+target);
                    context.succeed();  //Commented out for DEBUG
                  }
                }
              }); // End S3.upload()
            } // End readFile else
          }); // End readFile()
        }); // End zip.on('extract')
        zip.on('entry', function(entry) {
          console.log("Read entry ", entry.name);
        });
      }
    } // End deployS3

    // If deploy.type is EB
    function deployEB(err, target) {
      console.log("Deploy to Elastic Beanstalk functionality not currently supported.");
    } // End deployEB

    // Responds to new issues opened in github, no use for this really, just shows the use of the github API.
    if (githubEventObject.hasOwnProperty('issue') && githubEventObject.action == 'opened') {
        // An event for opening an issue
        boolIssue=true;

        // Get user name of the comment poster
        var poster = githubEventObject.issue.user.login;

        github.issues.createComment({
            user: githubEventObject.repository.owner.login,
            repo: githubEventObject.repository.name,
            number: githubEventObject.issue.nummber,
            body: "Hi @" + poster + "!\n" +
                  "\n" +
                  "Thank you for your interest in this project! Unfortunately, we're " +
                  "really busy at the moment, but we'll get to your issue as soon as " +
                  "possible. Have a great day!"
        }, context.done);
    } // End Issue-Opened

    // Checks if a push has been made to the master branch, if so, deploy to S3
    if (githubEventObject.hasOwnProperty('pusher') && githubEventObject.ref == 'refs/heads/master') {
      boolPusher=true;

      // Get the archive url
      getArchive(
        githubEventObject.repository.owner.name,
        githubEventObject.repository.name,
        githubEventObject.ref,
        getDeployJSON
      );
    }  // End If(Pusher)

  // Nothing, just close it. Honestly this should never happen if the
  // GitHub webhooks are configured properly.
  if(!boolIssue&&!boolPusher) {
    context.done();
  }
};  // End exports.handler
<|endoftext|>


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

//npm modules
var program     = require('commander');
var request     = require('request');

//connected devices
try {
  var Gpio        = require('onoff').Gpio;
  var powerTail   = new Gpio(408, 'out', null, {activeLow: true});
} catch (error) {
  console.log(error);
}

//misc variables
var lastUniqueMessage = '';
var SLACK_API = '';
var INCREMENT   = 900000; //15min
var TEMPERATURE = 24; //24C
var WEATHERAPI  = 'http://api.openweathermap.org/data/2.5/weather?q=Kelowna,ca&appid=794fc23dd8ce7ba66cd0361f58355010&units=metric';

program
  .version('1.0.0')

program
  .command('on')
  .description('turns device on via gpio pin')
  .action(function() {
    powerTail.writeSync(1);
    console.log('PowerTail State: ' + powerTail.readSync());
  });

program
  .command('off')
  .description('turns device off via gpio pin')
  .action(function() {
    powerTail.writeSync(0);
    console.log('PowerTail State: ' + powerTail.readSync());
  });

program
  .command('status')
  .description('return the status of the device')
  .action(function() {
    console.log('PowerTail State: ' + powerTail.readSync());
  });

program
  .command('auto')
  .option("-i, --inc [seconds]", "Sets auto to check api every x seconds")
  .option("-t, --temp [temperature]", "Sets auto to run if temp (C) is reached")
  .description('starts the auto with default timer and temperature if none provided')
  .action(function(options) {
    var increment = options.inc || INCREMENT;
    var temperature = options.temp || TEMPERATURE;
    console.log('State: { INC: ' + increment + ' Temp: ' + temperature + ' }');
    sendNotification(`State: { INC: ${increment} Temp: ${temperature} }`);
    auto(increment, temperature);
  });

/**
 * auto function that begins an infinite loop around whether to turn on or off
 * @param {integer} inc - wait time in seconds between loops
 * @param {integer} temp - temperature in C for the activation point
 */
function auto(inc, temp) {
  request(WEATHERAPI, function(err, res, body) {
    //continue auto operation even if http request fails
    if(err) {
      console.log(new Date() + ': Error');
      sendNotification(`${new Date()}: Error: ${JSON.stringify(err)}`);
    } else {
      checkState(powerTail, temp, JSON.parse(body).main.temp);
    }

    //set timeout for next check in
    setTimeout(function(){
      auto(inc, temp);
    }, inc);
  });
};

/**
 * sends a slack message to a predefined end point
 * @param {string} message - message to send to slack
 */
function sendNotification(message) {
  if(!SLACK_API) return ;
  if(lastUniqueMessage === message) return ;

  const lastUniqueMessage = message;
  const data = {
    text: `@ryanluker ${message}`,
    link_names: 1
  }

  request.post(SLACK_API, {body: JSON.stringify(data)});
}

/**
 * checkState takes the temp and target, then makes decisions on state
 * @param {Gpio} device - applicable Gpio device
 * @param {integer} target - target temperature
 * @param {integer} temp - temperature returned from the api
 */
function checkState(device, target, temp) {
  console.log(new Date() + ': Current Temp (' + temp + ')');
  if(target < temp) {
    console.log(new Date() + ': Switching on');
    sendNotification(`Switching on`);
    device.writeSync(1);
  } else {
    console.log(new Date() + ': Switching off');
    sendNotification(`Switching off`);
    device.writeSync(0);
  }
}

program.parse(process.argv);

//remove and shutdown resources
process.on('SIGINT', function () {
  console.log(new Date() + ': Switching off');
  powerTail.writeSync(0);
  console.log(new Date() + ': Shutting down!');
  sendNotification(`${new Date()}: Shutting down!`);
  powerTail.unexport();
  process.exit(0);
});

//exports
module.exports.checkState = checkState;
module.exports.sendNotification = sendNotification;<commit_msg>use global var and do not reassign<commit_after>#!/usr/bin/env node

//npm modules
var program     = require('commander');
var request     = require('request');

//connected devices
try {
  var Gpio        = require('onoff').Gpio;
  var powerTail   = new Gpio(408, 'out', null, {activeLow: true});
} catch (error) {
  console.log(error);
}

//misc variables
var lastUniqueMessage = '';
var SLACK_API = '';
var INCREMENT   = 900000; //15min
var TEMPERATURE = 24; //24C
var WEATHERAPI  = 'http://api.openweathermap.org/data/2.5/weather?q=Kelowna,ca&appid=794fc23dd8ce7ba66cd0361f58355010&units=metric';

program
  .version('1.0.0')

program
  .command('on')
  .description('turns device on via gpio pin')
  .action(function() {
    powerTail.writeSync(1);
    console.log('PowerTail State: ' + powerTail.readSync());
  });

program
  .command('off')
  .description('turns device off via gpio pin')
  .action(function() {
    powerTail.writeSync(0);
    console.log('PowerTail State: ' + powerTail.readSync());
  });

program
  .command('status')
  .description('return the status of the device')
  .action(function() {
    console.log('PowerTail State: ' + powerTail.readSync());
  });

program
  .command('auto')
  .option("-i, --inc [seconds]", "Sets auto to check api every x seconds")
  .option("-t, --temp [temperature]", "Sets auto to run if temp (C) is reached")
  .description('starts the auto with default timer and temperature if none provided')
  .action(function(options) {
    var increment = options.inc || INCREMENT;
    var temperature = options.temp || TEMPERATURE;
    console.log('State: { INC: ' + increment + ' Temp: ' + temperature + ' }');
    sendNotification(`State: { INC: ${increment} Temp: ${temperature} }`);
    auto(increment, temperature);
  });

/**
 * auto function that begins an infinite loop around whether to turn on or off
 * @param {integer} inc - wait time in seconds between loops
 * @param {integer} temp - temperature in C for the activation point
 */
function auto(inc, temp) {
  request(WEATHERAPI, function(err, res, body) {
    //continue auto operation even if http request fails
    if(err) {
      console.log(new Date() + ': Error');
      sendNotification(`${new Date()}: Error: ${JSON.stringify(err)}`);
    } else {
      checkState(powerTail, temp, JSON.parse(body).main.temp);
    }

    //set timeout for next check in
    setTimeout(function(){
      auto(inc, temp);
    }, inc);
  });
};

/**
 * sends a slack message to a predefined end point
 * @param {string} message - message to send to slack
 */
function sendNotification(message) {
  if(!SLACK_API) return ;
  if(lastUniqueMessage === message) return ;

  lastUniqueMessage = message;
  const data = {
    text: `@ryanluker ${message}`,
    link_names: 1
  }

  request.post(SLACK_API, {body: JSON.stringify(data)});
}

/**
 * checkState takes the temp and target, then makes decisions on state
 * @param {Gpio} device - applicable Gpio device
 * @param {integer} target - target temperature
 * @param {integer} temp - temperature returned from the api
 */
function checkState(device, target, temp) {
  console.log(new Date() + ': Current Temp (' + temp + ')');
  if(target < temp) {
    console.log(new Date() + ': Switching on');
    sendNotification(`Switching on`);
    device.writeSync(1);
  } else {
    console.log(new Date() + ': Switching off');
    sendNotification(`Switching off`);
    device.writeSync(0);
  }
}

program.parse(process.argv);

//remove and shutdown resources
process.on('SIGINT', function () {
  console.log(new Date() + ': Switching off');
  powerTail.writeSync(0);
  console.log(new Date() + ': Shutting down!');
  sendNotification(`${new Date()}: Shutting down!`);
  powerTail.unexport();
  process.exit(0);
});

//exports
module.exports.checkState = checkState;
module.exports.sendNotification = sendNotification;<|endoftext|>


<commit_before>'use strict';

var mongoose          = require('mongoose'),
      _               = require('lodash'),
      chalk = require('chalk'),
      inflection      = require('inflection'),
      slugs           = require('slugs');

function slugify(model, options){
    var slugParts = _.values(_.pick(model, options.source));
    return slugs(slugParts.join(' '));
}

function getModel(document, options){
    var modelName = options.modelName || inflection.singularize(inflection.camelize(document.collection.name));
    return document.collection.conn.model(modelName);
}

function incrementAndSave(document, options, cb){
    var Model = getModel(document, options);
    var params = {};
    var slugbaseKey = options.slugBase;
    var itKey = options.slugIt;
    params[slugbaseKey] = document[slugbaseKey];

    Model.findOne(params).sort('-'+itKey).exec(function(e, doc){
        if(e) return cb(e);

        var it = (doc[itKey] || 0) + Math.ceil(Math.random()*10);

        document[itKey] = it;
        document[options.slugName] = document[slugbaseKey]+'-'+it;
        document.markModified(slugbaseKey);

        _.forEach(options.source, function(item){
            document.markModified(item);
        });

        return document.save(cb);
    });
}

function Slugin(schema, options){
    options = _.defaults(options || {}, Slugin.defaultOptions);
    if(_.isString(options.source))
        options.source = [options.source];
    options.slugIt = options.slugName + '_it';
    options.slugBase = options.slugName + '_base';
    var fields = {};
    fields[options.slugName] = {
        type : String,
        unique: true
    };

    fields[options.slugBase] = {
        type: String,
        index:true
    };

    fields[options.slugIt] = {
        type: Number
    };

    schema.add(fields);

    schema.pre('save', function(next){
        var self = this;
        var slugBase = slugify(this,options);
        console.log('Old base: %s ; New Base: %s', this[options.slugBase], slugBase);
        if(this[options.slugBase] !== slugBase){
            this[options.slugName] = slugBase;
            this[options.slugBase] = slugBase;
            delete this[options.slugIt];
        }
        next();
    });

    schema.methods.save = function(cb){
        var self = this;
        mongoose.Model.prototype.save.call(self, function(e, model, num){
            if(e && (e.code === 11000  || e.code === 11001) && !!~e.err.indexOf(self[options.slugName])){
                incrementAndSave(self, options, cb);
            }else{
                cb(e,model,num);
            }
        });
    };
}

Slugin.defaultOptions = {
    slugName : 'slug',
    source : 'title',
    modelName : null
};

module.exports = Slugin;
<commit_msg>removed chalk for real this time<commit_after>'use strict';

var mongoose          = require('mongoose'),
      _               = require('lodash'),
      inflection      = require('inflection'),
      slugs           = require('slugs');

function slugify(model, options){
    var slugParts = _.values(_.pick(model, options.source));
    return slugs(slugParts.join(' '));
}

function getModel(document, options){
    var modelName = options.modelName || inflection.singularize(inflection.camelize(document.collection.name));
    return document.collection.conn.model(modelName);
}

function incrementAndSave(document, options, cb){
    var Model = getModel(document, options);
    var params = {};
    var slugbaseKey = options.slugBase;
    var itKey = options.slugIt;
    params[slugbaseKey] = document[slugbaseKey];

    Model.findOne(params).sort('-'+itKey).exec(function(e, doc){
        if(e) return cb(e);

        var it = (doc[itKey] || 0) + Math.ceil(Math.random()*10);

        document[itKey] = it;
        document[options.slugName] = document[slugbaseKey]+'-'+it;
        document.markModified(slugbaseKey);

        _.forEach(options.source, function(item){
            document.markModified(item);
        });

        return document.save(cb);
    });
}

function Slugin(schema, options){
    options = _.defaults(options || {}, Slugin.defaultOptions);
    if(_.isString(options.source))
        options.source = [options.source];
    options.slugIt = options.slugName + '_it';
    options.slugBase = options.slugName + '_base';
    var fields = {};
    fields[options.slugName] = {
        type : String,
        unique: true
    };

    fields[options.slugBase] = {
        type: String,
        index:true
    };

    fields[options.slugIt] = {
        type: Number
    };

    schema.add(fields);

    schema.pre('save', function(next){
        var self = this;
        var slugBase = slugify(this,options);
        console.log('Old base: %s ; New Base: %s', this[options.slugBase], slugBase);
        if(this[options.slugBase] !== slugBase){
            this[options.slugName] = slugBase;
            this[options.slugBase] = slugBase;
            delete this[options.slugIt];
        }
        next();
    });

    schema.methods.save = function(cb){
        var self = this;
        mongoose.Model.prototype.save.call(self, function(e, model, num){
            if(e && (e.code === 11000  || e.code === 11001) && !!~e.err.indexOf(self[options.slugName])){
                incrementAndSave(self, options, cb);
            }else{
                cb(e,model,num);
            }
        });
    };
}

Slugin.defaultOptions = {
    slugName : 'slug',
    source : 'title',
    modelName : null
};

module.exports = Slugin;
<|endoftext|>


<commit_before>const express = require('express')
const path = require('path')
const os = require('os')
const PaperUtils = require('./view/paperutils')
const CIESubjects = require('./view/CIESubjects')
const sspdf = require('./lib/sspdf')
const fs = require('fs')
const SVGO = require('svgo')
const cheerio = require('cheerio')
require('./dist-server/serverrender')
const serverRender = global.serverRender
global.serverRender = null

const svgo = new SVGO()

let indexPath = path.join(__dirname, 'dist/index.html')
let indexHtml = fs.readFileSync(indexPath)
if (process.env.NODE_ENV !== 'production') {
  fs.watch(indexPath, list => {
    fs.readFile(indexPath, { encoding: 'utf8' }, (err, data) => {
      if (err) {
        console.error(err)
        process.exit(1)
      } else {
        indexHtml = data
      }
    })
  })
}

module.exports = (db, mongoose) => {
  const {PastPaperDoc, PastPaperIndex, PastPaperFeedback} = require('./lib/dbModel.js')(db, mongoose)
  let rMain = express.Router()

  function statusInfo () {
    return Promise.all([PastPaperDoc.count({}), PastPaperIndex.count({}), Promise.resolve(os.loadavg())])
      .then(([docCount, indexCount, loadAvg]) => {
        return Promise.resolve({docCount, indexCount, loadAvg: loadAvg.map(num => Math.round(num * 100) / 100)})
      }, err => Promise.reject(err))
  }

  rMain.use(function (req, res, next) {
    if (req.hostname.match(/^www\./)) {
      res.redirect('https://schsrch.xyz' + req.path)
    } else {
      next()
    }
  })
  rMain.get('/', function (req, res) {
    res.type('html')
    let $ = cheerio.load(indexHtml)
    $('.react-root').html(serverRender({})) // We want this to be static so that service worker don't end up caching old data
    res.send($.html())
  })
  rMain.use('/resources', express.static(path.join(__dirname, 'dist')))
  // rMain.use('/resources', express.static(path.join(__dirname, 'view/public')))
  rMain.get('/sw.js', function (req, res) {
    res.set('cache-control', 'max-age=0')
    res.sendFile(path.join(__dirname, 'dist/sw.js'))
  })

  rMain.get('/status/', function (req, res, next) {
    statusInfo().then(rst => res.send(rst), err => next(err))
  })

  function doSearch (query) {
    function findRelated (doc) {
      return PastPaperDoc.find({subject: doc.subject, time: doc.time, paper: doc.paper, variant: doc.variant}, {_id: true, type: true, fileType: true, numPages: true})
        .then(rst => Promise.resolve(rst.filter(x => x.type !== doc.type)))
    }
    return new Promise((resolve, reject) => {
      let match
      if ((match = query.match(/^!!index!([0-9a-f]+)$/))) {
        let id = match[1]
        PastPaperIndex.findOne({_id: id}).then(rstIndex => {
          if (!rstIndex) {
            resolve({
              response: 'text',
              list: []
            })
          } else {
            PastPaperDoc.findOne({_id: rstIndex.doc}).then(rstDoc => {
              if (!rstDoc) {
                resolve({
                  response: 'text',
                  list: []
                })
              } else {
                findRelated(rstDoc).then(rstRelated => {
                  resolve({
                    response: 'text',
                    list: [{doc: rstDoc, index: rstIndex, related: rstRelated}]
                  })
                }, err => resolve(({ response: 'text', list: [{doc: rstDoc, index: rstIndex, related: []}] })))
              }
            }, err => reject({response: 'error', err: err.toString()}))
          }
        }, err => reject({response: 'error', err: err.toString()}))
      } else if (query.match(/^\d{4}$/)) {
        fetchPP(query, null, null, null, null)
      } else if ((match = query.match(/^(\d{4})[_ ]([a-z]\d{1,2})$/))) {
        fetchPP(match[1], match[2])
      } else if ((match = query.match(/^(\d{4})[_ ](\d)$/))) {
        fetchPP(match[1], null, match[2], null, null)
      } else if ((match = query.match(/^(\d{4})[_ ]([a-z]+)$/))) {
        fetchPP(match[1], null, null, null, match[2])
      } else if ((match = query.match(/^(\d{4})[_ ]([a-z]\d{1,2})[_ ]([a-z]+)$/))) {
        fetchPP(match[1], match[2], null, null, match[3])
      } else if ((match = query.match(/^(\d{4})[_ ]([a-z]\d{1,2})[_ ]*(paper[_ ]*)?(\d)$/))) {
        fetchPP(match[1], match[2], match[4], null, null)
      } else if ((match = query.match(/^(\d{4})[_ ]([a-z]\d{1,2})[_ ]*(paper[_ ]*)?(\d)[_ ]*(\d)$/))) {
        fetchPP(match[1], match[2], match[4], match[5], null)
      } else if ((match = query.match(/^(\d{4})[_ ]([a-z]\d{1,2})[_ ]([a-z]+)[_ ](\d)[_ ]*(\d)$/))) {
        fetchPP(match[1], match[2], match[4], match[5], match[3])
      } else if ((match = query.match(/^(\d{4})[_ ]([a-z]\d{1,2})[_ ](\d)(\d)[_ ]([a-z]+)$/))) {
        fetchPP(match[1], match[2], match[3], match[4], match[5])
      } else if ((match = query.match(/^(\d{4})[_ ]([a-z]\d{1,2})[_ ]([a-z]+)[_ ](\d)$/))) {
        fetchPP(match[1], match[2], match[4], null, match[3])
      } else if ((match = query.toUpperCase().match(/^(\d{4})\/(\d{2})\/([A-Z]\/[A-Z]|SP)\/(\d{2})$/))) {
        let month = PaperUtils.odashMonthToMyMonth(match[3])
        let time = month + match[4]
        let [paper, variant] = match[2].split('')
        fetchPP(match[1], time, paper, variant, null)
      } else {
        PastPaperIndex.search(query).then(results => {
          Promise.all(results.map(rst => new Promise((resolve, reject) => {
            findRelated(rst.doc).then(related => {
              resolve({doc: rst.doc, index: rst.index, related: related})
            }, err => {
              resolve({doc: rst.doc, index: rst.index, related: []})
            })
          }))).then(rst => resolve({
            response: 'text',
            list: rst
          }), err => reject({response: 'error', err: err.toString()}))
        }).catch(err => {
          reject({response: 'error', err: err.toString()})
        })
      }

      function fetchPP (subject, time, paper, variant, type) {
        let finder = {}
        subject && (finder.subject = subject)
        let tMat = null
        if (time && (tMat = time.match(/^([a-z])(\d)$/))) {
          let mo = tMat[1]
          let yr = '0' + tMat[2]
          time = mo + yr
        }
        time && (finder.time = time.toLowerCase())
        if (parseInt(paper) === 0 && Number.isSafeInteger(parseInt(variant)) && parseInt(variant) !== 0) {
          paper = variant
          variant = '0'
        }
        paper && (finder.paper = parseInt(paper))
        variant && (finder.variant = parseInt(variant))
        type && (finder.type = type.toLowerCase())
        PastPaperDoc.find(finder, {__v: false, doc: false}).limit(51).then(rst => {
          if (rst.length >= 50) {
            resolve({
              response: 'overflow'
            })
            return
          }
          resolve({
            response: 'pp',
            list: rst
          })
        }, err => {
          reject({response: 'error', err: err.toString()})
        })
      }
    })
  }

  rMain.get('/search/:query', function (req, res, next) {
    let query = req.params.query.toString().trim()
    doSearch(query).then(rst => res.send(rst), err => {
      res.status(500)
      res.send(err)
    })
  })
  rMain.get('/disclaim/', function (req, res, next) {
    res.type('html')
    let $ = cheerio.load(indexHtml)
    $('.react-root').html(serverRender({view: 'disclaim'}))
    res.send($.html())
  })
  rMain.get('/formsearch/', function (req, res, next) {
    let query = req.query.query.toString().trim()
    if (query.length === 0) {
      res.redirect('/')
      return
    }
    doSearch(query).then(rst => {
      res.type('html')
      let $ = cheerio.load(indexHtml)
      $('.react-root').html(serverRender({query: {query, result: JSON.parse(JSON.stringify(rst))}}))
      res.send($.html())
    }, err => {
      next(err.err || err)
    }).catch(err => next(err))
  })

  rMain.get('/fetchDoc/:id', function (req, res, next) {
    PastPaperDoc.findOne({_id: req.params.id}).then(doc => {
      if (!doc) {
        next()
        return
      }
      let fname = `${PaperUtils.setToString(doc)}_${doc.type}.${doc.fileType}`
      res.set('Content-Disposition', `inline; filename=${JSON.stringify(fname)}`)
      res.type(doc.fileType)
      res.send(doc.doc)
    }).catch(err => next(err))
  })
  rMain.get('/sspdf/:docid/:page', function (req, res, next) {
    let pn = parseInt(req.params.page)
    if (!Number.isSafeInteger(pn) || pn < 0) {
      next()
      return
    }
    PastPaperDoc.findOne({_id: req.params.docid}, {_v: false}).then(doc => {
      if (!doc) {
        next()
        return
      }
      processSSPDF(doc, pn).then(sspdf => {
        res.set('Cache-Control', 'max-age=' + (10 * 24 * 60 * 60).toString())
        res.send(sspdf)
      }, err => {
        next(err)
      })
    }).catch(err => next(err))
  })

  function processSSPDF (doc, pn) {
    return new Promise((resolve, reject) => {
      function postCache (stuff) {
        let result = stuff
        delete result.text
        delete result.rects
        result.doc = doc
        result.doc.doc = null
        return result
      }

      PastPaperIndex.find({doc: doc._id, page: pn}).then(ppIdxes => {
        if (!ppIdxes || ppIdxes.length < 1) {
          reject(new Error('PastPaperIndex not found.'))
          return
        }
        let ppIdx = ppIdxes[0]
        if (ppIdx.sspdfCache) {
          resolve(postCache(ppIdx.sspdfCache))
        } else {
          console.log(`Building sspdf for ${doc._id}::${pn} (idx//${ppIdx._id})`)
          let buff = doc.doc
          sspdf.getPage(buff, pn, function (err, result) {
            if (err) {
              reject(err)
            } else {
              result.rects = result.rects.map(rect => {
                function round (n) {
                  return Math.round(n * 100) / 100
                }
                rect.x1 = round(rect.x1)
                rect.x2 = round(rect.x2)
                rect.y1 = round(rect.y1)
                rect.y2 = round(rect.y2)
                return rect
              })
              let svg = result.svg.toString('utf-8')
              svgo.optimize(svg, rSvgo => {
                result.svg = rSvgo.data
                ppIdx.sspdfCache = result
                ppIdx.save(err => {
                  if (err) {
                    console.error('Unable to save sspdfCache: ' + err)
                  }
                  resolve(postCache(result))
                })
              })
            }
          })
        }
      }).catch(reject)
    })
  }

  rMain.post('/feedback/', function (req, res, next) {
    let ctype = req.get('Content-Type')
    let done = false
    if (ctype !== 'application/json') {
      res.status(415)
      res.send('Content type incorrect.')
      done = true
      return
    }
    let body = ''
    req.setEncoding('utf8')
    req.on('data', chunk => {
      if (done) return
      body += chunk
    })
    req.on('end', () => {
      if (done) return
      done = true
      body = body.trim()
      if (body.length === 0) {
        res.status(403)
        res.send('Content is empty.')
        return
      }
      let parsed = null
      try {
        parsed = JSON.parse(body)
        if (typeof parsed !== 'object') {
          throw new Error()
        }
      } catch (e) {
        res.status(403)
        res.send('Content is not valid JSON.')
        return
      }
      let fb = new PastPaperFeedback({
        time: Date.now(),
        ip: req.ip,
        email: parsed.email,
        text: parsed.text,
        search: parsed.search
      })
      fb.save().then(() => {
        res.status(200)
        res.end()
      }, err => {
        res.status(403)
        res.send(err.toString())
      })
    })
  })
  rMain.get('/robots.txt', function (req, res) {
    if (req.hostname.match(/^beta\./)) {
      res.sendFile(path.join(__dirname, 'view/betaRobots.txt'))
    } else {
      res.type('txt')
      res.send('')
    }
  })

  return rMain
}
<commit_msg>Logging<commit_after>const express = require('express')
const path = require('path')
const os = require('os')
const PaperUtils = require('./view/paperutils')
const CIESubjects = require('./view/CIESubjects')
const sspdf = require('./lib/sspdf')
const fs = require('fs')
const SVGO = require('svgo')
const cheerio = require('cheerio')
require('./dist-server/serverrender')
const serverRender = global.serverRender
global.serverRender = null

const svgo = new SVGO()

let indexPath = path.join(__dirname, 'dist/index.html')
let indexHtml = fs.readFileSync(indexPath)
if (process.env.NODE_ENV !== 'production') {
  fs.watch(indexPath, list => {
    fs.readFile(indexPath, { encoding: 'utf8' }, (err, data) => {
      if (err) {
        console.error(err)
        process.exit(1)
      } else {
        indexHtml = data
      }
    })
  })
}

module.exports = (db, mongoose) => {
  const {PastPaperDoc, PastPaperIndex, PastPaperFeedback} = require('./lib/dbModel.js')(db, mongoose)
  let rMain = express.Router()

  function statusInfo () {
    return Promise.all([PastPaperDoc.count({}), PastPaperIndex.count({}), Promise.resolve(os.loadavg())])
      .then(([docCount, indexCount, loadAvg]) => {
        return Promise.resolve({docCount, indexCount, loadAvg: loadAvg.map(num => Math.round(num * 100) / 100)})
      }, err => Promise.reject(err))
  }

  rMain.use(function (req, res, next) {
    console.log(`Serving ${req.method.toUpperCase()} ${req.path} for ${req.ip}...`)
    if (req.hostname.match(/^www\./)) {
      res.redirect('https://schsrch.xyz' + req.path)
    } else {
      next()
    }
  })
  rMain.get('/', function (req, res) {
    res.type('html')
    let $ = cheerio.load(indexHtml)
    $('.react-root').html(serverRender({})) // We want this to be static so that service worker don't end up caching old data
    res.send($.html())
  })
  rMain.use('/resources', express.static(path.join(__dirname, 'dist')))
  // rMain.use('/resources', express.static(path.join(__dirname, 'view/public')))
  rMain.get('/sw.js', function (req, res) {
    res.set('cache-control', 'max-age=0')
    res.sendFile(path.join(__dirname, 'dist/sw.js'))
  })

  rMain.get('/status/', function (req, res, next) {
    statusInfo().then(rst => res.send(rst), err => next(err))
  })

  function doSearch (query) {
    function findRelated (doc) {
      return PastPaperDoc.find({subject: doc.subject, time: doc.time, paper: doc.paper, variant: doc.variant}, {_id: true, type: true, fileType: true, numPages: true})
        .then(rst => Promise.resolve(rst.filter(x => x.type !== doc.type)))
    }
    return new Promise((resolve, reject) => {
      let match
      if ((match = query.match(/^!!index!([0-9a-f]+)$/))) {
        let id = match[1]
        PastPaperIndex.findOne({_id: id}).then(rstIndex => {
          if (!rstIndex) {
            resolve({
              response: 'text',
              list: []
            })
          } else {
            PastPaperDoc.findOne({_id: rstIndex.doc}).then(rstDoc => {
              if (!rstDoc) {
                resolve({
                  response: 'text',
                  list: []
                })
              } else {
                findRelated(rstDoc).then(rstRelated => {
                  resolve({
                    response: 'text',
                    list: [{doc: rstDoc, index: rstIndex, related: rstRelated}]
                  })
                }, err => resolve(({ response: 'text', list: [{doc: rstDoc, index: rstIndex, related: []}] })))
              }
            }, err => reject({response: 'error', err: err.toString()}))
          }
        }, err => reject({response: 'error', err: err.toString()}))
      } else if (query.match(/^\d{4}$/)) {
        fetchPP(query, null, null, null, null)
      } else if ((match = query.match(/^(\d{4})[_ ]([a-z]\d{1,2})$/))) {
        fetchPP(match[1], match[2])
      } else if ((match = query.match(/^(\d{4})[_ ](\d)$/))) {
        fetchPP(match[1], null, match[2], null, null)
      } else if ((match = query.match(/^(\d{4})[_ ]([a-z]+)$/))) {
        fetchPP(match[1], null, null, null, match[2])
      } else if ((match = query.match(/^(\d{4})[_ ]([a-z]\d{1,2})[_ ]([a-z]+)$/))) {
        fetchPP(match[1], match[2], null, null, match[3])
      } else if ((match = query.match(/^(\d{4})[_ ]([a-z]\d{1,2})[_ ]*(paper[_ ]*)?(\d)$/))) {
        fetchPP(match[1], match[2], match[4], null, null)
      } else if ((match = query.match(/^(\d{4})[_ ]([a-z]\d{1,2})[_ ]*(paper[_ ]*)?(\d)[_ ]*(\d)$/))) {
        fetchPP(match[1], match[2], match[4], match[5], null)
      } else if ((match = query.match(/^(\d{4})[_ ]([a-z]\d{1,2})[_ ]([a-z]+)[_ ](\d)[_ ]*(\d)$/))) {
        fetchPP(match[1], match[2], match[4], match[5], match[3])
      } else if ((match = query.match(/^(\d{4})[_ ]([a-z]\d{1,2})[_ ](\d)(\d)[_ ]([a-z]+)$/))) {
        fetchPP(match[1], match[2], match[3], match[4], match[5])
      } else if ((match = query.match(/^(\d{4})[_ ]([a-z]\d{1,2})[_ ]([a-z]+)[_ ](\d)$/))) {
        fetchPP(match[1], match[2], match[4], null, match[3])
      } else if ((match = query.toUpperCase().match(/^(\d{4})\/(\d{2})\/([A-Z]\/[A-Z]|SP)\/(\d{2})$/))) {
        let month = PaperUtils.odashMonthToMyMonth(match[3])
        let time = month + match[4]
        let [paper, variant] = match[2].split('')
        fetchPP(match[1], time, paper, variant, null)
      } else {
        PastPaperIndex.search(query).then(results => {
          Promise.all(results.map(rst => new Promise((resolve, reject) => {
            findRelated(rst.doc).then(related => {
              resolve({doc: rst.doc, index: rst.index, related: related})
            }, err => {
              resolve({doc: rst.doc, index: rst.index, related: []})
            })
          }))).then(rst => resolve({
            response: 'text',
            list: rst
          }), err => reject({response: 'error', err: err.toString()}))
        }).catch(err => {
          reject({response: 'error', err: err.toString()})
        })
      }

      function fetchPP (subject, time, paper, variant, type) {
        let finder = {}
        subject && (finder.subject = subject)
        let tMat = null
        if (time && (tMat = time.match(/^([a-z])(\d)$/))) {
          let mo = tMat[1]
          let yr = '0' + tMat[2]
          time = mo + yr
        }
        time && (finder.time = time.toLowerCase())
        if (parseInt(paper) === 0 && Number.isSafeInteger(parseInt(variant)) && parseInt(variant) !== 0) {
          paper = variant
          variant = '0'
        }
        paper && (finder.paper = parseInt(paper))
        variant && (finder.variant = parseInt(variant))
        type && (finder.type = type.toLowerCase())
        PastPaperDoc.find(finder, {__v: false, doc: false}).limit(51).then(rst => {
          if (rst.length >= 50) {
            resolve({
              response: 'overflow'
            })
            return
          }
          resolve({
            response: 'pp',
            list: rst
          })
        }, err => {
          reject({response: 'error', err: err.toString()})
        })
      }
    })
  }

  rMain.get('/search/:query', function (req, res, next) {
    let query = req.params.query.toString().trim()
    doSearch(query).then(rst => res.send(rst), err => {
      res.status(500)
      res.send(err)
    })
  })
  rMain.get('/disclaim/', function (req, res, next) {
    res.type('html')
    let $ = cheerio.load(indexHtml)
    $('.react-root').html(serverRender({view: 'disclaim'}))
    res.send($.html())
  })
  rMain.get('/formsearch/', function (req, res, next) {
    let query = req.query.query.toString().trim()
    if (query.length === 0) {
      res.redirect('/')
      return
    }
    doSearch(query).then(rst => {
      res.type('html')
      let $ = cheerio.load(indexHtml)
      $('.react-root').html(serverRender({query: {query, result: JSON.parse(JSON.stringify(rst))}}))
      res.send($.html())
    }, err => {
      next(err.err || err)
    }).catch(err => next(err))
  })

  rMain.get('/fetchDoc/:id', function (req, res, next) {
    PastPaperDoc.findOne({_id: req.params.id}).then(doc => {
      if (!doc) {
        next()
        return
      }
      let fname = `${PaperUtils.setToString(doc)}_${doc.type}.${doc.fileType}`
      res.set('Content-Disposition', `inline; filename=${JSON.stringify(fname)}`)
      res.type(doc.fileType)
      res.send(doc.doc)
    }).catch(err => next(err))
  })
  rMain.get('/sspdf/:docid/:page', function (req, res, next) {
    let pn = parseInt(req.params.page)
    if (!Number.isSafeInteger(pn) || pn < 0) {
      next()
      return
    }
    PastPaperDoc.findOne({_id: req.params.docid}, {_v: false}).then(doc => {
      if (!doc) {
        next()
        return
      }
      processSSPDF(doc, pn).then(sspdf => {
        res.set('Cache-Control', 'max-age=' + (10 * 24 * 60 * 60).toString())
        res.send(sspdf)
      }, err => {
        next(err)
      })
    }).catch(err => next(err))
  })

  function processSSPDF (doc, pn) {
    return new Promise((resolve, reject) => {
      function postCache (stuff) {
        let result = stuff
        delete result.text
        delete result.rects
        result.doc = doc
        result.doc.doc = null
        return result
      }

      PastPaperIndex.find({doc: doc._id, page: pn}).then(ppIdxes => {
        if (!ppIdxes || ppIdxes.length < 1) {
          reject(new Error('PastPaperIndex not found.'))
          return
        }
        let ppIdx = ppIdxes[0]
        if (ppIdx.sspdfCache) {
          resolve(postCache(ppIdx.sspdfCache))
        } else {
          console.log(`Building sspdf for ${doc._id}::${pn} (idx//${ppIdx._id})`)
          let buff = doc.doc
          sspdf.getPage(buff, pn, function (err, result) {
            if (err) {
              reject(err)
            } else {
              result.rects = result.rects.map(rect => {
                function round (n) {
                  return Math.round(n * 100) / 100
                }
                rect.x1 = round(rect.x1)
                rect.x2 = round(rect.x2)
                rect.y1 = round(rect.y1)
                rect.y2 = round(rect.y2)
                return rect
              })
              let svg = result.svg.toString('utf-8')
              svgo.optimize(svg, rSvgo => {
                result.svg = rSvgo.data
                ppIdx.sspdfCache = result
                ppIdx.save(err => {
                  if (err) {
                    console.error('Unable to save sspdfCache: ' + err)
                  }
                  resolve(postCache(result))
                })
              })
            }
          })
        }
      }).catch(reject)
    })
  }

  rMain.post('/feedback/', function (req, res, next) {
    let ctype = req.get('Content-Type')
    let done = false
    if (ctype !== 'application/json') {
      res.status(415)
      res.send('Content type incorrect.')
      done = true
      return
    }
    let body = ''
    req.setEncoding('utf8')
    req.on('data', chunk => {
      if (done) return
      body += chunk
    })
    req.on('end', () => {
      if (done) return
      done = true
      body = body.trim()
      if (body.length === 0) {
        res.status(403)
        res.send('Content is empty.')
        return
      }
      let parsed = null
      try {
        parsed = JSON.parse(body)
        if (typeof parsed !== 'object') {
          throw new Error()
        }
      } catch (e) {
        res.status(403)
        res.send('Content is not valid JSON.')
        return
      }
      let fb = new PastPaperFeedback({
        time: Date.now(),
        ip: req.ip,
        email: parsed.email,
        text: parsed.text,
        search: parsed.search
      })
      fb.save().then(() => {
        res.status(200)
        res.end()
      }, err => {
        res.status(403)
        res.send(err.toString())
      })
    })
  })
  rMain.get('/robots.txt', function (req, res) {
    if (req.hostname.match(/^beta\./)) {
      res.sendFile(path.join(__dirname, 'view/betaRobots.txt'))
    } else {
      res.type('txt')
      res.send('')
    }
  })

  return rMain
}
<|endoftext|>


<commit_before>var inherits = require('inherits');
var EventEmitter = require('events').EventEmitter;
var mkdirp = require('mkdirp');
var fs = require('fs');
var path = require('path');
var x256 = require('x256');
var through = require('through2');
var split = require('split');
var minimist = require('minimist');

var showMenu = require('./lib/menu.js');
var showHelp = require('./lib/help.js');

module.exports = Shop;
inherits(Shop, EventEmitter);

function Shop (opts) {
    if (!(this instanceof Shop)) return new Shop(opts);
    if (!opts) opts = {};
    if (typeof opts === 'string') opts = { name: opts };

    this.name = opts.name;
    this.options = opts;

    if (!this.name) return this._error(
        'Your adventure must have a name! '
        + 'Supply an `opts.name` to adventure().'
    );
    this.command = opts.command || commandify(this.name);

    this.datadir = opts.datadir || path.resolve(
        process.env.HOME || process.env.USERPROFILE,
        '.config/' + this.name
    );
    mkdirp.sync(this.datadir);

    this.files = {
        completed: path.join(this.datadir, 'completed.json'),
        current: path.join(this.datadir, 'current.json')
    };
    this.state = {
        completed: [],
        current: null
    };

    try { this.state.completed = require(this.files.completed) }
    catch (err) {}

    try { this.state.current = require(this.files.current) }
    catch (err) {}

    this.colors = opts.colors || {};
    var c = {
        pass: [0,255,0],
        fail: [255,0,0],
        info: [0,255,255]
    };
    var colors = Object.keys(c).reduce(function (acc, key) {
        acc[key] = '\x1b[38;5;' + x256(c[key]) + 'm';
        return acc;
    }, {});

    if (!this.colors.pass) this.colors.pass = colors.pass;
    if (!this.colors.fail) this.colors.fail = colors.fail;
    if (!this.colors.info) this.colors.info = colors.info;
    this.colors.reset = '\x1b[00m';

    this._adventures = [];
}

Shop.prototype.execute = function (args) {
    var cmd = args[0];
    var argv = minimist(args, { alias: { h: 'help' } });

    if (cmd === 'verify' || /^v/.test(cmd)) {
        this.verify(args.slice(1), this.state.current);
    }
    else if (cmd === 'run' || /^r/.test(cmd)) {
        this.run(args.slice(1), this.state.current);
    }
    else if (cmd === 'help' || argv.help) {
        showHelp({ command: this.command });
    }
    else if (cmd === 'selected') {
        console.log(this.state.current);
    }
    else if (cmd === 'list') {
        console.log(this._adventures
            .map(function (adv) { return adv.name })
            .join('\n')
        );
    }
    else if (cmd === 'completed') {
        console.log(this.state.completed.join('\n'));
    }
    else if (cmd === 'select') {
        this.select(args[1]);
    }
    else if (!cmd || cmd === 'menu') {
        this.showMenu(this.options);
    }
    else {
        console.log('unrecognized command: ' + cmd);
    }
};

Shop.prototype.add = function (name, fn) {
    this._adventures.push({ name: name, fn: fn });
};

Shop.prototype.find = function (name) {
    for (var i = 0; i < this._adventures.length; i++) {
        var adv = this._adventures[i];
        if (adv.name === name) return adv;
    }
};

Shop.prototype.verify = function (args, name) {
    var self = this;
    var adv = this.find(name);
    if (!adv) return this._error(
        'No adventure is currently selected. '
        + 'Select an adventure from the menu.'
    );
    var p = adv.fn();
    if (!p.verify) return this._error(
        "This problem doesn't have a .verify function yet!"
    );
    if (typeof p.verify !== 'function') return this._error(
        'This p.verify is a ' + typeof p.verify
        + '. It should be a function instead.'
    );

    var s = p.verify(args, function (ok) {
        if (ok) self.pass(name, p)
        else self.fail(name, p)
    });
    if (s) this._show(s);
};

Shop.prototype.run = function (args, name) {
    var self = this;
    var adv = this.find(name);
    if (!adv) return this._error(
        'No adventure is currently selected. '
        + 'Select an adventure from the menu.'
    );
    var p = adv.fn();
    if (!p.run) return this._error(
        "This problem doesn't have a .run function."
    );
    if (typeof p.run !== 'function') return this._error(
        'This p.run is a ' + typeof p.run
        + '. It should be a function instead.'
    );
    var s = p.run(args);
    if (s) this._show(s);
};

Shop.prototype.pass = function (name, p) {
    var ix = this.state.completed.indexOf(name);
    if (ix < 0) this.state.completed.push(name);
    this.save('completed');

    if (p.pass) {
        this._show(p.pass);
        console.log();
    }
    else {
        console.log(
            '\n' + this.colors.pass
            + '@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@'
        );
        console.log(
            '@@@' + this.colors.reset
            + '     YOUR SOLUTION IS CORRECT'
            + this.colors.pass + '!     @@@'
        );
        console.log('@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@');
        console.log(this.colors.reset + '\n');
    }
    if (p.solution) this._show(p.solution);
};

Shop.prototype.fail = function (name, p) {
    if (p.fail) {
        this._show(p.fail);
        console.log();
    }
    else {
        console.log(
            this.colors.fail
            + '#########################################'
        );
        console.log(
            '###' + this.colors.reset
            + '   YOUR SOLUTION IS NOT CORRECT!'
            + this.colors.fail + '   ###'
        );
        console.log('#########################################');
        console.log(this.colors.reset + '\n');
    }
};

Shop.prototype.select = function (name) {
    var adv = this.find(name);
    this.state.current = name;
    this.save('current');

    var p = adv.fn();
    if (!p.problem) {
        p.problem = this.colors.info + Array(67).join('!') + '\n'
            + '!!!' + this.colors.reset
            + ' This adventure does not have a .problem description yet! '
            + this.colors.info + ' !!!\n!!!' + this.colors.reset
            + ' Set .problem to a string, buffer, stream or function that'
            + this.colors.info + ' !!!\n!!!' + this.colors.reset
            + ' returns a string, buffer, or stream.                     '
            + this.colors.info + ' !!!\n' + Array(67).join('!') + '\n'
        ;
    }
    if (p.problem) this._show(p.problem);
};

Shop.prototype.showMenu = function (opts) {
    var self = this;
    if (!opts) opts = {};

    var menu = showMenu({
        fg: opts.fg,
        bg: opts.bg,
        command: this.command,
        title: opts.title || this.name.toUpperCase(),
        names: this._adventures.map(function (x) { return x.name }),
        completed: this.state.completed
    });
    menu.on('select', function (name) {
        console.log();
        self.select(name);
    });
    menu.on('exit', function () {
        menu.close();
        console.log();
    });
    return menu;
};

Shop.prototype.save = function (key) {
    fs.writeFile(this.files[key], JSON.stringify(this.state[key]));
};

Shop.prototype._error = function (msg) {
    console.error('ERROR: ' + msg);
    process.exit(1);
};

Shop.prototype._show = function (m) {
    var self = this;
    if (typeof m === 'object' && m.pipe) {
        m.pipe(split()).pipe(through(write)).pipe(process.stdout);
    }
    else if (typeof m === 'function') {
        this._show(m());
    }
    else console.log(replace(m));

    function write (buf, enc, next) {
        this.push(replace(buf) + '\n');
        next();
    }
    function replace (s) {
        if (typeof s !== 'string') s = String(s);
        return s
            .replace(/\$ADVENTURE_COMMAND/g, self.command)
            .replace(/\$ADVENTURE_NAME/g, self.name)
        ;
    }
}

function commandify (s) {
    return String(s).toLowerCase().replace(/\s+/g, '-');
}
<commit_msg>case, punctuation-insensitive search<commit_after>var inherits = require('inherits');
var EventEmitter = require('events').EventEmitter;
var mkdirp = require('mkdirp');
var fs = require('fs');
var path = require('path');
var x256 = require('x256');
var through = require('through2');
var split = require('split');
var minimist = require('minimist');

var showMenu = require('./lib/menu.js');
var showHelp = require('./lib/help.js');

module.exports = Shop;
inherits(Shop, EventEmitter);

function Shop (opts) {
    if (!(this instanceof Shop)) return new Shop(opts);
    if (!opts) opts = {};
    if (typeof opts === 'string') opts = { name: opts };

    this.name = opts.name;
    this.options = opts;

    if (!this.name) return this._error(
        'Your adventure must have a name! '
        + 'Supply an `opts.name` to adventure().'
    );
    this.command = opts.command || commandify(this.name);

    this.datadir = opts.datadir || path.resolve(
        process.env.HOME || process.env.USERPROFILE,
        '.config/' + this.name
    );
    mkdirp.sync(this.datadir);

    this.files = {
        completed: path.join(this.datadir, 'completed.json'),
        current: path.join(this.datadir, 'current.json')
    };
    this.state = {
        completed: [],
        current: null
    };

    try { this.state.completed = require(this.files.completed) }
    catch (err) {}

    try { this.state.current = require(this.files.current) }
    catch (err) {}

    this.colors = opts.colors || {};
    var c = {
        pass: [0,255,0],
        fail: [255,0,0],
        info: [0,255,255]
    };
    var colors = Object.keys(c).reduce(function (acc, key) {
        acc[key] = '\x1b[38;5;' + x256(c[key]) + 'm';
        return acc;
    }, {});

    if (!this.colors.pass) this.colors.pass = colors.pass;
    if (!this.colors.fail) this.colors.fail = colors.fail;
    if (!this.colors.info) this.colors.info = colors.info;
    this.colors.reset = '\x1b[00m';

    this._adventures = [];
}

Shop.prototype.execute = function (args) {
    var cmd = args[0];
    var argv = minimist(args, { alias: { h: 'help' } });

    if (cmd === 'verify' || /^v/.test(cmd)) {
        this.verify(args.slice(1), this.state.current);
    }
    else if (cmd === 'run' || /^r/.test(cmd)) {
        this.run(args.slice(1), this.state.current);
    }
    else if (cmd === 'help' || argv.help) {
        showHelp({ command: this.command });
    }
    else if (cmd === 'selected') {
        console.log(this.state.current);
    }
    else if (cmd === 'list') {
        console.log(this._adventures
            .map(function (adv) { return adv.name })
            .join('\n')
        );
    }
    else if (cmd === 'completed') {
        console.log(this.state.completed.join('\n'));
    }
    else if (cmd === 'select') {
        this.select(args[1]);
    }
    else if (!cmd || cmd === 'menu') {
        this.showMenu(this.options);
    }
    else {
        console.log('unrecognized command: ' + cmd);
    }
};

Shop.prototype.add = function (name, fn) {
    this._adventures.push({ name: name, fn: fn });
};

Shop.prototype.find = function (name) {
    for (var i = 0; i < this._adventures.length; i++) {
        var adv = this._adventures[i];
        if (norm(adv.name) === norm(name)) return adv;
    }
    function norm (s) { return String(s).replace(/\W/g, '').toLowerCase() }
};

Shop.prototype.verify = function (args, name) {
    var self = this;
    var adv = this.find(name);
    if (!adv) return this._error(
        'No adventure is currently selected. '
        + 'Select an adventure from the menu.'
    );
    var p = adv.fn();
    if (!p.verify) return this._error(
        "This problem doesn't have a .verify function yet!"
    );
    if (typeof p.verify !== 'function') return this._error(
        'This p.verify is a ' + typeof p.verify
        + '. It should be a function instead.'
    );

    var s = p.verify(args, function (ok) {
        if (ok) self.pass(name, p)
        else self.fail(name, p)
    });
    if (s) this._show(s);
};

Shop.prototype.run = function (args, name) {
    var self = this;
    var adv = this.find(name);
    if (!adv) return this._error(
        'No adventure is currently selected. '
        + 'Select an adventure from the menu.'
    );
    var p = adv.fn();
    if (!p.run) return this._error(
        "This problem doesn't have a .run function."
    );
    if (typeof p.run !== 'function') return this._error(
        'This p.run is a ' + typeof p.run
        + '. It should be a function instead.'
    );
    var s = p.run(args);
    if (s) this._show(s);
};

Shop.prototype.pass = function (name, p) {
    var ix = this.state.completed.indexOf(name);
    if (ix < 0) this.state.completed.push(name);
    this.save('completed');

    if (p.pass) {
        this._show(p.pass);
        console.log();
    }
    else {
        console.log(
            '\n' + this.colors.pass
            + '@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@'
        );
        console.log(
            '@@@' + this.colors.reset
            + '     YOUR SOLUTION IS CORRECT'
            + this.colors.pass + '!     @@@'
        );
        console.log('@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@');
        console.log(this.colors.reset + '\n');
    }
    if (p.solution) this._show(p.solution);
};

Shop.prototype.fail = function (name, p) {
    if (p.fail) {
        this._show(p.fail);
        console.log();
    }
    else {
        console.log(
            this.colors.fail
            + '#########################################'
        );
        console.log(
            '###' + this.colors.reset
            + '   YOUR SOLUTION IS NOT CORRECT!'
            + this.colors.fail + '   ###'
        );
        console.log('#########################################');
        console.log(this.colors.reset + '\n');
    }
};

Shop.prototype.select = function (name) {
    var adv = this.find(name);
    this.state.current = name;
    this.save('current');

    var p = adv.fn();
    if (!p.problem) {
        p.problem = this.colors.info + Array(67).join('!') + '\n'
            + '!!!' + this.colors.reset
            + ' This adventure does not have a .problem description yet! '
            + this.colors.info + ' !!!\n!!!' + this.colors.reset
            + ' Set .problem to a string, buffer, stream or function that'
            + this.colors.info + ' !!!\n!!!' + this.colors.reset
            + ' returns a string, buffer, or stream.                     '
            + this.colors.info + ' !!!\n' + Array(67).join('!') + '\n'
        ;
    }
    if (p.problem) this._show(p.problem);
};

Shop.prototype.showMenu = function (opts) {
    var self = this;
    if (!opts) opts = {};

    var menu = showMenu({
        fg: opts.fg,
        bg: opts.bg,
        command: this.command,
        title: opts.title || this.name.toUpperCase(),
        names: this._adventures.map(function (x) { return x.name }),
        completed: this.state.completed
    });
    menu.on('select', function (name) {
        console.log();
        self.select(name);
    });
    menu.on('exit', function () {
        menu.close();
        console.log();
    });
    return menu;
};

Shop.prototype.save = function (key) {
    fs.writeFile(this.files[key], JSON.stringify(this.state[key]));
};

Shop.prototype._error = function (msg) {
    console.error('ERROR: ' + msg);
    process.exit(1);
};

Shop.prototype._show = function (m) {
    var self = this;
    if (typeof m === 'object' && m.pipe) {
        m.pipe(split()).pipe(through(write)).pipe(process.stdout);
    }
    else if (typeof m === 'function') {
        this._show(m());
    }
    else console.log(replace(m));

    function write (buf, enc, next) {
        this.push(replace(buf) + '\n');
        next();
    }
    function replace (s) {
        if (typeof s !== 'string') s = String(s);
        return s
            .replace(/\$ADVENTURE_COMMAND/g, self.command)
            .replace(/\$ADVENTURE_NAME/g, self.name)
        ;
    }
}

function commandify (s) {
    return String(s).toLowerCase().replace(/\s+/g, '-');
}
<|endoftext|>