<commit_before>/*eslint no-process-exit:0 */
'use strict';

var pad = require('pad');
var path = require('path');
var async = require('async');
var split = require('split');
var fs = require('fs');
var glob = require('glob');
var spawn = require('child_process').spawn;
var exec = require('child_process').exec;
var slug = require('slug');
var stripAnsi = require('strip-ansi');

// bump the default of 10
process.setMaxListeners(50);

function casperCmds(argv, cb) {
  var cmds = {};
  async.parallel({
    includes: function(cb) {
      if (argv.includes) {
        glob(argv.includes, {
          cwd: argv.cwd
        }, cb);
      } else {
        cb();
      }
    },
    tests: function(cb) {
      glob(argv.tests || '*', {
        cwd: path.join(argv.cwd, argv.base)
      }, cb);
    }
  }, function(err, res) {
    var args = [];
    if (res.includes) {
      args.push('--includes=' + res.includes.join(','));
    }
    args = args.concat(argv._);

    if (argv['list-tests'] && !argv.record) {
      console.log('SPOOK will run:');
      for (var i in res.tests) {
        console.log(res.tests[i]);
      }
      console.log('');
    }

    var name;
    res.tests.forEach(function(test) {
      name = test;
      if (test.substr(-3) === '.js') {
        name = test.substr(0, test.length - 3);
      }
      cmds[name] = {
        // slimerjs chokes if relative paths to tests do not start './'
        cmd: ['test'].concat(['./' + path.join(argv.base, test)]).concat(args),
        base: argv.base
      };
    });

    if (!res.tests.length) {
      err = 'no test files found';
    }
    cb(err, cmds);
  });
}

// possible test states
var states = {
  NONE: 0,
  PASS: 1,
  WARN: 2,
  VOID: 3,
  FAIL: 4
};

// regexs for casperjs log output
var re = {
  results: /(PASS|FAIL) (\d+) tests* executed in (\d+\.\d+)s, (\d+) passed, (\d+) failed, (\d+) dubious, (\d+) skipped./,
  test: /Test file: (.*)/
};

function Spook(opts, cb) {
  opts = opts || {};
  opts.cwd = opts.cwd || process.cwd();
  opts.base = opts.base || '';
  // remove any trailing slash from end of path
  opts.base = opts.base.replace(/\/+$/, '');
  opts.listener = opts.listener || function() {};

  var tests;
  var voided;
  var timeout = (opts.timeout || 20) * 1000;
  var cmds;
  var start;
  var run = {
    ACT: 1,
    DU: 0,
    TE: {},
    TO: {
      ST: 'NONE',
      TO: 0,
      DU: 0,
      PA: 0,
      FA: 0,
      DB: 0,
      SK: 0
    }
  };
  var spook = {};
  var casper = {};
  var ws = {};
  var hanged = {};

  var cleanup = function() {
    voided = true;
    console.log('caught SIGINT');
    save();
    process.exit();
  };
  process.on('SIGINT', cleanup);

  function save(cb) {
    process.removeListener('SIGINT', cleanup);
    cb = cb || function() {};

    for (var name in run.TE) {
      if (run.TE[name] === null) {
        // remove runs which were ordered but didn't execute
        delete run.TE[name];
      } else {
        // check for tests that didn't return results
        if (run.TE[name].ST === 'NONE') {
          run.TE[name].ST = 'VOID';
        }
        // ensure overall state is correct
        if (states[run.TE[name].ST] > states[run.TO.ST]) {
          run.TO.ST = run.TE[name].ST;
        }
      }
    }
    // if run totally failed
    if (voided || run.TO.ST === 'NONE') {
      run.TO.ST = 'VOID';
    }
    // log duration and end time;
    var end = +new Date();
    run.END = parseFloat((end / 1000).toFixed(0));
    run.DU = (end - start) / 1000;

    // resolve any rounding issues
    run.TO.DU = parseFloat(run.TO.DU.toFixed(3));

    // run is no longer active
    delete run.ACT;

    cb(null, run);

    // TODO: find a way to handle the sigint event - drop run.json somewhere sync'ly? Have server ingest on startup?
  }

  casperCmds(opts, function(err, res) {
    if (err) {
      return cb(err);
    }
    cmds = res;
    tests = Object.keys(cmds);

    cb(err, {
      tests: tests
    });
  });

  spook.run = function runner(cb) {
    start = +new Date();
    run.STA = parseFloat((start / 1000).toFixed(0));
    var fns = [];

    run.test = {};
    // ensure run order
    tests.forEach(function(test) {
      run.TE[test] = null;
    });

    tests.forEach(function(test, index) {
      var testFile;
      fns.push(function(cb) {
        if (voided) {
          clearInterval(hanged[test]);
          return cb();
        }
        // add output to test command
        cmds[test].cmd.push('--output=' + opts.out);

        // no colors if recording
        if (opts.record) {
          cmds[test].cmd.push('--no-colors');
        }

        casper[test] = spawn('casperjs', cmds[test].cmd, {
          cwd: opts.cwd
        });

        // check for hangs
        var hung = false;
        hanged[test] = setInterval(function() {
          if (hung) {
            clearInterval(hanged[test]);
            var warning = 'SPOOK: casperjs process exceeded timeout (' + timeout + 'ms)';
            if (opts.record && ws[test]) {
              ws[test].write(warning + '\n');
            }
            console.log(warning);
            spook.kill(test);
            run.TO.ST = 'VOID';
          }
          hung = true;
        }, timeout);

        if (opts.record) {
          ws[test] = fs.createWriteStream(path.join(opts.out, pad(3, index, '0') + '.' + slug(test) + '.log'), {
            flags: 'a'
          });
        }

        casper[test].stdout.pipe(split())
          .on('data', function(ln) {
            // note that we're still alive
            hung = false;

            // casperjs output to stdout
            if (opts.verbose) {
              console.log(ln);
            }

            var match;
            // check to see if the test file has been logged
            match = ln.match(re.test);
            if (match) {
              match[1] = match[1].replace(/ /g, '');
              match[1] = stripAnsi(match[1]);
              // directories have absolute paths
              if (match[1].substr(0, 1) === '/') {
                testFile = path.relative(path.join(opts.cwd, cmds[test].base), path.dirname(match[1]));
              } else {
                // strip preceeding './' or opts.base length
                testFile = match[1].substr(3 + (opts.base.length || -1));
                testFile = testFile.substr(0, (testFile.length - 3));
              }
              ln = '[TEST] ' + testFile;

              run.TE[testFile] = run.TE[testFile] || {
                ST: 'NONE',
                TO: 0,
                DU: 0,
                PA: 0,
                FA: 0,
                DB: 0,
                SK: 0
              };
            } else {
              // check to see if the test results have been logged
              match = ln.match(re.results);
              if (match) {
                var testResults = {
                  ST: match[1],
                  TO: parseInt(match[2], 10),
                  DU: parseFloat(match[3]),
                  PA: parseInt(match[4], 10),
                  FA: parseInt(match[5], 10),
                  DB: parseInt(match[6], 10),
                  SK: parseInt(match[7], 10)
                };
                // record test results
                run.TE[testFile] = testResults;
                // update totals
                run.TO.TO += testResults.TO;
                run.TO.DU += testResults.DU;
                run.TO.PA += testResults.PA;
                run.TO.FA += testResults.FA;
                run.TO.DB += testResults.DB;
                run.TO.SK += testResults.SK;
                // set overall state
                if (states[testResults.ST] > states[run.TO.ST]) {
                  run.TO.ST = testResults.ST;
                }
              }
            }

            opts.listener({
              type: 'ln',
              test: test,
              val: ln
            });

            if (ws[test] && !ws[test].closed) {
              ws[test].write(ln + '\n');
            }
          })
          .on('end', function(err) {
            if (err) {
              console.log(err);
            }
            if (opts.record && ws[test]) {
              ws[test].end();
            }
          })
          .on('error', function(err) {
            console.log(err);
          });

        casper[test].on('close', function() {
          clearInterval(hanged[test]);
          cb();
        });

      });
    });

    if (opts.work === 'series') {
      async.series(fns, function(err, res) {
        if (err) {
          console.log(err);
        }
        save(function(err, res) {
          cb(err, run);
        });
      });
    } else if (opts.work === 'parallel') {
      async.parallelLimit(fns, opts['parallel-limit'] || 3, function(err, res) {
        if (err) {
          console.log(err);
        }
        save(function(err, res) {
          cb(err, run);
        });
      });
    }
  };

  spook.kill = function(test) {
    if (voided) {
      return;
    }
    var killCMD;
    var warning = 'SPOOK: detected KILL request.';
    if (test) {
      // TODO: change mask to look for last 2 dirs of opts.out, then don't need to pass in mask
      // kill one test
      clearInterval(hanged[test]);
      console.log('SPOOK: trying to kill test:', test);
      killCMD = 'ps -ef | grep ' + path.join(cmds[test].base, test) + ' | grep ' + opts.out.substr(-5) + ' | awk \'{print $2}\'';
      if (ws[test] && !ws[test].closed) {
        ws[test].end(warning + '\n');
      }
    } else {
      // kill all tests
      voided = true;
      killCMD = 'ps -ef | grep \'output=' + opts.out + '\' | awk \'{print $2}\'';
      Object.keys(cmds).forEach(function(test, index) {
        clearInterval(hanged[test]);
        if (ws[test] && !ws[test].closed) {
          ws[test].end(warning + '\n');
        }
        console.log(warning);
        run.TO.ST = 'VOID';
      });
    }
    // execute kill commands
    exec(killCMD, function(error, stdout, stderr) {
      if (stderr) {
        console.log('SPOOK: error trying to kill casperjs process');
        console.log('stderr: ' + stderr);
        return;
      }
      var pids = stdout.split('\n');
      for (var pid in pids) {
        pid = pids[pid];
        if (pid) {
          console.log('SPOOK: kill', pid);
          exec('kill ' + pid);
        }
      }
    });
  };

  return spook;
}

