<commit_before>/*
 * Licensed to Cloudkick, Inc ('Cloudkick') under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * Cloudkick licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var fs = require('fs');
var path = require('path');

var hooks = require('deployment/hooks');
var deployTemplates = require('deployment/templates');
var deployServices = require('deployment/services');
var deployFiles = require('deployment/files');

var serviceManagement = require('service_management');
var manifest = require('manifest');
var manifestConstants = require('manifest/constants');
var config = require('util/config');
var fsutil = require('util/fs');
var flowctrl = require('util/flow_control');

var async = require('async');
var sprintf = require('sprintf').sprintf;

var INSTANCE_NAME_RE = /^[a-zA-Z0-9_\-]+$/;

function Instance(name) {
  this.name = name;
  this.root = path.join(config.get()['app_dir'], name);
  this._bundleName = null;
}

/**
 * Check if an instance exists
 *
 * @param {String} instanceName The name of the instance.
 * @param {Function} callback Callback which is called with a boolean 'exists'.
 */
Instance.prototype.exists = function(callback) {
  fs.stat(this.root, function(err, stats) {
    if (!err && stats.isDirectory()) {
      callback(true);
      return;
    }

    callback(false);
    return;
  });
};

/**
 * Get the name of the bundle used by an instance. This is probably the
 * nastiest part of this whole instance filesystem layout because it requires
 * reading a link. To keep things fast this gets cached on the Instance object.
 *
 * @param {Function} callback Callback which is called with (err, bundleName).
 */
Instance.prototype.getBundleName = function(callback) {
  var self = this;
  if (self._bundleName) {
    process.nextTick(function() {
      callback(null, self._bundleName);
    });
  }
  else {
    var bundleLinkPath = path.join(self.root, 'bundle');
    fs.readlink(bundleLinkPath, function(err, target) {
      if (!err) {
        self._bundleName = path.basename(target);
      }
      callback(err, self._bundleName);
      return;
    });
  }
};

/**
 * Get the version pointed to by the 'current' link. This will give a value of
 * 'null' if the current link doesn't exist (currently it should always exist
 * but we may want to introduce situations where it won't in the future).
 *
 * @param {Function} callback Callback which is called with (version).
 */
Instance.prototype.getBundleVersion = function(callback) {
  var self = this;
  var currentLinkPath = path.join(self.root, 'current');
  fs.readlink(currentLinkPath, function(err, target) {
    if (err) {
      callback(null);
      return;
    }
    else {
      callback(target.split('@', 2)[1]);
      return;
    }
  });
};

/**
 * Get the path at which a version of this instance would reside. This does
 * not guarantee that this version exists, merely returns the hypothetical
 * path at which it would reside.
 *
 * @param {String} version    The version to get the path to.
 * @param {Function} callback A callback that takes (err, versionPath).
 */
Instance.prototype.getVersionPath = function(version, callback) {
  var self = this;
  self.getBundleName(function(err, bundleName) {
    var bundleNameFull = bundleName + '@' + version;
    var versionPath = path.join(self.root, 'versions', bundleNameFull);
    callback(err, versionPath);
    return;
  });
};

/**
 * Get the path at which the extracted bundle for the specified version would
 * reside. Again, this doesn't verify that the path actually exists.
 *
 * @param {String} version    The version to get the path to.
 * @param {Function} callback A callback that takes (err, bundleVersionPath).
 */
Instance.prototype.getBundleVersionPath = function(version, callback) {
  var self = this;
  self.getBundleName(function(err, bundleName) {
    var bundleNameFull = bundleName + '@' + version;
    var bundleVersionPath = path.join(config.get()['extracted_dir'], bundleName, bundleNameFull);
    callback(err, bundleVersionPath);
    return;
  });
};

/**
 * Remove all data related to an instance, including the instance, its data,
 * and any services. This performs very little validation (doesn't even check
 * that the instance exists first), it just starts deleting stuff.
 *
 * @param {Function} callback Callback which is called when the cleanup process has finished.
 */
Instance.prototype.destroy = function(callback) {
  var manager = serviceManagement.getDefaultManager().getManager();

  var ops = [
    async.apply(flowctrl.callIgnoringError, fsutil.rmtree, null, this.root)
  ];

  manager.getService(this.name, function(err, service) {
    if (!err) {
      ops.push(async.apply(flowctrl.callIgnoringError, service.destroy, service));
    }

    async.parallel(ops, function(err) {
      callback();
    });
  });
};

/**
 * Given a bundle version, hard hard link all the files into place, render
 * any templates and resolve all data files.
 *
 * @param {String} version    The version of the bundle to prepare.
 * @param {Function} callback A callback fired with (err).
 */
Instance.prototype.prepareVersion = function(version, callback) {
  var self = this;
  var extractedBundlePath, bundleName, bundleNameFull, manifestObj;
  var instanceVersionPath, ignoredPaths;

  var bundleLinkPath = path.join(self.root, 'bundle');
  var hookEnv = { 'CAST_INSTANCE_NAME': self.name };

  async.series([
    // Resolve the path at which the version will reside
    function(callback) {
      self.getVersionPath(version, function(err, vp) {
        instanceVersionPath = vp;
        callback(err);
        return;
      });
    },

    // Resolve the path to the extracted bundle
    function(callback) {
      self.getBundleVersionPath(version, function(err, bvp) {
        extractedBundlePath = bvp;
        callback(err);
        return;
      });
    },

    // Make sure this version doesn't exist
    function(callback) {
      path.exists(instanceVersionPath, function(exists) {
        var err = null;
        if (exists) {
          err = new Error(sprintf('Instance \'%s\' already has version \'%s\'', self.name, version));
        }
        callback(err);
        return;
      });
    },

    // Make sure an extracted bundle exists for the requested version
    function(callback) {
      fs.stat(extractedBundlePath, function(err, stats) {
        if (!err && !stats.isDirectory()) {
          err = new Error('No bundle for version \'' + version + '\'');
        }
        callback(err);
        return;
      });
    },

    // Create the directory for the target version
    function(callback) {
      fsutil.ensureDirectory(instanceVersionPath, callback);
    },

    // Retrieve the manifest object
    function(callback) {
      var manifestPath = path.join(extractedBundlePath, manifestConstants.MANIFEST_FILENAME);
      manifest.getManifestObject(manifestPath, true, function(err, _manifestObj) {
        manifestObj = _manifestObj;
        ignoredPaths = manifestObj['template_files'].concat(manifestObj['data_files']);
        callback(err);
        return;
      });
    },

    // Mirror the directory structure from the extracted bundle in the instance directory
    function(callback) {
      fsutil.treeToTemplate(extractedBundlePath, ignoredPaths, function(err, templateObject) {
        if (err) {
          callback(err);
          return;
        }

        // No directories
        if (Object.keys(templateObject).length === 0) {
          callback();
          return;
        }

        fsutil.templateToTree(instanceVersionPath, templateObject, true, callback);
      });
    },

    // Realize (render and save) templates
    function(callback) {
      var instanceData = deployTemplates.getInstanceTemplateObject(self.name, instanceVersionPath);
      deployTemplates.realizeApplicationTemplates(manifestObj, instanceData, extractedBundlePath,
                                                     instanceVersionPath, callback);
    },

    // Hard-link all the files (except template and data files)
    function(callback) {
      fsutil.hardLinkFiles(extractedBundlePath, instanceVersionPath, ignoredPaths, callback);
    },

    // Resolve the data files
    function(callback) {
      var instanceDataRoot = path.join(self.root, 'data');
      deployFiles.resolveDataFiles(extractedBundlePath, instanceDataRoot, instanceVersionPath,
                                      manifestObj['data_files'], callback);
    },

    // Execute the 'post_prepare' hook
    function(callback) {
      var hook = new hooks.InstanceHook('post', 'post_prepare',
                                        instanceVersionPath, false, hookEnv);
      hook.execute(null, [version], callback);
    }
  ], callback);
};

/**
 * Point the 'current' symlink to the specified version. Will verify
 * that the specified version exists before taking action.
 *
 * @param {String} version    The version to activate.
 * @param {Function} callback A callback fired with (err).
 */
Instance.prototype.activateVersion = function(version, callback) {
  var self = this;
  var newVersionPath;

  var newVersionLink = path.join(self.root, 'new');
  var currentVersionLink = path.join(self.root, 'current');
  var hookEnv = { 'CAST_INSTANCE_NAME': self.name };

  async.series([
    // Get the path to the specified version
    function(callback) {
      self.getVersionPath(version, function(err, vp) {
        newVersionPath = vp;
        callback(err);
        return;
      });
    },

    // Make sure the version exists
    function(callback) {
      path.exists(newVersionPath, function(exists) {
        var err = null;
        if (!exists) {
          err = new Error('Cannot activate nonexistent version \'' + version + '\'');
        }

        callback(err);
        return;
      });
    },

    function(callback) {
      var hook = new hooks.InstanceHook('post', 'pre_version_activate',
                                        newVersionPath, false, hookEnv);
      hook.execute(null, [version, newVersionPath], callback);
    },

    // Create the new link
    function(callback) {
      fs.symlink(path.resolve(newVersionPath), newVersionLink, callback);
    },

    // Atomically move it into place
    async.apply(fs.rename, newVersionLink, currentVersionLink),

    function(callback) {
      var hook = new hooks.InstanceHook('post', 'post_version_activate',
                                        newVersionPath, false, hookEnv);
      hook.execute(null, [version, newVersionPath], callback);
    }
  ], callback);
};

/**
 * Create an instance with a specified name for the specified bundle. The
 * instance will initially use the specified version of the bundle.
 *
 * @param {String} instanceName  A name to give to the instance.
 * @param {String} bundleName    The name of the bundle to use.
 * @param {String} bundleVersion The version of the bundle to use.
 * @param {Function} callback     A callback fired with (err).
 */
exports.createInstance = function(instanceName, bundleName, bundleVersion, callback) {
  if (!INSTANCE_NAME_RE.exec(instanceName)) {
    callback(new Error('Invalid instance name'));
    return;
  }

  var manifestObj, ignoredPaths, instance;
  var bundleNameFull = bundleName + '@' + bundleVersion;

  // Get the paths to the extracted bundle and the manifest
  var extractedBundleRoot = path.join(config.get()['extracted_dir'], bundleName);
  var extractedBundlePath = path.join(extractedBundleRoot, bundleNameFull);

  var instancePath = path.join(config.get()['app_dir'], instanceName);
  var instanceDataRoot = path.join(instancePath, 'data');
  var instanceVersionsRoot = path.join(instancePath, 'versions');
  var instanceBundleLink = path.join(instancePath, 'bundle');
  var instanceCurrentLink = path.join(instancePath, 'current');
  var instanceVersionPath = path.join(instanceVersionsRoot, bundleNameFull);

  async.series([
    // Make sure that the extracted bundle path exists
    function(callback) {
      path.exists(extractedBundlePath, function(exists) {
        if (!exists) {
          callback(new Error('Invalid bundle name or version'));
          return;
        }

        callback();
      });
    },

    // Make sure there isn't already an instance with this name
    function(callback) {
      path.exists(instancePath, function(exists) {
        if (exists) {
          callback(new Error('Instance name already in use'));
          return;
        }
        callback();
      });
    },

    // Create the instance directory
    function(callback) {
      fsutil.ensureDirectory(instancePath, function(err) {
        if (!err) {
          instance = new Instance(instanceName);
        }
        callback(err);
      });
    },

    // Create the data and versions directories
    async.apply(fsutil.ensureDirectory, instanceDataRoot),
    async.apply(fsutil.ensureDirectory, instanceVersionsRoot),

    // Create the bundle symlink
    async.apply(fs.symlink, path.resolve(extractedBundleRoot), instanceBundleLink),

    // Prepare the specified version
    function(callback) {
      instance.prepareVersion(bundleVersion, callback);
    },

    // Symlink it
    function(callback) {
      instance.activateVersion(bundleVersion, callback);
    },

    // Retrieve the manifest object
    function(callback) {
      var manifestPath = path.join(extractedBundlePath, manifestConstants.MANIFEST_FILENAME);
      manifest.getManifestObject(manifestPath, true, function(err, _manifestObj) {
        manifestObj = _manifestObj;
        callback(err);
        return;
      });
    },

    // Create a runit service
    function(callback) {
      deployServices.createService(instanceName, instanceCurrentLink, manifestObj, callback);
    }
  ],

  function(error) {
    if (error && instance) {
      instance.destroy(function() {
        callback(error);
        return;
      });
    }
    else {
      callback(error);
    }
  });
};

