<commit_before>const GM_GUID = "{e4a8a97b-f2ed-450b-b12d-ee082ba24781}";

// TODO: properly scope this constant
const NAMESPACE = "http://youngpup.net/greasemonkey";

var GM_consoleService = Components.classes["@mozilla.org/consoleservice;1"]
                        .getService(Components.interfaces.nsIConsoleService);

var GM_stringBundle = Components
    .classes["@mozilla.org/intl/stringbundle;1"]
    .getService(Components.interfaces.nsIStringBundleService)
    .createBundle("chrome://greasemonkey/locale/gm-browser.properties");

function GM_isDef(thing) {
  return typeof(thing) != "undefined";
}

function GM_getConfig() {
  return Components
    .classes["@greasemonkey.mozdev.org/greasemonkey-service;1"]
    .getService(Components.interfaces.gmIGreasemonkeyService)
    .wrappedJSObject.config;
}

function GM_hitch(obj, meth) {
  if (!obj[meth]) {
    throw "method '" + meth + "' does not exist on object '" + obj + "'";
  }

  var staticArgs = Array.prototype.splice.call(arguments, 2, arguments.length);

  return function() {
    // make a copy of staticArgs (don't modify it because it gets reused for
    // every invocation).
    var args = Array.prototype.slice.call(staticArgs);

    // add all the new arguments
    Array.prototype.push.apply(args, arguments);

    // invoke the original function with the correct this obj and the combined
    // list of static and dynamic arguments.
    return obj[meth].apply(obj, args);
  };
}

function GM_listen(source, event, listener, opt_capture) {
  Components.utils.lookupMethod(source, "addEventListener")(
    event, listener, opt_capture);
}

function GM_unlisten(source, event, listener, opt_capture) {
  Components.utils.lookupMethod(source, "removeEventListener")(
    event, listener, opt_capture);
}

/**
 * Utility to create an error message in the log without throwing an error.
 */
function GM_logError(e, opt_warn, fileName, lineNumber) {
  var consoleService = Components.classes["@mozilla.org/consoleservice;1"]
    .getService(Components.interfaces.nsIConsoleService);

  var consoleError = Components.classes["@mozilla.org/scripterror;1"]
    .createInstance(Components.interfaces.nsIScriptError);

  var flags = opt_warn ? 1 : 0;

  // third parameter "sourceLine" is supposed to be the line, of the source,
  // on which the error happened.  we don't know it. (directly...)
  consoleError.init(e.message, fileName, null, lineNumber,
                    e.columnNumber, flags, null);

  consoleService.logMessage(consoleError);
}

function GM_log(message, force) {
  if (force || GM_prefRoot.getValue("logChrome", false)) {
    GM_consoleService.logStringMessage(message);
  }
}

// TODO: this stuff was copied wholesale and not refactored at all. Lots of
// the UI and Config rely on it. Needs rethinking.

function GM_openInEditor(script) {
  var file = script.editFile;
  var editor = GM_getEditor();
  if (!editor) {
    // The user did not choose an editor.
    return;
  }

  try {
    GM_launchApplicationWithDoc(editor, file);
  } catch (e) {
    // Something may be wrong with the editor the user selected. Remove so that
    // next time they can pick a different one.
    alert(GM_stringBundle.GetStringFromName("editor.could_not_launch") + "\n" + e);
    GM_prefRoot.remove("editor");
    throw e;
  }
}

function GM_getEditor(change) {
  var editorPath = GM_prefRoot.getValue("editor");

  if (!change && editorPath) {
    GM_log("Found saved editor preference: " + editorPath);

    var editor = Components.classes["@mozilla.org/file/local;1"]
                 .createInstance(Components.interfaces.nsILocalFile);
    editor.followLinks = true;
    editor.initWithPath(editorPath);

    // make sure the editor preference is still valid
    if (editor.exists() && editor.isExecutable()) {
      return editor;
    } else {
      GM_log("Editor preference either does not exist or is not executable");
      GM_prefRoot.remove("editor");
    }
  }

  // Ask the user to choose a new editor. Sometimes users get confused and
  // pick a non-executable file, so we set this up in a loop so that if they do
  // that we can give them an error and try again.
  while (true) {
    GM_log("Asking user to choose editor...");
    var nsIFilePicker = Components.interfaces.nsIFilePicker;
    var filePicker = Components.classes["@mozilla.org/filepicker;1"]
                               .createInstance(nsIFilePicker);

    filePicker.init(window, GM_stringBundle.GetStringFromName("editor.prompt"),
                    nsIFilePicker.modeOpen);
    filePicker.appendFilters(nsIFilePicker.filterApplication);
    filePicker.appendFilters(nsIFilePicker.filterAll);

    if (filePicker.show() != nsIFilePicker.returnOK) {
      // The user canceled, return null.
      GM_log("User canceled file picker dialog");
      return null;
    }

    GM_log("User selected: " + filePicker.file.path);

    if (filePicker.file.exists() && filePicker.file.isExecutable()) {
      GM_prefRoot.setValue("editor", filePicker.file.path);
      return filePicker.file;
    } else {
      alert(GM_stringBundle.GetStringFromName("editor.please_pick_executable"));
    }
  }
}

function GM_launchApplicationWithDoc(appFile, docFile) {
  var args=[docFile.path];

  // For the mac, wrap with a call to "open".
  var xulRuntime = Components.classes["@mozilla.org/xre/app-info;1"]
                             .getService(Components.interfaces.nsIXULRuntime);
  if ("Darwin"==xulRuntime.OS) {
    args=["-a", appFile.path, docFile.path]

    appFile = Components.classes["@mozilla.org/file/local;1"]
                        .createInstance(Components.interfaces.nsILocalFile);
    appFile.followLinks = true;
    appFile.initWithPath("/usr/bin/open");
  }

  var process = Components.classes["@mozilla.org/process/util;1"]
                          .createInstance(Components.interfaces.nsIProcess);
  process.init(appFile);
  process.run(false, args, args.length);
}

function GM_parseScriptName(sourceUri) {
  var name = sourceUri.spec;
  name = name.substring(0, name.indexOf(".user.js"));
  name = name.substring(name.lastIndexOf("/") + 1);
  return name;
}

function GM_getTempFile() {
  var file = Components.classes["@mozilla.org/file/directory_service;1"]
        .getService(Components.interfaces.nsIProperties)
        .get("TmpD", Components.interfaces.nsILocalFile);

  file.append("gm-temp");
  file.createUnique(
    Components.interfaces.nsILocalFile.NORMAL_FILE_TYPE,
    0640
  );

  return file;
}

function GM_getBinaryContents(file) {
    var ioService = Components.classes["@mozilla.org/network/io-service;1"]
                              .getService(Components.interfaces.nsIIOService);

    var channel = ioService.newChannelFromURI(GM_getUriFromFile(file));
    var input = channel.open();

    var bstream = Components.classes["@mozilla.org/binaryinputstream;1"]
                            .createInstance(Components.interfaces.nsIBinaryInputStream);
    bstream.setInputStream(input);

    var bytes = bstream.readBytes(bstream.available());

    return bytes;
}

function GM_getContents(file, charset) {
  if( !charset ) {
    charset = "UTF-8"
  }
  var ioService=Components.classes["@mozilla.org/network/io-service;1"]
    .getService(Components.interfaces.nsIIOService);
  var scriptableStream=Components
    .classes["@mozilla.org/scriptableinputstream;1"]
    .getService(Components.interfaces.nsIScriptableInputStream);
  // http://lxr.mozilla.org/mozilla/source/intl/uconv/idl/nsIScriptableUConv.idl
  var unicodeConverter = Components
    .classes["@mozilla.org/intl/scriptableunicodeconverter"]
    .createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
  unicodeConverter.charset = charset;

  var channel = ioService.newChannelFromURI(GM_getUriFromFile(file));
  try {
    var input=channel.open();
  } catch (e) {
    GM_logError(new Error("Could not open file: " + file.path));
    return "";
  }

  scriptableStream.init(input);
  var str=scriptableStream.read(input.available());
  scriptableStream.close();
  input.close();

  try {
    return unicodeConverter.ConvertToUnicode(str);
  } catch( e ) {
    return str;
  }
}

function GM_getWriteStream(file) {
  var stream = Components.classes["@mozilla.org/network/file-output-stream;1"]
                         .createInstance(Components.interfaces.nsIFileOutputStream);

  stream.init(file, 0x02 | 0x08 | 0x20, 420, -1);

  return stream;
}

function GM_getUriFromFile(file) {
  return Components.classes["@mozilla.org/network/io-service;1"]
                   .getService(Components.interfaces.nsIIOService)
                   .newFileURI(file);
}

// Todo: replace with nsIVersionComparator?
/**
 * Compares two version numbers
 *
 * @param {String} aV1 Version of first item in 1.2.3.4..9. format
 * @param {String} aV2 Version of second item in 1.2.3.4..9. format
 *
 * @returns {Int}  1 if first argument is higher
 *                 0 if arguments are equal
 *                 -1 if second argument is higher
 */
function GM_compareVersions(aV1, aV2) {
  var v1 = aV1.split(".");
  var v2 = aV2.split(".");
  var numSubversions = (v1.length > v2.length) ? v1.length : v2.length;

  for (var i = 0; i < numSubversions; i++) {
    if (typeof v2[i] == "undefined") {
      return 1;
    }

    if (typeof v1[i] == "undefined") {
      return -1;
    }

    if (parseInt(v2[i], 10) > parseInt(v1[i], 10)) {
      return -1;
    } else if (parseInt(v2[i], 10) < parseInt(v1[i], 10)) {
      return 1;
    }
  }

  // v2 was never higher or lower than v1
  return 0;
}

function GM_isGreasemonkeyable(url) {
  var scheme = Components.classes["@mozilla.org/network/io-service;1"]
               .getService(Components.interfaces.nsIIOService)
               .extractScheme(url);

  switch (scheme) {
    case "http":
    case "https":
    case "ftp":
    case "data":
      return true;
    case "about":
      // Always allow "about:blank".
      if (/^about:blank/.test(url)) return true;
      // Conditionally allow the rest of "about:".
      return GM_prefRoot.getValue('aboutIsGreaseable');
    case "file":
      return GM_prefRoot.getValue('fileIsGreaseable');
    case "unmht":
      return GM_prefRoot.getValue('unmhtIsGreaseable');
  }

  return false;
}