module.exports = Spook;
<commit_msg>Adding support for env<commit_after>/*eslint no-process-exit:0 */
'use strict';

var pad = require('pad');
var path = require('path');
var async = require('async');
var split = require('split');
var fs = require('fs');
var glob = require('glob');
var spawn = require('child_process').spawn;
var exec = require('child_process').exec;
var slug = require('slug');
var stripAnsi = require('strip-ansi');

// bump the default of 10
process.setMaxListeners(50);

function casperCmds(argv, cb) {
  var cmds = {};
  async.parallel({
    includes: function(cb) {
      if (argv.includes) {
        glob(argv.includes, {
          cwd: argv.cwd
        }, cb);
      } else {
        cb();
      }
    },
    tests: function(cb) {
      glob(argv.tests || '*', {
        cwd: path.join(argv.cwd, argv.base)
      }, cb);
    }
  }, function(err, res) {
    var args = [];
    if (res.includes) {
      args.push('--includes=' + res.includes.join(','));
    }
    args = args.concat(argv._);

    if (argv['list-tests'] && !argv.record) {
      console.log('SPOOK will run:');
      for (var i in res.tests) {
        console.log(res.tests[i]);
      }
      console.log('');
    }

    var name;
    res.tests.forEach(function(test) {
      name = test;
      if (test.substr(-3) === '.js') {
        name = test.substr(0, test.length - 3);
      }
      cmds[name] = {
        // slimerjs chokes if relative paths to tests do not start './'
        cmd: ['test'].concat(['./' + path.join(argv.base, test)]).concat(args),
        base: argv.base
      };
    });

    if (!res.tests.length) {
      err = 'no test files found';
    }
    cb(err, cmds);
  });
}

// possible test states
var states = {
  NONE: 0,
  PASS: 1,
  WARN: 2,
  VOID: 3,
  FAIL: 4
};

// regexs for casperjs log output
var re = {
  results: /(PASS|FAIL) (\d+) tests* executed in (\d+\.\d+)s, (\d+) passed, (\d+) failed, (\d+) dubious, (\d+) skipped./,
  test: /Test file: (.*)/
};

function Spook(opts, cb) {
  opts = opts || {};
  opts.cwd = opts.cwd || process.cwd();
  opts.base = opts.base || '';
  // remove any trailing slash from end of path
  opts.base = opts.base.replace(/\/+$/, '');
  opts.listener = opts.listener || function() {};

  var tests;
  var voided;
  var timeout = (opts.timeout || 20) * 1000;
  var cmds;
  var start;
  var run = {
    ACT: 1,
    DU: 0,
    TE: {},
    TO: {
      ST: 'NONE',
      TO: 0,
      DU: 0,
      PA: 0,
      FA: 0,
      DB: 0,
      SK: 0
    }
  };
  var spook = {};
  var casper = {};
  var ws = {};
  var hanged = {};

  var cleanup = function() {
    voided = true;
    console.log('caught SIGINT');
    save();
    process.exit();
  };
  process.on('SIGINT', cleanup);

  function save(cb) {
    process.removeListener('SIGINT', cleanup);
    cb = cb || function() {};

    for (var name in run.TE) {
      if (run.TE[name] === null) {
        // remove runs which were ordered but didn't execute
        delete run.TE[name];
      } else {
        // check for tests that didn't return results
        if (run.TE[name].ST === 'NONE') {
          run.TE[name].ST = 'VOID';
        }
        // ensure overall state is correct
        if (states[run.TE[name].ST] > states[run.TO.ST]) {
          run.TO.ST = run.TE[name].ST;
        }
      }
    }
    // if run totally failed
    if (voided || run.TO.ST === 'NONE') {
      run.TO.ST = 'VOID';
    }
    // log duration and end time;
    var end = +new Date();
    run.END = parseFloat((end / 1000).toFixed(0));
    run.DU = (end - start) / 1000;

    // resolve any rounding issues
    run.TO.DU = parseFloat(run.TO.DU.toFixed(3));

    // run is no longer active
    delete run.ACT;

    cb(null, run);

    // TODO: find a way to handle the sigint event - drop run.json somewhere sync'ly? Have server ingest on startup?
  }

  casperCmds(opts, function(err, res) {
    if (err) {
      return cb(err);
    }
    cmds = res;
    tests = Object.keys(cmds);

    cb(err, {
      tests: tests
    });
  });

  spook.run = function runner(cb) {
    start = +new Date();
    run.STA = parseFloat((start / 1000).toFixed(0));
    var fns = [];

    run.test = {};
    // ensure run order
    tests.forEach(function(test) {
      run.TE[test] = null;
    });

    tests.forEach(function(test, index) {
      var testFile;
      fns.push(function(cb) {
        if (voided) {
          clearInterval(hanged[test]);
          return cb();
        }
        // add output to test command
        cmds[test].cmd.push('--output=' + opts.out);

        // no colors if recording
        if (opts.record) {
          cmds[test].cmd.push('--no-colors');
        }

        casper[test] = spawn('casperjs', cmds[test].cmd, {
          cwd: opts.cwd,
          env: opts.env
        });

        // check for hangs
        var hung = false;
        hanged[test] = setInterval(function() {
          if (hung) {
            clearInterval(hanged[test]);
            var warning = 'SPOOK: casperjs process exceeded timeout (' + timeout + 'ms)';
            if (opts.record && ws[test]) {
              ws[test].write(warning + '\n');
            }
            console.log(warning);
            spook.kill(test);
            run.TO.ST = 'VOID';
          }
          hung = true;
        }, timeout);

        if (opts.record) {
          ws[test] = fs.createWriteStream(path.join(opts.out, pad(3, index, '0') + '.' + slug(test) + '.log'), {
            flags: 'a'
          });
        }

        casper[test].stdout.pipe(split())
          .on('data', function(ln) {
            // note that we're still alive
            hung = false;

            // casperjs output to stdout
            if (opts.verbose) {
              console.log(ln);
            }

            var match;
            // check to see if the test file has been logged
            match = ln.match(re.test);
            if (match) {
              match[1] = match[1].replace(/ /g, '');
              match[1] = stripAnsi(match[1]);
              // directories have absolute paths
              if (match[1].substr(0, 1) === '/') {
                testFile = path.relative(path.join(opts.cwd, cmds[test].base), path.dirname(match[1]));
              } else {
                // strip preceeding './' or opts.base length
                testFile = match[1].substr(3 + (opts.base.length || -1));
                testFile = testFile.substr(0, (testFile.length - 3));
              }
              ln = '[TEST] ' + testFile;

              run.TE[testFile] = run.TE[testFile] || {
                ST: 'NONE',
                TO: 0,
                DU: 0,
                PA: 0,
                FA: 0,
                DB: 0,
                SK: 0
              };
            } else {
              // check to see if the test results have been logged
              match = ln.match(re.results);
              if (match) {
                var testResults = {
                  ST: match[1],
                  TO: parseInt(match[2], 10),
                  DU: parseFloat(match[3]),
                  PA: parseInt(match[4], 10),
                  FA: parseInt(match[5], 10),
                  DB: parseInt(match[6], 10),
                  SK: parseInt(match[7], 10)
                };
                // record test results
                run.TE[testFile] = testResults;
                // update totals
                run.TO.TO += testResults.TO;
                run.TO.DU += testResults.DU;
                run.TO.PA += testResults.PA;
                run.TO.FA += testResults.FA;
                run.TO.DB += testResults.DB;
                run.TO.SK += testResults.SK;
                // set overall state
                if (states[testResults.ST] > states[run.TO.ST]) {
                  run.TO.ST = testResults.ST;
                }
              }
            }

            opts.listener({
              type: 'ln',
              test: test,
              val: ln
            });

            if (ws[test] && !ws[test].closed) {
              ws[test].write(ln + '\n');
            }
          })
          .on('end', function(err) {
            if (err) {
              console.log(err);
            }
            if (opts.record && ws[test]) {
              ws[test].end();
            }
          })
          .on('error', function(err) {
            console.log(err);
          });

        casper[test].on('close', function() {
          clearInterval(hanged[test]);
          cb();
        });

      });
    });

    if (opts.work === 'series') {
      async.series(fns, function(err, res) {
        if (err) {
          console.log(err);
        }
        save(function(err, res) {
          cb(err, run);
        });
      });
    } else if (opts.work === 'parallel') {
      async.parallelLimit(fns, opts['parallel-limit'] || 3, function(err, res) {
        if (err) {
          console.log(err);
        }
        save(function(err, res) {
          cb(err, run);
        });
      });
    }
  };

  spook.kill = function(test) {
    if (voided) {
      return;
    }
    var killCMD;
    var warning = 'SPOOK: detected KILL request.';
    if (test) {
      // TODO: change mask to look for last 2 dirs of opts.out, then don't need to pass in mask
      // kill one test
      clearInterval(hanged[test]);
      console.log('SPOOK: trying to kill test:', test);
      killCMD = 'ps -ef | grep ' + path.join(cmds[test].base, test) + ' | grep ' + opts.out.substr(-5) + ' | awk \'{print $2}\'';
      if (ws[test] && !ws[test].closed) {
        ws[test].end(warning + '\n');
      }
    } else {
      // kill all tests
      voided = true;
      killCMD = 'ps -ef | grep \'output=' + opts.out + '\' | awk \'{print $2}\'';
      Object.keys(cmds).forEach(function(test, index) {
        clearInterval(hanged[test]);
        if (ws[test] && !ws[test].closed) {
          ws[test].end(warning + '\n');
        }
        console.log(warning);
        run.TO.ST = 'VOID';
      });
    }
    // execute kill commands
    exec(killCMD, function(error, stdout, stderr) {
      if (stderr) {
        console.log('SPOOK: error trying to kill casperjs process');
        console.log('stderr: ' + stderr);
        return;
      }
      var pids = stdout.split('\n');
      for (var pid in pids) {
        pid = pids[pid];
        if (pid) {
          console.log('SPOOK: kill', pid);
          exec('kill ' + pid);
        }
      }
    });
  };

  return spook;
}

