<commit_before>/* eslint-env node */
/* global require, module, process */

'use strict';
var chalk = require('chalk');

var buildPolicyString = require('./lib/utils')['buildPolicyString'];

var CSP_SELF        = "'self'";
var CSP_NONE        = "'none'";
var REPORT_PATH     = '/csp-report';

var CSP_HEADER              = 'Content-Security-Policy';
var CSP_HEADER_REPORT_ONLY  = 'Content-Security-Policy-Report-Only';

var CSP_REPORT_URI          = 'report-uri';
var CSP_FRAME_ANCESTORS     = 'frame-ancestors';
var CSP_SANDBOX             = 'sandbox';

var META_UNSUPPORTED_DIRECTIVES = [
  CSP_REPORT_URI,
  CSP_FRAME_ANCESTORS,
  CSP_SANDBOX,
];

var STATIC_TEST_NONCE = 'abcdefg';

var unsupportedDirectives = function(policyObject) {
  return META_UNSUPPORTED_DIRECTIVES.filter(function(name) {
    return policyObject && (name in policyObject);
  });
};

// CSP has a built-in fallback mechanism. If, say, `connect-src` is not defined it
// will fall back to `default-src`. This can cause issues. An example:
//
// Developer has has defined the following policy:
// `default-src: 'self' example.com;`
// and an addon appends the connect-src entry live-reload.local the result is:
// `default-src: 'self' example.com; connect-src: live-reload.local;`
//
// After the addons change an xhr to example.com (which was previously permitted, via fallback)
// will now be rejected since it doesn't match live-reload.local.
//
// To mitigate, whenever we append to a non-existing directive we must also copy all sources from
// default-src onto the specified directive.
var appendSourceList = function(policyObject, name, sourceList) {
  var oldSourceList;
  var oldValue = policyObject[name];

  // cast string syntax into array
  if (oldValue && typeof oldValue === 'string') {
    oldValue = oldValue.split(' ');
  }

  if (oldValue !== null && typeof oldValue !== 'undefined' && !Array.isArray(oldValue)) {
    throw new Error('Unknown source list value');
  }

  if (!oldValue || oldValue.length === 0) {
    // copy default-src (see above)
    oldSourceList = policyObject['default-src'] || [];
  } else { // array
    oldSourceList = oldValue;
  }

  oldSourceList.push(sourceList);
  policyObject[name] = oldSourceList.join(' ');
};

module.exports = {
  name: 'ember-cli-content-security-policy',

  config: function(environment/*, appConfig */) {
    var header = CSP_HEADER_REPORT_ONLY;

    var policy = {
      'default-src':  [CSP_NONE],
      'script-src':   [CSP_SELF],
      'font-src':     [CSP_SELF],
      'connect-src':  [CSP_SELF],
      'img-src':      [CSP_SELF],
      'style-src':    [CSP_SELF],
      'media-src':    [CSP_SELF],
    };

    // testem requires frame-src to run
    if (environment === 'test') {
      policy['frame-src'] = CSP_SELF;
    }

    return ({
      contentSecurityPolicyHeader: header,
      contentSecurityPolicy: policy,
    });
  },

  serverMiddleware: function(config) {
    var app = config.app;
    var options = config.options;
    var project = options.project;

    app.use(function(req, res, next) {
      var appConfig = project.config(options.environment);

      var header = appConfig.contentSecurityPolicyHeader;
      var policyObject = appConfig.contentSecurityPolicy;

      if (!header || !policyObject) {
        next();
        return;
      }

      if (policyObject) {
        appendSourceList(policyObject, 'script-src', "'nonce-" + STATIC_TEST_NONCE + "'");
      }

      // can be moved to the ember-cli-live-reload addon if RFC-22 is implemented
      // https://github.com/ember-cli/rfcs/pull/22
      if (options.liveReload) {
        ['localhost', '0.0.0.0', options.liveReloadHost].forEach(function(host) {
          var liveReloadHost = host + ':' + options.liveReloadPort;
          var liveReloadProtocol = options.ssl ? 'wss://' : 'ws://';
          appendSourceList(policyObject, 'connect-src', liveReloadProtocol + liveReloadHost);
          appendSourceList(policyObject, 'script-src', liveReloadHost);
        });
      }

      // only needed for headers, since report-uri cannot be specified in meta tag
      if (header.indexOf('Report-Only') !== -1 && !('report-uri' in policyObject)) {
        var ecHost = options.host || 'localhost';
        var ecProtocol = options.ssl ? 'https://' : 'http://';
        var ecOrigin = ecProtocol + ecHost + ':' + options.port;
        appendSourceList(policyObject, 'connect-src', ecOrigin);
        policyObject['report-uri'] = ecOrigin + REPORT_PATH;
      }

      var headerValue = buildPolicyString(policyObject);

      if (!headerValue) {
        next();
        return;
      }

      // clear existing headers before setting ours
      res.removeHeader(CSP_HEADER);
      res.removeHeader(CSP_HEADER_REPORT_ONLY);
      res.setHeader(header, headerValue);

      // for Internet Explorer 11 and below (Edge support the standard header name)
      res.removeHeader('X-' + CSP_HEADER);
      res.removeHeader('X-' + CSP_HEADER_REPORT_ONLY);
      res.setHeader('X-' + header, headerValue);

      next();
    });

    var bodyParser = require('body-parser');
    app.use(REPORT_PATH, bodyParser.json({ type: 'application/csp-report' }));
    app.use(REPORT_PATH, bodyParser.json({ type: 'application/json' }));
    app.use(REPORT_PATH, function(req, res, _next) {
      // eslint-disable-next-line no-console
      console.log(chalk.red('Content Security Policy violation:') + '\n\n' + JSON.stringify(req.body, null, 2));
      res.send({ status:'ok' });
    });
  },

  contentFor: function(type, appConfig, existingContent) {
    if ((type === 'head' && appConfig.contentSecurityPolicyMeta)) {
      var policyObject = appConfig.contentSecurityPolicy;
      var liveReloadPort = process.env.EMBER_CLI_INJECT_LIVE_RELOAD_PORT;

      // can be moved to the ember-cli-live-reload addon if RFC-22 is implemented
      // https://github.com/ember-cli/rfcs/pull/22
      if (policyObject && liveReloadPort) {
        ['localhost', '0.0.0.0'].forEach(function(host) {
          var liveReloadHost = host + ':' + liveReloadPort;
          appendSourceList(policyObject, 'connect-src', 'ws://' + liveReloadHost);
          appendSourceList(policyObject, 'connect-src', 'wss://' + liveReloadHost);
          appendSourceList(policyObject, 'script-src', liveReloadHost);
        });
      }

      if (policyObject && appConfig.environment === 'test') {
        appendSourceList(policyObject, 'script-src', "'nonce-" + STATIC_TEST_NONCE + "'");
      }

      var policyString = buildPolicyString(policyObject);

      unsupportedDirectives(policyObject).forEach(function(name) {
        var msg = 'CSP delivered via meta does not support `' + name + '`, ' +
                  'per the W3C recommendation.';
        console.log(chalk.yellow(msg)); // eslint-disable-line no-console
      });

      if (!policyString) {
        // eslint-disable-next-line no-console
        console.log(chalk.yellow('CSP via meta tag enabled but no policy exist.'));
      } else {
        return '<meta http-equiv="' + CSP_HEADER + '" content="' + policyString + '">';
      }
    }

    if (type === 'test-body-footer') {
      // Add nonce to <script> tag inserted by ember-cli to assert that test file was loaded.
      existingContent.forEach((entry, index) => {
        if (/<script>\s*Ember.assert\(.*EmberENV.TESTS_FILE_LOADED\);\s*<\/script>/.test(entry)) {
          existingContent[index] = entry.replace('<script>', '<script nonce="' + STATIC_TEST_NONCE + '">');
        }
      });
    }
  },

  includedCommands: function() {
    return require('./lib/commands');
  }
};
<commit_msg>add comment to make it clearer why it's safe to add nonce always<commit_after>/* eslint-env node */
/* global require, module, process */

'use strict';
var chalk = require('chalk');

var buildPolicyString = require('./lib/utils')['buildPolicyString'];

var CSP_SELF        = "'self'";
var CSP_NONE        = "'none'";
var REPORT_PATH     = '/csp-report';

var CSP_HEADER              = 'Content-Security-Policy';
var CSP_HEADER_REPORT_ONLY  = 'Content-Security-Policy-Report-Only';

var CSP_REPORT_URI          = 'report-uri';
var CSP_FRAME_ANCESTORS     = 'frame-ancestors';
var CSP_SANDBOX             = 'sandbox';

var META_UNSUPPORTED_DIRECTIVES = [
  CSP_REPORT_URI,
  CSP_FRAME_ANCESTORS,
  CSP_SANDBOX,
];

var STATIC_TEST_NONCE = 'abcdefg';

var unsupportedDirectives = function(policyObject) {
  return META_UNSUPPORTED_DIRECTIVES.filter(function(name) {
    return policyObject && (name in policyObject);
  });
};

// CSP has a built-in fallback mechanism. If, say, `connect-src` is not defined it
// will fall back to `default-src`. This can cause issues. An example:
//
// Developer has has defined the following policy:
// `default-src: 'self' example.com;`
// and an addon appends the connect-src entry live-reload.local the result is:
// `default-src: 'self' example.com; connect-src: live-reload.local;`
//
// After the addons change an xhr to example.com (which was previously permitted, via fallback)
// will now be rejected since it doesn't match live-reload.local.
//
// To mitigate, whenever we append to a non-existing directive we must also copy all sources from
// default-src onto the specified directive.
var appendSourceList = function(policyObject, name, sourceList) {
  var oldSourceList;
  var oldValue = policyObject[name];

  // cast string syntax into array
  if (oldValue && typeof oldValue === 'string') {
    oldValue = oldValue.split(' ');
  }

  if (oldValue !== null && typeof oldValue !== 'undefined' && !Array.isArray(oldValue)) {
    throw new Error('Unknown source list value');
  }

  if (!oldValue || oldValue.length === 0) {
    // copy default-src (see above)
    oldSourceList = policyObject['default-src'] || [];
  } else { // array
    oldSourceList = oldValue;
  }

  oldSourceList.push(sourceList);
  policyObject[name] = oldSourceList.join(' ');
};

