<commit_before>// Copyright © 2016 Jan Keromnes. All rights reserved.
// The following code is covered by the AGPL-3.0 license.

const certificates = require('./certificates');
const configurations = require('./configurations');
const db = require('./db');
const github = require('./github');
const hosts = require('./hosts');
const log = require('./log');
const metrics = require('./metrics');
const sessions = require('./sessions');

const hostname = db.get('hostname', 'localhost');
const security = db.get('security');
const baseUrl = (security.forceHttp ? 'http' : 'https') + '://' + hostname;

// Get a user for the current session.
exports.get = function (request, callback) {
  sessions.get(request, (error, session, token) => {
    if (error) {
      log('[fail] session', error);
      callback(null, session);
      return;
    }

    if (session.emailVerified()) {
      // The user is properly logged in.
      callback(getOrCreateUser(session.email), session);
      return;
    }

    const { key } = request.query;
    if (!key) {
      callback(null, session);
      return;
    }

    // We have a login key, let's see if it verifies the user's email.
    sessions.verifyEmail(token, key, (error, email) => {
      if (error || !email) {
        // No luck this time.
        log('email not verified', error);
        callback(null, session);
        return;
      }

      log(email, 'verified');
      callback(getOrCreateUser(email), session);
    });
  });
};

// Destroy the current session.
exports.logout = function (request, callback) {
  sessions.destroy(request, error => {
    callback(error);
  });
};

// Check if a given user has admin privileges.
exports.isAdmin = function (user) {
  if (user && user.email && (user.email in db.get('admins'))) {
    return true;
  }

  return false;
};

// Find the OAuth2 access scope and authorizing user for a request.
exports.getOAuth2ScopeWithUser = function (request) {
  const authenticatedScope = hosts.getOAuth2Scope(request);
  if (!authenticatedScope) {
    return null;
  }

  const { email, hostname, scopes } = authenticatedScope;
  const user = getOrCreateUser(email);
  return { hostname, scopes, user };
};

// Reset a user's dedicated Janitor SSH key pair.
exports.resetSSHKeyPair = function (user) {
  certificates.createSSHKeyPair((error, keypair) => {
    if (error) {
      log('error while creating ssh keypair', error);
      return;
    }

    user.keys.ssh.fingerprint = keypair.fingerprint;
    user.keys.ssh.privateKey = keypair.privateKey;
    user.keys.ssh.publicKey = keypair.publicKey;
    db.save();
  });
};

// Refresh a user's GitHub account details using an OAuth2 access token.
exports.refreshGitHubAccount = function (user, accessToken, refreshToken) {
  github.getUserProfile(accessToken, (error, profile) => {
    if (error) {
      log('[fail] could not get github username', error);
      return;
    }

    const { login: username, name } = profile;
    user.keys.github.username = username;
    user.keys.github.accessToken = accessToken;
    user.keys.github.refreshToken = refreshToken;
    user.profile.name = user.profile.name || name;

    db.save();

    github.getSSHPublicKeys(username, (error, sshPublicKeys) => {
      if (error) {
        log('[fail] could not get github public keys', error);
        return;
      }

      user.keys.github.authorizedKeys = sshPublicKeys;
      db.save();
    });
  });
};

// Forget a user's GitHub account details, including any access tokens.
exports.destroyGitHubAccount = function (user) {
  user.keys.github = {
    username: '',
    accessToken: '',
    refreshToken: '',
    authorizedKeys: []
  };
  db.save();
};

// Forget a user's Cloud9 account details.
exports.destroyCloud9Account = function (user) {
  user.keys.cloud9 = '';
  user.keys.cloud9user = '';
  db.save();
};

// Send a single-use login link to the user's email address.
exports.sendLoginEmail = function (email, request, callback) {
  sessions.get(request, (error, session, token) => {
    if (error) {
      log('[fail] could not send login email', error);
    }

    // Alpha version is invite-only.
    if (!(email in db.get('users')) && !exports.isAdmin({ email })) {
      // Add unknown emails to the waitlist.
      const waitlist = db.get('waitlist');
      if (!(email in waitlist)) {
        waitlist[email] = Date.now();
        db.save();
      }

      return callback(new Error('Signing in currently requires an invitation'));
    }

    // Login email template.
    const template = {
      subject () {
        return 'Janitor Sign-in link';
      },
      htmlMessage (key) {
        const url = baseUrl + '/?key=' + encodeURIComponent(key);

        if (security.forceInsecure) {
          log('[warning] sign-in link for ' + email + ':', url);
        }

        return '
Hello,
\n' +
        '
To sign in to the Janitor, please click ' +
          'here.
\n' +
        '
This link will only work once, but you can get as many links as ' +
          'you want.
\n' +
        '
Thanks!
\nThe Janitor
\n';
      },
      textMessage (key) {
        const url = baseUrl + '/?key=' + encodeURIComponent(key);
        return 'Hello,\n\n' +
        'To sign in to the Janitor, please visit the following URL:\n\n' +
          url + '\n\n' +
        'This link will only work once, but you can get as many links as ' +
          'you want.\n\n' +
        'Thanks!\nThe Janitor\n';
      }
    };

    sessions.sendVerificationEmail(email, token, template, callback);
  });
};

