<commit_before>/*
 * repository.js: Base implementation of the repository pattern.
 *
 * (C) 2010, Nodejitsu Inc.
 *
 */

var fs = require('fs'),
    path = require('path'),
    haibu = require('../../haibu');

//
// ### function Repository (app, options)
// #### @app {Object} The application to create the repository for
// #### @options {Object} Options to pass along to the repository
// Constructor function for the Repository base object. This contains
// the core implementations used by inherited prototypes in the
// `repositories` module.
//
var Repository = exports.Repository = function (app, options) {
  options = options || {};

  //
  // checks all properties of this.app recursively through
  // all child classes of Repository
  //
  this.validate([], app);

  this.app = app;
  this.app._id = app.name.replace(' ', '-');
  this.appsDir = options.appsDir || haibu.config.get('directories:apps');
};

//
// ### function validate ()
// #### @keys {Array||String} The keys to check in app. (i.e. 'scripts.start')
// #### @app {Object} (optional) The app object to check. if not given this.app will be used.
// #### @return {Error|| undefined} undefined if valid, Error if not
// Checks Application configuration attributes used by this repository type
//
Repository.prototype.validate = function (keys, app) {
  var i, i2, required, props, check;

  // Check for the basic required properties needed for haibu repositories + requested ones
  keys = keys || [];
  required = ['name', 'user', 'repository.type', 'scripts.start'].concat(keys);

  for (i = 0; i < required.length; i++) {
    // split property if needed and run over each part
    props = required[i].split('.');
    check = app || this.app;

    for (i2 = 0; i2 < props.length; i2++) {
      if (typeof(check[props[i2]]) == 'undefined') {
        var message = ['Property', required[i], 'is required.'].join(' ');
        haibu.emit('repo:validate', 'warn',  message, check);
        var err = new Error(message);
        err.blame = {
          type: 'user',
          message: 'Missing properties in repository configuration'
        }
        throw err;
        return;
      }

      check = check[props[i2]];
    }
  }
  // all ok!
  return;
}

//
// ### function installDependencies (callback)
// #### @callback {function} Continuation to pass control back to when complete.
// Installs the required npm dependencies for the app associated
// with this repository instance on this system.
//
Repository.prototype.installDependencies = function (callback) {
  var self = this;

  fs.readFile(path.join(this.homeDir, 'package.json'), function (err, data) {
    if (!err) {
      try {
        pkg = JSON.parse(data);
        pkg.dependencies = pkg.dependencies || {};
        self.app.dependencies = haibu.common.mixin({}, pkg.dependencies, self.app.dependencies || {});
      }
      catch (err) {
        //
        // Ignore errors
        //
      }
    }

    haibu.common.npm.install(self.homeDir, self.app, callback);
  });
};

//
// ### function stat (callback)
// #### @callback {function} Continuation to pass control back to when complete.
// Check if there is only one directory in the appDir path
//
Repository.prototype.stat = function (callback) {
  var self = this;

  //
  // Assume that there is ONLY one directory allowed in each application directory.
  // ex:
  //   /local
  //   /local/:username
  //   /local/:username/:appname
  //   /local/:username/:appname/:home
  //
  // By making this assumption we can infer the home directory immediately at run-time for
  // all possible package.json repository types and remotes.
  //
  fs.readdir(this.appDir, function (err, files) {
    if (err) {
      // Since we couldn't find the directory we need to pass it to addDir
      haibu.emit('repo:dir:notfound', 'warn', {
        app: self.app.name,
        dir: self.appDir,
        user: self.app.user
      });

      err.blame = {
        type: 'system',
        message: 'Cannot find application directories'
      }
      callback(err, false);
    }
    else if (files.length === 0) {
      haibu.emit('repo:dir:empty', 'warn', {
        app: self.app.name,
        user: self.app.user,
        dir: self.appDir
      });

      var err = new Error('Application directory is empty');
      err.blame = {
        type: 'user',
        message: 'Repository local directory empty'
      }
      callback(err);
    }
    else {
      // Now that we know the home directory, set it on the app managed by this repository
      var sourceDir = path.join(self.appDir, files[0]);
      self._setHome(files[0]);

      haibu.emit('repo:dir:exists', 'info', {
        app: self.app.name,
        user: self.app.user,
        dir: sourceDir
      });

      callback(null, true);
    }
  });
};

//
// ### function mkdir (callback)
// #### @callback {function} Continuation to pass control back to when complete.
// Creates directories for the applications and userspaces for the app
// associated with this repository instance.
//
Repository.prototype.mkdir = function (callback) {
  var self = this;
  haibu.emit('repo:dir:user:create', 'info', {
    app: self.app.name,
    user: self.app.user,
    dir: this.userDir
  });

  // check if the user's folder already exists
  fs.stat(self.userDir, function (userErr, stats) {
    function createAppDir() {
      haibu.emit('repo:dir:app:create', 'info', {
        app: self.app.name,
        user: self.app.user,
        dir: self.appDir
      });

      // check if the application's folder already exists
      fs.stat(self.appDir, function (droneErr, stats) {
        if (droneErr) { // folder doesn't exist
          return fs.mkdir(self.appDir, 0755, function (mkAppErr) {
            if (mkAppErr) {
              haibu.emit('repo:dir:app:create', 'error', {
                app: self.app.name,
                user: self.app.user,
                dir: self.appDir
              });

              return callback(mkAppErr, false);
            }

            // If we created the directory successfully callback with true
            haibu.emit('repo:dir:app:success', 'info', {
              app: self.app.name,
              user: self.app.user,
              dir: self.appDir
            });

            callback(null, true);
          });
        }

        callback(null, true);
      });
    }

    if (userErr) {
      return fs.mkdir(self.userDir, 0755, function (mkUserErr) {
        if (mkUserErr) {
          haibu.emit('repo:dir:user:create', 'error', {
            app: self.app.name,
            user: self.app.user,
            dir: self.userDir
          });

          return callback(mkUserErr, false);
        }

        haibu.emit('repo:dir:user:success', 'info', {
          app: self.app.name,
          user: self.app.user,
          dir: self.userDir
        });

        createAppDir();
      });
    }

    createAppDir();
  });
};

//
// ### function bootstrap (callback)
// #### @callback {function} Continuation to pass control back to when complete.
// Bootstraps the files on the local system for this instance.
// If the files already exist, it simply responds.
//
Repository.prototype.bootstrap = function (callback) {
  var self = this;
  this.stat(function (err, exists) {
    if (err) {
      return self.mkdir(function (err, created) {
        callback(null, false);
      });
    }

    // If it already exists assume mkdir and init have also been called
    callback(null, true);
  });
};

//
// ### @userDir {string}
// Location on disk of all the apps for the user who this
// repository instance manages
//
Repository.prototype.__defineGetter__('userDir', function () {
  return path.join(this.appsDir, this.app.user);
});

//
// ### @appDir {string}
// Location on disk of the app for the user who this
// repository instance manages.
//
Repository.prototype.__defineGetter__('appDir', function () {
  return path.join(this.userDir, this.app.name);
});

//
// ### @homeDir {string}
// Location on disk of the home directory for the app
// that this repository instance manages.
//
Repository.prototype.__defineGetter__('homeDir', function () {
  if (!this.app.directories || typeof this.app.directories.home == 'undefined') {
    return null;
  }

  return path.join(this.appDir, this.app.directories.home);
});

//
// ### @startScript {string}
// Location on disk of all the start script for the app
// that this repository instance manages
//
Repository.prototype.__defineGetter__('startScript', function () {
  if (!this.homeDir) {
    return null;
  }
  var allowedExecutables = haibu.config.get('allowedExecutables'),
    script = this.app.scripts.start,
    executable = script.split(/\s+/, 1)[0],
    replaced = false;
  if (allowedExecutables) {
    for(var i = 0; i < allowedExecutables.length; i++) {
      if (executable === allowedExecutables[i]) {
        this.executable = executable;
        script = script.substr(executable.length).trim();
        break;
      }
    }
  }
  return path.join(this.homeDir, script);
});

//
// ### @private function _setHome (path)
// @path {string} The home directory to set for this instance
// Sets the home directory for this instance to the specified path.
//
Repository.prototype._setHome = function (path) {
  this.app.directories = this.app.directories || {};
  this.app.directories.home = path;
};

<commit_msg>[fix] Set the home directory to the first non-dotfile directory in `this.appDir`<commit_after>/*
 * repository.js: Base implementation of the repository pattern.
 *
 * (C) 2010, Nodejitsu Inc.
 *
 */

var fs = require('fs'),
    path = require('path'),
    haibu = require('../../haibu');

//
// ### function Repository (app, options)
// #### @app {Object} The application to create the repository for
// #### @options {Object} Options to pass along to the repository
// Constructor function for the Repository base object. This contains
// the core implementations used by inherited prototypes in the
// `repositories` module.
//
var Repository = exports.Repository = function (app, options) {
  options = options || {};

  //
  // checks all properties of this.app recursively through
  // all child classes of Repository
  //
  this.validate([], app);

  this.app = app;
  this.app._id = app.name.replace(' ', '-');
  this.appsDir = options.appsDir || haibu.config.get('directories:apps');
};

//
// ### function validate ()
// #### @keys {Array||String} The keys to check in app. (i.e. 'scripts.start')
// #### @app {Object} (optional) The app object to check. if not given this.app will be used.
// #### @return {Error|| undefined} undefined if valid, Error if not
// Checks Application configuration attributes used by this repository type
//
Repository.prototype.validate = function (keys, app) {
  var i, i2, required, props, check;

  // Check for the basic required properties needed for haibu repositories + requested ones
  keys = keys || [];
  required = ['name', 'user', 'repository.type', 'scripts.start'].concat(keys);

  for (i = 0; i < required.length; i++) {
    // split property if needed and run over each part
    props = required[i].split('.');
    check = app || this.app;

    for (i2 = 0; i2 < props.length; i2++) {
      if (typeof(check[props[i2]]) == 'undefined') {
        var message = ['Property', required[i], 'is required.'].join(' ');
        haibu.emit('repo:validate', 'warn',  message, check);
        var err = new Error(message);
        err.blame = {
          type: 'user',
          message: 'Missing properties in repository configuration'
        }
        throw err;
        return;
      }

      check = check[props[i2]];
    }
  }
  // all ok!
  return;
}

//
// ### function installDependencies (callback)
// #### @callback {function} Continuation to pass control back to when complete.
// Installs the required npm dependencies for the app associated
// with this repository instance on this system.
//
Repository.prototype.installDependencies = function (callback) {
  var self = this;

  fs.readFile(path.join(this.homeDir, 'package.json'), function (err, data) {
    if (!err) {
      try {
        pkg = JSON.parse(data);
        pkg.dependencies = pkg.dependencies || {};
        self.app.dependencies = haibu.common.mixin({}, pkg.dependencies, self.app.dependencies || {});
      }
      catch (err) {
        //
        // Ignore errors
        //
      }
    }

    haibu.common.npm.install(self.homeDir, self.app, callback);
  });
};

