<commit_before>'use strict'

const {Buffer} = require('buffer')
const v8Util = process.atomBinding('v8_util')
const {ipcRenderer, isPromise, CallbacksRegistry} = require('electron')

const callbacksRegistry = new CallbacksRegistry()

const remoteObjectCache = v8Util.createIDWeakMap()

// Convert the arguments object into an array of meta data.
const wrapArgs = function (args, visited) {
  if (visited == null) {
    visited = new Set()
  }

  const valueToMeta = function (value) {
    // Check for circular reference.
    if (visited.has(value)) {
      return {
        type: 'value',
        value: null
      }
    }

    if (Array.isArray(value)) {
      visited.add(value)
      let meta = {
        type: 'array',
        value: wrapArgs(value, visited)
      }
      visited.delete(value)
      return meta
    } else if (ArrayBuffer.isView(value)) {
      return {
        type: 'buffer',
        value: Buffer.from(value)
      }
    } else if (value instanceof Date) {
      return {
        type: 'date',
        value: value.getTime()
      }
    } else if ((value != null) && typeof value === 'object') {
      if (isPromise(value)) {
        return {
          type: 'promise',
          then: valueToMeta(function (onFulfilled, onRejected) {
            value.then(onFulfilled, onRejected)
          })
        }
      } else if (v8Util.getHiddenValue(value, 'atomId')) {
        return {
          type: 'remote-object',
          id: v8Util.getHiddenValue(value, 'atomId')
        }
      }

      let meta = {
        type: 'object',
        name: value.constructor != null ? value.constructor.name : '',
        members: []
      }
      visited.add(value)
      for (let prop in value) {
        meta.members.push({
          name: prop,
          value: valueToMeta(value[prop])
        })
      }
      visited.delete(value)
      return meta
    } else if (typeof value === 'function' && v8Util.getHiddenValue(value, 'returnValue')) {
      return {
        type: 'function-with-return-value',
        value: valueToMeta(value())
      }
    } else if (typeof value === 'function') {
      return {
        type: 'function',
        id: callbacksRegistry.add(value),
        location: v8Util.getHiddenValue(value, 'location'),
        length: value.length
      }
    } else {
      return {
        type: 'value',
        value: value
      }
    }
  }
  return args.map(valueToMeta)
}

// Populate object's members from descriptors.
// The |ref| will be kept referenced by |members|.
// This matches |getObjectMemebers| in rpc-server.
const setObjectMembers = function (ref, object, metaId, members) {
  if (!Array.isArray(members)) return

  for (let member of members) {
    if (object.hasOwnProperty(member.name)) continue

    let descriptor = { enumerable: member.enumerable }
    if (member.type === 'method') {
      const remoteMemberFunction = function (...args) {
        if (this && this.constructor === remoteMemberFunction) {
          // Constructor call.
          let ret = ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_CONSTRUCTOR', metaId, member.name, wrapArgs(args))
          return metaToValue(ret)
        } else {
          // Call member function.
          let ret = ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_CALL', metaId, member.name, wrapArgs(args))
          return metaToValue(ret)
        }
      }

      let descriptorFunction = proxyFunctionProperties(remoteMemberFunction, metaId, member.name)

      descriptor.get = function () {
        descriptorFunction.ref = ref  // The member should reference its object.
        return descriptorFunction
      }
      // Enable monkey-patch the method
      descriptor.set = function (value) {
        descriptorFunction = value
        return value
      }
      descriptor.configurable = true
    } else if (member.type === 'get') {
      descriptor.get = function () {
        return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_GET', metaId, member.name))
      }

      // Only set setter when it is writable.
      if (member.writable) {
        descriptor.set = function (value) {
          ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_SET', metaId, member.name, value)
          return value
        }
      }
    }

    Object.defineProperty(object, member.name, descriptor)
  }
}

// Populate object's prototype from descriptor.
// This matches |getObjectPrototype| in rpc-server.
const setObjectPrototype = function (ref, object, metaId, descriptor) {
  if (descriptor === null) return
  let proto = {}
  setObjectMembers(ref, proto, metaId, descriptor.members)
  setObjectPrototype(ref, proto, metaId, descriptor.proto)
  Object.setPrototypeOf(object, proto)
}

// Wrap function in Proxy for accessing remote properties
const proxyFunctionProperties = function (remoteMemberFunction, metaId, name) {
  let loaded = false

  // Lazily load function properties
  const loadRemoteProperties = () => {
    if (loaded) return
    loaded = true
    const meta = ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_GET', metaId, name)
    setObjectMembers(remoteMemberFunction, remoteMemberFunction, meta.id, meta.members)
  }

  return new Proxy(remoteMemberFunction, {
    set: (target, property, value, receiver) => {
      if (property !== 'ref') loadRemoteProperties()
      target[property] = value
      return true
    },
    get: (target, property, receiver) => {
      if (!target.hasOwnProperty(property)) loadRemoteProperties()
      return target[property]
    },
    ownKeys: (target) => {
      loadRemoteProperties()
      return Object.getOwnPropertyNames(target)
    },
    getOwnPropertyDescriptor: (target, property) => {
      let descriptor = Object.getOwnPropertyDescriptor(target, property)
      if (descriptor != null) return descriptor
      loadRemoteProperties()
      return Object.getOwnPropertyDescriptor(target, property)
    }
  })
}

// Convert meta data from browser into real value.
const metaToValue = function (meta) {
  var el, i, len, ref1, results, ret
  switch (meta.type) {
    case 'value':
      return meta.value
    case 'array':
      ref1 = meta.members
      results = []
      for (i = 0, len = ref1.length; i < len; i++) {
        el = ref1[i]
        results.push(metaToValue(el))
      }
      return results
    case 'buffer':
      return Buffer.from(meta.value)
    case 'promise':
      return Promise.resolve({
        then: metaToValue(meta.then)
      })
    case 'error':
      return metaToPlainObject(meta)
    case 'date':
      return new Date(meta.value)
    case 'exception':
      throw new Error(meta.message + '\n' + meta.stack)
    default:
      if (remoteObjectCache.has(meta.id)) return remoteObjectCache.get(meta.id)

      if (meta.type === 'function') {
        // A shadow class to represent the remote function object.
        let remoteFunction = function (...args) {
          if (this && this.constructor === remoteFunction) {
            // Constructor call.
            let obj = ipcRenderer.sendSync('ELECTRON_BROWSER_CONSTRUCTOR', meta.id, wrapArgs(args))
            // Returning object in constructor will replace constructed object
            // with the returned object.
            // http://stackoverflow.com/questions/1978049/what-values-can-a-constructor-return-to-avoid-returning-this
            return metaToValue(obj)
          } else {
            // Function call.
            let obj = ipcRenderer.sendSync('ELECTRON_BROWSER_FUNCTION_CALL', meta.id, wrapArgs(args))
            return metaToValue(obj)
          }
        }
        ret = remoteFunction
      } else {
        ret = {}
      }

      // Populate delegate members.
      setObjectMembers(ret, ret, meta.id, meta.members)
      // Populate delegate prototype.
      setObjectPrototype(ret, ret, meta.id, meta.proto)

      // Set constructor.name to object's name.
      Object.defineProperty(ret.constructor, 'name', { value: meta.name })

      // Track delegate object's life time, and tell the browser to clean up
      // when the object is GCed.
      v8Util.setRemoteObjectFreer(ret, meta.id)

      // Remember object's id.
      v8Util.setHiddenValue(ret, 'atomId', meta.id)
      remoteObjectCache.set(meta.id, ret)
      return ret
  }
}

// Construct a plain object from the meta.
const metaToPlainObject = function (meta) {
  var i, len, obj, ref1
  obj = (function () {
    switch (meta.type) {
      case 'error':
        return new Error()
      default:
        return {}
    }
  })()
  ref1 = meta.members
  for (i = 0, len = ref1.length; i < len; i++) {
    let {name, value} = ref1[i]
    obj[name] = value
  }
  return obj
}

// Browser calls a callback in renderer.
ipcRenderer.on('ELECTRON_RENDERER_CALLBACK', function (event, id, args) {
  callbacksRegistry.apply(id, metaToValue(args))
})

// A callback in browser is released.
ipcRenderer.on('ELECTRON_RENDERER_RELEASE_CALLBACK', function (event, id) {
  callbacksRegistry.remove(id)
})

// Get remote module.
exports.require = function (module) {
  return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_REQUIRE', module))
}

// Alias to remote.require('electron').xxx.
exports.getBuiltin = function (module) {
  return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_GET_BUILTIN', module))
}

// Get current BrowserWindow.
exports.getCurrentWindow = function () {
  return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_CURRENT_WINDOW'))
}

// Get current WebContents object.
exports.getCurrentWebContents = function () {
  return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS'))
}

// Get a global object in browser.
exports.getGlobal = function (name) {
  return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_GLOBAL', name))
}

// Get the process object in browser.
exports.__defineGetter__('process', function () {
  return exports.getGlobal('process')
})

// Create a funtion that will return the specifed value when called in browser.
exports.createFunctionWithReturnValue = function (returnValue) {
  const func = function () {
    return returnValue
  }
  v8Util.setHiddenValue(func, 'returnValue', true)
  return func
}

// Get the guest WebContents from guestInstanceId.
exports.getGuestWebContents = function (guestInstanceId) {
  const meta = ipcRenderer.sendSync('ELECTRON_BROWSER_GUEST_WEB_CONTENTS', guestInstanceId)
  return metaToValue(meta)
}

const addBuiltinProperty = (name) => {
  Object.defineProperty(exports, name, {
    get: function () {
      return exports.getBuiltin(name)
    }
  })
}

// Add each browser module name as a property
// This list should match the exports in browser/api/exports/electron.js
const browserModules = [
  'app',
  'autoUpdater',
  'BrowserWindow',
  'contentTracing',
  'dialog',
  'globalShortcut',
  'ipcMain',
  'Menu',
  'MenuItem',
  'net',
  'powerMonitor',
  'powerSaveBlocker',
  'protocol',
  'screen',
  'session',
  'systemPreferences',
  'Tray',
  'webContents'
]
browserModules.forEach(addBuiltinProperty)

// Add each common module name as a property
const commonModules = {}
require('../../common/api/exports/electron').defineProperties(commonModules)
Object.getOwnPropertyNames(commonModules).forEach(addBuiltinProperty)
<commit_msg>Expose TouchBar on remote module<commit_after>'use strict'

const {Buffer} = require('buffer')
const v8Util = process.atomBinding('v8_util')
const {ipcRenderer, isPromise, CallbacksRegistry} = require('electron')

const callbacksRegistry = new CallbacksRegistry()

const remoteObjectCache = v8Util.createIDWeakMap()

