<commit_before>import {
  app,
  BrowserWindow,
  shell,
  ipcMain,
} from 'electron';
import isDevMode from 'electron-is-dev';
import fs from 'fs-extra';
import path from 'path';
import windowStateKeeper from 'electron-window-state';
import { enforceMacOSAppLocation } from 'electron-util';

// Set app directory before loading user modules
if (process.env.FRANZ_APPDATA_DIR != null) {
  app.setPath('appData', process.env.FRANZ_APPDATA_DIR);
  app.setPath('userData', path.join(app.getPath('appData')));
} else if (process.platform === 'win32') {
  app.setPath('appData', process.env.APPDATA);
  app.setPath('userData', path.join(app.getPath('appData'), app.getName()));
}

if (isDevMode) {
  app.setPath('userData', path.join(app.getPath('appData'), 'FranzDev'));
}

// workaround for https://github.com/electron/electron/pull/26432
app.allowRendererProcessReuse = false;
app.commandLine.appendSwitch('disable-features', 'CrossOriginOpenerPolicy');

/* eslint-disable import/first */
import {
  isMac,
  isWindows,
  isLinux,
} from './environment';
import { mainIpcHandler as basicAuthHandler } from './features/basicAuth';
import ipcApi from './electron/ipc-api';
import Tray from './lib/Tray';
import Settings from './electron/Settings';
import handleDeepLink from './electron/deepLinking';
import { isPositionValid } from './electron/windowUtils';
import askFormacOSPermissions from './electron/macOSPermissions';
import { appId } from './package.json'; // eslint-disable-line import/no-unresolved
import './electron/exception';

import {
  DEFAULT_APP_SETTINGS,
  DEFAULT_WINDOW_OPTIONS,
} from './config';
import { asarPath } from './helpers/asar-helpers';
import { isValidExternalURL } from './helpers/url-helpers';
import userAgent from './helpers/userAgent-helpers';
import ms from 'ms';

/* eslint-enable import/first */
const debug = require('debug')('Franz:App');

// Globally set useragent to fix user agent override in service workers
debug('Set userAgent to ', userAgent());
app.userAgentFallback = userAgent();

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow;
let willQuitApp = false;

// Register methods to be called once the window has been loaded.
let onDidLoadFns = [];

function onDidLoad(fn) {
  if (onDidLoadFns) {
    onDidLoadFns.push(fn);
  } else if (mainWindow) {
    fn(mainWindow);
  }
}

// Ensure that the recipe directory exists
fs.emptyDirSync(path.join(app.getPath('userData'), 'recipes', 'temp'));
fs.ensureFileSync(path.join(app.getPath('userData'), 'window-state.json'));

// Set App ID for Windows
if (isWindows) {
  app.setAppUserModelId(appId);
}

// Initialize Settings
const settings = new Settings('app', DEFAULT_APP_SETTINGS);
const proxySettings = new Settings('proxy');

// add `liftSingleInstanceLock` to settings.json to override the single instance lock
const liftSingleInstanceLock = settings.get('liftSingleInstanceLock') || false;

// Force single window
const gotTheLock = liftSingleInstanceLock ? true : app.requestSingleInstanceLock();
if (!gotTheLock) {
  app.quit();
} else {
  app.on('second-instance', (event, argv) => {
    // Someone tried to run a second instance, we should focus our window.
    if (mainWindow) {
      mainWindow.show();
      if (mainWindow.isMinimized()) {
        mainWindow.restore();
      }
      mainWindow.focus();

      if (isWindows) {
        onDidLoad((window) => {
          // Keep only command line / deep linked arguments
          const url = argv.slice(1);
          if (url) {
            handleDeepLink(window, url.toString());
          }

          if (argv.includes('--reset-window')) {
            // Needs to be delayed to not interfere with mainWindow.restore();
            setTimeout(() => {
              debug('Resetting windows via Task');
              window.setPosition(DEFAULT_WINDOW_OPTIONS.x + 100, DEFAULT_WINDOW_OPTIONS.y + 100);
              window.setSize(DEFAULT_WINDOW_OPTIONS.width, DEFAULT_WINDOW_OPTIONS.height);
            }, 1);
          } else if (argv.includes('--quit')) {
            // Needs to be delayed to not interfere with mainWindow.restore();
            setTimeout(() => {
              debug('Quitting Franz via Task');
              app.quit();
            }, 1);
          }
        });
      }
    }
  });
}

// Fix Unity indicator issue
// https://github.com/electron/electron/issues/9046
if (isLinux && ['Pantheon', 'Unity:Unity7'].indexOf(process.env.XDG_CURRENT_DESKTOP) !== -1) {
  process.env.XDG_CURRENT_DESKTOP = 'Unity';
}

// Disable GPU acceleration
if (!settings.get('enableGPUAcceleration')) {
  debug('Disable GPU Acceleration');
  app.disableHardwareAcceleration();
}

const createWindow = () => {
  // Remember window size
  const mainWindowState = windowStateKeeper({
    defaultWidth: DEFAULT_WINDOW_OPTIONS.width,
    defaultHeight: DEFAULT_WINDOW_OPTIONS.height,
  });

  let posX = mainWindowState.x || DEFAULT_WINDOW_OPTIONS.x;
  let posY = mainWindowState.y || DEFAULT_WINDOW_OPTIONS.y;

  if (!isPositionValid({ x: posX, y: posY })) {
    debug('Window is out of screen bounds, resetting window');
    posX = DEFAULT_WINDOW_OPTIONS.x;
    posY = DEFAULT_WINDOW_OPTIONS.y;
  }

  // Create the browser window.
  mainWindow = new BrowserWindow({
    x: posX,
    y: posY,
    width: mainWindowState.width,
    height: mainWindowState.height,
    minWidth: 600,
    minHeight: 500,
    titleBarStyle: isMac ? 'hidden' : '',
    frame: isLinux,
    backgroundColor: !settings.get('darkMode') ? '#3498db' : '#1E1E1E',
    webPreferences: {
      nodeIntegration: true,
      webviewTag: true,
      enableRemoteModule: true,
    },
  });

  mainWindow.webContents.on('did-finish-load', () => {
    const fns = onDidLoadFns;
    onDidLoadFns = null;

    if (!fns) return;

    for (const fn of fns) {
      fn(mainWindow);
    }
  });

  // Initialize System Tray
  const trayIcon = new Tray();

  // Initialize ipcApi
  ipcApi({
    mainWindow,
    settings: {
      app: settings,
      proxy: proxySettings,
    },
    trayIcon,
  });

  // Manage Window State
  mainWindowState.manage(mainWindow);

  // and load the index.html of the app.
  mainWindow.loadURL(`file://${__dirname}/index.html`);

  // Open the DevTools.
  if (isDevMode || process.argv.includes('--devtools')) {
    mainWindow.webContents.openDevTools();
  }

  // Windows deep linking handling on app launch
  if (isWindows) {
    onDidLoad((window) => {
      const url = process.argv.slice(1);
      if (url) {
        handleDeepLink(window, url.toString());
      }
    });
  }

  // Emitted when the window is closed.
  mainWindow.on('close', (e) => {
    debug('Window: close window');
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    if (!willQuitApp && (settings.get('runInBackground') === undefined || settings.get('runInBackground'))) {
      e.preventDefault();
      if (isWindows) {
        debug('Window: minimize');
        mainWindow.minimize();

        if (settings.get('minimizeToSystemTray')) {
          debug('Skip taskbar: true');
          mainWindow.setSkipTaskbar(true);
        }
      } else {
        debug('Window: hide');
        mainWindow.hide();
      }
    } else {
      app.quit();
    }
  });

  // For Windows we need to store a flag to properly restore the window
  // if the window was maximized before minimizing it so system tray
  mainWindow.on('minimize', () => {
    app.wasMaximized = app.isMaximized;

    if (settings.get('minimizeToSystemTray')) {
      debug('Skip taskbar: true');
      mainWindow.setSkipTaskbar(true);
      trayIcon.show();
    }
  });

  mainWindow.on('maximize', () => {
    debug('Window: maximize');
    app.isMaximized = true;
  });

  mainWindow.on('unmaximize', () => {
    debug('Window: unmaximize');
    app.isMaximized = false;
  });

  mainWindow.on('restore', () => {
    debug('Window: restore');
    mainWindow.setSkipTaskbar(false);

    if (app.wasMaximized) {
      debug('Window: was maximized before, maximize window');
      mainWindow.maximize();
    }

    if (!settings.get('enableSystemTray')) {
      debug('Tray: hiding tray icon');
      trayIcon.hide();
    }
  });

  if (isMac) {
    setTimeout(() => askFormacOSPermissions(mainWindow), ms('30s'));
  }

  mainWindow.on('show', () => {
    debug('Skip taskbar: false');
    mainWindow.setSkipTaskbar(false);
  });

  app.mainWindow = mainWindow;
  app.isMaximized = mainWindow.isMaximized();

  mainWindow.webContents.on('new-window', (e, url) => {
    debug('Open url', url);
    e.preventDefault();

    if (isValidExternalURL(url)) {
      shell.openExternal(url);
    }
  });
};

// Allow passing command line parameters/switches to electron
// https://electronjs.org/docs/api/chrome-command-line-switches
// used for Kerberos support
// Usage e.g. MACOS
// $ Franz.app/Contents/MacOS/Franz --auth-server-whitelist *.mydomain.com --auth-negotiate-delegate-whitelist *.mydomain.com
const argv = require('minimist')(process.argv.slice(1));

if (argv['auth-server-whitelist']) {
  app.commandLine.appendSwitch('auth-server-whitelist', argv['auth-server-whitelist']);
}
if (argv['auth-negotiate-delegate-whitelist']) {
  app.commandLine.appendSwitch('auth-negotiate-delegate-whitelist', argv['auth-negotiate-delegate-whitelist']);
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', () => {
  // force app to live in /Applications
  enforceMacOSAppLocation();

  // Register App URL
  app.setAsDefaultProtocolClient('franz');

  if (isDevMode) {
    app.setAsDefaultProtocolClient('franz-dev');
  }

  if (process.platform === 'win32') {
    app.setUserTasks([{
      program: process.execPath,
      arguments: `${isDevMode ? `${__dirname} ` : ''}--reset-window`,
      iconPath: asarPath(path.join(isDevMode ? `${__dirname}../src/` : __dirname, 'assets/images/taskbar/win32/display.ico')),
      iconIndex: 0,
      title: 'Move Franz to Current Display',
      description: 'Restore the position and size of Franz',
    }, {
      program: process.execPath,
      arguments: `${isDevMode ? `${__dirname} ` : ''}--quit`,
      iconIndex: 0,
      title: 'Quit Franz',
    }]);
  }

  createWindow();
});

// This is the worst possible implementation as the webview.webContents based callback doesn't work 🖕
// TODO: rewrite to handle multiple login calls
const noop = () => null;
let authCallback = noop;

app.on('login', (event, webContents, request, authInfo, callback) => {
  authCallback = callback;
  debug('browser login event', authInfo);
  event.preventDefault();

  if (!authInfo.isProxy && authInfo.scheme === 'basic') {
    debug('basic auth handler', authInfo);
    basicAuthHandler(mainWindow, authInfo);
  }
});

// TODO: evaluate if we need to store the authCallback for every service
ipcMain.on('feature-basic-auth-credentials', (e, { user, password }) => {
  debug('Received basic auth credentials', user, '********');

  authCallback(user, password);
  authCallback = noop;
});

ipcMain.on('feature-basic-auth-cancel', () => {
  debug('Cancel basic auth');

  authCallback(null);
  authCallback = noop;
});

// Quit when all windows are closed.
app.on('window-all-closed', () => {
  // On OS X it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (settings.get('runInBackground') === undefined
    || settings.get('runInBackground')) {
    debug('Window: all windows closed, quit app');
    app.quit();
  } else {
    debug('Window: don\'t quit app');
  }
});