//
// ### function stat (callback)
// #### @callback {function} Continuation to pass control back to when complete.
// Check if there is only one directory in the appDir path
//
Repository.prototype.stat = function (callback) {
  var self = this;

  //
  // Assume that there is ONLY one directory allowed in each application directory.
  // ex:
  //   /local
  //   /local/:username
  //   /local/:username/:appname
  //   /local/:username/:appname/:home
  //
  // By making this assumption we can infer the home directory immediately at run-time for
  // all possible package.json repository types and remotes.
  //
  fs.readdir(this.appDir, function (err, files) {
    if (err) {
      // Since we couldn't find the directory we need to pass it to addDir
      haibu.emit('repo:dir:notfound', 'warn', {
        app: self.app.name,
        dir: self.appDir,
        user: self.app.user
      });

      err.blame = {
        type: 'system',
        message: 'Cannot find application directories'
      }

      return callback(err, false);
    }
    else if (files.length === 0) {
      haibu.emit('repo:dir:empty', 'warn', {
        app: self.app.name,
        user: self.app.user,
        dir: self.appDir
      });

      err = new Error('Application directory is empty');
      err.blame = {
        type: 'user',
        message: 'Repository local directory empty'
      }

      return callback(err);
    }

    // Now that we know the home directory, set it on the app managed by this repository
    var sourceDir = path.join(self.appDir, files[0]),
        firstNonDot = files.filter(function (f) { return f[0] !== '.' });

    self._setHome(firstNonDot);

    haibu.emit('repo:dir:exists', 'info', {
      app: self.app.name,
      user: self.app.user,
      dir: sourceDir
    });

    callback(null, true);
  });
};

//
// ### function mkdir (callback)
// #### @callback {function} Continuation to pass control back to when complete.
// Creates directories for the applications and userspaces for the app
// associated with this repository instance.
//
Repository.prototype.mkdir = function (callback) {
  var self = this;
  haibu.emit('repo:dir:user:create', 'info', {
    app: self.app.name,
    user: self.app.user,
    dir: this.userDir
  });

  // check if the user's folder already exists
  fs.stat(self.userDir, function (userErr, stats) {
    function createAppDir() {
      haibu.emit('repo:dir:app:create', 'info', {
        app: self.app.name,
        user: self.app.user,
        dir: self.appDir
      });

      // check if the application's folder already exists
      fs.stat(self.appDir, function (droneErr, stats) {
        if (droneErr) { // folder doesn't exist
          return fs.mkdir(self.appDir, 0755, function (mkAppErr) {
            if (mkAppErr) {
              haibu.emit('repo:dir:app:create', 'error', {
                app: self.app.name,
                user: self.app.user,
                dir: self.appDir
              });

              return callback(mkAppErr, false);
            }

            // If we created the directory successfully callback with true
            haibu.emit('repo:dir:app:success', 'info', {
              app: self.app.name,
              user: self.app.user,
              dir: self.appDir
            });

            callback(null, true);
          });
        }

        callback(null, true);
      });
    }

    if (userErr) {
      return fs.mkdir(self.userDir, 0755, function (mkUserErr) {
        if (mkUserErr) {
          haibu.emit('repo:dir:user:create', 'error', {
            app: self.app.name,
            user: self.app.user,
            dir: self.userDir
          });

          return callback(mkUserErr, false);
        }

        haibu.emit('repo:dir:user:success', 'info', {
          app: self.app.name,
          user: self.app.user,
          dir: self.userDir
        });

        createAppDir();
      });
    }

    createAppDir();
  });
};

//
// ### function bootstrap (callback)
// #### @callback {function} Continuation to pass control back to when complete.
// Bootstraps the files on the local system for this instance.
// If the files already exist, it simply responds.
//
Repository.prototype.bootstrap = function (callback) {
  var self = this;
  this.stat(function (err, exists) {
    if (err) {
      return self.mkdir(function (err, created) {
        callback(null, false);
      });
    }

    // If it already exists assume mkdir and init have also been called
    callback(null, true);
  });
};

//
// ### @userDir {string}
// Location on disk of all the apps for the user who this
// repository instance manages
//
Repository.prototype.__defineGetter__('userDir', function () {
  return path.join(this.appsDir, this.app.user);
});

//
// ### @appDir {string}
// Location on disk of the app for the user who this
// repository instance manages.
//
Repository.prototype.__defineGetter__('appDir', function () {
  return path.join(this.userDir, this.app.name);
});

//
// ### @homeDir {string}
// Location on disk of the home directory for the app
// that this repository instance manages.
//
Repository.prototype.__defineGetter__('homeDir', function () {
  if (!this.app.directories || typeof this.app.directories.home == 'undefined') {
    return null;
  }

  return path.join(this.appDir, this.app.directories.home);
});

//
// ### @startScript {string}
// Location on disk of all the start script for the app
// that this repository instance manages
//
Repository.prototype.__defineGetter__('startScript', function () {
  if (!this.homeDir) {
    return null;
  }
  var allowedExecutables = haibu.config.get('allowedExecutables'),
    script = this.app.scripts.start,
    executable = script.split(/\s+/, 1)[0],
    replaced = false;
  if (allowedExecutables) {
    for(var i = 0; i < allowedExecutables.length; i++) {
      if (executable === allowedExecutables[i]) {
        this.executable = executable;
        script = script.substr(executable.length).trim();
        break;
      }
    }
  }
  return path.join(this.homeDir, script);
});

//
// ### @private function _setHome (path)
// @path {string} The home directory to set for this instance
// Sets the home directory for this instance to the specified path.
//
Repository.prototype._setHome = function (path) {
  this.app.directories = this.app.directories || {};
  this.app.directories.home = path;
};

<|endoftext|>


<commit_before>var Command = require('../../').Command;
var Logger = require('../../lib/logger');

var basename = require('path').basename;
var util = require('../../lib/util');
var exec = require('child_process').exec;
var fs = require('fs');

var walk = util.walk;

var NewCommand = module.exports = Command.extend({
	desc: 'Create new application',

	run: function (name) {
		this.name = name;

		var path = this.path = process.cwd() + '/' + name;

		try {
			var stat = fs.statSync(path);
		} catch (err) {
			// directory does not exist, good
		}

		if (stat) {
			var isEmpty = !fs.readdirSync(path).length;

			if (!isEmpty) {
				throw new Error('Directory \'' + name + '\' already exists and it is not empty.')
			}
		}

		if (!stat) {
			this.mkdir(path);
		}

		this.mkdir(path + '/bin');
		this.mkdir(path + '/lib');
		this.mkdir(path + '/commands');
		this.mkdir(path + '/test');

		var files = walk(__dirname + '/../skeleton/program');
		var index = 0;
		var file;

		while (file = files[index++]) {
			this.copy(file);
		}

		var self = this;

		this.invoke('npm install', function () {
			self.invoke('npm link');
		});
	},

	mkdir: function (path) {
		Logger.log('create', basename(path) + '/');
		fs.mkdirSync(path);
	},

	copy: function (srcPath) {
		var skeletonPath = __dirname + '/../skeleton/program';
		var filename = srcPath.replace(skeletonPath, '').replace('$name', this.name);
		var file = fs.readFileSync(srcPath, 'utf-8').replace(/\$name/g, this.name);

		Logger.log('create', filename.slice(1));
		fs.writeFileSync(this.path + filename, file, 'utf-8');
	},

	invoke: function (command, callback) {
		Logger.log('invoke', command);

		exec([
			'cd ' + this.path,
			command,
			'cd -'
		].join(' && '), callback);
	}
});<commit_msg>creating middleware folder when generating new project<commit_after>var Command = require('../../').Command;
var Logger = require('../../lib/logger');

var basename = require('path').basename;
var util = require('../../lib/util');
var exec = require('child_process').exec;
var fs = require('fs');

var walk = util.walk;

var NewCommand = module.exports = Command.extend({
	desc: 'Create new application',

	run: function (name) {
		this.name = name;

		var path = this.path = process.cwd() + '/' + name;

		try {
			var stat = fs.statSync(path);
		} catch (err) {
			// directory does not exist, good
		}

		if (stat) {
			var isEmpty = !fs.readdirSync(path).length;

			if (!isEmpty) {
				throw new Error('Directory \'' + name + '\' already exists and it is not empty.')
			}
		}

		if (!stat) {
			this.mkdir(path);
		}

		this.mkdir(path + '/bin');
		this.mkdir(path + '/lib');
		this.mkdir(path + '/middleware');
		this.mkdir(path + '/commands');
		this.mkdir(path + '/test');

		var files = walk(__dirname + '/../skeleton/program');
		var index = 0;
		var file;

		while (file = files[index++]) {
			this.copy(file);
		}

		var self = this;

		this.invoke('npm install', function () {
			self.invoke('npm link');
		});
	},

	mkdir: function (path) {
		Logger.log('create', basename(path) + '/');
		fs.mkdirSync(path);
	},

	copy: function (srcPath) {
		var skeletonPath = __dirname + '/../skeleton/program';
		var filename = srcPath.replace(skeletonPath, '').replace('$name', this.name);
		var file = fs.readFileSync(srcPath, 'utf-8').replace(/\$name/g, this.name);

		Logger.log('create', filename.slice(1));
		fs.writeFileSync(this.path + filename, file, 'utf-8');
	},

	invoke: function (command, callback) {
		Logger.log('invoke', command);

		exec([
			'cd ' + this.path,
			command,
			'cd -'
		].join(' && '), callback);
	}
});<|endoftext|>