// Convert the arguments object into an array of meta data.
const wrapArgs = function (args, visited) {
  if (visited == null) {
    visited = new Set()
  }

  const valueToMeta = function (value) {
    // Check for circular reference.
    if (visited.has(value)) {
      return {
        type: 'value',
        value: null
      }
    }

    if (Array.isArray(value)) {
      visited.add(value)
      let meta = {
        type: 'array',
        value: wrapArgs(value, visited)
      }
      visited.delete(value)
      return meta
    } else if (ArrayBuffer.isView(value)) {
      return {
        type: 'buffer',
        value: Buffer.from(value)
      }
    } else if (value instanceof Date) {
      return {
        type: 'date',
        value: value.getTime()
      }
    } else if ((value != null) && typeof value === 'object') {
      if (isPromise(value)) {
        return {
          type: 'promise',
          then: valueToMeta(function (onFulfilled, onRejected) {
            value.then(onFulfilled, onRejected)
          })
        }
      } else if (v8Util.getHiddenValue(value, 'atomId')) {
        return {
          type: 'remote-object',
          id: v8Util.getHiddenValue(value, 'atomId')
        }
      }

      let meta = {
        type: 'object',
        name: value.constructor != null ? value.constructor.name : '',
        members: []
      }
      visited.add(value)
      for (let prop in value) {
        meta.members.push({
          name: prop,
          value: valueToMeta(value[prop])
        })
      }
      visited.delete(value)
      return meta
    } else if (typeof value === 'function' && v8Util.getHiddenValue(value, 'returnValue')) {
      return {
        type: 'function-with-return-value',
        value: valueToMeta(value())
      }
    } else if (typeof value === 'function') {
      return {
        type: 'function',
        id: callbacksRegistry.add(value),
        location: v8Util.getHiddenValue(value, 'location'),
        length: value.length
      }
    } else {
      return {
        type: 'value',
        value: value
      }
    }
  }
  return args.map(valueToMeta)
}

// Populate object's members from descriptors.
// The |ref| will be kept referenced by |members|.
// This matches |getObjectMemebers| in rpc-server.
const setObjectMembers = function (ref, object, metaId, members) {
  if (!Array.isArray(members)) return

  for (let member of members) {
    if (object.hasOwnProperty(member.name)) continue

    let descriptor = { enumerable: member.enumerable }
    if (member.type === 'method') {
      const remoteMemberFunction = function (...args) {
        if (this && this.constructor === remoteMemberFunction) {
          // Constructor call.
          let ret = ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_CONSTRUCTOR', metaId, member.name, wrapArgs(args))
          return metaToValue(ret)
        } else {
          // Call member function.
          let ret = ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_CALL', metaId, member.name, wrapArgs(args))
          return metaToValue(ret)
        }
      }

      let descriptorFunction = proxyFunctionProperties(remoteMemberFunction, metaId, member.name)

      descriptor.get = function () {
        descriptorFunction.ref = ref  // The member should reference its object.
        return descriptorFunction
      }
      // Enable monkey-patch the method
      descriptor.set = function (value) {
        descriptorFunction = value
        return value
      }
      descriptor.configurable = true
    } else if (member.type === 'get') {
      descriptor.get = function () {
        return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_GET', metaId, member.name))
      }

      // Only set setter when it is writable.
      if (member.writable) {
        descriptor.set = function (value) {
          ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_SET', metaId, member.name, value)
          return value
        }
      }
    }

    Object.defineProperty(object, member.name, descriptor)
  }
}

// Populate object's prototype from descriptor.
// This matches |getObjectPrototype| in rpc-server.
const setObjectPrototype = function (ref, object, metaId, descriptor) {
  if (descriptor === null) return
  let proto = {}
  setObjectMembers(ref, proto, metaId, descriptor.members)
  setObjectPrototype(ref, proto, metaId, descriptor.proto)
  Object.setPrototypeOf(object, proto)
}

// Wrap function in Proxy for accessing remote properties
const proxyFunctionProperties = function (remoteMemberFunction, metaId, name) {
  let loaded = false

  // Lazily load function properties
  const loadRemoteProperties = () => {
    if (loaded) return
    loaded = true
    const meta = ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_GET', metaId, name)
    setObjectMembers(remoteMemberFunction, remoteMemberFunction, meta.id, meta.members)
  }

  return new Proxy(remoteMemberFunction, {
    set: (target, property, value, receiver) => {
      if (property !== 'ref') loadRemoteProperties()
      target[property] = value
      return true
    },
    get: (target, property, receiver) => {
      if (!target.hasOwnProperty(property)) loadRemoteProperties()
      return target[property]
    },
    ownKeys: (target) => {
      loadRemoteProperties()
      return Object.getOwnPropertyNames(target)
    },
    getOwnPropertyDescriptor: (target, property) => {
      let descriptor = Object.getOwnPropertyDescriptor(target, property)
      if (descriptor != null) return descriptor
      loadRemoteProperties()
      return Object.getOwnPropertyDescriptor(target, property)
    }
  })
}

// Convert meta data from browser into real value.
const metaToValue = function (meta) {
  var el, i, len, ref1, results, ret
  switch (meta.type) {
    case 'value':
      return meta.value
    case 'array':
      ref1 = meta.members
      results = []
      for (i = 0, len = ref1.length; i < len; i++) {
        el = ref1[i]
        results.push(metaToValue(el))
      }
      return results
    case 'buffer':
      return Buffer.from(meta.value)
    case 'promise':
      return Promise.resolve({
        then: metaToValue(meta.then)
      })
    case 'error':
      return metaToPlainObject(meta)
    case 'date':
      return new Date(meta.value)
    case 'exception':
      throw new Error(meta.message + '\n' + meta.stack)
    default:
      if (remoteObjectCache.has(meta.id)) return remoteObjectCache.get(meta.id)

      if (meta.type === 'function') {
        // A shadow class to represent the remote function object.
        let remoteFunction = function (...args) {
          if (this && this.constructor === remoteFunction) {
            // Constructor call.
            let obj = ipcRenderer.sendSync('ELECTRON_BROWSER_CONSTRUCTOR', meta.id, wrapArgs(args))
            // Returning object in constructor will replace constructed object
            // with the returned object.
            // http://stackoverflow.com/questions/1978049/what-values-can-a-constructor-return-to-avoid-returning-this
            return metaToValue(obj)
          } else {
            // Function call.
            let obj = ipcRenderer.sendSync('ELECTRON_BROWSER_FUNCTION_CALL', meta.id, wrapArgs(args))
            return metaToValue(obj)
          }
        }
        ret = remoteFunction
      } else {
        ret = {}
      }

      // Populate delegate members.
      setObjectMembers(ret, ret, meta.id, meta.members)
      // Populate delegate prototype.
      setObjectPrototype(ret, ret, meta.id, meta.proto)

      // Set constructor.name to object's name.
      Object.defineProperty(ret.constructor, 'name', { value: meta.name })

      // Track delegate object's life time, and tell the browser to clean up
      // when the object is GCed.
      v8Util.setRemoteObjectFreer(ret, meta.id)

      // Remember object's id.
      v8Util.setHiddenValue(ret, 'atomId', meta.id)
      remoteObjectCache.set(meta.id, ret)
      return ret
  }
}

// Construct a plain object from the meta.
const metaToPlainObject = function (meta) {
  var i, len, obj, ref1
  obj = (function () {
    switch (meta.type) {
      case 'error':
        return new Error()
      default:
        return {}
    }
  })()
  ref1 = meta.members
  for (i = 0, len = ref1.length; i < len; i++) {
    let {name, value} = ref1[i]
    obj[name] = value
  }
  return obj
}

// Browser calls a callback in renderer.
ipcRenderer.on('ELECTRON_RENDERER_CALLBACK', function (event, id, args) {
  callbacksRegistry.apply(id, metaToValue(args))
})

// A callback in browser is released.
ipcRenderer.on('ELECTRON_RENDERER_RELEASE_CALLBACK', function (event, id) {
  callbacksRegistry.remove(id)
})

// Get remote module.
exports.require = function (module) {
  return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_REQUIRE', module))
}

// Alias to remote.require('electron').xxx.
exports.getBuiltin = function (module) {
  return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_GET_BUILTIN', module))
}

// Get current BrowserWindow.
exports.getCurrentWindow = function () {
  return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_CURRENT_WINDOW'))
}

// Get current WebContents object.
exports.getCurrentWebContents = function () {
  return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS'))
}

// Get a global object in browser.
exports.getGlobal = function (name) {
  return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_GLOBAL', name))
}

// Get the process object in browser.
exports.__defineGetter__('process', function () {
  return exports.getGlobal('process')
})

// Create a funtion that will return the specifed value when called in browser.
exports.createFunctionWithReturnValue = function (returnValue) {
  const func = function () {
    return returnValue
  }
  v8Util.setHiddenValue(func, 'returnValue', true)
  return func
}

// Get the guest WebContents from guestInstanceId.
exports.getGuestWebContents = function (guestInstanceId) {
  const meta = ipcRenderer.sendSync('ELECTRON_BROWSER_GUEST_WEB_CONTENTS', guestInstanceId)
  return metaToValue(meta)
}

const addBuiltinProperty = (name) => {
  Object.defineProperty(exports, name, {
    get: function () {
      return exports.getBuiltin(name)
    }
  })
}

// Add each browser module name as a property
// This list should match the exports in browser/api/exports/electron.js
const browserModules = [
  'app',
  'autoUpdater',
  'BrowserWindow',
  'contentTracing',
  'dialog',
  'globalShortcut',
  'ipcMain',
  'Menu',
  'MenuItem',
  'net',
  'powerMonitor',
  'powerSaveBlocker',
  'protocol',
  'screen',
  'session',
  'systemPreferences',
  'TouchBar',
  'Tray',
  'webContents'
]
browserModules.forEach(addBuiltinProperty)

// Add each common module name as a property
const commonModules = {}
require('../../common/api/exports/electron').defineProperties(commonModules)
Object.getOwnPropertyNames(commonModules).forEach(addBuiltinProperty)
<|endoftext|>


<commit_before>// getMeta function: get a meta tag's content attr by its name
(function( w ){
  w.getMeta = function( metaname ){
  	var metas = window.document.getElementsByTagName( "meta" );
  	var meta;
  	for( var i = 0; i < metas.length; i ++ ){
  		if( metas[ i ].name && metas[ i ].name === metaname ){
  			meta = metas[ i ];
  			break;
  		}
  	}
  	return meta.content;
  };

  // commonjs
  if( typeof module !== "undefined" ){
  	module.exports = w.getMeta;
  }
}( this ));
<commit_msg>Expose getmeta on window, even when require'd via CommonJS and wrapped with Browserify<commit_after>// getMeta function: get a meta tag's content attr by its name
(function( w ){
  w.getMeta = function( metaname ){
  	var metas = window.document.getElementsByTagName( "meta" );
  	var meta;
  	for( var i = 0; i < metas.length; i ++ ){
  		if( metas[ i ].name && metas[ i ].name === metaname ){
  			meta = metas[ i ];
  			break;
  		}
  	}
  	return meta.content;
  };

  // commonjs
  if( typeof module !== "undefined" ){
  	module.exports = w.getMeta;
  }
}( typeof global !== "undefined" ? global : this ));
<|endoftext|>