app.on('before-quit', () => {
  willQuitApp = true;
});

app.on('activate', () => {
  // On OS X it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (mainWindow === null) {
    createWindow();
  } else {
    mainWindow.show();
  }
});

app.on('web-contents-created', (createdEvent, contents) => {
  contents.on('new-window', (event, url, frameNme, disposition) => {
    if (disposition === 'foreground-tab') event.preventDefault();
  });
});

app.on('will-finish-launching', () => {
  // Protocol handler for macOS
  app.on('open-url', (event, url) => {
    event.preventDefault();

    onDidLoad((window) => {
      debug('open-url event', url);
      handleDeepLink(window, url);
    });
  });
});
<commit_msg>fix linting<commit_after>import {
  app,
  BrowserWindow,
  shell,
  ipcMain,
} from 'electron';
import isDevMode from 'electron-is-dev';
import fs from 'fs-extra';
import path from 'path';
import windowStateKeeper from 'electron-window-state';
import { enforceMacOSAppLocation } from 'electron-util';
import ms from 'ms';

// Set app directory before loading user modules
if (process.env.FRANZ_APPDATA_DIR != null) {
  app.setPath('appData', process.env.FRANZ_APPDATA_DIR);
  app.setPath('userData', path.join(app.getPath('appData')));
} else if (process.platform === 'win32') {
  app.setPath('appData', process.env.APPDATA);
  app.setPath('userData', path.join(app.getPath('appData'), app.getName()));
}

if (isDevMode) {
  app.setPath('userData', path.join(app.getPath('appData'), 'FranzDev'));
}

// workaround for https://github.com/electron/electron/pull/26432
app.allowRendererProcessReuse = false;
app.commandLine.appendSwitch('disable-features', 'CrossOriginOpenerPolicy');

/* eslint-disable import/first */
import {
  isMac,
  isWindows,
  isLinux,
} from './environment';
import { mainIpcHandler as basicAuthHandler } from './features/basicAuth';
import ipcApi from './electron/ipc-api';
import Tray from './lib/Tray';
import Settings from './electron/Settings';
import handleDeepLink from './electron/deepLinking';
import { isPositionValid } from './electron/windowUtils';
import askFormacOSPermissions from './electron/macOSPermissions';
import { appId } from './package.json'; // eslint-disable-line import/no-unresolved
import './electron/exception';

import {
  DEFAULT_APP_SETTINGS,
  DEFAULT_WINDOW_OPTIONS,
} from './config';
import { asarPath } from './helpers/asar-helpers';
import { isValidExternalURL } from './helpers/url-helpers';
import userAgent from './helpers/userAgent-helpers';

/* eslint-enable import/first */
const debug = require('debug')('Franz:App');

// Globally set useragent to fix user agent override in service workers
debug('Set userAgent to ', userAgent());
app.userAgentFallback = userAgent();

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow;
let willQuitApp = false;

// Register methods to be called once the window has been loaded.
let onDidLoadFns = [];

function onDidLoad(fn) {
  if (onDidLoadFns) {
    onDidLoadFns.push(fn);
  } else if (mainWindow) {
    fn(mainWindow);
  }
}

// Ensure that the recipe directory exists
fs.emptyDirSync(path.join(app.getPath('userData'), 'recipes', 'temp'));
fs.ensureFileSync(path.join(app.getPath('userData'), 'window-state.json'));

// Set App ID for Windows
if (isWindows) {
  app.setAppUserModelId(appId);
}

// Initialize Settings
const settings = new Settings('app', DEFAULT_APP_SETTINGS);
const proxySettings = new Settings('proxy');

// add `liftSingleInstanceLock` to settings.json to override the single instance lock
const liftSingleInstanceLock = settings.get('liftSingleInstanceLock') || false;

// Force single window
const gotTheLock = liftSingleInstanceLock ? true : app.requestSingleInstanceLock();
if (!gotTheLock) {
  app.quit();
} else {
  app.on('second-instance', (event, argv) => {
    // Someone tried to run a second instance, we should focus our window.
    if (mainWindow) {
      mainWindow.show();
      if (mainWindow.isMinimized()) {
        mainWindow.restore();
      }
      mainWindow.focus();

      if (isWindows) {
        onDidLoad((window) => {
          // Keep only command line / deep linked arguments
          const url = argv.slice(1);
          if (url) {
            handleDeepLink(window, url.toString());
          }

          if (argv.includes('--reset-window')) {
            // Needs to be delayed to not interfere with mainWindow.restore();
            setTimeout(() => {
              debug('Resetting windows via Task');
              window.setPosition(DEFAULT_WINDOW_OPTIONS.x + 100, DEFAULT_WINDOW_OPTIONS.y + 100);
              window.setSize(DEFAULT_WINDOW_OPTIONS.width, DEFAULT_WINDOW_OPTIONS.height);
            }, 1);
          } else if (argv.includes('--quit')) {
            // Needs to be delayed to not interfere with mainWindow.restore();
            setTimeout(() => {
              debug('Quitting Franz via Task');
              app.quit();
            }, 1);
          }
        });
      }
    }
  });
}

// Fix Unity indicator issue
// https://github.com/electron/electron/issues/9046
if (isLinux && ['Pantheon', 'Unity:Unity7'].indexOf(process.env.XDG_CURRENT_DESKTOP) !== -1) {
  process.env.XDG_CURRENT_DESKTOP = 'Unity';
}

// Disable GPU acceleration
if (!settings.get('enableGPUAcceleration')) {
  debug('Disable GPU Acceleration');
  app.disableHardwareAcceleration();
}

const createWindow = () => {
  // Remember window size
  const mainWindowState = windowStateKeeper({
    defaultWidth: DEFAULT_WINDOW_OPTIONS.width,
    defaultHeight: DEFAULT_WINDOW_OPTIONS.height,
  });

  let posX = mainWindowState.x || DEFAULT_WINDOW_OPTIONS.x;
  let posY = mainWindowState.y || DEFAULT_WINDOW_OPTIONS.y;

  if (!isPositionValid({ x: posX, y: posY })) {
    debug('Window is out of screen bounds, resetting window');
    posX = DEFAULT_WINDOW_OPTIONS.x;
    posY = DEFAULT_WINDOW_OPTIONS.y;
  }

  // Create the browser window.
  mainWindow = new BrowserWindow({
    x: posX,
    y: posY,
    width: mainWindowState.width,
    height: mainWindowState.height,
    minWidth: 600,
    minHeight: 500,
    titleBarStyle: isMac ? 'hidden' : '',
    frame: isLinux,
    backgroundColor: !settings.get('darkMode') ? '#3498db' : '#1E1E1E',
    webPreferences: {
      nodeIntegration: true,
      webviewTag: true,
      enableRemoteModule: true,
    },
  });

  mainWindow.webContents.on('did-finish-load', () => {
    const fns = onDidLoadFns;
    onDidLoadFns = null;

    if (!fns) return;

    for (const fn of fns) {
      fn(mainWindow);
    }
  });

  // Initialize System Tray
  const trayIcon = new Tray();

  // Initialize ipcApi
  ipcApi({
    mainWindow,
    settings: {
      app: settings,
      proxy: proxySettings,
    },
    trayIcon,
  });

  // Manage Window State
  mainWindowState.manage(mainWindow);

  // and load the index.html of the app.
  mainWindow.loadURL(`file://${__dirname}/index.html`);

  // Open the DevTools.
  if (isDevMode || process.argv.includes('--devtools')) {
    mainWindow.webContents.openDevTools();
  }

  // Windows deep linking handling on app launch
  if (isWindows) {
    onDidLoad((window) => {
      const url = process.argv.slice(1);
      if (url) {
        handleDeepLink(window, url.toString());
      }
    });
  }

  // Emitted when the window is closed.
  mainWindow.on('close', (e) => {
    debug('Window: close window');
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    if (!willQuitApp && (settings.get('runInBackground') === undefined || settings.get('runInBackground'))) {
      e.preventDefault();
      if (isWindows) {
        debug('Window: minimize');
        mainWindow.minimize();

        if (settings.get('minimizeToSystemTray')) {
          debug('Skip taskbar: true');
          mainWindow.setSkipTaskbar(true);
        }
      } else {
        debug('Window: hide');
        mainWindow.hide();
      }
    } else {
      app.quit();
    }
  });

  // For Windows we need to store a flag to properly restore the window
  // if the window was maximized before minimizing it so system tray
  mainWindow.on('minimize', () => {
    app.wasMaximized = app.isMaximized;

    if (settings.get('minimizeToSystemTray')) {
      debug('Skip taskbar: true');
      mainWindow.setSkipTaskbar(true);
      trayIcon.show();
    }
  });

  mainWindow.on('maximize', () => {
    debug('Window: maximize');
    app.isMaximized = true;
  });

  mainWindow.on('unmaximize', () => {
    debug('Window: unmaximize');
    app.isMaximized = false;
  });

  mainWindow.on('restore', () => {
    debug('Window: restore');
    mainWindow.setSkipTaskbar(false);

    if (app.wasMaximized) {
      debug('Window: was maximized before, maximize window');
      mainWindow.maximize();
    }

    if (!settings.get('enableSystemTray')) {
      debug('Tray: hiding tray icon');
      trayIcon.hide();
    }
  });

  if (isMac) {
    setTimeout(() => askFormacOSPermissions(mainWindow), ms('30s'));
  }

  mainWindow.on('show', () => {
    debug('Skip taskbar: false');
    mainWindow.setSkipTaskbar(false);
  });

  app.mainWindow = mainWindow;
  app.isMaximized = mainWindow.isMaximized();

  mainWindow.webContents.on('new-window', (e, url) => {
    debug('Open url', url);
    e.preventDefault();

    if (isValidExternalURL(url)) {
      shell.openExternal(url);
    }
  });
};

// Allow passing command line parameters/switches to electron
// https://electronjs.org/docs/api/chrome-command-line-switches
// used for Kerberos support
// Usage e.g. MACOS
// $ Franz.app/Contents/MacOS/Franz --auth-server-whitelist *.mydomain.com --auth-negotiate-delegate-whitelist *.mydomain.com
const argv = require('minimist')(process.argv.slice(1));

if (argv['auth-server-whitelist']) {
  app.commandLine.appendSwitch('auth-server-whitelist', argv['auth-server-whitelist']);
}
if (argv['auth-negotiate-delegate-whitelist']) {
  app.commandLine.appendSwitch('auth-negotiate-delegate-whitelist', argv['auth-negotiate-delegate-whitelist']);
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', () => {
  // force app to live in /Applications
  enforceMacOSAppLocation();

  // Register App URL
  app.setAsDefaultProtocolClient('franz');

  if (isDevMode) {
    app.setAsDefaultProtocolClient('franz-dev');
  }

  if (process.platform === 'win32') {
    app.setUserTasks([{
      program: process.execPath,
      arguments: `${isDevMode ? `${__dirname} ` : ''}--reset-window`,
      iconPath: asarPath(path.join(isDevMode ? `${__dirname}../src/` : __dirname, 'assets/images/taskbar/win32/display.ico')),
      iconIndex: 0,
      title: 'Move Franz to Current Display',
      description: 'Restore the position and size of Franz',
    }, {
      program: process.execPath,
      arguments: `${isDevMode ? `${__dirname} ` : ''}--quit`,
      iconIndex: 0,
      title: 'Quit Franz',
    }]);
  }

  createWindow();
});

// This is the worst possible implementation as the webview.webContents based callback doesn't work 🖕
// TODO: rewrite to handle multiple login calls
const noop = () => null;
let authCallback = noop;

app.on('login', (event, webContents, request, authInfo, callback) => {
  authCallback = callback;
  debug('browser login event', authInfo);
  event.preventDefault();

  if (!authInfo.isProxy && authInfo.scheme === 'basic') {
    debug('basic auth handler', authInfo);
    basicAuthHandler(mainWindow, authInfo);
  }
});