<commit_before>exports.executeJsRunner = function(specCollection, done, jasmineEnv, setupFile) {
  var specs,
      specLoader = require('./requirejs-spec-loader'),
      requirejs = require('requirejs'),
      vm = require('vm'),
      fs = require('fs'),
      coffeescript = require('coffee-script'),
      template = fs.readFileSync(
        setupFile || (__dirname + '/requirejs-wrapper-template.js'),
        'utf8'
      ),
      ensureUnixPath = function(path){
        return path.replace(/^(.):/, '/$1').replace(/\\/g, '/');
      },
      buildNewContext = function(spec){
        var context = {
          describe: describe,
          it: it,
          xdescribe: xdescribe,
          xit: xit,
          beforeEach: beforeEach,
          spyOn: spyOn,
          waitsFor: waitsFor,
          runs: runs,
          jasmine: jasmine,
          expect: expect,
          require: require,
          console: console,
          process: process,
          module: module,
          specLoader: specLoader,
          __dirname: spec.directory(),
          __filename: spec.path(),
          baseUrl: buildRelativeDirName(spec.directory()),
          csPath: __dirname + '/cs'
        };

        context.global = context;

        return context;
      },
      buildRelativeDirName = function(dir){
        var retVal = "",
            thisDir = ensureUnixPath(process.cwd()),
            toDir = ensureUnixPath(dir).split('/'),
            index = 0;

        thisDir = thisDir.split('/');

        for(; index < thisDir.length || index < toDir.length; index++) {
          if(thisDir[index] != toDir[index]){
            for(var i = index; i < thisDir.length-1; i++){
              retVal += '../';
            }

            for(var i = index; i < toDir.length; i++){
              retVal += toDir[i] + '/';
            }

            break;
          }
        }

        return retVal.trim('/');
      };

  specCollection.getSpecs().forEach(function(s){
    var script = fs.readFileSync(s.path(), 'utf8'),
        wrappedScript;

    if (s.filename().substr(-6).toLowerCase() == 'coffee') {
      script = coffeescript.compile(script);
    }

    wrappedScript = template + script;

    var newContext = buildNewContext(s);
    newContext.setTimeout = jasmine.getGlobal().setTimeout;
    newContext.setInterval = jasmine.getGlobal().setInterval;

    vm.runInNewContext(wrappedScript, newContext, s.path());
  });

  specLoader.executeWhenAllSpecsAreComplete(jasmineEnv);
};
<commit_msg>Added afterEach and waits to the requirejs context<commit_after>exports.executeJsRunner = function(specCollection, done, jasmineEnv, setupFile) {
  var specs,
      specLoader = require('./requirejs-spec-loader'),
      requirejs = require('requirejs'),
      vm = require('vm'),
      fs = require('fs'),
      coffeescript = require('coffee-script'),
      template = fs.readFileSync(
        setupFile || (__dirname + '/requirejs-wrapper-template.js'),
        'utf8'
      ),
      ensureUnixPath = function(path){
        return path.replace(/^(.):/, '/$1').replace(/\\/g, '/');
      },
      buildNewContext = function(spec){
        var context = {
          describe: describe,
          it: it,
          xdescribe: xdescribe,
          xit: xit,
          beforeEach: beforeEach,
          afterEach: afterEach,
          spyOn: spyOn,
          waitsFor: waitsFor,
          waits: waits,
          runs: runs,
          jasmine: jasmine,
          expect: expect,
          require: require,
          console: console,
          process: process,
          module: module,
          specLoader: specLoader,
          __dirname: spec.directory(),
          __filename: spec.path(),
          baseUrl: buildRelativeDirName(spec.directory()),
          csPath: __dirname + '/cs'
        };

        context.global = context;

        return context;
      },
      buildRelativeDirName = function(dir){
        var retVal = "",
            thisDir = ensureUnixPath(process.cwd()),
            toDir = ensureUnixPath(dir).split('/'),
            index = 0;

        thisDir = thisDir.split('/');

        for(; index < thisDir.length || index < toDir.length; index++) {
          if(thisDir[index] != toDir[index]){
            for(var i = index; i < thisDir.length-1; i++){
              retVal += '../';
            }

            for(var i = index; i < toDir.length; i++){
              retVal += toDir[i] + '/';
            }

            break;
          }
        }

        return retVal.trim('/');
      };

  specCollection.getSpecs().forEach(function(s){
    var script = fs.readFileSync(s.path(), 'utf8'),
        wrappedScript;

    if (s.filename().substr(-6).toLowerCase() == 'coffee') {
      script = coffeescript.compile(script);
    }

    wrappedScript = template + script;

    var newContext = buildNewContext(s);
    newContext.setTimeout = jasmine.getGlobal().setTimeout;
    newContext.setInterval = jasmine.getGlobal().setInterval;

    vm.runInNewContext(wrappedScript, newContext, s.path());
  });

  specLoader.executeWhenAllSpecsAreComplete(jasmineEnv);
};
<|endoftext|>


<commit_before>var exports = {};

exports.simplePrint = function(video) {
  return `**${video.title}**`;
};

exports.prettyPrint = function(video) {
  viewCount = video.view_count.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
  return `**${video.title}** by **${video.author}** *(${viewCount} views)*`;
};

exports.prettyPrintWithUser = function(video) {
  return exports.prettyPrint(video) + `, added by <@${video.userId}>`;
};

exports.simplify = function(video) {
  return {
    vid: video.vid,
    title: video.title,
    author: video.author,
    view_count: video.view_count,
  };
};

exports.prettyTime = function(ms) {
  var seconds = ms / 1000;
  var actualSeconds = Math.ceil(seconds % 60);
  var minutes = Math.round((seconds - actualSeconds) / 60);
  return `${minutes}:${actualSeconds}`;
};

module.exports = exports;
<commit_msg>Fix a formatting issue when formatting time<commit_after>var exports = {};

exports.simplePrint = function(video) {
  return `**${video.title}**`;
};

exports.prettyPrint = function(video) {
  viewCount = video.view_count.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
  return `**${video.title}** by **${video.author}** *(${viewCount} views)*`;
};

exports.prettyPrintWithUser = function(video) {
  return exports.prettyPrint(video) + `, added by <@${video.userId}>`;
};

exports.simplify = function(video) {
  return {
    vid: video.vid,
    title: video.title,
    author: video.author,
    view_count: video.view_count,
  };
};

exports.prettyTime = function(ms) {
  var seconds = ms / 1000;
  var actualSeconds = Math.ceil(seconds % 60);
  var paddedSeconds = String('00' + actualSeconds).slice(-2);
  var minutes = Math.round((seconds - actualSeconds) / 60);
  return `${minutes}:${paddedSeconds}`;
};

module.exports = exports;
<|endoftext|>


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

/* eg. node bin/nearleythere.js examples/js/left.js --input "....."
   or, node bin/nearleythere.js examples/js/AycockHorspool.js --input "aa"
 */

var fs = require('fs');
var nearley = require('../lib/nearley.js');
var nomnom = require('nomnom');
var StreamWrapper = require('../lib/stream.js');

var opts = nomnom
    .script('nearley-test')
    .option('file', {
        position: 0,
        help: "A grammar .js file",
        required: true,
    })
    .option('input', {
        abbr: 'i',
        help: "An input string to parse (if not provided then read from stdin)",
    })
    .option('start', {
        abbr: 's',
        help: "An optional start symbol (if not provided then use the parser start symbol)",
    })
    .option('out', {
        abbr: 'o',
        help: "File to output to (defaults to stdout)",
    })
    .option('quiet', {
        abbr: 'q',
        flag: true,
        help: "Output parse results only (hide Earley table)",
    })
    .option('version', {
        abbr: 'v',
        flag: true,
        help: "Print version and exit",
        callback: function() {
            return require('../package.json').version;
        }
    })
    .parse();

var output = opts.out ? fs.createWriteStream(opts.out) : process.stdout;

var grammar = new require(require('path').resolve(opts.file));
var parser = new nearley.Parser(grammar.ParserRules, opts.start ? opts.start : grammar.ParserStart);

var writeTable = function (writeStream, parser) {
    writeStream.write("Table length: " + parser.table.length + "\n");
    writeStream.write("Number of parses: " + parser.results.length + "\n");
    writeStream.write("Parse Charts");
    var chartNumber = 0;
    parser.table.forEach(
        function (chart) {
            writeStream.write("\nChart: " + chartNumber++ + "\n");
            var stateNumber = 0;
            chart.forEach(
                function (state) {
                    writeStream.write(stateNumber++ + ": " + state.toString() + "\n");
                } )
        } )
    writeStream.write("\n\nParse results: \n");
}

var writeResults = function (writeStream, parser) {
    writeStream.write(require('util').inspect(parser.results, {colors: true, depth: null}));
    writeStream.write("\n");
}

if (typeof(opts.input) === "undefined") {
    process.stdin
        .pipe(new StreamWrapper(parser))
        .on('finish', function() {
            if (!opts.quiet) writeTable(output, parser);
            writeResults(output, parser);
        });
} else {
    parser.feed(opts.input);
    if (!opts.quiet) writeTable(output, parser);
    writeResults(output, parser);
}

<commit_msg>Omit ANSI escape codes in quiet mode<commit_after>#!/usr/bin/env node

/* eg. node bin/nearleythere.js examples/js/left.js --input "....."
   or, node bin/nearleythere.js examples/js/AycockHorspool.js --input "aa"
 */

var fs = require('fs');
var nearley = require('../lib/nearley.js');
var nomnom = require('nomnom');
var StreamWrapper = require('../lib/stream.js');

var opts = nomnom
    .script('nearley-test')
    .option('file', {
        position: 0,
        help: "A grammar .js file",
        required: true,
    })
    .option('input', {
        abbr: 'i',
        help: "An input string to parse (if not provided then read from stdin)",
    })
    .option('start', {
        abbr: 's',
        help: "An optional start symbol (if not provided then use the parser start symbol)",
    })
    .option('out', {
        abbr: 'o',
        help: "File to output to (defaults to stdout)",
    })
    .option('quiet', {
        abbr: 'q',
        flag: true,
        help: "Output parse results only (hide Earley table)",
    })
    .option('version', {
        abbr: 'v',
        flag: true,
        help: "Print version and exit",
        callback: function() {
            return require('../package.json').version;
        }
    })
    .parse();

var output = opts.out ? fs.createWriteStream(opts.out) : process.stdout;

var grammar = new require(require('path').resolve(opts.file));
var parser = new nearley.Parser(grammar.ParserRules, opts.start ? opts.start : grammar.ParserStart);

var writeTable = function (writeStream, parser) {
    writeStream.write("Table length: " + parser.table.length + "\n");
    writeStream.write("Number of parses: " + parser.results.length + "\n");
    writeStream.write("Parse Charts");
    var chartNumber = 0;
    parser.table.forEach(
        function (chart) {
            writeStream.write("\nChart: " + chartNumber++ + "\n");
            var stateNumber = 0;
            chart.forEach(
                function (state) {
                    writeStream.write(stateNumber++ + ": " + state.toString() + "\n");
                } )
        } )
    writeStream.write("\n\nParse results: \n");
}

var writeResults = function (writeStream, parser) {
    writeStream.write(require('util').inspect(parser.results, {colors: !opts.quiet, depth: null}));
    writeStream.write("\n");
}

if (typeof(opts.input) === "undefined") {
    process.stdin
        .pipe(new StreamWrapper(parser))
        .on('finish', function() {
            if (!opts.quiet) writeTable(output, parser);
            writeResults(output, parser);
        });
} else {
    parser.feed(opts.input);
    if (!opts.quiet) writeTable(output, parser);
    writeResults(output, parser);
}

<|endoftext|>


<commit_before>const {Subject, ReplaySubject, defer, from, zip, concat, of} = require('rxjs')
const {first, map, filter, delay, finalize, tap, mapTo, mergeMap} = require('rxjs/operators')
const {v4: uuid} = require('uuid')
const _ = require('lodash')
const log = require('sepal/log')('limiter')
const service = require('sepal/service')