<commit_before>module.exports = function (conf, log4js) {
    var logger = log4js.getLogger("shutdown");
    return function shutdownHook(signalName) {
        // signalName is either 'SIGINT' or 'SIGTERM'
        logger.debug('Shutdown hook - Cleaning/saving data up before exit');

    };
};

<commit_msg>Grammar fix<commit_after>module.exports = function (conf, log4js) {
    var logger = log4js.getLogger("shutdown");
    return function shutdownHook(signalName) {
        // signalName is either 'SIGINT' or 'SIGTERM'
        logger.debug('Shutdown hook - Cleaning/saving data before exit');

    };
};

<|endoftext|>


<commit_before>var fs = require("fs");
var path = require("path");
var util = require("util");
var rule = require("./rule.js");
var entity = require("./entity.js");
var shared = require("./shared.js");

function DockerBuild(owner, srcs, data, deps, name) {
  this.init("DockerBuild for " + owner.id);
  this.mnemonic = "DockerBuild";
  this.owner = owner;
  this.addAllInputs(srcs);
  this.srcs = srcs;
  this.addAllInputs(data);
  this.data = data;
  this.addAllInputs(deps);
  this.deps = deps;
  this.name = name;
  this.addParent(this.name);
  this.outputDir = new entity.Directory(owner.getRoot("gen"));
  owner.rule.engine.actions.addNode(this.outputDir);
  this.outputDir.addParent(this);

  this.done = new entity.File(owner.getRoot("gen") + "/done",
      "done_marker");
  owner.rule.engine.actions.addNode(this.done);
  this.done.addParent(this);

  this.version = "d251b622-2657-4ae9-8210-171b39fcdb8e";
}
DockerBuild.prototype = new entity.Action();
DockerBuild.prototype.createScript = function() {
  var destDir = this.outputDir.getPath();
  var lines = [
      "mkdir -p " + destDir,
      "cp " + this.srcs[0].getPath() + " " + destDir
  ];
  for (var i=0; i < this.data.length; i++) {
      lines.push("cp -Lr --preserve=all " + this.data[i].getPath() + " " + destDir);
  }
  lines.push("pushd " + destDir +"/..");

  var name = this.name.value;
  lines.push("docker build -t " + name + " " + path.basename(destDir));
  var tag = this.owner.getProperty("docker_tag");
  if (tag && tag.value) {
    lines.push("docker tag " + name + ":latest " + name + ":" + tag.value);
  }
  lines.push("popd");
  lines.push("date +%s > " + this.done.getPath());
  return lines;
};

function DockerPush(owner, name, remoteName) {
  this.init("DockerPush for " + owner.id);
  this.mnemonic = "DockerPush";
  this.owner = owner;

  this.name = name;
  this.remoteName = remoteName;
  this.tags = [];
  this.addParent(this.name);

  var tag = owner.getProperty("docker_tag");
  if (tag && tag.value) {
    this.addParent(tag);
    this.tags.push(tag.value);
  }

  var releaseVersion = owner.getProperty("release_version");
  if (releaseVersion && releaseVersion.value) {
    this.addParent(releaseVersion);
    var semVer = shared.parseVersion(releaseVersion.value);
    this.tags.push(semVer.major);
    this.tags.push(semVer.major + "." + semVer.minor);
    this.tags.push(semVer.major + "." + semVer.minor + "." + semVer.patch);
  }

  this.logFile = new entity.File(owner.getRoot("gen") + ".push.log",
      "test_log");
  owner.rule.engine.actions.addNode(this.logFile);
  this.logFile.addParent(this);

  this.version = "169eaceb-bf0e-44f7-86b9-b8a865216aa9";
}

DockerPush.prototype = new entity.Action();
DockerPush.prototype.createScript = function() {
  if (this.tags.length == 0) {
    return dockerTagPush(this);
  }

  var lines = [];
  for (var i = 0; i < this.tags.length; i++) {
    lines = lines.concat(dockerTagPush(this, this.tags[i]));
  }
  return lines;
};

function dockerTagPush(target, tag) {

  var remote = target.remoteName;
  if (tag) {
    remote += ":" + tag;
  }
  var pushCommand = "docker push "
  var convoyServer = target.owner.getProperty("convoy_server");
  if (convoyServer) {
    pushCommand = "gcloud preview docker --server=" + convoyServer.value + " push ";
  }
  return ["docker tag " + target.name.value +":latest " + remote,
          pushCommand + remote];
}

function DockerSave(owner, name) {
  this.init("DockerSave for " + owner.id);
  this.mnemonic = "DockerSave";
  this.owner = owner;

  this.name = name;
  this.addParent(this.name);

  this.tarBall = new entity.File(owner.getRoot("bin") + ".tar.gz",
      "docker_tarball");
  owner.rule.engine.actions.addNode(this.tarBall);
  this.tarBall.addParent(this);

  this.version = "24e972a7-c1f6-444a-b42a-cca8feb2a043";
}
DockerSave.prototype = new entity.Action();
DockerSave.prototype.createScript = function() {
  return [
      "mkdir -p " + path.dirname(this.tarBall.getPath()),
      "docker save " + this.name.value + " | gzip -c > " +
          this.tarBall.getPath()
  ];
};

function DockerPull(owner, name, remoteName) {
  this.init("DockerPull for " + owner.id);
  this.mnemonic = "DockerPull";
  this.owner = owner;

  this.name = name;
  this.remoteName = remoteName;
  this.tags = [];
  this.addParent(this.name);

  var tag = owner.getProperty("docker_tag");
  if (tag && tag.value) {
    this.addParent(tag);
    this.tags.push(tag.value);
  }

  var releaseVersion = owner.getProperty("release_version");
  if (releaseVersion && releaseVersion.value) {
    this.addParent(releaseVersion);
    var semVer = shared.parseVersion(releaseVersion.value);
    this.tags.push(semVer.major);
    this.tags.push(semVer.major + "." + semVer.minor);
    this.tags.push(semVer.major + "." + semVer.minor + "." + semVer.patch);
  }

  this.logFile = new entity.File(owner.getRoot("gen") + ".pull.log",
      "test_log");
  owner.rule.engine.actions.addNode(this.logFile);
  this.logFile.addParent(this);

  this.version = "16583dac-8bfe-490b-8caa-fae1808a4efe";
}

DockerPull.prototype = new entity.Action();
DockerPull.prototype.createScript = function() {
  if (this.tags.length == 0) {
    return dockerTagPull(this);
  }

  var lines = [];
  for (var i = 0; i < this.tags.length; i++) {
    lines = lines.concat(dockerTagPull(this, this.tags[i]));
  }
  return lines;
};

function dockerTagPull(target, tag) {

  var remote = target.remoteName;
  if (tag) {
    remote += ":" + tag;
  }
  var local = target.name.value;
  if (tag) {
    local += ":" + tag
  }
  return [ "docker pull " + remote,
           "docker tag " + remote + " " + local];
}

DockerDeploy = function(engine) {
  this.engine = engine;
};

DockerDeploy.prototype = new rule.Rule();

DockerDeploy.prototype.getOutputsFor = function(target, kind) {
  if (target.outs) {
    return target.outs;
  }
  var name = target.getProperty("docker_name");
  if (!name) {
    name = new entity.Property(target.id, "docker_name", target.name);
  }

  if (kind != "pull" && kind != "package" && kind != "deploy") {
    target.outs = [];
    return target.outs;
  }

  if (kind == "pull") {
    target.outs = [];
    var convoyBucket = target.getProperty("convoy_bucket");
    var dockerRepository = target.getProperty("docker_repository");
    if (convoyBucket) {
      var tokens = name.value.split("/");
      var remoteName = convoyServer.value +
          "/_b_" + convoyBucket.value + "/" + tokens[tokens.length-1];
      var dockerPull = new DockerPull(target, name, remoteName);
      target.outs.push(dockerPull.logFile);
    } else if (dockerRepository) {
      var dockerPull = new DockerPull(target, name,
          dockerRepository.value + "/" + name.value);
      target.outs.push(dockerPull.logFile);
    } else {
      console.log("Need at least --convoy_bucket or " +
          "--docker_repository to be able to pull docker images.");
      process.exit(1);
    }
    return target.outs;
  }

  var srcs = rule.getAllOutputsFor(target.inputsByKind["srcs"], "build",
      rule.fileFilter("src_file", "/Dockerfile"));
  var data = rule.getAllOutputsFor(target.inputsByKind["data"], "build",
      rule.allFilesAndDirsFilter);
  var deps = rule.getAllOutputsFor(target.inputsByKind["deps"], kind,
      rule.allFilesFilter);

  var dockerBuild = new DockerBuild(target, srcs, data, deps, name);
  target.outs = [ dockerBuild.done ];

  if (kind == "deploy") {
    var convoyBucket = target.getProperty("convoy_bucket");
    var convoyServer = target.getProperty("convoy_server");
    var dockerRepository = target.getProperty("docker_repository");
    if (convoyBucket) {
      var tokens = name.value.split("/");
      var remoteName = convoyServer.value +
          "/_b_" + convoyBucket.value + "/" + tokens[tokens.length-1];
      var dockerPush = new DockerPush(target, name, remoteName);
      dockerPush.addParent(dockerBuild.done);
      target.outs.push(dockerPush.logFile);
    } else if (dockerRepository) {
      var dockerPush = new DockerPush(target, name,
          dockerRepository.value + "/" + name.value);
      dockerPush.addParent(dockerBuild.done);
      target.outs.push(dockerPush.logFile);
    } else {
      var dockerSave = new DockerSave(target, name);
      dockerSave.addParent(dockerBuild.done);
      target.outs.push(dockerSave.tarBall);
    }
  }
  return target.outs;
};

exports.register = function(engine) {
  engine.addRule("docker_deploy", new DockerDeploy(engine));
};
<commit_msg>make pushes to gcr.io work<commit_after>var fs = require("fs");
var path = require("path");
var util = require("util");
var rule = require("./rule.js");
var entity = require("./entity.js");
var shared = require("./shared.js");