module.exports = Spook;
<|endoftext|>


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

/***********************************************************************
 *                                                                   _
 *       _____  _                           ____  _                 |_|
 *      |  _  |/ \   ____  ____ __ ___     / ___\/ \   __   _  ____  _
 *      | |_| || |  / __ \/ __ \\ '_  \ _ / /    | |___\ \ | |/ __ \| |
 *      |  _  || |__. ___/. ___/| | | ||_|\ \___ |  _  | |_| |. ___/| |
 *      |_/ \_|\___/\____|\____||_| |_|    \____/|_| |_|_____|\____||_|
 *
 *      ================================================================
 *                 More than a coder, More than a designer
 *      ================================================================
 *
 *
 *      - Document: start.js
 *      - Author: aleen42
 *      - Description: for shell script to use
 *      - Create Time: May 9th, 2016
 *      - Update Time: May 11st, 2016
 *
 *
 **********************************************************************/

/**
 * [cls: import the module]
 * @type {Object}
 */
const cls = require("./index");

/**
 * [opt: help options]
 * @type {[type]}
 */
const opt = require('node-getopt')
	.create([
		['u', 'user=ARG', '\tGitHub\'s user_name'],
		['t', 'token=ARG', '\tPersonal API tokens for calling GitHub APIs'],
		['', 'filter[=filter]', '\tSatistic filter: \n\t(ex.)\n\t--filter=all \t\tfor all repositories (default), \n\t--filter=original \tfor original repositories, \n\t--filter=fork \t\tfor your forking repositories'],
		['', 'color[=corlor]', '\tSatistic filter: \n\t(ex.)\n\t--color=random \t\tfor badges with random colors (default), \n\t--color=blue \t\tfor badges with blue themes, \n\t--color=red \t\tfor badges with red themes'],
		['', 'repo[=repo]', '\tSatistic for a specific repostiory: \n\t(ex.)\n\t--repo=all \t\tfor all repositories (default)'],
		['a', 'accurate', '\tShow accurate number'],
		['l', 'line', '\tShow lines'],
		['v', 'version', '\tShow current version'],
		['', 'badges-lists', '\tShow a list of badges for markdown'],
		['h' , 'help', '\tTutorial for this command']
	])
	.setHelp('\
\n\n\nUseAge: cls -u <user_name> -t <github token> [options]\
\n                                                                  _\
\n      _____  _                           ____  _                 |_|\
\n     |  _  |/ \\   ____  ____ __ ___     / ___\\/ \\   __   _  ____  _\
\n     | |_| || |  / __ \\/ __ \\\\ \'_  \\ _ / /    | |___\\ \\ | |/ __ \\| |\
\n     |  _  || |__. ___/. ___/| | | ||_|\\ \\___ |  _  | |_| |. ___/| |\
\n     |_/ \\_|\\___/\\____|\\____||_| |_|    \\____/|_| |_|_____|\\____||_| \
\n                                                                     \
\n     ================================================================\
\n                More than a coder, More than a designer              \
\n     ================================================================\n\n\n\n[[OPTIONS]]\n\n\n\n')
	.bindHelp()
	.parseSystem();

if (opt.options.version) {
	console.log('CLS v2.1.5, Copyright © aleen42 2012-2016');
	return;
}

cls.calculate(opt.options.user, opt.options);
console.time('CLS');
<commit_msg>update<commit_after>#! /usr/bin/env node

/***********************************************************************
 *                                                                   _
 *       _____  _                           ____  _                 |_|
 *      |  _  |/ \   ____  ____ __ ___     / ___\/ \   __   _  ____  _
 *      | |_| || |  / __ \/ __ \\ '_  \ _ / /    | |___\ \ | |/ __ \| |
 *      |  _  || |__. ___/. ___/| | | ||_|\ \___ |  _  | |_| |. ___/| |
 *      |_/ \_|\___/\____|\____||_| |_|    \____/|_| |_|_____|\____||_|
 *
 *      ================================================================
 *                 More than a coder, More than a designer
 *      ================================================================
 *
 *
 *      - Document: start.js
 *      - Author: aleen42
 *      - Description: for shell script to use
 *      - Create Time: May 9th, 2016
 *      - Update Time: May 11st, 2016
 *
 *
 **********************************************************************/

/**
 * [cls: import the module]
 * @type {Object}
 */
const cls = require("./index");

/**
 * [opt: help options]
 * @type {[type]}
 */
const opt = require('node-getopt')
	.create([
		['u', 'user=ARG', '\tGitHub\'s user_name'],
		['t', 'token=ARG', '\tPersonal API tokens for calling GitHub APIs'],
		['', 'filter[=filter]', '\tSatistic filter: \n\t(ex.)\n\t--filter=all \t\tfor all repositories (default), \n\t--filter=original \tfor original repositories, \n\t--filter=fork \t\tfor your forking repositories'],
		['', 'color[=corlor]', '\tSatistic filter: \n\t(ex.)\n\t--color=random \t\tfor badges with random colors (default), \n\t--color=blue \t\tfor badges with blue themes, \n\t--color=red \t\tfor badges with red themes'],
		['', 'repo[=repo]', '\tSatistic for a specific repostiory: \n\t(ex.)\n\t--repo=all \t\tfor all repositories (default)'],
		['a', 'accurate', '\tShow accurate number'],
		['l', 'line', '\tShow lines'],
		['v', 'version', '\tShow current version'],
		['', 'badges-lists', '\tShow a list of badges for markdown'],
		['h' , 'help', '\tTutorial for this command']
	])
	.setHelp('\
\n\n\nUseAge: cls -u <user_name> -t <github token> [options]\
\n                                                                  _\
\n      _____  _                           ____  _                 |_|\
\n     |  _  |/ \\   ____  ____ __ ___     / ___\\/ \\   __   _  ____  _\
\n     | |_| || |  / __ \\/ __ \\\\ \'_  \\ _ / /    | |___\\ \\ | |/ __ \\| |\
\n     |  _  || |__. ___/. ___/| | | ||_|\\ \\___ |  _  | |_| |. ___/| |\
\n     |_/ \\_|\\___/\\____|\\____||_| |_|    \\____/|_| |_|_____|\\____||_| \
\n                                                                     \
\n     ================================================================\
\n                More than a coder, More than a designer              \
\n     ================================================================\n\n\n\n[[OPTIONS]]\n\n\n\n')
	.bindHelp()
	.parseSystem();

if (opt.options.version) {
	console.log('\n\
\n                                                                  _\
\n      _____  _                           ____  _                 |_|\
\n     |  _  |/ \\   ____  ____ __ ___     / ___\\/ \\   __   _  ____  _\
\n     | |_| || |  / __ \\/ __ \\\\ \'_  \\ _ / /    | |___\\ \\ | |/ __ \\| |\
\n     |  _  || |__. ___/. ___/| | | ||_|\\ \\___ |  _  | |_| |. ___/| |\
\n     |_/ \\_|\\___/\\____|\\____||_| |_|    \\____/|_| |_|_____|\\____||_| \
\n                                                                     \
\n     ================================================================\
\n                More than a coder, More than a designer              \
\n     ================================================================\
\n\
\n               CLS v2.1.5, Copyright © aleen42 2012-2016\n\n');
	return;
}

cls.calculate(opt.options.user, opt.options);
console.time('CLS');
<|endoftext|>


<commit_before>'use strict';

