<commit_before>module.exports = {
  extends: [
    'airbnb-base',
    'plugin:promise/recommended',
    'plugin:unicorn/recommended',
    'prettier',
  ],
  plugins: [
    'babel',
    'eslint-comments',
    'no-use-extend-native',
    'prettier',
    'promise',
    'unicorn',
  ],
  rules: {
    'capitalized-comments': [2, 'never'],
    complexity: [2, 11],
    'max-depth': [2, 4],
    'max-nested-callbacks': [2, 3],
    'max-params': [2, 3],
    'max-statements-per-line': [2, {max: 1}],
    'max-statements': [2, 15],
    'no-compare-neg-zero': 2,
    'no-confusing-arrow': 0,
    'no-implicit-coercion': [2, {boolean: false, number: true, string: true}],
    'no-magic-numbers': [
      2,
      {
        ignore: [-1, 0, 1],
        ignoreArrayIndexes: true,
        enforceConst: true,
        detectObjects: false,
      },
    ],
    'no-underscore-dangle': 0,
    'no-unused-vars': 0, // deprecated in favor of `no-unused-vars-rest/no-unused-vars`
    semi: 0, // deprecated in favor of `babel/semi`
    // https://github.com/babel/eslint-plugin-babel
    'babel/array-bracket-spacing': 0,
    'babel/arrow-parens': 0,
    'babel/flow-object-type': 0,
    'babel/func-params-comma-dangle': 0,
    'babel/generator-star-spacing': 0,
    'babel/new-cap': [2, {newIsCap: true, capIsNew: true}],
    'babel/no-await-in-loop': 0,
    'babel/no-invalid-this': 2,
    'babel/object-curly-spacing': 0,
    'babel/object-shorthand': 0,
    'babel/semi': [2, 'always'],
    // https://github.com/mysticatea/eslint-plugin-eslint-comments
    'eslint-comments/disable-enable-pair': 2,
    'eslint-comments/no-duplicate-disable': 2,
    'eslint-comments/no-unlimited-disable': 2,
    'eslint-comments/no-unused-disable': 2,
    'eslint-comments/no-unused-enable': 2,
    'eslint-comments/no-use': [
      2,
      {
        allow: [
          'eslint-disable',
          'eslint-disable-line',
          'eslint-disable-next-line',
          'eslint-enable',
        ],
      },
    ],
    // https://github.com/benmosher/eslint-plugin-import
    'import/order': [
      2,
      {
        groups: [
          'builtin',
          'external',
          'internal',
          ['parent', 'sibling', 'index'],
        ],
        'newlines-between': 'always',
      },
    ],
    'import/prefer-default-export': 0,
    // https://github.com/dustinspecker/eslint-plugin-no-use-extend-native
    'no-use-extend-native/no-use-extend-native': 2,
    // https://github.com/not-an-aardvark/eslint-plugin-prettier
    'prettier/prettier': [
      2,
      {
        printWidth: 80,
        tabWidth: 2,
        singleQuote: true,
        trailingComma: 'all',
        bracketSpacing: false,
        jsxBracketSameLine: false,
        parser: 'babylon',
      },
    ],
    // https://github.com/xjamundx/eslint-plugin-promise
    'promise/avoid-new': 0,
    'promise/prefer-await-to-callbacks': 0,
    'promise/prefer-await-to-then': 2,
    // https://github.com/sindresorhus/eslint-plugin-unicorn
    'unicorn/catch-error-name': 0,
    'unicorn/custom-error-definition': 0,
    'unicorn/filename-case': 0,
    'unicorn/no-abusive-eslint-disable': 0, // in favor of `eslint-comments/no-unlimited-disable`
    'unicorn/prefer-type-error': 0,
  },
};
<commit_msg>fix(base): enable `no-unused-vars` rule<commit_after>module.exports = {
  extends: [
    'airbnb-base',
    'plugin:promise/recommended',
    'plugin:unicorn/recommended',
    'prettier',
  ],
  plugins: [
    'babel',
    'eslint-comments',
    'no-use-extend-native',
    'prettier',
    'promise',
    'unicorn',
  ],
  rules: {
    'capitalized-comments': [2, 'never'],
    complexity: [2, 11],
    'max-depth': [2, 4],
    'max-nested-callbacks': [2, 3],
    'max-params': [2, 3],
    'max-statements-per-line': [2, {max: 1}],
    'max-statements': [2, 15],
    'no-compare-neg-zero': 2,
    'no-confusing-arrow': 0,
    'no-implicit-coercion': [2, {boolean: false, number: true, string: true}],
    'no-magic-numbers': [
      2,
      {
        ignore: [-1, 0, 1],
        ignoreArrayIndexes: true,
        enforceConst: true,
        detectObjects: false,
      },
    ],
    'no-underscore-dangle': 0,
    semi: 0, // deprecated in favor of `babel/semi`
    // https://github.com/babel/eslint-plugin-babel
    'babel/array-bracket-spacing': 0,
    'babel/arrow-parens': 0,
    'babel/flow-object-type': 0,
    'babel/func-params-comma-dangle': 0,
    'babel/generator-star-spacing': 0,
    'babel/new-cap': [2, {newIsCap: true, capIsNew: true}],
    'babel/no-await-in-loop': 0,
    'babel/no-invalid-this': 2,
    'babel/object-curly-spacing': 0,
    'babel/object-shorthand': 0,
    'babel/semi': [2, 'always'],
    // https://github.com/mysticatea/eslint-plugin-eslint-comments
    'eslint-comments/disable-enable-pair': 2,
    'eslint-comments/no-duplicate-disable': 2,
    'eslint-comments/no-unlimited-disable': 2,
    'eslint-comments/no-unused-disable': 2,
    'eslint-comments/no-unused-enable': 2,
    'eslint-comments/no-use': [
      2,
      {
        allow: [
          'eslint-disable',
          'eslint-disable-line',
          'eslint-disable-next-line',
          'eslint-enable',
        ],
      },
    ],
    // https://github.com/benmosher/eslint-plugin-import
    'import/order': [
      2,
      {
        groups: [
          'builtin',
          'external',
          'internal',
          ['parent', 'sibling', 'index'],
        ],
        'newlines-between': 'always',
      },
    ],
    'import/prefer-default-export': 0,
    // https://github.com/dustinspecker/eslint-plugin-no-use-extend-native
    'no-use-extend-native/no-use-extend-native': 2,
    // https://github.com/not-an-aardvark/eslint-plugin-prettier
    'prettier/prettier': [
      2,
      {
        printWidth: 80,
        tabWidth: 2,
        singleQuote: true,
        trailingComma: 'all',
        bracketSpacing: false,
        jsxBracketSameLine: false,
        parser: 'babylon',
      },
    ],
    // https://github.com/xjamundx/eslint-plugin-promise
    'promise/avoid-new': 0,
    'promise/prefer-await-to-callbacks': 0,
    'promise/prefer-await-to-then': 2,
    // https://github.com/sindresorhus/eslint-plugin-unicorn
    'unicorn/catch-error-name': 0,
    'unicorn/custom-error-definition': 0,
    'unicorn/filename-case': 0,
    'unicorn/no-abusive-eslint-disable': 0, // in favor of `eslint-comments/no-unlimited-disable`
    'unicorn/prefer-type-error': 0,
  },
};
<|endoftext|>


<commit_before>"use strict";

var LRU = require("lru-cache");

module.exports = function(options) {
  var cacheFactory = arguments[arguments.length - 1];

  if (typeof options === "function") {
    options = {};
  }

  if (typeof cacheFactory !== "function") {
    cacheFactory = function(opts) {
      return LRU(opts);
    };
  }

  var locks = LRU(),
      cache = cacheFactory(options);

  return function locked(fn) {
    return function() {
      var args = Array.prototype.slice.call(arguments, 0);

      // extract the eventual callback, defaulting to a noop if none was
      // provided
      var callback = function() {};

      if (typeof args[args.length - 1] === "function") {
        callback = args.pop();
      }

      // replace the callback with a lock function
      args.push(function lock(key, generator) {
        if (typeof(key) === 'object') {
          key = JSON.stringify(key);
        }

        var data;
        if ((data = cache.get(key))) {
          // cache hit!
          return setImmediate(function() {
            return callback.apply(null, [null].concat(data));
          });
        }

        var callbacks;
        if ((callbacks = locks.get(key))) {
          // already cached
          callbacks.push(callback);
          locks.set(key, callbacks);
          return;
        }

        locks.set(key, [callback]);

        return generator(function unlock(err) {
          args = Array.prototype.slice.call(arguments, 0);

          var data = args.slice(1),
              waiting = locks.get(key);

          // clear the lock now that we've got a list of pending callbacks
          locks.del(key);

          if (!err) {
            // store the data as an array
            cache.set(key, data);
          }

          // pass generated data to all waiting callbacks
          waiting.forEach(function(cb) {
            return setImmediate(function() {
              return cb.apply(null, args);
            });
          });
        });
      });

      // call the wrapped function with updated arguments
      return fn.apply(null, args);
    };
  };
};
<commit_msg>Attach locks and cache to the locker<commit_after>"use strict";

var LRU = require("lru-cache");

module.exports = function(options) {
  var cacheFactory = arguments[arguments.length - 1];

  if (typeof options === "function") {
    options = {};
  }

  if (typeof cacheFactory !== "function") {
    cacheFactory = function(opts) {
      return LRU(opts);
    };
  }

  var locks = LRU(),
      cache = cacheFactory(options);

  var fn = function locked(fn) {
    return function() {
      var args = Array.prototype.slice.call(arguments, 0);

      // extract the eventual callback, defaulting to a noop if none was
      // provided
      var callback = function() {};

      if (typeof args[args.length - 1] === "function") {
        callback = args.pop();
      }

      // replace the callback with a lock function
      args.push(function lock(key, generator) {
        if (typeof(key) === 'object') {
          key = JSON.stringify(key);
        }

        var data;
        if ((data = cache.get(key))) {
          // cache hit!
          return setImmediate(function() {
            return callback.apply(null, [null].concat(data));
          });
        }

        var callbacks;
        if ((callbacks = locks.get(key))) {
          // already cached
          callbacks.push(callback);
          locks.set(key, callbacks);
          return;
        }

        locks.set(key, [callback]);

        return generator(function unlock(err) {
          args = Array.prototype.slice.call(arguments, 0);

          var data = args.slice(1),
              waiting = locks.get(key);

          // clear the lock now that we've got a list of pending callbacks
          locks.del(key);

          if (!err) {
            // store the data as an array
            cache.set(key, data);
          }

          // pass generated data to all waiting callbacks
          waiting.forEach(function(cb) {
            return setImmediate(function() {
              return cb.apply(null, args);
            });
          });
        });
      });

      // call the wrapped function with updated arguments
      return fn.apply(null, args);
    };
  };

  fn.locks = locks;
  fn.cache = cache;

  return fn;
};
<|endoftext|>


<commit_before>var util = require('util');
var spawn = require('child_process').spawn;
var Promise = require('promise');

// Expose methods.
exports.sign = sign;

/**
 * Sign a file.
 *
 * @param {object} options Options
 * @param {stream.Readable} options.content Content stream
 * @param {string} options.key Key path
 * @param {string} options.cert Cert path
 * @param {string} [options.password] Key password
 * @param {function} [cb] Optional callback
 * @returns {object} result Result
 * @returns {string} result.pem Pem signature
 * @returns {string} result.der Der signature
 * @returns {string} result.stdout Strict stdout
 * @returns {string} result.stderr Strict stderr
 * @returns {ChildProcess} result.child Child process
 */

function sign(options, cb) {
  return new Promise(function (resolve, reject) {
    options = options || {};

    if (!options.content)
      throw new Error('Invalid content.');

    if (!options.key)
      throw new Error('Invalid key.');

    if (!options.cert)
      throw new Error('Invalid certificate.');

    var command = util.format(
      'openssl smime -sign -text -signer %s -inkey %s -outform DER -binary',
      options.cert,
      options.key
    );

    if (options.password)
      command += util.format(' -passin pass:%s', options.password);

    var args = command.split(' ');
    var child = spawn(args[0], args.splice(1));

    var der = [];

    child.stdout.on('data', function (chunk) {
      der.push(chunk);
    });

    child.on('close', function (code) {
      if (code !== 0)
        reject(new Error('Process failed.'));
      else
        resolve({
          child: child,
          der: Buffer.concat(der)
        });
    });

    options.content.pipe(child.stdin);
  })
  .nodeify(cb);
}
<commit_msg>Fix comments.<commit_after>var util = require('util');
var spawn = require('child_process').spawn;
var Promise = require('promise');