module.exports = {
  name: 'ember-cli-content-security-policy',

  config: function(environment/*, appConfig */) {
    var header = CSP_HEADER_REPORT_ONLY;

    var policy = {
      'default-src':  [CSP_NONE],
      'script-src':   [CSP_SELF],
      'font-src':     [CSP_SELF],
      'connect-src':  [CSP_SELF],
      'img-src':      [CSP_SELF],
      'style-src':    [CSP_SELF],
      'media-src':    [CSP_SELF],
    };

    // testem requires frame-src to run
    if (environment === 'test') {
      policy['frame-src'] = CSP_SELF;
    }

    return ({
      contentSecurityPolicyHeader: header,
      contentSecurityPolicy: policy,
    });
  },

  serverMiddleware: function(config) {
    var app = config.app;
    var options = config.options;
    var project = options.project;

    console.log(options.environment);

    app.use(function(req, res, next) {
      var appConfig = project.config(options.environment);

      var header = appConfig.contentSecurityPolicyHeader;
      var policyObject = appConfig.contentSecurityPolicy;

      if (!header || !policyObject) {
        next();
        return;
      }

      // the local server will never run for production builds, so no danger in adding the nonce all the time
      // even so it's only needed if tests are executed by opening `http://localhost:4200/tests`
      if (policyObject) {
        appendSourceList(policyObject, 'script-src', "'nonce-" + STATIC_TEST_NONCE + "'");
      }

      // can be moved to the ember-cli-live-reload addon if RFC-22 is implemented
      // https://github.com/ember-cli/rfcs/pull/22
      if (options.liveReload) {
        ['localhost', '0.0.0.0', options.liveReloadHost].forEach(function(host) {
          var liveReloadHost = host + ':' + options.liveReloadPort;
          var liveReloadProtocol = options.ssl ? 'wss://' : 'ws://';
          appendSourceList(policyObject, 'connect-src', liveReloadProtocol + liveReloadHost);
          appendSourceList(policyObject, 'script-src', liveReloadHost);
        });
      }

      // only needed for headers, since report-uri cannot be specified in meta tag
      if (header.indexOf('Report-Only') !== -1 && !('report-uri' in policyObject)) {
        var ecHost = options.host || 'localhost';
        var ecProtocol = options.ssl ? 'https://' : 'http://';
        var ecOrigin = ecProtocol + ecHost + ':' + options.port;
        appendSourceList(policyObject, 'connect-src', ecOrigin);
        policyObject['report-uri'] = ecOrigin + REPORT_PATH;
      }

      var headerValue = buildPolicyString(policyObject);

      if (!headerValue) {
        next();
        return;
      }

      // clear existing headers before setting ours
      res.removeHeader(CSP_HEADER);
      res.removeHeader(CSP_HEADER_REPORT_ONLY);
      res.setHeader(header, headerValue);

      // for Internet Explorer 11 and below (Edge support the standard header name)
      res.removeHeader('X-' + CSP_HEADER);
      res.removeHeader('X-' + CSP_HEADER_REPORT_ONLY);
      res.setHeader('X-' + header, headerValue);

      next();
    });

    var bodyParser = require('body-parser');
    app.use(REPORT_PATH, bodyParser.json({ type: 'application/csp-report' }));
    app.use(REPORT_PATH, bodyParser.json({ type: 'application/json' }));
    app.use(REPORT_PATH, function(req, res, _next) {
      // eslint-disable-next-line no-console
      console.log(chalk.red('Content Security Policy violation:') + '\n\n' + JSON.stringify(req.body, null, 2));
      res.send({ status:'ok' });
    });
  },

  contentFor: function(type, appConfig, existingContent) {
    if ((type === 'head' && appConfig.contentSecurityPolicyMeta)) {
      var policyObject = appConfig.contentSecurityPolicy;
      var liveReloadPort = process.env.EMBER_CLI_INJECT_LIVE_RELOAD_PORT;

      // can be moved to the ember-cli-live-reload addon if RFC-22 is implemented
      // https://github.com/ember-cli/rfcs/pull/22
      if (policyObject && liveReloadPort) {
        ['localhost', '0.0.0.0'].forEach(function(host) {
          var liveReloadHost = host + ':' + liveReloadPort;
          appendSourceList(policyObject, 'connect-src', 'ws://' + liveReloadHost);
          appendSourceList(policyObject, 'connect-src', 'wss://' + liveReloadHost);
          appendSourceList(policyObject, 'script-src', liveReloadHost);
        });
      }

      if (policyObject && appConfig.environment === 'test') {
        appendSourceList(policyObject, 'script-src', "'nonce-" + STATIC_TEST_NONCE + "'");
      }

      var policyString = buildPolicyString(policyObject);

      unsupportedDirectives(policyObject).forEach(function(name) {
        var msg = 'CSP delivered via meta does not support `' + name + '`, ' +
                  'per the W3C recommendation.';
        console.log(chalk.yellow(msg)); // eslint-disable-line no-console
      });

      if (!policyString) {
        // eslint-disable-next-line no-console
        console.log(chalk.yellow('CSP via meta tag enabled but no policy exist.'));
      } else {
        return '<meta http-equiv="' + CSP_HEADER + '" content="' + policyString + '">';
      }
    }

    if (type === 'test-body-footer') {
      // Add nonce to <script> tag inserted by ember-cli to assert that test file was loaded.
      existingContent.forEach((entry, index) => {
        if (/<script>\s*Ember.assert\(.*EmberENV.TESTS_FILE_LOADED\);\s*<\/script>/.test(entry)) {
          existingContent[index] = entry.replace('<script>', '<script nonce="' + STATIC_TEST_NONCE + '">');
        }
      });
    }
  },

  includedCommands: function() {
    return require('./lib/commands');
  }
};
<|endoftext|>


<commit_before>import React from 'react'
import PropTypes from 'prop-types'
import { ActivityIndicator, StyleSheet, View } from 'react-native'

/**
 * A flexible little View Finder component to be used in conjunction with react-native-camera
 */
const ViewFinder = ({
                        backgroundColor,
                        containerStyle,
                        height,
                        width,
                        borderWidth,
                        borderRadius,
                        color,
                        loading,
                        loadingBelowVF,
                        borderLength
                    }) => (
    <View style={[styles.container, containerStyle, { backgroundColor }]}>
        <View style={[styles.viewfinder, { backgroundColor }, { width, height }]}>
            <View
                style={[
                    styles.topLeftEdge,
                    {
                        height: borderLength,
                        width: borderLength,
                        borderColor: color,
                        borderLeftWidth: borderWidth,
                        borderTopWidth: borderWidth,
                        borderTopLeftRadius: borderRadius
                    }
                ]}
            />
            <View
                style={[
                    styles.topRightEdge,
                    {
                        height: borderLength,
                        width: borderLength,
                        borderColor: color,
                        borderRightWidth: borderWidth,
                        borderTopWidth: borderWidth,
                        borderTopRightRadius: borderRadius
                    }
                ]}
            />
            <View
                style={[
                    styles.bottomLeftEdge,
                    {
                        height: borderLength,
                        width: borderLength,
                        borderColor: color,
                        borderLeftWidth: borderWidth,
                        borderBottomWidth: borderWidth,
                        borderBottomLeftRadius: borderRadius
                    }
                ]}
            />
            <View
                style={[
                    styles.bottomRightEdge,
                    {
                        height: borderLength,
                        width: borderLength,
                        borderColor: color,
                        borderRightWidth: borderWidth,
                        borderBottomWidth: borderWidth,
                        borderBottomRightRadius: borderRadius
                    }
                ]}
            />
            {loading && (
                <View>
                    <ActivityIndicator style={[loadingBelowVF ? { top: (height - 50)}: loadingBelowVF]} animating={loading} color={color} size='large' />
                </View>
            )}
        </View>
    </View>
)

ViewFinder.propTypes = {
    /**
     * Background color of View Finder
     */
    backgroundColor: PropTypes.string,
    /**
     * Style View Finder outer container
     */
    containerStyle: PropTypes.object,
    /**
     * Border with of View Finder edges
     */
    borderWidth: PropTypes.number,
    /**
     * Border Radius of View Finder edges
     */
    borderRadius: PropTypes.number,
    /**
     * Border length of View Finder edges
     */
    borderLength: PropTypes.number,
    /**
     * Color of View finder edges and Spinner
     */
    color: PropTypes.string,
    /**
     * Height of View Finder
     */
    height: PropTypes.number,
    /**
     * Bool to indicate if loading indicator should be shown
     */
    loading: PropTypes.bool,
     /**
     * Bool to indicate if loading indicator should be shown under View Finder
     */
    loadingBelowVF: PropTypes.bool,
    /**
     * Width of View Finder
     */
    width: PropTypes.number
}

ViewFinder.defaultProps = {
    containerStyle: {},
    backgroundColor: 'transparent',
    borderWidth: 3,
    borderRadius: 0,
    borderLength: 25,
    color: '#fff',
    height: 200,
    loading: false,
    width: 200
}

const styles = StyleSheet.create({
    container: {
        alignItems: 'center',
        justifyContent: 'center',
        position: 'absolute',
        top: 0,
        right: 0,
        bottom: 0,
        left: 0
    },
    viewfinder: {
        alignItems: 'center',
        justifyContent: 'center'
    },
    topLeftEdge: {
        position: 'absolute',
        top: 0,
        left: 0
    },
    topRightEdge: {
        position: 'absolute',

        top: 0,
        right: 0
    },
    bottomLeftEdge: {
        position: 'absolute',
        bottom: 0,
        left: 0
    },
    bottomRightEdge: {
        position: 'absolute',
        bottom: 0,
        right: 0
    },
})

export default ViewFinder<commit_msg>Added LoadingBelowVF As Props<commit_after>import React from 'react'
import PropTypes from 'prop-types'
import { ActivityIndicator, StyleSheet, View } from 'react-native'

/**
 * A flexible little View Finder component to be used in conjunction with react-native-camera
 */
const ViewFinder = ({
  backgroundColor,
  containerStyle,
  height,
  width,
  borderWidth,
  borderRadius,
  color,
  loading,
  borderLength
}) => (
  <View style={[styles.container, containerStyle, { backgroundColor }]}>
    <View style={[styles.viewfinder, { backgroundColor }, { width, height }]}>
      <View
        style={[
          styles.topLeftEdge,
          {
            height: borderLength,
            width: borderLength,
            borderColor: color,
            borderLeftWidth: borderWidth,
            borderTopWidth: borderWidth,
            borderTopLeftRadius: borderRadius
          }
        ]}
      />
      <View
        style={[
          styles.topRightEdge,
          {
            height: borderLength,
            width: borderLength,
            borderColor: color,
            borderRightWidth: borderWidth,
            borderTopWidth: borderWidth,
            borderTopRightRadius: borderRadius
          }
        ]}
      />
      {loading && (
        <ActivityIndicator style={[loadingBelowVF ? { top: (height - 50)}: loadingBelowVF]} animating={loading} color={color} size='large' />
      )}
      <View
        style={[
          styles.bottomLeftEdge,
          {
            height: borderLength,
            width: borderLength,
            borderColor: color,
            borderLeftWidth: borderWidth,
            borderBottomWidth: borderWidth,
            borderBottomLeftRadius: borderRadius
          }
        ]}
      />
      <View
        style={[
          styles.bottomRightEdge,
          {
            height: borderLength,
            width: borderLength,
            borderColor: color,
            borderRightWidth: borderWidth,
            borderBottomWidth: borderWidth,
            borderBottomRightRadius: borderRadius
          }
        ]}
      />
    </View>
  </View>
)

ViewFinder.propTypes = {
  /**
     * Background color of View Finder
     */
  backgroundColor: PropTypes.string,
  /**
     * Style View Finder outer container
     */
  containerStyle: PropTypes.object,
  /**
     * Border with of View Finder edges
     */
  borderWidth: PropTypes.number,
  /**
     * Border Radius of View Finder edges
     */
  borderRadius: PropTypes.number,
  /**
     * Border length of View Finder edges
     */
  borderLength: PropTypes.number,
  /**
     * Color of View finder edges and Spinner
     */
  color: PropTypes.string,
  /**
     * Height of View Finder
     */
  height: PropTypes.number,
  /**
     * Bool to indicate if loading indicator should be shown
     */
  loading: PropTypes.bool,
  /**
     * Bool to indicate if loading indicator should be shown under View Finder
     */
  loadingBelowVF: PropTypes.bool,
  /**
     * Width of View Finder
     */
  width: PropTypes.number
}

ViewFinder.defaultProps = {
  containerStyle: {},
  backgroundColor: 'transparent',
  borderWidth: 3,
  borderRadius: 0,
  borderLength: 25,
  color: '#fff',
  height: 200,
  loading: false,
  loadingBelowVF: false,
  width: 200
}

const styles = StyleSheet.create({
  container: {
    alignItems: 'center',
    justifyContent: 'center',
    position: 'absolute',
    top: 0,
    right: 0,
    bottom: 0,
    left: 0
  },
  viewfinder: {
    alignItems: 'center',
    justifyContent: 'center'
  },
  topLeftEdge: {
    position: 'absolute',
    top: 0,
    left: 0
  },
  topRightEdge: {
    position: 'absolute',

    top: 0,
    right: 0
  },
  bottomLeftEdge: {
    position: 'absolute',
    bottom: 0,
    left: 0
  },
  bottomRightEdge: {
    position: 'absolute',
    bottom: 0,
    right: 0
  }
})

export default ViewFinder<|endoftext|>


<commit_before>'use strict'
const electron = require('electron')
const app = electron.app

// adds debug features like hotkeys for triggering dev tools and reload
require('electron-debug')()

// prevent window being garbage collected
let mainWindow

function onClosed () {
  // dereference the window
  // for multiple windows store them in an array
  mainWindow = null
}

function createMainWindow () {
  const win = new electron.BrowserWindow({
    width: 600,
    height: 400
  })

  win.loadURL(`file://${__dirname}/index.html`)
  win.on('closed', onClosed)

  return win
}

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

app.on('activate', () => {
  if (!mainWindow) {
    mainWindow = createMainWindow()
  }
})

app.on('ready', () => {
  mainWindow = createMainWindow()
})
<commit_msg>start server on ready, add menubar setup<commit_after>'use strict'
const electron = require('electron')
const childProcess = require('child_process')
const app = electron.app
const Menu = electron.Menu
const Tray = electron.Tray

// adds debug features like hotkeys for triggering dev tools and reload
require('electron-debug')()

// prevent window being garbage collected
let mainWindow
let serverProcess
let appIcon

function onClosed () {
  mainWindow = null
}

function createMainWindow () {
  const win = new electron.BrowserWindow({
    width: 600,
    height: 400
  })

  win.loadURL(`file://${__dirname}/index.html`)
  win.on('closed', onClosed)

  return win
}

function startRedisServer (port) {
  serverProcess = childProcess.exec('redis-server')
}

function stopRedisServer () {
  serverProcess.kill()
}