module.exports = `
pre, code {
  font-family: 'Roboto Mono', monospace;
  background-color: #eee;
}
pre {
  border-top: 1px solid #999;
  border-bottom: 1px solid #999;
  display: block;
  padding: 10px 0px 10px 0px;
}
body {
  font-size: 16px;
  /* font-family: 'Open Sans', sans-serif; */
  font-family: 'IBM Plex Sans', sans-serif;
  hyphens: none;
}
.title {
  text-align: center;
}
.imageblock > .content > img {
  display: block;
  margin-left: auto;
  margin-right: auto;
}
.note {
  font-size: 0.875em;
  padding: 2px;
}
.important {
  background: hsla(58, 100%, 86%, 1);
  padding: 8px;
  margin: 8px 0px 8px 0px;
  border: 2px solid #999;
}

.icon .title {
  display: none;
}

.note .content {
  padding-left: 16px;
}

#toctitle {
  display: block;
  font-size: 1.5em;
  font-weight: bold;
  color: #003262;
}

.toc ul { list-style-type: none; }

.toc li { padding-top: 0.2em; }

.sectlevel1 li { font-size: 1.5rem; }

.sectlevel2 li { font-size: 1.2rem; }

.sectlevel3 li { font-size: 1.0rem; }

.ftitle {
  margin: 4em;
}
.ftitle1 {
  margin: 2em;
  font-size: 2em;
}
.ftitle2 {
  margin: 2em;
  font-size: 1.5em;
}

table {
  width: 100%;
  max-width: 100%;
  border-collapse: collapse;
  padding-bottom: 4px;
}

.tableblock {
  padding: 1px 4px 1px 4px;
  margin: 0px;
}

.imageblock {
  padding: 16px 0px 8px 0px;
  margin: 0px;
}

.frame-all {
  margin: 8px 0px 16px 0px;
}

.tableblock td, .tableblock th {
  border: 1px solid #000;
}

.halign-left {
  text-align: left;
}

.title {
  padding-top: 8px;
}

h2 {
  color: #003262;
}

.note, p {
  text-align: justify;
}

.hdlist1 {
  font-weight: bold;
}

.hljs {
  overflow-x: hidden;
}

@media screen and (min-width: 1400px) {
  #toc {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    width: min(820px, calc(100% - 1000px));
    overflow: auto;
  }
  body {
    padding-left: min(820px, calc(100% - 1000px));
  }
}

@media screen {
  table {
    border-color: #000;
    border-width: 1px;
  }
  img {
    max-width: 968px;
  }
  .container {
    max-width: 968px;
    margin: 0 auto;
  }

  a {
    color: #003262;

  }

}

@media print {
  img {
    max-width: 800px;
  }
  a {
    text-decoration-line: underline;
    color: #000;
  }
  body {
    font-size: 13pt;
  }

  h1 {
    string-set: header content();
  }

  @page {
    @top-center {
      content: "top-center"; border: solid green;
    }
  }

  @page {
    @bottom-center {
      content: "bottom-center"; border: solid green;
    }
  }

  @page {
    margin-top: 0pt;
    margin-bottom: 0pt;
  }
  @page :left {
    margin-left: 0pt;
    margin-right: 0pt;
  }
  @page :right {
    margin-left: 0pt;
    margin-right: 0pt;
  }
  @page :first {
    margin-left: 0pt;
    margin-right: 0pt;
  }

  h2 {
    break-before: page;
    page-break-before: page;
  }
  h2, h3, h4, h5 {
    break-after: avoid-page;
    page-break-after: avoid-page;
  }
  pre {
    font-size: 12pt;
    break-inside: avoid;
    page-break-inside: avoid;
  }
  .listingblock {
    break-inside: avoid;
    page-break-inside: avoid;
  }
  .ftitle {
    text-align: center;
    margin-top: 50%;
    break-after: always;
    page-break-after: always;
  }
  .note {
    break-inside: avoid;
    page-break-inside: avoid;
  }
  .important {
    break-inside: avoid;
    page-break-inside: avoid;
  }
  table {
    page-break-inside: avoid;
  }
}

`;
// `
// #spacer {
//   shape-outside: polygon(0 0, 800px 0, 800px 50px, 0 50px, 0 700px, 800px 700px, 800px 750px, 0 750px, 0 1500px, 800px 1500px, 800px 1550px, 0 1550px);
//   width: 800px;
//   height: 3200px;
//   float: left;
// }
// `;
<commit_msg>TOC page number styles<commit_after>'use strict';

module.exports = `
pre, code {
  font-family: 'Roboto Mono', monospace;
  background-color: #eee;
}
pre {
  border-top: 1px solid #999;
  border-bottom: 1px solid #999;
  display: block;
  padding: 10px 0px 10px 0px;
}
body {
  font-size: 16px;
  /* font-family: 'Open Sans', sans-serif; */
  font-family: 'IBM Plex Sans', sans-serif;
  hyphens: none;
}
.title {
  text-align: center;
}
.imageblock > .content > img {
  display: block;
  margin-left: auto;
  margin-right: auto;
}
.note {
  font-size: 0.875em;
  padding: 2px;
}
.important {
  background: hsla(58, 100%, 86%, 1);
  padding: 8px;
  margin: 8px 0px 8px 0px;
  border: 2px solid #999;
}

.icon .title {
  display: none;
}

.note .content {
  padding-left: 16px;
}

#toctitle {
  display: block;
  font-size: 24px;
  font-weight: bold;
  color: #003262;
}

.toc ul { list-style-type: none; }

.toc li { padding-top: 0.2em; }

.sectlevel1 li { font-size: 20px; }

.sectlevel2 li { font-size: 18px; }

.sectlevel3 li { font-size: 16px; }

.ftitle {
  margin: 4em;
}
.ftitle1 {
  margin: 2em;
  font-size: 3em;
}
.ftitle2 {
  margin: 2em;
  font-size: 2em;
}

table {
  width: 100%;
  max-width: 100%;
  border-collapse: collapse;
  padding-bottom: 4px;
}

.tableblock {
  padding: 1px 4px 1px 4px;
  margin: 0px;
}

.imageblock {
  padding: 16px 0px 8px 0px;
  margin: 0px;
}

.frame-all {
  margin: 8px 0px 16px 0px;
}

.tableblock td, .tableblock th {
  border: 1px solid #000;
}

.halign-left {
  text-align: left;
}

.title {
  padding-top: 8px;
}

h2 {
  color: #003262;
}

.note, p {
  text-align: justify;
}

.hdlist1 {
  font-weight: bold;
}

.hljs {
  overflow-x: hidden;
}

@media screen and (min-width: 1400px) {
  #toc {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    width: min(820px, calc(100% - 1000px));
    overflow: auto;
  }
  body {
    padding-left: min(820px, calc(100% - 1000px));
  }
}

@media screen {
  table {
    border-color: #000;
    border-width: 1px;
  }
  img {
    max-width: 968px;
  }
  .container {
    max-width: 968px;
    margin: 0 auto;
  }

  a {
    color: #003262;

  }

}

@media print {

  .sectlevel1 > li {
    padding: 0;
    overflow-x: hidden;
    list-style: none
  }

  .sectlevel1 > li > a + span {
    float: right;
    padding-left: 0.33em;
  }

  .sectlevel1 > li > a + span:before {
    leader(dotted)
  }

  img {
    max-width: 800px;
  }
  a {
    text-decoration-line: underline;
    color: #000;
  }
  body {
    font-size: 13pt;
  }

  h1 {
    string-set: header content();
  }

  @page {
    @top-center {
      content: "top-center"; border: solid green;
    }
  }

  @page {
    @bottom-center {
      content: "bottom-center"; border: solid green;
    }
  }

  @page {
    margin-top: 0pt;
    margin-bottom: 0pt;
  }
  @page :left {
    margin-left: 0pt;
    margin-right: 0pt;
  }
  @page :right {
    margin-left: 0pt;
    margin-right: 0pt;
  }
  @page :first {
    margin-left: 0pt;
    margin-right: 0pt;
  }

  h2 {
    break-before: page;
    page-break-before: page;
  }
  h2, h3, h4, h5 {
    break-after: avoid-page;
    page-break-after: avoid-page;
  }
  pre {
    font-size: 12pt;
    break-inside: avoid;
    page-break-inside: avoid;
  }
  .listingblock {
    break-inside: avoid;
    page-break-inside: avoid;
  }
  .ftitle {
    text-align: center;
    margin-top: 50%;
    break-after: always;
    page-break-after: always;
  }
  .toc {
    page-break-after: always;
  }
  .note {
    break-inside: avoid;
    page-break-inside: avoid;
  }
  .important {
    break-inside: avoid;
    page-break-inside: avoid;
  }
  table {
    page-break-inside: avoid;
  }
}

`;
// `
// #spacer {
//   shape-outside: polygon(0 0, 800px 0, 800px 50px, 0 50px, 0 700px, 800px 700px, 800px 750px, 0 750px, 0 1500px, 800px 1500px, 800px 1550px, 0 1550px);
//   width: 800px;
//   height: 3200px;
//   float: left;
// }
// `;
<|endoftext|>


<commit_before>// The Post Settings Menu available in the content preview screen, as well as the post editor.

/*global window, document, $, _, Backbone, Ghost, moment */