function GM_getEnabled() {
  return GM_prefRoot.getValue("enabled", true);
}

function GM_setEnabled(enabled) {
  GM_prefRoot.setValue("enabled", enabled);
}

function GM_uriFromUrl(url, baseUrl) {
  var ioService = Components.classes["@mozilla.org/network/io-service;1"]
                                     .getService(Components.interfaces.nsIIOService);
  var baseUri = null;
  if (baseUrl) baseUri = GM_uriFromUrl(baseUrl);
  try {
    return ioService.newURI(url, null, baseUri);
  } catch (e) {
    return null;
  }
}
GM_uriFromUrl = GM_memoize(GM_uriFromUrl);

// UTF-8 encodes input, SHA-1 hashes it and returns the 40-char hex version.
function GM_sha1(unicode) {
  var unicodeConverter = Components
      .classes["@mozilla.org/intl/scriptableunicodeconverter"]
      .createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
  unicodeConverter.charset = "UTF-8";

  var data = unicodeConverter.convertToByteArray(unicode, {});
  var ch = Components.classes["@mozilla.org/security/hash;1"]
      .createInstance(Components.interfaces.nsICryptoHash);
  ch.init(ch.SHA1);
  ch.update(data, data.length);
  var hash = ch.finish(false); // hash as raw octets

  var hex = [];
  for (var i = 0; i < hash.length; i++) {
    hex.push( ("0" + hash.charCodeAt(i).toString(16)).slice(-2) );
  }
  return hex.join('');
}
GM_sha1 = GM_memoize(GM_sha1);

// Decorate a function with a memoization wrapper, with a limited-size cache
// to reduce peak memory utilization.  Simple usage:
//
// function foo(arg1, arg2) { /* complex operation */ }
// foo = GM_memoize(foo);
//
// The memoized function may have any number of arguments, but they must be
// be serializable, and uniquely.  It's safest to use this only on functions
// that accept primitives.
function GM_memoize(func, limit) {
  limit = limit || 3000;
  var cache = {__proto__: null};
  var keylist = [];

  return function(a) {
    var args = Array.prototype.slice.call(arguments);
    var key = uneval(args);
    if (key in cache) return cache[key];

    var result = func.apply(null, args);

    cache[key] = result;

    if (keylist.push(key) > limit) delete cache[keylist.shift()];

    return result;
  }
}

<commit_msg>Remove unused/barely used code.<commit_after>const GM_GUID = "{e4a8a97b-f2ed-450b-b12d-ee082ba24781}";

// TODO: properly scope this constant
const NAMESPACE = "http://youngpup.net/greasemonkey";

var GM_consoleService = Components.classes["@mozilla.org/consoleservice;1"]
                        .getService(Components.interfaces.nsIConsoleService);

var GM_stringBundle = Components
    .classes["@mozilla.org/intl/stringbundle;1"]
    .getService(Components.interfaces.nsIStringBundleService)
    .createBundle("chrome://greasemonkey/locale/gm-browser.properties");

function GM_isDef(thing) {
  return typeof(thing) != "undefined";
}

function GM_getConfig() {
  return Components
    .classes["@greasemonkey.mozdev.org/greasemonkey-service;1"]
    .getService(Components.interfaces.gmIGreasemonkeyService)
    .wrappedJSObject.config;
}

function GM_hitch(obj, meth) {
  if (!obj[meth]) {
    throw "method '" + meth + "' does not exist on object '" + obj + "'";
  }

  var staticArgs = Array.prototype.splice.call(arguments, 2, arguments.length);

  return function() {
    // make a copy of staticArgs (don't modify it because it gets reused for
    // every invocation).
    var args = Array.prototype.slice.call(staticArgs);

    // add all the new arguments
    Array.prototype.push.apply(args, arguments);

    // invoke the original function with the correct this obj and the combined
    // list of static and dynamic arguments.
    return obj[meth].apply(obj, args);
  };
}

function GM_listen(source, event, listener, opt_capture) {
  Components.utils.lookupMethod(source, "addEventListener")(
    event, listener, opt_capture);
}

function GM_unlisten(source, event, listener, opt_capture) {
  Components.utils.lookupMethod(source, "removeEventListener")(
    event, listener, opt_capture);
}

/**
 * Utility to create an error message in the log without throwing an error.
 */
function GM_logError(e, opt_warn, fileName, lineNumber) {
  var consoleService = Components.classes["@mozilla.org/consoleservice;1"]
    .getService(Components.interfaces.nsIConsoleService);

  var consoleError = Components.classes["@mozilla.org/scripterror;1"]
    .createInstance(Components.interfaces.nsIScriptError);

  var flags = opt_warn ? 1 : 0;

  // third parameter "sourceLine" is supposed to be the line, of the source,
  // on which the error happened.  we don't know it. (directly...)
  consoleError.init(e.message, fileName, null, lineNumber,
                    e.columnNumber, flags, null);

  consoleService.logMessage(consoleError);
}

function GM_log(message, force) {
  if (force || GM_prefRoot.getValue("logChrome", false)) {
    GM_consoleService.logStringMessage(message);
  }
}

// TODO: this stuff was copied wholesale and not refactored at all. Lots of
// the UI and Config rely on it. Needs rethinking.

function GM_openInEditor(script) {
  var file = script.editFile;
  var editor = GM_getEditor();
  if (!editor) {
    // The user did not choose an editor.
    return;
  }

  try {
    GM_launchApplicationWithDoc(editor, file);
  } catch (e) {
    // Something may be wrong with the editor the user selected. Remove so that
    // next time they can pick a different one.
    alert(GM_stringBundle.GetStringFromName("editor.could_not_launch") + "\n" + e);
    GM_prefRoot.remove("editor");
    throw e;
  }
}

function GM_getEditor(change) {
  var editorPath = GM_prefRoot.getValue("editor");

  if (!change && editorPath) {
    GM_log("Found saved editor preference: " + editorPath);

    var editor = Components.classes["@mozilla.org/file/local;1"]
                 .createInstance(Components.interfaces.nsILocalFile);
    editor.followLinks = true;
    editor.initWithPath(editorPath);

    // make sure the editor preference is still valid
    if (editor.exists() && editor.isExecutable()) {
      return editor;
    } else {
      GM_log("Editor preference either does not exist or is not executable");
      GM_prefRoot.remove("editor");
    }
  }

  // Ask the user to choose a new editor. Sometimes users get confused and
  // pick a non-executable file, so we set this up in a loop so that if they do
  // that we can give them an error and try again.
  while (true) {
    GM_log("Asking user to choose editor...");
    var nsIFilePicker = Components.interfaces.nsIFilePicker;
    var filePicker = Components.classes["@mozilla.org/filepicker;1"]
                               .createInstance(nsIFilePicker);

    filePicker.init(window, GM_stringBundle.GetStringFromName("editor.prompt"),
                    nsIFilePicker.modeOpen);
    filePicker.appendFilters(nsIFilePicker.filterApplication);
    filePicker.appendFilters(nsIFilePicker.filterAll);

    if (filePicker.show() != nsIFilePicker.returnOK) {
      // The user canceled, return null.
      GM_log("User canceled file picker dialog");
      return null;
    }

    GM_log("User selected: " + filePicker.file.path);

    if (filePicker.file.exists() && filePicker.file.isExecutable()) {
      GM_prefRoot.setValue("editor", filePicker.file.path);
      return filePicker.file;
    } else {
      alert(GM_stringBundle.GetStringFromName("editor.please_pick_executable"));
    }
  }
}

function GM_launchApplicationWithDoc(appFile, docFile) {
  var args=[docFile.path];

  // For the mac, wrap with a call to "open".
  var xulRuntime = Components.classes["@mozilla.org/xre/app-info;1"]
                             .getService(Components.interfaces.nsIXULRuntime);
  if ("Darwin"==xulRuntime.OS) {
    args=["-a", appFile.path, docFile.path]

    appFile = Components.classes["@mozilla.org/file/local;1"]
                        .createInstance(Components.interfaces.nsILocalFile);
    appFile.followLinks = true;
    appFile.initWithPath("/usr/bin/open");
  }

  var process = Components.classes["@mozilla.org/process/util;1"]
                          .createInstance(Components.interfaces.nsIProcess);
  process.init(appFile);
  process.run(false, args, args.length);
}

function GM_parseScriptName(sourceUri) {
  var name = sourceUri.spec;
  name = name.substring(0, name.indexOf(".user.js"));
  name = name.substring(name.lastIndexOf("/") + 1);
  return name;
}

function GM_getTempFile() {
  var file = Components.classes["@mozilla.org/file/directory_service;1"]
        .getService(Components.interfaces.nsIProperties)
        .get("TmpD", Components.interfaces.nsILocalFile);

  file.append("gm-temp");
  file.createUnique(
    Components.interfaces.nsILocalFile.NORMAL_FILE_TYPE,
    0640
  );

  return file;
}

function GM_getBinaryContents(file) {
    var ioService = Components.classes["@mozilla.org/network/io-service;1"]
                              .getService(Components.interfaces.nsIIOService);

    var channel = ioService.newChannelFromURI(GM_getUriFromFile(file));
    var input = channel.open();

    var bstream = Components.classes["@mozilla.org/binaryinputstream;1"]
                            .createInstance(Components.interfaces.nsIBinaryInputStream);
    bstream.setInputStream(input);

    var bytes = bstream.readBytes(bstream.available());

    return bytes;
}