function setupMenuBar () {
  appIcon = new Tray(`${__dirname}/assets/images/tray-icon.png`)
  var contextMenu = Menu.buildFromTemplate([
    { label: 'Item1', type: 'radio' },
    { label: 'Item2', type: 'radio' },
    { label: 'Item3', type: 'radio', checked: true },
    { label: 'Item4', type: 'radio' }
  ])
  appIcon.setToolTip('This is my application.')
  appIcon.setContextMenu(contextMenu)
}

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

app.on('activate', () => {
  if (!mainWindow) {
    mainWindow = createMainWindow()
  }
})

app.on('ready', () => {
  mainWindow = createMainWindow()
  setupMenuBar()
  startRedisServer()
})
<|endoftext|>


<commit_before><commit_msg>created data sets<commit_after>const timestamps = require('./data.json');

const data = timestamps.map(function(timestamp) {
  const date = new Date(timestamp);
  const year = date.getFullYear();
  const firstDayOfTheYear = new Date(year + "-01-01");
  const millisecondsInADay = 60 * 60 * 1000 * 24;
  const dayOfTheYear = (date - firstDayOfTheYear) / millisecondsInADay;
  return {
    timestamp: timestamp,
    date: date,
    year: date.getFullYear(),
    month: date.getMonth() + 1,
    week: Math.floor(dayOfTheYear / 7) - 1,
    weekday: date.getDay(),
    hour: date.getHours()
  };
});

console.log(data[0], data[data.length - 1]);

const startDate = data[0].date;
const endDate = data[data.length - 1].date;

const deploysPerDay = [];
const deploysPerWeek = [];
const daysWithoutDeploys = [];
const deploysByHour = [];
const deploysByDayOfTheWeek = [];

const years = [2016, 2017];
const weeks = [];
let i = 0;
for (i = 0; i < 52; i++) {
  weeks[i] = i + 1;
}
const weekdays = [];
for (i = 0; i < 7; i++) {
  weekdays[i] = i + 1;
}
for (i = 0; i < 24; i++) {
  deploysByHour[i] = {
    hour: i,
    count: 0
  };
}

years.forEach(function(year) {
  weeks.forEach(function(week) {
    if (year === 2016 && week < 8) {
      return;
    }
    if (year === 2017 && week > 34) {
      return;
    }

    deploysPerWeek.push({
      yearWeek: year + 'w' + week,
      year: year,
      week: week,
      count: 0
    });

    weekdays.forEach(function(weekday) {
      deploysPerDay.push({
        yearWeekDay: year + 'w' + week + 'd' + weekday,
        year: year,
        week: week,
        weekday: weekday,
        count: 0
      });
    });
  });
});

data.forEach(function(datum) {
  deploysPerWeek.forEach(function(week) {
    if (week.week == datum.week) {
      week.count++;
    }
  });
  deploysPerDay.forEach(function(day) {
    if (day.week == datum.week && day.weekday == datum.weekday) {
      day.count++;
    }
  });

  deploysByHour[datum.hour].count++;
});

deploysPerDay.forEach(function(day) {
  if (!day.count && day.weekday > 1 && day.weekday < 6) {
    daysWithoutDeploys.push(day);
  }
});

console.log('deploysByHour', deploysByHour);
console.log('deploysPerWeek', deploysPerWeek[deploysPerWeek.length - 1]);
console.log('deploysPerDay', deploysPerDay[deploysPerDay.length - 3]);
console.log('daysWithoutDeploys: ' + daysWithoutDeploys.length, daysWithoutDeploys[daysWithoutDeploys.length - 3]);
<|endoftext|>


<commit_before>const fs = require('fs');
const {URL} = require('url');
const path = require('path');
const fetch = require('node-fetch');
const ffmpeg = require('fluent-ffmpeg');
const sqlite = require('sqlite');
const {Composer} = require('micro-bot');
const commandParts = require('telegraf-command-parts');
const mdEscape = require('markdown-escape');

Promise.resolve()
  .then(() => sqlite.open('file_ids.db', {cached: true}))
  .then(() => sqlite.run('CREATE TABLE IF NOT EXISTS files(id INTEGER PRIMARY KEY, file_id TEXT, is_document INTEGER);'))
  .catch((err) => {
    console.error(err);
    process.exit(1);
  })
  .then(() => console.log('Database open'));

const app = new Composer();

const reVideoId = new RegExp('^(?:https?://(?:[a-z-]+\\.)?twitter\\.com/[^/]+/status(?:es)?/|id:)(\\d+)(?:[?#/]|$)', 'i');
const reVideoUrl = new RegExp('"video_url":"(.+?)"');
const reVideoDomain = new RegExp('^https?://[^/]+', 'i');

const parseMarkdownExtra = {parse_mode: 'Markdown'};

function formatTime(date) {
  const hours = date.getHours(),
        minutes = date.getMinutes(),
        seconds = date.getSeconds(),
        milliseconds = date.getMilliseconds();

  return ((hours < 10) ? '0' + hours : hours) +
         ':' +
         ((minutes < 10) ? '0' + minutes : minutes) +
         ':' +
         ((seconds < 10) ? '0' + seconds : seconds) +
         '.' +
         ('00' + milliseconds).slice(-3);
}

app.use(commandParts());

app.use((ctx, next) => {
  const start = new Date();

  if (ctx.update.message) {
    console.log('[%s] “%s %s” (@%s, id: %s) sent “%s”', formatTime(start), ctx.chat.first_name, ctx.chat.last_name, ctx.chat.username, ctx.chat.id, ctx.update.message.text);
  }

  return next(ctx).then(() => {
    console.log(`[${ctx.updateType}] Response time %sms`, new Date() - start);
  });
});

async function getM3u8UrlList(url, prefix = '') {
  const m3u8Text = await (await fetch(url)).text();
  const m3u8Lines = m3u8Text.split('\n');

  return m3u8Lines.filter((line) => line && !line.startsWith('#')).map((line) => prefix + line);
}

async function downloadFile(url, folder = '/tmp') {
  const fileName = path.join(folder, path.basename(new URL(url).pathname));
  const buffer = await (await fetch(url)).buffer();

  fs.writeFileSync(fileName, buffer);
  return fileName;
}

async function saveUrlListToFiles(urlList, folder = '/tmp') {
  return Promise.all(urlList.map((url) => downloadFile(url, folder)));
}

function ffmpegAsync(command, outputVideo) {
  return new Promise((resolve, reject) => {
    command.on('end', (stdout, stderr) =>
      resolve([stdout, stderr])
    ).on('error', (err, stdout, stderr) =>
      reject([err, stdout, stderr])
    ).saveToFile(outputVideo);
  });
}

async function fetchVideo(videoId) {
  const playerUrl = 'https://twitter.com/i/videos/' + videoId;
  const playerHtml = await (await fetch(playerUrl)).text();
  const [, videoUrlDirty] = reVideoUrl.exec(playerHtml) || [];

  if (!videoUrlDirty) {
    return null;
  }

  const videoUrl = videoUrlDirty.replace(/\\/g, '');

  console.log(videoUrl);
  if (videoUrl.endsWith('.m3u8')) {
    const [videoDomain] = reVideoDomain.exec(videoUrl);
    const m3u8List = await getM3u8UrlList(videoUrl, videoDomain);
    const m3u8Url = m3u8List[m3u8List.length - 1];
    const tsList = await getM3u8UrlList(m3u8Url, videoDomain);
    const tmpDir = fs.mkdtempSync('/tmp/twvid');
    const videoList = await saveUrlListToFiles(tsList, tmpDir);
    const videoListFile = path.join(tmpDir, 'parts.list');
    const outputVideo = path.join(tmpDir, 'twvid_' + videoId + '.mp4');

    console.log(videoList);
    fs.writeFileSync(videoListFile, videoList.reduce((acc, cur) => acc + 'file ' + cur + '\n', ''));

    const command = ffmpeg(videoListFile).addInputOptions(['-f concat', '-safe 0']).withNoAudio().withVideoCodec('copy');

    console.log('Converting video with FFMpeg...', outputVideo);
    const [stdout, stderr] = await ffmpegAsync(command, outputVideo);
    console.log('Done.');
    //console.log(stdout, stderr);

    videoList.forEach((file) => fs.unlinkSync(file));
    fs.unlinkSync(videoListFile);

    return {video: outputVideo, isLocalFile: true};
  }
  else {
    return {video: videoUrl, isLocalFile: false};
  }
}

async function replyWithError(text, ctx, extra) {
  console.error(`Error: ${text}`);
  await ctx.replyWithMarkdown(`*⚠ ${text}*`, extra);
}

async function editWithError(msg, text, ctx, extra) {
  console.error(`Error: ${text}`);
  await ctx.telegram.editMessageText(msg.chat.id, msg.message_id, null, `*⚠ ${text}*`, Object.assign({}, parseMarkdownExtra, extra));
}

function getVideoId(url) {
  const [, videoId] = reVideoId.exec(url) || [];

  return videoId;
}

async function checkCache(videoId) {
  return (await sqlite.get('SELECT file_id, is_document FROM files WHERE id = ?;', videoId)) || {};
}

async function processStatusId(videoId, ctx, extra = {}, isInline = false) {
  const statusMsg = await ctx.replyWithMarkdown('_Processing your request..._', Object.assign({noWebhook: true}, extra));

  await ctx.replyWithChatAction('upload_video');

  const {file_id, is_document} = await checkCache(videoId);
  const replyExtra = Object.assign(isInline ? {
    reply_markup: {
      inline_keyboard: [
        [
          {
            text: 'Send to chat...',
            switch_inline_query: 'id:' + videoId
          }
        ]
      ]
    }
  } : {}, extra);

  if (file_id) {
    console.log('Cache hit!');
    await ctx[is_document ? 'replyWithDocument' : 'replyWithVideo'](file_id, replyExtra);
    ctx.telegram.deleteMessage(statusMsg.chat.id, statusMsg.message_id);
  }
  else {
    console.log('Cache miss!');
    ctx.telegram.editMessageText(statusMsg.chat.id, statusMsg.message_id, null, '_Downloading video..._', parseMarkdownExtra);

    const {video, isLocalFile} = await fetchVideo(videoId) || {};

    if (!video) {
      await editWithError(statusMsg, 'Tweet is not a video/GIF.', ctx, extra);
      return;
    }
    await ctx.replyWithChatAction('upload_video');

    let message,
        fileId,
        isDocument = false;

    if (isLocalFile) {
      console.log('Uploading video...');
      ctx.telegram.editMessageText(statusMsg.chat.id, statusMsg.message_id, null, '_Uploading video to Telegram..._', parseMarkdownExtra);
      message = await ctx.replyWithVideo({source: video}, Object.assign({noWebhook: true}, replyExtra));
      console.log('Done!');
      ctx.telegram.deleteMessage(statusMsg.chat.id, statusMsg.message_id);

      fs.unlinkSync(video);
      fs.rmdirSync(path.dirname(video));
    }
    else {
      ctx.telegram.editMessageText(statusMsg.chat.id, statusMsg.message_id, null, '_Sending video..._', parseMarkdownExtra);
      message = await ctx.replyWithVideo(video, Object.assign({noWebhook: true}, replyExtra));
      ctx.telegram.deleteMessage(statusMsg.chat.id, statusMsg.message_id);
    }

    if (message.video) {
      fileId = message.video.file_id;
    }
    else {
      fileId = message.document.file_id;
      isDocument = true;
    }

    console.log('isDocument', isDocument);
    await sqlite.run('INSERT OR IGNORE INTO files(id, file_id, is_document) VALUES(?, ?, ?);', videoId, fileId, isDocument ? 1 : 0);
  }
}

async function processUrl(url, ctx) {
  const replyToMessageExtra = {reply_to_message_id: ctx.message.message_id};

  if (!url) {
    await replyWithError('Please provide a video tweet URL.', ctx, replyToMessageExtra);
    return;
  }

  const videoId = getVideoId(url);

  if (!videoId) {
    await replyWithError('Invalid video tweet.', ctx, replyToMessageExtra);
    return;
  }

  await processStatusId(videoId, ctx, replyToMessageExtra);
}

app.command('/start', async (ctx) => {
  const [param] = ctx.state.command.splitArgs;

  if (param) {
    console.log('Deeplinking parameter:', param);
    await processStatusId(param, ctx, {}, true);
  }
  else {
    await ctx.replyWithMarkdown(
`Welcome!

Use /creategif \`<url>\`, or simply send/forward me a message that contains one or more Twitter URLs, and I’ll convert them all to Telegram GIFs for you.

I also work inline, try starting a message with @${mdEscape(ctx.botInfo.username)}, followed by a single video tweet link.

Have fun!`);
  }
});

app.command('/creategif', async (ctx) => {
  const url = ctx.state.command.splitArgs.find((arg) => arg);

  await processUrl(url, ctx);
});