// Invite someone to join the Alpha version.
exports.sendInviteEmail = function (email, callback) {
  // Generate a dummy token for that invite.
  sessions.create((error, token, session) => {
    if (error) {
      log('[fail] could not create the session', error);
    }

    // Invite email template.
    const template = {
      subject () {
        return 'Janitor Invite';
      },
      htmlMessage (key) {
        const url = baseUrl + '/settings/?key=' + encodeURIComponent(key);
        return '
You are invited to join the Alpha of the Janitor.
\n' +
        '
To activate your free and unlimited account, please follow the ' +
          'steps below:\n
' +
        '
Make sure you have a Cloud9 account (' +
          'click here to create one for ' +
          'free)
\n' +
        '
Click here to get your ' +
          'Cloud9 SSH public key
\n' +
        '
Click here to access your Janitor ' +
          'account, then add your Cloud9 username and your Cloud9 key
' +
          '

\n' +
        '
With that, you will be able to clone and edit all the supported ' +
          'projects, as often and for as long as you like.
\n' +
        '
Happy hacking!
\nThe Janitor
\n';
      },
      textMessage (key) {
        const url = baseUrl + '/settings/?key=' + encodeURIComponent(key);
        return 'You are invited to join the Alpha of the Janitor.\n\n' +
        'To activate your free and unlimited account, please follow the ' +
          'steps below:\n\n' +
        '1. Make sure you have a Cloud9 account. You can create one for free ' +
          'by visiting:\n' +
          'https://c9.io/signup\n\n' +
        '2. Get your Cloud9 SSH public key by visiting:\n' +
          'https://c9.io/account/ssh\n\n' +
        '3. Access your Janitor account, then add your Cloud9 username ' +
          'and your Cloud9 key:\n' + url + '\n\n' +
        'With that, you will be able to clone and edit all the supported ' +
          'projects, as often and for as long as you like.\n\n' +
        'Happy hacking!\nThe Janitor\n';
      }
    };

    sessions.sendVerificationEmail(email, token, template, error => {
      if (error) {
        callback(error);
        return;
      }

      // Ensure we have a user for that email address.
      const user = getOrCreateUser(email);

      // Remove this email from the waitlist.
      const waitlist = db.get('waitlist');
      if (email in waitlist) {
        // Remember when the user signed up, not when they were invited.
        metrics.set(user, 'joined', waitlist[email]);
        delete waitlist[email];
        db.save();
      }

      callback();
    });
  });
};

// Find an existing user, or create a new one.
function getOrCreateUser (email) {
  const users = db.get('users');
  let user = users[email];

  if (!user) {
    user = {
      email: email,
      profile: {
        name: '',
      },
      configurations: {
        '.gitconfig': '',
        '.gitignore': '',
        '.hgrc': '',
      },
      keys: {
        cloud9: '',
        cloud9user: '',
        github: {
          username: '',
          accessToken: '',
          refreshToken: '',
          authorizedKeys: []
        },
        ssh: {
          fingerprint: '',
          privateKey: '',
          publicKey: '',
        }
      },
      machines: {},
      notifications: {
        enabled: false,
        feed: [],
      },
      data: {},
    };

    metrics.set(user, 'joined', Date.now());
    exports.resetSSHKeyPair(user);

    users[email] = user;
    db.save();
  }

  // Temporary migration code: Previous users didn't have an SSH key pair.
  if (!user.keys.ssh) {
    user.keys.ssh = {
      fingerprint: '',
      privateKey: '',
      publicKey: '',
    };
    exports.resetSSHKeyPair(user);
  }

  // Temporary migration code: Previous users didn't have a profile.
  if (!user.profile) {
    user.profile = {
      name: ''
    };
    delete user.name;
  }

  for (const projectId in user.machines) {
    for (const machine of user.machines[projectId]) {
      // Temporary migration code: Previous users didn't have machine
      // properties.
      if (!machine.properties) {
        machine.properties = { name: machine.name };
        delete machine.name;
      }

      // Temporary migration code: Previous machines didn't keep keep track of
      // their project.
      if (!machine.project) {
        machine.project = projectId;
      }
    }
  }

  // Temporary migration code: Previous users didn't have GitHub credentials.
  if (!('github' in user.keys)) {
    exports.destroyGitHubAccount(user);
  }

  // Temporary migration code: Previous users didn't have configuration files.
  if (!user.configurations) {
    user.configurations = {};
  }
  Object.keys(configurations.defaults).forEach(file => {
    if (!user.configurations[file]) {
      configurations.resetToDefault(user, file, error => {
        if (error) {
          log('[fail] default user configuration:', file, error);
        }
      });
    }
  });

  // Temporary migration code: Previous users didn't have notification settings.
  if (!user.notifications) {
    user.notifications = {
      enabled: false,
      feed: [],
    };
  }

  return user;
}
<commit_msg>Simplify invite email now that a c9.io account is no longer required.<commit_after>// Copyright © 2016 Jan Keromnes. All rights reserved.
// The following code is covered by the AGPL-3.0 license.

const certificates = require('./certificates');
const configurations = require('./configurations');
const db = require('./db');
const github = require('./github');
const hosts = require('./hosts');
const log = require('./log');
const metrics = require('./metrics');
const sessions = require('./sessions');

const hostname = db.get('hostname', 'localhost');
const security = db.get('security');
const baseUrl = (security.forceHttp ? 'http' : 'https') + '://' + hostname;

// Get a user for the current session.
exports.get = function (request, callback) {
  sessions.get(request, (error, session, token) => {
    if (error) {
      log('[fail] session', error);
      callback(null, session);
      return;
    }

    if (session.emailVerified()) {
      // The user is properly logged in.
      callback(getOrCreateUser(session.email), session);
      return;
    }

    const { key } = request.query;
    if (!key) {
      callback(null, session);
      return;
    }

    // We have a login key, let's see if it verifies the user's email.
    sessions.verifyEmail(token, key, (error, email) => {
      if (error || !email) {
        // No luck this time.
        log('email not verified', error);
        callback(null, session);
        return;
      }

      log(email, 'verified');
      callback(getOrCreateUser(email), session);
    });
  });
};

// Destroy the current session.
exports.logout = function (request, callback) {
  sessions.destroy(request, error => {
    callback(error);
  });
};

// Check if a given user has admin privileges.
exports.isAdmin = function (user) {
  if (user && user.email && (user.email in db.get('admins'))) {
    return true;
  }

  return false;
};

// Find the OAuth2 access scope and authorizing user for a request.
exports.getOAuth2ScopeWithUser = function (request) {
  const authenticatedScope = hosts.getOAuth2Scope(request);
  if (!authenticatedScope) {
    return null;
  }

  const { email, hostname, scopes } = authenticatedScope;
  const user = getOrCreateUser(email);
  return { hostname, scopes, user };
};

// Reset a user's dedicated Janitor SSH key pair.
exports.resetSSHKeyPair = function (user) {
  certificates.createSSHKeyPair((error, keypair) => {
    if (error) {
      log('error while creating ssh keypair', error);
      return;
    }

    user.keys.ssh.fingerprint = keypair.fingerprint;
    user.keys.ssh.privateKey = keypair.privateKey;
    user.keys.ssh.publicKey = keypair.publicKey;
    db.save();
  });
};

// Refresh a user's GitHub account details using an OAuth2 access token.
exports.refreshGitHubAccount = function (user, accessToken, refreshToken) {
  github.getUserProfile(accessToken, (error, profile) => {
    if (error) {
      log('[fail] could not get github username', error);
      return;
    }

    const { login: username, name } = profile;
    user.keys.github.username = username;
    user.keys.github.accessToken = accessToken;
    user.keys.github.refreshToken = refreshToken;
    user.profile.name = user.profile.name || name;

    db.save();

    github.getSSHPublicKeys(username, (error, sshPublicKeys) => {
      if (error) {
        log('[fail] could not get github public keys', error);
        return;
      }

      user.keys.github.authorizedKeys = sshPublicKeys;
      db.save();
    });
  });
};

// Forget a user's GitHub account details, including any access tokens.
exports.destroyGitHubAccount = function (user) {
  user.keys.github = {
    username: '',
    accessToken: '',
    refreshToken: '',
    authorizedKeys: []
  };
  db.save();
};

// Forget a user's Cloud9 account details.
exports.destroyCloud9Account = function (user) {
  user.keys.cloud9 = '';
  user.keys.cloud9user = '';
  db.save();
};

// Send a single-use login link to the user's email address.
exports.sendLoginEmail = function (email, request, callback) {
  sessions.get(request, (error, session, token) => {
    if (error) {
      log('[fail] could not send login email', error);
    }

    // Alpha version is invite-only.
    if (!(email in db.get('users')) && !exports.isAdmin({ email })) {
      // Add unknown emails to the waitlist.
      const waitlist = db.get('waitlist');
      if (!(email in waitlist)) {
        waitlist[email] = Date.now();
        db.save();
      }

      return callback(new Error('Signing in currently requires an invitation'));
    }

    // Login email template.
    const template = {
      subject () {
        return 'Janitor Sign-in link';
      },
      htmlMessage (key) {
        const url = baseUrl + '/?key=' + encodeURIComponent(key);

        if (security.forceInsecure) {
          log('[warning] sign-in link for ' + email + ':', url);
        }

        return '
Hello,
\n' +
        '
To sign in to Janitor, please click ' +
          'here.
\n' +
        '
This link will only work once, but you can get as many links as ' +
          'you want.
\n' +
        '
Thanks!
\n';
      },
      textMessage (key) {
        const url = baseUrl + '/?key=' + encodeURIComponent(key);
        return 'Hello,\n\n' +
        'To sign in to Janitor, please visit the following URL:\n\n' +
          url + '\n\n' +
        'This link will only work once, but you can get as many links as ' +
          'you want.\n\n' +
        'Thanks!\n';
      }
    };

    sessions.sendVerificationEmail(email, token, template, callback);
  });
};

// Invite someone to join the Alpha version.
exports.sendInviteEmail = function (email, callback) {
  // Generate a dummy token for that invite.
  sessions.create((error, token, session) => {
    if (error) {
      log('[fail] could not create the session', error);
    }

    // Invite email template.
    const template = {
      subject () {
        return 'Janitor Invite';
      },
      htmlMessage (key) {
        const url = baseUrl + '/projects/?key=' + encodeURIComponent(key);
        return '
You are invited to try the Janitor Alpha.
\n' +
        '
To activate your free and unlimited account, please click ' +
          'here.
\n' +
        '
Happy hacking!
\n';
      },
      textMessage (key) {
        const url = baseUrl + '/projects/?key=' + encodeURIComponent(key);
        return 'You are invited to try the Janitor Alpha.\n\n' +
        'To activate your free and unlimited account, please visit the ' +
          'following URL:\n\n' + url + '\n\n' +
        'Happy hacking!\n';
      }
    };

    sessions.sendVerificationEmail(email, token, template, error => {
      if (error) {
        callback(error);
        return;
      }

      // Ensure we have a user for that email address.
      const user = getOrCreateUser(email);

      // Remove this email from the waitlist.
      const waitlist = db.get('waitlist');
      if (email in waitlist) {
        // Remember when the user signed up, not when they were invited.
        metrics.set(user, 'joined', waitlist[email]);
        delete waitlist[email];
        db.save();
      }

      callback();
    });
  });
};

// Find an existing user, or create a new one.
function getOrCreateUser (email) {
  const users = db.get('users');
  let user = users[email];

  if (!user) {
    user = {
      email: email,
      profile: {
        name: '',
      },
      configurations: {
        '.gitconfig': '',
        '.gitignore': '',
        '.hgrc': '',
      },
      keys: {
        cloud9: '',
        cloud9user: '',
        github: {
          username: '',
          accessToken: '',
          refreshToken: '',
          authorizedKeys: []
        },
        ssh: {
          fingerprint: '',
          privateKey: '',
          publicKey: '',
        }
      },
      machines: {},
      notifications: {
        enabled: false,
        feed: [],
      },
      data: {},
    };

    metrics.set(user, 'joined', Date.now());
    exports.resetSSHKeyPair(user);

    users[email] = user;
    db.save();
  }

  // Temporary migration code: Previous users didn't have an SSH key pair.
  if (!user.keys.ssh) {
    user.keys.ssh = {
      fingerprint: '',
      privateKey: '',
      publicKey: '',
    };
    exports.resetSSHKeyPair(user);
  }

  // Temporary migration code: Previous users didn't have a profile.
  if (!user.profile) {
    user.profile = {
      name: ''
    };
    delete user.name;
  }

  for (const projectId in user.machines) {
    for (const machine of user.machines[projectId]) {
      // Temporary migration code: Previous users didn't have machine
      // properties.
      if (!machine.properties) {
        machine.properties = { name: machine.name };
        delete machine.name;
      }

      // Temporary migration code: Previous machines didn't keep keep track of
      // their project.
      if (!machine.project) {
        machine.project = projectId;
      }
    }
  }

  // Temporary migration code: Previous users didn't have GitHub credentials.
  if (!('github' in user.keys)) {
    exports.destroyGitHubAccount(user);
  }

  // Temporary migration code: Previous users didn't have configuration files.
  if (!user.configurations) {
    user.configurations = {};
  }
  Object.keys(configurations.defaults).forEach(file => {
    if (!user.configurations[file]) {
      configurations.resetToDefault(user, file, error => {
        if (error) {
          log('[fail] default user configuration:', file, error);
        }
      });
    }
  });

  // Temporary migration code: Previous users didn't have notification settings.
  if (!user.notifications) {
    user.notifications = {
      enabled: false,
      feed: [],
    };
  }

  return user;
}
<|endoftext|>


<commit_before>"use strict";

const Cu = Components.utils;
const Cc = Components.classes;
const Ci = Components.interfaces;

Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");

function dumpln(s) { dump(s + "\n"); }

const CHILD_SCRIPT = "chrome://domfuzzhelper/content/domfuzzhelper.js";

/*****************
 * API INJECTION *
 *****************/

// Based on:
// https://bug549539.bugzilla.mozilla.org/attachment.cgi?id=429661
// https://developer.mozilla.org/en/XPCOM/XPCOM_changes_in_Gecko_1.9.3
// http://mxr.mozilla.org/mozilla-central/source/toolkit/components/console/hudservice/HUDService.jsm#3240
// https://developer.mozilla.org/en/how_to_build_an_xpcom_component_in_javascript

function DOMFuzzHelperObserver() {
  this._isFrameScriptLoaded = false;
}

// Use runSoon to avoid false-positive leaks due to content JS on the stack (?)
function quitFromContent() { dumpln("Page called quitApplication."); runSoon(goQuitApplication); }
function quitApplicationSoon() { dumpln("Page called quitApplicationSoon."); runOnTimer(goQuitApplication); }

DOMFuzzHelperObserver.prototype = {
  classDescription: "DOM fuzz helper observer",
  classID:          Components.ID("{73DD0F4A-B201-44A1-8C56-D1D72432B02A}"),
  contractID:       "@squarefree.com/dom-fuzz-helper-observer;1",
  _xpcom_categories: [{category: "profile-after-change", service: true }],

  //QueryInterface:   XPCOMUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer]),
  QueryInterface:   XPCOMUtils.generateQI([Ci.nsIObserver]),

  observe: function(aSubject, aTopic, aData) {
    if (aTopic == "profile-after-change") {
      this.init();
    } else if (!this.isFrameScriptLoaded && aTopic == "chrome-document-global-created") {

      var messageManager = Cc["@mozilla.org/globalmessagemanager;1"].
                               getService(Ci.nsIChromeFrameMessageManager);

      // Register for any messages our API needs us to handle
      messageManager.addMessageListener("DOMFuzzHelper.quitApplication", this);
      messageManager.addMessageListener("DOMFuzzHelper.quitApplicationSoon", this);
      messageManager.addMessageListener("DOMFuzzHelper.quitWithLeakCheck", this);
      messageManager.addMessageListener("DOMFuzzHelper.setGCZeal", this);
      messageManager.addMessageListener("DOMFuzzHelper.getProfileDirectory", this);
      messageManager.addMessageListener("DOMFuzzHelper.getBinDirectory", this);
      messageManager.addMessageListener("DOMFuzzHelper.openAboutMemory", this);
      messageManager.loadFrameScript(CHILD_SCRIPT, true);

      this.isFrameScriptLoaded = true;

    } else if (aTopic == "xpcom-shutdown") {
        this.uninit();
    }
  },

  init: function() {
    var obs = Services.obs;

    obs.addObserver(this, "xpcom-shutdown", false);
    obs.addObserver(this, "chrome-document-global-created", false);
  },

  uninit: function() {
    var obs = Services.obs;

    obs.removeObserver(this, "chrome-document-global-created", false);
  },

  /**
    * messageManager callback function
    * This will get requests from our API in the window and process them in chrome for it
    **/

  receiveMessage: function(aMessage) {
    switch(aMessage.name) {
      case "DOMFuzzHelper.quitApplication":
        quitFromContent();
        break;

      case "DOMFuzzHelper.quitApplicationSoon":
        quitApplicationSoon();
        break;

      case "DOMFuzzHelper.quitWithLeakCheck":
        quitWithLeakCheck();
        break;

      case "DOMFuzzHelper.setGCZeal":
        setGCZeal(aMessage.json.zeal);
        break;

      case "DOMFuzzHelper.getProfileDirectory":
        return getProfileDirectory();

      case "DOMFuzzHelper.getBinDirectory":
        return getBinDirectory();

      case "DOMFuzzHelper.openAboutMemory":
        openAboutMemory();
        break;

      default:
        dumpln("Unrecognized message sent to domfuzzhelperobserver.js");

    }
  }
};

const NSGetFactory = XPCOMUtils.generateNSGetFactory([DOMFuzzHelperObserver]);

/********************************************
 * MISC PRIVILEGED FUNCTIONS - MAIN PROCESS *
 ********************************************/

function runSoon(f)
{
  var tm = Components.classes["@mozilla.org/thread-manager;1"]
             .getService(Components.interfaces.nsIThreadManager);

  tm.mainThread.dispatch({
    run: function() {
      f();
    }
  }, Components.interfaces.nsIThread.DISPATCH_NORMAL);
}

function setGCZeal(zeal)
{
  if (typeof(zeal) == "number") {
    Services.prefs.setIntPref("javascript.options.gczeal", zeal)
  }
}

function getProfileDirectory()
{
  var d = Components.classes["@mozilla.org/file/directory_service;1"]
                    .getService(Components.interfaces.nsIProperties)
                    .get("ProfD", Components.interfaces.nsIFile);
  return d.path;
}

function getBinDirectory()
{
  var d = Components.classes["@mozilla.org/file/directory_service;1"]
                    .getService(Components.interfaces.nsIProperties)
                    .get("CurProcD", Components.interfaces.nsIFile);
  return d.path;
}

function openAboutMemory()
{
  var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
                     .getService(Ci.nsIWindowWatcher);

  ww.openWindow(null, "about:memory", null, "width=200,height=200", null);
}

/************************
 * QUIT WITH LEAK CHECK *
 ************************/

var quitting = false;

function quitWithLeakCheck(leaveWindowsOpen)
{
  leaveWindowsOpen = !!leaveWindowsOpen;

  // Magic string that rundomfuzz.py looks for
  var messagePrefix = "Leaked until " + (leaveWindowsOpen ? "tab close" : "shutdown");

  if (quitting)
    return;
  quitting = true;

  runSoon(a);
  function a() { dumpln("QA"); if (!leaveWindowsOpen) closeAllWindows(); runOnTimer(b); dumpln("QAA"); }
  function b() { dumpln("QB"); mpUntilDone(c); }
  function c() { dumpln("QC"); bloatStats(d); }
  function d(objectCounts) {
    dumpln("QD");

    // Mac normally has extra documents (due to the hidden window?)
    var isMac = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS == "Darwin";

    var expected = {
      'nsGlobalWindow':          4 + 6*leaveWindowsOpen,
      'nsDocument':              4 + 4*isMac + 24*leaveWindowsOpen,
      'nsDocShell':              5,
      'BackstagePass':           1,
      'nsGenericElement':        1927,
      'nsHTMLDivElement':        4,
      'xpc::CompartmentPrivate': 3,
    }

    for (var p in expected) {
      if (objectCounts[p] > expected[p]) {
        dumpln(messagePrefix + ": " + p + "(" + objectCounts[p] + " > " + expected[p] + ")");
      } else if (objectCounts[p] < expected[p]) {
        dumpln("That's odd"  + ": " + p + "(" + objectCounts[p] + " < " + expected[p] + ")");
      }
    }

    runSoon(e);
  }
  function e() { dumpln("QE"); goQuitApplication(); }
}

var timerDeathGrip;
function runOnTimer(f)
{
    timerDeathGrip = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer);
    timerDeathGrip.initWithCallback({notify: function(){ timerDeathGrip=null; f(); }}, 2000, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
}

function closeAllWindows()
{
  var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
                     .getService(Ci.nsIWindowWatcher);
  var enumerator = ww.getWindowEnumerator();

  var windowsToClose = [];

  while (enumerator.hasMoreElements()) {
    windowsToClose.push(enumerator.getNext().QueryInterface(Ci.nsIDOMWindow));
  }

  // if not mac...
  ww.openWindow(null, "about:blank", null, "width=200,height=200", null);

  for (var i = 0; i < windowsToClose.length; ++i) {
    windowsToClose[i].close();
  }

  dumpln("1");
}

function mpUntilDone(callback)
{
  function mpUntilDoneInner()
  {
    dumpln("MP " + j);
    sendMemoryPressureNotification();

    ++j;
    if (j > 9)
      runSoon(callback);
    else if (j % 2 == 1 && typeof Components.utils.schedulePreciseGC == "function")
      Components.utils.schedulePreciseGC(mpUntilDoneInner)
    else
      runSoon(mpUntilDoneInner);
  }

  var j = 0;
  mpUntilDoneInner();
}

/*
     |<----------------Class--------------->|<-----Bytes------>|<----------------Objects---------------->|<--------------References-------------->|
                                              Per-Inst   Leaked    Total      Rem      Mean       StdDev     Total      Rem      Mean       StdDev

*/
// Grab the class name and the number of remaining objects.
var bloatRex = /\s*\d+\s+(\S+)\s+\d+\s+\d+\s+\d+\s+(\d+)\s+.*/;
const SET_QUOTA = false;
const USE_QUOTA = false;

function bloatStats(callback)
{
  var objectCounts = {};

  try {
    //d.d.d;
    NetUtil.asyncFetch("about:bloat", fetched);
  } catch(e) {
    dumpln("Can't open about:bloat -- maybe you forgot to use XPCOM_MEM_LEAK_LOG");
    callback(objectCounts);
  }

  function fetched(aInputStream, aResult)
  {
    var r = NetUtil.readInputStreamToString(aInputStream, aInputStream.available());
    var lines = r.split("\n");
    for (var i = 0; i < lines.length; ++i)
    {
      var a = bloatRex.exec(lines[i]);
      if (a) {
        if (SET_QUOTA) {
          dumpln("'" + a[1] + "': " + a[2] + ",");
        } else if (USE_QUOTA) {
          var quotaA = QUOTA[a[1]] || 0;
          if (a[2] > quotaA) { dumpln("Leak? Too many " + a[1] + " (" + a[2] + " > " + quotaA + ")"); }
        }
        objectCounts[a[1]] = a[2];
      }
    }
    runSoon(callCallback);
  }

  function callCallback()
  {
    callback(objectCounts)
  }
}

/********
 * QUIT *
 ********/

// From quit.js, which Bob Clary extracted from mozilla/toolkit/content

function canQuitApplication()
{
  var os = Components.classes["@mozilla.org/observer-service;1"]
    .getService(Components.interfaces.nsIObserverService);
  if (!os)
  {
    return true;
  }

  try
 {
    var cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"]
      .createInstance(Components.interfaces.nsISupportsPRBool);
    os.notifyObservers(cancelQuit, "quit-application-requested", null);

    // Something aborted the quit process.
    if (cancelQuit.data)
    {
      return false;
    }
  }
  catch (ex)
  {
  }
  os.notifyObservers(null, "quit-application-granted", null);
  return true;
}

function goQuitApplication()
{
  dumpln("goQuitApplication (domfuzzhelperobserver.js component)");

  if (!canQuitApplication())
  {
    return false;
  }

  var kAppStartup = '@mozilla.org/toolkit/app-startup;1';
  var kAppShell   = '@mozilla.org/appshell/appShellService;1';
  var   appService;
  var   forceQuit;

  if (kAppStartup in Components.classes)
  {
    appService = Components.classes[kAppStartup].
      getService(Components.interfaces.nsIAppStartup);
    forceQuit  = Components.interfaces.nsIAppStartup.eForceQuit;

  }
  else if (kAppShell in Components.classes)
  {
    appService = Components.classes[kAppShell].
      getService(Components.interfaces.nsIAppShellService);
    forceQuit = Components.interfaces.nsIAppShellService.eForceQuit;
  }
  else
  {
    throw 'goQuitApplication: no AppStartup/appShell';
  }

  var windowManager = Components.
    classes['@mozilla.org/appshell/window-mediator;1'].getService();

  var windowManagerInterface = windowManager.
    QueryInterface(Components.interfaces.nsIWindowMediator);

  var enumerator = windowManagerInterface.getEnumerator(null);

  while (enumerator.hasMoreElements())
  {
    var domWindow = enumerator.getNext();
    if (("tryToClose" in domWindow) && !domWindow.tryToClose())
    {
      return false;
    }
    domWindow.close();
  }

  try
  {
    appService.quit(forceQuit);
  }
  catch(ex)
  {
    throw('goQuitApplication: ' + ex);
  }

  return true;
}

<commit_msg>Avoid quitting multiple times -- it confuses the download manager.<commit_after>"use strict";

const Cu = Components.utils;
const Cc = Components.classes;
const Ci = Components.interfaces;

Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");

function dumpln(s) { dump(s + "\n"); }

const CHILD_SCRIPT = "chrome://domfuzzhelper/content/domfuzzhelper.js";

/*****************
 * API INJECTION *
 *****************/

// Based on:
// https://bug549539.bugzilla.mozilla.org/attachment.cgi?id=429661
// https://developer.mozilla.org/en/XPCOM/XPCOM_changes_in_Gecko_1.9.3
// http://mxr.mozilla.org/mozilla-central/source/toolkit/components/console/hudservice/HUDService.jsm#3240
// https://developer.mozilla.org/en/how_to_build_an_xpcom_component_in_javascript

function DOMFuzzHelperObserver() {
  this._isFrameScriptLoaded = false;
}

// Use runSoon to avoid false-positive leaks due to content JS on the stack (?)
function quitFromContent() { dumpln("Page called quitApplication."); runSoon(quitOnce); }
function quitApplicationSoon() { dumpln("Page called quitApplicationSoon."); runOnTimer(quitOnce); }

DOMFuzzHelperObserver.prototype = {
  classDescription: "DOM fuzz helper observer",
  classID:          Components.ID("{73DD0F4A-B201-44A1-8C56-D1D72432B02A}"),
  contractID:       "@squarefree.com/dom-fuzz-helper-observer;1",
  _xpcom_categories: [{category: "profile-after-change", service: true }],

  //QueryInterface:   XPCOMUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer]),
  QueryInterface:   XPCOMUtils.generateQI([Ci.nsIObserver]),

  observe: function(aSubject, aTopic, aData) {
    if (aTopic == "profile-after-change") {
      this.init();
    } else if (!this.isFrameScriptLoaded && aTopic == "chrome-document-global-created") {

      var messageManager = Cc["@mozilla.org/globalmessagemanager;1"].
                               getService(Ci.nsIChromeFrameMessageManager);

      // Register for any messages our API needs us to handle
      messageManager.addMessageListener("DOMFuzzHelper.quitApplication", this);
      messageManager.addMessageListener("DOMFuzzHelper.quitApplicationSoon", this);
      messageManager.addMessageListener("DOMFuzzHelper.quitWithLeakCheck", this);
      messageManager.addMessageListener("DOMFuzzHelper.setGCZeal", this);
      messageManager.addMessageListener("DOMFuzzHelper.getProfileDirectory", this);
      messageManager.addMessageListener("DOMFuzzHelper.getBinDirectory", this);
      messageManager.addMessageListener("DOMFuzzHelper.openAboutMemory", this);
      messageManager.loadFrameScript(CHILD_SCRIPT, true);

      this.isFrameScriptLoaded = true;

    } else if (aTopic == "xpcom-shutdown") {
        this.uninit();
    }
  },

  init: function() {
    var obs = Services.obs;

    obs.addObserver(this, "xpcom-shutdown", false);
    obs.addObserver(this, "chrome-document-global-created", false);
  },

  uninit: function() {
    var obs = Services.obs;

    obs.removeObserver(this, "chrome-document-global-created", false);
  },

  /**
    * messageManager callback function
    * This will get requests from our API in the window and process them in chrome for it
    **/

  receiveMessage: function(aMessage) {
    switch(aMessage.name) {
      case "DOMFuzzHelper.quitApplication":
        quitFromContent();
        break;

      case "DOMFuzzHelper.quitApplicationSoon":
        quitApplicationSoon();
        break;

      case "DOMFuzzHelper.quitWithLeakCheck":
        quitWithLeakCheck();
        break;

      case "DOMFuzzHelper.setGCZeal":
        setGCZeal(aMessage.json.zeal);
        break;

      case "DOMFuzzHelper.getProfileDirectory":
        return getProfileDirectory();

      case "DOMFuzzHelper.getBinDirectory":
        return getBinDirectory();

      case "DOMFuzzHelper.openAboutMemory":
        openAboutMemory();
        break;

      default:
        dumpln("Unrecognized message sent to domfuzzhelperobserver.js");

    }
  }
};

const NSGetFactory = XPCOMUtils.generateNSGetFactory([DOMFuzzHelperObserver]);

/********************************************
 * MISC PRIVILEGED FUNCTIONS - MAIN PROCESS *
 ********************************************/

function runSoon(f)
{
  var tm = Components.classes["@mozilla.org/thread-manager;1"]
             .getService(Components.interfaces.nsIThreadManager);

  tm.mainThread.dispatch({
    run: function() {
      f();
    }
  }, Components.interfaces.nsIThread.DISPATCH_NORMAL);
}

function setGCZeal(zeal)
{
  if (typeof(zeal) == "number") {
    Services.prefs.setIntPref("javascript.options.gczeal", zeal)
  }
}

function getProfileDirectory()
{
  var d = Components.classes["@mozilla.org/file/directory_service;1"]
                    .getService(Components.interfaces.nsIProperties)
                    .get("ProfD", Components.interfaces.nsIFile);
  return d.path;
}

function getBinDirectory()
{
  var d = Components.classes["@mozilla.org/file/directory_service;1"]
                    .getService(Components.interfaces.nsIProperties)
                    .get("CurProcD", Components.interfaces.nsIFile);
  return d.path;
}

function openAboutMemory()
{
  var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
                     .getService(Ci.nsIWindowWatcher);

  ww.openWindow(null, "about:memory", null, "width=200,height=200", null);
}

/************************
 * QUIT WITH LEAK CHECK *
 ************************/

var quitting = false;

function quitWithLeakCheck(leaveWindowsOpen)
{
  leaveWindowsOpen = !!leaveWindowsOpen;

  // Magic string that rundomfuzz.py looks for
  var messagePrefix = "Leaked until " + (leaveWindowsOpen ? "tab close" : "shutdown");

  if (quitting)
    return;
  quitting = true;

  runSoon(a);
  function a() { dumpln("QA"); if (!leaveWindowsOpen) closeAllWindows(); runOnTimer(b); dumpln("QAA"); }
  function b() { dumpln("QB"); mpUntilDone(c); }
  function c() { dumpln("QC"); bloatStats(d); }
  function d(objectCounts) {
    dumpln("QD");

    // Mac normally has extra documents (due to the hidden window?)
    var isMac = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS == "Darwin";

    var expected = {
      'nsGlobalWindow':          4 + 6*leaveWindowsOpen,
      'nsDocument':              4 + 4*isMac + 24*leaveWindowsOpen,
      'nsDocShell':              5,
      'BackstagePass':           1,
      'nsGenericElement':        1927,
      'nsHTMLDivElement':        4,
      'xpc::CompartmentPrivate': 3,
    }

    for (var p in expected) {
      if (objectCounts[p] > expected[p]) {
        dumpln(messagePrefix + ": " + p + "(" + objectCounts[p] + " > " + expected[p] + ")");
      } else if (objectCounts[p] < expected[p]) {
        dumpln("That's odd"  + ": " + p + "(" + objectCounts[p] + " < " + expected[p] + ")");
      }
    }

    runSoon(e);
  }
  function e() { dumpln("QE"); quitOnce(); }
}

var timerDeathGrip;
function runOnTimer(f)
{
    timerDeathGrip = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer);
    timerDeathGrip.initWithCallback({notify: function(){ timerDeathGrip=null; f(); }}, 2000, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
}

function closeAllWindows()
{
  var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
                     .getService(Ci.nsIWindowWatcher);
  var enumerator = ww.getWindowEnumerator();

  var windowsToClose = [];

  while (enumerator.hasMoreElements()) {
    windowsToClose.push(enumerator.getNext().QueryInterface(Ci.nsIDOMWindow));
  }

  // if not mac...
  ww.openWindow(null, "about:blank", null, "width=200,height=200", null);

  for (var i = 0; i < windowsToClose.length; ++i) {
    windowsToClose[i].close();
  }

  dumpln("1");
}

function mpUntilDone(callback)
{
  function mpUntilDoneInner()
  {
    dumpln("MP " + j);
    sendMemoryPressureNotification();

    ++j;
    if (j > 9)
      runSoon(callback);
    else if (j % 2 == 1 && typeof Components.utils.schedulePreciseGC == "function")
      Components.utils.schedulePreciseGC(mpUntilDoneInner)
    else
      runSoon(mpUntilDoneInner);
  }

  var j = 0;
  mpUntilDoneInner();
}

/*
     |<----------------Class--------------->|<-----Bytes------>|<----------------Objects---------------->|<--------------References-------------->|
                                              Per-Inst   Leaked    Total      Rem      Mean       StdDev     Total      Rem      Mean       StdDev

*/
// Grab the class name and the number of remaining objects.
var bloatRex = /\s*\d+\s+(\S+)\s+\d+\s+\d+\s+\d+\s+(\d+)\s+.*/;
const SET_QUOTA = false;
const USE_QUOTA = false;

function bloatStats(callback)
{
  var objectCounts = {};

  try {
    //d.d.d;
    NetUtil.asyncFetch("about:bloat", fetched);
  } catch(e) {
    dumpln("Can't open about:bloat -- maybe you forgot to use XPCOM_MEM_LEAK_LOG");
    callback(objectCounts);
  }

  function fetched(aInputStream, aResult)
  {
    var r = NetUtil.readInputStreamToString(aInputStream, aInputStream.available());
    var lines = r.split("\n");
    for (var i = 0; i < lines.length; ++i)
    {
      var a = bloatRex.exec(lines[i]);
      if (a) {
        if (SET_QUOTA) {
          dumpln("'" + a[1] + "': " + a[2] + ",");
        } else if (USE_QUOTA) {
          var quotaA = QUOTA[a[1]] || 0;
          if (a[2] > quotaA) { dumpln("Leak? Too many " + a[1] + " (" + a[2] + " > " + quotaA + ")"); }
        }
        objectCounts[a[1]] = a[2];
      }
    }
    runSoon(callCallback);
  }

  function callCallback()
  {
    callback(objectCounts)
  }
}

/********
 * QUIT *
 ********/

// From quit.js, which Bob Clary extracted from mozilla/toolkit/content

function canQuitApplication()
{
  var os = Components.classes["@mozilla.org/observer-service;1"]
    .getService(Components.interfaces.nsIObserverService);
  if (!os)
  {
    return true;
  }

  try
 {
    var cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"]
      .createInstance(Components.interfaces.nsISupportsPRBool);
    os.notifyObservers(cancelQuit, "quit-application-requested", null);

    // Something aborted the quit process.
    if (cancelQuit.data)
    {
      return false;
    }
  }
  catch (ex)
  {
  }
  os.notifyObservers(null, "quit-application-granted", null);
  return true;
}

function goQuitApplication()
{
  dumpln("goQuitApplication (domfuzzhelperobserver.js component)");

  if (!canQuitApplication())
  {
    return false;
  }

  var kAppStartup = '@mozilla.org/toolkit/app-startup;1';
  var kAppShell   = '@mozilla.org/appshell/appShellService;1';
  var   appService;
  var   forceQuit;

  if (kAppStartup in Components.classes)
  {
    appService = Components.classes[kAppStartup].
      getService(Components.interfaces.nsIAppStartup);
    forceQuit  = Components.interfaces.nsIAppStartup.eForceQuit;

  }
  else if (kAppShell in Components.classes)
  {
    appService = Components.classes[kAppShell].
      getService(Components.interfaces.nsIAppShellService);
    forceQuit = Components.interfaces.nsIAppShellService.eForceQuit;
  }
  else
  {
    throw 'goQuitApplication: no AppStartup/appShell';
  }

  var windowManager = Components.
    classes['@mozilla.org/appshell/window-mediator;1'].getService();

  var windowManagerInterface = windowManager.
    QueryInterface(Components.interfaces.nsIWindowMediator);

  var enumerator = windowManagerInterface.getEnumerator(null);

  while (enumerator.hasMoreElements())
  {
    var domWindow = enumerator.getNext();
    if (("tryToClose" in domWindow) && !domWindow.tryToClose())
    {
      return false;
    }
    domWindow.close();
  }

  try
  {
    appService.quit(forceQuit);
  }
  catch(ex)
  {
    throw('goQuitApplication: ' + ex);
  }

  return true;
}

var alreadyQuitting = false;
function quitOnce()
{
  if (!alreadyQuitting) {
    alreadyQuitting = true;
    goQuitApplication();
  }
}
<|endoftext|>


<commit_before>const DrawCard = require('../../drawcard.js');

class BrokenVows extends DrawCard {
    setupCardAbilities() {
        this.action({
            title: 'Exchange characters',
            phase: 'marshal',
            chooseOpponent: () => true,
            target: {
                type: 'select',
                cardCondition: card => card.location === 'play area' && card.getType() === 'character' && card.controller === this.controller && card.isFaction('thenightswatch')
            },
            handler: context => {
                this.game.takeControl(context.opponent, context.target);
                this.game.promptForSelect(this.controller, {
                    cardCondition: card => (
                        card.location === 'play area' &&
                        card.getType() === 'character' &&
                        card.controller === context.opponent &&
                        card.getPrintedCost() < context.target.getPrintedCost() &&
                        this.controller.canControl(card)
                    ),
                    onSelect: (player, card) => this.resolveAbility(context, card),
                    onCancel: () => this.resolveAbility(context, null),
                    source: this
                });
            }
        });
    }

    resolveAbility(context, opponentCard) {
        if(opponentCard) {
            this.game.addMessage('{0} uses {1} to give control of {2} to {3} and take control of {4}', this.controller, this, context.target, context.opponent, opponentCard);
            this.game.takeControl(this.controller, opponentCard);
        } else {
            this.game.addMessage('{0} uses {1} to give control of {2} to {3}', this.controller, this, context.target, context.opponent);
        }

        return true;
    }
}

BrokenVows.code = '09034';

module.exports = BrokenVows;
<commit_msg>Broken Vows: do not allow to take control of loyal characters (#1634)<commit_after>const DrawCard = require('../../drawcard.js');

class BrokenVows extends DrawCard {
    setupCardAbilities() {
        this.action({
            title: 'Exchange characters',
            phase: 'marshal',
            chooseOpponent: () => true,
            target: {
                type: 'select',
                cardCondition: card => card.location === 'play area' && card.getType() === 'character' && card.controller === this.controller && card.isFaction('thenightswatch')
            },
            handler: context => {
                this.game.takeControl(context.opponent, context.target);
                this.game.promptForSelect(this.controller, {
                    cardCondition: card => (
                        card.location === 'play area' &&
                        card.getType() === 'character' &&
                        card.controller === context.opponent &&
                        card.getPrintedCost() < context.target.getPrintedCost() &&
                        !card.isLoyal() &&
                        this.controller.canControl(card)
                    ),
                    onSelect: (player, card) => this.resolveAbility(context, card),
                    onCancel: () => this.resolveAbility(context, null),
                    source: this
                });
            }
        });
    }

    resolveAbility(context, opponentCard) {
        if(opponentCard) {
            this.game.addMessage('{0} uses {1} to give control of {2} to {3} and take control of {4}', this.controller, this, context.target, context.opponent, opponentCard);
            this.game.takeControl(this.controller, opponentCard);
        } else {
            this.game.addMessage('{0} uses {1} to give control of {2} to {3}', this.controller, this, context.target, context.opponent);
        }

        return true;
    }
}

BrokenVows.code = '09034';

module.exports = BrokenVows;
<|endoftext|>


<commit_before>let session = require('../../database/models/session');

let isAuthorizedForRoute = (req, res, next) => {
  session.getSession(req.cookie.id)
  .then((session) => {
    console.log(session);
  });

};

module.exports = isAuthorizedForRoute;
<commit_msg>Add authorized route checking middleware<commit_after>let session = require('../../database/models/session');

let isAuthorizedForRoute = (req, res, next) => {
  session.getSession(req.cookies.session.id)
  .then((session) => {
    if (session.hash === req.cookies.session.hash &&
        session.houseId === req.cookies.session.houseId &&
        session.userId === req.cookies.session.userId) {
      next();
    } else {
      res.clearCookie('session');
      res.redirect(401, '/login');
    }
  });

};

module.exports = isAuthorizedForRoute;
<|endoftext|>


<commit_before>var url = require('url');
var xtend = require('xtend');

const IdGenerator = require('auth0-id-generator');
const generator = new IdGenerator({len: 20, alphabet:"abcdef0123456789"});

exports.generateUniqueID = function(){
  return generator.get();
};

exports.getRoundTripDateFormat = function() {
  //http://msdn.microsoft.com/en-us/library/az4se3k1.aspx#Roundtrip
  var date = new Date();
  return date.getUTCFullYear() + '-' +
        ('0' + (date.getUTCMonth()+1)).slice(-2) + '-' +
        ('0' + date.getUTCDate()).slice(-2) + 'T' +
        ('0' + date.getUTCHours()).slice(-2) + ":" +
        ('0' + date.getUTCMinutes()).slice(-2) + ":" +
        ('0' + date.getUTCSeconds()).slice(-2) + "Z";
};

exports.appendQueryString = function(initialUrl, query) {
  var parsed = url.parse(initialUrl, true);
  parsed.query = xtend(parsed.query, query);
  delete parsed.search;
  return url.format(parsed);
};
<commit_msg>chore: generateUniqueId docs and small reformat/refactor of vars<commit_after>const url = require('url');
const xtend = require('xtend');

const IdGenerator = require('auth0-id-generator');
const generator = new IdGenerator({len: 20, alphabet: "abcdef0123456789"});

/**
 * Generates a random alphanumeric ID.
 * > IDs are 20 characters long and use the hexadecimal alphabet
 * @returns {string}
 */
exports.generateUniqueID = function () {
  return generator.get();
};

exports.getRoundTripDateFormat = function() {
  //http://msdn.microsoft.com/en-us/library/az4se3k1.aspx#Roundtrip
  const date = new Date();
  return date.getUTCFullYear() + '-' +
        ('0' + (date.getUTCMonth()+1)).slice(-2) + '-' +
        ('0' + date.getUTCDate()).slice(-2) + 'T' +
        ('0' + date.getUTCHours()).slice(-2) + ":" +
        ('0' + date.getUTCMinutes()).slice(-2) + ":" +
        ('0' + date.getUTCSeconds()).slice(-2) + "Z";
};

exports.appendQueryString = function(initialUrl, query) {
  const parsed = url.parse(initialUrl, true);
  parsed.query = xtend(parsed.query, query);
  delete parsed.search;
  return url.format(parsed);
};
<|endoftext|>


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

var mongodb = require('mongodb')
  , ReadPref = mongodb.ReadPreference
  , ObjectId = mongodb.ObjectID

/**
 * Object clone with Mongoose natives support.
 *
 * Creates a minimal data Object.
 * It does not clone empty Arrays, empty Objects, and undefined values.
 * This makes the data payload sent to MongoDB as minimal as possible.
 *
 * @param {Object} obj the object to clone
 * @param {Object} options
 * @return {Object} the cloned object
 * @api private
 */

exports.clone = function clone (obj, options) {
  if (obj === undefined || obj === null)
    return obj;

  if (Array.isArray(obj))
    return exports.cloneArray(obj, options);

  if ('Object' === obj.constructor.name)
    return exports.cloneObject(obj, options);

  if ('Date' === obj.constructor.name || 'Function' === obj.constructor.name)
    return new obj.constructor(+obj);

  if ('RegExp' === obj.constructor.name)
    return new RegExp(obj.source);

  if (obj instanceof ObjectId)
    return new ObjectId(obj.id);

  if (obj.valueOf)
    return obj.valueOf();
};
var clone = exports.clone;

/*!
 * ignore
 */

var cloneObject = exports.cloneObject = function cloneObject (obj, options) {
  var retainKeyOrder = options && options.retainKeyOrder
    , minimize = options && options.minimize
    , ret = {}
    , hasKeys
    , keys
    , val
    , k
    , i

  if (retainKeyOrder) {
    for (k in obj) {
      val = clone(obj[k], options);

      if (!minimize || ('undefined' !== typeof val)) {
        hasKeys || (hasKeys = true);
        ret[k] = val;
      }
    }
  } else {
    // faster

    keys = Object.keys(obj);
    i = keys.length;

    while (i--) {
      k = keys[i];
      val = clone(obj[k], options);

      if (!minimize || ('undefined' !== typeof val)) {
        if (!hasKeys) hasKeys = true;
        ret[k] = val;
      }
    }
  }

  return minimize
    ? hasKeys && ret
    : ret;
};

var cloneArray = exports.cloneArray = function cloneArray (arr, options) {
  var ret = [];
  for (var i = 0, l = arr.length; i < l; i++)
    ret.push(clone(arr[i], options));
  return ret;
};

var later = 'function' == typeof setImmediate
  ? setImmediate
  : process.nextTick;

/**
 * process.nextTick helper.
 *
 * Wraps the given `callback` in a try/catch. If an error is
 * caught it will be thrown on nextTick.
 *
 * node-mongodb-native had a habit of state corruption when
 * an error was immediately thrown from within a collection
 * method (find, update, etc) callback.
 *
 * @param {Function} [callback]
 * @api private
 */

var tick = exports.tick = function tick (callback) {
  if ('function' !== typeof callback) return;
  return function () {
    // callbacks should always be fired on the next
    // turn of the event loop. A side benefit is
    // errors thrown from executing the callback
    // will not cause drivers state to be corrupted
    // which has historically been a problem.
    var args = arguments;
    later(function(){
      callback.apply(this, args);
    });
  }
}

/**
 * Merges `from` into `to` without overwriting existing properties.
 *
 * @param {Object} to
 * @param {Object} from
 * @api private
 */

var merge = exports.merge = function merge (to, from) {
  var keys = Object.keys(from)
    , i = keys.length
    , key

  while (i--) {
    key = keys[i];
    if ('undefined' === typeof to[key]) {
      to[key] = from[key];
    } else {
      if (exports.isObject(from[key])) {
        merge(to[key], from[key]);
      } else {
        to[key] = from[key];
      }
    }
  }
}

/**
 * Read pref helper (mongo 2.2 drivers support this)
 *
 * Allows using aliases instead of full preference names:
 *
 *     p   primary
 *     pp  primaryPreferred
 *     s   secondary
 *     sp  secondaryPreferred
 *     n   nearest
 *
 * @param {String|Array} pref
 * @param {Array} [tags]
 */

exports.readPref = function readPref (pref, tags) {
  if (Array.isArray(pref)) {
    tags = pref[1];
    pref = pref[0];
  }

  switch (pref) {
    case 'p':
      pref = 'primary';
      break;
    case 'pp':
      pref = 'primaryPrefered';
      break;
    case 's':
      pref = 'secondary';
      break;
    case 'sp':
      pref = 'secondaryPrefered';
      break;
    case 'n':
      pref = 'nearest';
      break;
  }

  return new ReadPref(pref, tags);
}

/**
 * Object.prototype.toString.call helper
 */

var toString = Object.prototype.toString;
exports.toString = function (arg) {
  return toString.call(arg);
}

/**
 * Determines if `arg` is an object.
 *
 * @param {Object|Array|String|Function|RegExp|any} arg
 * @return {Boolean}
 */

exports.isObject = function (arg) {
  return '[object Object]' == exports.toString(arg);
}

/**
 * Determines if `arg` is an array.
 *
 * @param {Object}
 * @return {Boolean}
 * @see nodejs utils
 */

exports.isArray = function (arg) {
  return Array.isArray(arg) ||
    'object' == typeof arg && '[object Array]' == exports.toString(arg);
}

/**
 * Object.keys helper
 */

exports.keys = Object.keys || function (obj) {
  var keys = [];
  for (var k in obj) if (obj.hasOwnProperty(k)) {
    keys.push(k);
  }
  return keys;
}
<commit_msg>Object.create polyfill<commit_after>
/*!
 * Module dependencies.
 */

var mongodb = require('mongodb')
  , ReadPref = mongodb.ReadPreference
  , ObjectId = mongodb.ObjectID

/**
 * Object clone with Mongoose natives support.
 *
 * Creates a minimal data Object.
 * It does not clone empty Arrays, empty Objects, and undefined values.
 * This makes the data payload sent to MongoDB as minimal as possible.
 *
 * @param {Object} obj the object to clone
 * @param {Object} options
 * @return {Object} the cloned object
 * @api private
 */

exports.clone = function clone (obj, options) {
  if (obj === undefined || obj === null)
    return obj;

  if (Array.isArray(obj))
    return exports.cloneArray(obj, options);

  if ('Object' === obj.constructor.name)
    return exports.cloneObject(obj, options);

  if ('Date' === obj.constructor.name || 'Function' === obj.constructor.name)
    return new obj.constructor(+obj);

  if ('RegExp' === obj.constructor.name)
    return new RegExp(obj.source);

  if (obj instanceof ObjectId)
    return new ObjectId(obj.id);

  if (obj.valueOf)
    return obj.valueOf();
};
var clone = exports.clone;

/*!
 * ignore
 */

var cloneObject = exports.cloneObject = function cloneObject (obj, options) {
  var retainKeyOrder = options && options.retainKeyOrder
    , minimize = options && options.minimize
    , ret = {}
    , hasKeys
    , keys
    , val
    , k
    , i

  if (retainKeyOrder) {
    for (k in obj) {
      val = clone(obj[k], options);

      if (!minimize || ('undefined' !== typeof val)) {
        hasKeys || (hasKeys = true);
        ret[k] = val;
      }
    }
  } else {
    // faster

    keys = Object.keys(obj);
    i = keys.length;

    while (i--) {
      k = keys[i];
      val = clone(obj[k], options);

      if (!minimize || ('undefined' !== typeof val)) {
        if (!hasKeys) hasKeys = true;
        ret[k] = val;
      }
    }
  }

  return minimize
    ? hasKeys && ret
    : ret;
};

var cloneArray = exports.cloneArray = function cloneArray (arr, options) {
  var ret = [];
  for (var i = 0, l = arr.length; i < l; i++)
    ret.push(clone(arr[i], options));
  return ret;
};

var later = 'function' == typeof setImmediate
  ? setImmediate
  : process.nextTick;

/**
 * process.nextTick helper.
 *
 * Wraps the given `callback` in a try/catch. If an error is
 * caught it will be thrown on nextTick.
 *
 * node-mongodb-native had a habit of state corruption when
 * an error was immediately thrown from within a collection
 * method (find, update, etc) callback.
 *
 * @param {Function} [callback]
 * @api private
 */

var tick = exports.tick = function tick (callback) {
  if ('function' !== typeof callback) return;
  return function () {
    // callbacks should always be fired on the next
    // turn of the event loop. A side benefit is
    // errors thrown from executing the callback
    // will not cause drivers state to be corrupted
    // which has historically been a problem.
    var args = arguments;
    later(function(){
      callback.apply(this, args);
    });
  }
}

/**
 * Merges `from` into `to` without overwriting existing properties.
 *
 * @param {Object} to
 * @param {Object} from
 * @api private
 */

var merge = exports.merge = function merge (to, from) {
  var keys = Object.keys(from)
    , i = keys.length
    , key

  while (i--) {
    key = keys[i];
    if ('undefined' === typeof to[key]) {
      to[key] = from[key];
    } else {
      if (exports.isObject(from[key])) {
        merge(to[key], from[key]);
      } else {
        to[key] = from[key];
      }
    }
  }
}

/**
 * Read pref helper (mongo 2.2 drivers support this)
 *
 * Allows using aliases instead of full preference names:
 *
 *     p   primary
 *     pp  primaryPreferred
 *     s   secondary
 *     sp  secondaryPreferred
 *     n   nearest
 *
 * @param {String|Array} pref
 * @param {Array} [tags]
 */

exports.readPref = function readPref (pref, tags) {
  if (Array.isArray(pref)) {
    tags = pref[1];
    pref = pref[0];
  }

  switch (pref) {
    case 'p':
      pref = 'primary';
      break;
    case 'pp':
      pref = 'primaryPrefered';
      break;
    case 's':
      pref = 'secondary';
      break;
    case 'sp':
      pref = 'secondaryPrefered';
      break;
    case 'n':
      pref = 'nearest';
      break;
  }

  return new ReadPref(pref, tags);
}

/**
 * Object.prototype.toString.call helper
 */

var toString = Object.prototype.toString;
exports.toString = function (arg) {
  return toString.call(arg);
}

/**
 * Determines if `arg` is an object.
 *
 * @param {Object|Array|String|Function|RegExp|any} arg
 * @return {Boolean}
 */

exports.isObject = function (arg) {
  return '[object Object]' == exports.toString(arg);
}

/**
 * Determines if `arg` is an array.
 *
 * @param {Object}
 * @return {Boolean}
 * @see nodejs utils
 */

exports.isArray = function (arg) {
  return Array.isArray(arg) ||
    'object' == typeof arg && '[object Array]' == exports.toString(arg);
}

/**
 * Object.keys helper
 */

exports.keys = Object.keys || function (obj) {
  var keys = [];
  for (var k in obj) if (obj.hasOwnProperty(k)) {
    keys.push(k);
  }
  return keys;
}

/**
 * Basic Object.create polyfill
 */

exports.create = 'function' == typeof Object.create
  ? Object.create
  : create;

function create (proto) {
  if (arguments.length > 1) {
    throw new Error("Adding properties is not supported")
  }

  function F () {}
  F.prototype = proto;
  return new F;
}
<|endoftext|>


<commit_before>"use strict"

const mm = require('./metamaya');
const util = require("util");

exports.Exception = function Exception(message) {
	this.message = message;
}
exports.Exception.prototype = {
	toString() {
		return this.message;
	}
}

// Customized console logger.
// depth - optional display depth for object trees
exports.log = function(obj, depth) {
	console.log(util.inspect(obj, {
		depth: depth === undefined ? null : depth,
		colors: true
	}));
}

// shorthands for metamaya object construction

// Binds a function to its arguments.
exports.bind = function(head, args) {
	return new mm.Application(head, args);
}

// Encloses an expression into a static context.
exports.enclose = function(expr, sc) {
	return new mm.Closure(expr, sc);
}
<commit_msg>Add comment<commit_after>"use strict"

const mm = require('./metamaya');
const util = require("util");

exports.Exception = function Exception(message) {
	this.message = message;
}
exports.Exception.prototype = {
	toString() {
		return this.message;
	}
}

// Customized console logger.
// depth - optional display depth for object trees
exports.log = function(obj, depth) {
	console.log(util.inspect(obj, {
		depth: depth === undefined ? null : depth,
		colors: true
	}));
}

// shorthands for metamaya object construction

// Binds a function to its arguments.
// head - an applicable object
// args - array of arguments
exports.bind = function(head, args) {
	return new mm.Application(head, args);
}

// Encloses an expression into a static context.
exports.enclose = function(expr, sc) {
	return new mm.Closure(expr, sc);
}
<|endoftext|>


<commit_before>const fs = require('fs');
const path = require('path');
const getFunctionArguments = require('fn-args');
const { convertColorToRGBA, isColorProperty } = require('./colorUtils');

function isObject(item) {
  return item && typeof item === 'object' && !Array.isArray(item);
}

function flatTail(flatParams, params) {
  const res = [];
  for (const fp of flatParams) {
    for (const p of params) {
      res.push([p].concat(fp));
    }
  }
  return res;
}

module.exports.flatTail = flatTail;

function deepMerge(target, source) {
  if (isObject(target) && isObject(source)) {
    for (const key in source) {
      if (isObject(source[key])) {
        if (!target[key]) Object.assign(target, { [key]: {} });
        deepMerge(target[key], source[key]);
      } else {
        Object.assign(target, { [key]: source[key] });
      }
    }
  }
  return target;
}

module.exports.deepMerge = deepMerge;

const isGenerator = module.exports.isGenerator = function (fn) {
  return fn.constructor.name === 'GeneratorFunction';
};

const isAsyncFunction = module.exports.isAsyncFunction = function (fn) {
  return fn[Symbol.toStringTag] === 'AsyncFunction';
};

module.exports.fileExists = function (filePath) {
  try {
    fs.statSync(filePath);
  } catch (err) {
    if (err.code === 'ENOENT') return false;
  }
  return true;
};

module.exports.isFile = function (filePath) {
  let filestat;
  try {
    filestat = fs.statSync(filePath);
  } catch (err) {
    if (err.code === 'ENOENT') return false;
  }
  if (!filestat) return false;
  return filestat.isFile();
};

module.exports.getParamNames = function (fn) {
  if (fn.isSinonProxy) return [];
  return getFunctionArguments(fn);
};

module.exports.installedLocally = function () {
  return path.resolve(`${__dirname}/../`).indexOf(process.cwd()) === 0;
};

module.exports.methodsOfObject = function (obj, className) {
  const methods = [];

  const standard = [
    'constructor',
    'toString',
    'toLocaleString',
    'valueOf',
    'hasOwnProperty',
    'isPrototypeOf',
    'propertyIsEnumerable',
  ];

  function pushToMethods(prop) {
    if (typeof obj[prop] !== 'function') return;
    if (standard.indexOf(prop) >= 0) return;
    if (prop.indexOf('_') === 0) return;
    methods.push(prop);
  }

  while (obj.constructor.name !== className) {
    Object.getOwnPropertyNames(obj).forEach(pushToMethods);
    obj = Object.getPrototypeOf(obj);

    if (!obj || !obj.constructor) break;
  }
  return methods;
};

module.exports.template = function (template, data) {
  return template.replace(/{{([^{}]*)}}/g, (a, b) => {
    const r = data[b];
    if (r === undefined) return '';
    return r.toString();
  });
};

module.exports.ucfirst = function (str) {
  return str.charAt(0).toUpperCase() + str.substr(1);
};

module.exports.lcfirst = function (str) {
  return str.charAt(0).toLowerCase() + str.substr(1);
};

module.exports.chunkArray = function (arr, chunk) {
  let i;
  let j;
  const tmp = [];
  for (i = 0, j = arr.length; i < j; i += chunk) {
    tmp.push(arr.slice(i, i + chunk));
  }
  return tmp;
};

module.exports.clearString = function (str) {
  /* Replace forbidden symbols in string
   */
  return str
    .replace(/ /g, '_')
    .replace(/"/g, "'")
    .replace(/\//g, '_')
    .replace(/</g, '(')
    .replace(/>/g, ')')
    .replace(/:/g, '_')
    .replace(/\\/g, '_')
    .replace(/\|/g, '_')
    .replace(/\?/g, '.')
    .replace(/\*/g, '^')
    .replace(/'/g, '');
};

module.exports.decodeUrl = function (url) {
  /* Replace forbidden symbols in string
   */
  return decodeURIComponent(decodeURIComponent(decodeURIComponent(url)));
};

module.exports.xpathLocator = {
  literal: (string) => {
    if (string.indexOf("'") > -1) {
      string = string.split("'", -1).map(substr => `'${substr}'`).join(',"\'",');
      return `concat(${string})`;
    }
    return `'${string}'`;
  },
  combine: locators => locators.join(' | '),
};

module.exports.test = {

  grepLines(array, startString, endString) {
    let startIndex = 0;
    let endIndex;
    array.every((elem, index) => {
      if (elem === startString) {
        startIndex = index;
        return true;
      }
      if (elem === endString) {
        endIndex = index;
        return false;
      }
      return true;
    });
    return array.slice(startIndex + 1, endIndex);
  },

  submittedData(dataFile) {
    return function (key) {
      if (!fs.existsSync(dataFile)) {
        const waitTill = new Date(new Date().getTime() + 1 * 1000); // wait for one sec for file to be created
        while (waitTill > new Date()) {} // eslint-disable-line no-empty
      }
      if (!fs.existsSync(dataFile)) {
        throw new Error('Data file was not created in time');
      }
      const data = JSON.parse(fs.readFileSync(dataFile, 'utf8'));
      if (key) {
        return data.form[key];
      }
      return data;
    };
  },

};

function toCamelCase(name) {
  if (typeof name !== 'string') {
    return name;
  }
  return name.replace(/-(\w)/gi, (word, letter) => {
    return letter.toUpperCase();
  });
}

function convertFontWeightToNumber(name) {
  const fontWeightPatterns = [
    { num: 100, pattern: /^Thin$/i },
    { num: 200, pattern: /^(Extra|Ultra)-?light$/i },
    { num: 300, pattern: /^Light$/i },
    { num: 400, pattern: /^(Normal|Regular|Roman|Book)$/i },
    { num: 500, pattern: /^Medium$/i },
    { num: 600, pattern: /^(Semi|Demi)-?bold$/i },
    { num: 700, pattern: /^Bold$/i },
    { num: 800, pattern: /^(Extra|Ultra)-?bold$/i },
    { num: 900, pattern: /^(Black|Heavy)$/i },
  ];

  if (/^[1-9]00$/.test(name)) {
    return Number(name);
  }

  const matches = fontWeightPatterns.filter(fontWeight => fontWeight.pattern.test(name));

  if (matches.length) {
    return String(matches[0].num);
  }
  return name;
}

function isFontWeightProperty(prop) {
  return ['fontWeight'].indexOf(prop) > -1;
}

module.exports.convertCssPropertiesToCamelCase = function (props) {
  const output = {};
  Object.keys(props).forEach((key) => {
    const keyCamel = toCamelCase(key);

    if (isFontWeightProperty(keyCamel)) {
      output[keyCamel] = convertFontWeightToNumber(props[key]);
    } else if (isColorProperty(keyCamel)) {
      output[keyCamel] = convertColorToRGBA(props[key]);
    } else {
      output[keyCamel] = props[key];
    }
  });
  return output;
};

module.exports.deleteDir = function (dir_path) {
  if (fs.existsSync(dir_path)) {
    fs.readdirSync(dir_path).forEach(function (entry) {
      const entry_path = path.join(dir_path, entry);
      if (fs.lstatSync(entry_path).isDirectory()) {
        this.deleteDir(entry_path);
      } else {
        fs.unlinkSync(entry_path);
      }
    });
    fs.rmdirSync(dir_path);
  }
};

module.exports.screenshotOutputFolder = function (fileName) {
  if (!fileName.includes('/')) {
    return path.join(global.output_dir, fileName);
  }
  return path.join(process.cwd(), fileName);
};

module.exports.fileToBase64Zip = async function (localPath) {
  const archiver = require('archiver');

  const zipData = [];
  const source = fs.createReadStream(localPath);

  return new Promise((resolve, reject) => {
    archiver('zip')
      .on('error', (e) => { throw new Error(e); })
      .on('data', data => zipData.push(data))
      .on('end', () => resolve(Buffer.concat(zipData).toString('base64')))
      .append(source, { name: path.basename(localPath) })
      .finalize((err) => {
        if (err) {
          reject(err);
        }
      });
  });
};
<commit_msg>Fix/no screenshot on windows (#1395)<commit_after>const fs = require('fs');
const path = require('path');
const getFunctionArguments = require('fn-args');
const { convertColorToRGBA, isColorProperty } = require('./colorUtils');

function isObject(item) {
  return item && typeof item === 'object' && !Array.isArray(item);
}

function flatTail(flatParams, params) {
  const res = [];
  for (const fp of flatParams) {
    for (const p of params) {
      res.push([p].concat(fp));
    }
  }
  return res;
}

module.exports.flatTail = flatTail;

function deepMerge(target, source) {
  if (isObject(target) && isObject(source)) {
    for (const key in source) {
      if (isObject(source[key])) {
        if (!target[key]) Object.assign(target, { [key]: {} });
        deepMerge(target[key], source[key]);
      } else {
        Object.assign(target, { [key]: source[key] });
      }
    }
  }
  return target;
}

module.exports.deepMerge = deepMerge;

const isGenerator = module.exports.isGenerator = function (fn) {
  return fn.constructor.name === 'GeneratorFunction';
};

const isAsyncFunction = module.exports.isAsyncFunction = function (fn) {
  return fn[Symbol.toStringTag] === 'AsyncFunction';
};

module.exports.fileExists = function (filePath) {
  try {
    fs.statSync(filePath);
  } catch (err) {
    if (err.code === 'ENOENT') return false;
  }
  return true;
};

module.exports.isFile = function (filePath) {
  let filestat;
  try {
    filestat = fs.statSync(filePath);
  } catch (err) {
    if (err.code === 'ENOENT') return false;
  }
  if (!filestat) return false;
  return filestat.isFile();
};

module.exports.getParamNames = function (fn) {
  if (fn.isSinonProxy) return [];
  return getFunctionArguments(fn);
};

module.exports.installedLocally = function () {
  return path.resolve(`${__dirname}/../`).indexOf(process.cwd()) === 0;
};

module.exports.methodsOfObject = function (obj, className) {
  const methods = [];

  const standard = [
    'constructor',
    'toString',
    'toLocaleString',
    'valueOf',
    'hasOwnProperty',
    'isPrototypeOf',
    'propertyIsEnumerable',
  ];

  function pushToMethods(prop) {
    if (typeof obj[prop] !== 'function') return;
    if (standard.indexOf(prop) >= 0) return;
    if (prop.indexOf('_') === 0) return;
    methods.push(prop);
  }

  while (obj.constructor.name !== className) {
    Object.getOwnPropertyNames(obj).forEach(pushToMethods);
    obj = Object.getPrototypeOf(obj);

    if (!obj || !obj.constructor) break;
  }
  return methods;
};

module.exports.template = function (template, data) {
  return template.replace(/{{([^{}]*)}}/g, (a, b) => {
    const r = data[b];
    if (r === undefined) return '';
    return r.toString();
  });
};

module.exports.ucfirst = function (str) {
  return str.charAt(0).toUpperCase() + str.substr(1);
};

module.exports.lcfirst = function (str) {
  return str.charAt(0).toLowerCase() + str.substr(1);
};

module.exports.chunkArray = function (arr, chunk) {
  let i;
  let j;
  const tmp = [];
  for (i = 0, j = arr.length; i < j; i += chunk) {
    tmp.push(arr.slice(i, i + chunk));
  }
  return tmp;
};

module.exports.clearString = function (str) {
  /* Replace forbidden symbols in string
   */
  return str
    .replace(/ /g, '_')
    .replace(/"/g, "'")
    .replace(/\//g, '_')
    .replace(/</g, '(')
    .replace(/>/g, ')')
    .replace(/:/g, '_')
    .replace(/\\/g, '_')
    .replace(/\|/g, '_')
    .replace(/\?/g, '.')
    .replace(/\*/g, '^')
    .replace(/'/g, '');
};

module.exports.decodeUrl = function (url) {
  /* Replace forbidden symbols in string
   */
  return decodeURIComponent(decodeURIComponent(decodeURIComponent(url)));
};

module.exports.xpathLocator = {
  literal: (string) => {
    if (string.indexOf("'") > -1) {
      string = string.split("'", -1).map(substr => `'${substr}'`).join(',"\'",');
      return `concat(${string})`;
    }
    return `'${string}'`;
  },
  combine: locators => locators.join(' | '),
};

module.exports.test = {

  grepLines(array, startString, endString) {
    let startIndex = 0;
    let endIndex;
    array.every((elem, index) => {
      if (elem === startString) {
        startIndex = index;
        return true;
      }
      if (elem === endString) {
        endIndex = index;
        return false;
      }
      return true;
    });
    return array.slice(startIndex + 1, endIndex);
  },

  submittedData(dataFile) {
    return function (key) {
      if (!fs.existsSync(dataFile)) {
        const waitTill = new Date(new Date().getTime() + 1 * 1000); // wait for one sec for file to be created
        while (waitTill > new Date()) {} // eslint-disable-line no-empty
      }
      if (!fs.existsSync(dataFile)) {
        throw new Error('Data file was not created in time');
      }
      const data = JSON.parse(fs.readFileSync(dataFile, 'utf8'));
      if (key) {
        return data.form[key];
      }
      return data;
    };
  },

};

function toCamelCase(name) {
  if (typeof name !== 'string') {
    return name;
  }
  return name.replace(/-(\w)/gi, (word, letter) => {
    return letter.toUpperCase();
  });
}

function convertFontWeightToNumber(name) {
  const fontWeightPatterns = [
    { num: 100, pattern: /^Thin$/i },
    { num: 200, pattern: /^(Extra|Ultra)-?light$/i },
    { num: 300, pattern: /^Light$/i },
    { num: 400, pattern: /^(Normal|Regular|Roman|Book)$/i },
    { num: 500, pattern: /^Medium$/i },
    { num: 600, pattern: /^(Semi|Demi)-?bold$/i },
    { num: 700, pattern: /^Bold$/i },
    { num: 800, pattern: /^(Extra|Ultra)-?bold$/i },
    { num: 900, pattern: /^(Black|Heavy)$/i },
  ];

  if (/^[1-9]00$/.test(name)) {
    return Number(name);
  }

  const matches = fontWeightPatterns.filter(fontWeight => fontWeight.pattern.test(name));

  if (matches.length) {
    return String(matches[0].num);
  }
  return name;
}

function isFontWeightProperty(prop) {
  return ['fontWeight'].indexOf(prop) > -1;
}

module.exports.convertCssPropertiesToCamelCase = function (props) {
  const output = {};
  Object.keys(props).forEach((key) => {
    const keyCamel = toCamelCase(key);

    if (isFontWeightProperty(keyCamel)) {
      output[keyCamel] = convertFontWeightToNumber(props[key]);
    } else if (isColorProperty(keyCamel)) {
      output[keyCamel] = convertColorToRGBA(props[key]);
    } else {
      output[keyCamel] = props[key];
    }
  });
  return output;
};

module.exports.deleteDir = function (dir_path) {
  if (fs.existsSync(dir_path)) {
    fs.readdirSync(dir_path).forEach(function (entry) {
      const entry_path = path.join(dir_path, entry);
      if (fs.lstatSync(entry_path).isDirectory()) {
        this.deleteDir(entry_path);
      } else {
        fs.unlinkSync(entry_path);
      }
    });
    fs.rmdirSync(dir_path);
  }
};

module.exports.screenshotOutputFolder = function (fileName) {
  const fileSep = path.sep;
  if (!fileName.includes(fileSep)) {
    return path.join(global.output_dir, fileName);
  }
  return path.join(process.cwd(), fileName);
};

module.exports.fileToBase64Zip = async function (localPath) {
  const archiver = require('archiver');

  const zipData = [];
  const source = fs.createReadStream(localPath);

  return new Promise((resolve, reject) => {
    archiver('zip')
      .on('error', (e) => { throw new Error(e); })
      .on('data', data => zipData.push(data))
      .on('end', () => resolve(Buffer.concat(zipData).toString('base64')))
      .append(source, { name: path.basename(localPath) })
      .finalize((err) => {
        if (err) {
          reject(err);
        }
      });
  });
};
<|endoftext|>


<commit_before>/*globals jQuery, FA*/

/**
 * Criar inputs na criação do tópico.
 *
 * @author lffg <https://github.com/lffg>
 * @version 1.0
 */

(function ($) {
  'use strict';

  var inputs = [{
    label: 'Label do Input 1',
    placeholder: 'Placeholder do Input 1',
    insert: '[b]Valor do Input 1:[/b] {{CONTENT}}. \n\n', // {{CONTENT}} = O que o usuário colocou.
    required: true
  }, {
    label: 'Label do Input 2',
    placeholder: 'Placeholder do Input 2',
    insert: '[b]Valor do Input 2:[/b] {{CONTENT}}. \n\n', // O caractere especial \n é o mesmo que uma quebra de linha.
    required: true
  }, {
    label: 'Label do Input 3',
    insert: '[b]Valor do Input 3:[/b] {{CONTENT}}. \n\n',
    required: false
  }];

  inputs.reverse();

  window.FA = window.FA || {};
  FA.Posting = FA.Posting || {};

  var Inputs;
  FA.Posting.Inputs = Inputs = function (userConfig) {
    var self = this;

    self.defaults = {
      label: undefined,
      placeholder: undefined,
      insert: undefined,
      forums: ['*'],
      required: false
    };

    self.userConfig = userConfig;

    self.config = $.extend({}, self.defaults, self.userConfig);
  };

  Inputs.prototype.init = function () {
    var self = this;

    if (!$.sceditor) {
      return false;
    }

    if (self.config.forums.indexOf('*') === -1) {
      var hrefId = parseInt(location.href.replace(/^.*\/post\?f=(\d+)&.*$/g, '$1'));

      if (self.config.forums.indexOf(hrefId) === -1) {
        return false;
      }
    }

    self.generate();
    self.listen(function () {
      self.parse();
    });
  };

  Inputs.prototype.generate = function () {
    var self = this;

    self.$wrapper = $([
      '
',
      '
',
      '    ' + $('', { 'text': self.config.label }).prop('outerHTML'),
      '
',
      '
',
      '    ' + $('<input />', { 'type': 'text' }).prop('outerHTML'),
      '
',
      '
'
    ].join('\n'));

    self.$input = self.$wrapper.find('input');

    self.$input
      .attr('class', 'inputbox medium fa-generated-input')
      .attr('data-content', self.config.insert)
      .attr('placeholder', self.config.placeholder || '')
    ;

    if (self.config.required) {
      self.$input.attr('required', 'required');
    }

    $('input[name="subject"]')
      .parents('dl')
      .after(self.$wrapper)
    ;
  };

  Inputs.prototype.listen = function (fn) {
    var selectors = [
      '[required]',
      '[required="required"]',
      '[name="subject"]'
    ];

    $('[type="submit"]').on('click', function () {
      if ($(selectors.join(',')).val() === '') {
        return;
      }

      fn();
    });
  };

  Inputs.prototype.parse = function () {
    var self = this;

    var $sceditor = $('#text_editor_textarea').sceditor('instance');

    var content = self.config.insert;
    var value = $.trim(self.$input.val());

    content = content.replace(/{{CONTENT}}/gi, value);

    $sceditor.val(content + $sceditor.val());
  };

  $(function () {
    $.each(inputs, function () {
      var self = this;

      (new FA.Posting.Inputs(self)).init();
    });
  });
}(jQuery));
<commit_msg>Update app.js<commit_after>/*globals jQuery, FA*/

/**
 * Criar inputs na criação do tópico.
 *
 * @author lffg <https://github.com/lffg>
 * @version 1.0
 */

(function($) {
  'use strict';

  var inputs = [{
    label: 'Label do Input 1',
    placeholder: 'Placeholder do Input 1',
    insert: '[b]Valor do Input 1:[/b] {{CONTENT}}. \n\n', // {{CONTENT}} = O que o usuário colocou.
    required: true
  }, {
    label: 'Label do Input 2',
    placeholder: 'Placeholder do Input 2',
    insert: '[b]Valor do Input 2:[/b] {{CONTENT}}. \n\n', // O caractere especial \n é o mesmo que uma quebra de linha.
    required: true
  }, {
    label: 'Label do Input 3',
    insert: '[b]Valor do Input 3:[/b] {{CONTENT}}. \n\n',
    required: false
  }];

  inputs.reverse();

  window.FA = window.FA || {};
  FA.Posting = FA.Posting || {};

  var Inputs;
  FA.Posting.Inputs = Inputs = function(userConfig) {
    var self = this;

    self.defaults = {
      label: undefined,
      placeholder: undefined,
      insert: undefined,
      forums: ['*'],
      required: false
    };

    self.userConfig = userConfig;

    self.config = $.extend({}, self.defaults, self.userConfig);
  };

  Inputs.prototype.init = function() {
    var self = this;

    if (!$.sceditor) {
      return false;
    }

    if (self.config.forums.indexOf('*') === -1) {
      var hrefId = parseInt(location.href.replace(/^.*\/post\?f=(\d+)&.*$/g, '$1'));

      if (self.config.forums.indexOf(hrefId) === -1) {
        return false;
      }
    }

    self.generate();
    self.listen(function() {
      self.parse();
    });
  };

  Inputs.prototype.generate = function() {
    var self = this;

    self.$wrapper = $([
      '
',
      '
',
      '    ' + $('', { 'text': self.config.label }).prop('outerHTML'),
      '
',
      '
',
      '    ' + $('<input />', { 'type': 'text' }).prop('outerHTML'),
      '
',
      '
'
    ].join('\n'));

    self.$input = self.$wrapper.find('input');

    self.$input
      .attr('class', 'inputbox medium fa-generated-input')
      .attr('data-content', self.config.insert)
      .attr('placeholder', self.config.placeholder || '')
    ;

    if (self.config.required) {
      self.$input.attr('required', 'required');
    }

    $('input[name="subject"]')
      .parents('dl')
      .after(self.$wrapper)
    ;
  };

  Inputs.prototype.listen = function(fn) {
    var selectors = [
      '[required]',
      '[required="required"]',
      '[name="subject"]'
    ];

    $('[type="submit"]').on('click', function() {
      if ($(selectors.join(',')).val() === '') {
        return;
      }

      fn();
    });
  };

  Inputs.prototype.parse = function() {
    var self = this;

    var $sceditor = $('#text_editor_textarea').sceditor('instance');

    var content = self.config.insert;
    var value = $.trim(self.$input.val());

    content = content.replace(/{{CONTENT}}/gi, value);

    $sceditor.val(content + $sceditor.val());
  };

  $(function() {
    $.each(inputs, function() {
      var self = this;

      (new FA.Posting.Inputs(self)).init();
    });
  });
}(jQuery));
<|endoftext|>


<commit_before>var async = require("async");
var crypto = require("crypto");
var fs = require("fs");
var path = require("path");

var md5 = function (text) {
  var hash = crypto.createHash("md5").update(text);
  return hash.digest("hex");
};

var patched_cleanly = function (result) {
  var clean_patch = true,
    i = 0;

  for (i; i < result[1].length; i++) {
    if (result[1][i] !== true) {
      clean_patch = false;
      break;
    }
  }
  return clean_patch;
};

var walk_dir = function (p, cb) {
  var paths = {
    files: [],
    dirs: []
  };

  fs.lstat(p, function (err, st) {
    if (err) {
      if (err.errno === 34) {
        return cb(null, paths);
      }
      return cb(err, paths);
    }
    // Ignore hidden files. Yeah I know this is lame and you can put hidden files in a repo/room.
    if (path.basename(p)[0] === ".") {
      return cb(null, paths);
    }
    if (!st.isDirectory()) {
      paths.files.push(p);
      return cb(null, paths);
    }
    paths.dirs.push(p);
    return fs.readdir(p, function (err, filenames) {
      async.each(filenames, function (file, callback) {
        var abs_path = path.join(p, file);
        walk_dir(abs_path, function (err, sub_paths) {
          paths.dirs = paths.dirs.concat(sub_paths.dirs);
          paths.files = paths.files.concat(sub_paths.files);
          callback(err);
        });
      },
        function (err, result) {
          cb(err, paths);
        });
    });
  });
};

module.exports = {
  md5: md5,
  patched_cleanly: patched_cleanly,
  walk_dir: walk_dir
};
<commit_msg>Fix md5 sums for unicode stuff<commit_after>/*global unescape: false */
var async = require("async");
var crypto = require("crypto");
var fs = require("fs");
var path = require("path");

var md5 = function (text) {
  // The unescape(encodeURIComponent()) converts to utf-8, which is what we want
  var hash = crypto.createHash("md5").update(unescape(encodeURIComponent(text)));
  return hash.digest("hex");
};

var patched_cleanly = function (result) {
  var clean_patch = true,
    i = 0;

  for (i; i < result[1].length; i++) {
    if (result[1][i] !== true) {
      clean_patch = false;
      break;
    }
  }
  return clean_patch;
};

var walk_dir = function (p, cb) {
  var paths = {
    files: [],
    dirs: []
  };

  fs.lstat(p, function (err, st) {
    if (err) {
      if (err.errno === 34) {
        return cb(null, paths);
      }
      return cb(err, paths);
    }
    // Ignore hidden files. Yeah I know this is lame and you can put hidden files in a repo/room.
    if (path.basename(p)[0] === ".") {
      return cb(null, paths);
    }
    if (!st.isDirectory()) {
      paths.files.push(p);
      return cb(null, paths);
    }
    paths.dirs.push(p);
    return fs.readdir(p, function (err, filenames) {
      async.each(filenames, function (file, callback) {
        var abs_path = path.join(p, file);
        walk_dir(abs_path, function (err, sub_paths) {
          paths.dirs = paths.dirs.concat(sub_paths.dirs);
          paths.files = paths.files.concat(sub_paths.files);
          callback(err);
        });
      },
        function (err, result) {
          cb(err, paths);
        });
    });
  });
};

module.exports = {
  md5: md5,
  patched_cleanly: patched_cleanly,
  walk_dir: walk_dir
};
<|endoftext|>


<commit_before>import React from "react";

import { storiesOf } from "@storybook/react";
import Card from "@crave/farmblocks-card";

import Tooltip, { alignments } from "./";

storiesOf("Tooltip", module)
  .add("isVisible true - left aligned", () => (

      <Tooltip isVisible text="This is a left aligned tooltip" />

  ))
  .add("isVisible true - right aligned", () => (

      <Tooltip
        isVisible
        text="This is a right aligned tooltip"
        align={alignments.RIGHT}
      />

  ))
  .add("isVisible true - center aligned", () => (

      <Tooltip
        isVisible
        text="This is a center aligned tooltip"
        align={alignments.CENTER}
      />

  ))
  .add("isVisible true - hideArrow", () => (

      <Tooltip isVisible hideArrow>
        This is a left aligned tooltip without arrow
      </Tooltip>

  ))
  .add("isVisible false", () => (

      <Tooltip isVisible={false} text="Hey yo" />

  ))
  .add("with card", () => (

      <Tooltip isVisible={true}>
        <Card>this is a Card</Card>
      </Tooltip>

  ))
  .add("without content", () => (

      <Tooltip />

  ))
  .add("auto aligned", () => (

        <Tooltip
          align={alignments.AUTO}
          text="This is an auto aligned tooltip"
        />

        <Tooltip
          align={alignments.AUTO}
          text="This is an auto aligned tooltip"
        />

  ))
  .add("auto aligned (with bondaries)", () => (

        Child
        <Tooltip
          align={alignments.AUTO}
          text="This is an auto aligned tooltip"
          bondariesSelector=".exampleContainer"
        />

        Child
        <Tooltip
          align={alignments.AUTO}
          text="This is an auto aligned tooltip"
          bondariesSelector=".exampleContainer"
        />

  ))
  .add("custom padding", () => (

      <Tooltip
        isVisible
        text="Left aligned with custom padding"
        padding="30px"
      />

  ))
  .add("custom top", () => (

      <Tooltip isVisible text="Left aligned with custom top" top="30vh" />

  ));
<commit_msg>docs(storybook): use withInfo addon in tooltip stories<commit_after>import React from "react";

import { storiesOf } from "@storybook/react";
import { withInfo } from "@storybook/addon-info";
import Card from "@crave/farmblocks-card";

import Tooltip, { alignments } from "./";

storiesOf("Tooltip", module)
  .add(
    "isVisible true - left aligned",
    withInfo()(() => (

        <Tooltip isVisible text="This is a left aligned tooltip" />

    ))
  )
  .add(
    "isVisible true - right aligned",
    withInfo()(() => (

        <Tooltip
          isVisible
          text="This is a right aligned tooltip"
          align={alignments.RIGHT}
        />

    ))
  )
  .add(
    "isVisible true - center aligned",
    withInfo()(() => (

        <Tooltip
          isVisible
          text="This is a center aligned tooltip"
          align={alignments.CENTER}
        />

    ))
  )
  .add(
    "isVisible true - hideArrow",
    withInfo()(() => (

        <Tooltip isVisible hideArrow>
          This is a left aligned tooltip without arrow
        </Tooltip>

    ))
  )
  .add(
    "isVisible false",
    withInfo()(() => (

        <Tooltip isVisible={false} text="Hey yo" />

    ))
  )
  .add(
    "with card",
    withInfo()(() => (

        <Tooltip isVisible={true}>
          <Card>this is a Card</Card>
        </Tooltip>

    ))
  )
  .add(
    "without content",
    withInfo()(() => (

        <Tooltip />

    ))
  )
  .add(
    "auto aligned",
    withInfo()(() => (

          <Tooltip
            align={alignments.AUTO}
            text="This is an auto aligned tooltip"
          />

          <Tooltip
            align={alignments.AUTO}
            text="This is an auto aligned tooltip"
          />

    ))
  )
  .add(
    "auto aligned (with bondaries)",
    withInfo()(() => (

          Child
          <Tooltip
            align={alignments.AUTO}
            text="This is an auto aligned tooltip"
            bondariesSelector=".exampleContainer"
          />

          Child
          <Tooltip
            align={alignments.AUTO}
            text="This is an auto aligned tooltip"
            bondariesSelector=".exampleContainer"
          />

    ))
  )
  .add(
    "custom padding",
    withInfo()(() => (

        <Tooltip
          isVisible
          text="Left aligned with custom padding"
          padding="30px"
        />

    ))
  )
  .add(
    "custom top",
    withInfo()(() => (

        <Tooltip isVisible text="Left aligned with custom top" top="30vh" />

    ))
  );
<|endoftext|>