/**
 * Get an Instance object for an instance whose existence is verified.
 *
 * @param {String} instanceName  The name of the instance to retrieve.
 * @param {Function} callback     A callback that takes (err, instance).
 */
exports.getInstance = function(instanceName, callback) {
  var instance = new Instance(instanceName);
  instance.exists(function(exists) {
    if (!exists) {
      callback(new Error('Instance doesn\'t exist'));
      return;
    }
    else {
      callback(null, instance);
      return;
    }
  });
};

/**
 * Get a list of all instances (in the form of Instance objects).
 *
 * @param {Function} callback A callback that takes (err, instances).
 */
exports.getInstanceList = function(callback) {
  fs.readdir(config.get()['app_dir'], function(err, files) {
    if (err) {
      callback(err);
      return;
    }

    // Construct an Instance for each file in the app_dir
    var instanceList = files.map(function(file) {
      return new Instance(file);
    });

    // Make sure each instance actually exists (ie, isn't a non-directory of some sort)
    async.filter(instanceList, function(instance, callback) {
      instance.exists(callback);
    },
    function(instances) {
      callback(null, instances);
      return;
    });
  });
};

exports.Instance = Instance;
<commit_msg>Use absolute path to the instance, not symlink.<commit_after>/*
 * Licensed to Cloudkick, Inc ('Cloudkick') under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * Cloudkick licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var fs = require('fs');
var path = require('path');

var hooks = require('deployment/hooks');
var deployTemplates = require('deployment/templates');
var deployServices = require('deployment/services');
var deployFiles = require('deployment/files');

var serviceManagement = require('service_management');
var manifest = require('manifest');
var manifestConstants = require('manifest/constants');
var config = require('util/config');
var fsutil = require('util/fs');
var flowctrl = require('util/flow_control');

var async = require('async');
var sprintf = require('sprintf').sprintf;

var INSTANCE_NAME_RE = /^[a-zA-Z0-9_\-]+$/;

function Instance(name) {
  this.name = name;
  this.root = path.join(config.get()['app_dir'], name);
  this._bundleName = null;
}

/**
 * Check if an instance exists
 *
 * @param {String} instanceName The name of the instance.
 * @param {Function} callback Callback which is called with a boolean 'exists'.
 */
Instance.prototype.exists = function(callback) {
  fs.stat(this.root, function(err, stats) {
    if (!err && stats.isDirectory()) {
      callback(true);
      return;
    }

    callback(false);
    return;
  });
};

/**
 * Get the name of the bundle used by an instance. This is probably the
 * nastiest part of this whole instance filesystem layout because it requires
 * reading a link. To keep things fast this gets cached on the Instance object.
 *
 * @param {Function} callback Callback which is called with (err, bundleName).
 */
Instance.prototype.getBundleName = function(callback) {
  var self = this;
  if (self._bundleName) {
    process.nextTick(function() {
      callback(null, self._bundleName);
    });
  }
  else {
    var bundleLinkPath = path.join(self.root, 'bundle');
    fs.readlink(bundleLinkPath, function(err, target) {
      if (!err) {
        self._bundleName = path.basename(target);
      }
      callback(err, self._bundleName);
      return;
    });
  }
};

/**
 * Get the version pointed to by the 'current' link. This will give a value of
 * 'null' if the current link doesn't exist (currently it should always exist
 * but we may want to introduce situations where it won't in the future).
 *
 * @param {Function} callback Callback which is called with (version).
 */
Instance.prototype.getBundleVersion = function(callback) {
  var self = this;
  var currentLinkPath = path.join(self.root, 'current');
  fs.readlink(currentLinkPath, function(err, target) {
    if (err) {
      callback(null);
      return;
    }
    else {
      callback(target.split('@', 2)[1]);
      return;
    }
  });
};

/**
 * Get the path at which a version of this instance would reside. This does
 * not guarantee that this version exists, merely returns the hypothetical
 * path at which it would reside.
 *
 * @param {String} version    The version to get the path to.
 * @param {Function} callback A callback that takes (err, versionPath).
 */
Instance.prototype.getVersionPath = function(version, callback) {
  var self = this;
  self.getBundleName(function(err, bundleName) {
    var bundleNameFull = bundleName + '@' + version;
    var versionPath = path.join(self.root, 'versions', bundleNameFull);
    callback(err, versionPath);
    return;
  });
};

/**
 * Get the path at which the extracted bundle for the specified version would
 * reside. Again, this doesn't verify that the path actually exists.
 *
 * @param {String} version    The version to get the path to.
 * @param {Function} callback A callback that takes (err, bundleVersionPath).
 */
Instance.prototype.getBundleVersionPath = function(version, callback) {
  var self = this;
  self.getBundleName(function(err, bundleName) {
    var bundleNameFull = bundleName + '@' + version;
    var bundleVersionPath = path.join(config.get()['extracted_dir'], bundleName, bundleNameFull);
    callback(err, bundleVersionPath);
    return;
  });
};

/**
 * Remove all data related to an instance, including the instance, its data,
 * and any services. This performs very little validation (doesn't even check
 * that the instance exists first), it just starts deleting stuff.
 *
 * @param {Function} callback Callback which is called when the cleanup process has finished.
 */
Instance.prototype.destroy = function(callback) {
  var manager = serviceManagement.getDefaultManager().getManager();

  var ops = [
    async.apply(flowctrl.callIgnoringError, fsutil.rmtree, null, this.root)
  ];

  manager.getService(this.name, function(err, service) {
    if (!err) {
      ops.push(async.apply(flowctrl.callIgnoringError, service.destroy, service));
    }

    async.parallel(ops, function(err) {
      callback();
    });
  });
};

/**
 * Given a bundle version, hard hard link all the files into place, render
 * any templates and resolve all data files.
 *
 * @param {String} version    The version of the bundle to prepare.
 * @param {Function} callback A callback fired with (err).
 */
Instance.prototype.prepareVersion = function(version, callback) {
  var self = this;
  var extractedBundlePath, bundleName, bundleNameFull, manifestObj;
  var instanceVersionPath, ignoredPaths;

  var bundleLinkPath = path.join(self.root, 'bundle');
  var hookEnv = { 'CAST_INSTANCE_NAME': self.name };

  async.series([
    // Resolve the path at which the version will reside
    function(callback) {
      self.getVersionPath(version, function(err, vp) {
        instanceVersionPath = vp;
        callback(err);
        return;
      });
    },

    // Resolve the path to the extracted bundle
    function(callback) {
      self.getBundleVersionPath(version, function(err, bvp) {
        extractedBundlePath = bvp;
        callback(err);
        return;
      });
    },

    // Make sure this version doesn't exist
    function(callback) {
      path.exists(instanceVersionPath, function(exists) {
        var err = null;
        if (exists) {
          err = new Error(sprintf('Instance \'%s\' already has version \'%s\'', self.name, version));
        }
        callback(err);
        return;
      });
    },

    // Make sure an extracted bundle exists for the requested version
    function(callback) {
      fs.stat(extractedBundlePath, function(err, stats) {
        if (!err && !stats.isDirectory()) {
          err = new Error('No bundle for version \'' + version + '\'');
        }
        callback(err);
        return;
      });
    },

    // Create the directory for the target version
    function(callback) {
      fsutil.ensureDirectory(instanceVersionPath, callback);
    },

    // Retrieve the manifest object
    function(callback) {
      var manifestPath = path.join(extractedBundlePath, manifestConstants.MANIFEST_FILENAME);
      manifest.getManifestObject(manifestPath, true, function(err, _manifestObj) {
        manifestObj = _manifestObj;
        ignoredPaths = manifestObj['template_files'].concat(manifestObj['data_files']);
        callback(err);
        return;
      });
    },

    // Mirror the directory structure from the extracted bundle in the instance directory
    function(callback) {
      fsutil.treeToTemplate(extractedBundlePath, ignoredPaths, function(err, templateObject) {
        if (err) {
          callback(err);
          return;
        }

        // No directories
        if (Object.keys(templateObject).length === 0) {
          callback();
          return;
        }

        fsutil.templateToTree(instanceVersionPath, templateObject, true, callback);
      });
    },

    // Realize (render and save) templates
    function(callback) {
      var instanceData = deployTemplates.getInstanceTemplateObject(self.name, instanceVersionPath);
      deployTemplates.realizeApplicationTemplates(manifestObj, instanceData, extractedBundlePath,
                                                     instanceVersionPath, callback);
    },

    // Hard-link all the files (except template and data files)
    function(callback) {
      fsutil.hardLinkFiles(extractedBundlePath, instanceVersionPath, ignoredPaths, callback);
    },

    // Resolve the data files
    function(callback) {
      var instanceDataRoot = path.join(self.root, 'data');
      deployFiles.resolveDataFiles(extractedBundlePath, instanceDataRoot, instanceVersionPath,
                                      manifestObj['data_files'], callback);
    },

    // Execute the 'post_prepare' hook
    function(callback) {
      var hook = new hooks.InstanceHook('post', 'post_prepare',
                                        instanceVersionPath, false, hookEnv);
      hook.execute(null, [version], callback);
    }
  ], callback);
};

/**
 * Point the 'current' symlink to the specified version. Will verify
 * that the specified version exists before taking action.
 *
 * @param {String} version    The version to activate.
 * @param {Function} callback A callback fired with (err).
 */
Instance.prototype.activateVersion = function(version, callback) {
  var self = this;
  var newVersionPath;

  var newVersionLink = path.join(self.root, 'new');
  var currentVersionLink = path.join(self.root, 'current');
  var hookEnv = { 'CAST_INSTANCE_NAME': self.name };

  async.series([
    // Get the path to the specified version
    function(callback) {
      self.getVersionPath(version, function(err, vp) {
        newVersionPath = vp;
        callback(err);
        return;
      });
    },

    // Make sure the version exists
    function(callback) {
      path.exists(newVersionPath, function(exists) {
        var err = null;
        if (!exists) {
          err = new Error('Cannot activate nonexistent version \'' + version + '\'');
        }

        callback(err);
        return;
      });
    },

    function(callback) {
      var hook = new hooks.InstanceHook('post', 'pre_version_activate',
                                        newVersionPath, false, hookEnv);
      hook.execute(null, [version, newVersionPath], callback);
    },

    // Create the new link
    function(callback) {
      fs.symlink(path.resolve(newVersionPath), newVersionLink, callback);
    },

    // Atomically move it into place
    async.apply(fs.rename, newVersionLink, currentVersionLink),

    function(callback) {
      var hook = new hooks.InstanceHook('post', 'post_version_activate',
                                        newVersionPath, false, hookEnv);
      hook.execute(null, [version, newVersionPath], callback);
    }
  ], callback);
};