app.hears((text) => !text.startsWith('/'), async (ctx) => {
  const urls = ctx.update.message.text.split(' ').filter((arg) => reVideoId.test(arg));

  if (urls.length < 2) {
    await processUrl(urls[0], ctx);
  }
  else {
    for (const url of urls) {
      await processUrl(url, ctx);
    }
  }
});

app.on('inline_query', async (ctx) => {
  console.log('INLINE QUERY:', ctx.inlineQuery);

  const url = ctx.inlineQuery.query.split(' ').find((arg) => reVideoId.test(arg));
  const videoId = getVideoId(url);

  const results = [];
  const extra = {
    cache_time: 1,
  };

  if (videoId) {
    const {file_id} = await checkCache(videoId);

    if (file_id) {
      results.push({
        type: 'mpeg4_gif',
        id: videoId,
        mpeg4_file_id: file_id
      });
      extra.cache_time = 31536000;
    }
    else {
      Object.assign(extra, {
        switch_pm_text: 'Press here to get your GIF',
        switch_pm_parameter: videoId
      });
    }
  }

  console.log('Inline answer:', results, extra);
  return ctx.answerInlineQuery(results, extra);
});

module.exports = {
  botHandler: app,
  initialize: (bot) => {
    //bot.webhookReply = false;
    return Promise.resolve();
  },
  requestHandler: (req, res) => {
    console.log('HTTP Request:', req.method, req.url, req.headers);
    if (req.url === '/') {
      res.writeHead(303, {location: 'https://t.me/TwitterGif_Bot'});
      res.end();
    }
    else {
      res.writeHead(404);
      res.end('404 Not Found');
    }
  }
};
<commit_msg>Split message content on any whitespace character (including new lines)<commit_after>const fs = require('fs');
const {URL} = require('url');
const path = require('path');
const fetch = require('node-fetch');
const ffmpeg = require('fluent-ffmpeg');
const sqlite = require('sqlite');
const {Composer} = require('micro-bot');
const commandParts = require('telegraf-command-parts');
const mdEscape = require('markdown-escape');

Promise.resolve()
  .then(() => sqlite.open('file_ids.db', {cached: true}))
  .then(() => sqlite.run('CREATE TABLE IF NOT EXISTS files(id INTEGER PRIMARY KEY, file_id TEXT, is_document INTEGER);'))
  .catch((err) => {
    console.error(err);
    process.exit(1);
  })
  .then(() => console.log('Database open'));

const app = new Composer();

const reVideoId = new RegExp('^(?:https?://(?:[a-z-]+\\.)?twitter\\.com/[^/]+/status(?:es)?/|id:)(\\d+)(?:[?#/]|$)', 'i');
const reVideoUrl = new RegExp('"video_url":"(.+?)"');
const reVideoDomain = new RegExp('^https?://[^/]+', 'i');

const parseMarkdownExtra = {parse_mode: 'Markdown'};

function formatTime(date) {
  const hours = date.getHours(),
        minutes = date.getMinutes(),
        seconds = date.getSeconds(),
        milliseconds = date.getMilliseconds();

  return ((hours < 10) ? '0' + hours : hours) +
         ':' +
         ((minutes < 10) ? '0' + minutes : minutes) +
         ':' +
         ((seconds < 10) ? '0' + seconds : seconds) +
         '.' +
         ('00' + milliseconds).slice(-3);
}

app.use(commandParts());

app.use((ctx, next) => {
  const start = new Date();

  if (ctx.update.message) {
    console.log('[%s] “%s %s” (@%s, id: %s) sent “%s”', formatTime(start), ctx.chat.first_name, ctx.chat.last_name, ctx.chat.username, ctx.chat.id, ctx.update.message.text);
  }

  return next(ctx).then(() => {
    console.log(`[${ctx.updateType}] Response time %sms`, new Date() - start);
  });
});

async function getM3u8UrlList(url, prefix = '') {
  const m3u8Text = await (await fetch(url)).text();
  const m3u8Lines = m3u8Text.split('\n');

  return m3u8Lines.filter((line) => line && !line.startsWith('#')).map((line) => prefix + line);
}

async function downloadFile(url, folder = '/tmp') {
  const fileName = path.join(folder, path.basename(new URL(url).pathname));
  const buffer = await (await fetch(url)).buffer();

  fs.writeFileSync(fileName, buffer);
  return fileName;
}

async function saveUrlListToFiles(urlList, folder = '/tmp') {
  return Promise.all(urlList.map((url) => downloadFile(url, folder)));
}

function ffmpegAsync(command, outputVideo) {
  return new Promise((resolve, reject) => {
    command.on('end', (stdout, stderr) =>
      resolve([stdout, stderr])
    ).on('error', (err, stdout, stderr) =>
      reject([err, stdout, stderr])
    ).saveToFile(outputVideo);
  });
}

async function fetchVideo(videoId) {
  const playerUrl = 'https://twitter.com/i/videos/' + videoId;
  const playerHtml = await (await fetch(playerUrl)).text();
  const [, videoUrlDirty] = reVideoUrl.exec(playerHtml) || [];

  if (!videoUrlDirty) {
    return null;
  }

  const videoUrl = videoUrlDirty.replace(/\\/g, '');

  console.log(videoUrl);
  if (videoUrl.endsWith('.m3u8')) {
    const [videoDomain] = reVideoDomain.exec(videoUrl);
    const m3u8List = await getM3u8UrlList(videoUrl, videoDomain);
    const m3u8Url = m3u8List[m3u8List.length - 1];
    const tsList = await getM3u8UrlList(m3u8Url, videoDomain);
    const tmpDir = fs.mkdtempSync('/tmp/twvid');
    const videoList = await saveUrlListToFiles(tsList, tmpDir);
    const videoListFile = path.join(tmpDir, 'parts.list');
    const outputVideo = path.join(tmpDir, 'twvid_' + videoId + '.mp4');

    console.log(videoList);
    fs.writeFileSync(videoListFile, videoList.reduce((acc, cur) => acc + 'file ' + cur + '\n', ''));

    const command = ffmpeg(videoListFile).addInputOptions(['-f concat', '-safe 0']).withNoAudio().withVideoCodec('copy');

    console.log('Converting video with FFMpeg...', outputVideo);
    const [stdout, stderr] = await ffmpegAsync(command, outputVideo);
    console.log('Done.');
    //console.log(stdout, stderr);

    videoList.forEach((file) => fs.unlinkSync(file));
    fs.unlinkSync(videoListFile);

    return {video: outputVideo, isLocalFile: true};
  }
  else {
    return {video: videoUrl, isLocalFile: false};
  }
}

async function replyWithError(text, ctx, extra) {
  console.error(`Error: ${text}`);
  await ctx.replyWithMarkdown(`*⚠ ${text}*`, extra);
}

async function editWithError(msg, text, ctx, extra) {
  console.error(`Error: ${text}`);
  await ctx.telegram.editMessageText(msg.chat.id, msg.message_id, null, `*⚠ ${text}*`, Object.assign({}, parseMarkdownExtra, extra));
}

function getVideoId(url) {
  const [, videoId] = reVideoId.exec(url) || [];

  return videoId;
}

async function checkCache(videoId) {
  return (await sqlite.get('SELECT file_id, is_document FROM files WHERE id = ?;', videoId)) || {};
}

async function processStatusId(videoId, ctx, extra = {}, isInline = false) {
  const statusMsg = await ctx.replyWithMarkdown('_Processing your request..._', Object.assign({noWebhook: true}, extra));

  await ctx.replyWithChatAction('upload_video');

  const {file_id, is_document} = await checkCache(videoId);
  const replyExtra = Object.assign(isInline ? {
    reply_markup: {
      inline_keyboard: [
        [
          {
            text: 'Send to chat...',
            switch_inline_query: 'id:' + videoId
          }
        ]
      ]
    }
  } : {}, extra);

  if (file_id) {
    console.log('Cache hit!');
    await ctx[is_document ? 'replyWithDocument' : 'replyWithVideo'](file_id, replyExtra);
    ctx.telegram.deleteMessage(statusMsg.chat.id, statusMsg.message_id);
  }
  else {
    console.log('Cache miss!');
    ctx.telegram.editMessageText(statusMsg.chat.id, statusMsg.message_id, null, '_Downloading video..._', parseMarkdownExtra);

    const {video, isLocalFile} = await fetchVideo(videoId) || {};

    if (!video) {
      await editWithError(statusMsg, 'Tweet is not a video/GIF.', ctx, extra);
      return;
    }
    await ctx.replyWithChatAction('upload_video');

    let message,
        fileId,
        isDocument = false;

    if (isLocalFile) {
      console.log('Uploading video...');
      ctx.telegram.editMessageText(statusMsg.chat.id, statusMsg.message_id, null, '_Uploading video to Telegram..._', parseMarkdownExtra);
      message = await ctx.replyWithVideo({source: video}, Object.assign({noWebhook: true}, replyExtra));
      console.log('Done!');
      ctx.telegram.deleteMessage(statusMsg.chat.id, statusMsg.message_id);

      fs.unlinkSync(video);
      fs.rmdirSync(path.dirname(video));
    }
    else {
      ctx.telegram.editMessageText(statusMsg.chat.id, statusMsg.message_id, null, '_Sending video..._', parseMarkdownExtra);
      message = await ctx.replyWithVideo(video, Object.assign({noWebhook: true}, replyExtra));
      ctx.telegram.deleteMessage(statusMsg.chat.id, statusMsg.message_id);
    }

    if (message.video) {
      fileId = message.video.file_id;
    }
    else {
      fileId = message.document.file_id;
      isDocument = true;
    }

    console.log('isDocument', isDocument);
    await sqlite.run('INSERT OR IGNORE INTO files(id, file_id, is_document) VALUES(?, ?, ?);', videoId, fileId, isDocument ? 1 : 0);
  }
}

async function processUrl(url, ctx) {
  const replyToMessageExtra = {reply_to_message_id: ctx.message.message_id};

  if (!url) {
    await replyWithError('Please provide a video tweet URL.', ctx, replyToMessageExtra);
    return;
  }

  const videoId = getVideoId(url);

  if (!videoId) {
    await replyWithError('Invalid video tweet.', ctx, replyToMessageExtra);
    return;
  }

  await processStatusId(videoId, ctx, replyToMessageExtra);
}

app.command('/start', async (ctx) => {
  const [param] = ctx.state.command.splitArgs;

  if (param) {
    console.log('Deeplinking parameter:', param);
    await processStatusId(param, ctx, {}, true);
  }
  else {
    await ctx.replyWithMarkdown(
`Welcome!

Use /creategif \`<url>\`, or simply send/forward me a message that contains one or more Twitter URLs, and I’ll convert them all to Telegram GIFs for you.

I also work inline, try starting a message with @${mdEscape(ctx.botInfo.username)}, followed by a single video tweet link.

Have fun!`);
  }
});

app.command('/creategif', async (ctx) => {
  const url = ctx.state.command.splitArgs.find((arg) => arg);

  await processUrl(url, ctx);
});

app.hears((text) => !text.startsWith('/'), async (ctx) => {
  const urls = ctx.update.message.text.split(/\s/).filter((arg) => reVideoId.test(arg));

  if (urls.length < 2) {
    await processUrl(urls[0], ctx);
  }
  else {
    for (const url of urls) {
      await processUrl(url, ctx);
    }
  }
});

app.on('inline_query', async (ctx) => {
  console.log('INLINE QUERY:', ctx.inlineQuery);

  const url = ctx.inlineQuery.query.split(/\s/).find((arg) => reVideoId.test(arg));
  const videoId = getVideoId(url);

  const results = [];
  const extra = {
    cache_time: 1,
  };

  if (videoId) {
    const {file_id} = await checkCache(videoId);

    if (file_id) {
      results.push({
        type: 'mpeg4_gif',
        id: videoId,
        mpeg4_file_id: file_id
      });
      extra.cache_time = 31536000;
    }
    else {
      Object.assign(extra, {
        switch_pm_text: 'Press here to get your GIF',
        switch_pm_parameter: videoId
      });
    }
  }

  console.log('Inline answer:', results, extra);
  return ctx.answerInlineQuery(results, extra);
});

module.exports = {
  botHandler: app,
  initialize: (bot) => {
    //bot.webhookReply = false;
    return Promise.resolve();
  },
  requestHandler: (req, res) => {
    console.log('HTTP Request:', req.method, req.url, req.headers);
    if (req.url === '/') {
      res.writeHead(303, {location: 'https://t.me/TwitterGif_Bot'});
      res.end();
    }
    else {
      res.writeHead(404);
      res.end('404 Not Found');
    }
  }
};
<|endoftext|>


<commit_before>var path = require('path');
var through = require('through2');
var cheerio = require('cheerio');
var gutil = require('gulp-util');
var fs = require('fs');
var dateformat = require('dateformat')
var url = require('url');
var _ = require('lodash');
var crypto = require('crypto');