// TODO: evaluate if we need to store the authCallback for every service
ipcMain.on('feature-basic-auth-credentials', (e, { user, password }) => {
  debug('Received basic auth credentials', user, '********');

  authCallback(user, password);
  authCallback = noop;
});

ipcMain.on('feature-basic-auth-cancel', () => {
  debug('Cancel basic auth');

  authCallback(null);
  authCallback = noop;
});

// Quit when all windows are closed.
app.on('window-all-closed', () => {
  // On OS X it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (settings.get('runInBackground') === undefined
    || settings.get('runInBackground')) {
    debug('Window: all windows closed, quit app');
    app.quit();
  } else {
    debug('Window: don\'t quit app');
  }
});

app.on('before-quit', () => {
  willQuitApp = true;
});

app.on('activate', () => {
  // On OS X it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (mainWindow === null) {
    createWindow();
  } else {
    mainWindow.show();
  }
});

app.on('web-contents-created', (createdEvent, contents) => {
  contents.on('new-window', (event, url, frameNme, disposition) => {
    if (disposition === 'foreground-tab') event.preventDefault();
  });
});

app.on('will-finish-launching', () => {
  // Protocol handler for macOS
  app.on('open-url', (event, url) => {
    event.preventDefault();

    onDidLoad((window) => {
      debug('open-url event', url);
      handleDeepLink(window, url);
    });
  });
});
<|endoftext|>


<commit_before>'use strict';

/**
 * simplecached: client.
 * (C) 2013 Alex Fernández.
 */

// requires
require('prototypes');
var net = require('net');
var Log = require('log');
var testing = require('testing');
var serverLib = require('./server.js');

// globals
var log = new Log('info');

// constants
var DEFAULT_PORT = 11311;

/**
 * Client for simplecached.
 */
var Client = function(port, callback)
{
	// self-reference
	var self = this;

	// attributes
	var client;
	var pending;

	// init
	init();

	/**
	 * Init the connection.
	 */
	function init()
	{
		var options = {
			port: port || DEFAULT_PORT,
			host: 'localhost',
		};
		client = net.connect(options, callback);
		client.on('data', receiveData);
		client.on('error', receiveError);
		client.on('end', end);
	}

	/**
	 * Receive some data.
	 */
	function receiveData(data)
	{
		var message = String(data).trim();
		log.debug('Received %s', message);
		if (!pending)
		{
			log.error('No pending function for message %s', message);
			return;
		}
		var callback = pending;
		pending = null;
		callback(null, message);
	}

	/**
	 * Receive a connection error.
	 */
	function receiveError(error)
	{
		if (!pending)
		{
			log.error('No pending function for error %s', error);
			return;
		}
		pending(error);
		pending = null;
	}

	/**
	 * End the connection.
	 */
	function end()
	{
		log.debug('Connection finished');
		if (pending)
		{
			pending(null);
		}
	}

	/**
	 * Get a value from the remote simplecached.
	 */
	self.get = function(key, callback)
	{
		client.write('get ' + key + '\r\n');
		pending = function(error, result)
		{
			if (error)
			{
				return callback(error);
			}
			if (result == 'END')
			{
				return callback(null);
			}
			if (!result.startsWith('VALUE '))
			{
				log.error('Invalid line %s', result);
				callback('Invalid result');
			}
			var value = result.substringFrom('VALUE ');
			callback(null, value);
		};
	};

	/**
	 * Set a value in the remote simplecached.
	 */
	self.set = function(key, value, callback)
	{
		client.write('set ' + key + ' ' + value + '\r\n');
		pending = function(error, result)
		{
			return callback(error, result == 'STORED');
		};
	};

	/**
	 * Delete a value in the remote simplecached.
	 */
	self.delete = function(key, callback)
	{
		client.write('delete ' + key + '\r\n');
		pending = function(error, result)
		{
			return callback(error, result == 'DELETED');
		};
	};

	/**
	 * Close the connection.
	 */
	self.close = function(callback)
	{
		pending = callback;
		client.end();
	};
};

/**
 * Test the client.
 */
function testClient(callback)
{
	var port = 11312;
	var server = new serverLib.Server(port, function(error)
	{
		testing.check(error, 'Error creating server', callback);
		var client = new Client(port, function(error)
		{
			testing.check(error, 'Error creating client', callback);
			var key = 'test' + Math.random();
			var value = 'naïve value';
			client.set(key, value, function(error, result)
			{
				testing.check(error, 'Error setting value', callback);
				testing.assertEquals(result, true, 'Invalid set', callback);
				client.get(key, function(error, result)
				{
					testing.check(error, 'Error getting value', callback);
					testing.assertEquals(result, value, 'Different value', callback);
					client.delete(key, function(error, result)
					{
						testing.check(error, 'Error deleting value', callback);
						testing.assertEquals(result, true, 'Invalid set', callback);
						client.close(function(error)
						{
							testing.check(error, 'Error closing client', callback);
							server.close(function(error)
							{
								testing.check(error, 'Error closing server', callback);
								testing.success(callback);
							});
						});
					});
				});
			});
		});
	});
}

/**
 * Run all tests.
 */
exports.test = function(callback)
{
	testing.run([ testClient ], callback);
};

// run tests if invoked directly
if (__filename == process.argv[1])
{
	exports.test(testing.show);
}

<commit_msg>Accept an options object with a host.<commit_after>'use strict';

/**
 * simplecached: client.
 * (C) 2013 Alex Fernández.
 */

// requires
require('prototypes');
var net = require('net');
var Log = require('log');
var testing = require('testing');
var serverLib = require('./server.js');

// globals
var log = new Log('info');

// constants
var DEFAULT_PORT = 11311;

/**
 * Client for simplecached.
 */
var Client = function(options, callback)
{
	// self-reference
	var self = this;

	// attributes
	var client;
	var pending;

	// init
	init();

	/**
	 * Init the connection.
	 */
	function init()
	{
		options = options || {};
		var parsedPort = parseInt(options, 10);
		var params = {
			port: parsedPort || options.port || DEFAULT_PORT,
			host: 'localhost',
		};
		client = net.connect(params, callback);
		client.on('data', receiveData);
		client.on('error', receiveError);
		client.on('end', end);
	}

	/**
	 * Receive some data.
	 */
	function receiveData(data)
	{
		var message = String(data).trim();
		log.debug('Received %s', message);
		if (!pending)
		{
			log.error('No pending function for message %s', message);
			return;
		}
		var callback = pending;
		pending = null;
		callback(null, message);
	}

	/**
	 * Receive a connection error.
	 */
	function receiveError(error)
	{
		if (!pending)
		{
			log.error('No pending function for error %s', error);
			return;
		}
		pending(error);
		pending = null;
	}

	/**
	 * End the connection.
	 */
	function end()
	{
		log.debug('Connection finished');
		if (pending)
		{
			pending(null);
		}
	}

	/**
	 * Get a value from the remote simplecached.
	 */
	self.get = function(key, callback)
	{
		client.write('get ' + key + '\r\n');
		pending = function(error, result)
		{
			if (error)
			{
				return callback(error);
			}
			if (result == 'END')
			{
				return callback(null);
			}
			if (!result.startsWith('VALUE '))
			{
				log.error('Invalid line %s', result);
				callback('Invalid result');
			}
			var value = result.substringFrom('VALUE ');
			callback(null, value);
		};
	};

	/**
	 * Set a value in the remote simplecached.
	 */
	self.set = function(key, value, callback)
	{
		client.write('set ' + key + ' ' + value + '\r\n');
		pending = function(error, result)
		{
			return callback(error, result == 'STORED');
		};
	};

	/**
	 * Delete a value in the remote simplecached.
	 */
	self.delete = function(key, callback)
	{
		client.write('delete ' + key + '\r\n');
		pending = function(error, result)
		{
			return callback(error, result == 'DELETED');
		};
	};

	/**
	 * Close the connection.
	 */
	self.close = function(callback)
	{
		pending = callback;
		client.end();
	};
};

/**
 * Test the client.
 */
function testClient(callback)
{
	var port = 11312;
	var server = new serverLib.Server(port, function(error)
	{
		testing.check(error, 'Error creating server', callback);
		var client = new Client(port, function(error)
		{
			testing.check(error, 'Error creating client', callback);
			var key = 'test' + Math.random();
			var value = 'naïve value';
			client.set(key, value, function(error, result)
			{
				testing.check(error, 'Error setting value', callback);
				testing.assertEquals(result, true, 'Invalid set', callback);
				client.get(key, function(error, result)
				{
					testing.check(error, 'Error getting value', callback);
					testing.assertEquals(result, value, 'Different value', callback);
					client.delete(key, function(error, result)
					{
						testing.check(error, 'Error deleting value', callback);
						testing.assertEquals(result, true, 'Invalid set', callback);
						client.close(function(error)
						{
							testing.check(error, 'Error closing client', callback);
							server.close(function(error)
							{
								testing.check(error, 'Error closing server', callback);
								testing.success(callback);
							});
						});
					});
				});
			});
		});
	});
}

/**
 * Run all tests.
 */
exports.test = function(callback)
{
	testing.run([ testClient ], callback);
};

// run tests if invoked directly
if (__filename == process.argv[1])
{
	exports.test(testing.show);
}

<|endoftext|>


<commit_before>import ModelObj from './model-obj.vue'
import ModelThree from './model-three.vue'
import ModelStl from './model-stl.vue'

const components = [
    ModelObj,
    ModelThree,
    ModelStl
]

const install = ( Vue ) => {
    components.map( component => {
        Vue.component( component.name, component );
    } );
};

if ( typeof window !== 'undefined' && window.Vue ) {
    install( window.Vue );
};

export { ModelObj, ModelThree }
<commit_msg>Updated src/index.js, export model-stl and model-collada<commit_after>import ModelObj from './model-obj.vue'
import ModelThree from './model-three.vue'
import ModelStl from './model-stl.vue'
import ModelCollada from './model-collada.vue'

const components = [
    ModelObj,
    ModelThree,
    ModelStl,
    ModelCollada
]

const install = ( Vue ) => {
    components.map( component => {
        Vue.component( component.name, component );
    } );
};

if ( typeof window !== 'undefined' && window.Vue ) {
    install( window.Vue );
};

export { ModelObj, ModelThree, ModelStl, ModelCollada }
<|endoftext|>


<commit_before>'use strict';

require("bootstrap-loader");
import * as agave from '../node_modules/agave-file-browser/AgaveFileBrowser.js';
import 'jstree';

// Require these files so they get copied to dist
require('../index.html');
require("../img/nav-header.png");
require('../css/imicrobe.css');
//require('../css/readable.min.css');
require('../css/spinner.css');

// Load config file
var config = require('../config.json');

// Start up Elm app
var Elm = require('./Main.elm');
var mountNode = document.getElementById('main');
var app = Elm.Main.embed(mountNode, {
    config: config,
    session: localStorage.session || ""
});

// Initial Google Maps and define Elm ports
var GoogleMapsLoader = require("google-maps");
GoogleMapsLoader.KEY = config.googleApiKey;
var Google;
GoogleMapsLoader.load(function(google) {
    Google = google;
});

app.ports.loadMap.subscribe(function(model) {
    var mapDiv = document.getElementsByTagName('gmap')[0];

    var myLatlng = new Google.maps.LatLng(model.lat, model.lng);
    var mapOptions = {
      zoom: 6,
      center: myLatlng
    };
    var gmap = new Google.maps.Map(mapDiv, mapOptions);
    app.ports.receiveMap.send(gmap);
});

app.ports.setCenter.subscribe(function(model) {
    var myLatlng = new Google.maps.LatLng(model.center.lat, model.center.lng);
    model.gmap.setCenter(myLatlng);
});

// Define ports for storing/watching session
app.ports.storeSession.subscribe(function(session) {
    console.log("storeSession: ", session);
    localStorage.session = session;
});