const Limiter$ = ({name, rateWindowMs = 1000, maxRate, maxConcurrency}) => {
    const requestId$ = new Subject()
    const rateToken$ = new Subject()
    const concurrencyToken$ = new Subject()
    const responseToken$ = new Subject()

    const msg = msg =>
        `Limiter [${name}] ${msg}`

    const tokenId = ({requestId, rateToken, concurrencyToken}) =>
        `[${name}.${requestId.substr(-4)}${rateToken ? `.R${rateToken}` : ''}${concurrencyToken ? `.C${concurrencyToken}` : ''}]`

    const initialToken$ = count =>
        of(...(_.range(1, count + 1)))

    const rateLimit$ = maxRate
        ? concat(initialToken$(maxRate), rateToken$)
        : requestId$.pipe(mapTo())

    const concurrencyLimit$ = maxConcurrency
        ? concat(initialToken$(maxConcurrency), concurrencyToken$)
        : requestId$.pipe(mapTo())

    const token$ = zip(
        requestId$,
        rateLimit$,
        concurrencyLimit$
    ).pipe(
        map(([requestId, rateToken, concurrencyToken]) =>
            ({requestId, rateToken, concurrencyToken})
        )
    )

    const serveToken = token =>
        responseToken$.next(token)

    const recycleRateToken = token => {
        if (!token)
            throw new Error('token is required')
        if (token && maxRate) {
            log.debug(msg(`recycling rate token ${token.rateToken}/${maxRate} from token ${tokenId(token)}`))
            rateToken$.next(token.rateToken)
        }
    }

    const recycleConcurrencytoken = token => {
        if (!token)
            throw new Error('token is required')
        if (maxConcurrency) {
            log.debug(msg(`recycling concurrency token ${token.concurrencyToken}/${maxConcurrency} from token ${tokenId(token)}`))
            concurrencyToken$.next(token.concurrencyToken)
        }
    }

    token$.pipe(
        tap(token => serveToken(token)),
        delay(rateWindowMs)
    ).subscribe(
        token => recycleRateToken(token),
        error => log.fatal(msg('token stream failed:'), error),
        () => log.fatal(msg('token stream completed'))
    )

    return (requestId = uuid()) => {
        log.debug(msg(`requesting token for request [${requestId.substr(-4)}]`))
        const response$ = new ReplaySubject()
        let currentToken
        let canceled
        responseToken$.pipe(
            filter(({requestId: currentRequestId}) => currentRequestId === requestId),
            tap(token => currentToken = token),
            map(token => tokenId(token)),
            first(),
            finalize(() => {
                if (canceled) {
                    log.debug(msg('cancelling token'), currentToken)
                    recycleConcurrencytoken(currentToken)
                }
            }),
        ).subscribe(
            token => {
                log.debug(msg(`serving token ${token}`))
                response$.next(token)
            },
            error => log.fatal(msg('token stream failed:'), error)
            // stream is allowed to complete
        )

        return defer(() => {
            requestId$.next(requestId)
            return response$.pipe(
                finalize(() => {
                    if (currentToken) {
                        recycleConcurrencytoken(currentToken)
                    } else {
                        canceled = true
                    }
                }),
            )
        })
    }
}

const withLimiter$ = limiter =>
    observable$ =>
        service.submit$(limiter, uuid()).pipe(
            mergeMap(() => observable$)
        )

const Limiter = (name, options) => {
    const limiter = {
        name,
        service$: Limiter$(options)
    }
    return {
        limiter,
        limiter$: withLimiter$(limiter)
    }
}

module.exports = {Limiter, Limiter$}
<commit_msg>Cleanup.<commit_after>const {Subject, ReplaySubject, defer, zip, concat, of} = require('rxjs')
const {first, map, filter, delay, finalize, tap, mapTo, mergeMap} = require('rxjs/operators')
const {v4: uuid} = require('uuid')
const _ = require('lodash')
const log = require('sepal/log')('limiter')
const service = require('sepal/service')

const Limiter$ = ({name, rateWindowMs = 1000, maxRate, maxConcurrency}) => {
    const requestId$ = new Subject()
    const rateToken$ = new Subject()
    const concurrencyToken$ = new Subject()
    const responseToken$ = new Subject()

    const msg = msg =>
        `Limiter [${name}] ${msg}`

    const tokenId = ({requestId, rateToken, concurrencyToken}) =>
        `[${name}.${requestId.substr(-4)}${rateToken ? `.R${rateToken}` : ''}${concurrencyToken ? `.C${concurrencyToken}` : ''}]`

    const initialToken$ = count =>
        of(...(_.range(1, count + 1)))

    const rateLimit$ = maxRate
        ? concat(initialToken$(maxRate), rateToken$)
        : requestId$.pipe(mapTo())

    const concurrencyLimit$ = maxConcurrency
        ? concat(initialToken$(maxConcurrency), concurrencyToken$)
        : requestId$.pipe(mapTo())

    const token$ = zip(
        requestId$,
        rateLimit$,
        concurrencyLimit$
    ).pipe(
        map(([requestId, rateToken, concurrencyToken]) =>
            ({requestId, rateToken, concurrencyToken})
        )
    )

    const serveToken = token =>
        responseToken$.next(token)

    const recycleRateToken = token => {
        if (!token)
            throw new Error('token is required')
        if (token && maxRate) {
            log.debug(msg(`recycling rate token ${token.rateToken}/${maxRate} from token ${tokenId(token)}`))
            rateToken$.next(token.rateToken)
        }
    }

    const recycleConcurrencytoken = token => {
        if (!token)
            throw new Error('token is required')
        if (maxConcurrency) {
            log.debug(msg(`recycling concurrency token ${token.concurrencyToken}/${maxConcurrency} from token ${tokenId(token)}`))
            concurrencyToken$.next(token.concurrencyToken)
        }
    }

    token$.pipe(
        tap(token => serveToken(token)),
        delay(rateWindowMs)
    ).subscribe(
        token => recycleRateToken(token),
        error => log.fatal(msg('token stream failed:'), error),
        () => log.fatal(msg('token stream completed'))
    )

    return (requestId = uuid()) => {
        log.debug(msg(`requesting token for request [${requestId.substr(-4)}]`))
        const response$ = new ReplaySubject()
        let currentToken
        let canceled
        responseToken$.pipe(
            filter(({requestId: currentRequestId}) => currentRequestId === requestId),
            tap(token => currentToken = token),
            map(token => tokenId(token)),
            first(),
            finalize(() => {
                if (canceled) {
                    log.debug(msg('cancelling token'), currentToken)
                    recycleConcurrencytoken(currentToken)
                }
            }),
        ).subscribe(
            token => {
                log.debug(msg(`serving token ${token}`))
                response$.next(token)
            },
            error => log.fatal(msg('token stream failed:'), error)
            // stream is allowed to complete
        )

        return defer(() => {
            requestId$.next(requestId)
            return response$.pipe(
                finalize(() => {
                    if (currentToken) {
                        recycleConcurrencytoken(currentToken)
                    } else {
                        canceled = true
                    }
                }),
            )
        })
    }
}

const withLimiter$ = limiter =>
    observable$ =>
        service.submit$(limiter, uuid()).pipe(
            mergeMap(() => observable$)
        )

const Limiter = (name, options) => {
    const limiter = {
        name,
        service$: Limiter$(options)
    }
    return {
        limiter,
        limiter$: withLimiter$(limiter)
    }
}

module.exports = {Limiter, Limiter$}
<|endoftext|>


<commit_before>// Copyright 2016 Luca-SAS, licensed under the Apache License 2.0

// worker module

'use strict';

var fs = require('fs');
var zlib = require('zlib');
var url = require('url');
var stream = require('stream');

var mkdirp = require('mkdirp');
var uuid = require('uuid');
var aws = require('aws-sdk');
var azure = require('azure-storage');
var parquet = require('./stub-parquet.js');

var Dataset = require('./dataset.js');
var Task = require('./task.js');
var sizeOf = require('./rough-sizeof.js');
var Lines = require('./lines.js');
var readSplit = require('./readsplit.js').readSplit;

var workerId = process.argv[2];
var memory = process.argv[3];

var mm = new MemoryManager(memory);

var start = Date.now();

if (process.env.SKALE_RANDOM_SEED)
  Dataset.setRandomSeed(process.env.SKALE_RANDOM_SEED);

process.title = 'skale-worker-' + workerId;

process.on('disconnect', function () {
  log('disconnected, exit');
  process.exit();
});

process.on('message', function (msg) {
  if (typeof msg === 'object' && msg.req) {
    switch (msg.req.cmd) {
    case 'runTask':
      runTask(msg);
      break;
    case 'runztask':
      runztask(msg);
      break;
    }
  }
});

function runztask(msg) {
  var file = msg.req.args;
  fs.readFile(file, function (err, data) {
    fs.unlink(file, function () {});
    if (err) throw new Error(err);
    zlib.gunzip(data, {chunkSize: 65536}, function (err, data) {
      if (err) throw new Error(err);
      msg.req.args = data;
      runTask(msg);
    });
  });
}

function runTask(msg) {
  var task = parseTask(msg.req.args);
  task.workerId = workerId;
  task.grid = {host: {}};
  task.mm = mm;
  task.lib = {aws: aws, azure: azure, fs: fs, Lines: Lines, mkdirp: mkdirp, mm: mm, parquet: parquet, readSplit: readSplit, stream: stream, url: url, uuid: uuid, zlib: zlib};
  task.log = log;
  task.dlog = dlog;
  // Expose system core dependencies explicitely for user evaluated code in workers
  global.fs = fs;
  // Indirect Eval to set user dependencies bundle in the worker global context
  (0, eval)(task.bundle);
  task.run(function (result) {
    delete msg.req.args;
    msg.result = result;
    msg.result.workerId = workerId;
    process.send(msg);
    if (global.gc) {
      setImmediate(function () {
        var gcs = Date.now();
        global.gc();
        dlog(gcs, 'gc');
      });
    }
    else log('no global.gc');
  });
}

function parseTask(str) {
  var i, j, n, ref;
  var task = JSON.parse(str, function (key, value) {
    if (typeof value === 'string') {
      // String value can be a regular function or an ES6 arrow function
      if (value.substring(0, 8) == 'function') {
        var args = value.match(/\(([^)]*)/)[1];
        var body = value.replace(/^function\s*[^)]*\)\s*{/, '').replace(/}$/, '');
        value = new Function(args, body);
      } else if (value.match(/^\s*\(\s*[^(][^)]*\)\s*=>/) || value.match(/^\s*\w+\s*=>/))
        value = ('indirect', eval)(value);
    }
    return value;
  });

  for (i in task.nodes) {
    n = task.nodes[i];
    for (j in n.dependencies) {
      ref = n.dependencies[j];
      n.dependencies[j] = task.nodes[ref];
    }
    for (j in n.partitions) {
      Object.setPrototypeOf(task.nodes[i].partitions[j], Dataset.Partition.prototype);
      task.nodes[i].partitions[j].count = 0;
      task.nodes[i].partitions[j].bsize = 0;
      task.nodes[i].partitions[j].tsize = 0;
      task.nodes[i].partitions[j].skip = false;
    }
    if (n.type) {
      Object.setPrototypeOf(task.nodes[i], Dataset[n.type].prototype);
    }
    if (n.partitioner && n.partitioner.type) {
      Object.setPrototypeOf(n.partitioner, Dataset[n.partitioner.type].prototype);
    }
  }
  Object.setPrototypeOf(task, Task.prototype);
  //log('task:', JSON.stringify(task, null, 2));
  return task;
}