var fileverCache = {};

module.exports = function (options) {

    var defaultOptions = {
        cwd: '',
        base: '',
        suffix: 'v',
        fileTypes: ['js', 'css', 'img'],
        hashLength: 8,
        dateFormat: 'yyyymmddHHMM',
        revType: 'hash',
        transformPath: function (orgPath, rev) {
            var reg = new RegExp('((\\?|\\&|\\&amp\\;)' + options.suffix + '=)([^&\\s]+)', 'gi');
            var newpath = orgPath;
            if (reg.test(orgPath)) {
                newpath = orgPath.replace(reg, "$1" + rev);
            } else {
                newpath += ((orgPath.indexOf('?') > -1 ? '&' : '?') + options.suffix + '=' + rev);
            }
            return newpath
        },
        elementAttributes: {
            js: {
                name: 'script',
                src: 'src'
            },
            css: {
                name: 'link[type="text/css"]',
                src: 'href'
            },
            img: {
                name: 'img',
                src: 'src'
            }
        }
    };

    options = _.assign(defaultOptions, options);

    return through.obj(function (file, enc, cb) {
        if(options.cwd !== '') {
            gutil.log(gutil.colors.red('options.cwd is obsoleted, use options.base or set gulp.src(path, {cwd:mycwd}) instead'));
            return;
        }

        gutil.log("====begin rev:" + gutil.colors.cyan(file.path));
        if (file.isNull()) {
            this.push(file);
            return cb();
        }

        if (file.isStream()) {
            this.emit('error', new gutil.PluginError('gulp-rev-easy', 'Streaming is not supported'));
            return cb();
        }

        try {
            var $ = cheerio.load(file.contents.toString(), {decodeEntities: false});

            var baseDir = options.base;

            if(baseDir === '')
                baseDir = file.cwd;

            for (var i = 0; i < options.fileTypes.length; i++) {
                var fileType = options.fileTypes[i];
                var attributes = options.elementAttributes[fileType];

                var $assets = $(attributes.name);
                for (var j = 0; j < $assets.length; j++) {
                    var $asset = $assets.eq(j);

                    var src = $asset.attr(attributes.src);

                    if (src && !src.match(/.*(\/\/).*/)) {
                        var revv = "";

                        if (options.revType == "hash") {
                            var srcpath = url.parse(src).pathname;

                            //if is a /a/b/c/i.png path need a basedir
                            if ((/^\/{1}[^\/]+/gi).test(srcpath))
                                filepath = path.join(baseDir, srcpath);
                            else
                                filepath = path.join(path.dirname(file.path), srcpath);

                            if (fs.existsSync(filepath)) {

                                if (fileverCache[filepath] !== undefined) {
                                    revv = fileverCache[filepath];
                                    gutil.log(gutil.colors.green(filepath + " found in cache"));
                                }
                                else {
                                    revv = crypto
                                        .createHash('md5')
                                        .update(
                                        fs.readFileSync(filepath, {encoding: 'utf8'}))
                                        .digest("hex").substring(0, options.hashLength);
                                }
                                fileverCache[filepath] = revv;
                            } else {
                                gutil.log(gutil.colors.red(filepath + " not found"));
                            }
                        } else {
                            revv = dateformat(new Date(), options.dateFormat)
                        }

                        if (revv != "") {
                            var newname = options.transformPath(src, revv);
                            $asset.attr(attributes.src, newname);
                            gutil.log(src + " --> ", gutil.colors.green(newname));
                        } else {
                            gutil.log(gutil.colors.blue("ignore:rev is empty "), src);
                        }
                    }
                }
            }
            file.contents = new Buffer($.html());

            gutil.log(fileverCache);
            gutil.log("====end rev:" + gutil.colors.cyan(file.path));
        }
        catch (err) {
            this.emit('error', new gutil.PluginError('gulp-rev-easy', err));
        }

        this.push(file);
        return cb();
    })
};<commit_msg>1.修改css选择器为'link[rel="stylesheet"]'；<commit_after>var path = require('path');
var through = require('through2');
var cheerio = require('cheerio');
var gutil = require('gulp-util');
var fs = require('fs');
var dateformat = require('dateformat');
var url = require('url');
var _ = require('lodash');
var crypto = require('crypto');

var fileverCache = {};

module.exports = function(options) {

    var defaultOptions = {
        cwd: '',
        base: '',
        suffix: 'v',
        fileTypes: ['js', 'css', 'img'],
        hashLength: 8,
        dateFormat: 'yyyymmddHHMM',
        revType: 'hash',
        transformPath: function(orgPath, rev) {
            var reg = new RegExp('((\\?|\\&|\\&amp\\;)' + options.suffix + '=)([^&\\s]+)', 'gi');
            var newpath = orgPath;
            if (reg.test(orgPath)) {
                newpath = orgPath.replace(reg, '$1' + rev);
            } else {
                newpath += ((orgPath.indexOf('?') > -1 ? '&' : '?') + options.suffix + '=' + rev);
            }
            return newpath;
        },
        elementAttributes: {
            js: {
                name: 'script',
                src: 'src'
            },
            css: {
                name: 'link[rel="stylesheet"]',
                src: 'href'
            },
            img: {
                name: 'img',
                src: 'src'
            }
        }
    };

    options = _.assign(defaultOptions, options);

    return through.obj(function(file, enc, cb) {
        if (options.cwd !== '') {
            gutil.log(gutil.colors.red('options.cwd is obsoleted, use options.base or set gulp.src(path, {cwd:mycwd}) instead'));
            return;
        }

        gutil.log('==== begin rev:' + gutil.colors.cyan(file.path));
        if (file.isNull()) {
            this.push(file);
            return cb();
        }

        if (file.isStream()) {
            this.emit('error', new gutil.PluginError('gulp-rev-easy', 'Streaming is not supported'));
            return cb();
        }

        try {
            var $ = cheerio.load(file.contents.toString(), {
                decodeEntities: false
            });

            var baseDir = options.base;

            if (baseDir === '')
                baseDir = file.cwd;

            for (var i = 0; i < options.fileTypes.length; i++) {
                var fileType = options.fileTypes[i];
                var attributes = options.elementAttributes[fileType];

                var $assets = $(attributes.name);
                for (var j = 0; j < $assets.length; j++) {
                    var $asset = $assets.eq(j);

                    var src = $asset.attr(attributes.src);

                    if (src && !src.match(/.*(\/\/).*/)) {
                        var revv = '';

                        if (options.revType == 'hash') {
                            var srcpath = url.parse(src).pathname;

                            //if is a /a/b/c/i.png path need a basedir
                            if ((/^\/{1}[^\/]+/gi).test(srcpath))
                                filepath = path.join(baseDir, srcpath);
                            else
                                filepath = path.join(path.dirname(file.path), srcpath);

                            if (fs.existsSync(filepath)) {

                                if (fileverCache[filepath] !== undefined) {
                                    revv = fileverCache[filepath];
                                    gutil.log(gutil.colors.green(filepath + ' found in cache'));
                                } else {
                                    revv = crypto
                                        .createHash('md5')
                                        .update(
                                            fs.readFileSync(filepath, {
                                                encoding: 'utf8'
                                            }))
                                        .digest('hex').substring(0, options.hashLength);
                                }
                                fileverCache[filepath] = revv;
                            } else {
                                gutil.log(gutil.colors.red(filepath + ' not found'));
                            }
                        } else {
                            revv = dateformat(new Date(), options.dateFormat);
                        }

                        if (revv !== '') {
                            var newname = options.transformPath(src, revv);
                            $asset.attr(attributes.src, newname);
                            gutil.log(src + ' --> ', gutil.colors.green(newname));
                        } else {
                            gutil.log(gutil.colors.blue('ignore:rev is empty'), src);
                        }
                    }
                }
            }
            file.contents = new Buffer($.html());

            gutil.log(fileverCache);
            gutil.log('==== end rev:' + gutil.colors.cyan(file.path));
        } catch (err) {
            this.emit('error', new gutil.PluginError('gulp-rev-easy', err));
        }

        this.push(file);
        return cb();
    });
};
<|endoftext|>


<commit_before>/* vim: set shiftwidth=2 tabstop=2 noexpandtab textwidth=80 wrap : */
"use strict";

import translate from 'translate-component';
import events from 'component-events';
import Emitter from 'component-emitter';
import autoscale from 'autoscale-canvas';

export default VirtualJoystick;

function VirtualJoystick(options) {
	options = options || {};
	this._checkValid       = options.checkValid || function () { return true; };
	this._container        = options.container || document.body;
	this._strokeStyle      = options.strokeStyle || 'cyan';
	this._stickEl          = options.stickElement || this._buildJoystickStick();
	this._baseEl           = options.baseElement || this._buildJoystickBase();
	this._stationaryBase   = options.stationaryBase || false;
	this._baseX            = this._stickX = options.baseX || 0;
	this._baseY            = this._stickY = options.baseY || 0;
	this._limitStickTravel = options.limitStickTravel || false;
	this._stickRadius      = options.stickRadius !== undefined ? options.stickRadius : 100;

	this._container.appendChild(this._baseEl);
	this._baseEl.style.position = "absolute";
	this._baseEl.style.display = "none";
	this._container.appendChild(this._stickEl);
	this._stickEl.style.position = "absolute";
	this._stickEl.style.display = "none";

	this._pressed = false;
	this._touchIdx = null;

	if (this._stationaryBase) {
		this._baseEl.style.display = "block";
		this._baseEl.style.left = (this._baseX - this._baseEl.width  / 2) + "px";
		this._baseEl.style.top  = (this._baseY - this._baseEl.height / 2) + "px";
	}

	this._containerevents = events(this._container, this);
	this._containerevents.bind('mousedown' , '_onMouseDown');
	this._containerevents.bind('touchstart', '_onTouchStart');
	this._docevents = events(document.body, this);
	this._docevents.bind('mousemove', '_onMouseMove');
	this._docevents.bind('mouseup'  , '_onMouseUp');
	this._docevents.bind('touchend' , '_onTouchEnd');
	this._docevents.bind('touchmove', '_onTouchMove');
}

Emitter(VirtualJoystick.prototype);

VirtualJoystick.prototype.destroy = function () {
	this._container.removeChild(this._baseEl);
	this._container.removeChild(this._stickEl);
	this._containerevents.unbind();
	this._docevents.unbind();
}

VirtualJoystick.prototype.deltaX = function () { return this._stickX - this._baseX; };
VirtualJoystick.prototype.deltaY = function () { return this._stickY - this._baseY; };

VirtualJoystick.prototype._onUp = function () {
	this.emit('end');
	this._pressed = false;
	this._stickEl.style.display = "none";

	if (!this._stationaryBase) {
		this._baseEl.style.display = "none";

		this._baseX  = this._baseY  = 0;
		this._stickX = this._stickY = 0;
	}
}

VirtualJoystick.prototype._onDown = function (coords) {
	var x = coords[0], y = coords[1];
	if (!this._checkValid(x, y)) return;

	this.emit('start', x, y);
	this._pressed = true;
	if (!this._stationaryBase) {
		this._baseX = x;
		this._baseY = y;
		this._baseEl.style.display = "block";
		translate(this._baseEl,
		          this._baseX - this._baseEl.width  / 2,
		          this._baseY - this._baseEl.height / 2);
	}

	this._stickX = x;
	this._stickY = y;

	if (this._limitStickTravel) {
		var deltaX = this.deltaX();
		var deltaY = this.deltaY();
		var stickDistance = Math.sqrt((deltaX * deltaX) + (deltaY * deltaY));
		if (stickDistance > this._stickRadius) {
			var stickNormalizedX = deltaX / stickDistance;
			var stickNormalizedY = deltaY / stickDistance;

			this._stickX = stickNormalizedX * this._stickRadius + this._baseX;
			this._stickY = stickNormalizedY * this._stickRadius + this._baseY;
		}
	}

	this._stickEl.style.display = "block";
	translate(this._stickEl,
	          this._stickX - this._stickEl.width  / 2,
	          this._stickY - this._stickEl.height / 2);
}

VirtualJoystick.prototype._onMove = function (coords) {
	if (!this._pressed) return;

	var x = coords[0], y = coords[1];
	this.emit('move', x, y);
	this._stickX = x;
	this._stickY = y;

	if (this._limitStickTravel) {
		var deltaX = this.deltaX();
		var deltaY = this.deltaY();
		var stickDistance = Math.sqrt((deltaX * deltaX) + (deltaY * deltaY));
		if (stickDistance > this._stickRadius) {
			var stickNormalizedX = deltaX / stickDistance;
			var stickNormalizedY = deltaY / stickDistance;

			this._stickX = stickNormalizedX * this._stickRadius + this._baseX;
			this._stickY = stickNormalizedY * this._stickRadius + this._baseY;
		}
	}
	translate(this._stickEl,
	          this._stickX - this._stickEl.width  / 2,
	          this._stickY - this._stickEl.height / 2);
}