function GM_getContents(file, charset) {
  if( !charset ) {
    charset = "UTF-8"
  }
  var ioService=Components.classes["@mozilla.org/network/io-service;1"]
    .getService(Components.interfaces.nsIIOService);
  var scriptableStream=Components
    .classes["@mozilla.org/scriptableinputstream;1"]
    .getService(Components.interfaces.nsIScriptableInputStream);
  // http://lxr.mozilla.org/mozilla/source/intl/uconv/idl/nsIScriptableUConv.idl
  var unicodeConverter = Components
    .classes["@mozilla.org/intl/scriptableunicodeconverter"]
    .createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
  unicodeConverter.charset = charset;

  var channel = ioService.newChannelFromURI(GM_getUriFromFile(file));
  try {
    var input=channel.open();
  } catch (e) {
    GM_logError(new Error("Could not open file: " + file.path));
    return "";
  }

  scriptableStream.init(input);
  var str=scriptableStream.read(input.available());
  scriptableStream.close();
  input.close();

  try {
    return unicodeConverter.ConvertToUnicode(str);
  } catch( e ) {
    return str;
  }
}

function GM_getWriteStream(file) {
  var stream = Components.classes["@mozilla.org/network/file-output-stream;1"]
                         .createInstance(Components.interfaces.nsIFileOutputStream);

  stream.init(file, 0x02 | 0x08 | 0x20, 420, -1);

  return stream;
}

function GM_getUriFromFile(file) {
  return Components.classes["@mozilla.org/network/io-service;1"]
                   .getService(Components.interfaces.nsIIOService)
                   .newFileURI(file);
}

// Todo: replace with nsIVersionComparator?
/**
 * Compares two version numbers
 *
 * @param {String} aV1 Version of first item in 1.2.3.4..9. format
 * @param {String} aV2 Version of second item in 1.2.3.4..9. format
 *
 * @returns {Int}  1 if first argument is higher
 *                 0 if arguments are equal
 *                 -1 if second argument is higher
 */
function GM_compareVersions(aV1, aV2) {
  var v1 = aV1.split(".");
  var v2 = aV2.split(".");
  var numSubversions = (v1.length > v2.length) ? v1.length : v2.length;

  for (var i = 0; i < numSubversions; i++) {
    if (typeof v2[i] == "undefined") {
      return 1;
    }

    if (typeof v1[i] == "undefined") {
      return -1;
    }

    if (parseInt(v2[i], 10) > parseInt(v1[i], 10)) {
      return -1;
    } else if (parseInt(v2[i], 10) < parseInt(v1[i], 10)) {
      return 1;
    }
  }

  // v2 was never higher or lower than v1
  return 0;
}

function GM_isGreasemonkeyable(url) {
  var scheme = Components.classes["@mozilla.org/network/io-service;1"]
               .getService(Components.interfaces.nsIIOService)
               .extractScheme(url);

  switch (scheme) {
    case "http":
    case "https":
    case "ftp":
    case "data":
      return true;
    case "about":
      // Always allow "about:blank".
      if (/^about:blank/.test(url)) return true;
      // Conditionally allow the rest of "about:".
      return GM_prefRoot.getValue('aboutIsGreaseable');
    case "file":
      return GM_prefRoot.getValue('fileIsGreaseable');
    case "unmht":
      return GM_prefRoot.getValue('unmhtIsGreaseable');
  }

  return false;
}

function GM_getEnabled() {
  return GM_prefRoot.getValue("enabled", true);
}

function GM_setEnabled(enabled) {
  GM_prefRoot.setValue("enabled", enabled);
}

function GM_uriFromUrl(url, baseUrl) {
  var ioService = Components.classes["@mozilla.org/network/io-service;1"]
                                     .getService(Components.interfaces.nsIIOService);
  var baseUri = null;
  if (baseUrl) baseUri = GM_uriFromUrl(baseUrl);
  try {
    return ioService.newURI(url, null, baseUri);
  } catch (e) {
    return null;
  }
}
GM_uriFromUrl = GM_memoize(GM_uriFromUrl);

// UTF-8 encodes input, SHA-1 hashes it and returns the 40-char hex version.
function GM_sha1(unicode) {
  var unicodeConverter = Components
      .classes["@mozilla.org/intl/scriptableunicodeconverter"]
      .createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
  unicodeConverter.charset = "UTF-8";

  var data = unicodeConverter.convertToByteArray(unicode, {});
  var ch = Components.classes["@mozilla.org/security/hash;1"]
      .createInstance(Components.interfaces.nsICryptoHash);
  ch.init(ch.SHA1);
  ch.update(data, data.length);
  var hash = ch.finish(false); // hash as raw octets

  var hex = [];
  for (var i = 0; i < hash.length; i++) {
    hex.push( ("0" + hash.charCodeAt(i).toString(16)).slice(-2) );
  }
  return hex.join('');
}
GM_sha1 = GM_memoize(GM_sha1);

// Decorate a function with a memoization wrapper, with a limited-size cache
// to reduce peak memory utilization.  Simple usage:
//
// function foo(arg1, arg2) { /* complex operation */ }
// foo = GM_memoize(foo);
//
// The memoized function may have any number of arguments, but they must be
// be serializable, and uniquely.  It's safest to use this only on functions
// that accept primitives.
function GM_memoize(func, limit) {
  limit = limit || 3000;
  var cache = {__proto__: null};
  var keylist = [];

  return function(a) {
    var args = Array.prototype.slice.call(arguments);
    var key = uneval(args);
    if (key in cache) return cache[key];

    var result = func.apply(null, args);

    cache[key] = result;

    if (keylist.push(key) > limit) delete cache[keylist.shift()];

    return result;
  }
}
<|endoftext|>


<commit_before>const VALID_PRIMARY_LANGUAGES = ['en', 'fr', 'es', 'ar', 'it'];
const VALID_TARGET_LANGUAGES = ['yug', 'mi', 'may', 'yi', 'tzm', 'rap', 'ppl', 'el-cal', 'scn', 'lou', 'zyg'];

exports.isTargetLanguage = function (code) {
    return VALID_TARGET_LANGUAGES.indexOf(code) >= 0;
};

exports.isPrimaryLanguage = function (code) {
    return VALID_PRIMARY_LANGUAGES.indexOf(code) >= 0;
};

exports.containsHTML = function (text) {
    return /<\/?[a-z][\s\S]*>/i.test(text);
};
<commit_msg>Added remaining new languages,<commit_after>const VALID_PRIMARY_LANGUAGES = ['en', 'fr', 'es', 'ar', 'it'];
const VALID_TARGET_LANGUAGES = ['yug', 'mi', 'may', 'tep', 'yi', 'tzm', 'rap',
    'ppl', 'el-cal', 'scn', 'lou', 'zyg', 'vm', 'pot', 'kum', 'san'];

exports.isTargetLanguage = function (code) {
    return VALID_TARGET_LANGUAGES.indexOf(code) >= 0;
};

exports.isPrimaryLanguage = function (code) {
    return VALID_PRIMARY_LANGUAGES.indexOf(code) >= 0;
};

exports.containsHTML = function (text) {
    return /<\/?[a-z][\s\S]*>/i.test(text);
};
<|endoftext|>


<commit_before>/* global google,gapi */
import { getLanguage } from '../lang';
import { observer as globalObserver } from '../utils/observer';
import { translate, trackAndEmitError } from '../utils/tools';
import { loadWorkspace, loadBlocks } from '../../botPage/view/blockly';

class GoogleDrive {
    constructor() {
        this.botFolderName = `Binary Bot - ${translate('Strategies')}`;
        this.appId = '';
        this.apiKey = '';
        this.clientId = '';

        this.googleAuth = null;
        this.isAuthorised = null;
        this.profile = null;

        $.getScript('https://apis.google.com/js/api.js', () => this.init());
    }

    init() {
        gapi.load('client:auth2:picker', {
            callback: () => {
                gapi.client
                    .init({
                        apiKey       : this.apiKey,
                        clientId     : this.clientId,
                        scope        : 'https://www.googleapis.com/auth/drive.file',
                        discoveryDocs: ['https://www.googleapis.com/discovery/v1/apis/drive/v3/rest'],
                    })
                    .then(
                        () => {
                            this.googleAuth = gapi.auth2.getAuthInstance();
                            this.googleAuth.isSignedIn.listen(isSignedIn => this.updateSigninStatus(isSignedIn));
                            this.updateSigninStatus(this.googleAuth.isSignedIn.get());

                            $('#integrations').removeClass('invisible');
                            $('#save-google-drive')
                                .parent()
                                .removeClass('invisible');
                            $('#load-google-drive')
                                .parent()
                                .removeClass('invisible');
                        },
                        error => {
                            trackAndEmitError(translate('There was an error initialising Google Drive'), error);
                        }
                    );
            },
            onerror: error => {
                trackAndEmitError(translate('There was an error loading Google Drive libraries'), error);
            },
        });
    }

    updateSigninStatus(isSignedIn) {
        if (isSignedIn) {
            this.profile = this.googleAuth.currentUser.get().getBasicProfile();
        } else {
            this.profile = null;
        }
        this.isAuthorised = isSignedIn;
        globalObserver.emit('googledrive.authorise', { isAuthorised: isSignedIn });
    }

    authorise() {
        return new Promise((resolve, reject) => {
            if (this.isAuthorised) {
                resolve();
            } else {
                this.googleAuth
                    .signIn({ prompt: 'select_account' })
                    .then(() => resolve())
                    .catch(response => {
                        if (response.error === 'access_denied') {
                            globalObserver.emit(
                                'ui.log.warn',
                                translate(
                                    'Please grant permission to view and manage Google Drive folders created with Binary Bot'
                                )
                            );
                        }
                        reject(response);
                    });
            }
        });
    }

    signOut() {
        if (this.isAuthorised) {
            return this.googleAuth.signOut();
        }
        return Promise.resolve();
    }

    // eslint-disable-next-line class-methods-use-this
    getPickerLanguage() {
        const language = getLanguage();

        if (language === 'zhTw') {
            return 'zh-TW';
        } else if (language === 'zhCn') {
            return 'zh-CN';
        }
        return language;
    }