(function () {
    "use strict";

    Ghost.View.PostSettings = Ghost.View.extend({

        events: {
            'blur  .post-setting-slug' : 'editSlug',
            'click .post-setting-slug' : 'selectSlug',
            'blur  .post-setting-date' : 'editDate',
            'click .post-setting-static-page' : 'toggleStaticPage',
            'click .delete' : 'deletePost'
        },

        initialize: function () {
            if (this.model) {
                this.listenTo(this.model, 'change:id', this.render);
                this.listenTo(this.model, 'change:status', this.render);
                this.listenTo(this.model, 'change:published_at', this.render);
                this.listenTo(this.model, 'change:page', this.render);
                this.listenTo(this.model, 'change:title', this.updateSlugPlaceholder);
            }
        },

        render: function () {
            var slug = this.model ? this.model.get('slug') : '',
                pubDate = this.model ? this.model.get('published_at') : 'Not Published',
                $pubDateEl = this.$('.post-setting-date'),
                $postSettingSlugEl = this.$('.post-setting-slug'),
                publishedDateFormat = 'DD MMM YY HH:mm';

            $postSettingSlugEl.val(slug);

            // Update page status test if already a page.
            if (this.model && this.model.get('page')) {
                $('.post-setting-static-page').prop('checked', this.model.get('page'));
            }

            // Insert the published date, and make it editable if it exists.
            if (this.model && this.model.get('published_at')) {
                pubDate = moment(pubDate).format(publishedDateFormat);
                $pubDateEl.attr('placeholder', '');
            } else {
                $pubDateEl.attr('placeholder', moment().format(publishedDateFormat));
            }

            if (this.model && this.model.get('id')) {
                this.$('.post-setting-page').removeClass('hidden');
                this.$('.delete').removeClass('hidden');
            }

            // Apply different style for model's that aren't
            // yet persisted to the server.
            // Mostly we're hiding the delete post UI
            if (this.model.id === undefined) {
                this.$el.addClass('unsaved');
            } else {
                this.$el.removeClass('unsaved');
            }

            $pubDateEl.val(pubDate);
        },

        // Requests a new slug when the title was changed
        updateSlugPlaceholder: function () {
            var title = this.model.get('title'),
                $postSettingSlugEl = this.$('.post-setting-slug');

            // If there's a title present we want to
            // validate it against existing slugs in the db
            // and then update the placeholder value.
            if (title) {
                $.ajax({
                    url: Ghost.paths.apiRoot + '/posts/getSlug/' + encodeURIComponent(title) + '/',
                    success: function (result) {
                        $postSettingSlugEl.attr('placeholder', result);
                    }
                });
            } else {
                // If there's no title set placeholder to blank
                // and don't make an ajax request to server
                // for a proper slug (as there won't be any).
                $postSettingSlugEl.attr('placeholder', '');
                return;
            }
        },

        selectSlug: function (e) {
            e.currentTarget.select();
        },

        editSlug: _.debounce(function (e) {
            e.preventDefault();
            var self = this,
                slug = self.model.get('slug'),
                slugEl = e.currentTarget,
                newSlug = slugEl.value;

            // If the model doesn't currently
            // exist on the server (aka has no id)
            // then just update the model's value
            if (self.model.id === undefined) {
                this.model.set({
                    slug: newSlug
                });
                return;
            }

            // Ignore unchanged slugs
            if (slug === newSlug) {
                slugEl.value = slug === undefined ? '' : slug;
                return;
            }

            this.model.save({
                slug: newSlug
            }, {
                success : function (model, response, options) {
                    /*jslint unparam:true*/
                    // Repopulate slug in case it changed on the server (e.g. 'new-slug-2')
                    slugEl.value = model.get('slug');
                    Ghost.notifications.addItem({
                        type: 'success',
                        message: "Permalink successfully changed to " + model.get('slug') + '.',
                        status: 'passive'
                    });
                },
                error : function (model, xhr) {
                    /*jslint unparam:true*/
                    Ghost.notifications.addItem({
                        type: 'error',
                        message: Ghost.Views.Utils.getRequestErrorMessage(xhr),
                        status: 'passive'
                    });
                }
            });
        }, 500),

        editDate: _.debounce(function (e) {
            e.preventDefault();
            var self = this,
                parseDateFormats = ['DD MMM YY HH:mm', 'DD MMM YYYY HH:mm', 'DD/MM/YY HH:mm', 'DD/MM/YYYY HH:mm', 'DD-MM-YY HH:mm', 'DD-MM-YYYY HH:mm'],
                displayDateFormat = 'DD MMM YY HH:mm',
                errMessage = '',
                pubDate = self.model.get('published_at'),
                pubDateEl = e.currentTarget,
                newPubDate = pubDateEl.value,
                pubDateMoment,
                newPubDateMoment;

            // if there is no new pub date do nothing
            if (!newPubDate) {
                return;
            }

            // Check for missing time stamp on new data
            // If no time specified, add a 12:00
            if (newPubDate && !newPubDate.slice(-5).match(/\d+:\d\d/)) {
                newPubDate += " 12:00";
            }

            newPubDateMoment = moment(newPubDate, parseDateFormats);

            // If there was a published date already set
            if (pubDate) {
                 // Check for missing time stamp on current model
                // If no time specified, add a 12:00
                if (!pubDate.slice(-5).match(/\d+:\d\d/)) {
                    pubDate += " 12:00";
                }

                pubDateMoment = moment(pubDate, parseDateFormats);

                 // Ensure the published date has changed
                if (newPubDate.length === 0 || pubDateMoment.isSame(newPubDateMoment)) {
                    // If it wasn't, reset it and return
                    pubDateEl.value = pubDateMoment.format(displayDateFormat);
                    return;
                }
            }

            // Validate new Published date
            if (!newPubDateMoment.isValid()) {
                errMessage = 'Published Date must be a valid date with format: DD MMM YY HH:mm (e.g. 6 Dec 14 15:00)';
            }

            if (newPubDateMoment.diff(new Date(), 'h') > 0) {
                errMessage = 'Published Date cannot currently be in the future.';
            }

            if (errMessage.length) {
                // Show error message
                Ghost.notifications.addItem({
                    type: 'error',
                    message: errMessage,
                    status: 'passive'
                });

                // Reset back to original value and return
                pubDateEl.value = pubDateMoment ? pubDateMoment.format(displayDateFormat) : '';
                return;
            }

            // If the model doesn't currently
            // exist on the server (aka has no id)
            // then just update the model's value
            if (self.model.id === undefined) {
                this.model.set({
                    published_at: newPubDateMoment.toDate()
                });
                return;
            }

            // Save new 'Published' date
            this.model.save({
                published_at: newPubDateMoment.toDate()
            }, {
                success : function (model) {
                    pubDateEl.value = moment(model.get('published_at')).format(displayDateFormat);
                    Ghost.notifications.addItem({
                        type: 'success',
                        message: 'Publish date successfully changed to ' + pubDateEl.value + '.',
                        status: 'passive'
                    });
                },
                error : function (model, xhr) {
                    /*jslint unparam:true*/
                    Ghost.notifications.addItem({
                        type: 'error',
                        message: Ghost.Views.Utils.getRequestErrorMessage(xhr),
                        status: 'passive'
                    });
                }
            });

        }, 500),

        toggleStaticPage: _.debounce(function (e) {
            var pageEl = $(e.currentTarget),
                page = pageEl.prop('checked');

            // Don't try to save
            // if the model doesn't currently
            // exist on the server
            if (this.model.id === undefined) {
                this.model.set({
                    page: page
                });
                return;
            }

            this.model.save({
                page: page
            }, {
                success : function (model, response, options) {
                    /*jslint unparam:true*/
                    pageEl.prop('checked', page);
                    Ghost.notifications.addItem({
                        type: 'success',
                        message: "Successfully converted " + (page ? "to static page" : "to post") + '.',
                        status: 'passive'
                    });
                },
                error : function (model, xhr) {
                    /*jslint unparam:true*/
                    Ghost.notifications.addItem({
                        type: 'error',
                        message: Ghost.Views.Utils.getRequestErrorMessage(xhr),
                        status: 'passive'
                    });
                }
            });
        }, 500),

        deletePost: function (e) {
            e.preventDefault();
            var self = this;
            // You can't delete a post
            // that hasn't yet been saved
            if (this.model.id === undefined) {
                return;
            }
            this.addSubview(new Ghost.Views.Modal({
                model: {
                    options: {
                        close: false,
                        confirm: {
                            accept: {
                                func: function () {
                                    self.model.destroy({
                                        wait: true
                                    }).then(function () {
                                        // Redirect to content screen if deleting post from editor.
                                        if (window.location.pathname.indexOf('editor') > -1) {
                                            window.location = Ghost.paths.subdir + '/ghost/content/';
                                        }
                                        Ghost.notifications.addItem({
                                            type: 'success',
                                            message: 'Your post has been deleted.',
                                            status: 'passive'
                                        });
                                    }, function () {
                                        Ghost.notifications.addItem({
                                            type: 'error',
                                            message: 'Your post could not be deleted. Please try again.',
                                            status: 'passive'
                                        });
                                    });
                                },
                                text: "Yes"
                            },
                            reject: {
                                func: function () {
                                    return true;
                                },
                                text: "No"
                            }
                        },
                        type: "action",
                        style: ["wide", "centered"],
                        animation: 'fade'
                    },
                    content: {
                        template: 'blank',
                        title: 'Are you sure you want to delete this post?',
                        text: '
This is permanent! No backups, no restores, no magic undo button.
 We warned you, ok?
'
                    }
                }
            }));
        }

    });

}());
<commit_msg>Publish date and time display fix<commit_after>// The Post Settings Menu available in the content preview screen, as well as the post editor.

/*global window, document, $, _, Backbone, Ghost, moment */