// Expose methods.
exports.sign = sign;

/**
 * Sign a file.
 *
 * @param {object} options Options
 * @param {stream.Readable} options.content Content stream
 * @param {string} options.key Key path
 * @param {string} options.cert Cert path
 * @param {string} [options.password] Key password
 * @param {function} [cb] Optional callback
 * @returns {object} result Result
 * @returns {string} result.der Der signature
 * @returns {ChildProcess} result.child Child process
 */

function sign(options, cb) {
  return new Promise(function (resolve, reject) {
    options = options || {};

    if (!options.content)
      throw new Error('Invalid content.');

    if (!options.key)
      throw new Error('Invalid key.');

    if (!options.cert)
      throw new Error('Invalid certificate.');

    var command = util.format(
      'openssl smime -sign -text -signer %s -inkey %s -outform DER -binary',
      options.cert,
      options.key
    );

    if (options.password)
      command += util.format(' -passin pass:%s', options.password);

    var args = command.split(' ');
    var child = spawn(args[0], args.splice(1));

    var der = [];

    child.stdout.on('data', function (chunk) {
      der.push(chunk);
    });

    child.on('close', function (code) {
      if (code !== 0)
        reject(new Error('Process failed.'));
      else
        resolve({
          child: child,
          der: Buffer.concat(der)
        });
    });

    options.content.pipe(child.stdin);
  })
  .nodeify(cb);
}
<|endoftext|>


<commit_before>"use strict";