    createFilePicker() {
        return new Promise((resolve, reject) => {
            // eslint-disable-next-line consistent-return
            const userPickedFile = data => {
                if (data.action === google.picker.Action.PICKED) {
                    const fileId = data.docs[0].id;
                    gapi.client.drive.files
                        .get({
                            alt     : 'media',
                            fileId,
                            mimeType: 'text/plain',
                        })
                        .then(response => {
                            try {
                                const xmlDom = Blockly.Xml.textToDom(response.body);
                                const loadFunction =
                                    xmlDom.hasAttribute('collection') && xmlDom.getAttribute('collection') === 'true'
                                        ? loadBlocks
                                        : loadWorkspace;
                                try {
                                    loadFunction(xmlDom);
                                    resolve();
                                } catch (error) {
                                    trackAndEmitError(translate('Could not load Google Drive blocks'), error);
                                    reject(error);
                                }
                            } catch (error) {
                                trackAndEmitError(translate('Unrecognized file format'), error);
                                reject(error);
                            }
                        })
                        .catch(error => {
                            if (error.status && error.status === 401) {
                                this.signOut();
                            }
                            trackAndEmitError(translate('There was an error retrieving data from Google Drive'), error);
                            reject(error);
                        });
                } else if (data.action === google.picker.Action.CANCEL) {
                    reject();
                }
            };

            this.authorise()
                .then(() => {
                    // FilePicker open doesn't give an unauthorised error, so check if we can list files
                    // first before attempting to open it (user revoked permissions through accounts.google.com)
                    gapi.client.drive.files
                        .list()
                        .then(() => {
                            const mimeTypes = ['application/xml'];
                            const docsView = new google.picker.DocsView();
                            docsView.setMimeTypes(mimeTypes.join(','));
                            docsView.setIncludeFolders(true);
                            docsView.setOwnedByMe(true);

                            const picker = new google.picker.PickerBuilder();
                            picker
                                .setOrigin(`${window.location.protocol}//${window.location.host}`)
                                .setTitle(translate('Select a Binary Bot strategy'))
                                .setLocale(this.getPickerLanguage())
                                .setAppId(this.appId)
                                .setOAuthToken(gapi.auth.getToken().access_token)
                                .addView(docsView)
                                .setDeveloperKey(this.apiKey)
                                .setCallback(userPickedFile)
                                .build()
                                .setVisible(true);
                        })
                        .catch(error => {
                            if (error.status && error.status === 401) {
                                this.signOut();
                            }
                            trackAndEmitError(translate('There was an error listing files from Google Drive'), error);
                            reject(error);
                        });
                })
                .catch(error => reject(error));
        });
    }

    getDefaultFolderId() {
        return new Promise((resolve, reject) => {
            // Avoid duplicate auth flow by checking if user is already authed
            const authorisePromise = [];
            if (!this.isAuthorised) {
                authorisePromise.push(this.authorise);
            }
            Promise.all(authorisePromise).then(() => {
                gapi.client.drive.files
                    .list({ q: 'trashed=false' })
                    // eslint-disable-next-line consistent-return
                    .then(response => {
                        const botFolder = response.result.files.find(
                            file =>
                                file.name === this.botFolderName &&
                                file.mimeType === 'application/vnd.google-apps.folder'
                        );
                        if (botFolder) {
                            return resolve(botFolder.id);
                        }
                        gapi.client.drive.files
                            .create({
                                resource: {
                                    name    : this.botFolderName,
                                    mimeType: 'application/vnd.google-apps.folder',
                                    fields  : 'id',
                                },
                            })
                            .then(createFileResponse => resolve(createFileResponse.result.id))
                            .catch(error => {
                                if (error.status && error.status === 401) {
                                    this.signOut();
                                }
                                trackAndEmitError(
                                    translate('There was an error retrieving files from Google Drive'),
                                    error
                                );
                                reject(error);
                            });
                    })
                    .catch(error => {
                        if (error.status && error.status === 401) {
                            this.signOut();
                        }
                        trackAndEmitError(translate('There was an error listing files from Google Drive'), error);
                        reject(error);
                    });
            });
        });
    }

    saveFile(options) {
        return new Promise((resolve, reject) => {
            // eslint-disable-next-line consistent-return
            const savePickerCallback = data => {
                if (data.action === google.picker.Action.PICKED) {
                    const folderId = data.docs[0].id;
                    const strategyFile = new Blob([options.content], { type: options.mimeType });
                    const strategyFileMetadata = JSON.stringify({
                        name    : options.name,
                        mimeType: options.mimeType,
                        parents : [folderId],
                    });

                    const formData = new FormData();
                    formData.append('metadata', new Blob([strategyFileMetadata], { type: 'application/json' }));
                    formData.append('file', strategyFile);

                    const xhr = new XMLHttpRequest();
                    xhr.responseType = 'json';
                    xhr.open('POST', 'https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart');
                    xhr.setRequestHeader('Authorization', `Bearer ${gapi.auth.getToken().access_token}`);
                    xhr.onload = () => {
                        if (xhr.status === 200) {
                            resolve();
                        } else {
                            if (xhr.status === 401) {
                                this.signOut();
                            }
                            trackAndEmitError(translate('There was an error processing your request'), xhr.status);
                            reject();
                        }
                    };
                    xhr.send(formData);
                } else if (data.action === google.picker.Action.CANCEL) {
                    reject();
                }
            };

            this.authorise()
                .then(() => {
                    // Calling getDefaultFolderId() ensures there's at least one folder available to save to.
                    // FilePicker doesn't allow for folder creation, so a user without any folder in
                    // their drive couldn't select anything.
                    this.getDefaultFolderId()
                        .then(() => {
                            const view = new google.picker.DocsView();
                            view.setIncludeFolders(true)
                                .setSelectFolderEnabled(true)
                                .setMimeTypes('application/vnd.google-apps.folder');

                            const picker = new google.picker.PickerBuilder();
                            picker
                                .setOrigin(`${window.location.protocol}//${window.location.host}`)
                                .setTitle(translate('Select a folder'))
                                .addView(view)
                                .setLocale(this.getPickerLanguage())
                                .setAppId(this.appId)
                                .setOAuthToken(gapi.auth.getToken().access_token)
                                .setDeveloperKey(this.apiKey)
                                .setCallback(savePickerCallback)
                                .build()
                                .setVisible(true);
                        })
                        .catch(error => reject(error));
                })
                .catch(error => reject(error));
        });
    }
}

const googleDrive = new GoogleDrive();

export default googleDrive;
<commit_msg>Minor fixes + eslint<commit_after>/* global google,gapi */
import { getLanguage } from '../lang';
import { observer as globalObserver } from '../utils/observer';
import { translate, trackAndEmitError } from '../utils/tools';
import { loadWorkspace, loadBlocks } from '../../botPage/view/blockly';
import config from '../../botPage/common/const';

class GoogleDrive {
    constructor() {
        this.botFolderName = `Binary Bot - ${translate('Strategies')}`;
        this.setInfo(config);
        this.googleAuth = null;
        this.isAuthorised = null;
        this.profile = null;

        $.getScript('https://apis.google.com/js/api.js', () => this.init());
    }

    init() {
        gapi.load('client:auth2:picker', {
            callback: () => {
                gapi.client
                    .init({
                        apiKey       : this.apiKey,
                        clientId     : this.clientId,
                        scope        : 'https://www.googleapis.com/auth/drive.file',
                        discoveryDocs: ['https://www.googleapis.com/discovery/v1/apis/drive/v3/rest'],
                    })
                    .then(
                        () => {
                            this.googleAuth = gapi.auth2.getAuthInstance();
                            this.googleAuth.isSignedIn.listen(isSignedIn => this.updateSigninStatus(isSignedIn));
                            this.updateSigninStatus(this.googleAuth.isSignedIn.get());

                            $('#integrations').removeClass('invisible');
                            $('#save-google-drive')
                                .parent()
                                .removeClass('invisible');
                            $('#load-google-drive')
                                .parent()
                                .removeClass('invisible');
                        },
                        error => {
                            if (window.trackJs) {
                                trackJs.track(
                                    `${translate(
                                        'There was an error initialising Google Drive'
                                    )} - Error: ${JSON.stringify(error)}`
                                );
                            }
                        }
                    );
            },
            onerror: error => {
                if (window.trackJs) {
                    trackJs.track(
                        `${translate('There was an error loading Google Drive libraries')} - Error: ${JSON.stringify(
                            error
                        )}`
                    );
                }
            },
        });
    }

    updateSigninStatus(isSignedIn) {
        if (isSignedIn) {
            this.profile = this.googleAuth.currentUser.get().getBasicProfile();
        } else {
            this.profile = null;
        }
        this.isAuthorised = isSignedIn;
        globalObserver.emit('googledrive.authorise', { isAuthorised: isSignedIn });
    }

    authorise() {
        return new Promise((resolve, reject) => {
            if (this.isAuthorised) {
                resolve();
            } else {
                this.googleAuth
                    .signIn({ prompt: 'select_account' })
                    .then(() => resolve())
                    .catch(response => {
                        if (response.error === 'access_denied') {
                            globalObserver.emit(
                                'ui.log.warn',
                                translate(
                                    'Please grant permission to view and manage Google Drive folders created with Binary Bot'
                                )
                            );
                        }
                        reject(response);
                    });
            }
        });
    }

    signOut() {
        if (this.isAuthorised) {
            return this.googleAuth.signOut();
        }
        return Promise.resolve();
    }

    setInfo(data) {
        this.clientId = data.gd.cid;
        this.appId = data.gd.aid;
        this.apiKey = data.gd.api;
    }

    // eslint-disable-next-line class-methods-use-this
    getPickerLanguage() {
        const language = getLanguage();

        if (language === 'zhTw') {
            return 'zh-TW';
        } else if (language === 'zhCn') {
            return 'zh-CN';
        }
        return language;
    }