VirtualJoystick.prototype._onMouseUp = function (event) {
	return this._onUp();
}

VirtualJoystick.prototype._offset = function (ev) {
	var coords = [ev.clientX, ev.clientY];
	var offset = this._container;
	do {
		coords[0] -= offset.offsetLeft;
		coords[1] -= offset.offsetTop;
	} while (offset = offset.offsetParent);
	return coords;
}

VirtualJoystick.prototype._onMouseDown = function (event) {
	event.preventDefault();
	return this._onDown(this._offset(event));
}

VirtualJoystick.prototype._onMouseMove = function (event) {
	return this._onMove(this._offset(event));
}

VirtualJoystick.prototype._onTouchStart = function (event) {
	// if there is already a touch inprogress do nothing
	if (this._touchIdx !== null) return;

	event.preventDefault();
	// get the first who changed
	var touch = event.changedTouches[0];
	// set the touchIdx of this joystick
	this._touchIdx = touch.identifier;

	// forward the action
	return this._onDown(this._offset(touch));
}

VirtualJoystick.prototype._onTouchEnd = function (event) {
	// if there is no touch in progress, do nothing
	if (this._touchIdx === null) return;

	// try to find our touch event
	var touchList = event.changedTouches;
	for (var i = 0; i < touchList.length && touchList[i].identifier !== this._touchIdx; i++);
	// if touch event isnt found,
	if (i === touchList.length) return;

	// reset touchIdx - mark it as no-touch-in-progress
	this._touchIdx = null;

	//??????
	// no preventDefault to get click event on ios
	event.preventDefault();

	return this._onUp();
}

VirtualJoystick.prototype._onTouchMove = function (event) {
	// if there is no touch in progress, do nothing
	if (this._touchIdx === null) return;

	// try to find our touch event
	var touchList = event.changedTouches;
	for (var i = 0; i < touchList.length && touchList[i].identifier !== this._touchIdx; i++ );
	// if touch event with the proper identifier isnt found, do nothing
	if (i === touchList.length) return;
	var touch = touchList[i];

	event.preventDefault();

	return this._onMove(this._offset(touch));
}

function circle(ctx, style, width, x, y, r) {
	ctx.beginPath();
	ctx.strokeStyle = style;
	ctx.lineWidth = width;
	ctx.arc(x, y, r, 0, Math.PI * 2, true);
	ctx.stroke();
}

VirtualJoystick.prototype._buildJoystickBase = function () {
	var canvas = document.createElement('canvas');
	canvas.width = canvas.height = 126;
	//autoscale(canvas);
	var ctx = canvas.getContext('2d');

	circle(ctx, this._strokeStyle, 6, 63, 63, 40);
	circle(ctx, this._strokeStyle, 2, 63, 63, 60);

	return canvas;
}

VirtualJoystick.prototype._buildJoystickStick = function () {
	var canvas = document.createElement('canvas');
	canvas.width = canvas.height = 86;
	//autoscale(canvas);
	var ctx = canvas.getContext('2d');

	circle(ctx, this._strokeStyle, 6, 43, 43, 40);

	return canvas;
}

<commit_msg>HOTFIX: Implement handlers for touchcancel event<commit_after>/* vim: set shiftwidth=2 tabstop=2 noexpandtab textwidth=80 wrap : */
"use strict";

import translate from 'translate-component';
import events from 'component-events';
import Emitter from 'component-emitter';
import autoscale from 'autoscale-canvas';

export default VirtualJoystick;

function VirtualJoystick(options) {
	options = options || {};
	this._checkValid       = options.checkValid || function () { return true; };
	this._container        = options.container || document.body;
	this._strokeStyle      = options.strokeStyle || 'cyan';
	this._stickEl          = options.stickElement || this._buildJoystickStick();
	this._baseEl           = options.baseElement || this._buildJoystickBase();
	this._stationaryBase   = options.stationaryBase || false;
	this._baseX            = this._stickX = options.baseX || 0;
	this._baseY            = this._stickY = options.baseY || 0;
	this._limitStickTravel = options.limitStickTravel || false;
	this._stickRadius      = options.stickRadius !== undefined ? options.stickRadius : 100;

	this._container.appendChild(this._baseEl);
	this._baseEl.style.position = "absolute";
	this._baseEl.style.display = "none";
	this._container.appendChild(this._stickEl);
	this._stickEl.style.position = "absolute";
	this._stickEl.style.display = "none";

	this._pressed = false;
	this._touchIdx = null;

	if (this._stationaryBase) {
		this._baseEl.style.display = "block";
		this._baseEl.style.left = (this._baseX - this._baseEl.width  / 2) + "px";
		this._baseEl.style.top  = (this._baseY - this._baseEl.height / 2) + "px";
	}

	this._containerevents = events(this._container, this);
	this._containerevents.bind('mousedown' , '_onMouseDown');
	this._containerevents.bind('touchstart', '_onTouchStart');
	this._docevents = events(document.body, this);
	this._docevents.bind('mousemove', '_onMouseMove');
	this._docevents.bind('mouseup'  , '_onMouseUp');
	this._docevents.bind('touchend' , '_onTouchEnd');
	this._docevents.bind('touchmove', '_onTouchMove');
	this._docevents.bind('touchcancel', '_onTouchCancel');
}

Emitter(VirtualJoystick.prototype);

VirtualJoystick.prototype.destroy = function () {
	this._container.removeChild(this._baseEl);
	this._container.removeChild(this._stickEl);
	this._containerevents.unbind();
	this._docevents.unbind();
}

VirtualJoystick.prototype.deltaX = function () { return this._stickX - this._baseX; };
VirtualJoystick.prototype.deltaY = function () { return this._stickY - this._baseY; };

VirtualJoystick.prototype._onUp = function () {
	this.emit('end');
	this._pressed = false;
	this._stickEl.style.display = "none";

	if (!this._stationaryBase) {
		this._baseEl.style.display = "none";

		this._baseX  = this._baseY  = 0;
		this._stickX = this._stickY = 0;
	}
}

VirtualJoystick.prototype._onDown = function (coords) {
	var x = coords[0], y = coords[1];
	if (!this._checkValid(x, y)) return;

	this.emit('start', x, y);
	this._pressed = true;
	if (!this._stationaryBase) {
		this._baseX = x;
		this._baseY = y;
		this._baseEl.style.display = "block";
		translate(this._baseEl,
		          this._baseX - this._baseEl.width  / 2,
		          this._baseY - this._baseEl.height / 2);
	}

	this._stickX = x;
	this._stickY = y;

	if (this._limitStickTravel) {
		var deltaX = this.deltaX();
		var deltaY = this.deltaY();
		var stickDistance = Math.sqrt((deltaX * deltaX) + (deltaY * deltaY));
		if (stickDistance > this._stickRadius) {
			var stickNormalizedX = deltaX / stickDistance;
			var stickNormalizedY = deltaY / stickDistance;

			this._stickX = stickNormalizedX * this._stickRadius + this._baseX;
			this._stickY = stickNormalizedY * this._stickRadius + this._baseY;
		}
	}

	this._stickEl.style.display = "block";
	translate(this._stickEl,
	          this._stickX - this._stickEl.width  / 2,
	          this._stickY - this._stickEl.height / 2);
}

VirtualJoystick.prototype._onMove = function (coords) {
	if (!this._pressed) return;

	var x = coords[0], y = coords[1];
	this.emit('move', x, y);
	this._stickX = x;
	this._stickY = y;

	if (this._limitStickTravel) {
		var deltaX = this.deltaX();
		var deltaY = this.deltaY();
		var stickDistance = Math.sqrt((deltaX * deltaX) + (deltaY * deltaY));
		if (stickDistance > this._stickRadius) {
			var stickNormalizedX = deltaX / stickDistance;
			var stickNormalizedY = deltaY / stickDistance;

			this._stickX = stickNormalizedX * this._stickRadius + this._baseX;
			this._stickY = stickNormalizedY * this._stickRadius + this._baseY;
		}
	}
	translate(this._stickEl,
	          this._stickX - this._stickEl.width  / 2,
	          this._stickY - this._stickEl.height / 2);
}

VirtualJoystick.prototype._onMouseUp = function (event) {
	return this._onUp();
}

VirtualJoystick.prototype._offset = function (ev) {
	var coords = [ev.clientX, ev.clientY];
	var offset = this._container;
	do {
		coords[0] -= offset.offsetLeft;
		coords[1] -= offset.offsetTop;
	} while (offset = offset.offsetParent);
	return coords;
}

VirtualJoystick.prototype._onMouseDown = function (event) {
	event.preventDefault();
	return this._onDown(this._offset(event));
}

VirtualJoystick.prototype._onMouseMove = function (event) {
	return this._onMove(this._offset(event));
}

VirtualJoystick.prototype._onTouchStart = function (event) {
	// if there is already a touch inprogress do nothing
	if (this._touchIdx !== null) return;

	event.preventDefault();
	// get the first who changed
	var touch = event.changedTouches[0];
	// set the touchIdx of this joystick
	this._touchIdx = touch.identifier;

	// forward the action
	return this._onDown(this._offset(touch));
}

VirtualJoystick.prototype._onTouchEnd = function (event) {
	// if there is no touch in progress, do nothing
	if (this._touchIdx === null) return;

	// try to find our touch event
	var touchList = event.changedTouches;
	for (var i = 0; i < touchList.length && touchList[i].identifier !== this._touchIdx; i++);
	// if touch event isnt found,
	if (i === touchList.length) return;

	// reset touchIdx - mark it as no-touch-in-progress
	this._touchIdx = null;

	//??????
	// no preventDefault to get click event on ios
	event.preventDefault();

	return this._onUp();
}

VirtualJoystick.prototype._onTouchMove = function (event) {
	// if there is no touch in progress, do nothing
	if (this._touchIdx === null) return;

	// try to find our touch event
	var touchList = event.changedTouches;
	for (var i = 0; i < touchList.length && touchList[i].identifier !== this._touchIdx; i++ );
	// if touch event with the proper identifier isnt found, do nothing
	if (i === touchList.length) return;
	var touch = touchList[i];

	event.preventDefault();

	return this._onMove(this._offset(touch));
}

Joystick.prototype._onTouchCancel = function (event) {
	this._touchIdx = null;
}

function circle(ctx, style, width, x, y, r) {
	ctx.beginPath();
	ctx.strokeStyle = style;
	ctx.lineWidth = width;
	ctx.arc(x, y, r, 0, Math.PI * 2, true);
	ctx.stroke();
}

VirtualJoystick.prototype._buildJoystickBase = function () {
	var canvas = document.createElement('canvas');
	canvas.width = canvas.height = 126;
	//autoscale(canvas);
	var ctx = canvas.getContext('2d');

	circle(ctx, this._strokeStyle, 6, 63, 63, 40);
	circle(ctx, this._strokeStyle, 2, 63, 63, 60);

	return canvas;
}

VirtualJoystick.prototype._buildJoystickStick = function () {
	var canvas = document.createElement('canvas');
	canvas.width = canvas.height = 86;
	//autoscale(canvas);
	var ctx = canvas.getContext('2d');

	circle(ctx, this._strokeStyle, 6, 43, 43, 40);

	return canvas;
}

<|endoftext|>


<commit_before>/* globals fetch __DEV__ */

export default class RestClient {
  constructor (baseUrl = '', { headers = {}, simulatedDelay = 0 } = {}) {
    if (!baseUrl) throw new Error('missing baseUrl');
    this.headers = {
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    };
    Object.assign(this.headers, headers);
    this.baseUrl = baseUrl;
    this.simulatedDelay = simulatedDelay;
  }

  _simulateDelay () {
    return new Promise(resolve => {
      setTimeout(() => {
        resolve();
      }, this.simulatedDelay);
    });
  }

  _fullRoute (url) {
    return `${this.baseUrl}${url}`;
  }

  _fetch (route, method, body, isQuery = false) {
    if (!route) throw new Error('Route is undefined');
    var fullRoute = this._fullRoute(route);
    if (isQuery && body) {
      var qs = require('qs');
      const query = qs.stringify(body);
      fullRoute = `${fullRoute}?${query}`;
      body = undefined;
    }
    let opts = {
      method,
      headers: this.headers
    };
    if (body) {
      Object.assign(opts, { body: JSON.stringify(body) });
    }
    console.log('opts', opts);
    const fetchPromise = () => fetch(fullRoute, opts);
    if (__DEV__) {
      // Simulate a 2 second delay in evry request
      return this._simulateDelay()
        .then(() => fetchPromise())
        .then(response => response.json());
    } else {
      return fetchPromise()
        .then(response => response.json());
    }
  }