addEventListener("DOMContentLoaded", function () {
	var createSubDiv;
	var wrappers = {};
	(function () {
		var defaultSubDiv = document.createElement("div");

		createSubDiv = function (dialogue, currentTime) {
			var result = defaultSubDiv.cloneNode(true);
			result.dialogue = dialogue;
			dialogue.drawTo(result, currentTime);
			wrappers[dialogue.layer][dialogue.alignment].appendChild(result);
			return result;
		};

		defaultSubDiv.constructor.prototype.remove = function () {
			if (this.parentElement !== null) {
				this.parentElement.removeChild(this);
				this.dialogue.erase();
			}
		};
	})();

	var video = document.querySelector("#video");

	var videoMetadataLoaded = false;
	var ass = null;

	var parser = null;
	var rawASS = null;

	var testVideoAndASSLoaded = function () {
		if (videoMetadataLoaded && ass) {
			var videoWidth = video.videoWidth;
			var videoHeight = video.videoHeight;
			document.body.style.width = video.style.width = videoWidth + "px";
			document.body.style.height = video.style.height = videoHeight + "px";
			document.body.style.marginLeft = video.style.marginLeft = (-videoWidth / 2) + "px";

			var subsWrapper = document.querySelector("#subs-wrapper");

			var info = ass.info;
			info.scaleTo(videoWidth, videoHeight);
			var zoom = (1 / info.scaleX);
			var zoomedDiv = document.querySelector(".zoomed");
			zoomedDiv.style.transform = "scale(" + zoom + ")";
			zoomedDiv.style.webkitTransform = "scale(" + zoom + ")";

			info.dpi = parseFloat(getComputedStyle(document.querySelector("#dpi-div")).height.match(/(\d+)px/)[1]);

			var layers = new Set();
			var dialogues = ass.dialogues;
			dialogues.forEach(function (dialogue) {
				layers.add(dialogue.layer);
			});
			layers.iterator().toArray().sort().forEach(function (layer) {
				var i;
				wrappers[layer] = {};
				for (i = 1; i <= 9; ++i) {
					var wrapperDiv = document.createElement("div");
					wrapperDiv.className = "an" + i + " layer" + layer;
					subsWrapper.appendChild(wrapperDiv);
					wrappers[layer][i] = wrapperDiv;
				}
			});

			var allFonts = new Set();
			ass.styles.forEach(function (style) {
				allFonts.add(style.fontName);
			});
			dialogues.forEach(function (dialogue) {
				dialogue.parts.forEach(function (part) {
					if (part instanceof ASS.Tags.FontName) {
						allFonts.add(part.value);
					}
				});
			});
			var numFonts = Array.prototype.filter.call(document.styleSheets, function (stylesheet) {
				return stylesheet.href && stylesheet.href.endsWith("/fonts.css");
			}).reduce(function (previousValue, currentValue) {
				return previousValue + Array.prototype.filter.call(currentValue.cssRules, function (rule) {
					return rule.type === CSSRule.FONT_FACE_RULE && allFonts.has(rule.style.getPropertyValue("font-family").match(/^['"]?(.*?)['"]?$/)[1]);
				}).map(function (fontFaceRule) {
					var xhr = new XMLHttpRequest();
					var fontSrc = fontFaceRule.style.src;
					if (fontSrc) {
						fontSrc = fontSrc.match(/url\((.+)\)$/)[1];
					}
					else {
						fontSrc = fontFaceRule.cssText.match(/url\("?(.+?)"?\)/)[1];
					}
					xhr.open("GET", fontSrc, true);
					xhr.addEventListener("readystatechange", function () {
						if (xhr.readyState === XMLHttpRequest.DONE) {
							--numFonts;
							if (numFonts === 0) {
								video.play();
							}
						}
					});
					xhr.send(null);
					return xhr;
				})
				.length;
			}, 0);
			if (numFonts === 0) {
				video.play();
			}

			var currentTime;
			var currentSubs = [];
			var newSubs = dialogues.toIterable().map(function (entry) {
				return entry[1];
			}).skipWhile(function (dialogue) {
				return dialogue.end < currentTime;
			}).takeWhile(function (dialogue) {
				return dialogue.start <= currentTime;
			}).filter(function (dialogue) {
				return dialogue.end >= currentTime && !dialogue.isDrawn();
			}).map(function (dialogue) {
				return createSubDiv(dialogue, currentTime);
			});
			video.addEventListener("timeupdate", function () {
				currentTime = video.currentTime;

				currentSubs = currentSubs.filter(function (sub) {
					if (sub.dialogue.start <= currentTime && sub.dialogue.end > currentTime) {
						return true;
					}
					else {
						sub.remove();
						return false;
					}
				}).concat(Iterator(newSubs).toArray());
			}, false);

			video.addEventListener("seeking", function () {
				currentSubs.forEach(function (sub) {
					sub.remove();
				});
				currentSubs = [];
			});

			video.addEventListener("pause", function () {
				subsWrapper.className = "paused";
			}, false);

			video.addEventListener("playing", function () {
				subsWrapper.className = "";
			}, false);

			video.addEventListener("click", function () {
				if (video.paused) {
					video.play();
				}
				else {
					video.pause();
				}
			}, false);
		};
	}

	if (video.readyState < HTMLMediaElement.HAVE_METADATA) {
		video.addEventListener("loadedmetadata", function () {
			videoMetadataLoaded = true;
			testVideoAndASSLoaded();
		}, false);
	}
	else {
		videoMetadataLoaded = true;
		testVideoAndASSLoaded();
	}

	var parserRequest = new XMLHttpRequest();
	parserRequest.open("GET", "ass.pegjs", true);
	parserRequest.addEventListener("readystatechange", function () {
		if (parserRequest.readyState === XMLHttpRequest.DONE) {
			parser = PEG.buildParser(parserRequest.responseText);

			if (rawASS) {
				ass = ASS.parse(rawASS, parser);
				testVideoAndASSLoaded();
			}
		}
	}, false);
	parserRequest.send(null);

	var track = document.querySelector("#video > track[data-format='ass']");
	var subsRequest = new XMLHttpRequest();
	subsRequest.open("GET", track.src || track.getAttribute("src"), true);
	subsRequest.addEventListener("readystatechange", function () {
		if (subsRequest.readyState === XMLHttpRequest.DONE) {
			rawASS = subsRequest.responseText;

			if (parser) {
				ass = ASS.parse(rawASS, parser);
				testVideoAndASSLoaded();
			}
		}
	}, false);
	subsRequest.send(null);
}, false);
<commit_msg>Revert "Play/pause video on click."<commit_after>"use strict";

addEventListener("DOMContentLoaded", function () {
	var createSubDiv;
	var wrappers = {};
	(function () {
		var defaultSubDiv = document.createElement("div");

		createSubDiv = function (dialogue, currentTime) {
			var result = defaultSubDiv.cloneNode(true);
			result.dialogue = dialogue;
			dialogue.drawTo(result, currentTime);
			wrappers[dialogue.layer][dialogue.alignment].appendChild(result);
			return result;
		};

		defaultSubDiv.constructor.prototype.remove = function () {
			if (this.parentElement !== null) {
				this.parentElement.removeChild(this);
				this.dialogue.erase();
			}
		};
	})();

	var video = document.querySelector("#video");

	var videoMetadataLoaded = false;
	var ass = null;

	var parser = null;
	var rawASS = null;

	var testVideoAndASSLoaded = function () {
		if (videoMetadataLoaded && ass) {
			var videoWidth = video.videoWidth;
			var videoHeight = video.videoHeight;
			document.body.style.width = video.style.width = videoWidth + "px";
			document.body.style.height = video.style.height = videoHeight + "px";
			document.body.style.marginLeft = video.style.marginLeft = (-videoWidth / 2) + "px";

			var subsWrapper = document.querySelector("#subs-wrapper");

			var info = ass.info;
			info.scaleTo(videoWidth, videoHeight);
			var zoom = (1 / info.scaleX);
			var zoomedDiv = document.querySelector(".zoomed");
			zoomedDiv.style.transform = "scale(" + zoom + ")";
			zoomedDiv.style.webkitTransform = "scale(" + zoom + ")";

			info.dpi = parseFloat(getComputedStyle(document.querySelector("#dpi-div")).height.match(/(\d+)px/)[1]);

			var layers = new Set();
			var dialogues = ass.dialogues;
			dialogues.forEach(function (dialogue) {
				layers.add(dialogue.layer);
			});
			layers.iterator().toArray().sort().forEach(function (layer) {
				var i;
				wrappers[layer] = {};
				for (i = 1; i <= 9; ++i) {
					var wrapperDiv = document.createElement("div");
					wrapperDiv.className = "an" + i + " layer" + layer;
					subsWrapper.appendChild(wrapperDiv);
					wrappers[layer][i] = wrapperDiv;
				}
			});

			var allFonts = new Set();
			ass.styles.forEach(function (style) {
				allFonts.add(style.fontName);
			});
			dialogues.forEach(function (dialogue) {
				dialogue.parts.forEach(function (part) {
					if (part instanceof ASS.Tags.FontName) {
						allFonts.add(part.value);
					}
				});
			});
			var numFonts = Array.prototype.filter.call(document.styleSheets, function (stylesheet) {
				return stylesheet.href && stylesheet.href.endsWith("/fonts.css");
			}).reduce(function (previousValue, currentValue) {
				return previousValue + Array.prototype.filter.call(currentValue.cssRules, function (rule) {
					return rule.type === CSSRule.FONT_FACE_RULE && allFonts.has(rule.style.getPropertyValue("font-family").match(/^['"]?(.*?)['"]?$/)[1]);
				}).map(function (fontFaceRule) {
					var xhr = new XMLHttpRequest();
					var fontSrc = fontFaceRule.style.src;
					if (fontSrc) {
						fontSrc = fontSrc.match(/url\((.+)\)$/)[1];
					}
					else {
						fontSrc = fontFaceRule.cssText.match(/url\("?(.+?)"?\)/)[1];
					}
					xhr.open("GET", fontSrc, true);
					xhr.addEventListener("readystatechange", function () {
						if (xhr.readyState === XMLHttpRequest.DONE) {
							--numFonts;
							if (numFonts === 0) {
								video.play();
							}
						}
					});
					xhr.send(null);
					return xhr;
				})
				.length;
			}, 0);
			if (numFonts === 0) {
				video.play();
			}

			var currentTime;
			var currentSubs = [];
			var newSubs = dialogues.toIterable().map(function (entry) {
				return entry[1];
			}).skipWhile(function (dialogue) {
				return dialogue.end < currentTime;
			}).takeWhile(function (dialogue) {
				return dialogue.start <= currentTime;
			}).filter(function (dialogue) {
				return dialogue.end >= currentTime && !dialogue.isDrawn();
			}).map(function (dialogue) {
				return createSubDiv(dialogue, currentTime);
			});
			video.addEventListener("timeupdate", function () {
				currentTime = video.currentTime;

				currentSubs = currentSubs.filter(function (sub) {
					if (sub.dialogue.start <= currentTime && sub.dialogue.end > currentTime) {
						return true;
					}
					else {
						sub.remove();
						return false;
					}
				}).concat(Iterator(newSubs).toArray());
			}, false);

			video.addEventListener("seeking", function () {
				currentSubs.forEach(function (sub) {
					sub.remove();
				});
				currentSubs = [];
			});

			video.addEventListener("pause", function () {
				subsWrapper.className = "paused";
			}, false);

			video.addEventListener("playing", function () {
				subsWrapper.className = "";
			}, false);
		};
	}

	if (video.readyState < HTMLMediaElement.HAVE_METADATA) {
		video.addEventListener("loadedmetadata", function () {
			videoMetadataLoaded = true;
			testVideoAndASSLoaded();
		}, false);
	}
	else {
		videoMetadataLoaded = true;
		testVideoAndASSLoaded();
	}

	var parserRequest = new XMLHttpRequest();
	parserRequest.open("GET", "ass.pegjs", true);
	parserRequest.addEventListener("readystatechange", function () {
		if (parserRequest.readyState === XMLHttpRequest.DONE) {
			parser = PEG.buildParser(parserRequest.responseText);

			if (rawASS) {
				ass = ASS.parse(rawASS, parser);
				testVideoAndASSLoaded();
			}
		}
	}, false);
	parserRequest.send(null);

	var track = document.querySelector("#video > track[data-format='ass']");
	var subsRequest = new XMLHttpRequest();
	subsRequest.open("GET", track.src || track.getAttribute("src"), true);
	subsRequest.addEventListener("readystatechange", function () {
		if (subsRequest.readyState === XMLHttpRequest.DONE) {
			rawASS = subsRequest.responseText;

			if (parser) {
				ass = ASS.parse(rawASS, parser);
				testVideoAndASSLoaded();
			}
		}
	}, false);
	subsRequest.send(null);
}, false);
<|endoftext|>


<commit_before>/*
 *  index.js
 *
 *  David Janes
 *  IOTDB.org
 *  2015-11-29
 *
 *  Copyright [2013-2015] [David P. Janes]
 *
 *  Licensed 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.
 */

"use strict";

exports.Bridge = require('./KNXBridge').Bridge;
exports.bindings = [
    require('./models/KnxValueBoolean').binding,
    require('./models/KnxValueInteger').binding,
    require('./models/KnxLight').binding,
    require('./models/KnxShutterRaw').binding,
    require('./models/KnxShutter').binding,
    require('./models/KnxSwitch').binding,
    require('./models/KnxDimmer').binding,
];

exports.iotdb = require("iotdb");
exports.wrap = function (name, initd) {
    return exports.iotdb.make_wrap(name, exports.bindings, initd);
};
<commit_msg>remove all the 'wrap' exports that were never used<commit_after>/*
 *  index.js
 *
 *  David Janes
 *  IOTDB.org
 *  2015-11-29
 *
 *  Copyright [2013-2015] [David P. Janes]
 *
 *  Licensed 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.
 */

"use strict";

exports.Bridge = require('./KNXBridge').Bridge;
exports.bindings = [
    require('./models/KnxValueBoolean').binding,
    require('./models/KnxValueInteger').binding,
    require('./models/KnxLight').binding,
    require('./models/KnxShutterRaw').binding,
    require('./models/KnxShutter').binding,
    require('./models/KnxSwitch').binding,
    require('./models/KnxDimmer').binding,
];
<|endoftext|>


<commit_before>var Botkit = require('botkit')

var token = process.env.SLACK_TOKEN

var races_original = [
	"The Barony of Letnev",
	"The Clan of Saar",
	"The Emirates of Hacan",
	"The Federation of Sol",
	"The Mentak Coalition",
	"The Naalu Collective",
	"The Nekro Virus",
	"The Sardakk N’orr",
	"The Universities of Jol-Nar",
	"The Winnu",
	"The Xxcha Kingdom",
	"The Yssaril Tribes",
	"The Brotherhood of Yin",
	"The Embers of Muaat",
	"The Ghosts of Creuss",
	"The L1z1x Mindnet",
	"The Arborec"
  ]
var races = races_original
var currently_choosing
var current_choices = []
var number_of_choices = 3

var controller = Botkit.slackbot({
  // reconnect to Slack RTM when connection goes bad
  retry: Infinity,
  debug: false
})

// Assume single team mode if we have a SLACK_TOKEN
if (token) {
  console.log('Starting in single-team mode')
  console.log("cause there's this token" + token)
  controller.spawn({
    token: token,
    retry: Infinity
  }).startRTM(function (err, bot, payload) {
    if (err) {
      throw new Error(err)
    }

    console.log('Connected to Slack RTM')
  })
// Otherwise assume multi-team mode - setup beep boop resourcer connection
} else {
  console.log('Starting in Beep Boop multi-team mode')
  require('beepboop-botkit').start(controller, { debug: true })
}

controller.on('bot_channel_join', function (bot, message) {
  bot.reply(message, "I'm here!")
})

controller.hears(['list'], ['direct_mention','direct_message'], function (bot, message) {
  var reply = "Here are the currently available races:\n"
  races.forEach(function(item) {
    reply += "* " + item + "\n";
  })
  bot.reply(message, reply);
})

controller.hears(['do @(.*)'], ['direct_mention'], function (bot, message) {
/*
  const { user, channel, text } = message;
  const userData = text.match(/<@([A-Z0–9]{9})>/); // parse the text for user's 9 character id
*/
  var reply = "I heard to do '" + message.match[1] + "'.";
  bot.reply(message, reply);
})

controller.hears(['hello', 'hi'], ['direct_mention'], function (bot, message) {
  bot.reply(message, 'Hello.')
})

controller.hears(['hello', 'hi'], ['direct_message'], function (bot, message) {
  bot.reply(message, 'Hello.')
  bot.reply(message, 'It\'s nice to talk to you directly.')
})

controller.hears('.*', ['mention'], function (bot, message) {
  bot.reply(message, 'You really do care about me. :heart:')
})

controller.hears('help', ['direct_message', 'direct_mention'], function (bot, message) {
  var help = 'I will respond to the following messages: \n' +
      '`bot hi` for a simple message.\n' +
      '`bot attachment` to see a Slack attachment message.\n' +
      '`@<your bot\'s name>` to demonstrate detecting a mention.\n' +
      '`bot help` to see this again.'
  bot.reply(message, help)
})

controller.hears(['attachment'], ['direct_message', 'direct_mention'], function (bot, message) {
  var text = 'Beep Beep Boop is a ridiculously simple hosting platform for your Slackbots.'
  var attachments = [{
    fallback: text,
    pretext: 'We bring bots to life. :sunglasses: :thumbsup:',
    title: 'Host, deploy and share your bot in seconds.',
    image_url: 'https://storage.googleapis.com/beepboophq/_assets/bot-1.22f6fb.png',
    title_link: 'https://beepboophq.com/',
    text: text,
    color: '#7CD197'
  }]

  bot.reply(message, {
    attachments: attachments
  }, function (err, resp) {
    console.log(err, resp)
  })
})

controller.hears('.*', ['direct_message', 'direct_mention'], function (bot, message) {
  bot.reply(message, 'Sorry <@' + message.user + '>, I don\'t understand. \n')
})
<commit_msg>Try to get more info about the message object<commit_after>var Botkit = require('botkit')

var token = process.env.SLACK_TOKEN

var races_original = [
	"The Barony of Letnev",
	"The Clan of Saar",
	"The Emirates of Hacan",
	"The Federation of Sol",
	"The Mentak Coalition",
	"The Naalu Collective",
	"The Nekro Virus",
	"The Sardakk N’orr",
	"The Universities of Jol-Nar",
	"The Winnu",
	"The Xxcha Kingdom",
	"The Yssaril Tribes",
	"The Brotherhood of Yin",
	"The Embers of Muaat",
	"The Ghosts of Creuss",
	"The L1z1x Mindnet",
	"The Arborec"
  ]
var races = races_original
var currently_choosing
var current_choices = []
var number_of_choices = 3

var controller = Botkit.slackbot({
  // reconnect to Slack RTM when connection goes bad
  retry: Infinity,
  debug: false
})

// Assume single team mode if we have a SLACK_TOKEN
if (token) {
  console.log('Starting in single-team mode')
  console.log("cause there's this token" + token)
  controller.spawn({
    token: token,
    retry: Infinity
  }).startRTM(function (err, bot, payload) {
    if (err) {
      throw new Error(err)
    }

    console.log('Connected to Slack RTM')
  })
// Otherwise assume multi-team mode - setup beep boop resourcer connection
} else {
  console.log('Starting in Beep Boop multi-team mode')
  require('beepboop-botkit').start(controller, { debug: true })
}

controller.on('bot_channel_join', function (bot, message) {
  bot.reply(message, "I'm here!")
})

controller.hears(['list'], ['direct_mention','direct_message'], function (bot, message) {
  var reply = "Here are the currently available races:\n"
  races.forEach(function(item) {
    reply += "* " + item + "\n";
  })
  bot.reply(message, reply);
})

controller.hears(['do'], ['direct_mention'], function (bot, message) {
/*
  const { user, channel, text } = message;
  const userData = text.match(/<@([A-Z0–9]{9})>/); // parse the text for user's 9 character id
*/
  var reply = "I heard " + message.match[0] + ".\n";
  reply += "So that's the text '" + message.text + "'.";
  bot.reply(message, reply);
})

controller.hears(['hello', 'hi'], ['direct_mention'], function (bot, message) {
  bot.reply(message, 'Hello.')
})

controller.hears(['hello', 'hi'], ['direct_message'], function (bot, message) {
  bot.reply(message, 'Hello.')
  bot.reply(message, 'It\'s nice to talk to you directly.')
})

controller.hears('.*', ['mention'], function (bot, message) {
  bot.reply(message, 'You really do care about me. :heart:')
})

controller.hears('help', ['direct_message', 'direct_mention'], function (bot, message) {
  var help = 'I will respond to the following messages: \n' +
      '`bot hi` for a simple message.\n' +
      '`bot attachment` to see a Slack attachment message.\n' +
      '`@<your bot\'s name>` to demonstrate detecting a mention.\n' +
      '`bot help` to see this again.'
  bot.reply(message, help)
})

controller.hears(['attachment'], ['direct_message', 'direct_mention'], function (bot, message) {
  var text = 'Beep Beep Boop is a ridiculously simple hosting platform for your Slackbots.'
  var attachments = [{
    fallback: text,
    pretext: 'We bring bots to life. :sunglasses: :thumbsup:',
    title: 'Host, deploy and share your bot in seconds.',
    image_url: 'https://storage.googleapis.com/beepboophq/_assets/bot-1.22f6fb.png',
    title_link: 'https://beepboophq.com/',
    text: text,
    color: '#7CD197'
  }]

  bot.reply(message, {
    attachments: attachments
  }, function (err, resp) {
    console.log(err, resp)
  })
})

controller.hears('.*', ['direct_message', 'direct_mention'], function (bot, message) {
  bot.reply(message, 'Sorry <@' + message.user + '>, I don\'t understand. \n')
})
<|endoftext|>


<commit_before>
/**
 * Module dependencies.
 */

var debug = require('debug')('koa-views');
var resolve = require('path').resolve;
var dirname = require('path').dirname;
var assign = require('object-assign');
var fmt = require('util').format;
var join = require('path').join;
var cons = require('co-views');
var send = require('koa-send');

/**
 * Add `render` method
 *
 * @param {String} path (optional)
 * @param {Object} opts (optional)
 * @api public
 */

module.exports = function (path, opts) {
  var base = dirname(module.parent.filename);

  // set path relative to the directory the function was called + path
  if (!path || typeof path == 'object') {
    opts = path;
    path = base;
  } else {
    path = resolve(base, path);
  }

  opts = opts || {};

  // default extension to `html`
  if (!opts.default) opts.default = 'html';

  if (opts) debug(fmt('options: %s', opts));

  return function *views (next) {
    if (this.render) return;

    /**
     * App-specific `locals`, but honor upstream
     * middlewares that may have already set this.locals.
     */

    this.locals = this.locals || {};

    /**
     * Render `view` with `locals`.
     *
     * @param {String} view
     * @param {Object} locals
     * @return {GeneratorFunction}
     * @api public
     */

    this.render = function *(view, locals) {
      var ext = opts.default;
      if(view[view.length - 1] === '/'){
        view += 'index';
      }
      var file = fmt('%s.%s', view, ext);

      locals = locals || {};
      locals = assign(locals, this.locals);

      debug(fmt('render `%s` with %j', file, locals));

      if (ext == 'html' && !opts.map) {
        yield send(this, join(path, file));
      } else {
        var render = cons(path, opts);
        this.body = yield render(view, locals);
      }

      this.type = 'text/html';
    }

    yield next;
  }
}
<commit_msg>instantiate co-views out of this.render<commit_after>
/**
 * Module dependencies.
 */

var debug = require('debug')('koa-views');
var resolve = require('path').resolve;
var dirname = require('path').dirname;
var assign = require('object-assign');
var fmt = require('util').format;
var join = require('path').join;
var cons = require('co-views');
var send = require('koa-send');

/**
 * Add `render` method
 *
 * @param {String} path (optional)
 * @param {Object} opts (optional)
 * @api public
 */

module.exports = function (path, opts) {
  var base = dirname(module.parent.filename);

  // set path relative to the directory the function was called + path
  if (!path || typeof path == 'object') {
    opts = path;
    path = base;
  } else {
    path = resolve(base, path);
  }

  opts = opts || {};

  // default extension to `html`
  if (!opts.default) opts.default = 'html';

  debug(fmt('options: %s', opts));

  return function *views (next) {
    if (this.render) return;

    var render = cons(path, opts);

    /**
     * App-specific `locals`, but honor upstream
     * middlewares that may have already set this.locals.
     */

    this.locals = this.locals || {};

    /**
     * Render `view` with `locals`.
     *
     * @param {String} view
     * @param {Object} locals
     * @return {GeneratorFunction}
     * @api public
     */

    this.render = function *(view, locals) {
      var ext = opts.default;
      if(view[view.length - 1] === '/'){
        view += 'index';
      }
      var file = fmt('%s.%s', view, ext);

      locals = locals || {};
      locals = assign(locals, this.locals);

      debug(fmt('render `%s` with %j', file, locals));

      if (ext == 'html' && !opts.map) {
        yield send(this, join(path, file));
      } else {
        this.body = yield render(view, locals);
      }

      this.type = 'text/html';
    }

    yield next;
  }
}
<|endoftext|>


<commit_before>const chalk = require('chalk');
const gutil = require('gulp-util');
const path = require('path');
const plur = require('plur');
const sharp = require('sharp');
const through = require('through2-concurrent');
const async = require('async');

const PLUGIN_NAME = require('./package.json').name;
const VALID_EXTS = ['.png'];

/**
 * Gamma correction for semi-transparent pixels.
 * @param  {Number}  value
 * @return {Number}
 */
function gammaCorrection(value) {
  return (Math.pow(value / 255, 0.45) * 255) | 0;
}

/**
 * Get SVG content.
 * @param  {Buffer}  buffer
 * @param  {Object}  params  {cwd, base, path, width, height}
 * @return {Buffer}
 */
function getSvg(buffer, params) {
  let newFile = new gutil.File({
    cwd: params.cwd,
    base: params.base,
    path: params.path,
    contents: Buffer.from(`<svg width="${params.width}" height="${params.height / 2}" viewBox="0 0 ${params.width} ${params.height / 2}" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <defs>
    <filter id="zorrosvg" primitiveUnits="objectBoundingBox">
      <feOffset in="SourceGraphic" result="bottom-half" dy="-0.5"></feOffset>
      <feColorMatrix type="matrix" in="bottom-half" result="luma-mask" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0"></feColorMatrix>
      <feComposite in="SourceGraphic" in2="luma-mask" operator="in"></feComposite>
    </filter>
  </defs>
  <image width="100%" height="200%" filter="url(#zorrosvg)" xlink:href="data:image/jpeg;base64,${buffer.toString('base64')}"></image>
</svg>`)
  });

  newFile.basename = newFile.basename.replace('.png', '.svg');

  return newFile;
}

/**
 * Generate ZorroSVG luminance masks from PNGs.
 *
 * Based on the following plugins:
 * https://github.com/sindresorhus/gulp-imagemin/blob/master/index.js
 * https://github.com/mahnunchik/gulp-responsive/blob/master/lib/index.js
 *
 * @param  {Object}  options
 */
module.exports = (options) => {
  options = Object.assign({
    verbose: process.argv.indexOf('--verbose') !== -1
  }, options);

  let totalFiles = 0;

  return through.obj((file, encoding, callback) => {
    if (file.isNull()) {
      callback(null, file);
      return;
    }

    if (file.isStream()) {
      callback(new gutil.PluginError(PLUGIN_NAME, 'Streaming not supported'));
      return;
    }

    if (VALID_EXTS.indexOf(path.extname(file.path).toLowerCase()) === -1) {
      if (options.verbose) {
        gutil.log(`${PLUGIN_NAME}: Skipping unsupported image ${chalk.blue(file.relative)}`);
      }
      callback(null, file);
      return;
    }

    /**
     * ZorroSVG.
     * Note: Assumes image has an alpha channel.
     */
    let image = sharp(file.contents);
    let errorPrefix = `${chalk.red('✘')} File \`${file.relative}\`: `;

    // Create a canvas 2x the height of the original image.
    // Keep the original image on top and paste a copy of it on the bottom.
    image.toBuffer(function(error, imageBuffer, imageInfo) {
      if (error) {
        error.message = errorPrefix + error.message;
        return callback(new gutil.PluginError(PLUGIN_NAME, error, { showStack: true }));
      }
      // Force transparent background
      return image.background({ r: 0, g: 0, b: 0, alpha: 0 })
      // Double canvas height
        .extend({ top: 0, right: 0, bottom: imageInfo.height, left: 0 })
      // Paste image
        .overlayWith(imageBuffer, { top: imageInfo.height, left: 0 })
      // Get raw pixel data to manipulate
        .raw().toBuffer(function(error, compositeBuffer, compositeInfo) {
        if (error) {
          error.message = errorPrefix + error.message;
          return callback(new gutil.PluginError(PLUGIN_NAME, error, { showStack: true }));
        }

        // Make the original image fully opaque
        for (let i = 0; i < compositeBuffer.length / 2; i = i + 4) {
          compositeBuffer[i + 3] = 255;
        }
        // Create a luminance mask based on the original image's alpha channel
        // Add gamma correction for semi-transparent pixels
        for (let i = compositeBuffer.length / 2; i < compositeBuffer.length; i = i + 4) {
          let alpha = gammaCorrection(compositeBuffer[i + 3]);
          compositeBuffer[i + 0] = alpha;
          compositeBuffer[i + 1] = alpha;
          compositeBuffer[i + 2] = alpha;
          compositeBuffer[i + 3] = 255;
        }

        // Compress as JPG
        sharp(compositeBuffer, { raw: { width: compositeInfo.width, height: compositeInfo.height, channels: compositeInfo.channels } })
          .jpeg().toBuffer(function(error, finalBuffer, finalInfo) {
          if (error) {
            error.message = errorPrefix + error.message;
            return callback(new gutil.PluginError(PLUGIN_NAME, error, { showStack: true }));
          }

          // Final SVG conversion
          let newFile = getSvg(finalBuffer, {
            cwd: file.cwd,
            base: file.base,
            path: file.path,
            width: finalInfo.width,
            height: finalInfo.height
          });

          totalFiles++;

          if (options.verbose) {
            gutil.log(`${PLUGIN_NAME}: ${chalk.green('✔')} ${chalk.blue(file.relative + ' -> ' + newFile.relative)}`);
          }

          // TODO: Responsive sizes
          callback(null, newFile);
        });
      });
    });
  }, callback => {
    gutil.log(`${PLUGIN_NAME}: Generated ${totalFiles} ZorroSVG ${plur('file', totalFiles)}.`);
    callback();
  });
};
<commit_msg>Replace confusing bitwise truncation with Math.floor<commit_after>const chalk = require('chalk');
const gutil = require('gulp-util');
const path = require('path');
const plur = require('plur');
const sharp = require('sharp');
const through = require('through2-concurrent');
const async = require('async');

const PLUGIN_NAME = require('./package.json').name;
const VALID_EXTS = ['.png'];

/**
 * Gamma correction for semi-transparent pixels.
 * @param  {Number}  value
 * @return {Number}
 */
function gammaCorrection(value) {
  return Math.floor(Math.pow(value / 255, 0.45) * 255);
}

/**
 * Get SVG content.
 * @param  {Buffer}  buffer
 * @param  {Object}  params  {cwd, base, path, width, height}
 * @return {Buffer}
 */
function getSvg(buffer, params) {
  let newFile = new gutil.File({
    cwd: params.cwd,
    base: params.base,
    path: params.path,
    contents: Buffer.from(`<svg width="${params.width}" height="${params.height / 2}" viewBox="0 0 ${params.width} ${params.height / 2}" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <defs>
    <filter id="zorrosvg" primitiveUnits="objectBoundingBox">
      <feOffset in="SourceGraphic" result="bottom-half" dy="-0.5"></feOffset>
      <feColorMatrix type="matrix" in="bottom-half" result="luma-mask" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0"></feColorMatrix>
      <feComposite in="SourceGraphic" in2="luma-mask" operator="in"></feComposite>
    </filter>
  </defs>
  <image width="100%" height="200%" filter="url(#zorrosvg)" xlink:href="data:image/jpeg;base64,${buffer.toString('base64')}"></image>
</svg>`)
  });

  newFile.basename = newFile.basename.replace('.png', '.svg');

  return newFile;
}

/**
 * Generate ZorroSVG luminance masks from PNGs.
 *
 * Based on the following plugins:
 * https://github.com/sindresorhus/gulp-imagemin/blob/master/index.js
 * https://github.com/mahnunchik/gulp-responsive/blob/master/lib/index.js
 *
 * @param  {Object}  options
 */
module.exports = (options) => {
  options = Object.assign({
    verbose: process.argv.indexOf('--verbose') !== -1
  }, options);

  let totalFiles = 0;

  return through.obj((file, encoding, callback) => {
    if (file.isNull()) {
      callback(null, file);
      return;
    }

    if (file.isStream()) {
      callback(new gutil.PluginError(PLUGIN_NAME, 'Streaming not supported'));
      return;
    }

    if (VALID_EXTS.indexOf(path.extname(file.path).toLowerCase()) === -1) {
      if (options.verbose) {
        gutil.log(`${PLUGIN_NAME}: Skipping unsupported image ${chalk.blue(file.relative)}`);
      }
      callback(null, file);
      return;
    }

    /**
     * ZorroSVG.
     * Note: Assumes image has an alpha channel.
     */
    let image = sharp(file.contents);
    let errorPrefix = `${chalk.red('✘')} File \`${file.relative}\`: `;

    // Create a canvas 2x the height of the original image.
    // Keep the original image on top and paste a copy of it on the bottom.
    image.toBuffer(function(error, imageBuffer, imageInfo) {
      if (error) {
        error.message = errorPrefix + error.message;
        return callback(new gutil.PluginError(PLUGIN_NAME, error, { showStack: true }));
      }
      // Force transparent background
      return image.background({ r: 0, g: 0, b: 0, alpha: 0 })
      // Double canvas height
        .extend({ top: 0, right: 0, bottom: imageInfo.height, left: 0 })
      // Paste image
        .overlayWith(imageBuffer, { top: imageInfo.height, left: 0 })
      // Get raw pixel data to manipulate
        .raw().toBuffer(function(error, compositeBuffer, compositeInfo) {
        if (error) {
          error.message = errorPrefix + error.message;
          return callback(new gutil.PluginError(PLUGIN_NAME, error, { showStack: true }));
        }

        // Make the original image fully opaque
        for (let i = 0; i < compositeBuffer.length / 2; i = i + 4) {
          compositeBuffer[i + 3] = 255;
        }
        // Create a luminance mask based on the original image's alpha channel
        // Add gamma correction for semi-transparent pixels
        for (let i = compositeBuffer.length / 2; i < compositeBuffer.length; i = i + 4) {
          let alpha = gammaCorrection(compositeBuffer[i + 3]);
          compositeBuffer[i + 0] = alpha;
          compositeBuffer[i + 1] = alpha;
          compositeBuffer[i + 2] = alpha;
          compositeBuffer[i + 3] = 255;
        }

        // Compress as JPG
        sharp(compositeBuffer, { raw: { width: compositeInfo.width, height: compositeInfo.height, channels: compositeInfo.channels } })
          .jpeg().toBuffer(function(error, finalBuffer, finalInfo) {
          if (error) {
            error.message = errorPrefix + error.message;
            return callback(new gutil.PluginError(PLUGIN_NAME, error, { showStack: true }));
          }

          // Final SVG conversion
          let newFile = getSvg(finalBuffer, {
            cwd: file.cwd,
            base: file.base,
            path: file.path,
            width: finalInfo.width,
            height: finalInfo.height
          });

          totalFiles++;

          if (options.verbose) {
            gutil.log(`${PLUGIN_NAME}: ${chalk.green('✔')} ${chalk.blue(file.relative + ' -> ' + newFile.relative)}`);
          }

          // TODO: Responsive sizes
          callback(null, newFile);
        });
      });
    });
  }, callback => {
    gutil.log(`${PLUGIN_NAME}: Generated ${totalFiles} ZorroSVG ${plur('file', totalFiles)}.`);
    callback();
  });
};
<|endoftext|>


<commit_before>var vdf = require('vdf');
var fs = require('fs');
var SteamID = require('steamid');

var protomask = 0x80000000;

var Language = require(__dirname + '/language.js');
var Protos = require(__dirname + '/protos.js');

var base_gcmessages = Protos.base_gcmessages;
var tf_gcmessages = Protos.tf_gcmessages;

module.exports = TeamFortress2;

require('util').inherits(TeamFortress2, require('events').EventEmitter);

function TeamFortress2(steam) {
	this._steam = steam;
	this.haveGCSession = false;
	this._hadGCSession = false;

	var self = this;

	// "extend" the default steam.gamesPlayed function so we can catch when TF2 starts up
	var gamesPlayed = steam.gamesPlayed;
	steam.gamesPlayed = function(appids) {
		if(appids.indexOf(440) != -1) {
			if(!self.haveGCSession) {
				self._connect();
			}
		} else {
			if(self._helloInterval) {
				clearInterval(self._helloInterval);
				self._helloInterval = null;
			}

			self.haveGCSession = false;
			self._hadGCSession = false;
		}

		gamesPlayed.call(steam, appids);
	};

	steam.on('fromGC', function(appID, type, body) {
		if(appID != 440) {
			// Not from the TF2 GC
			return;
		}

		var protobuf = !!(type & protomask);
		type &= ~protomask;

		if(self._handlers[type]) {
			self._handlers[type].call(self, body);
		} else {
			var msgName = type;
			for(var i in Language) {
				if(Language[i] == type) {
					msgName = i;
					break;
				}
			}

			self.emit('debug', "Got unhandled GC message " + msgName + (protobuf ? " (protobuf)" : ""));
		}
	});

	steam.on('loggedOff', function() {
		self._hadGCSession = self.haveGCSession;
		if(self.haveGCSession) {
			self.emit('disconnectedFromGC', TeamFortress2.GCGoodbyeReason.NO_SESSION);
			self.haveGCSession = false;
		}
	});
	steam.on('error', function(e) {
		self._hadGCSession = false;
		if(self.haveGCSession) {
			self.emit('disconnectedFromGC', TeamFortress2.GCGoodbyeReason.NO_SESSION);
			self.haveGCSession = false;
		}
	});

	steam.on('loggedOn', function() {
		if(self._hadGCSession) {
			self._connect();
			self._hadGCSession = false;
		}
	});
}

TeamFortress2.prototype._connect = function() {
	var self = this;
	this._helloInterval = setInterval(function() {
		if(self.haveGCSession) {
			clearInterval(self._helloInterval);
			self._helloInterval = null;
			return;
		}

		self._send(Language.ClientHello, base_gcmessages.CMsgClientHello, {});
	}, 2000);
};

TeamFortress2.prototype._send = function(type, protobuf, body) {
	var msgName = type;
	for(var i in Language) {
		if(Language[i] == type) {
			msgName = i;
			break;
		}
	}

	this.emit('debug', "Sending GC message " + msgName);

	if(protobuf) {
		this._steam.toGC(440, type | protomask, protobuf.serialize(body));
	} else {
		this._steam.toGC(440, type, body);
	}
};

TeamFortress2.prototype.setLang = function(langFile) {
	var lang = vdf.parse(langFile);
	// The vdf parser appears to add some random characters and quotes to the root 'lang' key, so we'll just use a loop to find it
	for(var i in lang) {
		this.lang = lang[i].Tokens;
	}
};

TeamFortress2.prototype.craft = function(items, recipe) {
	var buffer = new Buffer(2 + 2 + (8 * items.length));
	buffer.writeInt16LE(recipe || -2, 0); // -2 is wildcard
	buffer.writeInt16LE(items.length, 2);
	for(var i = 0; i < items.length; i++) {
		buffer.writeUInt64LE(items[i], 4 + (i * 8));
	}

	this._send(Language.Craft, null, buffer);
};

TeamFortress2.prototype.trade = function(steamID) {
	var buffer = new Buffer(12);
	buffer.writeUInt32LE(0, 0);
	buffer.writeUInt64LE(steamID, 4);
	this._send(Language.Trading_InitiateTradeRequest, null, buffer);
};

TeamFortress2.prototype.cancelTradeRequest = function() {
	var buffer = new Buffer(0);
	this._send(Language.Trading_CancelSession, null, buffer);
};

TeamFortress2.prototype.respondToTrade = function(tradeID, accept) {
	var buffer = new Buffer(8);
	buffer.writeUInt32LE(accept ? TeamFortress2.TradeResponse.Accepted : TeamFortress2.TradeResponse.Declined, 0);
	buffer.writeUInt32LE(tradeID, 4);
	this._send(Language.Trading_InitiateTradeResponse, null, buffer);
};

TeamFortress2.prototype.setStyle = function(item, style) {
	var buffer = new Buffer(12);
	buffer.writeUInt64LE(item, 0);
	buffer.writeUInt32LE(style, 8);
	this._send(Language.SetItemStyle, null, buffer);
};

TeamFortress2.prototype.setPosition = function(item, position) {
	var buffer = new Buffer(16);
	buffer.writeUInt64LE(item, 0);
	buffer.writeUInt64LE(position, 8);
	this._send(Language.SetSingleItemPosition, null, buffer);
};

TeamFortress2.prototype.setPositions = function(items) {
	this._send(Language.SetItemPositions, base_gcmessages.CMsgSetItemPositions, {"itemPositions": items});
};

TeamFortress2.prototype.deleteItem = function(item) {
	var buffer = new Buffer(8);
	buffer.writeUInt64LE(item);
	this._send(Language.Delete, null, buffer);
};

TeamFortress2.prototype.wrapItem = function(wrapID, itemID) {
	var buffer = new Buffer(16);
	buffer.writeUInt64LE(wrapID, 0);
	buffer.writeUInt64LE(itemID, 8);
	this._send(Language.GiftWrapItem, null, buffer);
};

TeamFortress2.prototype.deliverGift = function(gift, steamID) {
	var buffer = new Buffer(16);
	buffer.writeUInt64LE(gift, 0);
	buffer.writeUInt64LE(steamID, 8);
	this._send(Language.DeliverGift, null, buffer);
};

TeamFortress2.prototype.unwrapGift = function(gift) {
	var buffer = new Buffer(8);
	buffer.writeUInt64LE(gift);
	this._send(Language.UnwrapGiftRequest, null, buffer);
};

TeamFortress2.prototype.useItem = function(item) {
	this._send(Language.UseItemRequest, base_gcmessages.CMsgUseItem, {"itemId": item});
};

TeamFortress2.prototype.sortBackpack = function(sortType) {
	this._send(Language.SortItems, base_gcmessages.CMsgSortItems, {"sortType": sortType});
};

TeamFortress2.prototype.sendProfessorSpeks = function(steamID) {
	this._send(Language.FreeTrial_ChooseMostHelpfulFriend, tf_gcmessages.CMsgTFFreeTrialChooseMostHelpfulFriend, {"accountIdFriend": new SteamID(steamID).accountid});
};

TeamFortress2.prototype.createServerIdentity = function() {
	this._send(Language.GameServer_CreateIdentity, tf_gcmessages.CMsgGC_GameServer_CreateIdentity, {"accountId": new SteamID(this._steam.steamID).accountid});
};

TeamFortress2.prototype.getRegisteredServers = function() {
	this._send(Language.GameServer_List, tf_gcmessages.CMsgGC_GameServer_List, {"accountId": new SteamID(this._steam.steamID).accountid});
};

TeamFortress2.prototype.resetServerIdentity = function(id) {
	this._send(Language.GameServer_ResetIdentity, tf_gcmessages.CMsgGC_GameServer_ResetIdentity, {"gameServerAccountId": id});
};

TeamFortress2.prototype.openCrate = function(keyID, crateID) {
	var buffer = new Buffer(16);
	buffer.writeUInt64LE(keyID, 0);
	buffer.writeUInt64LE(crateID, 8);
	this._send(Language.UnlockCrate, null, buffer);
};

TeamFortress2.prototype.requestSpyVsEngiWarStats = function() {
	this._send(Language.SpyVsEngyWar_RequestGlobalStats, tf_gcmessages.CGCMsgGC_SpyVsEngyWar_RequestGlobalStats, {});
};

TeamFortress2.prototype._handlers = {};

require('./enums.js');
require('./handlers.js');<commit_msg>Increased time between ClientHello messages to 5 seconds<commit_after>var vdf = require('vdf');
var fs = require('fs');
var SteamID = require('steamid');

var protomask = 0x80000000;

var Language = require(__dirname + '/language.js');
var Protos = require(__dirname + '/protos.js');

var base_gcmessages = Protos.base_gcmessages;
var tf_gcmessages = Protos.tf_gcmessages;

module.exports = TeamFortress2;

require('util').inherits(TeamFortress2, require('events').EventEmitter);

function TeamFortress2(steam) {
	this._steam = steam;
	this.haveGCSession = false;
	this._hadGCSession = false;

	var self = this;

	// "extend" the default steam.gamesPlayed function so we can catch when TF2 starts up
	var gamesPlayed = steam.gamesPlayed;
	steam.gamesPlayed = function(appids) {
		if(appids.indexOf(440) != -1) {
			if(!self.haveGCSession) {
				self._connect();
			}
		} else {
			if(self._helloInterval) {
				clearInterval(self._helloInterval);
				self._helloInterval = null;
			}

			self.haveGCSession = false;
			self._hadGCSession = false;
		}

		gamesPlayed.call(steam, appids);
	};

	steam.on('fromGC', function(appID, type, body) {
		if(appID != 440) {
			// Not from the TF2 GC
			return;
		}

		var protobuf = !!(type & protomask);
		type &= ~protomask;

		if(self._handlers[type]) {
			self._handlers[type].call(self, body);
		} else {
			var msgName = type;
			for(var i in Language) {
				if(Language[i] == type) {
					msgName = i;
					break;
				}
			}

			self.emit('debug', "Got unhandled GC message " + msgName + (protobuf ? " (protobuf)" : ""));
		}
	});

	steam.on('loggedOff', function() {
		self._hadGCSession = self.haveGCSession;
		if(self.haveGCSession) {
			self.emit('disconnectedFromGC', TeamFortress2.GCGoodbyeReason.NO_SESSION);
			self.haveGCSession = false;
		}
	});

	steam.on('error', function(e) {
		self._hadGCSession = false;
		if(self.haveGCSession) {
			self.emit('disconnectedFromGC', TeamFortress2.GCGoodbyeReason.NO_SESSION);
			self.haveGCSession = false;
		}
	});

	steam.on('loggedOn', function() {
		if(self._hadGCSession) {
			self._connect();
			self._hadGCSession = false;
		}
	});
}

TeamFortress2.prototype._connect = function() {
	var self = this;
	this._helloInterval = setInterval(function() {
		if(self.haveGCSession) {
			clearInterval(self._helloInterval);
			self._helloInterval = null;
			return;
		}

		self._send(Language.ClientHello, base_gcmessages.CMsgClientHello, {});
	}, 5000);
};

TeamFortress2.prototype._send = function(type, protobuf, body) {
	var msgName = type;
	for(var i in Language) {
		if(Language[i] == type) {
			msgName = i;
			break;
		}
	}

	this.emit('debug', "Sending GC message " + msgName);

	if(protobuf) {
		this._steam.toGC(440, type | protomask, protobuf.serialize(body));
	} else {
		this._steam.toGC(440, type, body);
	}
};

TeamFortress2.prototype.setLang = function(langFile) {
	var lang = vdf.parse(langFile);
	// The vdf parser appears to add some random characters and quotes to the root 'lang' key, so we'll just use a loop to find it
	for(var i in lang) {
		this.lang = lang[i].Tokens;
	}
};

TeamFortress2.prototype.craft = function(items, recipe) {
	var buffer = new Buffer(2 + 2 + (8 * items.length));
	buffer.writeInt16LE(recipe || -2, 0); // -2 is wildcard
	buffer.writeInt16LE(items.length, 2);
	for(var i = 0; i < items.length; i++) {
		buffer.writeUInt64LE(items[i], 4 + (i * 8));
	}

	this._send(Language.Craft, null, buffer);
};

TeamFortress2.prototype.trade = function(steamID) {
	var buffer = new Buffer(12);
	buffer.writeUInt32LE(0, 0);
	buffer.writeUInt64LE(steamID, 4);
	this._send(Language.Trading_InitiateTradeRequest, null, buffer);
};

TeamFortress2.prototype.cancelTradeRequest = function() {
	var buffer = new Buffer(0);
	this._send(Language.Trading_CancelSession, null, buffer);
};

TeamFortress2.prototype.respondToTrade = function(tradeID, accept) {
	var buffer = new Buffer(8);
	buffer.writeUInt32LE(accept ? TeamFortress2.TradeResponse.Accepted : TeamFortress2.TradeResponse.Declined, 0);
	buffer.writeUInt32LE(tradeID, 4);
	this._send(Language.Trading_InitiateTradeResponse, null, buffer);
};

TeamFortress2.prototype.setStyle = function(item, style) {
	var buffer = new Buffer(12);
	buffer.writeUInt64LE(item, 0);
	buffer.writeUInt32LE(style, 8);
	this._send(Language.SetItemStyle, null, buffer);
};

TeamFortress2.prototype.setPosition = function(item, position) {
	var buffer = new Buffer(16);
	buffer.writeUInt64LE(item, 0);
	buffer.writeUInt64LE(position, 8);
	this._send(Language.SetSingleItemPosition, null, buffer);
};

TeamFortress2.prototype.setPositions = function(items) {
	this._send(Language.SetItemPositions, base_gcmessages.CMsgSetItemPositions, {"itemPositions": items});
};

TeamFortress2.prototype.deleteItem = function(item) {
	var buffer = new Buffer(8);
	buffer.writeUInt64LE(item);
	this._send(Language.Delete, null, buffer);
};

TeamFortress2.prototype.wrapItem = function(wrapID, itemID) {
	var buffer = new Buffer(16);
	buffer.writeUInt64LE(wrapID, 0);
	buffer.writeUInt64LE(itemID, 8);
	this._send(Language.GiftWrapItem, null, buffer);
};

TeamFortress2.prototype.deliverGift = function(gift, steamID) {
	var buffer = new Buffer(16);
	buffer.writeUInt64LE(gift, 0);
	buffer.writeUInt64LE(steamID, 8);
	this._send(Language.DeliverGift, null, buffer);
};

TeamFortress2.prototype.unwrapGift = function(gift) {
	var buffer = new Buffer(8);
	buffer.writeUInt64LE(gift);
	this._send(Language.UnwrapGiftRequest, null, buffer);
};

TeamFortress2.prototype.useItem = function(item) {
	this._send(Language.UseItemRequest, base_gcmessages.CMsgUseItem, {"itemId": item});
};

TeamFortress2.prototype.sortBackpack = function(sortType) {
	this._send(Language.SortItems, base_gcmessages.CMsgSortItems, {"sortType": sortType});
};

TeamFortress2.prototype.sendProfessorSpeks = function(steamID) {
	this._send(Language.FreeTrial_ChooseMostHelpfulFriend, tf_gcmessages.CMsgTFFreeTrialChooseMostHelpfulFriend, {"accountIdFriend": new SteamID(steamID).accountid});
};

TeamFortress2.prototype.createServerIdentity = function() {
	this._send(Language.GameServer_CreateIdentity, tf_gcmessages.CMsgGC_GameServer_CreateIdentity, {"accountId": new SteamID(this._steam.steamID).accountid});
};

TeamFortress2.prototype.getRegisteredServers = function() {
	this._send(Language.GameServer_List, tf_gcmessages.CMsgGC_GameServer_List, {"accountId": new SteamID(this._steam.steamID).accountid});
};

TeamFortress2.prototype.resetServerIdentity = function(id) {
	this._send(Language.GameServer_ResetIdentity, tf_gcmessages.CMsgGC_GameServer_ResetIdentity, {"gameServerAccountId": id});
};

TeamFortress2.prototype.openCrate = function(keyID, crateID) {
	var buffer = new Buffer(16);
	buffer.writeUInt64LE(keyID, 0);
	buffer.writeUInt64LE(crateID, 8);
	this._send(Language.UnlockCrate, null, buffer);
};

TeamFortress2.prototype.requestSpyVsEngiWarStats = function() {
	this._send(Language.SpyVsEngyWar_RequestGlobalStats, tf_gcmessages.CGCMsgGC_SpyVsEngyWar_RequestGlobalStats, {});
};

TeamFortress2.prototype._handlers = {};

require('./enums.js');
require('./handlers.js');<|endoftext|>


<commit_before>var AWS = require('aws-sdk');
var EventEmitter = require('events').EventEmitter;
var fs = require('fs');
var findit = require('findit');
var Pend = require('pend');
var path = require('path');
var crypto = require('crytpo');

// greater than 5 gigabytes and S3 requires a multipart upload. Multipart
// uploads have a different ETag format. For multipart upload ETags it is
// impossible to tell how te generate the ETag.
// unfortunately we're still assuming that files <= 5 GB were not uploaded with
// via multipart upload.
var MAX_PUTOBJECT_SIZE = 5 * 1024 * 1024 * 1024;

/*
TODO:
 - ability to query progress
 - deleteObjects
 - downloadFile
 - remove GroupPend
*/

exports.createClient = function(options) {
  var client = new Client(options);
  client.s3 = new AWS.S3(options);
  client.connectionDetails = {s3Client: client.s3};
  return client;
};

exports.fromAwsSdkS3 = function(s3, options) {
  var client = new Client(options);
  client.s3 = s3;
  client.connectionDetails = {s3Client: client.s3};
  return client;
}

exports.Client = Client;

function Client(options) {
  options = options || {};
  this.s3Pend = new Pend();
  this.s3Pend.max = options.maxAsyncS3 || Infinity;
  this.s3RetryCount = options.s3RetryCount || 3;
  this.s3RetryDelay = options.s3RetryDelay || 1000;
  this.freePend = new Pend();
}

Client.prototype.deleteObjects = function(params) {
  var deleter = new EventEmitter();

  // TODO handle the 1000 limit
  // TODO handle retries

  return deleter;
};

Client.prototype.uploadFile = function(params) {
  var self = this;
  var uploader = new EventEmitter();
  var localFile = params.localFile;
  var localFileStat = params.localFileStat;

  if (!localFileStat || !localFileStat.md5sum) {
    doStatAndMd5Sum();
  } else {
    startPuttingObject();
  }

  return uploader;

  function doStatAndMd5Sum() {
    var md5sum;
    var pend = new Pend();
    pend.go(doStat);
    pend.go(doMd5Sum);
    pend.wait(function(err) {
      if (err) {
        uploader.emit('error', err);
        return;
      }
      localFileStat.md5sum = md5sum;
      startPuttingObject();
    });

    function doStat(cb) {
      fs.stat(localFile, function(err, stat) {
        if (!err) localFileStat = stat;
        cb(err);
      });
    }

    function doMd5Sum(cb) {
      var inStream = fs.createReadStream(localFile);
      inStream.on('error', function(err) {
        cb(err);
      });
      var hash = crypto.createHash('md5');
      hash.on('data', function(digest) {
        md5sum = digest.toString('hex');
        cb();
      });
      inStream.pipe(hash);
    }
  }

  function startPuttingObject() {
    if (localFileStat.size > MAX_PUTOBJECT_SIZE) {
      uploader.emit('error', new Error("file exceeded max size for putObject"));
      return;
    }
    doWithRetry(tryPuttingObject, self.s3RetryCount, self.s3RetryDelay, function(err, data) {
      if (err) {
        uploader.emit('error', err);
        return;
      }

      uploader.emit('end');
    });
  }

  function tryPuttingObject(cb) {
    self.s3Pend.go(function(pendCb) {
      var inStream = fs.createReadStream(localFile);
      var errorOccurred = false;
      inStream.on('error', function(err) {
        if (errorOccurred) return;
        errorOccurred = true;
        uploader.emit('error', err);
      });
      params.Body = inStream;
      params.ContentMD5 = localFileStat.md5sum;
      params.ContentLength = localFileStat.size;
      self.s3.putObject(params, function(err, data) {
        pendCb();
        if (errorOccurred) return;
        if (err) {
          errorOccurred = true;
          cb(err);
          return;
        }
        if (data.ETag !== localFileStat.md5sum) {
          errorOccurred = true;
          cb(new Error("ETag does not match MD5 checksum"));
          return;
        }
        cb(null, data);
      });
    });
  }
};

Client.prototype.downloadFile = function(params) {
  var downloader = new EventEmitter();
  var localFile = params.localFile;
  var response = this.s3.getObject(params).createReadStream();
  var outStream = fs.createWriteStream(localFile);

  response.on('error', function(err) {
    downloader.emit('error', err);
  });

  outStream.on('error', function(err) {
    downloader.emit('error', err);
  });

  response.pipe(outStream);

  return downloader;
};

/* params:
 * - Bucket (required)
 * - Key (required)
 * - Delimiter - defaults to '/'
 * - deleteRemoved - delete s3 objects with no corresponding local file. default false
 * - localDir - path on local file system to sync
 */
Client.prototype.uploadDir = function(params) {
  syncDir(this, params, true);
};

Client.prototype.downloadDir = function(params) {
  syncDir(this, params, false);
};

function syncDir(self, params, directionIsToS3) {
  var ee = new EventEmitter();

  var localDir = params.localDir;
  var localFiles = {};
  var s3Objects = {};
  var deleteRemoved = params.deleteRemoved === true;
  var findS3ObjectsParams = {
    Bucket: params.Bucket,
    Delimiter: params.Delimiter || '/',
    EncodingType: 'url',
    Marker: null,
    MaxKeys: null,
    Prefix: params.Key,
  };
  var s3Details = extend({}, params);

  var pend = new Pend();
  pend.go(findAllS3Objects);
  pend.go(findAllFiles);
  pend.wait(compareResults);

  return ee;

  function compareResults(err) {
    if (err) {
      ee.emit('error', err);
      return;
    }

    var pend = new Pend();
    if (directionIsToS3) {
      if (deleteRemoved) pend.go(deleteRemovedObjects);
      pend.go(uploadDifferentObjects);
    } else {
      if (deleteRemoved) pend.go(deleteRemovedLocalFiles);
      pend.go(downloadDifferentObjects);
    }
    pend.wait(function(err) {
      if (err) {
        ee.emit('error', err);
        return;
      }
      ee.emit('end');
    });
  }

  function downloadDifferentObjects(cb) {
    var pend = new Pend();
    debugger
    for (var relPath in s3Objects) {
      var s3Object = s3Objects[relPath];
      var localFileStat = localFiles[relPath];
      if (!localFileStat || localFileStat.md5sum !== s3Object.ETag) {
        downloadOneFile(relPath);
      }
    }
    pend.wait(cb);

    function downloadOneFile(relPath) {
      var fullPath = path.join(localDir, relPath);
      pend.go(function(cb) {
        s3Details.Key = relPath;
        s3Details.localFile = fullPath;
        var downloader = self.downloadFile(s3Details);
        downloader.on('error', function(err) {
          cb(err);
        });
        downloader.on('end', function() {
          cb();
        });
      });
    }
  }

  function deleteRemovedLocalFiles(cb) {
    var pend = new Pend();
    debugger;
    for (var relPath in localFiles) {
      var localFileStat = localFiles[relPath];
      var s3Object = s3Objects[relPath];
      if (!s3Object) {
        deleteOneFile(relPath);
      }
    }
    pend.wait(cb);

    function deleteOneFile(relPath) {
      var fullPath = path.join(localDir, relPath);
      pend.go(function(cb) {
        fs.unlink(cb);
      });
    }
  }

  function uploadDifferentObjects(cb) {
    var pend = new Pend();
    debugger
    for (var relPath in localFiles) {
      var localFileStat = localFiles[relPath];
      var s3Object = s3Objects[relPath];
      if (!s3Object || localFileStat.md5sum !== s3Object.ETag) {
        uploadOneFile(relPath);
      }
    }
    pend.wait(cb);

    function uploadOneFile(relPath, localFileStat) {
      var fullPath = path.join(localDir, relPath);
      pend.go(function(cb) {
        s3Details.Key = relPath;
        s3Details.localFile = fullPath;
        s3Details.localFileStat = localFileStat;
        var uploader = self.uploadFile(s3Details);
        uploader.on('error', function(err) {
          cb(err);
        });
        uploader.on('end', function() {
          cb();
        });
      });
    }
  }

  function deleteRemovedObjects(cb) {
    debugger;
    var objectsToDelete = [];
    for (var relPath in s3Objects) {
      var s3Object = s3Objects[relPath];
      var localFileStat = localFiles[relPath];
      if (!localFileStat) {
        objectsToDelete.push({Key: relPath});
      }
    }
    var params = {
      Bucket: params.Bucket,
      Delete: {
        Objects: objectsToDelete,
        Quiet: true,
      },
    };
    var deleter = self.deleteObjects(params);
    deleter.on('error', function(err) {
      cb(err);
    });
    deleter.on('end', function() {
      cb();
    });
  }

  function findAllS3Objects(cb) {
    doWithRetry(listObjects, self.s3RetryCount, self.s3RetryDelay, function(err, data) {
      if (err) return cb(err);

      for (var i = 0; i < data.Contents.length; i += 1) {
        var object = data.Contents[i];
        s3Objects[object.Key] = object;
      }

      if (data.IsTruncated) {
        findS3ObjectsParams.Marker = data.NextMarker;
        findAllS3Objects(cb);
      } else {
        cb();
      }
    });

    function listObjects(cb) {
      self.s3Pend.go(function(pendCb) {
        self.s3.listObjects(findS3ObjectsParams, function(err, data) {
          pendCb();
          cb(err, data);
        });
      });
    }
  }

  function findAllFiles(cb) {
    var dirWithSlash = ensureSep(localDir);
    var walker = findit(dirWithSlash);
    var errorOccurred = false;
    var pend = new Pend();
    walker.on('error', function(err) {
      if (errorOccurred) return;
      errorOccurred = true;
      walker.stop();
      cb(err);
    });
    walker.on('file', function(file, stat) {
      var relPath = path.relative(localDir, file);
      if (stat.size > MAX_PUTOBJECT_SIZE) {
        stat.md5sum = ""; // ETag has different format for files this big
        localFiles[relPath] = stat;
        return;
      }
      pend.go(function(cb) {
        var inStream = fs.createReadStream(file);
        var hash = crypto.createHash('md5');
        inStream.on('error', function(err) {
          if (errorOccurred) return;
          errorOccurred = true;
          walker.stop();
          cb(err);
        });
        hash.on('data', function(digest) {
          stat.md5sum = digest.toString('hex');
          localFiles[relPath] = stat;
          cb();
        });
        inStream.pipe(hash);
      });
    });
    walker.on('end', function() {
      if (errorOccurred) return;
      pend.wait(cb);
    });
  }
}

function GroupPend() {
  this.pend = new Pend();
}

GroupPend.prototype.go = function(pend, fn) {
  var innerPend = this.pend;
  pend.go(function(cb) {
    innerPend.go(function(innerCb) {
      fn(function(err) {
        innerCb(err);
        cb(err);
      });
    });
  });
};

GroupPend.prototype.wait = function(fn) {
  this.pend.wait(fn);
};

function ensureSep(dir) {
  return (dir[dir.length - 1] === path.sep) ? dir : (dir + path.sep);
}

function doWithRetry(fn, tryCount, delay, cb) {
  var tryIndex = 0;

  tryOnce();

  function tryOnce() {
    fn(function(err, result) {
      if (err) {
        tryIndex += 1;
        if (tryIndex >= tryCount) {
          cb(err);
        } else {
          setTimeout(tryOnce, delay);
        }
      } else {
        cb(null, result);
      }
    });
  }
}

function extend(target, source) {
  for (var propName in source) {
    target[propName] = source[propName];
  }
  return target;
}
<commit_msg>remove GroupPend<commit_after>var AWS = require('aws-sdk');
var EventEmitter = require('events').EventEmitter;
var fs = require('fs');
var findit = require('findit');
var Pend = require('pend');
var path = require('path');
var crypto = require('crytpo');

// greater than 5 gigabytes and S3 requires a multipart upload. Multipart
// uploads have a different ETag format. For multipart upload ETags it is
// impossible to tell how te generate the ETag.
// unfortunately we're still assuming that files <= 5 GB were not uploaded with
// via multipart upload.
var MAX_PUTOBJECT_SIZE = 5 * 1024 * 1024 * 1024;

/*
TODO:
 - ability to query progress
 - deleteObjects
 - downloadFile
*/

exports.createClient = function(options) {
  var client = new Client(options);
  client.s3 = new AWS.S3(options);
  client.connectionDetails = {s3Client: client.s3};
  return client;
};

exports.fromAwsSdkS3 = function(s3, options) {
  var client = new Client(options);
  client.s3 = s3;
  client.connectionDetails = {s3Client: client.s3};
  return client;
}

exports.Client = Client;

function Client(options) {
  options = options || {};
  this.s3Pend = new Pend();
  this.s3Pend.max = options.maxAsyncS3 || Infinity;
  this.s3RetryCount = options.s3RetryCount || 3;
  this.s3RetryDelay = options.s3RetryDelay || 1000;
  this.freePend = new Pend();
}

Client.prototype.deleteObjects = function(params) {
  var deleter = new EventEmitter();

  // TODO handle the 1000 limit
  // TODO handle retries

  return deleter;
};

Client.prototype.uploadFile = function(params) {
  var self = this;
  var uploader = new EventEmitter();
  var localFile = params.localFile;
  var localFileStat = params.localFileStat;

  if (!localFileStat || !localFileStat.md5sum) {
    doStatAndMd5Sum();
  } else {
    startPuttingObject();
  }

  return uploader;

  function doStatAndMd5Sum() {
    var md5sum;
    var pend = new Pend();
    pend.go(doStat);
    pend.go(doMd5Sum);
    pend.wait(function(err) {
      if (err) {
        uploader.emit('error', err);
        return;
      }
      localFileStat.md5sum = md5sum;
      startPuttingObject();
    });

    function doStat(cb) {
      fs.stat(localFile, function(err, stat) {
        if (!err) localFileStat = stat;
        cb(err);
      });
    }

    function doMd5Sum(cb) {
      var inStream = fs.createReadStream(localFile);
      inStream.on('error', function(err) {
        cb(err);
      });
      var hash = crypto.createHash('md5');
      hash.on('data', function(digest) {
        md5sum = digest.toString('hex');
        cb();
      });
      inStream.pipe(hash);
    }
  }

  function startPuttingObject() {
    if (localFileStat.size > MAX_PUTOBJECT_SIZE) {
      uploader.emit('error', new Error("file exceeded max size for putObject"));
      return;
    }
    doWithRetry(tryPuttingObject, self.s3RetryCount, self.s3RetryDelay, function(err, data) {
      if (err) {
        uploader.emit('error', err);
        return;
      }

      uploader.emit('end');
    });
  }

  function tryPuttingObject(cb) {
    self.s3Pend.go(function(pendCb) {
      var inStream = fs.createReadStream(localFile);
      var errorOccurred = false;
      inStream.on('error', function(err) {
        if (errorOccurred) return;
        errorOccurred = true;
        uploader.emit('error', err);
      });
      params.Body = inStream;
      params.ContentMD5 = localFileStat.md5sum;
      params.ContentLength = localFileStat.size;
      self.s3.putObject(params, function(err, data) {
        pendCb();
        if (errorOccurred) return;
        if (err) {
          errorOccurred = true;
          cb(err);
          return;
        }
        if (data.ETag !== localFileStat.md5sum) {
          errorOccurred = true;
          cb(new Error("ETag does not match MD5 checksum"));
          return;
        }
        cb(null, data);
      });
    });
  }
};

Client.prototype.downloadFile = function(params) {
  var downloader = new EventEmitter();
  var localFile = params.localFile;
  var response = this.s3.getObject(params).createReadStream();
  var outStream = fs.createWriteStream(localFile);

  response.on('error', function(err) {
    downloader.emit('error', err);
  });

  outStream.on('error', function(err) {
    downloader.emit('error', err);
  });

  response.pipe(outStream);

  return downloader;
};

/* params:
 * - Bucket (required)
 * - Key (required)
 * - Delimiter - defaults to '/'
 * - deleteRemoved - delete s3 objects with no corresponding local file. default false
 * - localDir - path on local file system to sync
 */
Client.prototype.uploadDir = function(params) {
  syncDir(this, params, true);
};

Client.prototype.downloadDir = function(params) {
  syncDir(this, params, false);
};

function syncDir(self, params, directionIsToS3) {
  var ee = new EventEmitter();

  var localDir = params.localDir;
  var localFiles = {};
  var s3Objects = {};
  var deleteRemoved = params.deleteRemoved === true;
  var findS3ObjectsParams = {
    Bucket: params.Bucket,
    Delimiter: params.Delimiter || '/',
    EncodingType: 'url',
    Marker: null,
    MaxKeys: null,
    Prefix: params.Key,
  };
  var s3Details = extend({}, params);

  var pend = new Pend();
  pend.go(findAllS3Objects);
  pend.go(findAllFiles);
  pend.wait(compareResults);

  return ee;

  function compareResults(err) {
    if (err) {
      ee.emit('error', err);
      return;
    }

    var pend = new Pend();
    if (directionIsToS3) {
      if (deleteRemoved) pend.go(deleteRemovedObjects);
      pend.go(uploadDifferentObjects);
    } else {
      if (deleteRemoved) pend.go(deleteRemovedLocalFiles);
      pend.go(downloadDifferentObjects);
    }
    pend.wait(function(err) {
      if (err) {
        ee.emit('error', err);
        return;
      }
      ee.emit('end');
    });
  }

  function downloadDifferentObjects(cb) {
    var pend = new Pend();
    debugger
    for (var relPath in s3Objects) {
      var s3Object = s3Objects[relPath];
      var localFileStat = localFiles[relPath];
      if (!localFileStat || localFileStat.md5sum !== s3Object.ETag) {
        downloadOneFile(relPath);
      }
    }
    pend.wait(cb);

    function downloadOneFile(relPath) {
      var fullPath = path.join(localDir, relPath);
      pend.go(function(cb) {
        s3Details.Key = relPath;
        s3Details.localFile = fullPath;
        var downloader = self.downloadFile(s3Details);
        downloader.on('error', function(err) {
          cb(err);
        });
        downloader.on('end', function() {
          cb();
        });
      });
    }
  }

  function deleteRemovedLocalFiles(cb) {
    var pend = new Pend();
    debugger;
    for (var relPath in localFiles) {
      var localFileStat = localFiles[relPath];
      var s3Object = s3Objects[relPath];
      if (!s3Object) {
        deleteOneFile(relPath);
      }
    }
    pend.wait(cb);

    function deleteOneFile(relPath) {
      var fullPath = path.join(localDir, relPath);
      pend.go(function(cb) {
        fs.unlink(cb);
      });
    }
  }

  function uploadDifferentObjects(cb) {
    var pend = new Pend();
    debugger
    for (var relPath in localFiles) {
      var localFileStat = localFiles[relPath];
      var s3Object = s3Objects[relPath];
      if (!s3Object || localFileStat.md5sum !== s3Object.ETag) {
        uploadOneFile(relPath);
      }
    }
    pend.wait(cb);

    function uploadOneFile(relPath, localFileStat) {
      var fullPath = path.join(localDir, relPath);
      pend.go(function(cb) {
        s3Details.Key = relPath;
        s3Details.localFile = fullPath;
        s3Details.localFileStat = localFileStat;
        var uploader = self.uploadFile(s3Details);
        uploader.on('error', function(err) {
          cb(err);
        });
        uploader.on('end', function() {
          cb();
        });
      });
    }
  }

  function deleteRemovedObjects(cb) {
    debugger;
    var objectsToDelete = [];
    for (var relPath in s3Objects) {
      var s3Object = s3Objects[relPath];
      var localFileStat = localFiles[relPath];
      if (!localFileStat) {
        objectsToDelete.push({Key: relPath});
      }
    }
    var params = {
      Bucket: params.Bucket,
      Delete: {
        Objects: objectsToDelete,
        Quiet: true,
      },
    };
    var deleter = self.deleteObjects(params);
    deleter.on('error', function(err) {
      cb(err);
    });
    deleter.on('end', function() {
      cb();
    });
  }

  function findAllS3Objects(cb) {
    doWithRetry(listObjects, self.s3RetryCount, self.s3RetryDelay, function(err, data) {
      if (err) return cb(err);

      for (var i = 0; i < data.Contents.length; i += 1) {
        var object = data.Contents[i];
        s3Objects[object.Key] = object;
      }

      if (data.IsTruncated) {
        findS3ObjectsParams.Marker = data.NextMarker;
        findAllS3Objects(cb);
      } else {
        cb();
      }
    });

    function listObjects(cb) {
      self.s3Pend.go(function(pendCb) {
        self.s3.listObjects(findS3ObjectsParams, function(err, data) {
          pendCb();
          cb(err, data);
        });
      });
    }
  }

  function findAllFiles(cb) {
    var dirWithSlash = ensureSep(localDir);
    var walker = findit(dirWithSlash);
    var errorOccurred = false;
    var pend = new Pend();
    walker.on('error', function(err) {
      if (errorOccurred) return;
      errorOccurred = true;
      walker.stop();
      cb(err);
    });
    walker.on('file', function(file, stat) {
      var relPath = path.relative(localDir, file);
      if (stat.size > MAX_PUTOBJECT_SIZE) {
        stat.md5sum = ""; // ETag has different format for files this big
        localFiles[relPath] = stat;
        return;
      }
      pend.go(function(cb) {
        var inStream = fs.createReadStream(file);
        var hash = crypto.createHash('md5');
        inStream.on('error', function(err) {
          if (errorOccurred) return;
          errorOccurred = true;
          walker.stop();
          cb(err);
        });
        hash.on('data', function(digest) {
          stat.md5sum = digest.toString('hex');
          localFiles[relPath] = stat;
          cb();
        });
        inStream.pipe(hash);
      });
    });
    walker.on('end', function() {
      if (errorOccurred) return;
      pend.wait(cb);
    });
  }
}

function ensureSep(dir) {
  return (dir[dir.length - 1] === path.sep) ? dir : (dir + path.sep);
}

function doWithRetry(fn, tryCount, delay, cb) {
  var tryIndex = 0;

  tryOnce();

  function tryOnce() {
    fn(function(err, result) {
      if (err) {
        tryIndex += 1;
        if (tryIndex >= tryCount) {
          cb(err);
        } else {
          setTimeout(tryOnce, delay);
        }
      } else {
        cb(null, result);
      }
    });
  }
}

function extend(target, source) {
  for (var propName in source) {
    target[propName] = source[propName];
  }
  return target;
}
<|endoftext|>


<commit_before>/* jshint forin: false */
var util = require('util');
var Transport = require('winston').Transport;
var NodeSol = require('uber-nodesol-write').NodeSol;
var hostName = require('os').hostname();
var extend = require('xtend');

function KafkaLogger(options) {
    if (!(this instanceof KafkaLogger)) {
        return new KafkaLogger(options);
    }

    var self = this;

    function onConnect(err) {
        if (!err) {
            if (self.logger) {
                self.logger.info('KafkaClient connected to kafka');
            }
            self.connected = true;
            self._flush();
        } else {
            if (self.logger) {
                self.logger.warn('KafkaClient could not connect to kafka');
            }
            // connection failed, purge queue.
            self.initQueue.length = 0;
        }
    }

    options = options || {};

    Transport.call(this, options);
    this.topic = options.topic || 'unknown';
    this.leafHost = options.leafHost || 'localhost';
    this.leafPort = options.leafPort || 9093;
    this.logger = options.logger;
    this.properties = options.properties || {};
    this.dateFormats = options.dateFormats || { isodate: 'iso' };
    this.peerId = options.hasOwnProperty('peerId') ? options.peerId : -1;
    this.workerId = options.hasOwnProperty('workerId') ? options.workerId : -1;
    this.logTemplate = {
        host: hostName,
        level: "LOGLEVELHERE",
        msg: "MESSAGEHERE"
    };
    for (var dateFormat in this.dateFormats) {
        this.logTemplate[dateFormat] = this.dateFormats[dateFormat];
    }
    for (var property in this.properties) {
        this.logTemplate[property] = this.properties[property];
    }

    this.kafkaProber = options.kafkaProber || null;
    this.failureHandler = options.failureHandler || null;
    this.kafkaClient = options.kafkaClient || null;
    this.isDisabled = options.isDisabled || null;

    this.connected = true;
    this.initQueue = [];
    this.initTime = null;

    if (!this.kafkaClient) {
        this.connected = false;
        this.initTime = Date.now();
        this.kafkaClient = new NodeSol({
            leafHost: this.leafHost, leafPort: this.leafPort
        });
        this.kafkaClient.connect(onConnect);
    }
}

util.inherits(KafkaLogger, Transport);

KafkaLogger.prototype.name = 'KafkaLogger';

KafkaLogger.prototype.destroy = function destroy() {
    var producer = this.kafkaClient.get_producer(this.topic);

    if (producer && producer.connection &&
        producer.connection.connection &&
        producer.connection.connection._connection
    ) {
        producer.connection.connection._connection.destroy();
    }
};

KafkaLogger.prototype._flush = function _flush() {
    while (this.initQueue.length > 0) {
        var tuple = this.initQueue.shift();
        produceMessage(this, tuple[0], tuple[1]);
    }
};

KafkaLogger.prototype.log = function(level, msg, meta, callback) {
    var logMessage = extend(this.logTemplate);
    meta = meta || {};

    var d = new Date();
    for (var dateFormat in this.dateFormats) {
        switch(this.dateFormats[dateFormat]) {
        case 'epoch':
            logMessage[dateFormat] = Math.floor(d.getTime() / 1000);
            break;
        case 'jsepoch':
            logMessage[dateFormat] = d.getTime();
            break;
        case 'pyepoch':
            logMessage[dateFormat] = d.getTime() / 1000;
            break;
        case 'iso':
            /* falls through */
        default:
            logMessage[dateFormat] = d.toISOString();
            break;
        }
    }
    logMessage.level = level;
    var jsonMsg = {}
    jsonMsg.message = msg;
    for (var property in meta) {
        jsonMsg[property] = meta[property];
    }
    logMessage.msg = jsonMsg;

    if (!this.connected && Date.now() < this.initTime + 5000) {
        return this.initQueue.push([logMessage, callback]);
    } else if (this.connected && this.initQueue.length) {
        this._flush();
    }

    produceMessage(this, logMessage, callback)
};

function produceMessage(self, logMessage, callback) {
    if (self.isDisabled && self.isDisabled()) {
        if (callback) {
            process.nextTick(callback);
        }
        return;
    }

    var failureHandler = self.failureHandler

    if (self.kafkaProber) {
        var thunk = self.kafkaClient.produce.bind(self.kafkaClient,
            self.topic, logMessage);
        self.kafkaProber.probe(thunk, onFailure);
    } else {
        self.kafkaClient.produce(self.topic, logMessage, callback);
    }

    function onFailure(err) {
        if (failureHandler) {
            failureHandler(err, logMessage);
        }

        if (callback && typeof callback === 'function') {
            callback(err);
        }
    }
}

module.exports = KafkaLogger;
<commit_msg>use text as field name instead of message<commit_after>/* jshint forin: false */
var util = require('util');
var Transport = require('winston').Transport;
var NodeSol = require('uber-nodesol-write').NodeSol;
var hostName = require('os').hostname();
var extend = require('xtend');

function KafkaLogger(options) {
    if (!(this instanceof KafkaLogger)) {
        return new KafkaLogger(options);
    }

    var self = this;

    function onConnect(err) {
        if (!err) {
            if (self.logger) {
                self.logger.info('KafkaClient connected to kafka');
            }
            self.connected = true;
            self._flush();
        } else {
            if (self.logger) {
                self.logger.warn('KafkaClient could not connect to kafka');
            }
            // connection failed, purge queue.
            self.initQueue.length = 0;
        }
    }

    options = options || {};

    Transport.call(this, options);
    this.topic = options.topic || 'unknown';
    this.leafHost = options.leafHost || 'localhost';
    this.leafPort = options.leafPort || 9093;
    this.logger = options.logger;
    this.properties = options.properties || {};
    this.dateFormats = options.dateFormats || { isodate: 'iso' };
    this.peerId = options.hasOwnProperty('peerId') ? options.peerId : -1;
    this.workerId = options.hasOwnProperty('workerId') ? options.workerId : -1;
    this.logTemplate = {
        host: hostName,
        level: "LOGLEVELHERE",
        msg: "MESSAGEHERE"
    };
    for (var dateFormat in this.dateFormats) {
        this.logTemplate[dateFormat] = this.dateFormats[dateFormat];
    }
    for (var property in this.properties) {
        this.logTemplate[property] = this.properties[property];
    }

    this.kafkaProber = options.kafkaProber || null;
    this.failureHandler = options.failureHandler || null;
    this.kafkaClient = options.kafkaClient || null;
    this.isDisabled = options.isDisabled || null;

    this.connected = true;
    this.initQueue = [];
    this.initTime = null;

    if (!this.kafkaClient) {
        this.connected = false;
        this.initTime = Date.now();
        this.kafkaClient = new NodeSol({
            leafHost: this.leafHost, leafPort: this.leafPort
        });
        this.kafkaClient.connect(onConnect);
    }
}

util.inherits(KafkaLogger, Transport);

KafkaLogger.prototype.name = 'KafkaLogger';

KafkaLogger.prototype.destroy = function destroy() {
    var producer = this.kafkaClient.get_producer(this.topic);

    if (producer && producer.connection &&
        producer.connection.connection &&
        producer.connection.connection._connection
    ) {
        producer.connection.connection._connection.destroy();
    }
};

KafkaLogger.prototype._flush = function _flush() {
    while (this.initQueue.length > 0) {
        var tuple = this.initQueue.shift();
        produceMessage(this, tuple[0], tuple[1]);
    }
};

KafkaLogger.prototype.log = function(level, msg, meta, callback) {
    var logMessage = extend(this.logTemplate);
    meta = meta || {};

    var d = new Date();
    for (var dateFormat in this.dateFormats) {
        switch(this.dateFormats[dateFormat]) {
        case 'epoch':
            logMessage[dateFormat] = Math.floor(d.getTime() / 1000);
            break;
        case 'jsepoch':
            logMessage[dateFormat] = d.getTime();
            break;
        case 'pyepoch':
            logMessage[dateFormat] = d.getTime() / 1000;
            break;
        case 'iso':
            /* falls through */
        default:
            logMessage[dateFormat] = d.toISOString();
            break;
        }
    }
    logMessage.level = level;
    var jsonMsg = {}
    jsonMsg.text = msg;
    for (var property in meta) {
        jsonMsg[property] = meta[property];
    }
    logMessage.msg = jsonMsg;

    if (!this.connected && Date.now() < this.initTime + 5000) {
        return this.initQueue.push([logMessage, callback]);
    } else if (this.connected && this.initQueue.length) {
        this._flush();
    }

    produceMessage(this, logMessage, callback)
};

function produceMessage(self, logMessage, callback) {
    if (self.isDisabled && self.isDisabled()) {
        if (callback) {
            process.nextTick(callback);
        }
        return;
    }

    var failureHandler = self.failureHandler

    if (self.kafkaProber) {
        var thunk = self.kafkaClient.produce.bind(self.kafkaClient,
            self.topic, logMessage);
        self.kafkaProber.probe(thunk, onFailure);
    } else {
        self.kafkaClient.produce(self.topic, logMessage, callback);
    }

    function onFailure(err) {
        if (failureHandler) {
            failureHandler(err, logMessage);
        }

        if (callback && typeof callback === 'function') {
            callback(err);
        }
    }
}

module.exports = KafkaLogger;
<|endoftext|>