/**
 * Create an instance with a specified name for the specified bundle. The
 * instance will initially use the specified version of the bundle.
 *
 * @param {String} instanceName  A name to give to the instance.
 * @param {String} bundleName    The name of the bundle to use.
 * @param {String} bundleVersion The version of the bundle to use.
 * @param {Function} callback     A callback fired with (err).
 */
exports.createInstance = function(instanceName, bundleName, bundleVersion, callback) {
  if (!INSTANCE_NAME_RE.exec(instanceName)) {
    callback(new Error('Invalid instance name'));
    return;
  }

  var manifestObj, ignoredPaths, instance;
  var bundleNameFull = bundleName + '@' + bundleVersion;

  // Get the paths to the extracted bundle and the manifest
  var extractedBundleRoot = path.join(config.get()['extracted_dir'], bundleName);
  var extractedBundlePath = path.join(extractedBundleRoot, bundleNameFull);

  var instancePath = path.join(config.get()['app_dir'], instanceName);
  var instanceDataRoot = path.join(instancePath, 'data');
  var instanceVersionsRoot = path.join(instancePath, 'versions');
  var instanceBundleLink = path.join(instancePath, 'bundle');
  var instanceCurrentLink = path.join(instancePath, 'current');
  var instanceVersionPath = path.join(instanceVersionsRoot, bundleNameFull);

  async.series([
    // Make sure that the extracted bundle path exists
    function(callback) {
      path.exists(extractedBundlePath, function(exists) {
        if (!exists) {
          callback(new Error('Invalid bundle name or version'));
          return;
        }

        callback();
      });
    },

    // Make sure there isn't already an instance with this name
    function(callback) {
      path.exists(instancePath, function(exists) {
        if (exists) {
          callback(new Error('Instance name already in use'));
          return;
        }
        callback();
      });
    },

    // Create the instance directory
    function(callback) {
      fsutil.ensureDirectory(instancePath, function(err) {
        if (!err) {
          instance = new Instance(instanceName);
        }
        callback(err);
      });
    },

    // Create the data and versions directories
    async.apply(fsutil.ensureDirectory, instanceDataRoot),
    async.apply(fsutil.ensureDirectory, instanceVersionsRoot),

    // Create the bundle symlink
    async.apply(fs.symlink, path.resolve(extractedBundleRoot), instanceBundleLink),

    // Prepare the specified version
    function(callback) {
      instance.prepareVersion(bundleVersion, callback);
    },

    // Symlink it
    function(callback) {
      instance.activateVersion(bundleVersion, callback);
    },

    // Retrieve the manifest object
    function(callback) {
      var manifestPath = path.join(extractedBundlePath, manifestConstants.MANIFEST_FILENAME);
      manifest.getManifestObject(manifestPath, true, function(err, _manifestObj) {
        manifestObj = _manifestObj;
        callback(err);
        return;
      });
    },

    // Create a runit service
    function(callback) {
      deployServices.createService(instanceName, instanceVersionPath, manifestObj, callback);
    }
  ],

  function(error) {
    if (error && instance) {
      instance.destroy(function() {
        callback(error);
        return;
      });
    }
    else {
      callback(error);
    }
  });
};

/**
 * Get an Instance object for an instance whose existence is verified.
 *
 * @param {String} instanceName  The name of the instance to retrieve.
 * @param {Function} callback     A callback that takes (err, instance).
 */
exports.getInstance = function(instanceName, callback) {
  var instance = new Instance(instanceName);
  instance.exists(function(exists) {
    if (!exists) {
      callback(new Error('Instance doesn\'t exist'));
      return;
    }
    else {
      callback(null, instance);
      return;
    }
  });
};

/**
 * Get a list of all instances (in the form of Instance objects).
 *
 * @param {Function} callback A callback that takes (err, instances).
 */
exports.getInstanceList = function(callback) {
  fs.readdir(config.get()['app_dir'], function(err, files) {
    if (err) {
      callback(err);
      return;
    }

    // Construct an Instance for each file in the app_dir
    var instanceList = files.map(function(file) {
      return new Instance(file);
    });

    // Make sure each instance actually exists (ie, isn't a non-directory of some sort)
    async.filter(instanceList, function(instance, callback) {
      instance.exists(callback);
    },
    function(instances) {
      callback(null, instances);
      return;
    });
  });
};

exports.Instance = Instance;
<|endoftext|>


<commit_before>var DockerEvents = require('docker-events')
var SPM = require('spm-metrics-js')
var SpmAgent = require('spm-agent')
var os = require('os')

function DockerEventForwarder (docker) {
  var Docker = require('dockerode')
  try {
    this.docker = docker || new Docker()
    this.dockerEvents = new DockerEvents({docker: this.docker})
  } catch (dockerError) {
    SpmAgent.Logger.log('error', 'Error in sendEvent(): ' + err)
  }

  this.spm = new SPM(SpmAgent.Config.tokens.spm, 0)
  this.spm.on('error', function (err) {
    try {
      SpmAgent.Logger.log('error', 'Error in DockerEventForwarder: ' + err)
    } catch (err2) {
      console.log(err2)
      console.log(err)
    }
  })
  var self = this
  this.sendEvent = function (type, msg) {
    msg.creator = process.env.HOSTNAME || os.hostname()
    self.spm.sendEvent(type, msg, function (err, res) {
      if (err) {
        console.log('Error sending event to SPM ' + err)
      } else {
        if (res && res.statusCode > 299) {
          console.log('Error sending event to SPM ' + res.body)
        }
      }
    })
  }
  this.forwardEvent = function (dockerEvent) {
    try {
      if (dockerEvent.Type) {
        if (!dockerEvent.status) {
          dockerEvent.status = dockerEvent.Type + ' ' + (dockerEvent.Action || '')
        }
        if (dockerEvent.Actor && dockerEvent.Actor.Attributes) {
          dockerEvent.id = dockerEvent.Actor.Attributes.container
          dockerEvent.message = ''
          Object.keys(dockerEvent.Actor.Attributes).forEach(function (key, i) {
            if (i > 0) {
              dockerEvent.message += ', '
            }
            dockerEvent.message += key + ':' + dockerEvent.Actor.Attributes[key]
          })
        }
      }
      var msgStr = dockerEvent.status + ' ' + (dockerEvent.from || '') + ' ' + (dockerEvent.id || '') + ' ' + (dockerEvent.message || '')
      console.log('Docker Event: ' + msgStr)
      var msg = {
        name: dockerEvent.from,
        message: msgStr,
        tags: ['docker', process.env.HOSTNAME || os.hostname(), dockerEvent.status, dockerEvent.id ]
      }
      var type = (dockerEvent.status || 'docker')
      type = type.replace(/[\W]/gi, '_')
      if (/exec_.*/.test(type)) {
        type = 'exec'
      }
      self.sendEvent(type, msg)
    } catch (err) {
      console.error(err)
    }
  }
  setTimeout(function () {
    self.docker.version(function (err, data) {
      if (!err) {
        self.sendEvent('docker-info', { message: 'Docker: ' + data.Version + ' API: ' + data.ApiVersion + ' Kernel: ' + data.KernelVersion }, function (err, res) {
          if (err) {
            SpmAgent.Logger.log('error', 'Error in sendEvent(): ' + err)
          }
        })
      }
    })
  }, 500)
  this.dockerEvents.on('connect', function () {
    self.dockerEvents.on('_message', self.forwardEvent)
  })
  this.dockerEvents.on('disconnect', function () {
    self.dockerEvents.removeListener('_message', self.forwardEvent)
  })
  this.dockerEvents.start()
  return this
}

module.exports = DockerEventForwarder
<commit_msg>log message for Docker API error<commit_after>var DockerEvents = require('docker-events')
var SPM = require('spm-metrics-js')
var SpmAgent = require('spm-agent')
var os = require('os')

function DockerEventForwarder (docker) {
  var Docker = require('dockerode')
  try {
    this.docker = docker || new Docker()
    this.dockerEvents = new DockerEvents({docker: this.docker})
  } catch (dockerError) {
    SpmAgent.Logger.log('error', 'Error in sendEvent(): ' + err)
  }

  this.spm = new SPM(SpmAgent.Config.tokens.spm, 0)
  this.spm.on('error', function (err) {
    try {
      SpmAgent.Logger.log('error', 'Error in DockerEventForwarder: ' + err)
    } catch (err2) {
      console.log(err2)
      console.log(err)
    }
  })
  var self = this
  this.sendEvent = function (type, msg) {
    msg.creator = process.env.HOSTNAME || os.hostname()
    self.spm.sendEvent(type, msg, function (err, res) {
      if (err) {
        console.log('Error sending event to SPM ' + err)
      } else {
        if (res && res.statusCode > 299) {
          console.log('Error sending event to SPM ' + res.body)
        }
      }
    })
  }
  this.forwardEvent = function (dockerEvent) {
    try {
      if (dockerEvent.Type) {
        if (!dockerEvent.status) {
          dockerEvent.status = dockerEvent.Type + ' ' + (dockerEvent.Action || '')
        }
        if (dockerEvent.Actor && dockerEvent.Actor.Attributes) {
          dockerEvent.id = dockerEvent.Actor.Attributes.container
          dockerEvent.message = ''
          Object.keys(dockerEvent.Actor.Attributes).forEach(function (key, i) {
            if (i > 0) {
              dockerEvent.message += ', '
            }
            dockerEvent.message += key + ':' + dockerEvent.Actor.Attributes[key]
          })
        }
      }
      var msgStr = dockerEvent.status + ' ' + (dockerEvent.from || '') + ' ' + (dockerEvent.id || '') + ' ' + (dockerEvent.message || '')
      console.log('Docker Event: ' + msgStr)
      var msg = {
        name: dockerEvent.from,
        message: msgStr,
        tags: ['docker', process.env.HOSTNAME || os.hostname(), dockerEvent.status, dockerEvent.id ]
      }
      var type = (dockerEvent.status || 'docker')
      type = type.replace(/[\W]/gi, '_')
      if (/exec_.*/.test(type)) {
        type = 'exec'
      }
      self.sendEvent(type, msg)
    } catch (err) {
      console.error(err)
    }
  }
  setTimeout(function () {
    self.docker.version(function (err, data) {
      if (!err) {
        self.sendEvent('docker-info', { message: 'Docker: ' + data.Version + ' API: ' + data.ApiVersion + ' Kernel: ' + data.KernelVersion }, function (err, res) {
          if (err) {
            SpmAgent.Logger.log('error', 'Error in sendEvent(): ' + err)
          }
        })
      }
    })
  }, 500)
  this.dockerEvents.on('connect', function () {
    self.dockerEvents.on('_message', self.forwardEvent)
  })
  this.dockerEvents.on('disconnect', function () {
    self.dockerEvents.removeListener('_message', self.forwardEvent)
  })
  this.dockerEvents.on('error', function (err) {
    SpmAgent.Logger.log('error', 'Error in DockerEventsForwarder: ' + err)
  })
  this.dockerEvents.start()
  return this
}

module.exports = DockerEventForwarder
<|endoftext|>


<commit_before>var Module = null;