(function () {
    "use strict";

    Ghost.View.PostSettings = Ghost.View.extend({

        events: {
            'blur  .post-setting-slug' : 'editSlug',
            'click .post-setting-slug' : 'selectSlug',
            'blur  .post-setting-date' : 'editDate',
            'click .post-setting-static-page' : 'toggleStaticPage',
            'click .delete' : 'deletePost'
        },

        initialize: function () {
            if (this.model) {
                this.listenTo(this.model, 'change:id', this.render);
                this.listenTo(this.model, 'change:status', this.render);
                this.listenTo(this.model, 'change:published_at', this.render);
                this.listenTo(this.model, 'change:page', this.render);
                this.listenTo(this.model, 'change:title', this.updateSlugPlaceholder);
            }
        },

        render: function () {
            var slug = this.model ? this.model.get('slug') : '',
                pubDate = this.model ? this.model.get('published_at') : 'Not Published',
                $pubDateEl = this.$('.post-setting-date'),
                $postSettingSlugEl = this.$('.post-setting-slug'),
                publishedDateFormat = 'DD MMM YY @ HH:mm';

            $postSettingSlugEl.val(slug);

            // Update page status test if already a page.
            if (this.model && this.model.get('page')) {
                $('.post-setting-static-page').prop('checked', this.model.get('page'));
            }

            // Insert the published date, and make it editable if it exists.
            if (this.model && this.model.get('published_at')) {
                pubDate = moment(pubDate).format(publishedDateFormat);
                $pubDateEl.attr('placeholder', '');
            } else {
                $pubDateEl.attr('placeholder', moment().format(publishedDateFormat));
            }

            if (this.model && this.model.get('id')) {
                this.$('.post-setting-page').removeClass('hidden');
                this.$('.delete').removeClass('hidden');
            }

            // Apply different style for model's that aren't
            // yet persisted to the server.
            // Mostly we're hiding the delete post UI
            if (this.model.id === undefined) {
                this.$el.addClass('unsaved');
            } else {
                this.$el.removeClass('unsaved');
            }

            $pubDateEl.val(pubDate);
        },

        // Requests a new slug when the title was changed
        updateSlugPlaceholder: function () {
            var title = this.model.get('title'),
                $postSettingSlugEl = this.$('.post-setting-slug');

            // If there's a title present we want to
            // validate it against existing slugs in the db
            // and then update the placeholder value.
            if (title) {
                $.ajax({
                    url: Ghost.paths.apiRoot + '/posts/getSlug/' + encodeURIComponent(title) + '/',
                    success: function (result) {
                        $postSettingSlugEl.attr('placeholder', result);
                    }
                });
            } else {
                // If there's no title set placeholder to blank
                // and don't make an ajax request to server
                // for a proper slug (as there won't be any).
                $postSettingSlugEl.attr('placeholder', '');
                return;
            }
        },

        selectSlug: function (e) {
            e.currentTarget.select();
        },

        editSlug: _.debounce(function (e) {
            e.preventDefault();
            var self = this,
                slug = self.model.get('slug'),
                slugEl = e.currentTarget,
                newSlug = slugEl.value;

            // If the model doesn't currently
            // exist on the server (aka has no id)
            // then just update the model's value
            if (self.model.id === undefined) {
                this.model.set({
                    slug: newSlug
                });
                return;
            }

            // Ignore unchanged slugs
            if (slug === newSlug) {
                slugEl.value = slug === undefined ? '' : slug;
                return;
            }

            this.model.save({
                slug: newSlug
            }, {
                success : function (model, response, options) {
                    /*jslint unparam:true*/
                    // Repopulate slug in case it changed on the server (e.g. 'new-slug-2')
                    slugEl.value = model.get('slug');
                    Ghost.notifications.addItem({
                        type: 'success',
                        message: "Permalink successfully changed to " + model.get('slug') + '.',
                        status: 'passive'
                    });
                },
                error : function (model, xhr) {
                    /*jslint unparam:true*/
                    Ghost.notifications.addItem({
                        type: 'error',
                        message: Ghost.Views.Utils.getRequestErrorMessage(xhr),
                        status: 'passive'
                    });
                }
            });
        }, 500),

        editDate: _.debounce(function (e) {
            e.preventDefault();
            var self = this,
                parseDateFormats = ['DD MMM YY HH:mm', 'DD MMM YYYY HH:mm', 'DD/MM/YY HH:mm', 'DD/MM/YYYY HH:mm', 'DD-MM-YY HH:mm', 'DD-MM-YYYY HH:mm'],
                displayDateFormat = 'DD MMM YY @ HH:mm',
                errMessage = '',
                pubDate = self.model.get('published_at'),
                pubDateEl = e.currentTarget,
                newPubDate = pubDateEl.value,
                pubDateMoment,
                newPubDateMoment;

            // if there is no new pub date do nothing
            if (!newPubDate) {
                return;
            }

            // Check for missing time stamp on new data
            // If no time specified, add a 12:00
            if (newPubDate && !newPubDate.slice(-5).match(/\d+:\d\d/)) {
                newPubDate += " 12:00";
            }

            newPubDateMoment = moment(newPubDate, parseDateFormats);

            // If there was a published date already set
            if (pubDate) {
                 // Check for missing time stamp on current model
                // If no time specified, add a 12:00
                if (!pubDate.slice(-5).match(/\d+:\d\d/)) {
                    pubDate += " 12:00";
                }

                pubDateMoment = moment(pubDate, parseDateFormats);

                 // Ensure the published date has changed
                if (newPubDate.length === 0 || pubDateMoment.isSame(newPubDateMoment)) {
                    // If it wasn't, reset it and return
                    pubDateEl.value = pubDateMoment.format(displayDateFormat);
                    return;
                }
            }

            // Validate new Published date
            if (!newPubDateMoment.isValid()) {
                errMessage = 'Published Date must be a valid date with format: DD MMM YY @ HH:mm (e.g. 6 Dec 14 @ 15:00)';
            }

            if (newPubDateMoment.diff(new Date(), 'h') > 0) {
                errMessage = 'Published Date cannot currently be in the future.';
            }

            if (errMessage.length) {
                // Show error message
                Ghost.notifications.addItem({
                    type: 'error',
                    message: errMessage,
                    status: 'passive'
                });

                // Reset back to original value and return
                pubDateEl.value = pubDateMoment ? pubDateMoment.format(displayDateFormat) : '';
                return;
            }

            // If the model doesn't currently
            // exist on the server (aka has no id)
            // then just update the model's value
            if (self.model.id === undefined) {
                this.model.set({
                    published_at: newPubDateMoment.toDate()
                });
                return;
            }

            // Save new 'Published' date
            this.model.save({
                published_at: newPubDateMoment.toDate()
            }, {
                success : function (model) {
                    pubDateEl.value = moment(model.get('published_at')).format(displayDateFormat);
                    Ghost.notifications.addItem({
                        type: 'success',
                        message: 'Publish date successfully changed to ' + pubDateEl.value + '.',
                        status: 'passive'
                    });
                },
                error : function (model, xhr) {
                    /*jslint unparam:true*/
                    Ghost.notifications.addItem({
                        type: 'error',
                        message: Ghost.Views.Utils.getRequestErrorMessage(xhr),
                        status: 'passive'
                    });
                }
            });

        }, 500),

        toggleStaticPage: _.debounce(function (e) {
            var pageEl = $(e.currentTarget),
                page = pageEl.prop('checked');

            // Don't try to save
            // if the model doesn't currently
            // exist on the server
            if (this.model.id === undefined) {
                this.model.set({
                    page: page
                });
                return;
            }

            this.model.save({
                page: page
            }, {
                success : function (model, response, options) {
                    /*jslint unparam:true*/
                    pageEl.prop('checked', page);
                    Ghost.notifications.addItem({
                        type: 'success',
                        message: "Successfully converted " + (page ? "to static page" : "to post") + '.',
                        status: 'passive'
                    });
                },
                error : function (model, xhr) {
                    /*jslint unparam:true*/
                    Ghost.notifications.addItem({
                        type: 'error',
                        message: Ghost.Views.Utils.getRequestErrorMessage(xhr),
                        status: 'passive'
                    });
                }
            });
        }, 500),

        deletePost: function (e) {
            e.preventDefault();
            var self = this;
            // You can't delete a post
            // that hasn't yet been saved
            if (this.model.id === undefined) {
                return;
            }
            this.addSubview(new Ghost.Views.Modal({
                model: {
                    options: {
                        close: false,
                        confirm: {
                            accept: {
                                func: function () {
                                    self.model.destroy({
                                        wait: true
                                    }).then(function () {
                                        // Redirect to content screen if deleting post from editor.
                                        if (window.location.pathname.indexOf('editor') > -1) {
                                            window.location = Ghost.paths.subdir + '/ghost/content/';
                                        }
                                        Ghost.notifications.addItem({
                                            type: 'success',
                                            message: 'Your post has been deleted.',
                                            status: 'passive'
                                        });
                                    }, function () {
                                        Ghost.notifications.addItem({
                                            type: 'error',
                                            message: 'Your post could not be deleted. Please try again.',
                                            status: 'passive'
                                        });
                                    });
                                },
                                text: "Yes"
                            },
                            reject: {
                                func: function () {
                                    return true;
                                },
                                text: "No"
                            }
                        },
                        type: "action",
                        style: ["wide", "centered"],
                        animation: 'fade'
                    },
                    content: {
                        template: 'blank',
                        title: 'Are you sure you want to delete this post?',
                        text: '
This is permanent! No backups, no restores, no magic undo button.
 We warned you, ok?
'
                    }
                }
            }));
        }

    });

}());
<|endoftext|>


<commit_before>const handler = require('serve-handler');
const http = require('http');
const chalk = require('chalk');
const {spawn} = require('child_process');
const minimist = require('minimist');

/* eslint-disable no-console */
const log = console.log;
/* eslint-enable no-console */

let yarnStartProcess;
let stdOutChunks = [];
let stdErrChunks = [];
let startYarnOutput = false;

const {v, verbose, port = 5000} = minimist(process.argv.slice(2));
const showVerbose = !!(v || verbose);

function clearConsole({withHistory = true} = {}) {
    if (!withHistory) {
        process.stdout.write('\x1Bc');
        return;
    }
    process.stdout.write(
        process.platform === 'win32' ? '\x1B[2J\x1B[0f' : '\x1B[2J\x1B[3J\x1B[H'
    );
}

function printConfigInstruction() {
    const data = {
        portal: {
            url: `http://localhost:${port}/portal`
        }
    };
    const stringifedData = JSON.stringify(data, null, 2);
    const splitData = stringifedData.split('\n');
    log();
    splitData.forEach((data, idx, arr) => {
        if (idx === 0 || idx === arr.length - 1) {
            log(chalk.grey(data));
        } else {
            log(chalk.bold.whiteBright(data));
        }
    });
    log();
}