    createFilePicker() {
        return new Promise((resolve, reject) => {
            // eslint-disable-next-line consistent-return
            const userPickedFile = data => {
                if (data.action === google.picker.Action.PICKED) {
                    const fileId = data.docs[0].id;
                    gapi.client.drive.files
                        .get({
                            alt     : 'media',
                            fileId,
                            mimeType: 'text/plain',
                        })
                        .then(response => {
                            try {
                                const xmlDom = Blockly.Xml.textToDom(response.body);
                                const loadFunction =
                                    xmlDom.hasAttribute('collection') && xmlDom.getAttribute('collection') === 'true'
                                        ? loadBlocks
                                        : loadWorkspace;
                                try {
                                    loadFunction(xmlDom);
                                    resolve();
                                } catch (error) {
                                    trackAndEmitError(translate('Could not load Google Drive blocks'), error);
                                    reject(error);
                                }
                            } catch (error) {
                                trackAndEmitError(translate('Unrecognized file format'), error);
                                reject(error);
                            }
                        })
                        .catch(error => {
                            if (error.status && error.status === 401) {
                                this.signOut();
                            }
                            trackAndEmitError(translate('There was an error retrieving data from Google Drive'), error);
                            reject(error);
                        });
                } else if (data.action === google.picker.Action.CANCEL) {
                    reject();
                }
            };

            this.authorise()
                .then(() => {
                    // FilePicker open doesn't give an unauthorised error, so check if we can list files
                    // first before attempting to open it (user revoked permissions through accounts.google.com)
                    gapi.client.drive.files
                        .list()
                        .then(() => {
                            const mimeTypes = ['application/xml'];
                            const docsView = new google.picker.DocsView();
                            docsView.setMimeTypes(mimeTypes.join(','));
                            docsView.setIncludeFolders(true);
                            docsView.setOwnedByMe(true);

                            const picker = new google.picker.PickerBuilder();
                            picker
                                .setOrigin(`${window.location.protocol}//${window.location.host}`)
                                .setTitle(translate('Select a Binary Bot strategy'))
                                .setLocale(this.getPickerLanguage())
                                .setAppId(this.appId)
                                .setOAuthToken(gapi.auth.getToken().access_token)
                                .addView(docsView)
                                .setDeveloperKey(this.apiKey)
                                .setCallback(userPickedFile)
                                .build()
                                .setVisible(true);
                        })
                        .catch(error => {
                            if (error.status && error.status === 401) {
                                this.signOut();
                            }
                            trackAndEmitError(translate('There was an error listing files from Google Drive'), error);
                            reject(error);
                        });
                })
                .catch(error => reject(error));
        });
    }

    getDefaultFolderId() {
        return new Promise((resolve, reject) => {
            // Avoid duplicate auth flow by checking if user is already authed
            const authorisePromise = [];
            if (!this.isAuthorised) {
                authorisePromise.push(this.authorise);
            }
            Promise.all(authorisePromise).then(() => {
                gapi.client.drive.files
                    .list({ q: 'trashed=false' })
                    // eslint-disable-next-line consistent-return
                    .then(response => {
                        const botFolder = response.result.files.find(
                            file =>
                                file.name === this.botFolderName &&
                                file.mimeType === 'application/vnd.google-apps.folder'
                        );
                        if (botFolder) {
                            return resolve(botFolder.id);
                        }
                        gapi.client.drive.files
                            .create({
                                resource: {
                                    name    : this.botFolderName,
                                    mimeType: 'application/vnd.google-apps.folder',
                                    fields  : 'id',
                                },
                            })
                            .then(createFileResponse => resolve(createFileResponse.result.id))
                            .catch(error => {
                                if (error.status && error.status === 401) {
                                    this.signOut();
                                }
                                trackAndEmitError(
                                    translate('There was an error retrieving files from Google Drive'),
                                    error
                                );
                                reject(error);
                            });
                    })
                    .catch(error => {
                        if (error.status && error.status === 401) {
                            this.signOut();
                        }
                        trackAndEmitError(translate('There was an error listing files from Google Drive'), error);
                        reject(error);
                    });
            });
        });
    }

    saveFile(options) {
        return new Promise((resolve, reject) => {
            // eslint-disable-next-line consistent-return
            const savePickerCallback = data => {
                if (data.action === google.picker.Action.PICKED) {
                    const folderId = data.docs[0].id;
                    const strategyFile = new Blob([options.content], { type: options.mimeType });
                    const strategyFileMetadata = JSON.stringify({
                        name    : options.name,
                        mimeType: options.mimeType,
                        parents : [folderId],
                    });

                    const formData = new FormData();
                    formData.append('metadata', new Blob([strategyFileMetadata], { type: 'application/json' }));
                    formData.append('file', strategyFile);

                    const xhr = new XMLHttpRequest();
                    xhr.responseType = 'json';
                    xhr.open('POST', 'https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart');
                    xhr.setRequestHeader('Authorization', `Bearer ${gapi.auth.getToken().access_token}`);
                    xhr.onload = () => {
                        if (xhr.status === 200) {
                            resolve();
                        } else {
                            if (xhr.status === 401) {
                                this.signOut();
                            }
                            trackAndEmitError(translate('There was an error processing your request'), xhr.status);
                            reject();
                        }
                    };
                    xhr.send(formData);
                } else if (data.action === google.picker.Action.CANCEL) {
                    reject();
                }
            };

            this.authorise()
                .then(() => {
                    // Calling getDefaultFolderId() ensures there's at least one folder available to save to.
                    // FilePicker doesn't allow for folder creation, so a user without any folder in
                    // their drive couldn't select anything.
                    this.getDefaultFolderId()
                        .then(() => {
                            const view = new google.picker.DocsView();
                            view.setIncludeFolders(true)
                                .setSelectFolderEnabled(true)
                                .setMimeTypes('application/vnd.google-apps.folder');

                            const picker = new google.picker.PickerBuilder();
                            picker
                                .setOrigin(`${window.location.protocol}//${window.location.host}`)
                                .setTitle(translate('Select a folder'))
                                .addView(view)
                                .setLocale(this.getPickerLanguage())
                                .setAppId(this.appId)
                                .setOAuthToken(gapi.auth.getToken().access_token)
                                .setDeveloperKey(this.apiKey)
                                .setCallback(savePickerCallback)
                                .build()
                                .setVisible(true);
                        })
                        .catch(error => reject(error));
                })
                .catch(error => reject(error));
        });
    }
}

const googleDrive = new GoogleDrive();

export default googleDrive;
<|endoftext|>


<commit_before>var vec3 = require("vec3");

module.exports=inject;

function toFixedPosition(p)
{
  return new vec3(Math.floor(p.x*32),Math.floor(p.y*32),Math.floor(p.z*32))
}

function inject(serv,player)
{
  player._client.on('position', function (packet) {
    var position = new vec3(packet.x, packet.y, packet.z);
    var onGround = packet.onGround;
    sendRelativePositionChange(toFixedPosition(position), onGround);
  });

  function sendRelativePositionChange(newPosition, onGround) {
    if (player.entity.position.distanceTo(new vec3(0, 0, 0)) != 0) {
      var diff = newPosition.minus(player.entity.position);
      if (diff.distanceTo(new vec3(0, 0, 0)) != 0) {
        player._writeOthers('rel_entity_move', {
          entityId: player.entity.id,
          dX: diff.x,
          dY: diff.y,
          dZ: diff.z,
          onGround: onGround
        });
      }
    }
    player.entity.position = newPosition;
    player.entity.onGround = onGround;
  }
}<commit_msg>update position also on position_look, fix #2<commit_after>var vec3 = require("vec3");

module.exports=inject;

function toFixedPosition(p)
{
  return new vec3(Math.floor(p.x*32),Math.floor(p.y*32),Math.floor(p.z*32))
}

function inject(serv,player)
{
  player._client.on('position', function (packet) {
    var position = new vec3(packet.x, packet.y, packet.z);
    var onGround = packet.onGround;
    sendRelativePositionChange(toFixedPosition(position), onGround);
  });

  player._client.on('position_look', function (packet) {
    var position = new vec3(packet.x, packet.y, packet.z);
    var onGround = packet.onGround;
    sendRelativePositionChange(toFixedPosition(position), onGround);
  });

  function sendRelativePositionChange(newPosition, onGround) {
    if (player.entity.position.distanceTo(new vec3(0, 0, 0)) != 0) {
      var diff = newPosition.minus(player.entity.position);
      if (diff.distanceTo(new vec3(0, 0, 0)) != 0) {
        player._writeOthers('rel_entity_move', {
          entityId: player.entity.id,
          dX: diff.x,
          dY: diff.y,
          dZ: diff.z,
          onGround: onGround
        });
      }
    }
    player.entity.position = newPosition;
    player.entity.onGround = onGround;
  }
}<|endoftext|>


<commit_before>'use strict';

const fs = require('fs');

let adWhitelist = (Config.adWhitelist ? Config.adWhitelist : []);
let bannedMessages = (Config.bannedMessages ? Config.bannedMessages : []);
let adRegex = new RegExp("(play.pokemonshowdown.com\\/~~)(?!(" + adWhitelist.join('|') + "))", "g");

Config.chatfilter = function (message, user, room, connection) {
	user.lastActive = Date.now();

	for (let x in bannedMessages) {
		if (message.toLowerCase().indexOf(bannedMessages[x]) > -1 && bannedMessages[x] !== '' && message.substr(0, 1) !== '/') {
			if (user.locked) return false;
			Punishments.lock(user, Date.now() + 7 * 24 * 60 * 60 * 1000, "Said a banned word: " + bannedMessages[x]);
			user.popup('You have been automatically locked for sending a message containing a banned word.');
			Rooms('staff').add('[PornMonitor] ' + (room ? '(' + room + ') ' : '') + Tools.escapeHTML(user.name) +
			' was automatically locked for trying to say "' + message + '"').update();
			fs.appendFile('logs/modlog/modlog_staff.txt', '[' + (new Date().toJSON()) + '] (staff) ' + user.name + ' was locked from talking by the Server (' +
			bannedMessages[x] + ') (' + connection.ip + ')\n');
			Misc.pmAllStaff(user.name + ' has been automatically locked for sending a message containing a banned word' +
			(room ? ". **Room:**" + room.id : " in a private message.") + ' **Message:** ' + message, '~Server');
			return false;
		}
	}
	return message;
};
<commit_msg>Delete chatfilter.js<commit_after><|endoftext|>