function JSMESS(canvas, module, output, game, callback) {
  var js_data;
  var moduledata;
  var requests = [];
  var drawloadingtimer;
  var file_countdown;
  var spinnerrot = 0;
  var splashimg = new Image();
  var spinnerimg = new Image();

  this.setcallback = function(_callback) {
    callback = _callback;
    return this;
  }

  this.setmodule = function(_module) {
    module = _module;
    return this;
  }

  this.setgame = function(_game) {
    game = _game;
    return this;
  }

  var draw_loading_status = function() {
    var context = canvas.getContext('2d');
    context.clearRect(0, 0, canvas.width, canvas.height);
    context.drawImage(splashimg, canvas.width / 2 - (splashimg.width / 2), canvas.height / 3 - (splashimg.height / 2));
    var spinnerpos = (canvas.height / 2 + splashimg.height / 2) + 16;
    context.save();
    context.translate((canvas.width / 2), spinnerpos);
    context.rotate(spinnerrot);
    context.drawImage(spinnerimg, -(64/2), -(64/2), 64, 64);
    context.restore();
    spinnerrot += .25;
  };

  var progress_fetch_file = function(e) {
    if (e.lengthComputable) {
      e.target.progress = e.loaded / e.total;
      e.target.loaded = e.loaded;
      e.target.total = e.total;
      e.target.lengthComputable = e.lengthComputable;
    }
  };

  var fetch_file = function(title, url, cb, rt, raw, unmanaged) {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);
    xhr.responseType = rt ? rt : 'arraybuffer';
    xhr.onload = function(e) {
      if (xhr.status != 200) {
        return;
      }
      if (!unmanaged) {
        xhr.progress = 1.0;
      }
      var ints = raw ? xhr.response :  new Int8Array(xhr.response);
      cb(ints);
    };
    if (!unmanaged) {
      xhr.onprogress = progress_fetch_file;
      xhr.title = title;
      xhr.progress = 0;
      xhr.total = 0;
      xhr.loaded = 0;
      xhr.lengthComputable = false;
      requests.push(xhr);
    }
    xhr.send();
  };

  var update_countdown = function() {
    file_countdown -= 1
    if (file_countdown <= 0) {
      window.clearInterval(drawloadingtimer);
      var headID = document.getElementsByTagName('head')[0];
      var newScript = document.createElement('script');
      newScript.type = 'text/javascript';
      newScript.text = js_data;
      headID.appendChild(newScript);
    }
  };

  var init_module = function() {
    var modulecfg = JSON.parse(moduledata);

    var game_file = null;
    var bios_filenames = modulecfg['bios_filenames'];
    var bios_files = {};

    var nr = modulecfg['native_resolution'];

    var arguments = [
      modulecfg['driver'],
      '-verbose',
      '-rompath','.',
      '-window',
      '-resolution', nr[0]+'x' + nr[1],
      '-nokeepaspect'
    ];
    if (game) {
      arguments.push('-' + modulecfg['peripherals'][0], game.replace(/\//g,'_'))
    }
    if (modulecfg['extra_args']) {
      arguments = arguments.concat(modulecfg['extra_args'])
    }

    Module = {
      arguments: arguments,
      print: (function() {
        return function(text) {
          if (!output) {
            return;
          }
          text = text.replace(/&/g, '&');
          text = text.replace(/</g, '<');
          text = text.replace(/>/g, '>');
          text = text.replace('\n', '
', 'g');
          output.innerHTML += text + '
';
          output.scrollTop = output.scrollHeight;
        };
      })(),
      canvas: canvas,
      noInitialRun: false,
      preInit: function() {
        // Load the downloaded binary files into the filesystem.
        for (var bios_fname in bios_files) {
          if (bios_files.hasOwnProperty(bios_fname)) {
            Module['FS_createDataFile']('/', bios_fname, bios_files[bios_fname], true, true);
          }
        }
        if (game) {
            Module['FS_createDataFile']('/', game.replace(/\//g,'_'), game_file, true, true);
        }
        if (callback) {
          modulecfg.canvas = canvas;
          window.setTimeout(function() {callback(modulecfg)}, 0);
        }
      }
    };

    bios_filenames = bios_filenames.filter(String);
    file_countdown = bios_filenames.length + game ? 1 : 0 + 1

    // Fetch the BIOS and the game we want to run.
    for (var i=0; i < bios_filenames.length; i++) {
      var fname = bios_filenames[i];
      fetch_file('Bios', fname, function(data) { bios_files[fname] = data; update_countdown(); });
    }

    if (game) {
      fetch_file('Game', game, function(data) { game_file = data; update_countdown(); });
    }

    fetch_file('Javascript', modulecfg['js_filename'], function(data) { js_data = data; update_countdown(); }, 'text', true);
  };

  var keyevent = function(e) {
    if (e.which == 32) {
      e.preventDefault();
      window.removeEventListener('keypress', keyevent);
      drawloadingtimer = window.setInterval(draw_loading_status, 1000/60);
      fetch_file('ModuleInfo', module + '.json', function(data) { moduledata = data; init_module(); }, 'text', true, true);
    }
  }

  var drawsplash = function() {
    var context = canvas.getContext('2d');
    context.clearRect(0, 0, canvas.width, canvas.height);
    splashimg.onload = function(){
      context.drawImage(splashimg, canvas.width / 2 - (splashimg.width / 2), canvas.height / 3 - (splashimg.height / 2));
      context.font = '18px sans-serif';
      context.fillStyle = 'Black';
      context.textAlign = 'center';
      context.fillText('press space', canvas.width / 2, (canvas.height / 2) + (splashimg.height / 2));
      context.textAlign = 'start';
    };
    spinnerimg.onload = function() { splashimg.src = 'splash.png'; }
    spinnerimg.src = 'spinner.png';
  }

  window.addEventListener('keypress', keyevent);
  drawsplash();
}
<commit_msg>Fix being considered loaded too early.<commit_after>var Module = null;

function JSMESS(canvas, module, output, game, callback) {
  var js_data;
  var moduledata;
  var requests = [];
  var drawloadingtimer;
  var file_countdown;
  var spinnerrot = 0;
  var splashimg = new Image();
  var spinnerimg = new Image();

  this.setcallback = function(_callback) {
    callback = _callback;
    return this;
  }

  this.setmodule = function(_module) {
    module = _module;
    return this;
  }

  this.setgame = function(_game) {
    game = _game;
    return this;
  }

  var draw_loading_status = function() {
    var context = canvas.getContext('2d');
    context.clearRect(0, 0, canvas.width, canvas.height);
    context.drawImage(splashimg, canvas.width / 2 - (splashimg.width / 2), canvas.height / 3 - (splashimg.height / 2));
    var spinnerpos = (canvas.height / 2 + splashimg.height / 2) + 16;
    context.save();
    context.translate((canvas.width / 2), spinnerpos);
    context.rotate(spinnerrot);
    context.drawImage(spinnerimg, -(64/2), -(64/2), 64, 64);
    context.restore();
    spinnerrot += .25;
  };

  var progress_fetch_file = function(e) {
    if (e.lengthComputable) {
      e.target.progress = e.loaded / e.total;
      e.target.loaded = e.loaded;
      e.target.total = e.total;
      e.target.lengthComputable = e.lengthComputable;
    }
  };

  var fetch_file = function(title, url, cb, rt, raw, unmanaged) {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);
    xhr.responseType = rt ? rt : 'arraybuffer';
    xhr.onload = function(e) {
      if (xhr.status != 200) {
        return;
      }
      if (!unmanaged) {
        xhr.progress = 1.0;
      }
      var ints = raw ? xhr.response :  new Int8Array(xhr.response);
      cb(ints);
    };
    if (!unmanaged) {
      xhr.onprogress = progress_fetch_file;
      xhr.title = title;
      xhr.progress = 0;
      xhr.total = 0;
      xhr.loaded = 0;
      xhr.lengthComputable = false;
      requests.push(xhr);
    }
    xhr.send();
  };

  var update_countdown = function() {
    file_countdown -= 1
    if (file_countdown <= 0) {
      window.clearInterval(drawloadingtimer);
      var headID = document.getElementsByTagName('head')[0];
      var newScript = document.createElement('script');
      newScript.type = 'text/javascript';
      newScript.text = js_data;
      headID.appendChild(newScript);
    }
  };

  var init_module = function() {
    var modulecfg = JSON.parse(moduledata);

    var game_file = null;
    var bios_filenames = modulecfg['bios_filenames'];
    var bios_files = {};

    var nr = modulecfg['native_resolution'];

    var arguments = [
      modulecfg['driver'],
      '-verbose',
      '-rompath','.',
      '-window',
      '-resolution', nr[0]+'x' + nr[1],
      '-nokeepaspect'
    ];
    if (game) {
      arguments.push('-' + modulecfg['peripherals'][0], game.replace(/\//g,'_'))
    }
    if (modulecfg['extra_args']) {
      arguments = arguments.concat(modulecfg['extra_args'])
    }

    Module = {
      arguments: arguments,
      print: (function() {
        return function(text) {
          if (!output) {
            return;
          }
          text = text.replace(/&/g, '&');
          text = text.replace(/</g, '<');
          text = text.replace(/>/g, '>');
          text = text.replace('\n', '
', 'g');
          output.innerHTML += text + '
';
          output.scrollTop = output.scrollHeight;
        };
      })(),
      canvas: canvas,
      noInitialRun: false,
      preInit: function() {
        // Load the downloaded binary files into the filesystem.
        for (var bios_fname in bios_files) {
          if (bios_files.hasOwnProperty(bios_fname)) {
            Module['FS_createDataFile']('/', bios_fname, bios_files[bios_fname], true, true);
          }
        }
        if (game) {
            Module['FS_createDataFile']('/', game.replace(/\//g,'_'), game_file, true, true);
        }
        if (callback) {
          modulecfg.canvas = canvas;
          window.setTimeout(function() {callback(modulecfg)}, 0);
        }
      }
    };

    bios_filenames = bios_filenames.filter(String);
    file_countdown = bios_filenames.length + (game ? 1 : 0) + 1

    // Fetch the BIOS and the game we want to run.
    for (var i=0; i < bios_filenames.length; i++) {
      var fname = bios_filenames[i];
      fetch_file('Bios', fname, function(data) { bios_files[fname] = data; update_countdown(); });
    }

    if (game) {
      fetch_file('Game', game, function(data) { game_file = data; update_countdown(); });
    }

    fetch_file('Javascript', modulecfg['js_filename'], function(data) { js_data = data; update_countdown(); }, 'text', true);
  };

  var keyevent = function(e) {
    if (e.which == 32) {
      e.preventDefault();
      window.removeEventListener('keypress', keyevent);
      drawloadingtimer = window.setInterval(draw_loading_status, 1000/60);
      fetch_file('ModuleInfo', module + '.json', function(data) { moduledata = data; init_module(); }, 'text', true, true);
    }
  }

  var drawsplash = function() {
    var context = canvas.getContext('2d');
    context.clearRect(0, 0, canvas.width, canvas.height);
    splashimg.onload = function(){
      context.drawImage(splashimg, canvas.width / 2 - (splashimg.width / 2), canvas.height / 3 - (splashimg.height / 2));
      context.font = '18px sans-serif';
      context.fillStyle = 'Black';
      context.textAlign = 'center';
      context.fillText('press space', canvas.width / 2, (canvas.height / 2) + (splashimg.height / 2));
      context.textAlign = 'start';
    };
    spinnerimg.onload = function() { splashimg.src = 'splash.png'; }
    spinnerimg.src = 'spinner.png';
  }

  window.addEventListener('keypress', keyevent);
  drawsplash();
}
<|endoftext|>


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

import React from 'react';
import { StyleSheet } from 'react-native';
import PropTypes from 'prop-types';
import autobind from 'react-autobind';
import { Expansion } from 'react-native-data-table';
import { dataTableStyles, expansionPageStyles } from '../../globalStyles';
import { PageButton, ExpiryTextInput, PageInfo } from '../../widgets';
import { GenericPage } from '../GenericPage';
import { parsePositiveInteger } from '../../utilities';
import { tableStrings, buttonStrings } from '../../localization';

/**
* Renders page to be displayed in StocktakeEditPage -> expansion.
* @prop   {Realm}               database        App wide database.
* @prop   {Realm.object}        stocktakeItem   The stocktakeItem, a parent of
*                                               StocktakeBatches in this expansion
*/
export class StocktakeEditExpansion extends React.Component {
  constructor(props) {
    super(props);
    this.state = {};
    autobind(this);
  }

  onEndEditing(key, stocktakeBatch, newValue) {
    this.props.database.write(() => {
      switch (key) {
        case 'countedTotalQuantity': {
          const newCountedQuantity = parsePositiveInteger(newValue);
          if (newCountedQuantity === null) return;

          stocktakeBatch.countedTotalQuantity = parsePositiveInteger(newValue);
          break;
        }
        case 'batch': {
          if (!newValue || newValue === '' ||
              newValue === `(${tableStrings.no_batch_name})`) {
            stocktakeBatch.batch = '';
          } else stocktakeBatch.batch = newValue;
          break;
        }
        case 'expiryDate':
          stocktakeBatch.expiryDate = newValue;
          break;
        default:
          break;
      }
      this.props.database.save('StocktakeBatch', stocktakeBatch);
    });
  }

  refreshData() {
    this.setState({ data: this.props.stocktakeItem.batches });
  }

  renderCell(key, stocktakeBatch) {
    const { stocktake } = this.props.stocktakeItem;
    const isEditable = !stocktake.isFinalised;
    switch (key) {
      default:
        return {
          cellContents: stocktakeBatch[key],
        };
      case 'batch':
        return {
          type: isEditable ? 'editable' : 'text',
          cellContents: stocktakeBatch[key] && stocktakeBatch[key] !== '' ?
                        stocktakeBatch[key] : `(${tableStrings.no_batch_name})`,
        };
      case 'countedTotalQuantity': {
        const emptyCellContents = isEditable ? '' : tableStrings.not_counted;
        return {
          type: isEditable ? 'editable' : 'text',
          cellContents: stocktakeBatch.hasBeenCounted ?
                        stocktakeBatch.countedTotalQuantity : emptyCellContents,
          placeholder: tableStrings.no_change,
        };
      }
      case 'expiryDate': {
        return (
          <ExpiryTextInput
            key={key}
            isEditable={isEditable}
            onEndEditing={(newValue) => {
              this.onEndEditing(key, stocktakeBatch, newValue);
              this.refreshData();
            }}
            text={stocktakeBatch[key]}
            style={dataTableStyles.text}
          />
        );
      }
      case 'difference': {
        const difference = stocktakeBatch.difference;
        const prefix = difference > 0 ? '+' : '';
        return { cellContents: `${prefix}${difference}` };
      }
    }
  }

  renderAddBatchButton() {
    const { stocktakeItem } = this.props;
    const addNewBatch = () => {
      this.props.database.write(() => {
        stocktakeItem.createNewBatch(this.props.database);
      });
      this.refreshData();
    };
    return (
      <PageButton
        text={buttonStrings.add_batch}
        onPress={addNewBatch}
        isDisabled={stocktakeItem.stocktake.isFinalised}
        style={localStyles.addBatchButton}
      />
    );
  }

  renderFooter() {
    return null;
  }

  renderPageInfo() {
    const infoColumns = [
      [
        {
          title: 'By Batch:',
          info: this.props.stocktakeItem.item.name,
        },
      ],
    ];
    return (
      <PageInfo
        columns={infoColumns}
      />
    );
  }

  render() {
    return (
      <Expansion style={dataTableStyles.expansionWithInnerPage}>
        <GenericPage
          data={this.state.data}
          renderCell={this.renderCell}
          refreshData={this.refreshData}
          hideSearchBar={true}
          renderDataTableFooter={() => null} // overrides default generc pages footer
          dontRenderSearchBar={true}
          onEndEditing={this.onEndEditing}
          renderTopLeftComponent={this.renderPageInfo}
          renderTopRightComponent={this.renderAddBatchButton}
          columns={[
            {
              key: 'batch',
              width: 2,
              title: 'BATCH NAME',
              alignText: 'center',
            },
            {
              key: 'expiryDate',
              width: 1,
              title: 'EXPIRY',
              alignText: 'center',
            },
            {
              key: 'snapshotTotalQuantity',
              width: 1,
              title: unwrapText(tableStrings.snapshot_quantity),
              alignText: 'right',
            },
            {
              key: 'countedTotalQuantity',
              width: 1,
              title: unwrapText(tableStrings.actual_quantity),
              alignText: 'right',
            },
            {
              key: 'difference',
              width: 1,
              title: tableStrings.difference,
              alignText: 'right',
            },
          ]}
          dataTypesLinked={['StocktakeBatch']}
          database={this.props.database}
          {...this.props.genericTablePageStyles}
          pageStyles={expansionPageStyles}
        />
      </Expansion>
    );
  }
}

StocktakeEditExpansion.propTypes = {
  database: PropTypes.object,
  genericTablePageStyles: PropTypes.object,
  stocktakeItem: PropTypes.object.isRequired,
};

const unwrapText = (text) => text.replace(/\n/g, ' ');

const localStyles = StyleSheet.create({
  addBatchButton: {
    height: 30,
    width: 90,
  },
});
<commit_msg>Fix stray localisation key<commit_after>/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2016
 */

import React from 'react';
import { StyleSheet } from 'react-native';
import PropTypes from 'prop-types';
import autobind from 'react-autobind';
import { Expansion } from 'react-native-data-table';
import { dataTableStyles, expansionPageStyles } from '../../globalStyles';
import { PageButton, ExpiryTextInput, PageInfo } from '../../widgets';
import { GenericPage } from '../GenericPage';
import { parsePositiveInteger } from '../../utilities';
import { tableStrings, buttonStrings } from '../../localization';

/**
* Renders page to be displayed in StocktakeEditPage -> expansion.
* @prop   {Realm}               database        App wide database.
* @prop   {Realm.object}        stocktakeItem   The stocktakeItem, a parent of
*                                               StocktakeBatches in this expansion
*/
export class StocktakeEditExpansion extends React.Component {
  constructor(props) {
    super(props);
    this.state = {};
    autobind(this);
  }

  onEndEditing(key, stocktakeBatch, newValue) {
    this.props.database.write(() => {
      switch (key) {
        case 'countedTotalQuantity': {
          const newCountedQuantity = parsePositiveInteger(newValue);
          if (newCountedQuantity === null) return;

          stocktakeBatch.countedTotalQuantity = parsePositiveInteger(newValue);
          break;
        }
        case 'batch': {
          if (!newValue || newValue === '' ||
              newValue === `(${tableStrings.no_batch_name})`) {
            stocktakeBatch.batch = '';
          } else stocktakeBatch.batch = newValue;
          break;
        }
        case 'expiryDate':
          stocktakeBatch.expiryDate = newValue;
          break;
        default:
          break;
      }
      this.props.database.save('StocktakeBatch', stocktakeBatch);
    });
  }

  refreshData() {
    this.setState({ data: this.props.stocktakeItem.batches });
  }

  renderCell(key, stocktakeBatch) {
    const { stocktake } = this.props.stocktakeItem;
    const isEditable = !stocktake.isFinalised;
    switch (key) {
      default:
        return {
          cellContents: stocktakeBatch[key],
        };
      case 'batch':
        return {
          type: isEditable ? 'editable' : 'text',
          cellContents: stocktakeBatch[key] && stocktakeBatch[key] !== '' ?
                        stocktakeBatch[key] : `(${tableStrings.no_batch_name})`,
        };
      case 'countedTotalQuantity': {
        const emptyCellContents = isEditable ? '' : tableStrings.not_counted;
        return {
          type: isEditable ? 'editable' : 'text',
          cellContents: stocktakeBatch.hasBeenCounted ?
                        stocktakeBatch.countedTotalQuantity : emptyCellContents,
          placeholder: tableStrings.not_counted,
        };
      }
      case 'expiryDate': {
        return (
          <ExpiryTextInput
            key={key}
            isEditable={isEditable}
            onEndEditing={(newValue) => {
              this.onEndEditing(key, stocktakeBatch, newValue);
              this.refreshData();
            }}
            text={stocktakeBatch[key]}
            style={dataTableStyles.text}
          />
        );
      }
      case 'difference': {
        const difference = stocktakeBatch.difference;
        const prefix = difference > 0 ? '+' : '';
        return { cellContents: `${prefix}${difference}` };
      }
    }
  }

  renderAddBatchButton() {
    const { stocktakeItem } = this.props;
    const addNewBatch = () => {
      this.props.database.write(() => {
        stocktakeItem.createNewBatch(this.props.database);
      });
      this.refreshData();
    };
    return (
      <PageButton
        text={buttonStrings.add_batch}
        onPress={addNewBatch}
        isDisabled={stocktakeItem.stocktake.isFinalised}
        style={localStyles.addBatchButton}
      />
    );
  }

  renderFooter() {
    return null;
  }

  renderPageInfo() {
    const infoColumns = [
      [
        {
          title: 'By Batch:',
          info: this.props.stocktakeItem.itemName,
        },
      ],
    ];
    return (
      <PageInfo
        columns={infoColumns}
      />
    );
  }

  render() {
    return (
      <Expansion style={dataTableStyles.expansionWithInnerPage}>
        <GenericPage
          data={this.state.data}
          renderCell={this.renderCell}
          refreshData={this.refreshData}
          hideSearchBar={true}
          renderDataTableFooter={() => null} // overrides default generc pages footer
          dontRenderSearchBar={true}
          onEndEditing={this.onEndEditing}
          renderTopLeftComponent={this.renderPageInfo}
          renderTopRightComponent={this.renderAddBatchButton}
          columns={[
            {
              key: 'batch',
              width: 2,
              title: 'BATCH NAME',
              alignText: 'center',
            },
            {
              key: 'expiryDate',
              width: 1,
              title: 'EXPIRY',
              alignText: 'center',
            },
            {
              key: 'snapshotTotalQuantity',
              width: 1,
              title: unwrapText(tableStrings.snapshot_quantity),
              alignText: 'right',
            },
            {
              key: 'countedTotalQuantity',
              width: 1,
              title: unwrapText(tableStrings.actual_quantity),
              alignText: 'right',
            },
            {
              key: 'difference',
              width: 1,
              title: tableStrings.difference,
              alignText: 'right',
            },
          ]}
          dataTypesLinked={['StocktakeBatch']}
          database={this.props.database}
          {...this.props.genericTablePageStyles}
          pageStyles={expansionPageStyles}
        />
      </Expansion>
    );
  }
}

StocktakeEditExpansion.propTypes = {
  database: PropTypes.object,
  genericTablePageStyles: PropTypes.object,
  stocktakeItem: PropTypes.object.isRequired,
};

const unwrapText = (text) => text.replace(/\n/g, ' ');

const localStyles = StyleSheet.create({
  addBatchButton: {
    height: 30,
    width: 90,
  },
});
<|endoftext|>


<commit_before>var DockerEvents = require('docker-events')
var SPM = require('spm-metrics-js')
var SpmAgent = require('spm-agent')
var os = require('os')

function DockerEventForwarder (docker) {
  var Docker = require('dockerode')
  this.docker = docker || new Docker({socketPath: '/var/run/docker.sock'})
  this.dockerEvents = new DockerEvents({docker: this.docker})

  this.spm = new SPM(SpmAgent.Config.tokens.spm, 0)
  this.spm.on('error', function (err) {
    try {
      SpmAgent.Logger.log('error', 'Error in sendEvent(): ' + err)
    } catch (err2) {
      console.log(err2)
      console.log(err)
    }
  })
  var self = this
  this.sendEvent = function (type, msg) {
    msg.creator = process.env.HOSTNAME || os.hostname()
    self.spm.sendEvent(type, msg, function (err, res) {
      if (err) {
        console.log('Error sending event to SPM ' + err)
      } else {
        if (res && res.statusCode > 299) {
          console.log('Error sending event to SPM ' + res.body)
        }
      }})
  }
  this.forwardEvent = function (dockerEvent) {
    var msgStr = dockerEvent.status + ' ' + dockerEvent.from + ' ' + dockerEvent.id.substring(0, 12)
    console.log(msgStr)
    var msg = {
      name: dockerEvent.from,
      message: msgStr,
      tags: ['docker', process.env.HOSTNAME || os.hostname(), dockerEvent.status, dockerEvent.id ]
    }
    var type = (dockerEvent.status || 'docker')
    type = type.replace(/[\W]/gi, '_')
    self.sendEvent(type, msg)
  }
  setTimeout(function () {
    self.docker.version(function (err, data) {
      if (!err) {
        self.sendEvent('docker-info', {message: 'Docker: ' + data.Version + ' API: ' + data.ApiVersion + ' Kernel: ' + data.KernelVersion }, function (err, res) {
          if(err) {
            SpmAgent.Logger.log('error', 'Error in sendEvent(): ' + err)
          }
        })
      }
    })
  }, 500)

  this.dockerEvents.on('connect', function () {
    self.dockerEvents.on('_message', self.forwardEvent)
  })
  this.dockerEvents.on('disconnect', function () {
    self.dockerEvents.removeListener('_message', self.forwardEvent)
  })
  this.dockerEvents.start()
  return this
}

module.exports = DockerEventForwarder
<commit_msg>format<commit_after>var DockerEvents = require('docker-events')
var SPM = require('spm-metrics-js')
var SpmAgent = require('spm-agent')
var os = require('os')

function DockerEventForwarder (docker) {
  var Docker = require('dockerode')
  this.docker = docker || new Docker({socketPath: '/var/run/docker.sock'})
  this.dockerEvents = new DockerEvents({docker: this.docker})

  this.spm = new SPM(SpmAgent.Config.tokens.spm, 0)
  this.spm.on('error', function (err) {
    try {
      SpmAgent.Logger.log('error', 'Error in sendEvent(): ' + err)
    } catch (err2) {
      console.log(err2)
      console.log(err)
    }
  })
  var self = this
  this.sendEvent = function (type, msg) {
    msg.creator = process.env.HOSTNAME || os.hostname()
    self.spm.sendEvent(type, msg, function (err, res) {
      if (err) {
        console.log('Error sending event to SPM ' + err)
      } else {
        if (res && res.statusCode > 299) {
          console.log('Error sending event to SPM ' + res.body)
        }
      }})
  }
  this.forwardEvent = function (dockerEvent) {
    var msgStr = dockerEvent.status + ' ' + (dockerEvent.from || '') + ' ' + dockerEvent.id.substring(0, 12)
    console.log(JSON.stringify({
        _type: 'dockerEvent',
        '@timestamp': dockerEvent.time,
        message: msgStr,
        container_id: dockerEvent.id.substring(0, 12),
        image_name: dockerEvent.from,
        event_status: dockerEvent.status
    }))
    var msg = {
      name: dockerEvent.from,
      message: msgStr,
      tags: ['docker', process.env.HOSTNAME || os.hostname(), dockerEvent.status, dockerEvent.id ]
    }
    var type = (dockerEvent.status || 'docker')
    type = type.replace(/[\W]/gi, '_')
    if (/exec_.*/.test(type)) {
      type = 'exec'
    }
    self.sendEvent(type, msg)
  }
  setTimeout(function () {
    self.docker.version(function (err, data) {
      if (!err) {
        self.sendEvent('docker-info', {message: 'Docker: ' + data.Version + ' API: ' + data.ApiVersion + ' Kernel: ' + data.KernelVersion }, function (err, res) {
          if (err) {
            SpmAgent.Logger.log('error', 'Error in sendEvent(): ' + err)
          }
        })
      }
    })
  }, 500)
  this.dockerEvents.on('connect', function () {
    self.dockerEvents.on('_message', self.forwardEvent)
  })
  this.dockerEvents.on('disconnect', function () {
    self.dockerEvents.removeListener('_message', self.forwardEvent)
  })
  this.dockerEvents.start()
  return this
}

module.exports = DockerEventForwarder
<|endoftext|>


<commit_before>var Module = null;

function JSMESS(canvas, module, game, precallback, callback, scale) {
  var js_data;
  var moduledata;
  var requests = [];
  var drawloadingtimer;
  var file_countdown;
  var spinnerrot = 0;
  var splashimg = new Image();
  var spinnerimg = new Image();
  var mute = true;

  var hasaudioinit = function() {
      return !!(typeof Module != 'undefined' && typeof SDL != 'undefined' && SDL.audio);
  }

  var muteonce = function() {
    if(!hasaudioinit()) {
        window.setTimeout(muteonce, 0)
    } else {
        this.setmuted(mute)
    }
  }

  this.setmuted = function(_mute) {
    mute = _mute;
    if (hasaudioinit()) {
        Module.ccall('SDL_PauseAudio', '', ['number'], [mute ? 1 : 0])
    }
    return this;
  }

  this.setscale = function(_scale) {
    scale = _scale
    return this;
  }

  this.setprecallback = function(_precallback) {
    precallback = _precallback;
    return this;
  }

  this.setcallback = function(_callback) {
    callback = _callback;
    return this;
  }

  this.setmodule = function(_module) {
    module = _module;
    return this;
  }

  this.setgame = function(_game) {
    game = _game;
    return this;
  }

  var draw_loading_status = function() {
    var context = canvas.getContext('2d');
    context.clearRect(0, 0, canvas.width, canvas.height);
    context.drawImage(splashimg, canvas.width / 2 - (splashimg.width / 2), canvas.height / 3 - (splashimg.height / 2));
    var spinnerpos = (canvas.height / 2 + splashimg.height / 2) + 16;
    context.save();
    context.translate((canvas.width / 2), spinnerpos);
    context.rotate(spinnerrot);
    context.drawImage(spinnerimg, -(64/2), -(64/2), 64, 64);
    context.restore();
    spinnerrot += .25;
  };

  var progress_fetch_file = function(e) {
    if (e.lengthComputable) {
      e.target.progress = e.loaded / e.total;
      e.target.loaded = e.loaded;
      e.target.total = e.total;
      e.target.lengthComputable = e.lengthComputable;
    }
  };

  var fetch_file = function(title, url, cb, rt, raw, unmanaged) {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);
    xhr.responseType = rt ? rt : 'arraybuffer';
    xhr.onload = function(e) {
      if (xhr.status != 200) {
        return;
      }
      if (!unmanaged) {
        xhr.progress = 1.0;
      }
      var ints = raw ? xhr.response :  new Int8Array(xhr.response);
      cb(ints);
    };
    if (!unmanaged) {
      xhr.onprogress = progress_fetch_file;
      xhr.title = title;
      xhr.progress = 0;
      xhr.total = 0;
      xhr.loaded = 0;
      xhr.lengthComputable = false;
      requests.push(xhr);
    }
    xhr.send();
  };

  var update_countdown = function() {
    file_countdown -= 1
    if (file_countdown <= 0) {
      window.clearInterval(drawloadingtimer);
      var headID = document.getElementsByTagName('head')[0];
      var newScript = document.createElement('script');
      newScript.type = 'text/javascript';
      newScript.text = js_data;
      headID.appendChild(newScript);
    }
  };

  var init_module = function() {
    var modulecfg = JSON.parse(moduledata);

    var game_file = null;
    var bios_filenames = modulecfg['bios_filenames'];
    var bios_files = {};

    var nr = modulecfg['native_resolution'];

    canvas.style.width = (nr[0] * scale) + 'px';
    canvas.style.height = (nr[1] * scale) + 'px';
    canvas.width = nr[0] * scale;
    canvas.height = nr[1] * scale;

    var arguments = [
      modulecfg['driver'],
      '-verbose',
      '-rompath','.',
      '-window',
      '-resolution', nr[0]+'x' + nr[1],
      '-nokeepaspect'
    ];
    if (game) {
      arguments.push('-' + modulecfg['peripherals'][0], game.replace(/\//g,'_'))
    }
    if (modulecfg['extra_args']) {
      arguments = arguments.concat(modulecfg['extra_args'])
    }

    var self = this;

    Module = {
      arguments: arguments,
      SDL_numSimultaneouslyQueuedBuffers: 5,
      print: (function() {
        return function(text) {
          console.log(text);
        };
      })(),
      canvas: canvas,
      noInitialRun: false,
      preInit: function() {
        // Load the downloaded binary files into the filesystem.
        for (var bios_fname in bios_files) {
          if (bios_files.hasOwnProperty(bios_fname)) {
            Module['FS_createDataFile']('/', bios_fname, bios_files[bios_fname], true, true);
          }
        }
        if (game) {
            Module['FS_createDataFile']('/', game.replace(/\//g,'_'), game_file, true, true);
        }
        if (callback) {
          modulecfg.canvas = canvas;
          window.setTimeout(function() {callback(modulecfg)}, 0);
        }
        if (mute) {
          muteonce();
        }
      }
    };

    bios_filenames = bios_filenames.filter(String);
    file_countdown = bios_filenames.length + (game ? 1 : 0) + 1

    // Fetch the BIOS and the game we want to run.
    for (var i=0; i < bios_filenames.length; i++) {
      var fname = bios_filenames[i];
      fetch_file('Bios', 'bios/' + fname, function(data) { bios_files[fname] = data; update_countdown(); });
    }

    if (game) {
      fetch_file('Game', game, function(data) { game_file = data; update_countdown(); });
    }

    fetch_file('Javascript', 'jsmess/' + modulecfg['js_filename'], function(data) { js_data = data; update_countdown(); }, 'text', true);
  };

  var keyevent = function(e) {
    if (e.which == 32) {
      e.preventDefault();
      start();
    }
  }

  this.hasStarted = false;
  var self = this;
  var start = function() {
    self.hasStarted = true;
    ignorecommonkeys();
    window.removeEventListener('keypress', keyevent);
    drawloadingtimer = window.setInterval(draw_loading_status, 1000/60);
    if (precallback) {
      window.setTimeout(function() {precallback()}, 0);
    }
    fetch_file('ModuleInfo', 'json/' + module + '.json', function(data) { moduledata = data; init_module(); }, 'text', true, true);
    return this;
  }
  this.start = start;

  var drawsplash = function() {
    var context = canvas.getContext('2d');
    context.clearRect(0, 0, canvas.width, canvas.height);
    splashimg.onload = function(){
      context.drawImage(splashimg, canvas.width / 2 - (splashimg.width / 2), canvas.height / 3 - (splashimg.height / 2));
      context.font = '18px sans-serif';
      context.fillStyle = 'Black';
      context.textAlign = 'center';
      context.fillText('Press the SPACEBAR to start.', canvas.width / 2, (canvas.height / 2) + (splashimg.height / 2));
      context.textAlign = 'start';
    };
    spinnerimg.onload = function() { splashimg.src = 'splash.png'; }
    spinnerimg.src = 'spinner.png';
  }

  var ignorecommonkeys = function() {
      window.addEventListener('keydown', function(e) {
      if([32, 37, 38, 39, 40].indexOf(e.keyCode) > -1) {
              e.preventDefault();
      }
      }, false);
  }

  window.addEventListener('keypress', keyevent);
  drawsplash();
}
<commit_msg>Fix audio muting issues again.<commit_after>var Module = null;

function JSMESS(canvas, module, game, precallback, callback, scale) {
  var js_data;
  var moduledata;
  var requests = [];
  var drawloadingtimer;
  var file_countdown;
  var spinnerrot = 0;
  var splashimg = new Image();
  var spinnerimg = new Image();
  var mute = true;

  var hasaudioinit = function() {
      return !!(typeof Module != 'undefined' && typeof SDL != 'undefined' && SDL.audio);
  }

  var muteonce = function() {
    if(!hasaudioinit()) {
        window.setTimeout(muteonce, 0)
    } else {
        window.setTimeout(function() {setmuted(mute)}, 0)
    }
  }

  this.setmuted = function(_mute) {
    mute = _mute;

    if (!mute || hasaudioinit()) {
        Module.ccall('SDL_PauseAudio', '', ['number'], [mute ? 1 : 0])
    }
    return this;
  }
  var setmuted = this.setmuted

  this.setscale = function(_scale) {
    scale = _scale
    return this;
  }

  this.setprecallback = function(_precallback) {
    precallback = _precallback;
    return this;
  }

  this.setcallback = function(_callback) {
    callback = _callback;
    return this;
  }

  this.setmodule = function(_module) {
    module = _module;
    return this;
  }

  this.setgame = function(_game) {
    game = _game;
    return this;
  }

  var draw_loading_status = function() {
    var context = canvas.getContext('2d');
    context.clearRect(0, 0, canvas.width, canvas.height);
    context.drawImage(splashimg, canvas.width / 2 - (splashimg.width / 2), canvas.height / 3 - (splashimg.height / 2));
    var spinnerpos = (canvas.height / 2 + splashimg.height / 2) + 16;
    context.save();
    context.translate((canvas.width / 2), spinnerpos);
    context.rotate(spinnerrot);
    context.drawImage(spinnerimg, -(64/2), -(64/2), 64, 64);
    context.restore();
    spinnerrot += .25;
  };

  var progress_fetch_file = function(e) {
    if (e.lengthComputable) {
      e.target.progress = e.loaded / e.total;
      e.target.loaded = e.loaded;
      e.target.total = e.total;
      e.target.lengthComputable = e.lengthComputable;
    }
  };

  var fetch_file = function(title, url, cb, rt, raw, unmanaged) {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);
    xhr.responseType = rt ? rt : 'arraybuffer';
    xhr.onload = function(e) {
      if (xhr.status != 200) {
        return;
      }
      if (!unmanaged) {
        xhr.progress = 1.0;
      }
      var ints = raw ? xhr.response :  new Int8Array(xhr.response);
      cb(ints);
    };
    if (!unmanaged) {
      xhr.onprogress = progress_fetch_file;
      xhr.title = title;
      xhr.progress = 0;
      xhr.total = 0;
      xhr.loaded = 0;
      xhr.lengthComputable = false;
      requests.push(xhr);
    }
    xhr.send();
  };

  var update_countdown = function() {
    file_countdown -= 1
    if (file_countdown <= 0) {
      window.clearInterval(drawloadingtimer);
      var headID = document.getElementsByTagName('head')[0];
      var newScript = document.createElement('script');
      newScript.type = 'text/javascript';
      newScript.text = js_data;
      headID.appendChild(newScript);
    }
  };

  var init_module = function() {
    var modulecfg = JSON.parse(moduledata);

    var game_file = null;
    var bios_filenames = modulecfg['bios_filenames'];
    var bios_files = {};

    var nr = modulecfg['native_resolution'];

    canvas.style.width = (nr[0] * scale) + 'px';
    canvas.style.height = (nr[1] * scale) + 'px';
    canvas.width = nr[0] * scale;
    canvas.height = nr[1] * scale;

    var arguments = [
      modulecfg['driver'],
      '-verbose',
      '-rompath','.',
      '-window',
      '-resolution', nr[0]+'x' + nr[1],
      '-nokeepaspect'
    ];
    if (game) {
      arguments.push('-' + modulecfg['peripherals'][0], game.replace(/\//g,'_'))
    }
    if (modulecfg['extra_args']) {
      arguments = arguments.concat(modulecfg['extra_args'])
    }

    var self = this;

    Module = {
      arguments: arguments,
      SDL_numSimultaneouslyQueuedBuffers: 5,
      print: (function() {
        return function(text) {
          console.log(text);
        };
      })(),
      canvas: canvas,
      noInitialRun: false,
      preInit: function() {
        // Load the downloaded binary files into the filesystem.
        for (var bios_fname in bios_files) {
          if (bios_files.hasOwnProperty(bios_fname)) {
            Module['FS_createDataFile']('/', bios_fname, bios_files[bios_fname], true, true);
          }
        }
        if (game) {
            Module['FS_createDataFile']('/', game.replace(/\//g,'_'), game_file, true, true);
        }
        if (callback) {
          modulecfg.canvas = canvas;
          window.setTimeout(function() {callback(modulecfg)}, 0);
        }
        if (mute) {
          muteonce();
        }
      }
    };

    bios_filenames = bios_filenames.filter(String);
    file_countdown = bios_filenames.length + (game ? 1 : 0) + 1

    // Fetch the BIOS and the game we want to run.
    for (var i=0; i < bios_filenames.length; i++) {
      var fname = bios_filenames[i];
      fetch_file('Bios', 'bios/' + fname, function(data) { bios_files[fname] = data; update_countdown(); });
    }

    if (game) {
      fetch_file('Game', game, function(data) { game_file = data; update_countdown(); });
    }

    fetch_file('Javascript', 'jsmess/' + modulecfg['js_filename'], function(data) { js_data = data; update_countdown(); }, 'text', true);
  };

  var keyevent = function(e) {
    if (e.which == 32) {
      e.preventDefault();
      start();
    }
  }

  this.hasStarted = false;
  var self = this;
  var start = function() {
    self.hasStarted = true;
    ignorecommonkeys();
    window.removeEventListener('keypress', keyevent);
    drawloadingtimer = window.setInterval(draw_loading_status, 1000/60);
    if (precallback) {
      window.setTimeout(function() {precallback()}, 0);
    }
    fetch_file('ModuleInfo', 'json/' + module + '.json', function(data) { moduledata = data; init_module(); }, 'text', true, true);
    return this;
  }
  this.start = start;

  var drawsplash = function() {
    var context = canvas.getContext('2d');
    context.clearRect(0, 0, canvas.width, canvas.height);
    splashimg.onload = function(){
      context.drawImage(splashimg, canvas.width / 2 - (splashimg.width / 2), canvas.height / 3 - (splashimg.height / 2));
      context.font = '18px sans-serif';
      context.fillStyle = 'Black';
      context.textAlign = 'center';
      context.fillText('Press the SPACEBAR to start.', canvas.width / 2, (canvas.height / 2) + (splashimg.height / 2));
      context.textAlign = 'start';
    };
    spinnerimg.onload = function() { splashimg.src = 'splash.png'; }
    spinnerimg.src = 'spinner.png';
  }

  var ignorecommonkeys = function() {
      window.addEventListener('keydown', function(e) {
      if([32, 37, 38, 39, 40].indexOf(e.keyCode) > -1) {
              e.preventDefault();
      }
      }, false);
  }

  window.addEventListener('keypress', keyevent);
  drawsplash();
}
<|endoftext|>


<commit_before>const { promisify } = require('util');
const fs = require('fs');
const path = require('path');
const symlink = promisify(fs.symlink);
const readFile = promisify(fs.readFile);
const writeFile = promisify(fs.writeFile);
const mkdirp = promisify(require('mkdirp'));
const chokidar = require('chokidar');
const chalk = require('chalk');
const globby = require('globby');
const ora = require('ora');
const sharp = require('sharp');
const SVGO = require('svgo');
const { spawnSync } = require('child_process');
const log = require('../utils/log');
const timer = require('../utils/timer');
const { getConfig } = require('../utils/config-store');
const { flattenArray } = require('../utils/general');
let config;

const {
  TRAVIS,
  TRAVIS_BRANCH,
  TRAVIS_PULL_REQUEST_BRANCH,
  TRAVIS_PULL_REQUEST,
} = process.env;

let branchName = 'detached-HEAD';
try {
  branchName = spawnSync('git', ['symbolic-ref', 'HEAD'], {
    encoding: 'utf8',
  })
    .stdout.replace('refs/heads/', '')
    .replace(/\//g, '-')
    .trim();
} catch (error) {
  process.exit(1);
}

if (TRAVIS === 'true') {
  if (TRAVIS_PULL_REQUEST === 'false') {
    branchName = TRAVIS_BRANCH;
  } else {
    branchName = TRAVIS_PULL_REQUEST_BRANCH;
  }
}

const svgo = new SVGO({
  plugins: [
    {
      removeViewBox: false,
    },
    {
      removeXMLNS: false,
    },
  ],
});

// full set of image sizes used by default unless being run on a feature-specific branch
let boltImageSizes = [
  50,
  100,
  200,
  320,
  480,
  640,
  800,
  1024,
  1366,
  1536,
  1920,
  2560,
  2880,
];

// don't resize images to all available options on feature-specific branches to speed up build times
// if (branchName.includes('feature') === true) {
//  boltImageSizes = [320, 640, 1024, 1920];
// }

function makeLocalPath(imagePath) {
  return `${path.join(config.wwwDir, imagePath)}`;
}

function makeWebPath(imagePath) {
  return `${path.relative(config.wwwDir, imagePath)}`;
}

async function writeImageManifest(imgManifest) {
  config = config || (await getConfig());

  await writeFile(
    path.join(config.dataDir, 'images.bolt.json'),
    JSON.stringify(imgManifest, null, '  '),
  );
}

async function processImage(file, set) {
  config = config || (await getConfig());

  if (config.verbosity > 3) {
    log.dim(`Processing image: ${file}`);
  }
  // If `set.base` is `images/` and `file` is `images/header/main.png`, then `fileId` is `header/main.png`
  const fileId = path.relative(set.base, file);
  const newPath = path.join(set.dist, fileId);
  const pathInfo = path.parse(newPath);
  // pathInfo.ext and pathInfo.name are ignored if pathInfo.base exists
  delete pathInfo.base;

  await mkdirp(pathInfo.dir);

  // we want to read the original file once, instead of reading for each size
  const originalFileBuffer = await readFile(file);
  // http://sharp.pixelplumbing.com/en/stable/api-input/#metadata
  const { width, height } = await sharp(originalFileBuffer).metadata();

  // We add `null` to beginning b/c we want the original file too
  // Filter the list to not include sizes bigger than our file
  const sizes = [null, ...boltImageSizes].filter(size => width > size);

  // looping through all sizes and resizing
  return Promise.all(
    sizes.map(async size => {
      const isOrig = size === null; // original file
      if (!isOrig) {
        // no need to resize these file extensions
        if (pathInfo.ext === '.svg' || pathInfo.ext === '.gif') {
          return;
        }
      }

      // Goes on end of filename
      const sizeSuffix = size ? `-${size}` : '';
      const thisPathInfo = Object.assign({}, pathInfo, {
        name: `${pathInfo.name}${sizeSuffix}`,
      });
      const newSizedPath = path.format(thisPathInfo);
      const newSizeWebPath = makeWebPath(newSizedPath);
      const newSizeLocalPath = makeLocalPath(newSizedPath);

      if (config.prod) {
        if (isOrig) {
          await sharp(originalFileBuffer)
            .jpeg({
              quality: 50,
              progressive: true,
              optimiseScans: true,
              force: false,
            })
            .png({
              progressive: true,
              force: false,
              compressionLevel: 9,
            })
            .toFile(newSizeLocalPath);

          if (
            pathInfo.ext === '.jpeg' ||
            pathInfo.ext === '.jpg' ||
            pathInfo.ext === '.png'
          ) {
            await sharp(originalFileBuffer)
              .resize(size)
              .jpeg({
                quality: 50,
                progressive: true,
                optimiseScans: true,
                force: false,
              })
              .png({
                progressive: true,
                force: false,
                compressionLevel: 9,
              })
              .toFile(newSizeLocalPath);
          } else if (pathInfo.ext === '.svg') {
            const result = await svgo.optimize(originalFileBuffer);
            const optimizedSVG = result.data;
            await writeFile(newSizeLocalPath, optimizedSVG);
          } else {
            await sharp(originalFileBuffer)
              .jpeg({
                quality: 50,
                progressive: true,
                optimiseScans: true,
                force: false,
              })
              .png({
                progressive: true,
                force: false,
                compressionLevel: 9,
              })
              .toFile(newSizeLocalPath);
          }
        } else {
          // http://sharp.pixelplumbing.com/en/stable/
          if (
            pathInfo.ext === '.jpeg' ||
            pathInfo.ext === '.jpg' ||
            pathInfo.ext === '.png'
          ) {
            await sharp(originalFileBuffer)
              .resize(size)
              .jpeg({
                quality: 50,
                progressive: true,
                optimiseScans: true,
                force: false,
              })
              .png({
                progressive: true,
                force: false,
                compressionLevel: 9,
              })
              .toFile(newSizeLocalPath);
          } else {
            await sharp(originalFileBuffer)
              .resize(size)
              .toFile(newSizeLocalPath);
          }
        }
      } else {
        // Not prod, so let's be quick.
        // Symlinking works even if the original file is not served
        const symlinkPath = path.relative(thisPathInfo.dir, file);
        try {
          await symlink(symlinkPath, newSizedPath);
        } catch (error) {
          // If it's the error for symlink already exists, we don't care.
          if (error.code !== 'EEXIST') {
            log.errorAndExit(
              `Problem when attempting to symlink ${file} to ${newSizedPath}.`,
              error,
            );
          }
        }
      }

      if (!isOrig) {
        return {
          path: newSizeWebPath,
          size,
        };
      }
    }),
  ).then(resizedImagePaths => {
    // removes `undefined` & other non-truthy values (mainly original images & non processed file types like SVG or GIF)
    const sets = resizedImagePaths.filter(resizedImagePath => resizedImagePath);
    const imageMeta = {
      original: file,
      // width,
      // height,
      fileId,
      src: makeWebPath(newPath),
      sizePaths: sets,
      srcset: sets.map(set => `${set.path} ${set.size}w`).join(', '),
    };

    return imageMeta;
  });
}

async function processImages() {
  config = config || (await getConfig());

  if (!config.images) {
    return;
  }
  const startMessage = chalk.blue('Processing images...');
  const startTime = timer.start();
  let spinner;
  if (config.verbosity > 2) {
    console.log(startMessage);
  } else {
    spinner = ora(startMessage).start();
  }

  return Promise.all(
    config.images.sets.map(async set => {
      const imagePaths = await globby(path.join(set.base, set.glob));
      return Promise.all(
        imagePaths.map(imagePath => processImage(imagePath, set)),
      );
    }),
  ).then(async setsOfImageMetas => {
    // When it's all done
    const imageMetas = flattenArray(setsOfImageMetas);
    const imageManifest = {};
    imageMetas.forEach(imageMeta => {
      imageManifest[imageMeta.src] = imageMeta;
    });
    await writeImageManifest(imageManifest);

    const endMessage = chalk.green(
      `Processed images in ${timer.end(startTime)}`,
    );
    if (config.verbosity > 2) {
      console.log(endMessage);
    } else {
      spinner.succeed(endMessage);
    }
  });
}

module.exports = {
  processImages,
};
<commit_msg>Update image-tasks.js<commit_after>const { promisify } = require('util');
const fs = require('fs');
const path = require('path');
const symlink = promisify(fs.symlink);
const readFile = promisify(fs.readFile);
const writeFile = promisify(fs.writeFile);
const mkdirp = promisify(require('mkdirp'));
const chokidar = require('chokidar');
const chalk = require('chalk');
const globby = require('globby');
const ora = require('ora');
const sharp = require('sharp');
const SVGO = require('svgo');
const { spawnSync } = require('child_process');
const log = require('../utils/log');
const timer = require('../utils/timer');
const { getConfig } = require('../utils/config-store');
const { flattenArray } = require('../utils/general');
let config;

const {
  TRAVIS,
  TRAVIS_BRANCH,
  TRAVIS_PULL_REQUEST_BRANCH,
  TRAVIS_PULL_REQUEST,
} = process.env;

let branchName = 'detached-HEAD';
try {
  branchName = spawnSync('git', ['symbolic-ref', 'HEAD'], {
    encoding: 'utf8',
  })
    .stdout.replace('refs/heads/', '')
    .replace(/\//g, '-')
    .trim();
} catch (error) {
  process.exit(1);
}

if (TRAVIS === 'true') {
  if (TRAVIS_PULL_REQUEST === 'false') {
    branchName = TRAVIS_BRANCH;
  } else {
    branchName = TRAVIS_PULL_REQUEST_BRANCH;
  }
}

const svgo = new SVGO({
  plugins: [
    {
      removeViewBox: false,
    },
    {
      removeXMLNS: false,
    },
  ],
});

// full set of image sizes used by default unless being run on a feature-specific branch
let boltImageSizes = [
  50,
  100,
  200,
  320,
  480,
  640,
  800,
  1024,
  1366,
  1536,
  1920,
  2560,
  2880,
];

// don't resize images to all available options on feature-specific branches to speed up build times
// if (branchName.includes('feature') === true) {
//  boltImageSizes = [320, 640, 1024, 1920];
// }

function makeLocalPath(imagePath) {
  return `${path.join(config.wwwDir, imagePath)}`;
}

function makeWebPath(imagePath) {
  return `${path.relative(config.wwwDir, imagePath)}`;
}

async function writeImageManifest(imgManifest) {
  config = config || (await getConfig());

  await writeFile(
    path.join(config.dataDir, 'images.bolt.json'),
    JSON.stringify(imgManifest, null, '  '),
  );
}

async function processImage(file, set) {
  config = config || (await getConfig());

  if (config.verbosity > 3) {
    log.dim(`Processing image: ${file}`);
  }
  // If `set.base` is `images/` and `file` is `images/header/main.png`, then `fileId` is `header/main.png`
  const fileId = path.relative(set.base, file);
  const newPath = path.join(set.dist, fileId);
  const pathInfo = path.parse(newPath);
  // pathInfo.ext and pathInfo.name are ignored if pathInfo.base exists
  delete pathInfo.base;

  await mkdirp(pathInfo.dir);

  // we want to read the original file once, instead of reading for each size
  const originalFileBuffer = await readFile(file);
  // http://sharp.pixelplumbing.com/en/stable/api-input/#metadata
  const { width, height } = await sharp(originalFileBuffer).metadata();

  // We add `null` to beginning b/c we want the original file too
  // Filter the list to not include sizes bigger than our file
  const sizes = [null, ...boltImageSizes].filter(size => width > size);

  // looping through all sizes and resizing
  return Promise.all(
    sizes.map(async size => {
      const isOrig = size === null; // original file
      if (!isOrig) {
        // no need to resize these file extensions
        if (pathInfo.ext === '.svg' || pathInfo.ext === '.gif') {
          return;
        }
      }

      // Goes on end of filename
      const sizeSuffix = size ? `-${size}` : '';
      const thisPathInfo = Object.assign({}, pathInfo, {
        name: `${pathInfo.name}${sizeSuffix}`,
      });
      const newSizedPath = path.format(thisPathInfo);
      const newSizeWebPath = makeWebPath(newSizedPath);
      const newSizeLocalPath = makeLocalPath(newSizedPath);

      if (config.prod) {
        if (isOrig) {
          await sharp(originalFileBuffer)
            .jpeg({
              quality: 50,
              progressive: true,
              optimiseScans: true,
              force: false,
            })
            .png({
              progressive: true,
              force: false,
              compressionLevel: 9,
            })
            .toFile(newSizedPath);

          if (
            pathInfo.ext === '.jpeg' ||
            pathInfo.ext === '.jpg' ||
            pathInfo.ext === '.png'
          ) {
            await sharp(originalFileBuffer)
              .resize(size)
              .jpeg({
                quality: 50,
                progressive: true,
                optimiseScans: true,
                force: false,
              })
              .png({
                progressive: true,
                force: false,
                compressionLevel: 9,
              })
              .toFile(newSizedPath);
          } else if (pathInfo.ext === '.svg') {
            const result = await svgo.optimize(originalFileBuffer);
            const optimizedSVG = result.data;
            await writeFile(newSizeLocalPath, optimizedSVG);
          } else {
            await sharp(originalFileBuffer)
              .jpeg({
                quality: 50,
                progressive: true,
                optimiseScans: true,
                force: false,
              })
              .png({
                progressive: true,
                force: false,
                compressionLevel: 9,
              })
              .toFile(newSizedPath);
          }
        } else {
          // http://sharp.pixelplumbing.com/en/stable/
          if (
            pathInfo.ext === '.jpeg' ||
            pathInfo.ext === '.jpg' ||
            pathInfo.ext === '.png'
          ) {
            await sharp(originalFileBuffer)
              .resize(size)
              .jpeg({
                quality: 50,
                progressive: true,
                optimiseScans: true,
                force: false,
              })
              .png({
                progressive: true,
                force: false,
                compressionLevel: 9,
              })
              .toFile(newSizedPath);
          } else {
            await sharp(originalFileBuffer)
              .resize(size)
              .toFile(newSizedPath);
          }
        }
      } else {
        // Not prod, so let's be quick.
        // Symlinking works even if the original file is not served
        const symlinkPath = path.relative(thisPathInfo.dir, file);
        try {
          await symlink(symlinkPath, newSizedPath);
        } catch (error) {
          // If it's the error for symlink already exists, we don't care.
          if (error.code !== 'EEXIST') {
            log.errorAndExit(
              `Problem when attempting to symlink ${file} to ${newSizedPath}.`,
              error,
            );
          }
        }
      }

      if (!isOrig) {
        return {
          path: newSizeWebPath,
          size,
        };
      }
    }),
  ).then(resizedImagePaths => {
    // removes `undefined` & other non-truthy values (mainly original images & non processed file types like SVG or GIF)
    const sets = resizedImagePaths.filter(resizedImagePath => resizedImagePath);
    const imageMeta = {
      original: file,
      // width,
      // height,
      fileId,
      src: makeWebPath(newPath),
      sizePaths: sets,
      srcset: sets.map(set => `${set.path} ${set.size}w`).join(', '),
    };

    return imageMeta;
  });
}

async function processImages() {
  config = config || (await getConfig());

  if (!config.images) {
    return;
  }
  const startMessage = chalk.blue('Processing images...');
  const startTime = timer.start();
  let spinner;
  if (config.verbosity > 2) {
    console.log(startMessage);
  } else {
    spinner = ora(startMessage).start();
  }

  return Promise.all(
    config.images.sets.map(async set => {
      const imagePaths = await globby(path.join(set.base, set.glob));
      return Promise.all(
        imagePaths.map(imagePath => processImage(imagePath, set)),
      );
    }),
  ).then(async setsOfImageMetas => {
    // When it's all done
    const imageMetas = flattenArray(setsOfImageMetas);
    const imageManifest = {};
    imageMetas.forEach(imageMeta => {
      imageManifest[imageMeta.src] = imageMeta;
    });
    await writeImageManifest(imageManifest);

    const endMessage = chalk.green(
      `Processed images in ${timer.end(startTime)}`,
    );
    if (config.verbosity > 2) {
      console.log(endMessage);
    } else {
      spinner.succeed(endMessage);
    }
  });
}

module.exports = {
  processImages,
};
<|endoftext|>