  GET (route, query) { return this._fetch(route, 'GET', query, true); }
  POST (route, body) { return this._fetch(route, 'POST', body); }
  PUT (route, body) { return this._fetch(route, 'PUT', body); }
  DELETE (route, query) { return this._fetch(route, 'DELETE', query, true); }
}
<commit_msg>Removed unwanted console.log and corrected typo in comment<commit_after>/* globals fetch __DEV__ */

export default class RestClient {
  constructor (baseUrl = '', { headers = {}, simulatedDelay = 0 } = {}) {
    if (!baseUrl) throw new Error('missing baseUrl');
    this.headers = {
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    };
    Object.assign(this.headers, headers);
    this.baseUrl = baseUrl;
    this.simulatedDelay = simulatedDelay;
  }

  _simulateDelay () {
    return new Promise(resolve => {
      setTimeout(() => {
        resolve();
      }, this.simulatedDelay);
    });
  }

  _fullRoute (url) {
    return `${this.baseUrl}${url}`;
  }

  _fetch (route, method, body, isQuery = false) {
    if (!route) throw new Error('Route is undefined');
    var fullRoute = this._fullRoute(route);
    if (isQuery && body) {
      var qs = require('qs');
      const query = qs.stringify(body);
      fullRoute = `${fullRoute}?${query}`;
      body = undefined;
    }
    let opts = {
      method,
      headers: this.headers
    };
    if (body) {
      Object.assign(opts, { body: JSON.stringify(body) });
    }
    const fetchPromise = () => fetch(fullRoute, opts);
    if (__DEV__) {
      // Simulate a 2 second delay in every request
      return this._simulateDelay()
        .then(() => fetchPromise())
        .then(response => response.json());
    } else {
      return fetchPromise()
        .then(response => response.json());
    }
  }

  GET (route, query) { return this._fetch(route, 'GET', query, true); }
  POST (route, body) { return this._fetch(route, 'POST', body); }
  PUT (route, body) { return this._fetch(route, 'PUT', body); }
  DELETE (route, query) { return this._fetch(route, 'DELETE', query, true); }
}
<|endoftext|>


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

function cim(prefix, parent, patch) {
  var ob;
  patch = Array.prototype.slice.call(arguments, -1)[0];

  if (typeof patch !== "boolean") {
    patch = false;
  }

  if (typeof prefix === "object" && prefix !== null) {
    parent = prefix;
    prefix = undefined;
  }

  // Patch given object if requested
  if (patch && parent) {
    ob = parent;
    ob._prefixes = ob._prefixes || [];
  }
  // Otherwise create new object and inherit prefixes from parent
  else {
    ob = { _prefixes: [] };
    if (parent && parent._prefixes) {
      ob._prefixes = parent._prefixes.slice();
    }
  }

  consoleProxy(ob);

  // Append new prefix if any
  if (prefix) {
    ob._prefixes.push(prefix);
  }

  ob.info = createLogger("INFO", ob._prefixes);
  ob.warn = createLogger("WARNING", ob._prefixes);
  ob.error = createLogger("ERROR", ob._prefixes);
  ob.log = ob.info;
  return ob;
}

// By default write all logs to stderr
cim.logWrite = function(method, msg){
  process.stderr.write(msg + "\n");
};

cim.getTime = function(){
  return new Date().toString();
};

// Just proxy methods we don't care about to original console object
// Get method list from http://nodejs.org/api/stdio.html
function consoleProxy(ob){
  var methods = ["dir", "time", "timeEnd", "trace", "assert"];
  methods.forEach(function(method){
    if (ob[method]) return;
    ob[method] = function(){
      return console[method].apply(console, arguments);
    };
  });
}

function createLogger(method, prefixes) {
  return function () {
    var msg = util.format.apply(this, arguments);
    var prefix = method + " " + prefixes.join(" ");

    if (cim.getTime) {
      prefix = cim.getTime() + " " + prefix;
    }

    cim.logWrite(method, prefix.trim() + " " + msg);
  };
}

module.exports = cim;
<commit_msg>Rename WARNING to WARN<commit_after>
var util = require("util");

function cim(prefix, parent, patch) {
  var ob;
  patch = Array.prototype.slice.call(arguments, -1)[0];

  if (typeof patch !== "boolean") {
    patch = false;
  }

  if (typeof prefix === "object" && prefix !== null) {
    parent = prefix;
    prefix = undefined;
  }

  // Patch given object if requested
  if (patch && parent) {
    ob = parent;
    ob._prefixes = ob._prefixes || [];
  }
  // Otherwise create new object and inherit prefixes from parent
  else {
    ob = { _prefixes: [] };
    if (parent && parent._prefixes) {
      ob._prefixes = parent._prefixes.slice();
    }
  }

  consoleProxy(ob);

  // Append new prefix if any
  if (prefix) {
    ob._prefixes.push(prefix);
  }

  ob.info = createLogger("INFO", ob._prefixes);
  ob.warn = createLogger("WARN", ob._prefixes);
  ob.error = createLogger("ERROR", ob._prefixes);
  ob.log = ob.info;
  return ob;
}

// By default write all logs to stderr
cim.logWrite = function(method, msg){
  process.stderr.write(msg + "\n");
};

cim.getTime = function(){
  return new Date().toString();
};

// Just proxy methods we don't care about to original console object
// Get method list from http://nodejs.org/api/stdio.html
function consoleProxy(ob){
  var methods = ["dir", "time", "timeEnd", "trace", "assert"];
  methods.forEach(function(method){
    if (ob[method]) return;
    ob[method] = function(){
      return console[method].apply(console, arguments);
    };
  });
}

function createLogger(method, prefixes) {
  return function () {
    var msg = util.format.apply(this, arguments);
    var prefix = method + " " + prefixes.join(" ");

    if (cim.getTime) {
      prefix = cim.getTime() + " " + prefix;
    }

    cim.logWrite(method, prefix.trim() + " " + msg);
  };
}

module.exports = cim;
<|endoftext|>


<commit_before>// vim: set ts=2 sw=2 et ai si :

var Epoll = require('epoll').Epoll;
var Promise = require('bluebird');
var fs = require('fs');
var util = require('util');
var EventEmitter = require('events').EventEmitter;

var basePath = '/sys/class/gpio/';
var byPin = {};
var byFD = {};

var promiseOrCallback = function(thiz, promise, callback) {
  if (typeof callback !== 'function') {
    return promise;
  }
  promise.then(function() {
    var args = Array.prototype.slice.call(arguments);
    args.unshift(null);
    callback.apply(thiz, args);
  }).catch(function(e) {
    callback.apply(thiz, [e]);
  });
}

var poller = new Epoll(function (err, fd, events) {
  // Read GPIO value file. Reading also clears the interrupt.
  var buffer = new Buffer(1);
  // readSync here is used intentionally
  // it guarantees that events are emited in order
  fs.readSync(fd, buffer, 0, 1, 0);
  var pin = byFD[fd];
  if (pin) {
    var value = parseInt(buffer.toString()) ? 0 : 1;
    pin.emit('interrupt', value);
  };
});

var isExported = function(pin, callback) {
  var file = basePath + 'gpio' + pin + '/value';
  fs.stat(file, function(err, stat) {
    callback(!err);
  });
};

var Pin = function(pin) {
  EventEmitter.call(this);
  this.pin = pin;
}

util.inherits(Pin, EventEmitter);

var readFile = function(name) {
  return new Promise(function(fulfill, reject) {
    fs.readFile(name, function(err, data) {
      if (err) {
        reject(err);
      } else {
        fulfill(data.toString().trim());
      }
    });
  });
};

var writeFile = function(name, data) {
  return new Promise(function(fulfill, reject) {
    fs.writeFile(name, data, function(err) {
      if (err) {
        reject(err);
      } else {
        fulfill();
      }
    });
  });
};

Pin.prototype.unexport = function(callback) {
  var self = this;
  var promise = new Promise(function(fulfill, reject) {
    var file = basePath + 'unexport';
    if (self.fd) {
      poller.remove(self.fd);
      fs.close(self.fd);
      delete byFD[self.fd];
    }
    delete byPin[self.pin];
    self.fd = null;
    fs.stat(file, function(err, stat) {
      if (err) {
        reject(err);
      } else {
        isExported(self.pin, function(yes) {
          if (yes) {
            fs.writeFile(file, self.pin, function(err) {
              if (err) {
                reject(err);
              } else {
                fulfill();
              }
            });
          } else {
            fulfill();
          }
        });
      }
    });
  });
  return promiseOrCallback(this, promise, callback);
};

Pin.prototype.value = function(callback) {
  var file = basePath + 'gpio' + this.pin + '/value';
  var promise = new Promise(function(fulfill, reject) {
    fs.readFile(file, function(err, data) {
      if (err) {
        reject(err);
      } else {
        fulfill(parseInt(data));
      }
    });
  });
  return promiseOrCallback(this, promise, callback);
};

Pin.prototype.set = function(value, callback) {
  if (arguments.length == 1 && typeof value == 'function') {
    callback = value;
    value = undefined;
  }
  var file = basePath + 'gpio' + this.pin + '/value';
  var promise = new Promise(function(fulfill, reject) {
    if (typeof value === 'undefined') {
      value = 1;
    }
    value = value ? 1 : 0;
    fs.writeFile(file, value, function(err) {
      if (err) {
        reject(err);
      } else {
        fulfill();
      }
    });
  });
  return promiseOrCallback(this, promise, callback);
};

Pin.prototype.reset = function(callback) {
  return this.set(0, callback);
};

Pin.prototype.toggle = function(callback) {
  var file = basePath + 'gpio' + this.pin + '/value';
  var self = this;
  var promise = self.value().then(function(val) {
    return self.set(val ? 0 : 1).then(function() {
      return val;
    });
  });
  return promiseOrCallback(this, promise, callback);
};

var readOrWrite = function(file, args, boolValue) {
  var value, callback;
  if (args.length == 1 && typeof args[0] === 'function') {
    callback = args[0];
  } else if (args.length == 1) {
    value = args[0];
  } else if (args.length > 1) {
    value = args[0];
    callback = args[1];
  }
  if (boolValue) {
    value = value ? 1 : 0;
  }
  var promise;
  if (typeof value === 'undefined') {
    promise = readFile(file);
  } else {
    promise = writeFile(file, value);
  }
  return promiseOrCallback(this, promise, callback);
}

Pin.prototype.direction = function(value, callback) {
  var file = basePath + 'gpio' + this.pin + '/direction';
  return readOrWrite(file, arguments);
}

Pin.prototype.edge = function(value, callback) {
  var file = basePath + 'gpio' + this.pin + '/edge';
  return readOrWrite(file, arguments);
}

Pin.prototype.invert = function(value, callback) {
  var file = basePath + 'gpio' + this.pin + '/active_low';
  return readOrWrite(file, arguments, true);
}

var watchPin = function(pin) {
  return new Promise(function(fulfill, reject) {
    var file = basePath + 'gpio' + pin.pin + '/value';
    fs.open(file, 'r', function(err, fd) {
      if (err) {
        reject(err);
      } else {
        var buffer = new Buffer(1);
        fs.read(fd, buffer, 0, 1, 0, function(err) {
          if (err) {
            reject(err);
          } else {
            pin.fd = fd;
            byFD[fd] = pin;
            poller.add(fd, Epoll.EPOLLPRI);
            fulfill();
          }
        });
      }
    });
  });
};

Pin.export = function(pin, callback) {
  var file = basePath + 'export';
  fs.stat(file, function(err, stat) {
    if (err) {
      return callback(err);
    }
    isExported(pin, function(yes) {
      if (!yes) {
        fs.writeFile(file, pin, function(err) {
          if (err) {
            return callback(err);
          }
          return callback(null, new Pin(pin));
        });
      } else {
        return callback(null, new Pin(pin));
      }
    })
  });
};

var _export = function(pin, options, callback) {
  if (arguments.length == 2 && typeof options === 'function') {
    callback = options;
    options = {};
  }
  if (typeof options !== 'object') {
    options = {};
  }
  pin = parseInt(pin);
  var promise = new Promise(function(fulfill, reject) {
    if (isNaN(pin)) {
      reject(new Error('Invalid pin number'));
      return;
    }
    if (byPin[pin]) {
      fulfill(byPin[pin]);
      return;
    }
    Pin.export(pin, function(err, thePin) {
      if (err) {
        reject(err);
        return;
      }
      byPin[pin] = thePin;
      byFD[thePin.fd] = thePin;
      fulfill(thePin);
    });
  });
  var thePin;
  var ret = promise.then(function(p) {
    thePin = p;
    if (typeof options.direction !== 'undefined') {
      return thePin.direction(options.direction);
    }
  }).then(function() {
    if (typeof options.invert !== 'undefined') {
      return thePin.invert(options.invert);
    }
  }).then(function() {
    if (typeof options.edge !== 'undefined') {
      return thePin.edge(options.edge);
    }
  }).then(function() {
    return watchPin(thePin);
  }).then(function() {
    return thePin;
  });
  return promiseOrCallback(this, ret, callback);
};