function DockerBuild(owner, srcs, data, deps, name) {
  this.init("DockerBuild for " + owner.id);
  this.mnemonic = "DockerBuild";
  this.owner = owner;
  this.addAllInputs(srcs);
  this.srcs = srcs;
  this.addAllInputs(data);
  this.data = data;
  this.addAllInputs(deps);
  this.deps = deps;
  this.name = name;
  this.addParent(this.name);
  this.outputDir = new entity.Directory(owner.getRoot("gen"));
  owner.rule.engine.actions.addNode(this.outputDir);
  this.outputDir.addParent(this);

  this.done = new entity.File(owner.getRoot("gen") + "/done",
      "done_marker");
  owner.rule.engine.actions.addNode(this.done);
  this.done.addParent(this);

  this.version = "d251b622-2657-4ae9-8210-171b39fcdb8e";
}
DockerBuild.prototype = new entity.Action();
DockerBuild.prototype.createScript = function() {
  var destDir = this.outputDir.getPath();
  var lines = [
      "mkdir -p " + destDir,
      "cp " + this.srcs[0].getPath() + " " + destDir
  ];
  for (var i=0; i < this.data.length; i++) {
      lines.push("cp -Lr --preserve=all " + this.data[i].getPath() + " " + destDir);
  }
  lines.push("pushd " + destDir +"/..");

  var name = this.name.value;
  lines.push("docker build -t " + name + " " + path.basename(destDir));
  var tag = this.owner.getProperty("docker_tag");
  if (tag && tag.value) {
    lines.push("docker tag " + name + ":latest " + name + ":" + tag.value);
  }
  lines.push("popd");
  lines.push("date +%s > " + this.done.getPath());
  return lines;
};

function DockerPush(owner, name, remoteName) {
  this.init("DockerPush for " + owner.id);
  this.mnemonic = "DockerPush";
  this.owner = owner;

  this.name = name;
  this.remoteName = remoteName;
  this.tags = [];
  this.addParent(this.name);

  var tag = owner.getProperty("docker_tag");
  if (tag && tag.value) {
    this.addParent(tag);
    this.tags.push(tag.value);
  }

  var releaseVersion = owner.getProperty("release_version");
  if (releaseVersion && releaseVersion.value) {
    this.addParent(releaseVersion);
    var semVer = shared.parseVersion(releaseVersion.value);
    this.tags.push(semVer.major);
    this.tags.push(semVer.major + "." + semVer.minor);
    this.tags.push(semVer.major + "." + semVer.minor + "." + semVer.patch);
  }

  this.logFile = new entity.File(owner.getRoot("gen") + ".push.log",
      "test_log");
  owner.rule.engine.actions.addNode(this.logFile);
  this.logFile.addParent(this);

  this.version = "169eaceb-bf0e-44f7-86b9-b8a865216aa9";
}

DockerPush.prototype = new entity.Action();
DockerPush.prototype.createScript = function() {
  if (this.tags.length == 0) {
    return dockerTagPush(this);
  }

  var lines = [];
  for (var i = 0; i < this.tags.length; i++) {
    lines = lines.concat(dockerTagPush(this, this.tags[i]));
  }
  return lines;
};

function dockerTagPush(target, tag) {

  var remote = target.remoteName;
  if (tag) {
    remote += ":" + tag;
  }
  var pushCommand = "docker push "
  var convoyServer = target.owner.getProperty("convoy_server");
  if (convoyServer) {
    pushCommand = "gcloud preview docker push ";
  }
  return ["docker tag " + target.name.value +":latest " + remote,
          pushCommand + remote];
}

function DockerSave(owner, name) {
  this.init("DockerSave for " + owner.id);
  this.mnemonic = "DockerSave";
  this.owner = owner;

  this.name = name;
  this.addParent(this.name);

  this.tarBall = new entity.File(owner.getRoot("bin") + ".tar.gz",
      "docker_tarball");
  owner.rule.engine.actions.addNode(this.tarBall);
  this.tarBall.addParent(this);

  this.version = "24e972a7-c1f6-444a-b42a-cca8feb2a043";
}
DockerSave.prototype = new entity.Action();
DockerSave.prototype.createScript = function() {
  return [
      "mkdir -p " + path.dirname(this.tarBall.getPath()),
      "docker save " + this.name.value + " | gzip -c > " +
          this.tarBall.getPath()
  ];
};

function DockerPull(owner, name, remoteName) {
  this.init("DockerPull for " + owner.id);
  this.mnemonic = "DockerPull";
  this.owner = owner;

  this.name = name;
  this.remoteName = remoteName;
  this.tags = [];
  this.addParent(this.name);

  var tag = owner.getProperty("docker_tag");
  if (tag && tag.value) {
    this.addParent(tag);
    this.tags.push(tag.value);
  }

  var releaseVersion = owner.getProperty("release_version");
  if (releaseVersion && releaseVersion.value) {
    this.addParent(releaseVersion);
    var semVer = shared.parseVersion(releaseVersion.value);
    this.tags.push(semVer.major);
    this.tags.push(semVer.major + "." + semVer.minor);
    this.tags.push(semVer.major + "." + semVer.minor + "." + semVer.patch);
  }

  this.logFile = new entity.File(owner.getRoot("gen") + ".pull.log",
      "test_log");
  owner.rule.engine.actions.addNode(this.logFile);
  this.logFile.addParent(this);

  this.version = "16583dac-8bfe-490b-8caa-fae1808a4efe";
}

DockerPull.prototype = new entity.Action();
DockerPull.prototype.createScript = function() {
  if (this.tags.length == 0) {
    return dockerTagPull(this);
  }

  var lines = [];
  for (var i = 0; i < this.tags.length; i++) {
    lines = lines.concat(dockerTagPull(this, this.tags[i]));
  }
  return lines;
};

function dockerTagPull(target, tag) {

  var remote = target.remoteName;
  if (tag) {
    remote += ":" + tag;
  }
  var local = target.name.value;
  if (tag) {
    local += ":" + tag
  }
  return [ "docker pull " + remote,
           "docker tag " + remote + " " + local];
}

DockerDeploy = function(engine) {
  this.engine = engine;
};

DockerDeploy.prototype = new rule.Rule();

DockerDeploy.prototype.getOutputsFor = function(target, kind) {
  if (target.outs) {
    return target.outs;
  }
  var name = target.getProperty("docker_name");
  if (!name) {
    name = new entity.Property(target.id, "docker_name", target.name);
  }

  if (kind != "pull" && kind != "package" && kind != "deploy") {
    target.outs = [];
    return target.outs;
  }

  if (kind == "pull") {
    target.outs = [];
    var convoyBucket = target.getProperty("convoy_bucket");
    var dockerRepository = target.getProperty("docker_repository");
    if (convoyBucket) {
      var tokens = name.value.split("/");
      var remoteName = convoyServer.value +
          "/" + convoyBucket.value + "/" + tokens[tokens.length-1];
      var dockerPull = new DockerPull(target, name, remoteName);
      target.outs.push(dockerPull.logFile);
    } else if (dockerRepository) {
      var dockerPull = new DockerPull(target, name,
          dockerRepository.value + "/" + name.value);
      target.outs.push(dockerPull.logFile);
    } else {
      console.log("Need at least --convoy_bucket or " +
          "--docker_repository to be able to pull docker images.");
      process.exit(1);
    }
    return target.outs;
  }

  var srcs = rule.getAllOutputsFor(target.inputsByKind["srcs"], "build",
      rule.fileFilter("src_file", "/Dockerfile"));
  var data = rule.getAllOutputsFor(target.inputsByKind["data"], "build",
      rule.allFilesAndDirsFilter);
  var deps = rule.getAllOutputsFor(target.inputsByKind["deps"], kind,
      rule.allFilesFilter);

  var dockerBuild = new DockerBuild(target, srcs, data, deps, name);
  target.outs = [ dockerBuild.done ];

  if (kind == "deploy") {
    var convoyBucket = target.getProperty("convoy_bucket");
    var convoyServer = target.getProperty("convoy_server");
    var dockerRepository = target.getProperty("docker_repository");
    if (convoyBucket) {
      var tokens = name.value.split("/");
      var remoteName = convoyServer.value + "/" +
          convoyBucket.value + "/" + tokens[tokens.length-1];
      var dockerPush = new DockerPush(target, name, remoteName);
      dockerPush.addParent(dockerBuild.done);
      target.outs.push(dockerPush.logFile);
    } else if (dockerRepository) {
      var dockerPush = new DockerPush(target, name,
          dockerRepository.value + "/" + name.value);
      dockerPush.addParent(dockerBuild.done);
      target.outs.push(dockerPush.logFile);
    } else {
      var dockerSave = new DockerSave(target, name);
      dockerSave.addParent(dockerBuild.done);
      target.outs.push(dockerSave.tarBall);
    }
  }
  return target.outs;
};

exports.register = function(engine) {
  engine.addRule("docker_deploy", new DockerDeploy(engine));
};
<|endoftext|>


<commit_before>/* Special case intra-cluster replicator service,
 * see orchestrator/member.js for regular cluster replication
 */

module.exports = Replicator;

var EventEmitter = require('events').EventEmitter;
var util = require('util');
var Promise = require('bluebird');
var property = require('../utils/property');

function Replicator(opts) {
  property(this, 'log', opts.logger.createLogger('Replicator'));
};

util.inherits(Replicator, EventEmitter);

Replicator.prototype.initialize = function (config, callback) {
  property(this, 'happn', this.happn);
  property(this, 'config', config);

  this.__defaults(callback);
};

Replicator.prototype.stop = function (options, callback) {
  if (typeof options == 'function') callback = options;

  this.log.info('stopped');
  callback();
};

Replicator.prototype.start = Promise.promisify(function (callback) {
  property(this, 'localClient', this.happn.services.orchestrator.members.__self.client);

  this.localClient.on('/__REPLICATE',
    function (data, meta) {

      console.log('TODO: emit with isLocal flag and origin name');

    },
    function (err) {
      if (err) return callback(err);
      callback();
    }
  );
});

Replicator.prototype.replicate = function (topic, payload, callback) {
  if (!this.localClient) return callback(new Error('Replicator not ready'));

  console.log('replicate', topic, payload);
  callback();
};

Replicator.prototype.__defaults = function (callback) {
  callback();
};
<commit_msg>subscribe to replicated data and emit by topic name<commit_after>/* Special case intra-cluster replicator service,
 * see orchestrator/member.js for regular cluster replication
 */

module.exports = Replicator;

var EventEmitter = require('events').EventEmitter;
var util = require('util');
var Promise = require('bluebird');
var property = require('../utils/property');

function Replicator(opts) {
  property(this, 'log', opts.logger.createLogger('Replicator'));
};

util.inherits(Replicator, EventEmitter);