function MemoryManager(memory) {
  memory = memory || 1024;
  var Kb = 1024, Mb = 1024 * Kb;
  var MAX_MEMORY = (memory - 100) * Mb;
  var maxStorageMemory = MAX_MEMORY * 0.4;
  var maxShuffleMemory = MAX_MEMORY * 0.2;
  var maxCollectMemory = MAX_MEMORY * 0.2;

  this.storageMemory = 0;
  this.shuffleMemory = 0;
  this.collectMemory = 0;
  this.sizeOf = sizeOf;

  this.storageFull = function () {return (this.storageMemory > maxStorageMemory);};
  this.shuffleFull = function () {return (this.shuffleMemory > maxShuffleMemory);};
  this.collectFull = function () {return (this.collectMemory > maxCollectMemory);};

  this.partitions = {};
  this.register = function (partition) {
    var key = partition.datasetId + '.' + partition.partitionIndex;
    if (!(key in this.partitions)) this.partitions[key] = partition;
  };

  this.unregister = function (partition) {
    this.partitions[partition.datasetId + '.' + partition.partitionIndex] = undefined;
  };

  this.isAvailable = function (partition) {
    return (this.partitions[partition.datasetId + '.' + partition.partitionIndex] !== undefined);
  };
}

if (process.env.SKALE_DEBUG > 1) {
  var log =  function() {
    var args = Array.prototype.slice.call(arguments);
    args.unshift('[worker-' +  process.argv[2] + ' ' + (Date.now() - start) / 1000 + 's]');
    console.error.apply(null, args);
  };
  var dlog = function() {
    var args = Array.prototype.slice.call(arguments);
    var now = Date.now();
    var lstart = args.shift();
    args.unshift('[worker-' +  process.argv[2] + ' ' + (now - start) / 1000 + 's]');
    args.push('in ' + (now - lstart) / 1000 + 's');
    console.error.apply(null, args);
  };
} else {
  dlog = log = function () {};
}
<commit_msg>modernize javascript syntax as per #210<commit_after>// Copyright 2016 Luca-SAS, licensed under the Apache License 2.0

// worker module

'use strict';

const fs = require('fs');
const zlib = require('zlib');
const url = require('url');
const stream = require('stream');

const mkdirp = require('mkdirp');
const uuid = require('uuid');
const aws = require('aws-sdk');
const azure = require('azure-storage');
const parquet = require('./stub-parquet.js');

const Dataset = require('./dataset.js');
const Task = require('./task.js');
const sizeOf = require('./rough-sizeof.js');
const Lines = require('./lines.js');
const readSplit = require('./readsplit.js').readSplit;

const workerId = process.argv[2];
let memory = process.argv[3];

const mm = new MemoryManager(memory);

var start = Date.now();

if (process.env.SKALE_RANDOM_SEED)
  Dataset.setRandomSeed(process.env.SKALE_RANDOM_SEED);

process.title = 'skale-worker-' + workerId;

process.on('disconnect', function () {
  log('disconnected, exit');
  process.exit();
});

process.on('message', function (msg) {
  if (typeof msg === 'object' && msg.req) {
    switch (msg.req.cmd) {
    case 'runTask':
      runTask(msg);
      break;
    case 'runztask':
      runztask(msg);
      break;
    }
  }
});

function runztask(msg) {
  var file = msg.req.args;
  fs.readFile(file, function (err, data) {
    fs.unlink(file, function () {});
    if (err) throw new Error(err);
    zlib.gunzip(data, {chunkSize: 65536}, function (err, data) {
      if (err) throw new Error(err);
      msg.req.args = data;
      runTask(msg);
    });
  });
}

function runTask(msg) {
  var task = parseTask(msg.req.args);
  task.workerId = workerId;
  task.grid = {host: {}};
  task.mm = mm;
  task.lib = {aws: aws, azure: azure, fs: fs, Lines: Lines, mkdirp: mkdirp, mm: mm, parquet: parquet, readSplit: readSplit, stream: stream, url: url, uuid: uuid, zlib: zlib};
  task.log = log;
  task.dlog = dlog;
  // Expose system core dependencies explicitely for user evaluated code in workers
  global.fs = fs;
  // Indirect Eval to set user dependencies bundle in the worker global context
  (0, eval)(task.bundle);
  task.run(function (result) {
    delete msg.req.args;
    msg.result = result;
    msg.result.workerId = workerId;
    process.send(msg);
    if (global.gc) {
      setImmediate(function () {
        var gcs = Date.now();
        global.gc();
        dlog(gcs, 'gc');
      });
    }
    else log('no global.gc');
  });
}

function parseTask(str) {
  var i, j, n, ref;
  var task = JSON.parse(str, function (key, value) {
    if (typeof value === 'string') {
      // String value can be a regular function or an ES6 arrow function
      if (value.substring(0, 8) == 'function') {
        var args = value.match(/\(([^)]*)/)[1];
        var body = value.replace(/^function\s*[^)]*\)\s*{/, '').replace(/}$/, '');
        value = new Function(args, body);
      } else if (value.match(/^\s*\(\s*[^(][^)]*\)\s*=>/) || value.match(/^\s*\w+\s*=>/))
        value = ('indirect', eval)(value);
    }
    return value;
  });

  for (i in task.nodes) {
    n = task.nodes[i];
    for (j in n.dependencies) {
      ref = n.dependencies[j];
      n.dependencies[j] = task.nodes[ref];
    }
    for (j in n.partitions) {
      Object.setPrototypeOf(task.nodes[i].partitions[j], Dataset.Partition.prototype);
      task.nodes[i].partitions[j].count = 0;
      task.nodes[i].partitions[j].bsize = 0;
      task.nodes[i].partitions[j].tsize = 0;
      task.nodes[i].partitions[j].skip = false;
    }
    if (n.type) {
      Object.setPrototypeOf(task.nodes[i], Dataset[n.type].prototype);
    }
    if (n.partitioner && n.partitioner.type) {
      Object.setPrototypeOf(n.partitioner, Dataset[n.partitioner.type].prototype);
    }
  }
  Object.setPrototypeOf(task, Task.prototype);
  //log('task:', JSON.stringify(task, null, 2));
  return task;
}

function MemoryManager(memory = 1024) {
  const Mb = 1024 * 1024;
  const MAX_MEMORY = (memory - 100) * Mb;
  const maxStorageMemory = MAX_MEMORY * 0.4;
  const maxShuffleMemory = MAX_MEMORY * 0.2;
  const maxCollectMemory = MAX_MEMORY * 0.2;

  this.storageMemory = 0;
  this.shuffleMemory = 0;
  this.collectMemory = 0;
  this.sizeOf = sizeOf;

  this.storageFull = function () {return (this.storageMemory > maxStorageMemory);};
  this.shuffleFull = function () {return (this.shuffleMemory > maxShuffleMemory);};
  this.collectFull = function () {return (this.collectMemory > maxCollectMemory);};

  this.partitions = {};
  this.register = function (partition) {
    const key = partition.datasetId + '.' + partition.partitionIndex;
    if (!(key in this.partitions)) this.partitions[key] = partition;
  };

  this.unregister = function (partition) {
    this.partitions[partition.datasetId + '.' + partition.partitionIndex] = undefined;
  };

  this.isAvailable = function (partition) {
    return (this.partitions[partition.datasetId + '.' + partition.partitionIndex] !== undefined);
  };
}

let log;
let dlog;
if (process.env.SKALE_DEBUG > 1) {
  log = function log() {
    const args = Array.prototype.slice.call(arguments);
    args.unshift('[worker-' +  process.argv[2] + ' ' + (Date.now() - start) / 1000 + 's]');
    console.error.apply(null, args);
  };
  dlog = function dlog() {
    const args = Array.prototype.slice.call(arguments);
    const now = Date.now();
    const lstart = args.shift();
    args.unshift('[worker-' +  process.argv[2] + ' ' + (now - start) / 1000 + 's]');
    args.push('in ' + (now - lstart) / 1000 + 's');
    console.error.apply(null, args);
  };
} else {
  dlog = log = function noop() {};
}
<|endoftext|>


<commit_before>// @flow
import React from 'react';
import { paper, Point, Layer } from 'paper';

import type { SeatsMap } from '../reducers/types';
import type { SeatData } from '../types';

import { toSeatData } from '../conversions';
import Seat from './Seat';

type Props = {
  seats: SeatsMap,
  zoom: number,

  // JS api callback
  onSelectSeat: (seat: SeatData) => void,
  seatColor: (seat: SeatData) => string,

  showTooltip: (text: string ) => void,
  hideTooltip: () => void,
}

export default class Floorplan extends React.Component {

  props: Props;
  view: paper.view;
  rasterLayer: Layer;
  seatsLayer: Layer;

  shouldRaster: bool;
  seats: Seat[];
  update: () => void;

  constructor(props: Props) {
    super(props);
    this.update = this.update.bind(this);
  }

  componentDidMount() {
    const canvas = this.refs.canvas;

    // Setup canvas
    canvas.width = 960;
    canvas.height = 540;

    // Configure the floorplan
    paper.setup(canvas);
    this.view = paper.view;
    this.view.autoUpdate = false;
    this.view.zoom = this.props.zoom;
    this.view.onMouseDrag = (e) => this.onMouseDrag(e);
    this.view.onMouseUp = (e) => this.onMouseUp(e);

    this.seatsLayer = paper.project.activeLayer;
    this.rasterLayer = paper.project.addLayer(new Layer());

    // HARDCODED: center the view for our mocked seats
    this.view.center = new Point(1175, 371);

    // Create and render the seats based on the loaded floorplan.
    this.seats = [];
    for (const id in this.props.seats) {
      const seatstate = this.props.seats[id];
      const seat = new Seat(id, seatstate.x, seatstate.y);

      // events binding
      seat.onMouseEnter = () => this.props.showTooltip(seatstate.label);
      seat.onMouseLeave = () => this.props.hideTooltip();
      seat.onSelect = () => this.props.onSelectSeat(toSeatData(seatstate));

      this.seats.push(seat);
    }

    this.update();
  }

  /**
   * Called when the stores the floorplan is subscribed to is updated.
   */
  componentDidUpdate() {
    this.view.zoom = this.props.zoom;
    this.update();
  }

  /**
   * Called when the user drags the floorplan around.
   */
  onMouseDrag(e: Object) {
    const delta = e.delta;

    // reduce drag length
    delta.length /= 2;

    // inverts the scroll from the drag direction
    this.view.center = this.view.center.add(new Point(-delta.x, -delta.y));

    // When dragging the floorplan, raster the viewport for insane performances.
    this.shouldRaster = true;

    this.update();
  }

  /**
   * Called when the user is done dragging the floorplan
   */
  onMouseUp(e: Object) {
    this.shouldRaster = false;
    this.update();
  }

  update() {
    if (this.shouldRaster) {
      if(this.rasterLayer.isEmpty())
        this.rasterLayer.addChild(this.seatsLayer.rasterize());

      // hide the seats layer and activate the raster layer.
      this.seatsLayer.visible = false;
      this.rasterLayer.visible = true;
    } else {
      // hide the raster layer and activate the seats layer.
      this.rasterLayer.visible = false;
      this.seatsLayer.visible = true;

      this.updateSeats();
    }

    // reset the raster flag and redraw the floorplan.
    this.shouldRaster = false;
    this.view.requestUpdate(this.view.update())
  }