<commit_before>$(document).ready(function() {
	$('img[src]').attr('src', chrome.extension.getURL("res/potato.png"));
});
<commit_msg>Update contentScript.js to 1.3<commit_after>$(document).ready(function() {
	chrome.storage.sync.get("enabled", function(items) {
		if (items["enabled"]) {a
			$('img[src]').attr('src', chrome.extension.getURL("res/potato.png"));
		}
	});
});
<|endoftext|>


<commit_before>import fs from 'fs';

import { NightmarePlus, WebEventRecorder } from 'nightmare-handler';
import ScreenshotConfig from './SceenshotConfig';

export default class BaseHandle {
    /**
     * 构造函数
     *
     * @param {String} pageUrl 页面的 URL 地址
     * @param {String} crawlerScriptPath 运行在浏览器中的脚本文件本地地址，需要是绝对路径
     * @param {Object} [opts] 额外参数
     * @param {Boolean} [opts.show] 是否需要展示调试的webview窗口
     * @param {String} [opts.proxyServer] 代理服务器
     * @param {String | Number} [opts.wait] wait配置，会直接透传给 nightmare 的 wait 配置项，详细请查看 https://github.com/segmentio/nightmare#waitms
     * @param {Boolean} [opts.doNotEnd] 是否在执行完成之后关闭界面
     * @param {String} [opts.cookie] document.cookie的内容
     * @param {String} [opts.matmanQuery] 指定matman的query参数
     * @param {String | Boolean} [opts.useRecorder] 是否使用记录器记录整个请求队列
     * @param {undefined | ScreenshotConfig} [opts.screenshotConfig] 截图设置
     */
    constructor(pageUrl, crawlerScriptPath, opts = {}) {
        this.pageUrl = pageUrl;
        this.crawlerScriptPath = crawlerScriptPath;

        // 校验 crawlerScriptPath 必须是存在的，否则后续的逻辑也执行不了
        // 如果获取 client script 地址不存在，则抛出异常提示
        if (!fs.existsSync(crawlerScriptPath)) {
            throw new Error('Unknown crawlerScriptPath=' + crawlerScriptPath);
        }

        this.show = opts.show;
        this.wait = opts.wait;
        this.doNotEnd = opts.doNotEnd;
        this.cookie = opts.cookie;

        /**
         * 设置代理服务器。
         * https://github.com/segmentio/nightmare#switches
         *
         * @type {String} 代理服务器，类似 my_proxy_server.example.com:8080
         */
        this.proxyServer = opts.proxyServer;

        this.matmanQuery = opts.matmanQuery || null;

        // 是否使用记录器记录整个请求队列
        // 如果为 true，则可以从 this.globalInfo.recorder 中获取，
        // 如果为 字符串，则可以从 this.globalInfo[xxx] 中获取，
        this.useRecorder = (function (useRecorder) {
            if (!useRecorder) {
                return false;
            }

            return (typeof useRecorder === 'boolean') ? 'recorder' : useRecorder;
        })(opts.useRecorder);

        // 截屏设置
        this.screenshotConfig = opts.screenshotConfig;

        this.globalInfo = {};

        this.onNightmareCreated = (self) => {

        };

        this.onBeforeGotoPage = (self) => {

        };

        this.actionList = [];
    }

    async getResult() {
        // nightmare 初始化参数
        let nightmareConfig = {
            show: this.show,
            webPreferences: {
                preload: this.crawlerScriptPath
            }
        };

        // 设置代理服务器
        if (this.proxyServer) {
            nightmareConfig.switches = {
                'proxy-server': this.proxyServer
            };
        }

        // 如果有设置符合要求的 matman 服务设置，则还需要额外处理一下
        if (this.matmanQuery && (typeof this.matmanQuery.getQueryString === 'function')) {
            this.pageUrl = this.pageUrl + ((this.pageUrl.indexOf('?') > -1) ? '&' : '?') + this.matmanQuery.getQueryString();
        }

        // console.log('===nightmareConfig====', nightmareConfig);

        // 创建 nightmare 对象
        this.nightmare = NightmarePlus(nightmareConfig);

        // 创建完成之后，可能会有一些自己的处理
        this.onNightmareCreated(this);

        // 使用记录器
        if (this.useRecorder) {
            this.globalInfo[this.useRecorder] = new WebEventRecorder(this.nightmare);
        }

        // 初始化一些行为
        this.nightmareRun = this.nightmare
            .exDevice('mobile')
            .header('x-mat-from', 'nightmare')
            .header('x-mat-timestamp', Date.now());

        // 设置 cookie
        if (this.cookie) {
            this.nightmareRun.exCookies(this.cookie, getMainUrl(this.pageUrl));
        }

        // 加载页面之前要执行的方法
        this.onBeforeGotoPage(this);

        // 加载页面
        this.nightmareRun = this.nightmareRun.goto(this.pageUrl);

        // 如果指定了 opts.wait，则会传递给 nightmare 处理，具体使用方法可以参考：
        // https://github.com/segmentio/nightmare#waitms
        if (typeof this.wait !== 'undefined') {
            this.nightmareRun = this.nightmareRun.wait(this.wait);
        }

        // 循环处理多个 action
        let result = [];

        for (let i = 0, length = this.actionList.length; i < length; i++) {
            let curRun = this.actionList[i](this.nightmareRun);

            if (this.screenshotConfig) {
                curRun.screenshot(this.screenshotConfig.getPathWithId(i + 1), this.screenshotConfig.clip);
            }

            let t = await curRun.evaluate(evaluate);

            result.push(t);
        }

        // 不关闭界面
        if (this.doNotEnd) {
            await this.nightmareRun;
        } else {
            await this.nightmareRun.end();
        }

        return {
            data: result,
            globalInfo: this.globalInfo
        };
    }

    addAction(actionCall) {
        this.actionList.push(actionCall);
    }

    //
    // onNightmareCreated(callback) {
    //     if (typeof callback === 'function') {
    //         return callback(this.nightmare);
    //     }
    //
    //     return this.nightmare;
    // }

}

function evaluate() {
    // 如果没有这个变量，说明注入代码失败
    if (!window.matman_ver) {
        return {
            error: 'preload failed!'
        };
    }

    // window.getPageInfo 必须是个函数
    // window.getPageInfo 方法和其他变量均由 preload 配置中的 js 文件引入
    if (typeof window.getPageInfo !== 'function') {
        return {
            error: 'window.getPageInfo is not function!'
        };
    }

    // 如果存在需要前端执行的代码，则在所有逻辑开始之前执行
    if (window.evalList && window.evalList.length) {
        window.evalList.forEach((item) => {
            eval(window[item]);
        });
    }

    return window.getPageInfo();
}

function getMainUrl(url) {
    let arr1 = url.split('//');

    if (arr1.length > 1) {
        let arr2 = arr1[1].split('/');
        return arr1[0] + '//' + arr2[0];
    } else {
        let arr2 = arr1[0].split('/');
        return arr2[0];
    }
}

<commit_msg>feat: 增加 mockstarQuery<commit_after>import fs from 'fs';

import { NightmarePlus, WebEventRecorder } from 'nightmare-handler';
import ScreenshotConfig from './SceenshotConfig';

export default class BaseHandle {
    /**
     * 构造函数
     *
     * @param {String} pageUrl 页面的 URL 地址
     * @param {String} crawlerScriptPath 运行在浏览器中的脚本文件本地地址，需要是绝对路径
     * @param {Object} [opts] 额外参数
     * @param {Boolean} [opts.show] 是否需要展示调试的webview窗口
     * @param {String} [opts.proxyServer] 代理服务器
     * @param {String | Number} [opts.wait] wait配置，会直接透传给 nightmare 的 wait 配置项，详细请查看 https://github.com/segmentio/nightmare#waitms
     * @param {Boolean} [opts.doNotEnd] 是否在执行完成之后关闭界面
     * @param {String} [opts.cookie] document.cookie的内容
     * @param {String} [opts.mockstarQuery] 指定 mockstar 的query参数
     * @param {String | Boolean} [opts.useRecorder] 是否使用记录器记录整个请求队列
     * @param {undefined | ScreenshotConfig} [opts.screenshotConfig] 截图设置
     */
    constructor(pageUrl, crawlerScriptPath, opts = {}) {
        this.pageUrl = pageUrl;
        this.crawlerScriptPath = crawlerScriptPath;

        // 校验 crawlerScriptPath 必须是存在的，否则后续的逻辑也执行不了
        // 如果获取 client script 地址不存在，则抛出异常提示
        if (!fs.existsSync(crawlerScriptPath)) {
            throw new Error('Unknown crawlerScriptPath=' + crawlerScriptPath);
        }

        this.show = opts.show;
        this.wait = opts.wait;
        this.doNotEnd = opts.doNotEnd;
        this.cookie = opts.cookie;

        /**
         * 设置代理服务器。
         * https://github.com/segmentio/nightmare#switches
         *
         * @type {String} 代理服务器，类似 my_proxy_server.example.com:8080
         */
        this.proxyServer = opts.proxyServer;

        this.mockstarQuery = opts.mockstarQuery || null;

        // 是否使用记录器记录整个请求队列
        // 如果为 true，则可以从 this.globalInfo.recorder 中获取，
        // 如果为 字符串，则可以从 this.globalInfo[xxx] 中获取，
        this.useRecorder = (function (useRecorder) {
            if (!useRecorder) {
                return false;
            }

            return (typeof useRecorder === 'boolean') ? 'recorder' : useRecorder;
        })(opts.useRecorder);

        // 截屏设置
        this.screenshotConfig = opts.screenshotConfig;

        this.globalInfo = {};

        this.onNightmareCreated = (self) => {

        };

        this.onBeforeGotoPage = (self) => {

        };

        this.actionList = [];
    }