Replicator.prototype.initialize = function (config, callback) {
  property(this, 'happn', this.happn);
  property(this, 'config', config);

  this.__defaults(callback);
};

Replicator.prototype.stop = function (options, callback) {
  if (typeof options == 'function') callback = options;

  this.log.info('stopped');
  callback();
};

Replicator.prototype.start = Promise.promisify(function (callback) {
  property(this, 'localClient', this.happn.services.orchestrator.members.__self.client);

  var _this = this;

  this.localClient.on('/__REPLICATE',
    function (data) {
      var topic = data.topic;
      var payload = data.payload;
      var origin = data.origin;
      var isLocal = origin == _this.happn.name;

      _this.emit(topic, payload, isLocal, origin);
    },
    function (err) {
      if (err) return callback(err);
      callback();
    }
  );
});

Replicator.prototype.replicate = function (topic, payload, callback) {
  if (!this.localClient) return callback(new Error('Replicator not ready'));

  this.localClient.set('/__REPLICATE', {
    topic: topic,
    payload: payload,
    origin: this.happn.name
  }, {noStore: true}, function (err) {
    if (err) return callback(err);
    callback();
  });

};

Replicator.prototype.__defaults = function (callback) {
  callback();
};
<|endoftext|>


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

'use strict';

var spawn = require('child_process').spawn;
var yaml = require('js-yaml');
var P = require('bluebird');
var fs = P.promisifyAll(require('fs'));
var path = require('path');

// Info from the package definition
var pkg;
// Info from the service-runner config file
var config;
// Target info
var targets;

// The options used in the script
var opts;
// The image name
var img_name;
// The container's name
var name;

// Holds the curently running process
var child;

/**
 * Wraps a child process spawn in a promise which resolves
 * when the child process exists.
 *
 * @param {Array} args the command and its arguments to run (uses /usr/bin/env)
 * @param {Boolean} capture whether to capture stdout and return its contents
 * @return {Promise} the promise which is fulfilled once the child exists
 */
function promised_spawn(args, capture) {

    return new P(function(resolve, reject) {
        var options = capture ? undefined : {stdio: 'inherit'};
        var ret = '';
        if (opts.verbose) {
            console.log('# RUNNING: ' + args.join(' ') + '\n' +
                '  (in ' + process.cwd() + ')');
        }
        child = spawn('/usr/bin/env', args, options);
        if (capture) {
            child.stdout.on('data', function(data) {
                ret += data.toString();
            });
        }
        child.on('close', function() {
            child = undefined;
            ret = ret.trim();
            if (ret === '') { ret = undefined; }
            resolve(ret);
        });
    });

}

/**
 * Generates the Dockerfile used to build the image and start the container
 *
 * @return {Promise} the promise which creates the image file
 */
function create_docker_file() {

    var contents = '';
    var base_img;
    var extra_pkgs = ['nodejs', 'nodejs-legacy', 'npm', 'git'];

    // set some defaults
    if (!pkg.deploy) {
        pkg.deploy = {};
    }
    if (!pkg.deploy.target) {
        pkg.deploy.target = 'debian';
    }
    if (!pkg.deploy.dependencies) {
        pkg.deploy.dependencies = {};
    }
    if (!pkg.deploy.dependencies._all) {
        pkg.deploy.dependencies._all = [];
    }

    // set the deploy target
    base_img = targets[pkg.deploy.target];
    // get any additional packages that need to be installed
    Object.keys(pkg.deploy.dependencies).forEach(function(sys) {
        if (sys !== '_all' && (sys === base_img || (new RegExp(sys)).test(base_img))) {
            Array.prototype.push.apply(extra_pkgs, pkg.deploy.dependencies[sys]);
        }
    });
    Array.prototype.push.apply(extra_pkgs, pkg.deploy.dependencies._all);

    if (!base_img || base_img === '') {
        console.error('ERROR: You must specify a valid target!');
        console.error('ERROR: Check the deploy stanza in package.json and targets.yaml');
        process.exit(2);
    }

    contents = 'FROM ' + base_img + '\n' +
        'RUN apt-get update && apt-get install -y ' + extra_pkgs.join(' ') +
        ' && rm -rf /var/lib/apt/lists/*\n';

    if (!opts.deploy) {
        contents += 'RUN mkdir /opt/service\n' +
            'ADD . /opt/service\n' +
            'WORKDIR /opt/service\n' +
            'RUN npm install\n';
    }

    if (opts.uid !== 0) {
        contents += 'RUN groupadd -g ' + opts.gid + ' -r rungroup && ' +
        'useradd -m -r -g rungroup -u ' + opts.uid + ' runuser\n' +
        'USER runuser\n';
    }

    if (opts.deploy) {
        contents += 'CMD /usr/bin/npm install --production && /usr/bin/npm install heapdump';
    } else if (opts.tests) {
        contents += 'CMD ["/usr/bin/npm", "test"]';
    } else if (opts.coverage) {
        contents += 'CMD ["/usr/bin/npm", "run-script", "coverage"]';
    } else {
        contents += 'CMD ["/usr/bin/npm", "start"]';
    }

    return fs.writeFileAsync('Dockerfile', contents);

}

/**
 * Spawns a docker process which (re)builds the image
 *
 * @return {Promise} the promise starting the build
 */
function build_img() {

    return promised_spawn(['docker', 'build', '-t', img_name, '.']);

}

/**
 * Starts the container and returns once it has finished executing
 *
 * @param {Array} args the array of extra parameters to pass, optional
 * @return {Promise} the promise starting the container
 */
function start_container(args) {

    var cmd = ['docker', 'run', '--name', name, '--rm'];

    // add the extra args as well
    if (args && Array.isArray(args)) {
        Array.prototype.push.apply(cmd, args);
    }

    // list all of the ports defined in the config file
    config.services.forEach(function(srv) {
        srv.conf = srv.conf || {};
        srv.conf.port = srv.conf.port || 8888;
        cmd.push('-p', srv.conf.port + ':' + srv.conf.port);
    });

    // append the image name to create a container from
    cmd.push(img_name);

    // ok, start the container
    return promised_spawn(cmd);

}

/**
 * Updates the deploy repository to current master and
 * rebuilds the node modules, committing and git-review-ing
 * the result
 */
function update_deploy() {

    function promised_git(args) {
        var args_arr = ['git'];
        Array.prototype.push.apply(args_arr, args);
        return promised_spawn(args_arr, true);
    }

    function chained_pgit(args) {
        var arg = args.shift();
        if (!arg) {
            return P.resolve();
        }
        return promised_git(arg)
        .then(function(data) {
            if (args.length === 0) {
                return P.resolve(data);
            }
            return chained_pgit(args);
        });
    }

    // check if there is an alternative repo name defined
    return promised_git(['config', 'deploy.name'])
    .then(function(name) {
        opts.name = name ? name : pkg.name;
        // we need to CHDIR into the deploy dir for subsequent operations
        process.chdir(opts.dir);
        return chained_pgit([
            // make sure we are on master
            ['checkout', 'master'],
            // fetch any possible updates
            ['fetch', 'origin'],
            // work on a topic branch
            ['checkout', '-B', 'sync-repo', 'origin/master'],
            // check if the submodule is present
            ['submodule', 'status']
        ]);
    }).then(function(list) {
        if (list) {
            // the submodule is present
            opts.submodule = list.split(' ')[1];
            // update it fully
            return promised_git(['submodule', 'update', '--init'])
            .then(function() {
                process.chdir(opts.dir + '/' + opts.submodule);
                return chained_pgit([
                    // fetch new commits
                    ['fetch', 'origin'],
                    // inspect what has changed
                    ['diff', '--name-only', 'origin/master']
                ]).then(function(changes) {
                    if (/package\.json/.test(changes)) {
                        // package.json has changed, so we need
                        // to rebuild the node_modules directory
                        opts.need_build = true;
                    }
                    // get the SHA1 of the latest commit on master
                    return promised_git(['rev-parse', '--short', 'origin/master']);
                }).then(function(short_sha1) {
                    opts.commit_msg = 'Update ' + opts.name + ' to ' + short_sha1 + '\n\n';
                    // get a nice list of commits included in the change
                    return promised_git(['log', '..origin/master', '--oneline', '--no-merges', '--reverse', '--color=never']);
                }).then(function(logs) {
                    if (!logs && !opts.need_build) {
                        // no updates have happened, nothing to do here any more but clean up
                        // go back to the root dir
                        process.chdir(opts.dir);
                        // and get back to master
                        return promised_git(['checkout', 'master'])
                        .then(function() {
                            console.log('The deploy repository is up to date already, exiting.');
                            process.exit(0);
                        });
                    } else if (logs) {
                        logs += '\n';
                    } else if (!logs) {
                        logs = '';
                    }
                    opts.commit_msg += 'List of changes:\n' + logs;
                    return promised_git(['checkout', 'origin/master']);
                }).then(function() {
                    // go back to the root dir
                    process.chdir(opts.dir);
                    // add the submodule changes
                    return promised_git(['add', opts.submodule]);
                });
            });
        } else {
            // no submodule, need to add it
            opts.submodule = 'src';
            opts.need_build = true;
            opts.commit_msg = 'Initial import of ' + opts.name;
            return promised_git(['submodule', 'add', 'https://gerrit.wikimedia.org/r/mediawiki/services/' + opts.name, opts.submodule]);
        }
    }).then(function() {
        // make sure the package.json symlink is in place
        return fs.symlinkAsync(opts.submodule + '/package.json', 'package.json')
        .catch(function() {}).then(function() {
            return promised_git(['add', 'package.json']);
        });
    }).then(function() {
        if (!opts.need_build) {
            return;
        }
        // update the commit message
        if (!/^initial/i.test(opts.commit_msg)) {
            opts.commit_msg += 'xxxxxxx Update node module dependencies\n';
        }
        // a rebuild is needed, start by removing the existing modules
        return promised_git(['rm', '-r', 'node_modules'])
        .then(function() {
            return promised_spawn(['rm', '-rf', 'node_modules'], true);
        }).then(function() {
            // start the container which builds the modules
            return start_container(['-v', opts.dir + ':/opt/service', '-w', '/opt/service']);
        }).then(function() {
            // remove unnecessary files
            return promised_spawn([
                'find',
                'node_modules/',
                '-regextype',
                'posix-egrep',
                '-iregex',
                '(.*\\.git.*|.*\\.md|.*\\.txt|.*readme|.*licence)',
                '-exec', 'rm', '-rf', '{}', ';'
            ], true);
        }).then(function() {
            // add the built submodules
            return promised_git(['add', 'node_modules']);
        });
    }).then(function() {
        // commit the changes
        return promised_git(['commit', '-m', opts.commit_msg]);
    }).then(function() {
        if (!opts.review) {
            console.log('\n\nChanges are sitting in the sync-repo branch in');
            console.log(opts.dir + ' with the commit:');
            console.log(opts.commit_msg);
            return;
        }
        return chained_pgit([
            // send them for review
            ['review', '-R'],
            // get back to master
            ['checkout', 'master']
        ]).then(function() {
            console.log('\n\nChanges sent to Gerrit for review!');
        });
    });

}