  /**
   * Update each seats based on the callback properties of the configuration provided
   * by the user.
   */
  updateSeats() {
    this.seats.forEach((seat) => {
      // generate the seatData used for callbacks
      const seatData = toSeatData(this.props.seats[seat.id]);

      // seat.visible = seat.position.isInside(this.view.bounds);
      seat.color = this.props.seatColor(seatData);
    });
  }

  render() {
    return <canvas ref="canvas" />;
  }
}
<commit_msg>remove unused function call<commit_after>// @flow
import React from 'react';
import { paper, Point, Layer } from 'paper';

import type { SeatsMap } from '../reducers/types';
import type { SeatData } from '../types';

import { toSeatData } from '../conversions';
import Seat from './Seat';

type Props = {
  seats: SeatsMap,
  zoom: number,

  // JS api callback
  onSelectSeat: (seat: SeatData) => void,
  seatColor: (seat: SeatData) => string,

  showTooltip: (text: string ) => void,
  hideTooltip: () => void,
}

export default class Floorplan extends React.Component {

  props: Props;
  view: paper.view;
  rasterLayer: Layer;
  seatsLayer: Layer;

  shouldRaster: bool;
  seats: Seat[];
  update: () => void;

  constructor(props: Props) {
    super(props);
    this.update = this.update.bind(this);
  }

  componentDidMount() {
    const canvas = this.refs.canvas;

    // Setup canvas
    canvas.width = 960;
    canvas.height = 540;

    // Configure the floorplan
    paper.setup(canvas);
    this.view = paper.view;
    this.view.autoUpdate = false;
    this.view.zoom = this.props.zoom;
    this.view.onMouseDrag = (e) => this.onMouseDrag(e);
    this.view.onMouseUp = (e) => this.onMouseUp(e);

    this.seatsLayer = paper.project.activeLayer;
    this.rasterLayer = paper.project.addLayer(new Layer());

    // HARDCODED: center the view for our mocked seats
    this.view.center = new Point(1175, 371);

    // Create and render the seats based on the loaded floorplan.
    this.seats = [];
    for (const id in this.props.seats) {
      const seatstate = this.props.seats[id];
      const seat = new Seat(id, seatstate.x, seatstate.y);

      // events binding
      seat.onMouseEnter = () => this.props.showTooltip(seatstate.label);
      seat.onMouseLeave = () => this.props.hideTooltip();
      seat.onSelect = () => this.props.onSelectSeat(toSeatData(seatstate));

      this.seats.push(seat);
    }

    this.update();
  }

  /**
   * Called when the stores the floorplan is subscribed to is updated.
   */
  componentDidUpdate() {
    this.view.zoom = this.props.zoom;
    this.update();
  }

  /**
   * Called when the user drags the floorplan around.
   */
  onMouseDrag(e: Object) {
    const delta = e.delta;

    // reduce drag length
    delta.length /= 2;

    // inverts the scroll from the drag direction
    this.view.center = this.view.center.add(new Point(-delta.x, -delta.y));

    // When dragging the floorplan, raster the viewport for insane performances.
    this.shouldRaster = true;

    this.update();
  }

  /**
   * Called when the user is done dragging the floorplan
   */
  onMouseUp(e: Object) {
    this.shouldRaster = false;
    this.update();
  }

  update() {
    if (this.shouldRaster) {
      if(this.rasterLayer.isEmpty())
        this.rasterLayer.addChild(this.seatsLayer.rasterize());

      // hide the seats layer and activate the raster layer.
      this.seatsLayer.visible = false;
      this.rasterLayer.visible = true;
    } else {
      // hide the raster layer and activate the seats layer.
      this.rasterLayer.visible = false;
      this.seatsLayer.visible = true;

      this.updateSeats();
    }

    // reset the raster flag and redraw the floorplan.
    this.shouldRaster = false;
    this.view.requestUpdate(this.view.update())
  }

  /**
   * Update each seats based on the callback properties of the configuration provided
   * by the user.
   */
  updateSeats() {
    this.seats.forEach((seat) => {
      const seatData = toSeatData(this.props.seats[seat.id]);

      seat.color = this.props.seatColor(seatData);
    });
  }

  render() {
    return <canvas ref="canvas" />;
  }
}
<|endoftext|>


<commit_before>/* eslint-env browser */

import { getRoutesByName, getRoutesSearchSequence } from './route';
import { isEqual } from './utils';

export const searchStateByLocation = (location) => {
  const routesByName = getRoutesByName();
  const name = getRoutesSearchSequence().find((routeName) => {
    const route = routesByName[routeName];

    return route.pathMaskExp.test(location.pathname || '');
  });

  if (!name) {
    return null;
  }

  const route = routesByName[name];

  return {
    name,
    params: route.parseLocationParams(location)
  };
};

// ACTIONS
const CHANGE_STATE = '@@router/CHANGE_STATE';
const NOT_FOUND_STATE = '@@router/STATE_NOT_FOUND';
const THE_SAME_STATE = '@@router/THE_SAME_STATE';

// ACTION CREATORS
export const changeStateAction = (location, action) => (dispatch, getState) => {
  const newState = searchStateByLocation(location);

  if (!newState) {
    return dispatch({
      type: NOT_FOUND_STATE,
      payload: {}
    });
  }

  const { routerState } = getState();

  if (routerState.name === newState.name && isEqual(newState.params, routerState.params)) {
    return dispatch({
      type: THE_SAME_STATE,
      payload: {}
    });
  }

  return dispatch({
    type: CHANGE_STATE,
    payload: newState
  });
};

// REDUCER
const initialState = {
  name: '',
  params: {}
};

export default (state = initialState, action) => {
  if (action.type === CHANGE_STATE) {
    return {
      ...state,
      name: action.payload.name,
      params: action.payload.params
    };
  }

  return state;
};
<commit_msg>add console error for not-found-state action<commit_after>/* eslint-env browser */

import { getRoutesByName, getRoutesSearchSequence } from './route';
import { isEqual } from './utils';

export const searchStateByLocation = (location) => {
  const routesByName = getRoutesByName();
  const name = getRoutesSearchSequence().find((routeName) => {
    const route = routesByName[routeName];

    return route.pathMaskExp.test(location.pathname || '');
  });

  if (!name) {
    return null;
  }

  const route = routesByName[name];

  return {
    name,
    params: route.parseLocationParams(location)
  };
};

// ACTIONS
const CHANGE_STATE = '@@router/CHANGE_STATE';
const NOT_FOUND_STATE = '@@router/STATE_NOT_FOUND';
const THE_SAME_STATE = '@@router/THE_SAME_STATE';

// ACTION CREATORS
export const changeStateAction = (location, action) => (dispatch, getState) => {
  const newState = searchStateByLocation(location);

  if (!newState) {
    window.console && console.error && console.error(`state is not defined for location ${JSON.stringify(location)}`); // eslint-disable-line no-console, no-unused-expressions
    return dispatch({
      type: NOT_FOUND_STATE,
      payload: {}
    });
  }

  const { routerState } = getState();

  if (routerState.name === newState.name && isEqual(newState.params, routerState.params)) {
    return dispatch({
      type: THE_SAME_STATE,
      payload: {}
    });
  }

  return dispatch({
    type: CHANGE_STATE,
    payload: newState
  });
};

// REDUCER
const initialState = {
  name: '',
  params: {}
};

export default (state = initialState, action) => {
  if (action.type === CHANGE_STATE) {
    return {
      ...state,
      name: action.payload.name,
      params: action.payload.params
    };
  }

  return state;
};
<|endoftext|>


<commit_before>var _           = require('underscore'),
    moment      = require('moment'),
    downsize    = require('downsize'),
    when        = require('when'),
    hbs         = require('express-hbs'),
    packageInfo = require('../../../package.json'),
    errors      = require('../errorHandling'),
    models      = require('../models'),
    coreHelpers;