window.addEventListener("storage",
    function(event) {
        if (event.storageArea === localStorage && event.key === "session") {
            console.log("storage listener: ", event.newValue);
            app.ports.onSessionChange.send(event.newValue);
        }
    },
    false
);

var fileBrowser;
app.ports.createFileBrowser.subscribe(function(input) { // TODO get username/token from localStorage.session instead of passing in
    if (typeof fileBrowser == 'undefined') {
        fileBrowser = new agave.AgaveFileBrowser({
            elementId: "file-browser",
            baseUrl:   "https://agave.iplantc.org/files/v2/listings", // TODO move base URL into config.json
            path:      input.username,
            authToken: input.token,
            selectCallback: function(node) {
                console.log("selected: ", node);
                app.ports.onFileSelect.send({
                    id: input.id,
                    username: input.username,
                    token: input.token,
                    path: node.id
                });
            }
        });
    }

    $('#file-browser-dialog').modal('show');
});
<commit_msg>Reformat comments<commit_after>'use strict';

require("bootstrap-loader");
import * as agave from '../node_modules/agave-file-browser/AgaveFileBrowser.js';
import 'jstree';

// Require these files so they get copied to dist
require('../index.html');
require("../img/nav-header.png");
require('../css/imicrobe.css');
//require('../css/readable.min.css');
require('../css/spinner.css');

// Load config file
var config = require('../config.json');

// Start up Elm app
var Elm = require('./Main.elm');
var mountNode = document.getElementById('main');
var app = Elm.Main.embed(mountNode, {
    config: config,
    session: localStorage.session || ""
});

/*
 * Initialize Google Maps and define ports
 */

var GoogleMapsLoader = require("google-maps");
GoogleMapsLoader.KEY = config.googleApiKey;
var Google;
GoogleMapsLoader.load(function(google) {
    Google = google;
});

app.ports.loadMap.subscribe(function(model) {
    var mapDiv = document.getElementsByTagName('gmap')[0];

    var myLatlng = new Google.maps.LatLng(model.lat, model.lng);
    var mapOptions = {
      zoom: 6,
      center: myLatlng
    };
    var gmap = new Google.maps.Map(mapDiv, mapOptions);
    app.ports.receiveMap.send(gmap);
});

app.ports.setCenter.subscribe(function(model) {
    var myLatlng = new Google.maps.LatLng(model.center.lat, model.center.lng);
    model.gmap.setCenter(myLatlng);
});

/*
 * Define ports for storing/watching session
 */

app.ports.storeSession.subscribe(function(session) {
    console.log("storeSession: ", session);
    localStorage.session = session;
});

window.addEventListener("storage",
    function(event) {
        if (event.storageArea === localStorage && event.key === "session") {
            console.log("storage listener: ", event.newValue);
            app.ports.onSessionChange.send(event.newValue);
        }
    },
    false
);

/*
 * Define ports for Agave File Browser
 */

var fileBrowser;
app.ports.createFileBrowser.subscribe(function(input) { // TODO get username/token from localStorage.session instead of passing in
    if (typeof fileBrowser == 'undefined') {
        fileBrowser = new agave.AgaveFileBrowser({
            elementId: "file-browser",
            baseUrl:   "https://agave.iplantc.org/files/v2/listings", // TODO move base URL into config.json
            path:      input.username,
            authToken: input.token,
            selectCallback: function(node) {
                console.log("selected: ", node);
                app.ports.onFileSelect.send({
                    id: input.id,
                    username: input.username,
                    token: input.token,
                    path: node.id
                });
            }
        });
    }

    $('#file-browser-dialog').modal('show');
});<|endoftext|>


<commit_before>/**
 * Created by Cristian García on 6/29/2014.
 */

var R = ramda,
    L = LambdaJS,
    N = {};

/**
 * @class
 * @property {number} val
 */
function Val (val) {
    this.val = val;
}
N.Val = Val;
    /**
 *
 * @class
 * @property {Val} w
 * @property {LinearNeuron} source
 * @property {LinearNeuron} target
 * @param {LinearNeuron} source
 */
function LinearWeight (source, target, val) {

    this.w = new Val (val || Math.random ());
    this.source = source;
    this.target = target;
}
N.LinearWeight = LinearWeight;

/**
 * @returns {number}
 */
LinearWeight.prototype.filterSignal = function filterSignal() {
    return this.source.f() * this.w.val;
};

/**
 * @class
 * @property {Val} z
 * @property {Val} y
 * @property {LinearWeight[]} sources
 * @property {LinearWeight[]} targets
 */
function LinearNeuron () {
    this.z = new Val (undefined);
    this.y = new Val (undefined);
    this.sources = [];
    this.targets = [];
}

N.LinearNeuron = LinearNeuron;

/**
 * @returns {number}
 */

LinearNeuron.prototype.f =  function f () {
    return  this.y.val ||
            (this.y.val = this.z.val = R.foldl (addSignals, 0, this.sources));
};

//Layers
var LinearLayer = N.LinearLayer = function LinearLayer (n) {

};

//Activation Functions
/**
 * @param {number} x
 * @returns {number}
 */
function sigmoid (x) {
    return 1 / (1 + Math.exp (-x));
}

function addSignals (acc, weight){
    return acc + weight.filterSignal();
}<commit_msg>ChangeName<commit_after>/**
 * Created by Cristian García and Carlos Alvarez on 6/29/2014.
 */

var R = ramda,
    L = LambdaJS,
    N = {};

/**
 * @class
 * @property {number} val
 */
function Val (val) {
    this.val = val;
}
N.Val = Val;
    /**
 *
 * @class
 * @property {Val} w
 * @property {LinearNeuron} source
 * @property {LinearNeuron} target
 * @param {LinearNeuron} source
 */
function LinearWeight (source, target, val) {

    this.w = new Val (val || Math.random ());
    this.source = source;
    this.target = target;
}
N.LinearWeight = LinearWeight;

/**
 * @returns {number}
 */
LinearWeight.prototype.filterSignal = function filterSignal() {
    return this.source.f() * this.w.val;
};

/**
 * @class
 * @property {Val} z
 * @property {Val} y
 * @property {LinearWeight[]} sources
 * @property {LinearWeight[]} targets
 */
function LinearNeuron () {
    this.z = new Val (undefined);
    this.y = new Val (undefined);
    this.sources = [];
    this.targets = [];
}

N.LinearNeuron = LinearNeuron;

/**
 * @returns {number}
 */

LinearNeuron.prototype.f =  function f () {
    return  this.y.val ||
            (this.y.val = this.z.val = R.foldl (addSignals, 0, this.sources));
};

//Layers
var LinearLayer = N.LinearLayer = function LinearLayer (n) {

};

//Activation Functions
/**
 * @param {number} x
 * @returns {number}
 */
function sigmoid (x) {
    return 1 / (1 + Math.exp (-x));
}

function addSignals (acc, weight){
    return acc + weight.filterSignal();
}<|endoftext|>


<commit_before>var request = require('request');

var baseUrl = 'http://api.hubapi.com/';

function Client() {
  var self = this;

  self.token;
  self.key;

  function useToken (token) {
    if (!token || typeof token !== 'string') { return cb(new Error("You must provide a token.")); }
    self.token = token;
  }

  function useKey (key) {
    if (!key || typeof key !== 'string') { return cb(new Error("You must provide a key.")); }
    self.key = key;
  }

  var contacts = {
    get: function(options, cb) {
      if (typeof options === 'function') {
        cb = options;
        options = {};
      }
      var call = {
        method: 'GET',
        url: baseUrl + 'contacts/v1/lists/all/contacts/all',
        qs: options
      };
      sendRequest(call, cb);
    }
  };

  var lists = {
    get: function(options, cb) {
      if (typeof options === 'function') {
        cb = options;
        options = {};
      }
      var call = {
        method: 'GET',
        url: baseUrl + 'contacts/v1/lists',
        qs: options
      };
      sendRequest(call, cb);
    }
  };

  var campaigns = {
    get: function(options, cb) {
      if (typeof options === 'function') {
        cb = options;
        options = {};
      }
      var call = {
        method: 'GET',
        url: baseUrl + '/email/public/v1/campaigns',
        qs: options
      };
      sendRequest(call, cb);
    },
    getOne: function(id, cb) {
      if (!id || typeof id === "function") { return cb(new Error("id parameter must be provided."))}
      var call = {
        method: 'GET',
        url: baseUrl + '/email/public/v1/campaigns/' + id
      };
      sendRequest(call, cb);
    }
  };

  function sendRequest (call, cb) {
    if (!self.key && !self.token) { return cb(new Error("You need to provide either a token or a key.")); }

    call.qs = call.qs || {};
    if (self.key) {
      call.qs.hapikey = self.key;
    } else if (self.token) {
      call.qs.access_token = self.token;
    }

    console.log(call);
    request(call, handleResponse(cb));
  }

  function handleResponse (cb) {
    return function (err, res, data) {
      if (err) { return cb(err); }

      if (typeof data === 'string') {
        try {
          var parsed = JSON.parse(data);
          data = parsed;
        } catch (e) {
          console.log('error parsing response');
        }
      }
      //if (data.error) { return cb(new Error(data.error)); }
      //if (data && data.length && data.length > 0 && data[0].error_message) { return cb(new Error(data[0].error_message)); }
      data.statusCode = res.statusCode;
      return cb(null, data);
    }
  }

  return {
    campaigns: campaigns,
    contacts: contacts,
    lists: lists,
    useToken: useToken,
    useKey: useKey
  }
}

module.exports = Client;
<commit_msg>- add the getOne and get calls for lists - add stubs for other list operations<commit_after>var request = require('request');

var baseUrl = 'http://api.hubapi.com/';

function Client() {
  var self = this;

  self.token;
  self.key;

  function useToken (token) {
    if (!token || typeof token !== 'string') { return cb(new Error("You must provide a token.")); }
    self.token = token;
  }

  function useKey (key) {
    if (!key || typeof key !== 'string') { return cb(new Error("You must provide a key.")); }
    self.key = key;
  }

  var contacts = {
    get: function(options, cb) {
      if (typeof options === 'function') {
        cb = options;
        options = {};
      }
      var call = {
        method: 'GET',
        url: baseUrl + 'contacts/v1/lists/all/contacts/all',
        qs: options
      };
      sendRequest(call, cb);
    }
  };

  var lists = {
    get: function(options, cb) {
      if (typeof options === 'function') {
        cb = options;
        options = {};
      }
      var call = {
        method: 'GET',
        url: baseUrl + 'contacts/v1/lists',
        qs: options
      };
      sendRequest(call, cb);
    },
    post: notReadyError,
    getOne: function(id, cb) {
      if (!id || typeof(id) === 'function') { return cb(new Error("id parameter must be provided.")); }
      var call = {
        method: 'GET',
        url: baseUrl + 'contacts/v1/lists/' + id,
      };
      sendRequest(call, cb);
    },
    put: notReadyError,
    del: notReadyError,
    getContacts: function(id, options, cb) {
      if (!id || typeof(id) === 'function') { return cb(new Error("id parameter must be provided.")); }
      if (typeof(options) === 'function') {
        cb = options;
        options = {};
      }
      var call = {
        method: 'GET',
        url: baseUrl + 'contacts/v1/lists/' + id + '/contacts/all',
        qs: options
      };
      sendRequest(call, cb);
    }
  };

  var campaigns = {
    get: function(options, cb) {
      if (typeof options === 'function') {
        cb = options;
        options = {};
      }
      var call = {
        method: 'GET',
        url: baseUrl + '/email/public/v1/campaigns',
        qs: options
      };
      sendRequest(call, cb);
    },
    getOne: function(id, cb) {
      if (!id || typeof id === "function") { return cb(new Error("id parameter must be provided."))}
      var call = {
        method: 'GET',
        url: baseUrl + '/email/public/v1/campaigns/' + id
      };
      sendRequest(call, cb);
    }
  };

  function sendRequest (call, cb) {
    if (!self.key && !self.token) { return cb(new Error("You need to provide either a token or a key.")); }

    call.qs = call.qs || {};
    if (self.key) {
      call.qs.hapikey = self.key;
    } else if (self.token) {
      call.qs.access_token = self.token;
    }

    console.log(call);
    request(call, handleResponse(cb));
  }

  function handleResponse (cb) {
    return function (err, res, data) {
      if (err) { return cb(err); }

      if (typeof data === 'string') {
        try {
          var parsed = JSON.parse(data);
          data = parsed;
        } catch (e) {
          console.log('error parsing response');
        }
      }
      //if (data.error) { return cb(new Error(data.error)); }
      //if (data && data.length && data.length > 0 && data[0].error_message) { return cb(new Error(data[0].error_message)); }
      data.statusCode = res.statusCode;
      return cb(null, data);
    }
  }

  function notReadyError () {
    var cb = arguments[arguments.length - 1];
    return cb(new Error("this call is not ready yet."));
  }

  return {
    campaigns: campaigns,
    contacts: contacts,
    lists: lists,
    useToken: useToken,
    useKey: useKey
  }
}