    async getResult() {
        // nightmare 初始化参数
        let nightmareConfig = {
            show: this.show,
            webPreferences: {
                preload: this.crawlerScriptPath
            }
        };

        // 设置代理服务器
        if (this.proxyServer) {
            nightmareConfig.switches = {
                'proxy-server': this.proxyServer
            };
        }

        // 如果有设置符合要求的 matman 服务设置，则还需要额外处理一下
        if (this.mockstarQuery && (typeof this.mockstarQuery.getQueryString === 'function')) {
            this.pageUrl = this.pageUrl + ((this.pageUrl.indexOf('?') > -1) ? '&' : '?') + this.mockstarQuery.getQueryString();
        }

        // console.log('===nightmareConfig====', nightmareConfig);

        // 创建 nightmare 对象
        this.nightmare = NightmarePlus(nightmareConfig);

        // 创建完成之后，可能会有一些自己的处理
        this.onNightmareCreated(this);

        // 使用记录器
        if (this.useRecorder) {
            this.globalInfo[this.useRecorder] = new WebEventRecorder(this.nightmare);
        }

        // 初始化一些行为
        this.nightmareRun = this.nightmare
            .exDevice('mobile')
            .header('x-mat-from', 'nightmare')
            .header('x-mat-timestamp', Date.now());

        // 设置 cookie
        if (this.cookie) {
            this.nightmareRun.exCookies(this.cookie, getMainUrl(this.pageUrl));
        }

        // 加载页面之前要执行的方法
        this.onBeforeGotoPage(this);

        // 加载页面
        this.nightmareRun = this.nightmareRun.goto(this.pageUrl);

        // 如果指定了 opts.wait，则会传递给 nightmare 处理，具体使用方法可以参考：
        // https://github.com/segmentio/nightmare#waitms
        if (typeof this.wait !== 'undefined') {
            this.nightmareRun = this.nightmareRun.wait(this.wait);
        }

        // 循环处理多个 action
        let result = [];

        for (let i = 0, length = this.actionList.length; i < length; i++) {
            let curRun = this.actionList[i](this.nightmareRun);

            if (this.screenshotConfig) {
                curRun.screenshot(this.screenshotConfig.getPathWithId(i + 1), this.screenshotConfig.clip);
            }

            let t = await curRun.evaluate(evaluate);

            result.push(t);
        }

        // 不关闭界面
        if (this.doNotEnd) {
            await this.nightmareRun;
        } else {
            await this.nightmareRun.end();
        }

        return {
            data: result,
            globalInfo: this.globalInfo
        };
    }

    addAction(actionCall) {
        this.actionList.push(actionCall);
    }

    //
    // onNightmareCreated(callback) {
    //     if (typeof callback === 'function') {
    //         return callback(this.nightmare);
    //     }
    //
    //     return this.nightmare;
    // }

}

function evaluate() {
    // 如果没有这个变量，说明注入代码失败
    if (!window.matman_ver) {
        return {
            error: 'preload failed!'
        };
    }

    // window.getPageInfo 必须是个函数
    // window.getPageInfo 方法和其他变量均由 preload 配置中的 js 文件引入
    if (typeof window.getPageInfo !== 'function') {
        return {
            error: 'window.getPageInfo is not function!'
        };
    }

    // 如果存在需要前端执行的代码，则在所有逻辑开始之前执行
    if (window.evalList && window.evalList.length) {
        window.evalList.forEach((item) => {
            eval(window[item]);
        });
    }

    return window.getPageInfo();
}

function getMainUrl(url) {
    let arr1 = url.split('//');

    if (arr1.length > 1) {
        let arr2 = arr1[1].split('/');
        return arr1[0] + '//' + arr2[0];
    } else {
        let arr2 = arr1[0].split('/');
        return arr2[0];
    }
}

<|endoftext|>


<commit_before>'use strict';
/**
 * Lighthouse plugin runs https://www.npmjs.com/package/lighthouse on paths of your build.
 */
var Script = require('./Script');