/**
 * Determines the UID and GID to run under in the container
 *
 * @return {Promise} a promise resolving when the check is done
 */
function get_uid() {

    if (opts.deploy) {
        // get the deploy repo location
        return promised_spawn(['git', 'config', 'deploy.dir'], true)
        .then(function(dir) {
            if (!dir) {
                console.error('ERROR: You must set the location of the deploy repo!');
                console.error('ERROR: Use git config deploy.dir /full/path/to/deploy/dir');
                process.exit(2);
            }
            opts.dir = dir;
            // make sure that the dir exists and it is a git repo
            return fs.statAsync(dir + '/.git');
        }).then(function(stat) {
            opts.uid = stat.uid;
            opts.gid = stat.gid;
        }).catch(function(err) {
            console.error('ERROR: The deploy repo dir ' + opts.dir + ' does not exist or is not a git repo!');
            process.exit(3);
        });
    }

    // get the uid/gid from statting package.json
    return fs.statAsync('package.json')
    .then(function(stat) {
        opts.uid = stat.uid;
        opts.gid = stat.gid;
    }).catch(function(err) {
        console.error('ERROR: package.json does not exist!');
        process.exit(4);
    });

}

/**
 * Main process signal handler
 */
function sig_handle() {
    if (child) {
        child.kill('SIGINT');
    }
}

function main(options, configuration) {

    opts = {
        tests: options.dockerTest,
        coverage: false,
        deploy: options.buildDeploy,
        verbose: options.verbose,
        need_build: options.buildDeploy && options.force,
        review: options.sendReview,
        path: options.basePath
    };

    // config and package info
    config = configuration;
    if (!config) {
        config = yaml.safeLoad(fs.readFileSync(path.join(opts.path, 'config.yaml')));
    }
    pkg = config.package;
    if (!pkg) {
        pkg = require(path.join(opts.path, 'package.json'));
    }

    // target info
    try {
        targets = yaml.safeLoad(fs.readFileSync(path.join(opts.path, 'targets.yaml')));
    } catch (e) {
        // no such file or wrong format,
        // set the defaults
        targets = {
            debian: 'debian:jessie',
            ubuntu: 'ubuntu:14.04'
        };
    }

    // use the package's name as the image name
    img_name = pkg.name;
    if (opts.deploy) { img_name += 'deploy'; }
    // the container's name
    name = pkg.name + '-' + Date.now() + '-' + Math.floor(Math.random() * 1000);

    // trap exit signals
    process.on('SIGINT', sig_handle);
    process.on('SIGTERM', sig_handle);

    // change the dir
    process.chdir(opts.path);

    // start the process
    return get_uid()
    .then(create_docker_file)
    .then(build_img)
    .then(function() {
        if (opts.deploy) {
            return update_deploy();
        } else {
            return start_container();
        }
    });
}

module.exports = main;

<commit_msg>Docker: Ensure the HOME and LINK variables are set<commit_after>#!/usr/bin/env node

'use strict';

var spawn = require('child_process').spawn;
var yaml = require('js-yaml');
var P = require('bluebird');
var fs = P.promisifyAll(require('fs'));
var path = require('path');

// Info from the package definition
var pkg;
// Info from the service-runner config file
var config;
// Target info
var targets;

// The options used in the script
var opts;
// The image name
var img_name;
// The container's name
var name;

// Holds the curently running process
var child;

/**
 * Wraps a child process spawn in a promise which resolves
 * when the child process exists.
 *
 * @param {Array} args the command and its arguments to run (uses /usr/bin/env)
 * @param {Boolean} capture whether to capture stdout and return its contents
 * @return {Promise} the promise which is fulfilled once the child exists
 */
function promised_spawn(args, capture) {

    return new P(function(resolve, reject) {
        var options = capture ? undefined : {stdio: 'inherit'};
        var ret = '';
        if (opts.verbose) {
            console.log('# RUNNING: ' + args.join(' ') + '\n' +
                '  (in ' + process.cwd() + ')');
        }
        child = spawn('/usr/bin/env', args, options);
        if (capture) {
            child.stdout.on('data', function(data) {
                ret += data.toString();
            });
        }
        child.on('close', function() {
            child = undefined;
            ret = ret.trim();
            if (ret === '') { ret = undefined; }
            resolve(ret);
        });
    });

}

/**
 * Generates the Dockerfile used to build the image and start the container
 *
 * @return {Promise} the promise which creates the image file
 */
function create_docker_file() {

    var contents = '';
    var base_img;
    var extra_pkgs = ['nodejs', 'nodejs-legacy', 'npm', 'git'];

    // set some defaults
    if (!pkg.deploy) {
        pkg.deploy = {};
    }
    if (!pkg.deploy.target) {
        pkg.deploy.target = 'debian';
    }
    if (!pkg.deploy.dependencies) {
        pkg.deploy.dependencies = {};
    }
    if (!pkg.deploy.dependencies._all) {
        pkg.deploy.dependencies._all = [];
    }

    // set the deploy target
    base_img = targets[pkg.deploy.target];
    // get any additional packages that need to be installed
    Object.keys(pkg.deploy.dependencies).forEach(function(sys) {
        if (sys !== '_all' && (sys === base_img || (new RegExp(sys)).test(base_img))) {
            Array.prototype.push.apply(extra_pkgs, pkg.deploy.dependencies[sys]);
        }
    });
    Array.prototype.push.apply(extra_pkgs, pkg.deploy.dependencies._all);

    if (!base_img || base_img === '') {
        console.error('ERROR: You must specify a valid target!');
        console.error('ERROR: Check the deploy stanza in package.json and targets.yaml');
        process.exit(2);
    }

    contents = 'FROM ' + base_img + '\n' +
        'RUN apt-get update && apt-get install -y ' + extra_pkgs.join(' ') +
        ' && rm -rf /var/lib/apt/lists/*\n';

    if (!opts.deploy) {
        contents += 'RUN mkdir /opt/service\n' +
            'ADD . /opt/service\n' +
            'WORKDIR /opt/service\n' +
            'RUN npm install\n';
    }

    if (opts.uid !== 0) {
        contents += 'RUN groupadd -g ' + opts.gid + ' -r rungroup && ' +
        'useradd -m -r -g rungroup -u ' + opts.uid + ' runuser\n' +
        'USER runuser\n' + 'ENV HOME=/home/runuser LINK=g++\n';
    } else {
        contents += 'HOME=/root/ LINK=g++\n';
    }

    if (opts.deploy) {
        contents += 'CMD /usr/bin/npm install --production && /usr/bin/npm install heapdump';
    } else if (opts.tests) {
        contents += 'CMD ["/usr/bin/npm", "test"]';
    } else if (opts.coverage) {
        contents += 'CMD ["/usr/bin/npm", "run-script", "coverage"]';
    } else {
        contents += 'CMD ["/usr/bin/npm", "start"]';
    }

    return fs.writeFileAsync('Dockerfile', contents);

}

/**
 * Spawns a docker process which (re)builds the image
 *
 * @return {Promise} the promise starting the build
 */
function build_img() {

    return promised_spawn(['docker', 'build', '-t', img_name, '.']);

}

/**
 * Starts the container and returns once it has finished executing
 *
 * @param {Array} args the array of extra parameters to pass, optional
 * @return {Promise} the promise starting the container
 */
function start_container(args) {

    var cmd = ['docker', 'run', '--name', name, '--rm'];

    // add the extra args as well
    if (args && Array.isArray(args)) {
        Array.prototype.push.apply(cmd, args);
    }

    // list all of the ports defined in the config file
    config.services.forEach(function(srv) {
        srv.conf = srv.conf || {};
        srv.conf.port = srv.conf.port || 8888;
        cmd.push('-p', srv.conf.port + ':' + srv.conf.port);
    });

    // append the image name to create a container from
    cmd.push(img_name);

    // ok, start the container
    return promised_spawn(cmd);

}

/**
 * Updates the deploy repository to current master and
 * rebuilds the node modules, committing and git-review-ing
 * the result
 */