module.exports = Client;
<|endoftext|>


<commit_before>import React from 'react'
import PropTypes from 'prop-types'
import InputMask from 'inputmask-core'

var KEYCODE_Z = 90
var KEYCODE_Y = 89

function isUndo(e) {
  return (e.ctrlKey || e.metaKey) && e.keyCode === (e.shiftKey ? KEYCODE_Y : KEYCODE_Z)
}

function isRedo(e) {
  return (e.ctrlKey || e.metaKey) && e.keyCode === (e.shiftKey ? KEYCODE_Z : KEYCODE_Y)
}

function getSelection (el) {
  var start, end, rangeEl, clone

  if (el.selectionStart !== undefined) {
    start = el.selectionStart
    end = el.selectionEnd
  }
  else {
    try {
      el.focus()
      rangeEl = el.createTextRange()
      clone = rangeEl.duplicate()

      rangeEl.moveToBookmark(document.selection.createRange().getBookmark())
      clone.setEndPoint('EndToStart', rangeEl)

      start = clone.text.length
      end = start + rangeEl.text.length
    }
    catch (e) { /* not focused or not visible */ }
  }

  return { start, end }
}

function setSelection(el, selection) {
  var rangeEl

  try {
    if (el.selectionStart !== undefined) {
      el.focus()
      el.setSelectionRange(selection.start, selection.end)
    }
    else {
      el.focus()
      rangeEl = el.createTextRange()
      rangeEl.collapse(true)
      rangeEl.moveStart('character', selection.start)
      rangeEl.moveEnd('character', selection.end - selection.start)
      rangeEl.select()
    }
  }
  catch (e) { /* not focused or not visible */ }
}

class MaskedInput extends React.Component {
  constructor(props) {
    super(props)

    this._onChange = this._onChange.bind(this)
    this._onKeyDown = this._onKeyDown.bind(this)
    this._onPaste = this._onPaste.bind(this)
    this._onKeyPress = this._onKeyPress.bind(this)
  }

  componentWillMount() {
    var options = {
      pattern: this.props.mask,
      value: this.props.value,
      formatCharacters: this.props.formatCharacters
    }
    if (this.props.placeholderChar) {
      options.placeholderChar = this.props.placeholderChar
    }
    this.mask = new InputMask(options)
  }

  componentWillReceiveProps(nextProps) {
    if (this.props.mask !== nextProps.mask && this.props.value !== nextProps.mask) {
      // if we get a new value and a new mask at the same time
      // check if the mask.value is still the initial value
      // - if so use the nextProps value
      // - otherwise the `this.mask` has a value for us (most likely from paste action)
      if (this.mask.getValue() === this.mask.emptyValue) {
        this.mask.setPattern(nextProps.mask, {value: nextProps.value})
      }
      else {
        this.mask.setPattern(nextProps.mask, {value: this.mask.getRawValue()})
      }
    }
    else if (this.props.mask !== nextProps.mask) {
      this.mask.setPattern(nextProps.mask, {value: this.mask.getRawValue()})
    }
    else if (this.props.value !== nextProps.value) {
      this.mask.setValue(nextProps.value)
    }
  }

  componentWillUpdate(nextProps, nextState) {
    if (nextProps.mask !== this.props.mask) {
      this._updatePattern(nextProps)
    }
  }

  componentDidUpdate(prevProps) {
    if (prevProps.mask !== this.props.mask && this.mask.selection.start) {
      this._updateInputSelection()
    }
  }

  _updatePattern(props) {
    this.mask.setPattern(props.mask, {
      value: this.mask.getRawValue(),
      selection: getSelection(this.input)
    })
  }

  _updateMaskSelection() {
    this.mask.selection = getSelection(this.input)
  }

  _updateInputSelection() {
    setSelection(this.input, this.mask.selection)
  }

  _onChange(e) {
    // console.log('onChange', JSON.stringify(getSelection(this.input)), e.target.value)

    var maskValue = this.mask.getValue()
    if (e.target.value !== maskValue) {
      // Cut or delete operations will have shortened the value
      if (e.target.value.length < maskValue.length) {
        var sizeDiff = maskValue.length - e.target.value.length
        this._updateMaskSelection()
        this.mask.selection.end = this.mask.selection.start + sizeDiff
        this.mask.backspace()
      }
      var value = this._getDisplayValue()
      e.target.value = value
      if (value) {
        this._updateInputSelection()
      }
    }
    if (this.props.onChange) {
      this.props.onChange(e)
    }
  }

  _onKeyDown(e) {
    // console.log('onKeyDown', JSON.stringify(getSelection(this.input)), e.key, e.target.value)

    if (isUndo(e)) {
      e.preventDefault()
      if (this.mask.undo()) {
        e.target.value = this._getDisplayValue()
        this._updateInputSelection()
        if (this.props.onChange) {
          this.props.onChange(e)
        }
      }
      return
    }
    else if (isRedo(e)) {
      e.preventDefault()
      if (this.mask.redo()) {
        e.target.value = this._getDisplayValue()
        this._updateInputSelection()
        if (this.props.onChange) {
          this.props.onChange(e)
        }
      }
      return
    }

    if (e.key === 'Backspace') {
      e.preventDefault()
      this._updateMaskSelection()
      if (this.mask.backspace()) {
        var value = this._getDisplayValue()
        e.target.value = value
        if (value) {
          this._updateInputSelection()
        }
        if (this.props.onChange) {
          this.props.onChange(e)
        }
      }
    }
  }

  _onKeyPress(e) {
    // console.log('onKeyPress', JSON.stringify(getSelection(this.input)), e.key, e.target.value)

    // Ignore modified key presses
    // Ignore enter key to allow form submission
    if (e.metaKey || e.altKey || e.ctrlKey || e.key === 'Enter') { return }

    e.preventDefault()
    this._updateMaskSelection()
    if (this.mask.input((e.key || e.data))) {
      e.target.value = this.mask.getValue()
      this._updateInputSelection()
      if (this.props.onChange) {
        this.props.onChange(e)
      }
    }
  }

  _onPaste(e) {
    // console.log('onPaste', JSON.stringify(getSelection(this.input)), e.clipboardData.getData('Text'), e.target.value)

    e.preventDefault()
    this._updateMaskSelection()
    // getData value needed for IE also works in FF & Chrome
    if (this.mask.paste(e.clipboardData.getData('Text'))) {
      e.target.value = this.mask.getValue()
      // Timeout needed for IE
      setTimeout(this._updateInputSelection.bind(this), 0)
      if (this.props.onChange) {
        this.props.onChange(e)
      }
    }
  }

  _getDisplayValue() {
    var value = this.mask.getValue()
    return value === this.mask.emptyValue ? '' : value
  }

  _keyPressPropName() {
    if (typeof navigator !== 'undefined') {
      return navigator.userAgent.match(/Android/i)
      ? 'onBeforeInput'
      : 'onKeyPress'
    }
    return 'onKeyPress'
  }

  _getEventHandlers() {
    return {
      onChange: this._onChange,
      onKeyDown: this._onKeyDown,
      onPaste: this._onPaste,
      [this._keyPressPropName()]: this._onKeyPress
    }
  }

  focus() {
    this.input.focus()
  }

  blur() {
    this.input.blur()
  }

  render() {
    var ref = r => { this.input = r }
    var maxLength = this.mask.pattern.length
    var value = this._getDisplayValue()
    var eventHandlers = this._getEventHandlers()
    var { size = maxLength, placeholder = this.mask.emptyValue } = this.props

    var { placeholderChar, formatCharacters, ...cleanedProps } = this.props // eslint-disable-line
    var inputProps = { ...cleanedProps, ...eventHandlers, ref, maxLength, value, size, placeholder }
    return <input {...inputProps} />
  }
}

MaskedInput.propTypes = {
  mask: PropTypes.string.isRequired,

  formatCharacters: PropTypes.object,
  placeholderChar: PropTypes.string
}

MaskedInput.defaultProps = {
  value: ''
}