module.exports = class Lighthouse extends Script {
  /**
   * Options (used by this task):
   *   @param {array} [options.paths] - Array of relative paths to test, e.g. ['/', '/about']. Defaults to ['/'].
   *   @param {array} [options.categories] - Array of objects, each object has a key of the category
   *     name (performance, pwa, accessibility, best-practices, seo) and a value of the minimum
   *     score accepted. Note a full audit is always run. This influences what is displayed
   *     and allows optional lower limits for scores.
   *   @param {array} [options.lighthouseOptions] - Array of options to pass to the lighthouse cli command if necessary.
   *     @see https://www.npmjs.com/package/lighthouse
   */
  constructor(container, options) {

    super(container, options);

    this.paths = options.paths || ['/'];
    this.categories = options.categories || [{performance: 0}, {pwa:0}, {accessibility:0}, {'best-practices':0}, {seo:0}];

    options.lighthouseOptions = options.lighthouseOptions || [];
    this.lighthouseOptions = [
      {'output': 'json'},
      {'output': 'html'},
      {'chrome-flags': "--no-sandbox=true --disable-setuid-sandbox --headless"},
    ];
    // These default options can be changed with options.lighthouseOptions.
    this.lighthouseOptions = this.lighthouseOptions.concat(options.lighthouseOptions);

    this.script = [];

    this.installLighthouse();
    // run for each path
    this.paths.forEach(this.runLighthouse, this);

    this.setScript(this.script);
  }

  installLighthouse() {
    // Lighthouse requires Node 8 LTS (8.9) or later.
    // TODO: Can we include this in images?

    this.script = this.script.concat([
      'set +ux', // Don't be so verbose in logs that we can't even find results.
      'echo "Installing Lighthouse"',
      'npm cache clean -f &> /dev/null',
      'npm install -g n &> /dev/null',
      'n stable &> /dev/null',
      'npm install -g lighthouse &> /dev/null',
      'apt-get install jq &> /dev/null', // Install jq for parsing json.
    ]);
  }

  runLighthouse(path) {
    this.pathText = path.replace(/\//g, 'slash');
    this.lighthouseFlags = '--output-path="/var/www/html/lighthouse-results-'+this.pathText+'.json" ';
    this.lighthouseOptions.forEach(this.parseOptions, this);
    this.script = this.script.concat([
      'echo "Starting Lighthouse tests of "' + path,
      'URL=$BUILD_DOMAIN' + path,
      'mkdir -p /var/www/html',
      'lighthouse $URL ' + this.lighthouseFlags +' &> /dev/null',
      'echo "\x1B[1;30;43mView full Lighthouse report at $BUILD_DOMAIN/lighthouse-results-'+this.pathText+'.report.html"',

    ]);
    this.categories.forEach(this.parseCategory, this);
  }

  parseOptions(value) {
    let key=Object.keys(value)[0];
    this.lighthouseFlags += "--"+key+"='"+value[key]+"' ";
  }

  parseCategory(value) {
    let id=Object.keys(value)[0];
    let scoreLimit=value[id];
    this.script = this.script.concat([
      'id='+id,
      'scoreLimit='+scoreLimit,
      "score=$(cat /var/www/html/lighthouse-results-"+this.pathText+".report.json | jq -r '.categories[] | select(.id == \"'$id'\") | .score')",
      'score=$(awk "BEGIN {print $score*100}")',
      "title=$(cat /var/www/html/lighthouse-results-"+this.pathText+".report.json | jq -r '.categories[] | select(.id == \"'$id'\") | .title')",
      'echo "\x1B[1;30;43m$title: $score"',
      'pass=$(awk "BEGIN {if($score >= $scoreLimit) {print 0} else {print 1} }")',
      'if [ $pass -eq 0 ]; then echo "\x1B[1;30;42mPASS. $title score is greater than or equal to limit of $scoreLimit"; else echo "\x1B[1;30;41mFAIL. $title score is less than limit of $scoreLimit" >&2; fi',
      'if [ $pass -eq 1 ]; then exit 1; fi', // Step fails.
    ]);
  };

  description() {
    return 'Run Lighthouse checks';
  }
};<commit_msg>More verbose logs for debugging and give an exit code at end<commit_after>'use strict';
/**
 * Lighthouse plugin runs https://www.npmjs.com/package/lighthouse on paths of your build.
 */
var Script = require('./Script');

module.exports = class Lighthouse extends Script {
  /**
   * Options (used by this task):
   *   @param {array} [options.paths] - Array of relative paths to test, e.g. ['/', '/about']. Defaults to ['/'].
   *   @param {array} [options.categories] - Array of objects, each object has a key of the category
   *     name (performance, pwa, accessibility, best-practices, seo) and a value of the minimum
   *     score accepted. Note a full audit is always run. This influences what is displayed
   *     and allows optional lower limits for scores.
   *   @param {array} [options.lighthouseOptions] - Array of options to pass to the lighthouse cli command if necessary.
   *     @see https://www.npmjs.com/package/lighthouse
   */
  constructor(container, options) {

    super(container, options);

    this.paths = options.paths || ['/'];
    this.categories = options.categories || [{performance: 0}, {pwa:0}, {accessibility:0}, {'best-practices':0}, {seo:0}];

    options.lighthouseOptions = options.lighthouseOptions || [];
    this.lighthouseOptions = [
      {'output': 'json'},
      {'output': 'html'},
      {'chrome-flags': "--no-sandbox=true --disable-setuid-sandbox --headless"},
    ];
    // These default options can be changed with options.lighthouseOptions.
    this.lighthouseOptions = this.lighthouseOptions.concat(options.lighthouseOptions);

    this.script = [];

    this.installLighthouse();
    // run for each path
    this.paths.forEach(this.runLighthouse, this);
    this.script = this.script.concat([
      'exit 0',
    ]);
    this.setScript(this.script);
  }

  installLighthouse() {
    // Lighthouse requires Node 8 LTS (8.9) or later.
    // TODO: Can we include this in images?

    this.script = this.script.concat([
      'echo "Installing Lighthouse"',
      'npm cache clean -f',
      'npm install -g n',
      'n stable',
      'npm install -g lighthouse',
      'apt-get install jq -y', // Install jq for parsing json.
    ]);
  }

  runLighthouse(path) {
    this.pathText = path.replace(/\//g, 'slash');
    this.lighthouseFlags = '--output-path="/var/www/html/lighthouse-results-'+this.pathText+'.json" ';
    this.lighthouseOptions.forEach(this.parseOptions, this);
    this.script = this.script.concat([
      'set +ux', // Don't be so verbose in logs that we can't even find results.
      'echo "Starting Lighthouse tests of "' + path,
      'URL=$BUILD_DOMAIN' + path,
      'mkdir -p /var/www/html',
      'lighthouse $URL ' + this.lighthouseFlags +' &> /dev/null',
      'echo "\x1B[1;30;43mView full Lighthouse report at $BUILD_DOMAIN/lighthouse-results-'+this.pathText+'.report.html"',
    ]);
    this.categories.forEach(this.parseCategory, this);
  }

  parseOptions(value) {
    let key=Object.keys(value)[0];
    this.lighthouseFlags += "--"+key+"='"+value[key]+"' ";
  }

  parseCategory(value) {
    let id=Object.keys(value)[0];
    let scoreLimit=value[id];
    this.script = this.script.concat([
      'id='+id,
      'scoreLimit='+scoreLimit,
      "score=$(cat /var/www/html/lighthouse-results-"+this.pathText+".report.json | jq -r '.categories[] | select(.id == \"'$id'\") | .score')",
      'score=$(awk "BEGIN {print $score*100}")',
      "title=$(cat /var/www/html/lighthouse-results-"+this.pathText+".report.json | jq -r '.categories[] | select(.id == \"'$id'\") | .title')",
      'echo "\x1B[1;30;43m$title: $score"',
      'pass=$(awk "BEGIN {if($score >= $scoreLimit) {print 0} else {print 1} }")',
      'if [ $pass -eq 0 ]; then echo "\x1B[1;30;42mPASS. $title score is greater than or equal to limit of $scoreLimit"; else echo "\x1B[1;30;41mFAIL. $title score is less than limit of $scoreLimit" >&2; fi',
      'if [ $pass -eq 1 ]; then exit 1; fi', // Step fails.
    ]);
  };

  description() {
    return 'Run Lighthouse checks';
  }
};<|endoftext|>


<commit_before>var azureStar = ({
  boundaries: function() {
    return {
      width: (document.documentElement.clientWidth || document.body.clientWidth || window.innerWidth || 930) - 30,
      height: (document.documentElement.clientHeight || document.body.clientHeight || window.innerHeight || 530) - 30
    };
  },
  input: new Input(),
  fps: 60,
  sleep: 1000 / this.fps,

  init: function () {
    function createGameCanvas(game, inputHandler) {
      var canvas = document.getElementById('game-canvas');

      canvas.addEventListener('blur', canvas.focus);
      canvas.addEventListener('keydown', inputHandler.keydown);
      canvas.addEventListener('keyup', inputHandler.keyup);

      window.addEventListener('after-resize', function() {
        var boundaries = game.boundaries();
        canvas.width = boundaries.width;
        canvas.height = boundaries.height;
        game.state.resize(boundaries);
      });
      return canvas;
    }

    var self = this,
        canvas = createGameCanvas(this, this.input),
        context = canvas.getContext("2d");

    this.state = new LoadingState(this);
    this.loop(context);

    window.addEventListener('resize', function(e) {
      setTimeout(function() {
        window.dispatchEvent(new Event('after-resize'))
      }, 500);
    });

    window.dispatchEvent(new Event('after-resize'));
    return this;
  },

  loop: function (context) {
    var self = this,
        interval = setInterval(function() {
          if(self.state.isComplete()) {
            self.state = self.state.nextStage();
          }
          self.state.readInput(self.input);
          self.state.update();

          self.sleep = (1000 / self.fps) - measureTime(function(){
            context.clearRect(0 ,0, context.canvas.width, context.canvas.height);
            self.state.render(context);
          });

          clearInterval(interval);
          self.loop(context);
        }, this.sleep);
  },

  gameOver: function(context) {
      context.save();
      context.fillStyle = "#D30035";
      context.font = '70px Guardians';
      context.textAlign = 'center';
      context.fillText("Game Over", this.boundaries.width / 2 , this.boundaries.height / 2);
      context.restore();
  },
}).init();
<commit_msg>Use requestAnimationFrame instead of setInterval<commit_after>var azureStar = ({
  boundaries: function() {
    return {
      width: (document.documentElement.clientWidth || document.body.clientWidth || window.innerWidth || 930) - 30,
      height: (document.documentElement.clientHeight || document.body.clientHeight || window.innerHeight || 530) - 30
    };
  },
  input: new Input(),

  init: function () {
    function createGameCanvas(game, inputHandler) {
      var canvas = document.getElementById('game-canvas');

      canvas.addEventListener('blur', canvas.focus);
      canvas.addEventListener('keydown', inputHandler.keydown);
      canvas.addEventListener('keyup', inputHandler.keyup);

      window.addEventListener('after-resize', function() {
        var boundaries = game.boundaries();
        canvas.width = boundaries.width;
        canvas.height = boundaries.height;
        game.state.resize(boundaries);
      });
      return canvas;
    }

    var self = this,
        canvas = createGameCanvas(this, this.input);

    this.context = canvas.getContext("2d");
    this.state = new LoadingState(this);
    this.loop();

    window.addEventListener('resize', function(e) {
      setTimeout(function() {
        window.dispatchEvent(new Event('after-resize'))
      }, 500);
    });

    window.dispatchEvent(new Event('after-resize'));
    return this;
  },

  loop: function() {
    var self = this;

    (function gameLoop() {
      if(self.state.isComplete()) {
        self.state = self.state.nextStage();
      }
      self.state.readInput(self.input);
      self.state.update();

      self.context.clearRect(0 ,0, self.context.canvas.width, self.context.canvas.height);
      self.state.render(self.context);
      requestAnimationFrame(gameLoop);
    })();
  },

  gameOver: function(context) {
      context.save();
      context.fillStyle = "#D30035";
      context.font = '70px Guardians';
      context.textAlign = 'center';
      context.fillText("Game Over", this.boundaries.width / 2 , this.boundaries.height / 2);
      context.restore();
  },
}).init();
<|endoftext|>


<commit_before>/**
 * Simple interface for interacting with user roles and permissions
 *
 * permissions is an object with the following structure
 *
 * permissions {
 * 	'<permission name>' : [<'role name', 'role name' ...]
 *
 * }
 */
const state = {
	permissions: {
		'editSite': ['admin', 'moderator', 'spanner']
	},
	role: 'moderator',
};

const getters = {
	/**
	 * checks if the current user has a specific permission
	 * the role of the current user has with the current site is assumed to be in the state as role
	 *
	 * @param {object} state - the current vuex state - automagically added
	 * @param {string} permission_name - the name of a permission e.g. 'editSite', 'addSite' etc
	 * @returns {boolean} - true if the role has the permission_name
	 */
	userCan: (state, getters) => (permission_name) => {
		if (state.permissions.hasOwnProperty(permission_name)) {
			return state.permissions[permission_name].includes(state.role);
		}
		else {
			return false;
		}
	}
};

const actions = {};

const mutations = {

	/**
	 * updates the role that the current user has in the current site
	 * @param {*} state
	 * @param {string} role_name - the name of the role being assigned
	 */
	setRole(state, role_name) {
		state.role = role_name;
	},

	/**
	 * Used to initially set the permissions the editor understands
	 *
	 * @param {*} state
	 * @param {object} permissions - the set of permissions as received from the API
	 */
	setPermissions(state, permissions) {

	}

};

export default {
	state,
	getters,
	mutations
};<commit_msg>WIP - keeping this point before api re-write by Sam<commit_after>/**
 * Simple interface for interacting with user roles and permissions
 *
 * permissions is an object with the following structure
 *
 * permissions {
 * 	'<permission name>' : [<'role name', 'role name' ...]
 *
 * }
 */
const state = {
	permissions: {
		'editSite': ['admin', 'moderator', 'spanner']
	}
};

const getters = {
	/**
	 * checks if the current user has a specific permission
	 * the role of the current user has with the current site is assumed to be in the state as role
	 *
	 * @param {object} state - the current vuex state - automagically added
	 * @param {string} permission_name - the name of a permission e.g. 'editSite', 'addSite' etc
	 * @returns {boolean} - true if the role has the permission_name
	 */
	userCan: (state, getters) => (permission_name) => {
		if (state.permissions.hasOwnProperty(permission_name)) {
			return state.permissions[permission_name].includes(state.role);
		}
		else {
			return false;
		}
	},

	/**
	 * returns the role that the logged in user has in the current site
	 * if we do not have a current site then it returns null
	 */
	role: (state, getters) => () => {

	}
};

const actions = {

	/**
	 * gets list of roles from the api can calls mutation to store this in the state
	 */
	loadGlobalRolls() {

		let permissions = {};

		api
			.get(`permissions`, {
				// blocks: page.blocks
			})
			.then(({data}) => {
				permissions =
				dispatch('fetchSite');
			})

	},

	/**
	 * gets list of a user's roles from the api can calls mutation to store this in the state
	 */
	loadUserRoles() {

	}

};

const mutations = {

	/** REPLACE WITH PAYLOAD
	 * sets the role for a user within a given site
	 * @param	{string}		username eg. 'cfc7'
	 * @param	{array}			array of users for a given site as returned by /api/v1/sites/1?include=users
	 * @return	{string|null}	the rolename e.g. 'Admin', 'Editor' etc or null is the user has no role
	 */
	setRole(state, user_name, site_roles) {

		role = null;

		if (!Array.isArray(site_roles)) {
			roll = null
		} else {
			currentRole = site_roles.find(user => user.user.username === user_name);
			if (currentRole) {
				roll =  currentRole.role;
			} else {
				roll = null;
			}
		}

		state.role = roll;
	},

	/**
	 * Used to initially set the permissions the editor understands
	 *
	 * @param {*} state
	 * @param {object} permissions - the set of permissions as received from the API
	 */
	setPermissions(state, permissions) {
		state.permissions = permissions;
	}

};

export default {
	state,
	getters,
	mutations
};<|endoftext|>