function printInstructions() {
    log();
    log(chalk.yellowBright.underline(`Add portal to your local Ghost config`));
    printConfigInstruction();
    log(chalk.cyanBright('='.repeat(50)));
    log();
}

function onProcessClose(code) {
    yarnStartProcess = null;
    stdErrChunks = [];
    stdOutChunks = [];
    log(chalk.redBright.bold.underline(`Please restart the script...\n`));
}

function getBuildOptions() {
    process.env.FORCE_COLOR = 'true';
    const options = {
        shell: true,
        env: process.env
    };
    if (showVerbose) {
        options.stdio = 'inherit';
    }
    return options;
}

function doYarnStart() {
    if (yarnStartProcess) {
        return;
    }
    const options = getBuildOptions();
    yarnStartProcess = spawn('yarn start:combined', options);

    ['SIGINT', 'SIGTERM'].forEach(function (sig) {
        yarnStartProcess.on(sig, function () {
            yarnStartProcess && yarnStartProcess.exit();
        });
    });

    yarnStartProcess.on('close', onProcessClose);

    if (!showVerbose) {
        yarnStartProcess.stdout.on('data', (data) => {
            stdOutChunks.push(data);
            printYarnProcessOutput(data);
        });
        yarnStartProcess.stderr.on('data', (data) => {
            log(Buffer.from(data).toString());
            stdErrChunks.push(data);
        });
    }
}

function printYarnProcessOutput(data) {
    const dataStr = Buffer.from(data).toString();
    const dataArr = dataStr.split('\n').filter((d) => {
        return /\S/.test(d.trim());
    });
    if (dataArr.find(d => d.includes('Starting the development'))) {
        startYarnOutput = true;
        log(chalk.yellowBright('Starting the development server...\n'));
        return;
    }
    dataArr.forEach((dataOut) => {
        if (startYarnOutput) {
            log(dataOut);
        }
    });
    if (startYarnOutput) {
        log();
    }
}

function startDevServer() {
    const server = http.createServer((request, response) => {
        return handler(request, response, {
            rewrites: [
                {source: '/portal', destination: 'scripts/load-portal.js'}
            ],
            headers: [
                {
                    source: '**',
                    headers: [{
                        key: 'Cache-Control',
                        value: 'no-cache'
                    }]
                }
            ]
        });
    });

    server.listen(port, () => {
        log(chalk.whiteBright(`Portal dev server is running on http://localhost:${port}`));
        printInstructions();
        doYarnStart();
    });
}

clearConsole({withHistory: false});
startDevServer();
<commit_msg>Added access control header for start mode<commit_after>const handler = require('serve-handler');
const http = require('http');
const chalk = require('chalk');
const {spawn} = require('child_process');
const minimist = require('minimist');

/* eslint-disable no-console */
const log = console.log;
/* eslint-enable no-console */

let yarnStartProcess;
let stdOutChunks = [];
let stdErrChunks = [];
let startYarnOutput = false;

const {v, verbose, port = 5000} = minimist(process.argv.slice(2));
const showVerbose = !!(v || verbose);

function clearConsole({withHistory = true} = {}) {
    if (!withHistory) {
        process.stdout.write('\x1Bc');
        return;
    }
    process.stdout.write(
        process.platform === 'win32' ? '\x1B[2J\x1B[0f' : '\x1B[2J\x1B[3J\x1B[H'
    );
}

function printConfigInstruction() {
    const data = {
        portal: {
            url: `http://localhost:${port}/portal`
        }
    };
    const stringifedData = JSON.stringify(data, null, 2);
    const splitData = stringifedData.split('\n');
    log();
    splitData.forEach((data, idx, arr) => {
        if (idx === 0 || idx === arr.length - 1) {
            log(chalk.grey(data));
        } else {
            log(chalk.bold.whiteBright(data));
        }
    });
    log();
}

function printInstructions() {
    log();
    log(chalk.yellowBright.underline(`Add portal to your local Ghost config`));
    printConfigInstruction();
    log(chalk.cyanBright('='.repeat(50)));
    log();
}

function onProcessClose(code) {
    yarnStartProcess = null;
    stdErrChunks = [];
    stdOutChunks = [];
    log(chalk.redBright.bold.underline(`Please restart the script...\n`));
}

function getBuildOptions() {
    process.env.FORCE_COLOR = 'true';
    const options = {
        shell: true,
        env: process.env
    };
    if (showVerbose) {
        options.stdio = 'inherit';
    }
    return options;
}

function doYarnStart() {
    if (yarnStartProcess) {
        return;
    }
    const options = getBuildOptions();
    yarnStartProcess = spawn('yarn start:combined', options);

    ['SIGINT', 'SIGTERM'].forEach(function (sig) {
        yarnStartProcess.on(sig, function () {
            yarnStartProcess && yarnStartProcess.exit();
        });
    });

    yarnStartProcess.on('close', onProcessClose);

    if (!showVerbose) {
        yarnStartProcess.stdout.on('data', (data) => {
            stdOutChunks.push(data);
            printYarnProcessOutput(data);
        });
        yarnStartProcess.stderr.on('data', (data) => {
            log(Buffer.from(data).toString());
            stdErrChunks.push(data);
        });
    }
}

function printYarnProcessOutput(data) {
    const dataStr = Buffer.from(data).toString();
    const dataArr = dataStr.split('\n').filter((d) => {
        return /\S/.test(d.trim());
    });
    if (dataArr.find(d => d.includes('Starting the development'))) {
        startYarnOutput = true;
        log(chalk.yellowBright('Starting the development server...\n'));
        return;
    }
    dataArr.forEach((dataOut) => {
        if (startYarnOutput) {
            log(dataOut);
        }
    });
    if (startYarnOutput) {
        log();
    }
}

function startDevServer() {
    const server = http.createServer((request, response) => {
        return handler(request, response, {
            rewrites: [
                {source: '/portal', destination: 'scripts/load-portal.js'}
            ],
            headers: [
                {
                    source: '**',
                    headers: [{
                        key: 'Cache-Control',
                        value: 'no-cache'
                    },{
                        key: 'Access-Control-Allow-Origin',
                        value: '*'
                    }]
                }
            ]
        });
    });

    server.listen(port, () => {
        log(chalk.whiteBright(`Portal dev server is running on http://localhost:${port}`));
        printInstructions();
        doYarnStart();
    });
}

clearConsole({withHistory: false});
startDevServer();
<|endoftext|>


<commit_before>var protocol = require('./protocol')
var bitfield = require('./bitfield')

module.exports = Swarm

function Swarm (feed) {
  if (!(this instanceof Swarm)) return new Swarm(feed)

  var self = this

  this.feed = feed
  this.wires = []
  this.selections = []

  this._blocks = 0
  this._critical = []
  this._requesting = bitfield(8)
  this._synchronized = 0

  feed.on('have', function (block) {
    for (var i = 0; i < self.wires.length; i++) {
      var wire = self.wires[i]
      if (!wire.remoteBitfield.get(block)) {
        wire.have({blocks: [block]})
      }
    }
    self._maybeSynced()
  })

  process.nextTick(function () {
    feed.open(function (err) {
      if (!err) self._maybeSynced()
    })
  })
}

Swarm.prototype._maybeSynced = function () {
  while (this.feed.bitfield.get(this._synchronized)) {
    this._synchronized++
  }
  if (this._synchronized === this._blocks && this._blocks) {
    this.emit('synchronized')
  }
}

Swarm.prototype.select = function (from, to, priority) {
  throw new Error('Not yet implemented')
}

Swarm.prototype.deselect = function (from, to, priority) {
  throw new Error('Not yet implemented')
}

Swarm.prototype.critical = function (block, cb) {
  this._critical.push({block: block, callback: cb})
}

Swarm.prototype._checkCritical = function (block, data) {
  if (!this._critical.length) return

  var picked = null

  for (var i = 0; i < this._critical.length; i++) {
    if (this._critical[i].block === block) {
      if (!picked) picked = []
      picked.push(i)
    }
  }

  while (picked && picked.length) {
    var next = picked.pop()
    var pick = this._critical[next]
    this._critical.splice(pick, 1)
    pick.callback(null, data)
  }
}

Swarm.prototype.replicate = function (opts) {
  var self = this
  var stream = protocol(opts)

  stream.setTimeout(5000, function () {
    stream.emit('timeout')
    stream.destroy()
  })

  stream.on('channel', function (wire) {
    self.add(wire)
  })

  stream.join(this.feed)

  return stream
}

Swarm.prototype.add = function (wire) {
  var self = this
  this.wires.push(wire)
  this.onwire(wire)
  wire.once('close', function () {
    self.remove(wire)
  })
}

Swarm.prototype.remove = function (wire) {
  var i = this.wires.indexOf(wire)
  if (i > -1) this.wires.splice(i, 1)

  for (i = 0; i < wire.requests.length; i++) {
    this._requesting.set(wire.requests[i])
  }
  for (i = 0; i < this.wires.length; i++) {
    this.update(wire)
  }
}