export default MaskedInput
<commit_msg>Fix autofill scenarios by using data from onchange events (#112)<commit_after>import React from 'react'
import PropTypes from 'prop-types'
import InputMask from 'inputmask-core'

var KEYCODE_Z = 90
var KEYCODE_Y = 89

function isUndo(e) {
  return (e.ctrlKey || e.metaKey) && e.keyCode === (e.shiftKey ? KEYCODE_Y : KEYCODE_Z)
}

function isRedo(e) {
  return (e.ctrlKey || e.metaKey) && e.keyCode === (e.shiftKey ? KEYCODE_Z : KEYCODE_Y)
}

function getSelection (el) {
  var start, end, rangeEl, clone

  if (el.selectionStart !== undefined) {
    start = el.selectionStart
    end = el.selectionEnd
  }
  else {
    try {
      el.focus()
      rangeEl = el.createTextRange()
      clone = rangeEl.duplicate()

      rangeEl.moveToBookmark(document.selection.createRange().getBookmark())
      clone.setEndPoint('EndToStart', rangeEl)

      start = clone.text.length
      end = start + rangeEl.text.length
    }
    catch (e) { /* not focused or not visible */ }
  }

  return { start, end }
}

function setSelection(el, selection) {
  var rangeEl

  try {
    if (el.selectionStart !== undefined) {
      el.focus()
      el.setSelectionRange(selection.start, selection.end)
    }
    else {
      el.focus()
      rangeEl = el.createTextRange()
      rangeEl.collapse(true)
      rangeEl.moveStart('character', selection.start)
      rangeEl.moveEnd('character', selection.end - selection.start)
      rangeEl.select()
    }
  }
  catch (e) { /* not focused or not visible */ }
}

class MaskedInput extends React.Component {
  constructor(props) {
    super(props)

    this._onChange = this._onChange.bind(this)
    this._onKeyDown = this._onKeyDown.bind(this)
    this._onPaste = this._onPaste.bind(this)
    this._onKeyPress = this._onKeyPress.bind(this)
    this._updateInputSelection = this._updateInputSelection.bind(this)
  }

  componentWillMount() {
    var options = {
      pattern: this.props.mask,
      value: this.props.value,
      formatCharacters: this.props.formatCharacters
    }
    if (this.props.placeholderChar) {
      options.placeholderChar = this.props.placeholderChar
    }
    this.mask = new InputMask(options)
  }

  componentWillReceiveProps(nextProps) {
    if (this.props.mask !== nextProps.mask && this.props.value !== nextProps.mask) {
      // if we get a new value and a new mask at the same time
      // check if the mask.value is still the initial value
      // - if so use the nextProps value
      // - otherwise the `this.mask` has a value for us (most likely from paste action)
      if (this.mask.getValue() === this.mask.emptyValue) {
        this.mask.setPattern(nextProps.mask, {value: nextProps.value})
      }
      else {
        this.mask.setPattern(nextProps.mask, {value: this.mask.getRawValue()})
      }
    }
    else if (this.props.mask !== nextProps.mask) {
      this.mask.setPattern(nextProps.mask, {value: this.mask.getRawValue()})
    }
    else if (this.props.value !== nextProps.value) {
      this.mask.setValue(nextProps.value)
    }
  }

  componentWillUpdate(nextProps, nextState) {
    if (nextProps.mask !== this.props.mask) {
      this._updatePattern(nextProps)
    }
  }

  componentDidUpdate(prevProps) {
    if (prevProps.mask !== this.props.mask && this.mask.selection.start) {
      this._updateInputSelection()
    }
  }

  _updatePattern(props) {
    this.mask.setPattern(props.mask, {
      value: this.mask.getRawValue(),
      selection: getSelection(this.input)
    })
  }

  _updateMaskSelection() {
    this.mask.selection = getSelection(this.input)
  }

  _updateInputSelection() {
    setSelection(this.input, this.mask.selection)
  }

  _onChange(e) {
    // console.log('onChange', JSON.stringify(getSelection(this.input)), e.target.value)

    var maskValue = this.mask.getValue()
    var incomingValue = e.target.value
    if (incomingValue !== maskValue) { // only modify mask if form contents actually changed
      this._updateMaskSelection()
      this.mask.setValue(incomingValue) // write the whole updated value into the mask
      e.target.value = this._getDisplayValue() // update the form with pattern applied to the value
      this._updateInputSelection()
    }

    if (this.props.onChange) {
      this.props.onChange(e)
    }
  }

  _onKeyDown(e) {
    // console.log('onKeyDown', JSON.stringify(getSelection(this.input)), e.key, e.target.value)

    if (isUndo(e)) {
      e.preventDefault()
      if (this.mask.undo()) {
        e.target.value = this._getDisplayValue()
        this._updateInputSelection()
        if (this.props.onChange) {
          this.props.onChange(e)
        }
      }
      return
    }
    else if (isRedo(e)) {
      e.preventDefault()
      if (this.mask.redo()) {
        e.target.value = this._getDisplayValue()
        this._updateInputSelection()
        if (this.props.onChange) {
          this.props.onChange(e)
        }
      }
      return
    }

    if (e.key === 'Backspace') {
      e.preventDefault()
      this._updateMaskSelection()
      if (this.mask.backspace()) {
        var value = this._getDisplayValue()
        e.target.value = value
        if (value) {
          this._updateInputSelection()
        }
        if (this.props.onChange) {
          this.props.onChange(e)
        }
      }
    }
  }

  _onKeyPress(e) {
    // console.log('onKeyPress', JSON.stringify(getSelection(this.input)), e.key, e.target.value)

    // Ignore modified key presses
    // Ignore enter key to allow form submission
    if (e.metaKey || e.altKey || e.ctrlKey || e.key === 'Enter') { return }

    e.preventDefault()
    this._updateMaskSelection()
    if (this.mask.input((e.key || e.data))) {
      e.target.value = this.mask.getValue()
      this._updateInputSelection()
      if (this.props.onChange) {
        this.props.onChange(e)
      }
    }
  }

  _onPaste(e) {
    // console.log('onPaste', JSON.stringify(getSelection(this.input)), e.clipboardData.getData('Text'), e.target.value)

    e.preventDefault()
    this._updateMaskSelection()
    // getData value needed for IE also works in FF & Chrome
    if (this.mask.paste(e.clipboardData.getData('Text'))) {
      e.target.value = this.mask.getValue()
      // Timeout needed for IE
      setTimeout(this._updateInputSelection.bind(this), 0)
      if (this.props.onChange) {
        this.props.onChange(e)
      }
    }
  }

  _getDisplayValue() {
    var value = this.mask.getValue()
    return value === this.mask.emptyValue ? '' : value
  }

  _keyPressPropName() {
    if (typeof navigator !== 'undefined') {
      return navigator.userAgent.match(/Android/i)
      ? 'onBeforeInput'
      : 'onKeyPress'
    }
    return 'onKeyPress'
  }

  _getEventHandlers() {
    return {
      onChange: this._onChange,
      onKeyDown: this._onKeyDown,
      onPaste: this._onPaste,
      [this._keyPressPropName()]: this._onKeyPress
    }
  }

  focus() {
    this.input.focus()
  }

  blur() {
    this.input.blur()
  }

  render() {
    var ref = r => { this.input = r }
    var maxLength = this.mask.pattern.length
    var value = this._getDisplayValue()
    var eventHandlers = this._getEventHandlers()
    var { size = maxLength, placeholder = this.mask.emptyValue } = this.props

    var { placeholderChar, formatCharacters, ...cleanedProps } = this.props // eslint-disable-line
    var inputProps = { ...cleanedProps, ...eventHandlers, ref, maxLength, value, size, placeholder }
    return <input {...inputProps} />
  }
}

MaskedInput.propTypes = {
  mask: PropTypes.string.isRequired,

  formatCharacters: PropTypes.object,
  placeholderChar: PropTypes.string
}

MaskedInput.defaultProps = {
  value: ''
}

export default MaskedInput
<|endoftext|>


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

var parser = require('socket.io-parser');
var encode = parser.encode;
var decode = parser.decode;
var debug = require('debug')('socket.io:client');

/**
 * Module exports.
 */

module.exports = Client;

/**
 * Client constructor.
 *
 * @param {Server} server instance
 * @param {Socket} connection
 * @api private
 */

function Client(server, conn){
  this.server = server;
  this.conn = conn;
  this.id = conn.id;
  this.request = conn.request;
  this.setup();
  this.sockets = [];
  this.nsps = {};
  this.connectBuffer = [];
}

/**
 * Sets up event listeners.
 *
 * @api private
 */

Client.prototype.setup = function(){
  this.onclose = this.onclose.bind(this);
  this.ondata = this.ondata.bind(this);
  this.conn.on('data', this.ondata);
  this.conn.on('close', this.onclose);
};

/**
 * Connects a client to a namespace.
 *
 * @param {String} namespace name
 * @api private
 */

Client.prototype.connect = function(name){
  debug('connecting to namespace %s', name);
  var nsp = this.server.of(name);
  if ('/' != name && !this.nsps['/']) {
    this.connectBuffer.push(name);
    return;
  }

  var self = this;
  var socket = nsp.add(this, function(){
    self.sockets.push(socket);
    self.nsps[nsp.name] = socket;

    if ('/' == nsp.name) {
      self.connectBuffer.forEach(self.connect, self);
      delete self.connectBuffer;
    }
  });
};

/**
 * Disconnects from all namespaces and closes transport.
 *
 * @api private
 */

Client.prototype.disconnect = function(){
  var socket;
  // we don't use a for loop because the length of
  // `sockets` changes upon each iteration
  while (socket = this.sockets.shift()) {
    socket.disconnect();
  }
  this.close();
};

/**
 * Removes a socket. Called by each `Socket`.
 *
 * @api private
 */

Client.prototype.remove = function(socket){
  var i = this.sockets.indexOf(socket);
  if (~i) {
    var nsp = this.sockets[i].nsp.name;
    this.sockets.splice(i, 1);
    delete this.nsps[nsp];
  } else {
    debug('ignoring remove for %s', socket.id);
  }
};

/**
 * Closes the underlying connection.
 *
 * @api private
 */

Client.prototype.close = function(){
  if ('open' == this.conn.readyState) {
    debug('forcing transport close');
    this.conn.close();
    this.onclose('forced server close');
  }
};

/**
 * Writes a packet to the transport.
 *
 * @param {Object} packet object
 * @api private
 */

Client.prototype.packet = function(packet){
  if ('open' == this.conn.readyState) {
    debug('writing packet %j', packet);
    var self = this;
    // encode, then write to engine
    encode(packet, function engineWriter(encodedPacket) {
      self.conn.write(encodedPacket);
    });
  } else {
    debug('ignoring packet write %j', packet);
  }
};

/**
 * Called with incoming transport data.
 *
 * @api private
 */

Client.prototype.ondata = function(data){
  var packet = decode(data);
  debug('got packet %j', packet);

  if (parser.CONNECT == packet.type) {
    this.connect(packet.nsp);
  } else {
    var socket = this.nsps[packet.nsp];
    if (socket) {
      socket.onpacket(packet);
    } else {
      debug('no socket for namespace %s', packet.nsp);
    }
  }
};

/**
 * Called upon transport close.
 *
 * @param {String} reason
 * @api private
 */

Client.prototype.onclose = function(reason){
  debug('client close with reason %s', reason);

  // ignore a potential subsequent `close` event
  this.destroy();

  // `nsps` and `sockets` are cleaned up seamlessly
  this.sockets.forEach(function(socket){
    socket.onclose(reason);
  });
};

/**
 * Cleans up event listeners.
 *
 * @api private
 */

Client.prototype.destroy = function(){
  this.conn.removeListener('data', this.ondata);
  this.conn.removeListener('close', this.onclose);
};
<commit_msg>anonymous function style correct<commit_after>
/**
 * Module dependencies.
 */

var parser = require('socket.io-parser');
var encode = parser.encode;
var decode = parser.decode;
var debug = require('debug')('socket.io:client');

/**
 * Module exports.
 */

module.exports = Client;

/**
 * Client constructor.
 *
 * @param {Server} server instance
 * @param {Socket} connection
 * @api private
 */

function Client(server, conn){
  this.server = server;
  this.conn = conn;
  this.id = conn.id;
  this.request = conn.request;
  this.setup();
  this.sockets = [];
  this.nsps = {};
  this.connectBuffer = [];
}

/**
 * Sets up event listeners.
 *
 * @api private
 */

Client.prototype.setup = function(){
  this.onclose = this.onclose.bind(this);
  this.ondata = this.ondata.bind(this);
  this.conn.on('data', this.ondata);
  this.conn.on('close', this.onclose);
};

/**
 * Connects a client to a namespace.
 *
 * @param {String} namespace name
 * @api private
 */

Client.prototype.connect = function(name){
  debug('connecting to namespace %s', name);
  var nsp = this.server.of(name);
  if ('/' != name && !this.nsps['/']) {
    this.connectBuffer.push(name);
    return;
  }

  var self = this;
  var socket = nsp.add(this, function(){
    self.sockets.push(socket);
    self.nsps[nsp.name] = socket;

    if ('/' == nsp.name) {
      self.connectBuffer.forEach(self.connect, self);
      delete self.connectBuffer;
    }
  });
};

/**
 * Disconnects from all namespaces and closes transport.
 *
 * @api private
 */

Client.prototype.disconnect = function(){
  var socket;
  // we don't use a for loop because the length of
  // `sockets` changes upon each iteration
  while (socket = this.sockets.shift()) {
    socket.disconnect();
  }
  this.close();
};

/**
 * Removes a socket. Called by each `Socket`.
 *
 * @api private
 */

Client.prototype.remove = function(socket){
  var i = this.sockets.indexOf(socket);
  if (~i) {
    var nsp = this.sockets[i].nsp.name;
    this.sockets.splice(i, 1);
    delete this.nsps[nsp];
  } else {
    debug('ignoring remove for %s', socket.id);
  }
};

/**
 * Closes the underlying connection.
 *
 * @api private
 */

Client.prototype.close = function(){
  if ('open' == this.conn.readyState) {
    debug('forcing transport close');
    this.conn.close();
    this.onclose('forced server close');
  }
};

/**
 * Writes a packet to the transport.
 *
 * @param {Object} packet object
 * @api private
 */

Client.prototype.packet = function(packet){
  if ('open' == this.conn.readyState) {
    debug('writing packet %j', packet);
    var self = this;
    // encode, then write to engine
    encode(packet, function (encodedPacket) {
      self.conn.write(encodedPacket);
    });
  } else {
    debug('ignoring packet write %j', packet);
  }
};

/**
 * Called with incoming transport data.
 *
 * @api private
 */

Client.prototype.ondata = function(data){
  var packet = decode(data);
  debug('got packet %j', packet);

  if (parser.CONNECT == packet.type) {
    this.connect(packet.nsp);
  } else {
    var socket = this.nsps[packet.nsp];
    if (socket) {
      socket.onpacket(packet);
    } else {
      debug('no socket for namespace %s', packet.nsp);
    }
  }
};

/**
 * Called upon transport close.
 *
 * @param {String} reason
 * @api private
 */

Client.prototype.onclose = function(reason){
  debug('client close with reason %s', reason);

  // ignore a potential subsequent `close` event
  this.destroy();

  // `nsps` and `sockets` are cleaned up seamlessly
  this.sockets.forEach(function(socket){
    socket.onclose(reason);
  });
};

/**
 * Cleans up event listeners.
 *
 * @api private
 */

Client.prototype.destroy = function(){
  this.conn.removeListener('data', this.ondata);
  this.conn.removeListener('close', this.onclose);
};
<|endoftext|>


<commit_before>import { logoBase64, svgLogo } from './sources';

class Github {
  constructor(options, request) {
    this.options = options || {};
    if (!this.options.user && !this.options.org) {
      throw new Error('options must have `user` or `org` param');
    }
    this.request = request;
    this.options.isUser = this.isUser();
    this.options.name = this.options.isUser
      ? this.options.user
      : this.options.org;
  }

  getBase() {
    return {
      name: 'Github',
      home: `https://github.com/${this.options.name}`,
      logo: {
        svg: svgLogo,
        base64: logoBase64,
      },
    };
  }

  isUser() {
    return !!this.options.user;
  }

  async getUserProfile() {
    const options = this.options;
    const url = `https://api.github.com/users/${options.name}`;
    const headers = {
      'User-Agent': 'representation-source-github',
    };
    if (options.token && options.token.length) {
      headers.Authorization = `token ${options.token}`;
    }
    return this.request({
      url,
      json: true,
      headers,
    });
  }

  async load() {
    const result = this.getBase();
    const profile = await this.getUserProfile();
    result.stats = [
      {
        title: 'repos',
        count: profile.public_repos,
      },
      {
        title: 'gists',
        count: profile.public_gists,
      },
      {
        title: 'followers',
        count: profile.followers,
      },
    ];
    result.profile = profile;
    return result;
  }
}

export default Github;
<commit_msg>export default module as Source<commit_after>import { logoBase64, svgLogo } from './sources';

class Github {
  constructor(options, request) {
    this.options = options || {};
    if (!this.options.user && !this.options.org) {
      throw new Error('options must have `user` or `org` param');
    }
    this.request = request;
    this.options.isUser = this.isUser();
    this.options.name = this.options.isUser
      ? this.options.user
      : this.options.org;
  }

  getBase() {
    return {
      name: 'Github',
      home: `https://github.com/${this.options.name}`,
      logo: {
        svg: svgLogo,
        base64: logoBase64,
      },
    };
  }

  isUser() {
    return !!this.options.user;
  }

  async getUserProfile() {
    const options = this.options;
    const url = `https://api.github.com/users/${options.name}`;
    const headers = {
      'User-Agent': 'representation-source-github',
    };
    if (options.token && options.token.length) {
      headers.Authorization = `token ${options.token}`;
    }
    return this.request({
      url,
      json: true,
      headers,
    });
  }

  async load() {
    const result = this.getBase();
    const profile = await this.getUserProfile();
    result.stats = [
      {
        title: 'repos',
        count: profile.public_repos,
      },
      {
        title: 'gists',
        count: profile.public_gists,
      },
      {
        title: 'followers',
        count: profile.followers,
      },
    ];
    result.profile = profile;
    return result;
  }
}

export default Github;
export { Github as Source };
<|endoftext|>


<commit_before>let ShortKey = {}
let mapFunctions = {}
let objAvoided = []
let elementAvoided = []
let keyPressed = false

ShortKey.install = (Vue, options) => {
  elementAvoided = [...(options && options.prevent ? options.prevent : [])]
  Vue.directive('shortkey', {
    bind: (el, binding, vnode) => {
      // Mapping the commands
      let b = typeof binding.value === 'string' ? JSON.parse(binding.value.replace(/\'/gi, '"')) : binding.value
      let pushButton = binding.modifiers.push === true
      let avoid = binding.modifiers.avoid === true
      let focus = binding.modifiers.focus === true
      let once = binding.modifiers.once === true
      if (avoid) {
        objAvoided.push(el)
      } else {
        mappingFunctions({b, pushButton, once, focus, el: vnode.elm})
      }
    },
    unbind: (el, binding) => {
      let b = []
      b = typeof binding.value === 'string' ? JSON.parse(binding.value.replace(/\'/gi, '"')) : binding.value
      if (b instanceof Array) {
        const k = b.join('')
        const idxElm = mapFunctions[k].el.indexOf(el)
        if (mapFunctions[k].el.length > 1 && idxElm > -1) {
          mapFunctions[k].el.splice(idxElm, 1)
        } else {
          delete mapFunctions[k]
        }
      } else {
        for (let item in b) {
          const k = b[item].join('')
          const idxElm = mapFunctions[k].el.indexOf(el)
          if (mapFunctions[k].el.length > 1 && idxElm > -1) {
            mapFunctions[k].el.splice(idxElm, 1)
          } else {
            delete mapFunctions[k]
          }
        }
      }

      objAvoided = objAvoided.filter((itm) => {
        return !itm === el;
      })
    }
  })
}

ShortKey.decodeKey = (pKey) => {
  let k = ''
  if (pKey.key === 'Shift' || pKey.shiftKey) { k += 'shift' }
  if (pKey.key === 'Control' || pKey.ctrlKey) { k += 'ctrl' }
  if (pKey.key === 'Meta'|| pKey.metaKey) { k += 'meta' }
  if (pKey.key === 'Alt' || pKey.altKey) { k += 'alt' }
  if (pKey.key === 'ArrowUp') { k += 'arrowup' }
  if (pKey.key === 'ArrowLeft') { k += 'arrowleft' }
  if (pKey.key === 'ArrowRight') { k += 'arrowright' }
  if (pKey.key === 'ArrowDown') { k += 'arrowdown' }
  if (pKey.key === 'AltGraph') { k += 'altgraph' }
  if (pKey.key === 'Escape') { k += 'esc' }
  if (pKey.key === 'Enter') { k += 'enter' }
  if (pKey.key === 'Tab') { k += 'tab' }
  if (pKey.key === ' ') { k += 'space' }
  if (pKey.key === 'PageUp') { k += 'pageup' }
  if (pKey.key === 'PageDown') { k += 'pagedown' }
  if (pKey.key === 'Home') { k += 'home' }
  if (pKey.key === 'End') { k += 'end' }
  if (pKey.key === 'Delete') { k += 'del' }
  if (pKey.key === 'Insert') { k += 'insert' }
  if (pKey.key === 'NumLock') { k += 'numlock' }
  if (pKey.key === 'CapsLock') { k += 'capslock' }
  if (pKey.key === 'Pause') { k += 'pause' }
  if (pKey.key === 'ContextMenu') { k += 'contextmenu' }
  if (pKey.key === 'ScrollLock') { k += 'scrolllock' }
  if (pKey.key === 'BrowserHome') { k += 'browserhome' }
  if (pKey.key === 'MediaSelect') { k += 'mediaselect' }
  if ((pKey.key && pKey.key !== ' ' && pKey.key.length === 1) || /F\d{1,2}|\//g.test(pKey.key)) k += pKey.key.toLowerCase()
  return k
}

ShortKey.keyDown = (pKey) => {
  if ((!mapFunctions[pKey].oc && !mapFunctions[pKey].ps) || (mapFunctions[pKey].ps && !keyPressed)) {
    const e = new Event('shortkey', { bubbles: false })
    if (mapFunctions[pKey].key) e.srcKey = mapFunctions[pKey].key
    const elm = mapFunctions[pKey].el
    elm[elm.length - 1].dispatchEvent(e)
  }
}
ShortKey.keyUp = (pKey) => {
  const e = new Event('shortkey', { bubbles: false })
  if (mapFunctions[pKey].key) e.srcKey = mapFunctions[pKey].key
  const elm = mapFunctions[pKey].el
  elm[elm.length - 1].dispatchEvent(e)
}

if (process.env.NODE_ENV !== 'test') {
  ;(function () {
    document.addEventListener('keydown', (pKey) => {
      const decodedKey = ShortKey.decodeKey(pKey)

      // Check evict
      if (filteringElement(pKey)) {
        pKey.preventDefault()
        pKey.stopPropagation()
        if (mapFunctions[decodedKey].fn) {
          ShortKey.keyDown(decodedKey)
          keyPressed = true
        } else if (!keyPressed) {
          const elm = mapFunctions[decodedKey].el
          elm[elm.length - 1].focus()
          keyPressed = true
        }
      }
    }, true)

    document.addEventListener('keyup', (pKey) => {
      const decodedKey = ShortKey.decodeKey(pKey)
      if (filteringElement(pKey)) {
        pKey.preventDefault()
        pKey.stopPropagation()
        if (mapFunctions[decodedKey].oc || mapFunctions[decodedKey].ps) {
          ShortKey.keyUp(decodedKey)
        }
      }
      keyPressed = false
    }, true)
  })()
}

const mappingFunctions = ({b, pushButton, once, focus, el}) => {
  if (b instanceof Array) {
    const k = b.join('')
    const elm = mapFunctions[k] && mapFunctions[k].el ? mapFunctions[k].el : []
    elm.push(el)
    mapFunctions[k] = {
      'ps': pushButton,
      'oc': once,
      'fn': !focus,
      el: elm
    }
  } else {
    for (let item in b) {
      const k = b[item].join('')
      const elm = mapFunctions[k] && mapFunctions[k].el ? mapFunctions[k].el : []
      elm.push(el)
      mapFunctions[k] = {
        'ps': pushButton,
        'oc': once,
        'fn': !focus,
        'key': item,
        el: elm
      }
    }
  }
}

const filteringElement = (pKey) => {
  const decodedKey = ShortKey.decodeKey(pKey)
  const objectAvoid = objAvoided.find(r => r === document.activeElement)
  const elementSeparate = checkElementType()
  const elementTypeAvoid = elementSeparate.avoidedTypes
  const elementClassAvoid = elementSeparate.avoidedClasses
  const filterTypeAvoid = elementTypeAvoid.find(r => r === document.activeElement && document.activeElement.tagName.toLowerCase())
  const filterClassAvoid = elementClassAvoid.find(r => r === '.' + document.activeElement && document.activeElement.className.toLowerCase())
  return !objectAvoid && mapFunctions[decodedKey] && !filterTypeAvoid && !filterClassAvoid
}

const checkElementType = () => {
  let elmTypeAvoid = []
  let elmClassAvoid = []
  elementAvoided.forEach(r => {
    const dotPosition = r.indexOf('.')
    if (dotPosition === 0) {
      elmClassAvoid.push(r)
    } else if (dotPosition > 0) {
      elmTypeAvoid.push(r.split('.')[0])
      elmClassAvoid.push('.' + r.split('.')[1])
    } else {
      elmTypeAvoid.push(r)
    }
  })

  return {avoidedTypes: elmTypeAvoid, avoidedClasses: elmClassAvoid}
}

if (typeof module != 'undefined' && module.exports) {
  module.exports = ShortKey;
} else if (typeof define == 'function' && define.amd) {
  define( function () { return ShortKey; } );
} else {
  window.ShortKey = ShortKey;
}
<commit_msg>Fix filtering prevented elements<commit_after>let ShortKey = {}
let mapFunctions = {}
let objAvoided = []
let elementAvoided = []
let keyPressed = false

ShortKey.install = (Vue, options) => {
  elementAvoided = [...(options && options.prevent ? options.prevent : [])]
  Vue.directive('shortkey', {
    bind: (el, binding, vnode) => {
      // Mapping the commands
      let b = typeof binding.value === 'string' ? JSON.parse(binding.value.replace(/\'/gi, '"')) : binding.value
      let pushButton = binding.modifiers.push === true
      let avoid = binding.modifiers.avoid === true
      let focus = binding.modifiers.focus === true
      let once = binding.modifiers.once === true
      if (avoid) {
        objAvoided.push(el)
      } else {
        mappingFunctions({b, pushButton, once, focus, el: vnode.elm})
      }
    },
    unbind: (el, binding) => {
      let b = []
      b = typeof binding.value === 'string' ? JSON.parse(binding.value.replace(/\'/gi, '"')) : binding.value
      if (b instanceof Array) {
        const k = b.join('')
        const idxElm = mapFunctions[k].el.indexOf(el)
        if (mapFunctions[k].el.length > 1 && idxElm > -1) {
          mapFunctions[k].el.splice(idxElm, 1)
        } else {
          delete mapFunctions[k]
        }
      } else {
        for (let item in b) {
          const k = b[item].join('')
          const idxElm = mapFunctions[k].el.indexOf(el)
          if (mapFunctions[k].el.length > 1 && idxElm > -1) {
            mapFunctions[k].el.splice(idxElm, 1)
          } else {
            delete mapFunctions[k]
          }
        }
      }

      objAvoided = objAvoided.filter((itm) => {
        return !itm === el;
      })
    }
  })
}

ShortKey.decodeKey = (pKey) => {
  let k = ''
  if (pKey.key === 'Shift' || pKey.shiftKey) { k += 'shift' }
  if (pKey.key === 'Control' || pKey.ctrlKey) { k += 'ctrl' }
  if (pKey.key === 'Meta'|| pKey.metaKey) { k += 'meta' }
  if (pKey.key === 'Alt' || pKey.altKey) { k += 'alt' }
  if (pKey.key === 'ArrowUp') { k += 'arrowup' }
  if (pKey.key === 'ArrowLeft') { k += 'arrowleft' }
  if (pKey.key === 'ArrowRight') { k += 'arrowright' }
  if (pKey.key === 'ArrowDown') { k += 'arrowdown' }
  if (pKey.key === 'AltGraph') { k += 'altgraph' }
  if (pKey.key === 'Escape') { k += 'esc' }
  if (pKey.key === 'Enter') { k += 'enter' }
  if (pKey.key === 'Tab') { k += 'tab' }
  if (pKey.key === ' ') { k += 'space' }
  if (pKey.key === 'PageUp') { k += 'pageup' }
  if (pKey.key === 'PageDown') { k += 'pagedown' }
  if (pKey.key === 'Home') { k += 'home' }
  if (pKey.key === 'End') { k += 'end' }
  if (pKey.key === 'Delete') { k += 'del' }
  if (pKey.key === 'Insert') { k += 'insert' }
  if (pKey.key === 'NumLock') { k += 'numlock' }
  if (pKey.key === 'CapsLock') { k += 'capslock' }
  if (pKey.key === 'Pause') { k += 'pause' }
  if (pKey.key === 'ContextMenu') { k += 'contextmenu' }
  if (pKey.key === 'ScrollLock') { k += 'scrolllock' }
  if (pKey.key === 'BrowserHome') { k += 'browserhome' }
  if (pKey.key === 'MediaSelect') { k += 'mediaselect' }
  if ((pKey.key && pKey.key !== ' ' && pKey.key.length === 1) || /F\d{1,2}|\//g.test(pKey.key)) k += pKey.key.toLowerCase()
  return k
}

ShortKey.keyDown = (pKey) => {
  if ((!mapFunctions[pKey].oc && !mapFunctions[pKey].ps) || (mapFunctions[pKey].ps && !keyPressed)) {
    const e = new Event('shortkey', { bubbles: false })
    if (mapFunctions[pKey].key) e.srcKey = mapFunctions[pKey].key
    const elm = mapFunctions[pKey].el
    elm[elm.length - 1].dispatchEvent(e)
  }
}
ShortKey.keyUp = (pKey) => {
  const e = new Event('shortkey', { bubbles: false })
  if (mapFunctions[pKey].key) e.srcKey = mapFunctions[pKey].key
  const elm = mapFunctions[pKey].el
  elm[elm.length - 1].dispatchEvent(e)
}

if (process.env.NODE_ENV !== 'test') {
  ;(function () {
    document.addEventListener('keydown', (pKey) => {
      const decodedKey = ShortKey.decodeKey(pKey)

      // Check evict
      if (filteringElement(pKey)) {
        pKey.preventDefault()
        pKey.stopPropagation()
        if (mapFunctions[decodedKey].fn) {
          ShortKey.keyDown(decodedKey)
          keyPressed = true
        } else if (!keyPressed) {
          const elm = mapFunctions[decodedKey].el
          elm[elm.length - 1].focus()
          keyPressed = true
        }
      }
    }, true)

    document.addEventListener('keyup', (pKey) => {
      const decodedKey = ShortKey.decodeKey(pKey)
      if (filteringElement(pKey)) {
        pKey.preventDefault()
        pKey.stopPropagation()
        if (mapFunctions[decodedKey].oc || mapFunctions[decodedKey].ps) {
          ShortKey.keyUp(decodedKey)
        }
      }
      keyPressed = false
    }, true)
  })()
}

const mappingFunctions = ({b, pushButton, once, focus, el}) => {
  if (b instanceof Array) {
    const k = b.join('')
    const elm = mapFunctions[k] && mapFunctions[k].el ? mapFunctions[k].el : []
    elm.push(el)
    mapFunctions[k] = {
      'ps': pushButton,
      'oc': once,
      'fn': !focus,
      el: elm
    }
  } else {
    for (let item in b) {
      const k = b[item].join('')
      const elm = mapFunctions[k] && mapFunctions[k].el ? mapFunctions[k].el : []
      elm.push(el)
      mapFunctions[k] = {
        'ps': pushButton,
        'oc': once,
        'fn': !focus,
        'key': item,
        el: elm
      }
    }
  }
}

const filteringElement = (pKey) => {
  const decodedKey = ShortKey.decodeKey(pKey)
  const objectAvoid = objAvoided.find(r => r === document.activeElement)
  const elementSeparate = checkElementType()
  const elementTypeAvoid = elementSeparate.avoidedTypes
  const elementClassAvoid = elementSeparate.avoidedClasses
  const filterTypeAvoid = elementTypeAvoid.find(r => document.activeElement && r === document.activeElement.tagName.toLowerCase())
  const filterClassAvoid = elementClassAvoid.find(r => document.activeElement && r === '.' + document.activeElement.className.toLowerCase())
  return !objectAvoid && mapFunctions[decodedKey] && !filterTypeAvoid && !filterClassAvoid
}

const checkElementType = () => {
  let elmTypeAvoid = []
  let elmClassAvoid = []
  elementAvoided.forEach(r => {
    const dotPosition = r.indexOf('.')
    if (dotPosition === 0) {
      elmClassAvoid.push(r)
    } else if (dotPosition > 0) {
      elmTypeAvoid.push(r.split('.')[0])
      elmClassAvoid.push('.' + r.split('.')[1])
    } else {
      elmTypeAvoid.push(r)
    }
  })

  return {avoidedTypes: elmTypeAvoid, avoidedClasses: elmClassAvoid}
}

if (typeof module != 'undefined' && module.exports) {
  module.exports = ShortKey;
} else if (typeof define == 'function' && define.amd) {
  define( function () { return ShortKey; } );
} else {
  window.ShortKey = ShortKey;
}
<|endoftext|>


<commit_before>'use strict';
var SchemaObject = require('./mixed')
  , locale = require('./locale.js').number
  , isAbsent = require('./util/isAbsent')
  , { isDate, inherits } = require('./util/_');

module.exports = NumberSchema

let isInteger = val => isAbsent(val) || val === (val | 0)

function NumberSchema(){
  if ( !(this instanceof NumberSchema))
    return new NumberSchema()

  SchemaObject.call(this, { type: 'number' })

  this.withMutation(() => {
    this.transform(function(value) {
      if (this.isType(value)) return value
      if (typeof value === 'boolean') return value ? 1 : 0

      return isDate(value) ? +value : parseFloat(value)
    })
  })
}

inherits(NumberSchema, SchemaObject, {

  _typeCheck(v) {
    if ( typeof v === 'number' && !(v !== +v) ) return true
    if ( typeof v === 'object' && v instanceof Number ) return true

    return false
  },

  min(min, msg) {
    return this.test({
      name: 'min',
      exclusive: true,
      params: { min },
      message: msg || locale.min,
      test(value) {
        return isAbsent(value) || value >= this.resolve(min)
      }
    })
  },

  max(max, msg) {
    return this.test({
      name: 'max',
      exclusive: true,
      params: { max },
      message: msg || locale.max,
      test(value) {
        return isAbsent(value) || value <= this.resolve(max)
      }
    })
  },

  positive(msg) {
    return this.min(0, msg || locale.positive)
  },

  negative(msg) {
    return this.max(0, msg || locale.negative)
  },

  integer(msg) {
    msg = msg || locale.integer

    return this
      .transform(value => !isAbsent(value) ? (value | 0) : value)
      .test('integer', msg, isInteger)
  },

  round(method) {
    var avail = ['ceil', 'floor', 'round']
    method = (method && method.toLowerCase()) || 'round'

    if( avail.indexOf(method.toLowerCase()) === -1 )
      throw new TypeError('Only valid options for round() are: ' + avail.join(', '))

    return this.transform(value => !isAbsent(value) ? Math[method](value) : value)
  }
})
<commit_msg>[fixed] boxed number allowed NaN<commit_after>'use strict';
var SchemaObject = require('./mixed')
  , locale = require('./locale.js').number
  , isAbsent = require('./util/isAbsent')
  , { isDate, inherits } = require('./util/_');

module.exports = NumberSchema

let isInteger = val => isAbsent(val) || val === (val | 0)

function NumberSchema(){
  if ( !(this instanceof NumberSchema))
    return new NumberSchema()

  SchemaObject.call(this, { type: 'number' })

  this.withMutation(() => {
    this.transform(function(value) {
      if (this.isType(value)) return value
      if (typeof value === 'boolean') return value ? 1 : 0

      return isDate(value) ? +value : parseFloat(value)
    })
  })
}

inherits(NumberSchema, SchemaObject, {

  _typeCheck(v) {
    if (typeof v === 'number' && !(v !== +v)) return true
    if (v instanceof Number && !isNaN(+v))  return true

    return false
  },

  min(min, msg) {
    return this.test({
      name: 'min',
      exclusive: true,
      params: { min },
      message: msg || locale.min,
      test(value) {
        return isAbsent(value) || value >= this.resolve(min)
      }
    })
  },

  max(max, msg) {
    return this.test({
      name: 'max',
      exclusive: true,
      params: { max },
      message: msg || locale.max,
      test(value) {
        return isAbsent(value) || value <= this.resolve(max)
      }
    })
  },

  positive(msg) {
    return this.min(0, msg || locale.positive)
  },

  negative(msg) {
    return this.max(0, msg || locale.negative)
  },

  integer(msg) {
    msg = msg || locale.integer

    return this
      .transform(value => !isAbsent(value) ? (value | 0) : value)
      .test('integer', msg, isInteger)
  },

  round(method) {
    var avail = ['ceil', 'floor', 'round']
    method = (method && method.toLowerCase()) || 'round'

    if( avail.indexOf(method.toLowerCase()) === -1 )
      throw new TypeError('Only valid options for round() are: ' + avail.join(', '))

    return this.transform(value => !isAbsent(value) ? Math[method](value) : value)
  }
})
<|endoftext|>