coreHelpers = function (ghost) {
    var paginationHelper,
        scriptTemplate = _.template("<script src='/built/scripts/<%= name %>?v=<%= version %>'></script>"),
        isProduction = process.env.NODE_ENV === 'production',
        version = encodeURIComponent(packageInfo.version);

    /**
     * [ description]
     * @todo ghost core helpers + a way for themes to register them
     * @param  {Object} context date object
     * @param  {*} options
     * @return {Object} A Moment time / date object
     */
    ghost.registerThemeHelper('date', function (context, options) {
        if (!options && context.hasOwnProperty('hash')) {
            options = context;
            context = undefined;

            // set to published_at by default, if it's available
            // otherwise, this will print the current date
            if (this.published_at) {
                context = this.published_at;
            }
        }

        var f = options.hash.format || 'MMM Do, YYYY',
            timeago = options.hash.timeago,
            date;

        if (timeago) {
            date = moment(context).fromNow();
        } else {
            date = moment(context).format(f);
        }
        return date;
    });

    // ### Page URL Helper
    //
    // *Usage example:*
    // `{{pageUrl 2}}`
    //
    // Returns the URL for the page specified in the current object
    // context.
    //
    ghost.registerThemeHelper('pageUrl', function (context, block) {
        return context === 1 ? '/' : ('/page/' + context + '/');
    });

    // ### URL helper
    //
    // *Usage example:*
    // `{{url}}`
    // `{{url absolute}}`
    //
    // Returns the URL for the current object context
    // i.e. If inside a post context will return post permalink
    // absolute flag outputs absolute URL, else URL is relative
    ghost.registerThemeHelper('url', function (options) {
        var output = '';

        if (options && options.hash.absolute) {
            output += ghost.config().url;
        }

        if (models.isPost(this)) {
            output += '/' + this.slug + '/';
        }

        return output;
    });

    // ### Author Helper
    //
    // *Usage example:*
    // `{{author}}`
    //
    // Returns the full name of the author of a given post, or a blank string
    // if the author could not be determined.
    //
    ghost.registerThemeHelper('author', function (context, options) {
        return this.author ? this.author.name : '';
    });

    // ### Tags Helper
    //
    // *Usage example:*
    // `{{tags}}`
    // `{{tags separator=" - "}}`
    //
    // Returns a string of the tags on the post.
    // By default, tags are separated by commas.
    //
    // Note that the standard {{#each tags}} implementation is unaffected by this helper
    // and can be used for more complex templates.
    ghost.registerThemeHelper('tags', function (options) {
        var separator = ', ',
            tagNames;

        if (typeof options.hash.separator === 'string') {
            separator = options.hash.separator;
        }

        tagNames = _.pluck(this.tags, 'name');
        return tagNames.join(separator);
    });

    // ### Content Helper
    //
    // *Usage example:*
    // `{{content}}`
    // `{{content words=20}}`
    // `{{content characters=256}}`
    //
    // Turns content html into a safestring so that the user doesn't have to
    // escape it or tell handlebars to leave it alone with a triple-brace.
    //
    // Enables tag-safe truncation of content by characters or words.
    //
    // **returns** SafeString content html, complete or truncated.
    //
    ghost.registerThemeHelper('content', function (options) {
        var truncateOptions = (options || {}).hash || {};
        truncateOptions = _.pick(truncateOptions, ['words', 'characters']);

        if (truncateOptions.words || truncateOptions.characters) {
            return new hbs.handlebars.SafeString(
                downsize(this.html, truncateOptions)
            );
        }

        return new hbs.handlebars.SafeString(this.html);
    });

    // ### Excerpt Helper
    //
    // *Usage example:*
    // `{{excerpt}}`
    // `{{excerpt words=50}}`
    // `{{excerpt characters=256}}`
    //
    // Attempts to remove all HTML from the string, and then shortens the result according to the provided option.
    //
    // Defaults to words=50
    //
    // **returns** SafeString truncated, HTML-free content.
    //
    ghost.registerThemeHelper('excerpt', function (options) {
        var truncateOptions = (options || {}).hash || {},
            excerpt;

        truncateOptions = _.pick(truncateOptions, ['words', 'characters']);

        /*jslint regexp:true */
        excerpt = String(this.html).replace(/<\/?[^>]+>/gi, '');
        /*jslint regexp:false */

        if (!truncateOptions.words && !truncateOptions.characters) {
            truncateOptions.words = 50;
        }

        return new hbs.handlebars.SafeString(
            downsize(excerpt, truncateOptions)
        );
    });

    ghost.registerThemeHelper('body_class', function (options) {
        var classes = [],
            tags = this.post && this.post.tags ? this.post.tags : this.tags || [];

        if (_.isString(this.path) && this.path.match(/\/page/)) {
            classes.push('archive-template');
        } else if (!this.path || this.path === '/' || this.path === '') {
            classes.push('home-template');
        } else {
            classes.push('post-template');
        }

        if (tags) {
            classes = classes.concat(tags.map(function (tag) { return 'tag-' + tag.slug; }));
        }

        return ghost.doFilter('body_class', classes, function (classes) {
            var classString = _.reduce(classes, function (memo, item) { return memo + ' ' + item; }, '');
            return new hbs.handlebars.SafeString(classString.trim());
        });
    });

    ghost.registerThemeHelper('post_class', function (options) {
        var classes = ['post'],
            tags = this.post && this.post.tags ? this.post.tags : this.tags || [];

        if (tags) {
            classes = classes.concat(tags.map(function (tag) { return 'tag-' + tag.slug; }));
        }

        return ghost.doFilter('post_class', classes, function (classes) {
            var classString = _.reduce(classes, function (memo, item) { return memo + ' ' + item; }, '');
            return new hbs.handlebars.SafeString(classString.trim());
        });
    });

    ghost.registerThemeHelper('ghost_head', function (options) {
        var head = [],
            majorMinor = /^(\d+\.)?(\d+)/,
            trimmedVersion = this.version.match(majorMinor)[0];

        head.push('<meta name="generator" content="Ghost ' + trimmedVersion + '" />');
        head.push('<link rel="alternate" type="application/rss+xml" title="RSS" href="/rss/">');

        return ghost.doFilter('ghost_head', head, function (head) {
            var headString = _.reduce(head, function (memo, item) { return memo + '\n' + item; }, '');
            return new hbs.handlebars.SafeString(headString.trim());
        });
    });

    ghost.registerThemeHelper('meta_title', function (options) {
        var title, blog;
        blog = ghost.blogGlobals();
        if (_.isString(this.path)) {
            if (!this.path || this.path === '/' || this.path === '' || this.path.match(/\/page/)) {
                blog = ghost.blogGlobals();
                title = blog.title;
            } else {
                title = this.post.title;
            }
        }

        return ghost.doFilter('meta_title', title, function (title) {
            return new hbs.handlebars.SafeString(title.trim());
        });
    });

    ghost.registerThemeHelper('meta_description', function (options) {
        var description, blog;
        blog = ghost.blogGlobals();
        if (_.isString(this.path)) {
            if (!this.path || this.path === '/' || this.path === '' || this.path.match(/\/page/)) {
                blog = ghost.blogGlobals();
                description = blog.description;
            } else {
                description = '';
            }
        }

        return ghost.doFilter('meta_description', description, function (description) {
            return new hbs.handlebars.SafeString(description.trim());
        });
    });

    ghost.registerThemeHelper('ghost_foot', function (options) {
        var foot = [];
        foot.push('<script src="/shared/vendor/jquery/jquery.js"></script>');

        return ghost.doFilter('ghost_foot', foot, function (foot) {
            var footString = _.reduce(foot, function (memo, item) { return memo + ' ' + item; }, '');
            return new hbs.handlebars.SafeString(footString.trim());
        });
    });
    /**
     * [ description]
     *
     * @param String key
     * @param String default translation
     * @param {Object} options
     * @return String A correctly internationalised string
     */
    ghost.registerThemeHelper('e', function (key, defaultString, options) {
        var output;

        if (ghost.settings('defaultLang') === 'en' && _.isEmpty(options.hash) && !ghost.settings('forceI18n')) {
            output = defaultString;
        } else {
            output = ghost.polyglot().t(key, options.hash);
        }

        return output;
    });

    ghost.registerThemeHelper('json', function (object, options) {
        return JSON.stringify(object);
    });

    ghost.registerThemeHelper('foreach', function (context, options) {
        var fn = options.fn,
            inverse = options.inverse,
            i = 0,
            j = 0,
            columns = options.hash.columns,
            key,
            ret = '',
            data;

        if (options.data) {
            data = hbs.handlebars.createFrame(options.data);
        }

        function setKeys(_data, _i, _j, _columns) {
            if (_i === 0) {
                _data.first = true;
            }
            if (_i === _j - 1) {
                _data.last = true;
            }
            // first post is index zero but still needs to be odd
            if (_i % 2 === 1) {
                _data.even = true;
            } else {
                _data.odd = true;
            }
            if (_i % _columns === 0) {
                _data.rowStart = true;
            } else if (_i % _columns === (_columns - 1)) {
                _data.rowEnd = true;
            }
            return _data;
        }
        if (context && typeof context === 'object') {
            if (context instanceof Array) {
                for (j = context.length; i < j; i += 1) {
                    if (data) {
                        data.index = i;
                        data.first = data.rowEnd = data.rowStart = data.last = data.even = data.odd = false;
                        data = setKeys(data, i, j, columns);
                    }
                    ret = ret + fn(context[i], { data: data });
                }
            } else {
                for (key in context) {
                    if (context.hasOwnProperty(key)) {
                        j += 1;
                    }
                }
                for (key in context) {
                    if (context.hasOwnProperty(key)) {
                        if (data) {
                            data.key = key;
                            data.first = data.rowEnd = data.rowStart = data.last = data.even = data.odd = false;
                            data = setKeys(data, i, j, columns);
                        }
                        ret = ret + fn(context[key], {data: data});
                        i += 1;
                    }
                }
            }
        }

        if (i === 0) {
            ret = inverse(this);
        }
        return ret;
    });

    // A helper for inserting the javascript tags with version hashes
    ghost.registerThemeHelper('ghostScriptTags', function () {
        var scriptFiles = [];

        if (isProduction) {
            scriptFiles.push("ghost.min.js");
        } else {
            scriptFiles = [
                'vendor.js',
                'helpers.js',
                'templates.js',
                'models.js',
                'views.js'
            ];
        }

        scriptFiles = _.map(scriptFiles, function (fileName) {
            return scriptTemplate({
                name: fileName,
                version: version
            });
        });

        return scriptFiles.join('');
    });

    // ## Template driven helpers
    // Template driven helpers require that their template is loaded before they can be registered.

    // ### Pagination Helper
    // `{{pagination}}`
    // Outputs previous and next buttons, along with info about the current page
    paginationHelper = ghost.loadTemplate('pagination').then(function (templateFn) {
        ghost.registerThemeHelper('pagination', function (options) {
            if (!_.isObject(this.pagination) || _.isFunction(this.pagination)) {
                errors.logAndThrowError('pagination data is not an object or is a function');
                return;
            }
            if (_.isUndefined(this.pagination.page) || _.isUndefined(this.pagination.pages)
                    || _.isUndefined(this.pagination.total) || _.isUndefined(this.pagination.limit)) {
                errors.logAndThrowError('All values must be defined for page, pages, limit and total');
                return;
            }
            if ((!_.isUndefined(this.pagination.next) && !_.isNumber(this.pagination.next))
                    || (!_.isUndefined(this.pagination.prev) && !_.isNumber(this.pagination.prev))) {
                errors.logAndThrowError('Invalid value, Next/Prev must be a number');
                return;
            }
            if (!_.isNumber(this.pagination.page) || !_.isNumber(this.pagination.pages)
                    || !_.isNumber(this.pagination.total) || !_.isNumber(this.pagination.limit)) {
                errors.logAndThrowError('Invalid value, check page, pages, limit and total are numbers');
                return;
            }
            return new hbs.handlebars.SafeString(templateFn(this.pagination));
        });
    });

    ghost.registerThemeHelper('helperMissing', function (arg) {
        if (arguments.length === 2) {
            return undefined;
        }
        errors.logError("Missing helper: '" + arg + "'");
    });
    // Return once the template-driven helpers have loaded
    return when.join(
        paginationHelper
    );
};

module.exports.loadCoreHelpers = coreHelpers;
<commit_msg>fix heroku error<commit_after>var _           = require('underscore'),
    moment      = require('moment'),
    downsize    = require('downsize'),
    when        = require('when'),
    hbs         = require('express-hbs'),
    packageInfo = require('../../../package.json'),
    errors      = require('../errorHandling'),
    models      = require('../models'),
    coreHelpers;