var close = function() {
  poller.close();
  for (var pinNumber in byPin) {
    var pin = byPin[pinNumber];
    if (pin.fd) {
      fs.close(pin.fd);
      pin.fd = null;
    }
  }
  byPin = {};
  byFD = {};
};

module.exports = {
  export: _export,
  close: close,
  DIR_IN : 'in',
  DIR_OUT : 'out',
  EDGE_NONE: 'none',
  EDGE_RISING: 'rising',
  EDGE_FALLING: 'falling',
  EDGE_BOTH: 'both'
};

<commit_msg>fix invert() method<commit_after>// vim: set ts=2 sw=2 et ai si :

var Epoll = require('epoll').Epoll;
var Promise = require('bluebird');
var fs = require('fs');
var util = require('util');
var EventEmitter = require('events').EventEmitter;

var basePath = '/sys/class/gpio/';
var byPin = {};
var byFD = {};

var promiseOrCallback = function(thiz, promise, callback) {
  if (typeof callback !== 'function') {
    return promise;
  }
  promise.then(function() {
    var args = Array.prototype.slice.call(arguments);
    args.unshift(null);
    callback.apply(thiz, args);
  }).catch(function(e) {
    callback.apply(thiz, [e]);
  });
}

var poller = new Epoll(function (err, fd, events) {
  // Read GPIO value file. Reading also clears the interrupt.
  var buffer = new Buffer(1);
  // readSync here is used intentionally
  // it guarantees that events are emited in order
  fs.readSync(fd, buffer, 0, 1, 0);
  var pin = byFD[fd];
  if (pin) {
    var value = parseInt(buffer.toString()) ? 0 : 1;
    pin.emit('interrupt', value);
  };
});

var isExported = function(pin, callback) {
  var file = basePath + 'gpio' + pin + '/value';
  fs.stat(file, function(err, stat) {
    callback(!err);
  });
};

var Pin = function(pin) {
  EventEmitter.call(this);
  this.pin = pin;
}

util.inherits(Pin, EventEmitter);

var readFile = function(name) {
  return new Promise(function(fulfill, reject) {
    fs.readFile(name, function(err, data) {
      if (err) {
        reject(err);
      } else {
        fulfill(data.toString().trim());
      }
    });
  });
};

var writeFile = function(name, data) {
  return new Promise(function(fulfill, reject) {
    fs.writeFile(name, data, function(err) {
      if (err) {
        reject(err);
      } else {
        fulfill();
      }
    });
  });
};

Pin.prototype.unexport = function(callback) {
  var self = this;
  var promise = new Promise(function(fulfill, reject) {
    var file = basePath + 'unexport';
    if (self.fd) {
      poller.remove(self.fd);
      fs.close(self.fd);
      delete byFD[self.fd];
    }
    delete byPin[self.pin];
    self.fd = null;
    fs.stat(file, function(err, stat) {
      if (err) {
        reject(err);
      } else {
        isExported(self.pin, function(yes) {
          if (yes) {
            fs.writeFile(file, self.pin, function(err) {
              if (err) {
                reject(err);
              } else {
                fulfill();
              }
            });
          } else {
            fulfill();
          }
        });
      }
    });
  });
  return promiseOrCallback(this, promise, callback);
};

Pin.prototype.value = function(callback) {
  var file = basePath + 'gpio' + this.pin + '/value';
  var promise = new Promise(function(fulfill, reject) {
    fs.readFile(file, function(err, data) {
      if (err) {
        reject(err);
      } else {
        fulfill(parseInt(data));
      }
    });
  });
  return promiseOrCallback(this, promise, callback);
};

Pin.prototype.set = function(value, callback) {
  if (arguments.length == 1 && typeof value == 'function') {
    callback = value;
    value = undefined;
  }
  var file = basePath + 'gpio' + this.pin + '/value';
  var promise = new Promise(function(fulfill, reject) {
    if (typeof value === 'undefined') {
      value = 1;
    }
    value = value ? 1 : 0;
    fs.writeFile(file, value, function(err) {
      if (err) {
        reject(err);
      } else {
        fulfill();
      }
    });
  });
  return promiseOrCallback(this, promise, callback);
};

Pin.prototype.reset = function(callback) {
  return this.set(0, callback);
};

Pin.prototype.toggle = function(callback) {
  var file = basePath + 'gpio' + this.pin + '/value';
  var self = this;
  var promise = self.value().then(function(val) {
    return self.set(val ? 0 : 1).then(function() {
      return val;
    });
  });
  return promiseOrCallback(this, promise, callback);
};

var readOrWrite = function(file, args, boolValue) {
  var value, callback;
  if (args.length == 1 && typeof args[0] === 'function') {
    callback = args[0];
  } else if (args.length == 1) {
    value = args[0];
  } else if (args.length > 1) {
    value = args[0];
    callback = args[1];
  }
  if (boolValue && typeof value !== 'undefined') {
    value = value ? 1 : 0;
  }
  var promise;
  if (typeof value === 'undefined') {
    promise = readFile(file);
  } else {
    promise = writeFile(file, value);
  }
  return promiseOrCallback(this, promise, callback);
}

Pin.prototype.direction = function(value, callback) {
  var file = basePath + 'gpio' + this.pin + '/direction';
  return readOrWrite(file, arguments);
}

Pin.prototype.edge = function(value, callback) {
  var file = basePath + 'gpio' + this.pin + '/edge';
  return readOrWrite(file, arguments);
}

Pin.prototype.invert = function(value, callback) {
  var file = basePath + 'gpio' + this.pin + '/active_low';
  return readOrWrite(file, arguments, true);
}

var watchPin = function(pin) {
  return new Promise(function(fulfill, reject) {
    var file = basePath + 'gpio' + pin.pin + '/value';
    fs.open(file, 'r', function(err, fd) {
      if (err) {
        reject(err);
      } else {
        var buffer = new Buffer(1);
        fs.read(fd, buffer, 0, 1, 0, function(err) {
          if (err) {
            reject(err);
          } else {
            pin.fd = fd;
            byFD[fd] = pin;
            poller.add(fd, Epoll.EPOLLPRI);
            fulfill();
          }
        });
      }
    });
  });
};

Pin.export = function(pin, callback) {
  var file = basePath + 'export';
  fs.stat(file, function(err, stat) {
    if (err) {
      return callback(err);
    }
    isExported(pin, function(yes) {
      if (!yes) {
        fs.writeFile(file, pin, function(err) {
          if (err) {
            return callback(err);
          }
          return callback(null, new Pin(pin));
        });
      } else {
        return callback(null, new Pin(pin));
      }
    })
  });
};

var _export = function(pin, options, callback) {
  if (arguments.length == 2 && typeof options === 'function') {
    callback = options;
    options = {};
  }
  if (typeof options !== 'object') {
    options = {};
  }
  pin = parseInt(pin);
  var promise = new Promise(function(fulfill, reject) {
    if (isNaN(pin)) {
      reject(new Error('Invalid pin number'));
      return;
    }
    if (byPin[pin]) {
      fulfill(byPin[pin]);
      return;
    }
    Pin.export(pin, function(err, thePin) {
      if (err) {
        reject(err);
        return;
      }
      byPin[pin] = thePin;
      byFD[thePin.fd] = thePin;
      fulfill(thePin);
    });
  });
  var thePin;
  var ret = promise.then(function(p) {
    thePin = p;
    if (typeof options.direction !== 'undefined') {
      return thePin.direction(options.direction);
    }
  }).then(function() {
    if (typeof options.invert !== 'undefined') {
      return thePin.invert(options.invert);
    }
  }).then(function() {
    if (typeof options.edge !== 'undefined') {
      return thePin.edge(options.edge);
    }
  }).then(function() {
    return watchPin(thePin);
  }).then(function() {
    return thePin;
  });
  return promiseOrCallback(this, ret, callback);
};

var close = function() {
  poller.close();
  for (var pinNumber in byPin) {
    var pin = byPin[pinNumber];
    if (pin.fd) {
      fs.close(pin.fd);
      pin.fd = null;
    }
  }
  byPin = {};
  byFD = {};
};

module.exports = {
  export: _export,
  close: close,
  DIR_IN : 'in',
  DIR_OUT : 'out',
  EDGE_NONE: 'none',
  EDGE_RISING: 'rising',
  EDGE_FALLING: 'falling',
  EDGE_BOTH: 'both'
};

<|endoftext|>


<commit_before>"use strict"

const Style = (open, close) => ({
  open: `\x1b[${open}m`,
  close: `\x1b[${close}m`,
  strip: new RegExp(`\\x1b\\[${close}m`, "g")
})

const Styles = {
  reset: Style(0, 0),
  bold: Style(1, 22),
  dim: Style(2, 22),
  italic: Style(3, 23),
  underline: Style(4, 24),
  inverse: Style(7, 27),
  hidden: Style(8, 28),
  strikethrough: Style(9, 29),

  black: Style(30, 39), // Dark
  red: Style(91, 39),
  green: Style(92, 39),
  yellow: Style(93, 39),
  blue: Style(94, 39),
  magenta: Style(95, 39),
  cyan: Style(96, 39),
  white: Style(97, 39),
  gray: Style(90, 39),

  bgBlack: Style(40, 49), // Dark
  bgRed: Style(101, 49),
  bgGreen: Style(102, 49),
  bgYellow: Style(103, 49),
  bgBlue: Style(104, 49),
  bgMagenta: Style(105, 49),
  bgCyan: Style(106, 49),
  bgWhite: Style(107, 49)
}

const Turbocolor = { enabled: true, Styles }

const color = function(out) {
  if (!Turbocolor.enabled) return out

  let i, style
  const styles = this.styles
  const length = styles.length

  for (i = 0, out = out + ""; i < length; i++) {
    style = Styles[styles[i]]
    out = `${style.open}${out.replace(style.strip, style.open)}${style.close}`
  }

  return out
}

const defineProperty = Object.defineProperty

for (const style in Styles) {
  defineProperty(Turbocolor, style, {
    get() {
      if (this.styles === undefined) {
        const o = {}
        const f = color.bind(o)

        f.__proto__ = Turbocolor
        f.styles = o.styles = [style]

        return f
      }

      this.styles.push(style)
      return this
    }
  })
}

module.exports = Turbocolor
<commit_msg>refactor: move consts to top<commit_after>"use strict"

const defineProperty = Object.defineProperty

const Style = (open, close) => ({
  open: `\x1b[${open}m`,
  close: `\x1b[${close}m`,
  strip: new RegExp(`\\x1b\\[${close}m`, "g")
})

const Styles = {
  reset: Style(0, 0),
  bold: Style(1, 22),
  dim: Style(2, 22),
  italic: Style(3, 23),
  underline: Style(4, 24),
  inverse: Style(7, 27),
  hidden: Style(8, 28),
  strikethrough: Style(9, 29),

  black: Style(30, 39), // Dark
  red: Style(91, 39),
  green: Style(92, 39),
  yellow: Style(93, 39),
  blue: Style(94, 39),
  magenta: Style(95, 39),
  cyan: Style(96, 39),
  white: Style(97, 39),
  gray: Style(90, 39),

  bgBlack: Style(40, 49), // Dark
  bgRed: Style(101, 49),
  bgGreen: Style(102, 49),
  bgYellow: Style(103, 49),
  bgBlue: Style(104, 49),
  bgMagenta: Style(105, 49),
  bgCyan: Style(106, 49),
  bgWhite: Style(107, 49)
}

const Turbocolor = { enabled: true, Styles }

const color = function(out) {
  if (!Turbocolor.enabled) return out

  let i, style
  const styles = this.styles
  const length = styles.length

  for (i = 0, out = out + ""; i < length; i++) {
    style = Styles[styles[i]]
    out = `${style.open}${out.replace(style.strip, style.open)}${style.close}`
  }

  return out
}

for (const style in Styles) {
  defineProperty(Turbocolor, style, {
    get() {
      if (this.styles === undefined) {
        const o = {}
        const f = color.bind(o)

        f.__proto__ = Turbocolor
        f.styles = o.styles = [style]

        return f
      }

      this.styles.push(style)
      return this
    }
  })
}

module.exports = Turbocolor
<|endoftext|>


<commit_before>export default require('./lib');
<commit_msg>alter index<commit_after>module.exports = require('./lib');<|endoftext|>