Swarm.prototype.onwire = function (wire) {
  var self = this
  var synchronized = 0
  var feed = this.feed

  wire.requests = []
  wire.remoteBitfield = bitfield(16)
  wire.remotePausing = false

  wire.on('have', function (have) {
    var i = 0
    var blocks = 0

    if (have.bitfield) {
      wire.remoteBitfield = bitfield(have.bitfield)
      for (i = Math.max(0, wire.remoteBitfield.length - 8); i < wire.remoteBitfield.length; i++) {
        if (wire.remoteBitfield.get(i)) blocks = i + 1
      }
    }

    if (have.blocks) {
      for (i = 0; i < have.blocks.length; i++) {
        wire.remoteBitfield.set(have.blocks[i], true)
        if (have.blocks[i] >= blocks) blocks = have.blocks[i] + 1
      }
    }

    if (self._blocks < blocks) {
      self._blocks = blocks
    }

    maybeSynchronized()
    self.update(wire)
  })

  wire.on('resume', function () {
    wire.remotePausing = false
    self.update(wire)
  })

  wire.on('pause', function () {
    wire.remotePausing = true
  })

  wire.on('response', function (response) {
    feed.put(response.block, response.data, response, function (err) {
      if (err) return destroy(err)

      var i = wire.requests.indexOf(response.block)
      if (i > -1) wire.requests.splice(i, 1)

      feed.emit('download', response.block, response.data, response)
      self._checkCritical(response.block, response.data)
      self.update(wire)
    })
  })

  wire.on('request', function (request) {
    // TODO: pass in remoteTree
    wire.remoteBitfield.set(request.block, true)
    feed.proof(request.block, {digest: request.digest}, function (err, proof) {
      if (err) return destroy(err)
      feed.get(request.block, function (err, data) {
        if (err) return destroy(err)

        var response = {
          block: request.block,
          data: data,
          nodes: proof.nodes,
          signature: proof.signature
        }

        wire.response(response)
        feed.emit('upload', request.block, data, response)
      })
    })
  })

  wire.have({
    bitfield: trim(feed.bitfield.buffer)
  })

  wire.resume()

  function destroy () {
    wire.close()
  }

  function maybeSynchronized () {
    while (wire.remoteBitfield.get(synchronized)) {
      synchronized++
    }
    if (synchronized === feed.blocks && feed.blocks) {
      feed.emit('remote-synchronized')
    }
  }
}

Swarm.prototype.pickBlock = function (wire) {
  var seen = 0
  var block = -1
  var i = 0

  for (i = 0; i < this._critical.length; i++) {
    var blk = this._critical[i].block
    if (!this._requesting.get(blk) && wire.remoteBitfield.get(blk)) {
      return blk
    }
  }

  for (i = 0; i < this._blocks; i++) {
    if (this.feed.bitfield.get(i) || !wire.remoteBitfield.get(i) || this._requesting.get(i)) continue
    if (Math.random() < (1 / ++seen)) block = i
  }

  return block
}

Swarm.prototype.update = function (wire) {
  if (wire.remotePausing) return

  var block = -1

  while (wire.requests.length < 16 && (block = this.pickBlock(wire)) > -1) {
    this._requesting.set(block, true)
    wire.requests.push(block)
    wire.request({
      block: block,
      digest: this.feed.digest(block)
    })
  }
}

function trim (buf) {
  var length = buf.length
  while (length && !buf[length - 1]) length--
  if (length !== buf.length) return buf.slice(0, length)
  return buf
}
<commit_msg>fix sync events<commit_after>var protocol = require('./protocol')
var bitfield = require('./bitfield')

module.exports = Swarm

function Swarm (feed) {
  if (!(this instanceof Swarm)) return new Swarm(feed)

  var self = this

  this.feed = feed
  this.wires = []
  this.selections = []

  this._blocks = 0
  this._critical = []
  this._requesting = bitfield(8)
  this._synchronized = 0
  this._synchronizedEmitted = 0

  feed.on('have', function (block) {
    for (var i = 0; i < self.wires.length; i++) {
      var wire = self.wires[i]
      if (!wire.remoteBitfield.get(block)) {
        wire.have({blocks: [block]})
      }
    }
    self._maybeSynced()
  })

  process.nextTick(function () {
    feed.open(function (err) {
      if (!err) self._maybeSynced()
    })
  })
}

Swarm.prototype._maybeSynced = function () {
  while (this.feed.bitfield.get(this._synchronized)) {
    this._synchronized++
  }

  if (this._synchronized === this.feed.blocks && this.feed.blocks) {
    if (this._synchronizedEmitted === this._synchronized) return
    this._synchronizedEmitted = this._synchronized
    this.feed.emit('synchronized')
  }
}

Swarm.prototype.select = function (from, to, priority) {
  throw new Error('Not yet implemented')
}

Swarm.prototype.deselect = function (from, to, priority) {
  throw new Error('Not yet implemented')
}

Swarm.prototype.critical = function (block, cb) {
  this._critical.push({block: block, callback: cb})
}

Swarm.prototype._checkCritical = function (block, data) {
  if (!this._critical.length) return

  var picked = null

  for (var i = 0; i < this._critical.length; i++) {
    if (this._critical[i].block === block) {
      if (!picked) picked = []
      picked.push(i)
    }
  }

  while (picked && picked.length) {
    var next = picked.pop()
    var pick = this._critical[next]
    this._critical.splice(pick, 1)
    pick.callback(null, data)
  }
}

Swarm.prototype.replicate = function (opts) {
  var self = this
  var stream = protocol(opts)

  stream.setTimeout(5000, function () {
    stream.emit('timeout')
    stream.destroy()
  })

  stream.on('channel', function (wire) {
    self.add(wire)
  })

  stream.join(this.feed)

  return stream
}

Swarm.prototype.add = function (wire) {
  var self = this
  this.wires.push(wire)
  this.onwire(wire)
  wire.once('close', function () {
    self.remove(wire)
  })
}

Swarm.prototype.remove = function (wire) {
  var i = this.wires.indexOf(wire)
  if (i > -1) this.wires.splice(i, 1)

  for (i = 0; i < wire.requests.length; i++) {
    this._requesting.set(wire.requests[i])
  }
  for (i = 0; i < this.wires.length; i++) {
    this.update(wire)
  }
}

Swarm.prototype.onwire = function (wire) {
  var self = this
  var feed = this.feed

  var synchronized = 0
  var synchronizedEmitted = 0

  wire.requests = []
  wire.remoteBitfield = bitfield(16)
  wire.remotePausing = false

  wire.on('have', function (have) {
    var i = 0
    var blocks = 0

    if (have.bitfield) {
      wire.remoteBitfield = bitfield(have.bitfield)
      for (i = Math.max(0, wire.remoteBitfield.length - 8); i < wire.remoteBitfield.length; i++) {
        if (wire.remoteBitfield.get(i)) blocks = i + 1
      }
    }

    if (have.blocks) {
      for (i = 0; i < have.blocks.length; i++) {
        wire.remoteBitfield.set(have.blocks[i], true)
        if (have.blocks[i] >= blocks) blocks = have.blocks[i] + 1
      }
    }

    if (self._blocks < blocks) {
      self._blocks = blocks
    }

    maybeSynchronized()
    self.update(wire)
  })

  wire.on('resume', function () {
    wire.remotePausing = false
    self.update(wire)
  })

  wire.on('pause', function () {
    wire.remotePausing = true
  })

  wire.on('response', function (response) {
    feed.put(response.block, response.data, response, function (err) {
      if (err) return destroy(err)

      var i = wire.requests.indexOf(response.block)
      if (i > -1) wire.requests.splice(i, 1)

      feed.emit('download', response.block, response.data, response)
      self._checkCritical(response.block, response.data)
      self.update(wire)
    })
  })

  wire.on('request', function (request) {
    // TODO: pass in remoteTree
    wire.remoteBitfield.set(request.block, true)
    feed.proof(request.block, {digest: request.digest}, function (err, proof) {
      if (err) return destroy(err)
      feed.get(request.block, function (err, data) {
        if (err) return destroy(err)

        var response = {
          block: request.block,
          data: data,
          nodes: proof.nodes,
          signature: proof.signature
        }

        wire.response(response)
        feed.emit('upload', request.block, data, response)
      })
    })
  })

  wire.have({
    bitfield: trim(feed.bitfield.buffer)
  })

  wire.resume()

  function destroy () {
    wire.close()
  }

  function maybeSynchronized () {
    while (wire.remoteBitfield.get(synchronized)) {
      synchronized++
    }

    if (synchronized === feed.blocks && feed.blocks) {
      if (synchronizedEmitted === synchronized) return
      synchronizedEmitted = synchronized
      feed.emit('remote-synchronized')
    }
  }
}

Swarm.prototype.pickBlock = function (wire) {
  var seen = 0
  var block = -1
  var i = 0

  for (i = 0; i < this._critical.length; i++) {
    var blk = this._critical[i].block
    if (!this._requesting.get(blk) && wire.remoteBitfield.get(blk)) {
      return blk
    }
  }

  for (i = 0; i < this._blocks; i++) {
    if (this.feed.bitfield.get(i) || !wire.remoteBitfield.get(i) || this._requesting.get(i)) continue
    if (Math.random() < (1 / ++seen)) block = i
  }

  return block
}

Swarm.prototype.update = function (wire) {
  if (wire.remotePausing) return

  var block = -1

  while (wire.requests.length < 16 && (block = this.pickBlock(wire)) > -1) {
    this._requesting.set(block, true)
    wire.requests.push(block)
    wire.request({
      block: block,
      digest: this.feed.digest(block)
    })
  }
}

function trim (buf) {
  var length = buf.length
  while (length && !buf[length - 1]) length--
  if (length !== buf.length) return buf.slice(0, length)
  return buf
}
<|endoftext|>