coreHelpers = function (ghost) {
    var paginationHelper,
        scriptTemplate = _.template("<script src='/built/scripts/<%= name %>?v=<%= version %>'></script>"),
        isProduction = process.env.NODE_ENV === 'production',
        version = encodeURIComponent(packageInfo.version);

    /**
     * [ description]
     * @todo ghost core helpers + a way for themes to register them
     * @param  {Object} context date object
     * @param  {*} options
     * @return {Object} A Moment time / date object
     */
    ghost.registerThemeHelper('date', function (context, options) {
        if (!options && context.hasOwnProperty('hash')) {
            options = context;
            context = undefined;

            // set to published_at by default, if it's available
            // otherwise, this will print the current date
            if (this.published_at) {
                context = this.published_at;
            }
        }

        var f = options.hash.format || 'MMM Do, YYYY',
            timeago = options.hash.timeago,
            date;

        if (timeago) {
            date = moment(context).fromNow();
        } else {
            date = moment(context).format(f);
        }
        return date;
    });

    // ### Page URL Helper
    //
    // *Usage example:*
    // `{{pageUrl 2}}`
    //
    // Returns the URL for the page specified in the current object
    // context.
    //
    ghost.registerThemeHelper('pageUrl', function (context, block) {
        return context === 1 ? '/' : ('/page/' + context + '/');
    });

    // ### URL helper
    //
    // *Usage example:*
    // `{{url}}`
    // `{{url absolute}}`
    //
    // Returns the URL for the current object context
    // i.e. If inside a post context will return post permalink
    // absolute flag outputs absolute URL, else URL is relative
    ghost.registerThemeHelper('url', function (options) {
        var output = '';

        if (options && options.hash.absolute) {
            output += ghost.config().url;
        }

        if (models.isPost(this)) {
            output += '/' + this.slug + '/';
        }

        return output;
    });

    // ### Author Helper
    //
    // *Usage example:*
    // `{{author}}`
    //
    // Returns the full name of the author of a given post, or a blank string
    // if the author could not be determined.
    //
    ghost.registerThemeHelper('author', function (context, options) {
        return this.author ? this.author.name : '';
    });

    // ### Tags Helper
    //
    // *Usage example:*
    // `{{tags}}`
    // `{{tags separator=" - "}}`
    //
    // Returns a string of the tags on the post.
    // By default, tags are separated by commas.
    //
    // Note that the standard {{#each tags}} implementation is unaffected by this helper
    // and can be used for more complex templates.
    ghost.registerThemeHelper('tags', function (options) {
        var separator = ', ',
            tagNames;

        if (typeof options.hash.separator === 'string') {
            separator = options.hash.separator;
        }

        tagNames = _.pluck(this.tags, 'name');
        return tagNames.join(separator);
    });

    // ### Content Helper
    //
    // *Usage example:*
    // `{{content}}`
    // `{{content words=20}}`
    // `{{content characters=256}}`
    //
    // Turns content html into a safestring so that the user doesn't have to
    // escape it or tell handlebars to leave it alone with a triple-brace.
    //
    // Enables tag-safe truncation of content by characters or words.
    //
    // **returns** SafeString content html, complete or truncated.
    //
    ghost.registerThemeHelper('content', function (options) {
        var truncateOptions = (options || {}).hash || {};
        truncateOptions = _.pick(truncateOptions, ['words', 'characters']);

        if (truncateOptions.words || truncateOptions.characters) {
            return new hbs.handlebars.SafeString(
                downsize(this.html, truncateOptions)
            );
        }

        return new hbs.handlebars.SafeString(this.html);
    });

    // ### Excerpt Helper
    //
    // *Usage example:*
    // `{{excerpt}}`
    // `{{excerpt words=50}}`
    // `{{excerpt characters=256}}`
    //
    // Attempts to remove all HTML from the string, and then shortens the result according to the provided option.
    //
    // Defaults to words=50
    //
    // **returns** SafeString truncated, HTML-free content.
    //
    ghost.registerThemeHelper('excerpt', function (options) {
        var truncateOptions = (options || {}).hash || {},
            excerpt;

        truncateOptions = _.pick(truncateOptions, ['words', 'characters']);

        /*jslint regexp:true */
        excerpt = String(this.html).replace(/<\/?[^>]+>/gi, '');
        /*jslint regexp:false */

        if (!truncateOptions.words && !truncateOptions.characters) {
            truncateOptions.words = 50;
        }

        return new hbs.handlebars.SafeString(
            downsize(excerpt, truncateOptions)
        );
    });

    ghost.registerThemeHelper('body_class', function (options) {
        var classes = [],
            tags = this.post && this.post.tags ? this.post.tags : this.tags || [];

        if (_.isString(this.path) && this.path.match(/\/page/)) {
            classes.push('archive-template');
        } else if (!this.path || this.path === '/' || this.path === '') {
            classes.push('home-template');
        } else {
            classes.push('post-template');
        }

        if (tags) {
            classes = classes.concat(tags.map(function (tag) { return 'tag-' + tag.slug; }));
        }

        return ghost.doFilter('body_class', classes, function (classes) {
            var classString = _.reduce(classes, function (memo, item) { return memo + ' ' + item; }, '');
            return new hbs.handlebars.SafeString(classString.trim());
        });
    });

    ghost.registerThemeHelper('post_class', function (options) {
        var classes = ['post'],
            tags = this.post && this.post.tags ? this.post.tags : this.tags || [];

        if (tags) {
            classes = classes.concat(tags.map(function (tag) { return 'tag-' + tag.slug; }));
        }

        return ghost.doFilter('post_class', classes, function (classes) {
            var classString = _.reduce(classes, function (memo, item) { return memo + ' ' + item; }, '');
            return new hbs.handlebars.SafeString(classString.trim());
        });
    });

    ghost.registerThemeHelper('ghost_head', function (options) {
        var head = [],
            majorMinor = /^(\d+\.)?(\d+)/,
            trimmedVersion = this.version.match(majorMinor)[0];

        head.push('<meta name="generator" content="Ghost ' + trimmedVersion + '" />');
        head.push('<link rel="alternate" type="application/rss+xml" title="RSS" href="/rss/">');

        return ghost.doFilter('ghost_head', head, function (head) {
            var headString = _.reduce(head, function (memo, item) { return memo + '\n' + item; }, '');
            return new hbs.handlebars.SafeString(headString.trim());
        });
    });

    ghost.registerThemeHelper('meta_title', function (options) {
        var title, blog;
        blog = ghost.blogGlobals();
        if (_.isString(this.path)) {
            if (!this.path || this.path === '/' || this.path === '' || this.path.match(/\/page/)) {
                blog = ghost.blogGlobals();
                title = blog.title;
            } else {
                title = this.post.title;
            }
        }

        return ghost.doFilter('meta_title', title, function (title) {
            return new hbs.handlebars.SafeString(title.trim());
        });
    });

    ghost.registerThemeHelper('meta_description', function (options) {
        var description, blog;
        blog = ghost.blogGlobals();
        if (_.isString(this.path)) {
            if (!this.path || this.path === '/' || this.path === '' || this.path.match(/\/page/)) {
                blog = ghost.blogGlobals();
                description = blog.description;
            } else {
                description = '';
            }
        }

        return ghost.doFilter('meta_description', description, function (description) {
            return new hbs.handlebars.SafeString(description.trim());
        });
    });

    ghost.registerThemeHelper('ghost_foot', function (options) {
        var foot = [];
        foot.push('<script src="/shared/vendor/jquery/jquery.js"></script>');

        return ghost.doFilter('ghost_foot', foot, function (foot) {
            var footString = _.reduce(foot, function (memo, item) { return memo + ' ' + item; }, '');
            return new hbs.handlebars.SafeString(footString.trim());
        });
    });
    /**
     * [ description]
     *
     * @param String key
     * @param String default translation
     * @param {Object} options
     * @return String A correctly internationalised string
     */
    ghost.registerThemeHelper('e', function (key, defaultString, options) {
        var output;

        if (ghost.settings('defaultLang') === 'en' && _.isEmpty(options.hash) && !ghost.settings('forceI18n')) {
            output = defaultString;
        } else {
            output = ghost.polyglot().t(key, options.hash);
        }

        return output;
    });

    ghost.registerThemeHelper('json', function (object, options) {
        return JSON.stringify(object);
    });

    ghost.registerThemeHelper('foreach', function (context, options) {
        var fn = options.fn,
            inverse = options.inverse,
            i = 0,
            j = 0,
            columns = options.hash.columns,
            key,
            ret = '',
            data;

        if (options.data) {
            data = hbs.handlebars.createFrame(options.data);
        }

        function setKeys(_data, _i, _j, _columns) {
            if (_i === 0) {
                _data.first = true;
            }
            if (_i === _j - 1) {
                _data.last = true;
            }
            // first post is index zero but still needs to be odd
            if (_i % 2 === 1) {
                _data.even = true;
            } else {
                _data.odd = true;
            }
            if (_i % _columns === 0) {
                _data.rowStart = true;
            } else if (_i % _columns === (_columns - 1)) {
                _data.rowEnd = true;
            }
            return _data;
        }
        if (context && typeof context === 'object') {
            if (context instanceof Array) {
                for (j = context.length; i < j; i += 1) {
                    if (data) {
                        data.index = i;
                        data.first = data.rowEnd = data.rowStart = data.last = data.even = data.odd = false;
                        data = setKeys(data, i, j, columns);
                    }
                    ret = ret + fn(context[i], { data: data });
                }
            } else {
                for (key in context) {
                    if (context.hasOwnProperty(key)) {
                        j += 1;
                    }
                }
                for (key in context) {
                    if (context.hasOwnProperty(key)) {
                        if (data) {
                            data.key = key;
                            data.first = data.rowEnd = data.rowStart = data.last = data.even = data.odd = false;
                            data = setKeys(data, i, j, columns);
                        }
                        ret = ret + fn(context[key], {data: data});
                        i += 1;
                    }
                }
            }
        }

        if (i === 0) {
            ret = inverse(this);
        }
        return ret;
    });

    // A helper for inserting the javascript tags with version hashes
    ghost.registerThemeHelper('ghostScriptTags', function () {
        var scriptFiles = [];

        if (isProduction) {
            scriptFiles.push("ghost.min.js");
        } else {
            scriptFiles = [
                'vendor.js',
                'helpers.js',
                'templates.js',
                'models.js',
                'views.js'
            ];
        }

        scriptFiles = _.map(scriptFiles, function (fileName) {
            return scriptTemplate({
                name: fileName,
                version: version
            });
        });

        return scriptFiles.join('');
    });

    // ## Template driven helpers
    // Template driven helpers require that their template is loaded before they can be registered.

    // ### Pagination Helper
    // `{{pagination}}`
    // Outputs previous and next buttons, along with info about the current page
    paginationHelper = ghost.loadTemplate('pagination').then(function (templateFn) {
        ghost.registerThemeHelper('pagination', function (options) {
            if (!_.isObject(this.pagination) || _.isFunction(this.pagination)) {
                errors.logAndThrowError('pagination data is not an object or is a function');
                return;
            }
            if (_.isUndefined(this.pagination.page) || _.isUndefined(this.pagination.pages)
                    || _.isUndefined(this.pagination.total) || _.isUndefined(this.pagination.limit)) {
                errors.logAndThrowError('All values must be defined for page, pages, limit and total');
                return;
            }
            if ((!_.isUndefined(this.pagination.next) && !_.isNumber(this.pagination.next))
                    || (!_.isUndefined(this.pagination.prev) && !_.isNumber(this.pagination.prev))) {
                errors.logAndThrowError('Invalid value, Next/Prev must be a number');
                return;
            }
            /* TODO - this is a hack, since pagination.total is a string for
            * some reason; find root cause and fix it */
            this.pagination.total = parseInt(this.pagination.total);

            if (!_.isNumber(this.pagination.page) || !_.isNumber(this.pagination.pages)
                    || !_.isNumber(this.pagination.total) || !_.isNumber(this.pagination.limit)) {
                errors.logAndThrowError('Invalid value, check page, pages, limit and total are numbers');
                return;
            }
            return new hbs.handlebars.SafeString(templateFn(this.pagination));
        });
    });

    ghost.registerThemeHelper('helperMissing', function (arg) {
        if (arguments.length === 2) {
            return undefined;
        }
        errors.logError("Missing helper: '" + arg + "'");
    });
    // Return once the template-driven helpers have loaded
    return when.join(
        paginationHelper
    );
};

module.exports.loadCoreHelpers = coreHelpers;
<|endoftext|>