function update_deploy() {

    function promised_git(args) {
        var args_arr = ['git'];
        Array.prototype.push.apply(args_arr, args);
        return promised_spawn(args_arr, true);
    }

    function chained_pgit(args) {
        var arg = args.shift();
        if (!arg) {
            return P.resolve();
        }
        return promised_git(arg)
        .then(function(data) {
            if (args.length === 0) {
                return P.resolve(data);
            }
            return chained_pgit(args);
        });
    }

    // check if there is an alternative repo name defined
    return promised_git(['config', 'deploy.name'])
    .then(function(name) {
        opts.name = name ? name : pkg.name;
        // we need to CHDIR into the deploy dir for subsequent operations
        process.chdir(opts.dir);
        return chained_pgit([
            // make sure we are on master
            ['checkout', 'master'],
            // fetch any possible updates
            ['fetch', 'origin'],
            // work on a topic branch
            ['checkout', '-B', 'sync-repo', 'origin/master'],
            // check if the submodule is present
            ['submodule', 'status']
        ]);
    }).then(function(list) {
        if (list) {
            // the submodule is present
            opts.submodule = list.split(' ')[1];
            // update it fully
            return promised_git(['submodule', 'update', '--init'])
            .then(function() {
                process.chdir(opts.dir + '/' + opts.submodule);
                return chained_pgit([
                    // fetch new commits
                    ['fetch', 'origin'],
                    // inspect what has changed
                    ['diff', '--name-only', 'origin/master']
                ]).then(function(changes) {
                    if (/package\.json/.test(changes)) {
                        // package.json has changed, so we need
                        // to rebuild the node_modules directory
                        opts.need_build = true;
                    }
                    // get the SHA1 of the latest commit on master
                    return promised_git(['rev-parse', '--short', 'origin/master']);
                }).then(function(short_sha1) {
                    opts.commit_msg = 'Update ' + opts.name + ' to ' + short_sha1 + '\n\n';
                    // get a nice list of commits included in the change
                    return promised_git(['log', '..origin/master', '--oneline', '--no-merges', '--reverse', '--color=never']);
                }).then(function(logs) {
                    if (!logs && !opts.need_build) {
                        // no updates have happened, nothing to do here any more but clean up
                        // go back to the root dir
                        process.chdir(opts.dir);
                        // and get back to master
                        return promised_git(['checkout', 'master'])
                        .then(function() {
                            console.log('The deploy repository is up to date already, exiting.');
                            process.exit(0);
                        });
                    } else if (logs) {
                        logs += '\n';
                    } else if (!logs) {
                        logs = '';
                    }
                    opts.commit_msg += 'List of changes:\n' + logs;
                    return promised_git(['checkout', 'origin/master']);
                }).then(function() {
                    // go back to the root dir
                    process.chdir(opts.dir);
                    // add the submodule changes
                    return promised_git(['add', opts.submodule]);
                });
            });
        } else {
            // no submodule, need to add it
            opts.submodule = 'src';
            opts.need_build = true;
            opts.commit_msg = 'Initial import of ' + opts.name;
            return promised_git(['submodule', 'add', 'https://gerrit.wikimedia.org/r/mediawiki/services/' + opts.name, opts.submodule]);
        }
    }).then(function() {
        // make sure the package.json symlink is in place
        return fs.symlinkAsync(opts.submodule + '/package.json', 'package.json')
        .catch(function() {}).then(function() {
            return promised_git(['add', 'package.json']);
        });
    }).then(function() {
        if (!opts.need_build) {
            return;
        }
        // update the commit message
        if (!/^initial/i.test(opts.commit_msg)) {
            opts.commit_msg += 'xxxxxxx Update node module dependencies\n';
        }
        // a rebuild is needed, start by removing the existing modules
        return promised_git(['rm', '-r', 'node_modules'])
        .then(function() {
            return promised_spawn(['rm', '-rf', 'node_modules'], true);
        }).then(function() {
            // start the container which builds the modules
            return start_container(['-v', opts.dir + ':/opt/service', '-w', '/opt/service']);
        }).then(function() {
            // remove unnecessary files
            return promised_spawn([
                'find',
                'node_modules/',
                '-regextype',
                'posix-egrep',
                '-iregex',
                '(.*\\.git.*|.*\\.md|.*\\.txt|.*readme|.*licence)',
                '-exec', 'rm', '-rf', '{}', ';'
            ], true);
        }).then(function() {
            // add the built submodules
            return promised_git(['add', 'node_modules']);
        });
    }).then(function() {
        // commit the changes
        return promised_git(['commit', '-m', opts.commit_msg]);
    }).then(function() {
        if (!opts.review) {
            console.log('\n\nChanges are sitting in the sync-repo branch in');
            console.log(opts.dir + ' with the commit:');
            console.log(opts.commit_msg);
            return;
        }
        return chained_pgit([
            // send them for review
            ['review', '-R'],
            // get back to master
            ['checkout', 'master']
        ]).then(function() {
            console.log('\n\nChanges sent to Gerrit for review!');
        });
    });

}

/**
 * Determines the UID and GID to run under in the container
 *
 * @return {Promise} a promise resolving when the check is done
 */
function get_uid() {

    if (opts.deploy) {
        // get the deploy repo location
        return promised_spawn(['git', 'config', 'deploy.dir'], true)
        .then(function(dir) {
            if (!dir) {
                console.error('ERROR: You must set the location of the deploy repo!');
                console.error('ERROR: Use git config deploy.dir /full/path/to/deploy/dir');
                process.exit(2);
            }
            opts.dir = dir;
            // make sure that the dir exists and it is a git repo
            return fs.statAsync(dir + '/.git');
        }).then(function(stat) {
            opts.uid = stat.uid;
            opts.gid = stat.gid;
        }).catch(function(err) {
            console.error('ERROR: The deploy repo dir ' + opts.dir + ' does not exist or is not a git repo!');
            process.exit(3);
        });
    }

    // get the uid/gid from statting package.json
    return fs.statAsync('package.json')
    .then(function(stat) {
        opts.uid = stat.uid;
        opts.gid = stat.gid;
    }).catch(function(err) {
        console.error('ERROR: package.json does not exist!');
        process.exit(4);
    });

}

/**
 * Main process signal handler
 */
function sig_handle() {
    if (child) {
        child.kill('SIGINT');
    }
}

function main(options, configuration) {

    opts = {
        tests: options.dockerTest,
        coverage: false,
        deploy: options.buildDeploy,
        verbose: options.verbose,
        need_build: options.buildDeploy && options.force,
        review: options.sendReview,
        path: options.basePath
    };

    // config and package info
    config = configuration;
    if (!config) {
        config = yaml.safeLoad(fs.readFileSync(path.join(opts.path, 'config.yaml')));
    }
    pkg = config.package;
    if (!pkg) {
        pkg = require(path.join(opts.path, 'package.json'));
    }

    // target info
    try {
        targets = yaml.safeLoad(fs.readFileSync(path.join(opts.path, 'targets.yaml')));
    } catch (e) {
        // no such file or wrong format,
        // set the defaults
        targets = {
            debian: 'debian:jessie',
            ubuntu: 'ubuntu:14.04'
        };
    }

    // use the package's name as the image name
    img_name = pkg.name;
    if (opts.deploy) { img_name += 'deploy'; }
    // the container's name
    name = pkg.name + '-' + Date.now() + '-' + Math.floor(Math.random() * 1000);

    // trap exit signals
    process.on('SIGINT', sig_handle);
    process.on('SIGTERM', sig_handle);

    // change the dir
    process.chdir(opts.path);

    // start the process
    return get_uid()
    .then(create_docker_file)
    .then(build_img)
    .then(function() {
        if (opts.deploy) {
            return update_deploy();
        } else {
            return start_container();
        }
    });
}

module.exports = main;

<|endoftext|>


<commit_before>import assign from 'object-assign';
import blacklist from 'blacklist';
import React from 'react';
import ReactDOM from 'react-dom';

if (typeof document !== 'undefined') {
  var MediumEditor = require('medium-editor');
}

export default class ReactMediumEditor extends React.Component {
  static defaultProps = {
    tag: 'div'
  };

  constructor(props) {
    super(props);

    this.state = {
      text: this.props.text
    };
  }

  componentDidMount() {
    const dom = ReactDOM.findDOMNode(this);

    this.medium = new MediumEditor(dom, this.props.options);
    this.medium.subscribe('editableInput', (e) => {
      this._updated = true;
      this.change(dom.innerHTML);
    });
  }

  componentDidUpdate() {
    this.medium.restoreSelection();
  }

  componentWillUnmount() {
    this.medium.destroy();
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.text !== this.state.text && !this._updated) {
      this.setState({ text: nextProps.text });
    }

    if (this._updated) this._updated = false;
  }

  render() {
    const tag = this.props.tag;
    const props = blacklist(this.props, 'tag', 'contentEditable', 'dangerouslySetInnerHTML');

    assign(props, {
      dangerouslySetInnerHTML: { __html: this.state.text }
    });

    if (this.medium) {
      this.medium.saveSelection();
    }

    return React.createElement(tag, props);
  }

  change(text) {
    if (this.props.onChange) this.props.onChange(text, this.medium);
  }
}
<commit_msg>add `options` + `text` to blacklist<commit_after>import assign from 'object-assign';
import blacklist from 'blacklist';
import React from 'react';
import ReactDOM from 'react-dom';

if (typeof document !== 'undefined') {
  var MediumEditor = require('medium-editor');
}

export default class ReactMediumEditor extends React.Component {
  static defaultProps = {
    tag: 'div',
  };

  constructor(props) {
    super(props);

    this.state = {
      text: this.props.text,
    };
  }

  componentDidMount() {
    const dom = ReactDOM.findDOMNode(this);

    this.medium = new MediumEditor(dom, this.props.options);
    this.medium.subscribe('editableInput', (e) => {
      this._updated = true;
      this.change(dom.innerHTML);
    });
  }

  componentDidUpdate() {
    this.medium.restoreSelection();
  }

  componentWillUnmount() {
    this.medium.destroy();
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.text !== this.state.text && !this._updated) {
      this.setState({ text: nextProps.text });
    }

    if (this._updated) this._updated = false;
  }

  render() {
    const tag = this.props.tag;
    const props = blacklist(this.props, 'options', 'text', 'tag', 'contentEditable', 'dangerouslySetInnerHTML');

    assign(props, {
      dangerouslySetInnerHTML: { __html: this.state.text },
    });

    if (this.medium) {
      this.medium.saveSelection();
    }

    return React.createElement(tag, props);
  }

  change(text) {
    if (this.props.onChange) this.props.onChange(text, this.medium);
  }
}
<|endoftext|>


<commit_before>const getSelectedTabs = () => new Promise((resolve, reject) => {
  chrome.tabs.query({
    highlighted: true,
    lastFocusedWindow: true,
  }, (tabs) => {
      resolve(tabs);
  })
});

const closeSelectedTabs = (tabIds) => new Promise((resolve, reject) => {
  chrome.tabs.remove(tabIds, () => resolve());
});

const createTabs = (urls) => new Promise((resolve) => {
  chrome.windows.create({
    url: urls,
  }, (window) => resolve(window));
});

const createOpenButton = (id) => {
  const openButton = document.createElement('button');
  const buttonText = document.createTextNode('open');
  openButton.setAttribute('class', 'tab-group-open');
  openButton.setAttribute('data-tab-group-id', id);
  openButton.appendChild(buttonText);
  return openButton;
};

const createRemoveButton = (id) => {
  const remove = document.createElement('button');
  const buttonText = document.createTextNode('remove');
  remove.setAttribute('class', 'tab-group-remove danger');
  remove.setAttribute('data-tab-group-id', id);
  remove.appendChild(buttonText);
  return remove;
};

const createTabListItem = (name, id) => {
  const listItem = document.createElement('li');
  const listItemContentElement = document.createElement('div');
  const listItemContent = document.createTextNode(name);
  listItemContentElement.setAttribute('class', 'tab-group-list-item-content');
  listItem.setAttribute('class', 'tab-group-list-item');
  listItemContentElement.appendChild(listItemContent);
  listItem.appendChild(listItemContentElement);
  listItem.appendChild(createOpenButton(id));
  listItem.appendChild(createRemoveButton(id));
  return listItem;
}

const createTabGroupList = (tabGroupData) => {
  const windowList = document.createElement('ul');
  tabGroupData.forEach((window, index) => {
    windowList.appendChild(createTabListItem(window.name, index));
  });
  return windowList;
};

const createEmptyTabGroupList = () => {
  const emptyList = document.createElement('div');
  const emptyListContent = document.createTextNode('No saved tab groups... yet');
  emptyList.setAttribute('class', 'tab-group-list--empty');
  emptyList.appendChild(emptyListContent);
  return emptyList;
};

const displayTabGroupList = (tabGroupData) => {
  let tabGroupListContent;
  if (tabGroupData.length) {
    tabGroupListContent = createTabGroupList(tabGroupData);
  } else {
    tabGroupListContent = createEmptyTabGroupList();
  }
  const listElement = document.getElementsByClassName('list')[0];
  listElement.innerHTML = '';
  listElement.appendChild(tabGroupListContent);
};

const bindClickHandlers = (elementClass, clickhandler) => {
  const elements = document.getElementsByClassName(elementClass);
  for(let i = 0; i < elements.length; i++) {
    var element = elements[i];
    element.onclick = clickhandler;
  }
};

const getTabGroups = () => new Promise((resolve) => {
  chrome.storage.sync.get(null, (tabGroups) => resolve(tabGroups.tabGroups || []));
});

const addTabGroup = (newTabGroup, tabGroups) => {
  tabGroups.push(newTabGroup);
  chrome.storage.sync.set({ tabGroups }, () => {
    if (chrome.runtime.error) {
      console.error(chrome.runtime.error);
    }
  });
};

const removeTabGroup = (id, tabGroups) => {
  tabGroups.splice(id, 1);
  chrome.storage.sync.set({ tabGroups }, () => {
    if (chrome.runtime.error) {
      console.error(chrome.runtime.error);
    }
  });
};

const clearTabGroups = () => new Promise((resolve) => {
  chrome.storage.sync.clear(() => {
    if (chrome.runtime.error) {
      console.error(chrome.runtime.error);
    }
    resolve();
  });
});

const updateAndBindTabGroupList = () => {
  getTabGroups()
    .then((tabGroups) => {
      displayTabGroupList(tabGroups);
      bindClickHandlers('tab-group-open', (e) => {
        const tabGroupId = e.target.dataset.tabGroupId;
        const { tabs } = tabGroups[tabGroupId];
        createTabs(tabs);
        ga('send', {
          hitType: 'event',
          eventCategory: 'TabGroup',
          eventAction: 'open',
          eventValue: tabs.length,
        });
      });
      bindClickHandlers('tab-group-remove', (e) => {
        const tabGroupId = e.target.dataset.tabGroupId;
        const { tabs } = tabGroups[tabGroupId];
        removeTabGroup(tabGroupId, tabGroups);
        ga('send', {
          hitType: 'event',
          eventCategory: 'TabGroup',
          eventAction: 'remove',
          eventValue: tabs.length,
        });
      });
      const tabGroupNameInput = document.getElementById('tab-group-new-name')
      tabGroupNameInput.onblur = () => tabGroupNameInput.classList.remove('invalid');
      bindClickHandlers('tab-group-save', (e) => {
        e.preventDefault();
        const newTabGroupName = tabGroupNameInput.value;
        if (newTabGroupName) {
          getSelectedTabs()
            .then((tabs) => tabs.map((tab) => tab.url))
            .then((urls) => {
              addTabGroup({
                  name: newTabGroupName,
                  tabs: urls,
                },
                tabGroups
              );
              tabGroupNameInput.value = '';
              ga('send', {
                hitType: 'event',
                eventCategory: 'TabGroup',
                eventAction: 'save',
                eventValue: urls.length,
              });
            });
        } else {
          tabGroupNameInput.classList.add('invalid');
        }
      });
      bindClickHandlers('tab-group-save-close', (e) => {
        e.preventDefault();
        const newTabGroupName = tabGroupNameInput.value;
        if (newTabGroupName) {
          getSelectedTabs()
            .then((tabs) => {
              addTabGroup({
                  name: newTabGroupName,
                  tabs: tabs.map((tab) => tab.url),
                },
                tabGroups
              );
              tabGroupNameInput.value = '';
              ga('send', {
                hitType: 'event',
                eventCategory: 'TabGroup',
                eventAction: 'saveAndClose',
                eventValue: tabs.length,
              });
              return closeSelectedTabs(tabs.map((tab) => tab.id));
            });
        } else {
          tabGroupNameInput.classList.add('invalid');
        }
      })
    });
};

document.addEventListener('DOMContentLoaded', () => {
  updateAndBindTabGroupList();
  chrome.storage.onChanged.addListener(() => updateAndBindTabGroupList());
});
<commit_msg>refactor save click handlers<commit_after>const getSelectedTabs = () => new Promise((resolve, reject) => {
  chrome.tabs.query({
    highlighted: true,
    lastFocusedWindow: true,
  }, (tabs) => {
      resolve(tabs);
  });
});

const closeTabsWithIds = (tabIds) => new Promise((resolve, reject) => {
  chrome.tabs.remove(tabIds, () => resolve());
});

const createTabs = (urls) => new Promise((resolve) => {
  chrome.windows.create({
    url: urls,
  }, (window) => resolve(window));
});

const createOpenButton = (id) => {
  const openButton = document.createElement('button');
  const buttonText = document.createTextNode('open');
  openButton.setAttribute('class', 'tab-group-open');
  openButton.setAttribute('data-tab-group-id', id);
  openButton.appendChild(buttonText);
  return openButton;
};

const createRemoveButton = (id) => {
  const remove = document.createElement('button');
  const buttonText = document.createTextNode('remove');
  remove.setAttribute('class', 'tab-group-remove danger');
  remove.setAttribute('data-tab-group-id', id);
  remove.appendChild(buttonText);
  return remove;
};

const createTabListItem = (name, id) => {
  const listItem = document.createElement('li');
  const listItemContentElement = document.createElement('div');
  const listItemContent = document.createTextNode(name);
  listItemContentElement.setAttribute('class', 'tab-group-list-item-content');
  listItem.setAttribute('class', 'tab-group-list-item');
  listItemContentElement.appendChild(listItemContent);
  listItem.appendChild(listItemContentElement);
  listItem.appendChild(createOpenButton(id));
  listItem.appendChild(createRemoveButton(id));
  return listItem;
}

const createTabGroupList = (tabGroupData) => {
  const windowList = document.createElement('ul');
  tabGroupData.forEach((window, index) => {
    windowList.appendChild(createTabListItem(window.name, index));
  });
  return windowList;
};

const createEmptyTabGroupList = () => {
  const emptyList = document.createElement('div');
  const emptyListContent = document.createTextNode('No saved tab groups... yet');
  emptyList.setAttribute('class', 'tab-group-list--empty');
  emptyList.appendChild(emptyListContent);
  return emptyList;
};

const displayTabGroupList = (tabGroupData) => {
  let tabGroupListContent;
  if (tabGroupData.length) {
    tabGroupListContent = createTabGroupList(tabGroupData);
  } else {
    tabGroupListContent = createEmptyTabGroupList();
  }
  const listElement = document.getElementsByClassName('list')[0];
  listElement.innerHTML = '';
  listElement.appendChild(tabGroupListContent);
};

const bindClickHandlers = (elementClass, clickhandler) => {
  const elements = document.getElementsByClassName(elementClass);
  for(let i = 0; i < elements.length; i++) {
    var element = elements[i];
    element.onclick = clickhandler;
  }
};

const getTabGroups = () => new Promise((resolve) => {
  chrome.storage.sync.get(null, (tabGroups) => resolve(tabGroups.tabGroups || []));
});

const addTabGroup = (newTabGroup, tabGroups) => {
  tabGroups.push(newTabGroup);
  chrome.storage.sync.set({ tabGroups }, () => {
    if (chrome.runtime.error) {
      console.error(chrome.runtime.error);
    }
  });
};

const removeTabGroup = (id, tabGroups) => {
  tabGroups.splice(id, 1);
  chrome.storage.sync.set({ tabGroups }, () => {
    if (chrome.runtime.error) {
      console.error(chrome.runtime.error);
    }
  });
};

const clearTabGroups = () => new Promise((resolve) => {
  chrome.storage.sync.clear(() => {
    if (chrome.runtime.error) {
      console.error(chrome.runtime.error);
    }
    resolve();
  });
});

const handleSaveClick = (e, tabGroups) => {
  e.preventDefault();
  const tabGroupNameInput = document.getElementById('tab-group-new-name');
  const newTabGroupName = tabGroupNameInput.value;
  if (newTabGroupName) {
    getSelectedTabs()
      .then((tabs) => tabs.map((tab) => tab.url))
      .then((urls) => {
        addTabGroup({
            name: newTabGroupName,
            tabs: urls,
          },
          tabGroups
        );
        tabGroupNameInput.value = '';
        ga('send', {
          hitType: 'event',
          eventCategory: 'TabGroup',
          eventAction: 'save',
          eventValue: urls.length,
        });
      });
  } else {
    tabGroupNameInput.classList.add('invalid');
  }
};

const handleSaveAndCloseClick = (e, tabGroups) => {
  e.preventDefault();
  const tabGroupNameInput = document.getElementById('tab-group-new-name');
  const newTabGroupName = tabGroupNameInput.value;
  if (newTabGroupName) {
    getSelectedTabs()
      .then((tabs) => {
        addTabGroup({
            name: newTabGroupName,
            tabs: tabs.map((tab) => tab.url),
          },
          tabGroups
        );
        tabGroupNameInput.value = '';
        ga('send', {
          hitType: 'event',
          eventCategory: 'TabGroup',
          eventAction: 'saveAndClose',
          eventValue: tabs.length,
        });
        closeTabsWithIds(tabs.map((tab) => tab.id));
      });
  } else {
    tabGroupNameInput.classList.add('invalid');
  }
};

const updateAndBindTabGroupList = () => {
  getTabGroups()
    .then((tabGroups) => {
      displayTabGroupList(tabGroups);
      bindClickHandlers('tab-group-open', (e) => {
        const tabGroupId = e.target.dataset.tabGroupId;
        const { tabs } = tabGroups[tabGroupId];
        createTabs(tabs);
        ga('send', {
          hitType: 'event',
          eventCategory: 'TabGroup',
          eventAction: 'open',
          eventValue: tabs.length,
        });
      });
      bindClickHandlers('tab-group-remove', (e) => {
        const tabGroupId = e.target.dataset.tabGroupId;
        const { tabs } = tabGroups[tabGroupId];
        removeTabGroup(tabGroupId, tabGroups);
        ga('send', {
          hitType: 'event',
          eventCategory: 'TabGroup',
          eventAction: 'remove',
          eventValue: tabs.length,
        });
      });
      const tabGroupNameInput = document.getElementById('tab-group-new-name')
      tabGroupNameInput.onblur = () => tabGroupNameInput.classList.remove('invalid');
      bindClickHandlers('tab-group-save', (e) => handleSaveClick(e, tabGroups));
      bindClickHandlers('tab-group-save-close', (e) => handleSaveAndCloseClick(e, tabGroups));
    });
};

document.addEventListener('DOMContentLoaded', () => {
  updateAndBindTabGroupList();
  chrome.storage.onChanged.addListener(() => updateAndBindTabGroupList());
});
<|endoftext|>