<commit_before>import fs from 'fs';
import path from 'path';
import glob from 'glob';
import test from 'tape';
import documentation from 'documentation';
import camelcase from 'camelcase';
import * as turf from '.';

// Helpers
const directory = path.join(__dirname, '..');
let modules = [];
for (const name of fs.readdirSync(directory)) {
    const pckgPath = path.join(directory, name, 'package.json');
    if (!fs.existsSync(pckgPath)) continue;
    const pckg = JSON.parse(fs.readFileSync(pckgPath));
    modules.push({
        name,
        dir: path.join(directory, name),
        pckg,
        dependencies: pckg.dependencies || {},
        devDependencies: pckg.devDependencies || {}
    });
}
// Exclude main Turf module
modules = modules.filter(({name}) => name !== 'turf');

test('turf -- required files', t => {
    for (const {name, dir} of modules) {
        for (const filename of ['test.js', 'bench.js', 'index.js', 'index.d.ts', 'LICENSE', 'README.md', 'yarn.lock']) {
            if (!fs.existsSync(path.join(dir, filename))) t.fail(`${name} ${filename} is required`);
        }
        // if (!fs.existsSync(path.join(dir, 'types.ts'))) t.fail(`${name} types.ts is required`);
    }
    t.skip('add "types.ts" to all packages');
    t.end();
});

test('turf -- invalid dependencies', t => {
    for (const {name, dependencies, devDependencies} of modules) {
        for (const invalidDependency of ['load-json-file', 'write-json-file', 'tape', 'benchmark', 'glob', 'lerna', 'documentation', 'uglify-js']) {
            if (dependencies[invalidDependency]) t.fail(`${name} ${invalidDependency} should be defined as devDependencies`);
        }
        if (devDependencies['eslint'] || devDependencies['eslint-config-mourner']) t.fail(`${name} eslint is handled at the root level`);
        // if (devDependencies['mkdirp']) t.fail(`${name} tests should not have to create folders`);
    }
    t.skip('remove "mkdirp" from testing');
    t.end();
});

test('turf -- strict version dependencies', t => {
    for (const {name, dependencies} of modules) {
        if (dependencies['jsts'] && dependencies['jsts'] !== '1.4.0') t.fail(`${name} jsts must use v1.3.0`);
    }
    t.end();
});

test('turf -- duplicate dependencies', t => {
    for (const {name, dependencies, devDependencies} of modules) {
        for (const dependency of Object.keys(dependencies)) {
            if (devDependencies[dependency]) t.fail(`${name} ${dependency} is duplicated in devDependencies`);
        }
    }
    t.end();
});

test('turf -- check if files exists', t => {
    for (const {name, dir, pckg} of modules) {
        const {files} = pckg;
        if (!files || !files.length) t.fail(`${name} (files) must be included in package.json`);
        for (const file of files) {
            if (!fs.existsSync(path.join(dir, file))) t.fail(`${name} missing file ${file} in "files"`);
        }
    }
    t.end();
});

test('turf -- MIT license', t => {
    const text = fs.readFileSync(path.join(__dirname, 'LICENSE'), 'utf8');
    for (const {name, dir, pckg} of modules) {
        const {license} = pckg;
        if (license !== 'MIT') t.fail(`${name} (license) must be "MIT"`);
        if (fs.readFileSync(path.join(dir, 'LICENSE'), 'utf8') !== text) t.fail(`${name} (LICENSE) is different from @turf/turf`);
    }
    t.end();
});

test('turf -- contributors', t => {
    for (const {name, pckg} of modules) {
        for (const contributor of pckg.contributors || []) {
            if (!contributor.match(/<@.+>/)) t.fail(`${name} ${contributor} (contributors) should use "Full Name <@GitHub>"`);
        }
    }
    t.end();
});

test('turf -- scoped package name', t => {
    for (const {name, pckg} of modules) {
        const expected = name.replace('turf-', '@turf/');
        if (pckg.name !== expected) t.fail(`${name} (name) must use ${expected} in package.json`);
    }
    t.end();
});

test('turf -- pre-defined attributes in package.json', t => {
    for (const {name, pckg} of modules) {
        if (pckg.author !== 'Turf Authors') t.fail(name + ' (author) should be "Turf Authors"');
        if (pckg.main !== 'dist/index') t.fail(`${name} (main) must be "dist/index" in package.json`);
        if (pckg.module !== 'index') t.fail(`${name} (module) must be "index" in package.json`);
        if (pckg['jsnext:main'] !== 'index') t.fail(`${name} (jsnext:main) must be "index" in package.json`);
        if (pckg.types !== 'index.d.ts') t.fail(`${name} (types) must be "index.d.ts" in package.json`);
        if (!pckg.bugs || pckg.bugs.url !== 'https://github.com/Turfjs/turf/issues') t.fail(`${name} (bugs.url) must be "https://github.com/Turfjs/turf/issues" in package.json`);
        if (pckg.homepage !== 'https://github.com/Turfjs/turf') t.fail(`${name} (homepage) must be "https://github.com/Turfjs/turf" in package.json`);
    }
    t.end();
});

test('turf -- parsing dependencies from index.js', t => {
    for (const {name, dir, dependencies} of modules) {
        const index = fs.readFileSync(path.join(dir, 'index.js'), 'utf8');

        // Read Depedencies from index.js
        const dependenciesUsed = new Set();
        for (const dependency of index.match(/(require\(|from )'[@/a-z-\d]+'/gi) || []) {
            const dependencyName = dependency.split(/'/)[1];
            if (!dependencies[dependencyName]) t.fail(`${name} ${dependencyName} is missing from dependencies`);
            if (dependenciesUsed.has(dependencyName)) t.fail(`${name} ${dependencyName} is duplicated in index.js`);
            dependenciesUsed.add(dependencyName);
        }

        // Read Dependencies from package.json
        for (const dependencyName of Object.keys(dependencies)) {
            // Ignore @turf/helpers since it could be used in Typescript definition
            switch (dependencyName) {
            case '@turf/helpers':
            case '@turf/invariant':
            case '@turf/meta':
                continue;
            }
            if (!dependenciesUsed.has(dependencyName)) t.fail(`${name} ${dependencyName} is not required in index.js`);
        }
    }
    t.end();
});

/**
 * =========================
 * Builds => test.example.js
 * =========================
 * will be run as `posttest`
 */

// File Paths
const testFilePath = path.join(__dirname, 'test.example.js');
const turfModulesPath = path.join(__dirname, '..', 'turf-*', 'index.js');

// Test Strings
const requireString = `const test = require('tape');
const turf = require('.');
`;

/**
 * Generate Test String
 *
 * @param {Object} turfFunction Documentation function object
 * @param {Object} example Documentation example object
 * @returns {string} Test String
 */
function testString(turfFunction, example) {
    const turfName = turfFunction.name;
    const testFunctionName = turfName + 'Test';

    // New modules will be excluded from tests
    if (!turf.hasOwnProperty(turfName)) return `
test('turf-${turfName}', t => {
    t.skip('${turfName}');
    t.end();
});
`;
    return `
test('turf-${turfName}', t => {
    const ${testFunctionName} = () => {
        ${example.description}
    }
    ${testFunctionName}();
    t.pass('${turfName}');
    t.end();
});
`;
}

// // Test for missing modules
// test('turf -- missing modules', t => {
//     const files = {
//         typescript: fs.readFileSync(path.join(__dirname, 'index.d.ts')),
//         modules: fs.readFileSync(path.join(__dirname, 'index.js'))
//     };

//     modules.forEach(({name}) => {
//         name = camelcase(name.replace('turf-', ''));
//         // name exception with linestring => lineString
//         name = name.replace('linestring', 'lineString').replace('Linestring', 'LineString');

//         if (!files.typescript.includes(name)) t.fail(name + ' is missing from index.d.ts');
//         if (!files.modules.includes(name)) t.fail(name + ' is missing from index.js');

//         switch (typeof turf[name]) {
//         case 'function': break;
//         case 'object':
//             Object.keys(turf[name]).forEach(method => {
//                 if (typeof turf[method] !== 'function') t.skip(name + '.' + method + ' is missing from index.js');
//                 if (!files.typescript.includes(method)) t.skip(name + '.' + method + ' is missing from index.d.ts');
//                 if (!files.modules.includes(method)) t.skip(name + '.' + method + ' is missing from index.js');
//             });
//             break;
//         case 'undefined':
//             t.fail(name + ' is missing from index.js');
//         }
//     });
//     t.end();
// });

// // Iterate over each module and retrieve @example to build tests from them
// glob(turfModulesPath, (err, files) => {
//     if (err) throw err;

//     // Read each JSDocs from index.js files
//     documentation.build(files, {}).then(turfFunctions => {
//         if (err) throw err;

//         // Write header of test.js
//         const writeableStream = fs.createWriteStream(testFilePath);
//         writeableStream.write(requireString);
//         writeableStream.on('error', err => { throw err; });

//         // Retrieve @example
//         turfFunctions.forEach(turfFunction => {
//             if (turfFunction.examples) {

//                 // Save to test.js
//                 turfFunction.examples.forEach(example => {
//                     writeableStream.write(testString(turfFunction, example));
//                 });
//             }
//         });
//         writeableStream.end();
//     });
// });
<commit_msg>Update test.js<commit_after>import fs from 'fs';
import path from 'path';
import glob from 'glob';
import test from 'tape';
import documentation from 'documentation';
import camelcase from 'camelcase';
import * as turf from '.';

// Helpers
const directory = path.join(__dirname, '..');
let modules = [];
for (const name of fs.readdirSync(directory)) {
    const pckgPath = path.join(directory, name, 'package.json');
    if (!fs.existsSync(pckgPath)) continue;
    const pckg = JSON.parse(fs.readFileSync(pckgPath));
    modules.push({
        name,
        dir: path.join(directory, name),
        pckg,
        dependencies: pckg.dependencies || {},
        devDependencies: pckg.devDependencies || {}
    });
}
// Exclude main Turf module
modules = modules.filter(({name}) => name !== 'turf');

test('turf -- required files', t => {
    for (const {name, dir} of modules) {
        for (const filename of ['test.js', 'bench.js', 'index.js', 'index.d.ts', 'LICENSE', 'README.md', 'yarn.lock']) {
            if (!fs.existsSync(path.join(dir, filename))) t.fail(`${name} ${filename} is required`);
        }
        // if (!fs.existsSync(path.join(dir, 'types.ts'))) t.fail(`${name} types.ts is required`);
    }
    t.skip('add "types.ts" to all packages');
    t.end();
});

test('turf -- invalid dependencies', t => {
    for (const {name, dependencies, devDependencies} of modules) {
        for (const invalidDependency of ['load-json-file', 'write-json-file', 'tape', 'benchmark', 'glob', 'lerna', 'documentation', 'uglify-js']) {
            if (dependencies[invalidDependency]) t.fail(`${name} ${invalidDependency} should be defined as devDependencies`);
        }
        if (devDependencies['eslint'] || devDependencies['eslint-config-mourner']) t.fail(`${name} eslint is handled at the root level`);
        // if (devDependencies['mkdirp']) t.fail(`${name} tests should not have to create folders`);
    }
    t.skip('remove "mkdirp" from testing');
    t.end();
});

test('turf -- strict version dependencies', t => {
    for (const {name, dependencies} of modules) {
        if (dependencies['jsts'] && dependencies['jsts'] !== '1.4.0') t.fail(name + ' jsts must use v1.4.0');
    }
    t.end();
});

test('turf -- duplicate dependencies', t => {
    for (const {name, dependencies, devDependencies} of modules) {
        for (const dependency of Object.keys(dependencies)) {
            if (devDependencies[dependency]) t.fail(`${name} ${dependency} is duplicated in devDependencies`);
        }
    }
    t.end();
});

test('turf -- check if files exists', t => {
    for (const {name, dir, pckg} of modules) {
        const {files} = pckg;
        if (!files || !files.length) t.fail(`${name} (files) must be included in package.json`);
        for (const file of files) {
            if (!fs.existsSync(path.join(dir, file))) t.fail(`${name} missing file ${file} in "files"`);
        }
    }
    t.end();
});

test('turf -- MIT license', t => {
    const text = fs.readFileSync(path.join(__dirname, 'LICENSE'), 'utf8');
    for (const {name, dir, pckg} of modules) {
        const {license} = pckg;
        if (license !== 'MIT') t.fail(`${name} (license) must be "MIT"`);
        if (fs.readFileSync(path.join(dir, 'LICENSE'), 'utf8') !== text) t.fail(`${name} (LICENSE) is different from @turf/turf`);
    }
    t.end();
});

test('turf -- contributors', t => {
    for (const {name, pckg} of modules) {
        for (const contributor of pckg.contributors || []) {
            if (!contributor.match(/<@.+>/)) t.fail(`${name} ${contributor} (contributors) should use "Full Name <@GitHub>"`);
        }
    }
    t.end();
});

test('turf -- scoped package name', t => {
    for (const {name, pckg} of modules) {
        const expected = name.replace('turf-', '@turf/');
        if (pckg.name !== expected) t.fail(`${name} (name) must use ${expected} in package.json`);
    }
    t.end();
});

test('turf -- pre-defined attributes in package.json', t => {
    for (const {name, pckg} of modules) {
        if (pckg.author !== 'Turf Authors') t.fail(name + ' (author) should be "Turf Authors"');
        if (pckg.main !== 'dist/index') t.fail(`${name} (main) must be "dist/index" in package.json`);
        if (pckg.module !== 'index') t.fail(`${name} (module) must be "index" in package.json`);
        if (pckg['jsnext:main'] !== 'index') t.fail(`${name} (jsnext:main) must be "index" in package.json`);
        if (pckg.types !== 'index.d.ts') t.fail(`${name} (types) must be "index.d.ts" in package.json`);
        if (!pckg.bugs || pckg.bugs.url !== 'https://github.com/Turfjs/turf/issues') t.fail(`${name} (bugs.url) must be "https://github.com/Turfjs/turf/issues" in package.json`);
        if (pckg.homepage !== 'https://github.com/Turfjs/turf') t.fail(`${name} (homepage) must be "https://github.com/Turfjs/turf" in package.json`);
    }
    t.end();
});

test('turf -- parsing dependencies from index.js', t => {
    for (const {name, dir, dependencies} of modules) {
        const index = fs.readFileSync(path.join(dir, 'index.js'), 'utf8');

        // Read Depedencies from index.js
        const dependenciesUsed = new Set();
        for (const dependency of index.match(/(require\(|from )'[@/a-z-\d]+'/gi) || []) {
            const dependencyName = dependency.split(/'/)[1];
            if (!dependencies[dependencyName]) t.fail(`${name} ${dependencyName} is missing from dependencies`);
            if (dependenciesUsed.has(dependencyName)) t.fail(`${name} ${dependencyName} is duplicated in index.js`);
            dependenciesUsed.add(dependencyName);
        }

        // Read Dependencies from package.json
        for (const dependencyName of Object.keys(dependencies)) {
            // Ignore @turf/helpers since it could be used in Typescript definition
            switch (dependencyName) {
            case '@turf/helpers':
            case '@turf/invariant':
            case '@turf/meta':
                continue;
            }
            if (!dependenciesUsed.has(dependencyName)) t.fail(`${name} ${dependencyName} is not required in index.js`);
        }
    }
    t.end();
});

/**
 * =========================
 * Builds => test.example.js
 * =========================
 * will be run as `posttest`
 */

// File Paths
const testFilePath = path.join(__dirname, 'test.example.js');
const turfModulesPath = path.join(__dirname, '..', 'turf-*', 'index.js');

// Test Strings
const requireString = `const test = require('tape');
const turf = require('.');
`;

/**
 * Generate Test String
 *
 * @param {Object} turfFunction Documentation function object
 * @param {Object} example Documentation example object
 * @returns {string} Test String
 */
function testString(turfFunction, example) {
    const turfName = turfFunction.name;
    const testFunctionName = turfName + 'Test';

    // New modules will be excluded from tests
    if (!turf.hasOwnProperty(turfName)) return `
test('turf-${turfName}', t => {
    t.skip('${turfName}');
    t.end();
});
`;
    return `
test('turf-${turfName}', t => {
    const ${testFunctionName} = () => {
        ${example.description}
    }
    ${testFunctionName}();
    t.pass('${turfName}');
    t.end();
});
`;
}

// // Test for missing modules
// test('turf -- missing modules', t => {
//     const files = {
//         typescript: fs.readFileSync(path.join(__dirname, 'index.d.ts')),
//         modules: fs.readFileSync(path.join(__dirname, 'index.js'))
//     };

//     modules.forEach(({name}) => {
//         name = camelcase(name.replace('turf-', ''));
//         // name exception with linestring => lineString
//         name = name.replace('linestring', 'lineString').replace('Linestring', 'LineString');

//         if (!files.typescript.includes(name)) t.fail(name + ' is missing from index.d.ts');
//         if (!files.modules.includes(name)) t.fail(name + ' is missing from index.js');

//         switch (typeof turf[name]) {
//         case 'function': break;
//         case 'object':
//             Object.keys(turf[name]).forEach(method => {
//                 if (typeof turf[method] !== 'function') t.skip(name + '.' + method + ' is missing from index.js');
//                 if (!files.typescript.includes(method)) t.skip(name + '.' + method + ' is missing from index.d.ts');
//                 if (!files.modules.includes(method)) t.skip(name + '.' + method + ' is missing from index.js');
//             });
//             break;
//         case 'undefined':
//             t.fail(name + ' is missing from index.js');
//         }
//     });
//     t.end();
// });

// // Iterate over each module and retrieve @example to build tests from them
// glob(turfModulesPath, (err, files) => {
//     if (err) throw err;

//     // Read each JSDocs from index.js files
//     documentation.build(files, {}).then(turfFunctions => {
//         if (err) throw err;

//         // Write header of test.js
//         const writeableStream = fs.createWriteStream(testFilePath);
//         writeableStream.write(requireString);
//         writeableStream.on('error', err => { throw err; });

//         // Retrieve @example
//         turfFunctions.forEach(turfFunction => {
//             if (turfFunction.examples) {

//                 // Save to test.js
//                 turfFunction.examples.forEach(example => {
//                     writeableStream.write(testString(turfFunction, example));
//                 });
//             }
//         });
//         writeableStream.end();
//     });
// });
<|endoftext|>


<commit_before>// ==UserScript==
// @name       Frys.com Amazon Data
// @namespace  https://github.com/keyvanfatehi/noapikey_amazon_rating
// @version    0.1
// @description  Puts Amazon.com ratings and prices next to items
// @require http://code.jquery.com/jquery-latest.js
// @match      http://www.frys.com/search?*
// @copyright  2013+, Keyvan Fatehi
// ==/UserScript==

function template() {
  return $('
 \

Fetching Amazon data...
 \

 \

Amazon.com  Match:
\

Amazon.com Price:
 \

Amazon.com Rating:  out of 5
 \

 \

');
}

function AmazonRatingURL(k) {
  //return "http://amazon-rating-api.herokuapp.com/amazon/rating/"+k;
  return "http://127.0.0.1:1337/amazon/rating/"+k;
}

function eachProductLink(callback) {
  $('a font[face=Verdana][size=1]').each(function(i,e) {
    callback($(e).parent('a'));
  });
};

eachProductLink(function(a) {
  var div = template();
  a.after(div);

  var price = div.find('#amazon-price');
  var link = div.find('#amazon-product-name');
  var loading = div.find('#amazon-loading');
  var amazon = div.find('#amazon-data');
  var rating = div.find('#amazon-rating');
  var closeness = div.find('#amazon-match-closeness');

  var productName = a.text().trim();
  $.getJSON(AmazonRatingURL(productName), function(data){

    if (data.name === productName) {
      closeness.text("Exact");
    } else {
      closeness.text("Possible");
    }

    link.attr('href', data.url).text(data.name);

    price.text(data.price);
    rating.text(data.rating);

    loading.hide();
    amazon.show();

  }).fail(function() {
    div.text('Fail to retrieve Amazon data.');
  });
});

<commit_msg>point userscript to heroku<commit_after>// ==UserScript==
// @name       Frys.com Amazon Data
// @namespace  https://github.com/keyvanfatehi/noapikey_amazon_rating
// @version    0.1
// @description  Puts Amazon.com ratings and prices next to items
// @require http://code.jquery.com/jquery-latest.js
// @match      http://www.frys.com/search?*
// @copyright  2013+, Keyvan Fatehi
// ==/UserScript==

function template() {
  return $('
 \

Fetching Amazon data...
 \

 \

Amazon.com  Match:
\

Amazon.com Price:
 \

Amazon.com Rating:  out of 5
 \

 \

');
}

function AmazonRatingURL(k) {
  return "http://amazon-rating-api.herokuapp.com/amazon/rating/"+k;
  //return "http://127.0.0.1:1337/amazon/rating/"+k;
}

function eachProductLink(callback) {
  $('a font[face=Verdana][size=1]').each(function(i,e) {
    callback($(e).parent('a'));
  });
};

eachProductLink(function(a) {
  var div = template();
  a.after(div);

  var price = div.find('#amazon-price');
  var link = div.find('#amazon-product-name');
  var loading = div.find('#amazon-loading');
  var amazon = div.find('#amazon-data');
  var rating = div.find('#amazon-rating');
  var closeness = div.find('#amazon-match-closeness');

  var productName = a.text().trim();
  $.getJSON(AmazonRatingURL(productName), function(data){

    if (data.name === productName) {
      closeness.text("Exact");
    } else {
      closeness.text("Possible");
    }

    link.attr('href', data.url).text(data.name);

    price.text(data.price);
    rating.text(data.rating);

    loading.hide();
    amazon.show();

  }).fail(function() {
    div.text('Fail to retrieve Amazon data.');
  });
});

<|endoftext|>


<commit_before>import { config } from 'config'; // eslint-disable-line
import React, { PropTypes } from 'react';
import { Link } from 'react-router';
import moment from 'moment';
import Helmet from 'react-helmet';
import QuantfiedSelf from '../src/components/QuantifiedSelf';
import { getBlogPosts } from '../src/utils/blog-helpers';
import '../src/css/lists.css';

const Anchor = props =>

    {props.title}
  ;

Anchor.propTypes = {
  href: PropTypes.string,
  title: PropTypes.string
};

export default function BlogIndex(props) {
  const latestBlogPost = getBlogPosts(props.route).pop();
  const { data: { title, date }, path } = latestBlogPost;
  const fromNow = moment(date, 'MM/DD/YYYY').fromNow();
  const docTitle = `${config.blogTitle} by ${config.authorName}`;

  return (

      <Helmet
        title={docTitle}
        meta={[
          { name: 'description', content: config.description },
          { property: 'og:type', content: 'website' },
          { property: 'og:title', content: docTitle },
          { property: 'og:description', content: config.description },
          { name: 'twitter:description', content: config.description },
          { name: 'twitter:title', content: docTitle }
        ]}
      />

Hello, my name is <Link to='/about/'>Siddharth Jain</Link>.

        I am a web developer-designer living in Los Angeles
         New Delhi.

        I use <Link to='/about/#about-yuppies'>this space</Link> primarily for <Link to='/blog/'>writing</Link> and <Link to='/hire/'>reaching out</Link> to clients. The last piece I wrote was
        <Link to={path}>‘{title}’</Link> {fromNow}.

        I am a huge proponent of <Anchor href='//en.wikipedia.org/wiki/Lifelog' title='lifelogging' /> and have been collecting actionable data on myself since early 2014. Here are the latest metrics from <Anchor href='//rescuetime.com' title='Rescuetime' /> and <Anchor href='//last.fm/user/sidjain26' title='Last.fm' />:

      <QuantfiedSelf />

  );
}

BlogIndex.propTypes = {
  route: PropTypes.object
};
<commit_msg>Update work reference<commit_after>import { config } from 'config'; // eslint-disable-line
import React, { PropTypes } from 'react';
import { Link } from 'react-router';
import moment from 'moment';
import Helmet from 'react-helmet';
import QuantfiedSelf from '../src/components/QuantifiedSelf';
import { getBlogPosts } from '../src/utils/blog-helpers';
import '../src/css/lists.css';

const Anchor = props =>

    {props.title}
  ;

Anchor.propTypes = {
  href: PropTypes.string,
  title: PropTypes.string
};

export default function BlogIndex(props) {
  const latestBlogPost = getBlogPosts(props.route).pop();
  const { data: { title, date }, path } = latestBlogPost;
  const fromNow = moment(date, 'MM/DD/YYYY').fromNow();
  const docTitle = `${config.blogTitle} by ${config.authorName}`;

  return (

      <Helmet
        title={docTitle}
        meta={[
          { name: 'description', content: config.description },
          { property: 'og:type', content: 'website' },
          { property: 'og:title', content: docTitle },
          { property: 'og:description', content: config.description },
          { name: 'twitter:description', content: config.description },
          { name: 'twitter:title', content: docTitle }
        ]}
      />

Hello, my name is <Link to='/about/'>Siddharth Jain</Link>.

        I am a web developer-designer living in Los Angeles
         New Delhi, currently contracting independently for <Anchor href='//bridg.com' title='Bridg' />.

        I use <Link to='/about/#about-yuppies'>this space</Link> primarily for <Link to='/blog/'>writing</Link> and <Link to='/hire/'>reaching out</Link> to clients. The last piece I wrote was
        <Link to={path}>‘{title}’</Link> {fromNow}.

        I am a huge proponent of <Anchor href='//en.wikipedia.org/wiki/Lifelog' title='lifelogging' /> and have been collecting actionable data on myself since early 2014. Here are the latest metrics from <Anchor href='//rescuetime.com' title='Rescuetime' /> and <Anchor href='http://last.fm/user/sidjain26' title='Last.fm' />:

      <QuantfiedSelf />

  );
}

BlogIndex.propTypes = {
  route: PropTypes.object
};
<|endoftext|>


<commit_before>'use strict'

const { parse } = require('url')
import React from 'react'
import Router from 'next/router'
import Container from 'muicss/lib/react/container'
import Form from 'muicss/lib/react/form'
import Input from 'muicss/lib/react/input'
import Button from 'muicss/lib/react/button'
import Head from '../components/head'
import Loading from '../components/loading'
import Comparison from '../components/comparison.js'
const getResult = require('../lib/get-result')
const getComparison = require('../lib/get-comparison')
const loadResults = require('../lib/load-results')
const generateComparison = require('../lib/generate-comparison')
const saveComparison = require('../lib/save-comparison')

export default class Index extends React.Component {
  constructor (props) {
    super(props)
    this.state = Object.assign(this.props)
    this.handleChange = this.handleChange.bind(this)
    this.handleSubmit = this.handleSubmit.bind(this)
    this.handleSave = this.handleSave.bind(this)
    this.handleToggle = this.handleToggle.bind(this)
  }

  static async getInitialProps (ctx) {
    return {
      data: [],
      name: '',
      id: '',
      resultId: ctx.query.id || false,
      isLoading: false,
      show: 'domains'
    }
  }

  async componentDidMount () {
    if (this.state.resultId) {
      this.setState({isLoading: true})
      const saved = await getComparison(this.state.resultId)
      const data = await loadResults(saved.comparison)
      const comparisons = generateComparison(data)
      const show = this.state.show
      this.setState({data: data, isLoading: false, comparison: comparisons[show], comparisons: comparisons})
    }
  }

  handleChange (event) {
    const target = event.target
    const value = target.value
    const name = target.name

    this.setState({
      [name]: value
    })
  }

  async handleSubmit (event) {
    event.preventDefault()
    this.setState({isLoading: true})
    const prevData = this.state.data
    const input = parse(this.state.id, true).query
    const id = input.id || this.state.id
    const data = await getResult(id)
    prevData.push({name: this.state.name, id: id, data: data})
    const comparisons = generateComparison(prevData)
    const show = this.state.show
    this.setState({name: '', id: '', isLoading: false, data: prevData, comparison: comparisons[show], comparisons: comparisons})
  }

  async handleSave (event) {
    event.preventDefault()
    this.setState({isLoading: true})
    try {
      const save = await saveComparison({id: this.state.resultId, comparisons: this.state.data})
      const resultId = save.id
      this.setState({resultId: resultId, isLoading: false})
      Router.push({pathname: '/', query: {id: resultId}})
    } catch (error) {
      this.setState({isLoading: false})
    }
  }

  handleToggle (event) {
    event.preventDefault()
    const comparisons = this.state.comparisons
    const show = this.state.show === 'domains' ? 'facets' : 'domains'
    this.setState({show: show, comparison: comparisons[show]})
  }

  render () {
    return (

        <Container fluid>
          <Form onSubmit={this.handleSubmit}>
            <legend>Results to compare</legend>
            <Input name='name' label='Name for result' floatingLabel value={this.state.name} onChange={this.handleChange} />
            <Input name='id' label='ID or URL for result' floatingLabel value={this.state.id} onChange={this.handleChange} />
            <Button variant='raised' type='submit' disabled={this.state.isLoading}>Add to compare</Button>
          </Form>
          <Loading loading={this.state.isLoading} />
          {
            this.state.data.length > 0 ? <Comparison title={this.state.comparison.title} headers={this.state.comparison.headers} scores={this.state.comparison.scores} /> : null
          }
          {
            this.state.data.length > 0 ? <Button variant='raised' onClick={this.handleToggle}>Show {this.state.show === 'domains' ? 'facets' : 'domains'}</Button> : null
          }
          {
            this.state.data.length > 0 ? <Button variant='raised' className='mui--pull-right' onClick={this.handleSave} disabled={this.state.isLoading}>Save comparison</Button> : null
          }
        </Container>

          bigfive-compare

              Made with ❤ by zrrrzzt and maccyber

    )
  }
}
<commit_msg>Makes standard happy again<commit_after>'use strict'

import React from 'react'
import Router from 'next/router'
import Container from 'muicss/lib/react/container'
import Form from 'muicss/lib/react/form'
import Input from 'muicss/lib/react/input'
import Button from 'muicss/lib/react/button'
import Head from '../components/head'
import Loading from '../components/loading'
import Comparison from '../components/comparison.js'
const { parse } = require('url')
const getResult = require('../lib/get-result')
const getComparison = require('../lib/get-comparison')
const loadResults = require('../lib/load-results')
const generateComparison = require('../lib/generate-comparison')
const saveComparison = require('../lib/save-comparison')

export default class Index extends React.Component {
  constructor (props) {
    super(props)
    this.state = Object.assign(this.props)
    this.handleChange = this.handleChange.bind(this)
    this.handleSubmit = this.handleSubmit.bind(this)
    this.handleSave = this.handleSave.bind(this)
    this.handleToggle = this.handleToggle.bind(this)
  }

  static async getInitialProps (ctx) {
    return {
      data: [],
      name: '',
      id: '',
      resultId: ctx.query.id || false,
      isLoading: false,
      show: 'domains'
    }
  }

  async componentDidMount () {
    if (this.state.resultId) {
      this.setState({isLoading: true})
      const saved = await getComparison(this.state.resultId)
      const data = await loadResults(saved.comparison)
      const comparisons = generateComparison(data)
      const show = this.state.show
      this.setState({data: data, isLoading: false, comparison: comparisons[show], comparisons: comparisons})
    }
  }

  handleChange (event) {
    const target = event.target
    const value = target.value
    const name = target.name

    this.setState({
      [name]: value
    })
  }

  async handleSubmit (event) {
    event.preventDefault()
    this.setState({isLoading: true})
    const prevData = this.state.data
    const input = parse(this.state.id, true).query
    const id = input.id || this.state.id
    const data = await getResult(id)
    prevData.push({name: this.state.name, id: id, data: data})
    const comparisons = generateComparison(prevData)
    const show = this.state.show
    this.setState({name: '', id: '', isLoading: false, data: prevData, comparison: comparisons[show], comparisons: comparisons})
  }

  async handleSave (event) {
    event.preventDefault()
    this.setState({isLoading: true})
    try {
      const save = await saveComparison({id: this.state.resultId, comparisons: this.state.data})
      const resultId = save.id
      this.setState({resultId: resultId, isLoading: false})
      Router.push({pathname: '/', query: {id: resultId}})
    } catch (error) {
      this.setState({isLoading: false})
    }
  }

  handleToggle (event) {
    event.preventDefault()
    const comparisons = this.state.comparisons
    const show = this.state.show === 'domains' ? 'facets' : 'domains'
    this.setState({show: show, comparison: comparisons[show]})
  }

  render () {
    return (

        <Container fluid>
          <Form onSubmit={this.handleSubmit}>
            <legend>Results to compare</legend>
            <Input name='name' label='Name for result' floatingLabel value={this.state.name} onChange={this.handleChange} />
            <Input name='id' label='ID or URL for result' floatingLabel value={this.state.id} onChange={this.handleChange} />
            <Button variant='raised' type='submit' disabled={this.state.isLoading}>Add to compare</Button>
          </Form>
          <Loading loading={this.state.isLoading} />
          {
            this.state.data.length > 0 ? <Comparison title={this.state.comparison.title} headers={this.state.comparison.headers} scores={this.state.comparison.scores} /> : null
          }
          {
            this.state.data.length > 0 ? <Button variant='raised' onClick={this.handleToggle}>Show {this.state.show === 'domains' ? 'facets' : 'domains'}</Button> : null
          }
          {
            this.state.data.length > 0 ? <Button variant='raised' className='mui--pull-right' onClick={this.handleSave} disabled={this.state.isLoading}>Save comparison</Button> : null
          }
        </Container>

          bigfive-compare

              Made with ❤ by zrrrzzt and maccyber

    )
  }
}
<|endoftext|>


<commit_before>import Head from "next/head";
import Link from "next/link";
import React from "react";
import { DateTime } from "luxon";

import Container from "../components/Container";
import Section from "../components/Section";
import SingleColumn from "../components/SingleColumn";
import Header from "../components/Header";
import Footer from "../components/Footer";
import Contact from "../components/Contact";
import PostPreview from "../components/PostPreview";
import EventPreview from "../components/EventPreview";
import Cross from "../components/icons/Cross";
import TwoUp from "../components/TwoUp";
import BubbleLink from "../components/BubbleLink";
import { getAllPosts, getAllEvents, generateRealtimeDate } from "../lib/lib";
import { eventKeys } from "../lib/constants";

const Banner = ({ children, isOpen, href, dismiss }) => {
  return (

      <SingleColumn>

              {children}

            <button
              className="type-ui w-6 h-6 bg-green-400 flex items-center justify-center rounded-full text-white hover:opacity-70"
              onClick={(e) => {
                e.stopPropagation();
                dismiss();
              }}
            >
              <Cross width="10" height="10" fill="#E5F7F1" />
            </button>

      </SingleColumn>

  );
};

export default function Home({ posts, events, grantNumbers, search }) {
  return (
    <Container>

        <title>urbit.org</title>

      {/* {bannerElement} */}
      <SingleColumn>

        {
          // Hero Statement
        }

A clean-slate OS

and network

for the 21st century.

        {
          // Hero
        }

                Urbit is for everyone, but sometimes it is easy to get lost in
                this universe.

                Follow these links to contribute to the network through
                developing, operating, and exploring.

              <BubbleLink
                href="https://developers.urbit.org"
                title="Developers"
                target="_blank"
                className="md:h-16"
                caption="Start building on Urbit today."
              >

              </BubbleLink>
              <BubbleLink
                href="https://operators.urbit.org"
                className="mt-4 md:h-16"
                target="_blank"
                title="Operator’s Guide"
                caption="Own a star or galaxy? This is for you."
              >

              </BubbleLink>
              <BubbleLink
                href="https://network.urbit.org/"
                className="mt-4 md:h-16"
                target="_blank"
                title="Network Explorer"
                caption="View on-chain Urbit activity in real-time."
              >

              </BubbleLink>

        {
          // Grants
        }

Grants

{grantNumbers?.starsAwarded}

                Urbit stars awarded

{grantNumbers?.contributors}

                Different Contributors

{grantNumbers?.active}

                Active Projects

              We regularly grant address space to give motivated contributors
              real ownership and authority over the network.

              <Link href="/grants" passHref>

                  Learn More

              </Link>
              <Link href="/grants#view-grants" passHref>

                  View Grants

              </Link>

        {
          // Build on Urbit Developer CTA
        }

Build on Urbit

              Urbit is a personal OS designed from scratch to run peer-to-peer
              applications.

              It solves the hard problems of implementing a peer-to-peer network
              (including identity, NAT traversal, and exactly-once delivery) in
              the kernel so app developers can focus on business logic.

              The entire OS is a{" "}
              <Link href="https://urbit.org/docs/nock/definition/" passHref>
                single pure function
              </Link>{" "}
              that provides application developers with strong guarantees:
              automated persistence and memory management, repeatable builds,
              and support for hot code reloading.

              You can get started learning how to{" "}
              <Link href="https://urbit.org/docs/development/develop/" passHref>
                contribute to the project
              </Link>
              , or view a variety of{" "}
              <Link
                href="https://github.com/urbit/awesome-urbit#http-apis-airlock"
                passHref
              >
                libraries
              </Link>{" "}
              for building on Urbit using the languages you already know.

            <Link href="/docs" passHref>

                Read the Developer Docs

            </Link>

        {
          // Blog Posts
        }

Blog

          <TwoUp>
            <PostPreview post={posts[0]} key={posts[0].slug} />
            <PostPreview post={posts[1]} key={posts[1].slug} />
          </TwoUp>

          <Link href="/blog" passHref>

              See More

          </Link>

        {
          // Events Posts
        }

Events

          <TwoUp>
            <EventPreview event={events[0]} key={events[0].slug} />
            <EventPreview event={events[1]} key={events[1].slug} />
          </TwoUp>

          <Link href="/events" passHref>

              More Events

          </Link>

        {
          // Newsletter and social media
        }

          <Contact emphasize />

      </SingleColumn>

    </Container>
  );
}

export async function getStaticProps() {
  const posts = getAllPosts(
    ["title", "slug", "date", "description", "extra"],
    "blog",
    "date"
  );

  const events = getAllEvents(eventKeys, "events");

  const grants = getAllPosts(["extra", "taxonomies"], "grants", "date");

  const grantNumbers = {
    starsAwarded: grants.reduce(
      (total, a) =>
        total + (Number(a?.extra?.reward.replace(/ star[s]?/, "")) || 0),
      0
    ),
    contributors: [...new Set(grants.map((e) => e.extra.assignee))].length,
    active: grants.filter(
      (e) =>
        !e.extra.canceled &&
        !e.extra.completed &&
        e.extra.assignee &&
        e.extra.assignee.length > 0
    ).length,
  };

  console.log(grantNumbers.active);
  const now = DateTime.now();

  const pastEvents = events.filter((event) => {
    const ends = generateRealtimeDate(event.ends);
    return ends < now;
  });

  const futureEvents = events
    .filter((event) => {
      const starts = generateRealtimeDate(event.starts);
      return starts > now;
    })
    .sort((a, b) => {
      const aStarts = generateRealtimeDate(a.starts).ts;
      const bStarts = generateRealtimeDate(b.starts).ts;
      if (aStarts > bStarts) {
        return 1;
      } else if (aStarts === bStarts) {
        return 0;
      }
      return -1;
    });

  const happeningNow = events.filter((event) => {
    const starts = generateRealtimeDate(event.starts);
    const ends = generateRealtimeDate(event.ends);
    return starts > DateTime.now() && ends < now;
  });

  return {
    props: {
      posts,
      grantNumbers,
      events: happeningNow.concat(futureEvents).concat(pastEvents),
    },
  };
}
<commit_msg>pages/index: remove errant console.log<commit_after>import Head from "next/head";
import Link from "next/link";
import React from "react";
import { DateTime } from "luxon";

import Container from "../components/Container";
import Section from "../components/Section";
import SingleColumn from "../components/SingleColumn";
import Header from "../components/Header";
import Footer from "../components/Footer";
import Contact from "../components/Contact";
import PostPreview from "../components/PostPreview";
import EventPreview from "../components/EventPreview";
import Cross from "../components/icons/Cross";
import TwoUp from "../components/TwoUp";
import BubbleLink from "../components/BubbleLink";
import { getAllPosts, getAllEvents, generateRealtimeDate } from "../lib/lib";
import { eventKeys } from "../lib/constants";

const Banner = ({ children, isOpen, href, dismiss }) => {
  return (

      <SingleColumn>

              {children}

            <button
              className="type-ui w-6 h-6 bg-green-400 flex items-center justify-center rounded-full text-white hover:opacity-70"
              onClick={(e) => {
                e.stopPropagation();
                dismiss();
              }}
            >
              <Cross width="10" height="10" fill="#E5F7F1" />
            </button>

      </SingleColumn>

  );
};

export default function Home({ posts, events, grantNumbers, search }) {
  return (
    <Container>

        <title>urbit.org</title>

      {/* {bannerElement} */}
      <SingleColumn>

        {
          // Hero Statement
        }

A clean-slate OS

and network

for the 21st century.

        {
          // Hero
        }

                Urbit is for everyone, but sometimes it is easy to get lost in
                this universe.

                Follow these links to contribute to the network through
                developing, operating, and exploring.

              <BubbleLink
                href="https://developers.urbit.org"
                title="Developers"
                target="_blank"
                className="md:h-16"
                caption="Start building on Urbit today."
              >

              </BubbleLink>
              <BubbleLink
                href="https://operators.urbit.org"
                className="mt-4 md:h-16"
                target="_blank"
                title="Operator’s Guide"
                caption="Own a star or galaxy? This is for you."
              >

              </BubbleLink>
              <BubbleLink
                href="https://network.urbit.org/"
                className="mt-4 md:h-16"
                target="_blank"
                title="Network Explorer"
                caption="View on-chain Urbit activity in real-time."
              >

              </BubbleLink>

        {
          // Grants
        }

Grants

{grantNumbers?.starsAwarded}

                Urbit stars awarded

{grantNumbers?.contributors}

                Different Contributors

{grantNumbers?.active}

                Active Projects

              We regularly grant address space to give motivated contributors
              real ownership and authority over the network.

              <Link href="/grants" passHref>

                  Learn More

              </Link>
              <Link href="/grants#view-grants" passHref>

                  View Grants

              </Link>

        {
          // Build on Urbit Developer CTA
        }

Build on Urbit

              Urbit is a personal OS designed from scratch to run peer-to-peer
              applications.

              It solves the hard problems of implementing a peer-to-peer network
              (including identity, NAT traversal, and exactly-once delivery) in
              the kernel so app developers can focus on business logic.

              The entire OS is a{" "}
              <Link href="https://urbit.org/docs/nock/definition/" passHref>
                single pure function
              </Link>{" "}
              that provides application developers with strong guarantees:
              automated persistence and memory management, repeatable builds,
              and support for hot code reloading.

              You can get started learning how to{" "}
              <Link href="https://urbit.org/docs/development/develop/" passHref>
                contribute to the project
              </Link>
              , or view a variety of{" "}
              <Link
                href="https://github.com/urbit/awesome-urbit#http-apis-airlock"
                passHref
              >
                libraries
              </Link>{" "}
              for building on Urbit using the languages you already know.

            <Link href="/docs" passHref>

                Read the Developer Docs

            </Link>

        {
          // Blog Posts
        }

Blog

          <TwoUp>
            <PostPreview post={posts[0]} key={posts[0].slug} />
            <PostPreview post={posts[1]} key={posts[1].slug} />
          </TwoUp>

          <Link href="/blog" passHref>

              See More

          </Link>

        {
          // Events Posts
        }

Events

          <TwoUp>
            <EventPreview event={events[0]} key={events[0].slug} />
            <EventPreview event={events[1]} key={events[1].slug} />
          </TwoUp>

          <Link href="/events" passHref>

              More Events

          </Link>

        {
          // Newsletter and social media
        }

          <Contact emphasize />

      </SingleColumn>

    </Container>
  );
}

export async function getStaticProps() {
  const posts = getAllPosts(
    ["title", "slug", "date", "description", "extra"],
    "blog",
    "date"
  );

  const events = getAllEvents(eventKeys, "events");

  const grants = getAllPosts(["extra", "taxonomies"], "grants", "date");

  const grantNumbers = {
    starsAwarded: grants.reduce(
      (total, a) =>
        total + (Number(a?.extra?.reward.replace(/ star[s]?/, "")) || 0),
      0
    ),
    contributors: [...new Set(grants.map((e) => e.extra.assignee))].length,
    active: grants.filter(
      (e) =>
        !e.extra.canceled &&
        !e.extra.completed &&
        e.extra.assignee &&
        e.extra.assignee.length > 0
    ).length,
  };

  const now = DateTime.now();

  const pastEvents = events.filter((event) => {
    const ends = generateRealtimeDate(event.ends);
    return ends < now;
  });

  const futureEvents = events
    .filter((event) => {
      const starts = generateRealtimeDate(event.starts);
      return starts > now;
    })
    .sort((a, b) => {
      const aStarts = generateRealtimeDate(a.starts).ts;
      const bStarts = generateRealtimeDate(b.starts).ts;
      if (aStarts > bStarts) {
        return 1;
      } else if (aStarts === bStarts) {
        return 0;
      }
      return -1;
    });

  const happeningNow = events.filter((event) => {
    const starts = generateRealtimeDate(event.starts);
    const ends = generateRealtimeDate(event.ends);
    return starts > DateTime.now() && ends < now;
  });

  return {
    props: {
      posts,
      grantNumbers,
      events: happeningNow.concat(futureEvents).concat(pastEvents),
    },
  };
}
<|endoftext|>


<commit_before>import React from "react";
import Head from "next/head";
import { LiveProvider, LiveEditor, LiveError, LivePreview } from "react-live";
import Obfuscate from "../src/obfuscate";
import {Browser, Terminal} from 'react-window-ui'

class App extends React.Component {
  render() {
    return (

          <title>react-obfuscate ☁</title>
          <meta name="viewport" content="width=device-width, initial-scale=1" />

react-obfuscate ☁

          <LiveProvider
            code={headerCode}
            scope={{
              Obfuscate
            }}
          >

Try Me

                  Inspect and click these robot-resistant, user friendly,
                  contact links

                <Browser  boxShadow="none">
                  <LivePreview />
                </Browser>

Live Code

            <Terminal boxShadow="none">

              <LiveEditor
                aria-describedby="Live Code Editor"
                theme={theme}
                style={{
                  background: "black",
                  overflow: "scroll"
                }}
              />
              <LiveError />
            </Terminal>
          </LiveProvider>

How It Works

            Pass the contact link as an email, tel,{" "}
            sms, facetime, or href prop. The component
            obfuscates href data until an onClick event. Links are given their
            proper URL schemes (mailto, facetime, etc.) The link is rendered in
            reverse in the dom, but reversed again with css. This making the
            link useless for spammers, but user friendly on screen.

Why

            The world needs obfuscated links that display the link in a friendly
            way.

Install

            npm install react-obfuscate --save

Github

              View documentation on GitHub

    );
  }
}

export default App;

const theme /*: PrismTheme */ = {
  plain: {
    color: "#fff"
  },
  styles: [
    {
      types: ["comment", "prolog", "doctype", "cdata", "punctuation"],
      style: {
        color: "#fc929e"
      }
    },
    {
      types: ["namespace"],
      style: {
        opacity: 0.7
      }
    },
    {
      types: ["tag", "operator", "number"],
      style: {
        color: "#fc929e"
      }
    },
    {
      types: ["property", "function"],
      style: {
        color: "#9a86fd"
      }
    },
    {
      types: ["tag-id", "selector", "atrule-id"],
      style: {
        color: "#fff"
      }
    },
    {
      types: ["attr-name"],
      style: {
        color: "rgba(0,255,255,1)"
      }
    },
    {
      types: [
        "boolean",
        "string",
        "entity",
        "url",
        "attr-value",
        "keyword",
        "control",
        "directive",
        "unit",
        "statement",
        "regex",
        "at-rule",
        "placeholder",
        "variable"
      ],
      style: {
        color: "rgba(0,255,255,1)"
      }
    },
    {
      types: ["deleted"],
      style: {
        textDecorationLine: "line-through"
      }
    },
    {
      types: ["inserted"],
      style: {
        textDecorationLine: "underline"
      }
    },
    {
      types: ["italic"],
      style: {
        fontStyle: "italic"
      }
    },
    {
      types: ["important", "bold"],
      style: {
        fontWeight: "bold"
      }
    },
    {
      types: ["important"],
      style: {
        color: "#6c6783"
      }
    }
  ]
};

const headerCode = `

    Telephone:
    <Obfuscate tel="(202) 224-5744" />

    Email:
    <Obfuscate
      email="hello@coston.io"
      headers={{
        cc: 'kate@acidburn.af',
        bcc: 'tanderson@metacortex.net',
        subject: 'react-obfuscate',
        body: 'Down with the machines!'
      }}
    />

    SMS:
    <Obfuscate sms="+1 (202) 224-5744" />

    Facetime:
    <Obfuscate facetime="202.224.5744" />

    Any other URL:
    <Obfuscate href="https://wa.me/15551234567">
      Chat On WhatsApp
    </Obfuscate>

    Child Elements:
    <Obfuscate email="hello@coston.io" aria-label="Email Me">
      <svg width={24} height={21}>
        <path
          fill="#000"
          d="M12 12.713L.0 3h23.97L12 12.713zm0 2.574L0 5.562V21h24V5"
        />
      </svg>
    </Obfuscate>

`.trim();
<commit_msg>Update background color<commit_after>import React from "react";
import Head from "next/head";
import { LiveProvider, LiveEditor, LiveError, LivePreview } from "react-live";
import Obfuscate from "../src/obfuscate";
import {Browser, Terminal} from 'react-window-ui'

class App extends React.Component {
  render() {
    return (

          <title>react-obfuscate ☁</title>
          <meta name="viewport" content="width=device-width, initial-scale=1" />

react-obfuscate ☁

          <LiveProvider
            code={headerCode}
            scope={{
              Obfuscate
            }}
          >

Try Me

                  Inspect and click these robot-resistant, user friendly,
                  contact links

                <Browser  boxShadow="none">
                  <LivePreview />
                </Browser>

Live Code

            <Terminal boxShadow="none">

              <LiveEditor
                aria-describedby="Live Code Editor"
                theme={theme}
                style={{
                  background: "black",
                  overflow: "scroll"
                }}
              />
              <LiveError />
            </Terminal>
          </LiveProvider>

How It Works

            Pass the contact link as an email, tel,{" "}
            sms, facetime, or href prop. The component
            obfuscates href data until an onClick event. Links are given their
            proper URL schemes (mailto, facetime, etc.) The link is rendered in
            reverse in the dom, but reversed again with css. This making the
            link useless for spammers, but user friendly on screen.

Why

            The world needs obfuscated links that display the link in a friendly
            way.

Install

            npm install react-obfuscate --save

Github

              View documentation on GitHub

    );
  }
}

export default App;

const theme /*: PrismTheme */ = {
  plain: {
    color: "#fff"
  },
  styles: [
    {
      types: ["comment", "prolog", "doctype", "cdata", "punctuation"],
      style: {
        color: "#fc929e"
      }
    },
    {
      types: ["namespace"],
      style: {
        opacity: 0.7
      }
    },
    {
      types: ["tag", "operator", "number"],
      style: {
        color: "#fc929e"
      }
    },
    {
      types: ["property", "function"],
      style: {
        color: "#9a86fd"
      }
    },
    {
      types: ["tag-id", "selector", "atrule-id"],
      style: {
        color: "#fff"
      }
    },
    {
      types: ["attr-name"],
      style: {
        color: "rgba(0,255,255,1)"
      }
    },
    {
      types: [
        "boolean",
        "string",
        "entity",
        "url",
        "attr-value",
        "keyword",
        "control",
        "directive",
        "unit",
        "statement",
        "regex",
        "at-rule",
        "placeholder",
        "variable"
      ],
      style: {
        color: "rgba(0,255,255,1)"
      }
    },
    {
      types: ["deleted"],
      style: {
        textDecorationLine: "line-through"
      }
    },
    {
      types: ["inserted"],
      style: {
        textDecorationLine: "underline"
      }
    },
    {
      types: ["italic"],
      style: {
        fontStyle: "italic"
      }
    },
    {
      types: ["important", "bold"],
      style: {
        fontWeight: "bold"
      }
    },
    {
      types: ["important"],
      style: {
        color: "#6c6783"
      }
    }
  ]
};

const headerCode = `

    Telephone:
    <Obfuscate tel="(202) 224-5744" />

    Email:
    <Obfuscate
      email="hello@coston.io"
      headers={{
        cc: 'kate@acidburn.af',
        bcc: 'tanderson@metacortex.net',
        subject: 'react-obfuscate',
        body: 'Down with the machines!'
      }}
    />

    SMS:
    <Obfuscate sms="+1 (202) 224-5744" />

    Facetime:
    <Obfuscate facetime="202.224.5744" />

    Any other URL:
    <Obfuscate href="https://wa.me/15551234567">
      Chat On WhatsApp
    </Obfuscate>

    Child Elements:
    <Obfuscate email="hello@coston.io" aria-label="Email Me">
      <svg width={24} height={21}>
        <path
          fill="#000"
          d="M12 12.713L.0 3h23.97L12 12.713zm0 2.574L0 5.562V21h24V5"
        />
      </svg>
    </Obfuscate>

`.trim();
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import Head from 'next/head'
import Link from 'next/link'
import Router from 'next/router'

import { graphql, gql, compose } from 'react-apollo'

import withData from '../lib/withData'
import withAuth from '../lib/withAuth'

import { GC_USER_ID, GC_USERNAME } from '../constants';

import Page from '../layouts/main'
import NewChat from '../components/NewChat'
import ChatList from '../components/ChatList'
import ChatListItem from '../components/ChatListItem'
import Chatroom from '../components/Chatroom'

import { CHATROOM_QUERY } from '../components/Chatroom'

class IndexPage extends Component {

  static async getInitialProps({ query }) {
    // for those who enters from link platonos.com/chatrooms/chatroomId
    return { initialChatroomId: query.chatroomId }
  }

  constructor(props) {
    super(props)
    this.state = {
      title: '',
    }
  }

  goToChatroom = (id) => {
    Router.push(`/?chatroomId=${id}`, `/chatrooms/${id}`, { shallow: true })
  }

  render() {
    // This works after redirect to first page after login
    const { currentUserId, currentUsername } = this.props.auth

    const currentRoomId = this.props.url.query.chatroomId || this.props.initialChatroomId

    const initialChatroom = this.props.initialChatroom
    let initialChatroomLoading, initialChatroomError, initialChat
    if (initialChatroom) {
      initialChatroomLoading = initialChatroom.loading
      initialChatroomError = initialChatroom.error
      initialChat = initialChatroom.Chatroom
    }
    return (
      <Page style={{ overflow: 'auto' }}>

          <title>Platonos</title>

          {/* DISPLAY CURRENT USER  */}
          {currentUsername &&
Hi, {currentUsername}
}

          {/* NEW CHAT INPUT */}
          {currentUserId ?
            <NewChat onCreateNewChatroom={this.goToChatroom} currentUserId={currentUserId} />
            :

<Link prefetch href="/join">Login</Link> to create a chat

          }

          {/* CHAT PANEL  */}

              { initialChatroom && (initialChat ?

Directed

 this.goToChatroom(this.props.initialChatroomId)}>
                    <ChatListItem title={initialChat.title} count={initialChat.messages.length} createdAt={initialChat.createdAt} />

                :
                initialChatroomError ?
Error: {initialChatroomError}
 : null)
              }

Latest

              <ChatList onClickChatroom={this.goToChatroom} />

            {
                currentRoomId
              &&

                  <Chatroom roomId={currentRoomId} currentUserId={currentUserId} />

              ||
                'Select chatroom'
            }

      </Page>
    )
  }
}

const CURRENT_USER_QUERY = gql`
query {
  user {
    id,
    username
  }
}
`

export default withData(compose(
  graphql(CURRENT_USER_QUERY, { name: "currentUserQuery" }),
  graphql(CHATROOM_QUERY, {
    name: "initialChatroom",
    options: ({ initialChatroomId }) => {
      return {
        variables: {
          roomId: initialChatroomId,
        }
      }
    },
    skip: ({ initialChatroomId }) => {
      return initialChatroomId ? false : true
    }
  }),
)(withAuth(IndexPage)))
<commit_msg>Layout<commit_after>import React, { Component } from 'react';
import Head from 'next/head'
import Link from 'next/link'
import Router from 'next/router'

import { graphql, gql, compose } from 'react-apollo'

import withData from '../lib/withData'
import withAuth from '../lib/withAuth'

import { GC_USER_ID, GC_USERNAME } from '../constants';

import Page from '../layouts/main'
import NewChat from '../components/NewChat'
import ChatList from '../components/ChatList'
import ChatListItem from '../components/ChatListItem'
import Chatroom from '../components/Chatroom'

import { CHATROOM_QUERY } from '../components/Chatroom'

class IndexPage extends Component {

  static async getInitialProps({ query }) {
    // for those who enters from link platonos.com/chatrooms/chatroomId
    return { initialChatroomId: query.chatroomId }
  }

  constructor(props) {
    super(props)
    this.state = {
      title: '',
    }
  }

  goToChatroom = (id) => {
    Router.push(`/?chatroomId=${id}`, `/chatrooms/${id}`, { shallow: true })
  }

  render() {
    // This works after redirect to first page after login
    const { currentUserId, currentUsername } = this.props.auth

    const currentRoomId = this.props.url.query.chatroomId || this.props.initialChatroomId

    const initialChatroom = this.props.initialChatroom
    let initialChatroomLoading, initialChatroomError, initialChat
    if (initialChatroom) {
      initialChatroomLoading = initialChatroom.loading
      initialChatroomError = initialChatroom.error
      initialChat = initialChatroom.Chatroom
    }
    return (
      <Page>

          <title>Platonos</title>

          {/* DISPLAY CURRENT USER  */}
          {/* {currentUsername &&
Hi, {currentUsername}
} */}

          {/* NEW CHAT INPUT */}
          {/* {currentUserId ?
            <NewChat onCreateNewChatroom={this.goToChatroom} currentUserId={currentUserId} />
            :

<Link prefetch href="/join">Login</Link> to create a chat

          } */}

          {/*
 */}

          {/* CHAT PANEL  */}

            { initialChatroom && (initialChat ?

Directed

 this.goToChatroom(this.props.initialChatroomId)}>
                  <ChatListItem title={initialChat.title} count={initialChat.messages.length} createdAt={initialChat.createdAt} />

              :
              initialChatroomError ?
Error: {initialChatroomError}
 : null)
            }

Latest

            <ChatList onClickChatroom={this.goToChatroom} />

          {
              currentRoomId
            &&

                <Chatroom roomId={currentRoomId} currentUserId={currentUserId} />

            ||
              'Select chatroom'
          }

      </Page>
    )
  }
}

const CURRENT_USER_QUERY = gql`
query {
  user {
    id,
    username
  }
}
`

export default withData(compose(
  graphql(CURRENT_USER_QUERY, { name: "currentUserQuery" }),
  graphql(CHATROOM_QUERY, {
    name: "initialChatroom",
    options: ({ initialChatroomId }) => {
      return {
        variables: {
          roomId: initialChatroomId,
        }
      }
    },
    skip: ({ initialChatroomId }) => {
      return initialChatroomId ? false : true
    }
  }),
)(withAuth(IndexPage)))
<|endoftext|>


<commit_before>/**
 * Tag Manager
 *
 * @Param {Object} $el : 태그 input폼
 * @Param {Object} $tags : 태그목록
 */
var TagManager = function($el, $tags)
{
	var self = this;

	this.$input = $el;
	this.tags = new Array();
	this.alltags = new Array();
	this.$alltagsIndex = null;

	/**
	 * events
	 */
	var events = function()
	{
		$el.on('keypress', function(e){
			if (e.keyCode == 13)
			{
				self.add($(this).val());
				return false;
			}
		});
	}

	/**
	 * tag template
	 *
	 * @Param {String} keyword
	 * @Return {Object}
	 */
	var tagTemplate = function(keyword)
	{
		var str = '
';
		str += '' + keyword + '';
		str += '<button type="button" role-action="removeTag" title="remove tag">remove</button>';
		str += '
';
		return $(str);
	}

	/**
	 * add tag
	 *
	 * @Param {String} keyword
	 */
	this.add = function(keyword)
	{
		keyword = keyword.replace(/[^a-zA-Z0-9가-힣]|/g, '');

		if (keyword.replace(/[^a-zA-Z0-9가-힣]|/g, ''))
		{
			if (self.tags.indexOf(keyword) < 0)
			{
				self.tags.push(keyword);
				var $tag = tagTemplate(keyword);
				$tag.children('button').on('click', function(){
					self.remove($tag);
				});
				$tags.append($tag);
				$el.val('');
			}
			else
			{
				alert('중복된 키워드가 있습니다.');
				$el.focus();
			}
		}
	}

	/**
	 * remove tag
	 *
	 * @Param {Object} $tag
	 */
	this.remove = function($tag)
	{
		$tag.each(function(){
			var text = $(this).children('span').text();
			var position = self.tags.indexOf(text);
			self.tags.splice(position, 1);
			$tag.remove();
			self.$alltagsIndex.eq(self.alltags.indexOf(text)).removeClass('on');

		});
	}

	/**
	 * import tag
	 *
	 * @Param {Array} keywords
	 */
	this.import = function(keywords)
	{
		for (var i=0; i<keywords.length; i++)
		{
			self.add(keywords[i]);
		}
	}

	/**
	 * export tag
	 *
	 * @Return {Array}
	 */
	this.export = function()
	{
		return self.tags;
	}

	this.allTagsInit = function($wrap)
	{
		self.$alltagsIndex = $wrap.find('ul > li');
		self.alltags = self.$alltagsIndex.map(function(){
			return $(this).children('span').text();
		}).get();

		// sync tags
		for (var i=0; i< self.tags.length; i++)
		{
			if (self.alltags.indexOf(self.tags[i]) >= 0)
			{
				self.$alltagsIndex.eq(self.alltags.indexOf(self.tags[i])).addClass('on');
			}
		}

		// button event
		$wrap.children('button').on('click', function(){
			$wrap.toggleClass('on');
			$(this).toggleClass('btn-highlight');
		});

		// tag event
		self.$alltagsIndex.on('click', function(){
			if (!$(this).hasClass('on'))
			{
				self.add($(this).children('span').text());
				$(this).addClass('on');
			}
		});
	}

	// action
	events();
}
<commit_msg>태그 키워드를 추가하면 모든 키워드를 검색하여 있으면 활성화 시키기<commit_after>/**
 * Tag Manager
 *
 * @Param {Object} $el : 태그 input폼
 * @Param {Object} $tags : 태그목록
 */
var TagManager = function($el, $tags)
{
	var self = this;

	this.$input = $el;
	this.tags = new Array();
	this.alltags = new Array();
	this.$alltagsIndex = null;

	/**
	 * events
	 */
	var events = function()
	{
		$el.on('keypress', function(e){
			if (e.keyCode == 13)
			{
				self.add($(this).val());
				return false;
			}
		});
	}

	/**
	 * tag template
	 *
	 * @Param {String} keyword
	 * @Return {Object}
	 */
	var tagTemplate = function(keyword)
	{
		var str = '
';
		str += '' + keyword + '';
		str += '<button type="button" role-action="removeTag" title="remove tag">remove</button>';
		str += '
';
		return $(str);
	}

	/**
	 * add tag
	 *
	 * @Param {String} keyword
	 */
	this.add = function(keyword)
	{
		keyword = keyword.replace(/[^a-zA-Z0-9가-힣]|/g, '');

		if (keyword.replace(/[^a-zA-Z0-9가-힣]|/g, ''))
		{
			if (self.tags.indexOf(keyword) < 0)
			{
				self.tags.push(keyword);
				var $tag = tagTemplate(keyword);
				$tag.children('button').on('click', function(){
					self.remove($tag);
				});
				$tags.append($tag);
				$el.val('');
				if (self.alltags.length)
				{
					var pos = self.alltags.indexOf(keyword);
					if (pos >= 0)
					{
						self.$alltagsIndex.eq(pos).addClass('on');
					}
				}
			}
			else
			{
				alert('중복된 키워드가 있습니다.');
				$el.focus();
			}
		}
	}

	/**
	 * remove tag
	 *
	 * @Param {Object} $tag
	 */
	this.remove = function($tag)
	{
		$tag.each(function(){
			var text = $(this).children('span').text();
			var position = self.tags.indexOf(text);
			self.tags.splice(position, 1);
			$tag.remove();
			self.$alltagsIndex.eq(self.alltags.indexOf(text)).removeClass('on');
		});
	}

	/**
	 * import tag
	 *
	 * @Param {Array} keywords
	 */
	this.import = function(keywords)
	{
		for (var i=0; i<keywords.length; i++)
		{
			self.add(keywords[i]);
		}
	}

	/**
	 * export tag
	 *
	 * @Return {Array}
	 */
	this.export = function()
	{
		return self.tags;
	}

	this.allTagsInit = function($wrap)
	{
		self.$alltagsIndex = $wrap.find('ul > li');
		self.alltags = self.$alltagsIndex.map(function(){
			return $(this).children('span').text();
		}).get();

		// sync tags
		for (var i=0; i< self.tags.length; i++)
		{
			if (self.alltags.indexOf(self.tags[i]) >= 0)
			{
				self.$alltagsIndex.eq(self.alltags.indexOf(self.tags[i])).addClass('on');
			}
		}

		// button event
		$wrap.children('button').on('click', function(){
			$wrap.toggleClass('on');
			$(this).toggleClass('btn-highlight');
		});

		// tag event
		self.$alltagsIndex.on('click', function(){
			if (!$(this).hasClass('on'))
			{
				self.add($(this).children('span').text());
			}
		});
	}

	// action
	events();
}
<|endoftext|>


<commit_before>/* REQUIRED DEPENDENCIES */
var reload = require('require-reload')(require);
var fs     = require('fs');

/* REQUIRED FILES */
var utils   = reload('./utils.js');
var Command = reload('./Command.js');
var _Logger = reload('./Logger.js');

/**
 * @class
 * @classdesc Handles a directory of .js files formatted as {@link Command}.
 * @prop {String} prefix Prefix for the commands handled by this CommandManager.
 * @prop {String} dir Path where the commands are located from the root directory.
 * @prop {Object<Command>} commands The loaded {@link Command}s.
 */
class CommandManager {

	/**
	 * @constructor
	 * @arg {Object} config The bot's config settings.
	 * @arg {String} prefix Prefix for the commands handled by this CommandManager.
	 * @arg {String} [dir="commands/user/"] Path to load command from, from the root directory of the bot.
	 * @arg {Sting} [color] The color to log commands with.
	 */
	constructor(config, prefix, dir = 'commands/user/', color) {
		this.prefix = prefix;
		this.directory = `${__dirname}/../${dir}`;
		this.commands = {};
		this.logger = new _Logger(config.logTimestamp, color);
	}

	/**
	 * Initialize the command manager, loading each command in the set directory.
	 * @arg {Client} bot
	 * @arg {Object} config The bot's config settings.
	 * @arg {settingsManager} settingsManager The bot's {@link settingsManager}
	 * @returns {Promise}
	 */
	initialize(bot, config, settingsManager) {
		return new Promise((resolve, reject) => {
			fs.readdir(this.directory, (err, files) => {
				if (err) reject(`Error reading commands directory: ${err}`);
				else if (!files) reject(`No files in directory ${this.directory}`);
				else {
					settingsManager.commandList[this.prefix] = [];
					for (let name of files) {
						if (name.endsWith('.js')) {
							try {
								name = name.replace(/\.js$/, '');
								let command = new Command(name, this.prefix, reload(this.directory + name + '.js'), bot, config);
								this.commands[name] = command;
								settingsManager.commandList[this.prefix].push(name);
							} catch (e) {
								this.logger.error(`${e}\n${e.stack}`, 'Error loading command ' + name);
							}
						}
					}
					resolve();
				}
			});
		});
	}

	/**
	 * Called when a message is detected with the prefix. Decides what to do.
	 * @arg {Client} bot The client.
	 * @arg {Message} msg The matching message.
	 * @arg {Object} config The JSON formatted config file.
	 * @arg {settingsManager} settingsManager The bot's {@link settingsManager}
	 */
	processCommand(bot, msg, config, settingsManager) {
		let name = msg.content.replace(this.prefix, '').split(/ |\n/)[0];
		let command = this.checkForMatch(name.toLowerCase());
		let suffix = msg.content.replace(this.prefix + name, '').trim();
		if (command !== null) {
			if (msg.channel.guild !== undefined && !msg.channel.permissionsOf(msg.author.id).has('manageChannels') && settingsManager.isCommandIgnored(this.prefix, command.name, msg.channel.guild.id, msg.channel.id, msg.author.id) === true) {
				return;
			}
			this.logger.logCommand(msg.channel.guild === undefined ? null : msg.channel.guild.name, msg.author.username, this.prefix + command.name, msg.cleanContent.replace(this.prefix + name, '').trim());
			if (msg.guild !== null) utils.logCommandToMixpanel(msg, command, suffix, 'SERVER');
			if (msg.guild === null) utils.logCommandToMixpanel(msg, command, suffix, 'DM');
			return command.execute(bot, msg, suffix, config, settingsManager, this.logger);
		} else if (name.toLowerCase() === 'help') {
			return this.help(bot, msg, msg.content.replace(this.prefix + name, '').trim());
		}
	}

	/**
	 * Checks if there is a matching command in this CommandManager.
	 * @arg {String} name The command name to look for.
	 * @returns {?Command} Returns the matching {@link Command} or false.
	 */
	checkForMatch(name) {
		if (name.startsWith(this.prefix)) {
			name = name.substr(1);
		}
		for (let key in this.commands) {
			if (key === name || this.commands[key].aliases.includes(name)) {
				return this.commands[key];
			}
		}
		return null;
	}

	/**
	 * Built-in help command
	 * If no command is specified it will DM a list of commands.
	 * If a command is specified it will send info on that command.
	 * @arg {Eris} bot The client.
	 * @arg {Eris.Message} msg The message that triggered the command.
	 * @arg {String} [command] The command to get help for.
	 */
	help(bot, msg, command) {
		this.logger.logCommand(msg.channel.guild === undefined ? null : msg.channel.guild.name, msg.author.username, this.prefix + 'help', command);
		if (!command) {
			let messageQueue = [];
			let currentMessage = `\n// Here's a list of my commands. For more info do: ${this.prefix}help <command>`;
			for (let cmd in this.commands) {
				if (this.commands[cmd].hidden === true) continue;
				let toAdd = this.commands[cmd].helpShort;
				if (currentMessage.length + toAdd.length >= 1900) {
					messageQueue.push(currentMessage);
					currentMessage = '';
				}
				currentMessage += '\n' + toAdd;
			}
			messageQueue.push(currentMessage);
			bot.getDMChannel(msg.author.id).then(chan => {
				let sendInOrder = setInterval(() => {
					if (messageQueue.length > 0) {
						bot.createMessage(chan.id, '```glsl' + messageQueue.shift() + '```');
					} else {
						clearInterval(sendInOrder);
					}
				}, 300);
			}).then(bot.createMessage(msg.channel.id, ':thumbsup:').then(sentMsg => {
				setTimeout(() => { sentMsg.delete(); }, 5000);
			}));
		} else {
			let cmd = this.checkForMatch(command);
			if (cmd === null) {
				bot.createMessage(msg.channel.id, `Command \`${this.prefix}${command}\` not found`);
			} else {
				bot.createMessage(msg.channel.id, cmd.helpMessage);
			}
		}
	}

	/**
	 * Reload or load a command.
	 * @arg {Client} bot The client.
	 * @arg {Message} msg The message that was sent.
	 * @arg {String} command The command to reload or load.
	 * @arg {Object} config The bot's config.
	 * @arg {settingsManager} settingsManager The bot's {@link settingsManager}
	 */
	reload(bot, msg, command, config, settingsManager) {
		fs.access(`${this.directory}${command}.js`, fs.R_OK | fs.F_OK, error => {
			if (error) {
				bot.createMessage(msg.channel.id, ':question: Command does not exist!').then(sentMsg => {
					setTimeout(() => { msg.delete(); sentMsg.delete(); }, 5000); // Delete messages after 5 seconds.
				});
			} else {
				try {
					if (this.commands.hasOwnProperty(command)) {
						this.commands[command].destroy();
					}
					this.commands[command] = new Command(command, this.prefix, reload(`${this.directory}${command}.js`), config, bot);
					bot.createMessage(msg.channel.id, `:white_check_mark: Command **${this.prefix}${command}** loaded!`).then(sentMsg => {
						setTimeout(() => { msg.delete(); sentMsg.delete(); }, 5000); // Delete messages after 5 seconds.
					});
					if (!settingsManager.commandList[this.prefix].includes(command)) {
						settingsManager.commandList[this.prefix].push(command);
					}
				} catch (error) {
					this.logger.error(error, 'Error reloading command ' + command);
					bot.createMessage(msg.channel.id, `:x: Error loading command: ${error}`).then(sentMsg => {
						setTimeout(() => { msg.delete(); sentMsg.delete(); }, 5000); // Delete messages after 5 seconds.
					});
				}
			}
		});
	}
}

module.exports = CommandManager;<commit_msg>really fixed it this time<commit_after>/* REQUIRED DEPENDENCIES */
var reload = require('require-reload')(require);
var fs     = require('fs');

/* REQUIRED FILES */
var utils   = reload('./utils.js');
var Command = reload('./Command.js');
var _Logger = reload('./Logger.js');

/**
 * @class
 * @classdesc Handles a directory of .js files formatted as {@link Command}.
 * @prop {String} prefix Prefix for the commands handled by this CommandManager.
 * @prop {String} dir Path where the commands are located from the root directory.
 * @prop {Object<Command>} commands The loaded {@link Command}s.
 */
class CommandManager {

	/**
	 * @constructor
	 * @arg {Object} config The bot's config settings.
	 * @arg {String} prefix Prefix for the commands handled by this CommandManager.
	 * @arg {String} [dir="commands/user/"] Path to load command from, from the root directory of the bot.
	 * @arg {Sting} [color] The color to log commands with.
	 */
	constructor(config, prefix, dir = 'commands/user/', color) {
		this.prefix = prefix;
		this.directory = `${__dirname}/../${dir}`;
		this.commands = {};
		this.logger = new _Logger(config.logTimestamp, color);
	}

	/**
	 * Initialize the command manager, loading each command in the set directory.
	 * @arg {Client} bot
	 * @arg {Object} config The bot's config settings.
	 * @arg {settingsManager} settingsManager The bot's {@link settingsManager}
	 * @returns {Promise}
	 */
	initialize(bot, config, settingsManager) {
		return new Promise((resolve, reject) => {
			fs.readdir(this.directory, (err, files) => {
				if (err) reject(`Error reading commands directory: ${err}`);
				else if (!files) reject(`No files in directory ${this.directory}`);
				else {
					settingsManager.commandList[this.prefix] = [];
					for (let name of files) {
						if (name.endsWith('.js')) {
							try {
								name = name.replace(/\.js$/, '');
								let command = new Command(name, this.prefix, reload(this.directory + name + '.js'), bot, config);
								this.commands[name] = command;
								settingsManager.commandList[this.prefix].push(name);
							} catch (e) {
								this.logger.error(`${e}\n${e.stack}`, 'Error loading command ' + name);
							}
						}
					}
					resolve();
				}
			});
		});
	}

	/**
	 * Called when a message is detected with the prefix. Decides what to do.
	 * @arg {Client} bot The client.
	 * @arg {Message} msg The matching message.
	 * @arg {Object} config The JSON formatted config file.
	 * @arg {settingsManager} settingsManager The bot's {@link settingsManager}
	 */
	processCommand(bot, msg, config, settingsManager) {
		let name = msg.content.replace(this.prefix, '').split(/ |\n/)[0];
		let command = this.checkForMatch(name.toLowerCase());
		let suffix = msg.content.replace(this.prefix + name, '').trim();
		if (command !== null) {
			if (msg.channel.guild !== undefined && !msg.channel.permissionsOf(msg.author.id).has('manageChannels') && settingsManager.isCommandIgnored(this.prefix, command.name, msg.channel.guild.id, msg.channel.id, msg.author.id) === true) {
				return;
			}
			this.logger.logCommand(msg.channel.guild === undefined ? null : msg.channel.guild.name, msg.author.username, this.prefix + command.name, msg.cleanContent.replace(this.prefix + name, '').trim());
			if (msg.guild !== undefined) utils.logCommandToMixpanel(msg, command, suffix, 'SERVER');
			if (msg.guild === undefined) utils.logCommandToMixpanel(msg, command, suffix, 'DM');
			return command.execute(bot, msg, suffix, config, settingsManager, this.logger);
		} else if (name.toLowerCase() === 'help') {
			return this.help(bot, msg, msg.content.replace(this.prefix + name, '').trim());
		}
	}

	/**
	 * Checks if there is a matching command in this CommandManager.
	 * @arg {String} name The command name to look for.
	 * @returns {?Command} Returns the matching {@link Command} or false.
	 */
	checkForMatch(name) {
		if (name.startsWith(this.prefix)) {
			name = name.substr(1);
		}
		for (let key in this.commands) {
			if (key === name || this.commands[key].aliases.includes(name)) {
				return this.commands[key];
			}
		}
		return null;
	}

	/**
	 * Built-in help command
	 * If no command is specified it will DM a list of commands.
	 * If a command is specified it will send info on that command.
	 * @arg {Eris} bot The client.
	 * @arg {Eris.Message} msg The message that triggered the command.
	 * @arg {String} [command] The command to get help for.
	 */
	help(bot, msg, command) {
		this.logger.logCommand(msg.channel.guild === undefined ? null : msg.channel.guild.name, msg.author.username, this.prefix + 'help', command);
		if (!command) {
			let messageQueue = [];
			let currentMessage = `\n// Here's a list of my commands. For more info do: ${this.prefix}help <command>`;
			for (let cmd in this.commands) {
				if (this.commands[cmd].hidden === true) continue;
				let toAdd = this.commands[cmd].helpShort;
				if (currentMessage.length + toAdd.length >= 1900) {
					messageQueue.push(currentMessage);
					currentMessage = '';
				}
				currentMessage += '\n' + toAdd;
			}
			messageQueue.push(currentMessage);
			bot.getDMChannel(msg.author.id).then(chan => {
				let sendInOrder = setInterval(() => {
					if (messageQueue.length > 0) {
						bot.createMessage(chan.id, '```glsl' + messageQueue.shift() + '```');
					} else {
						clearInterval(sendInOrder);
					}
				}, 300);
			}).then(bot.createMessage(msg.channel.id, ':thumbsup:').then(sentMsg => {
				setTimeout(() => { sentMsg.delete(); }, 5000);
			}));
		} else {
			let cmd = this.checkForMatch(command);
			if (cmd === null) {
				bot.createMessage(msg.channel.id, `Command \`${this.prefix}${command}\` not found`);
			} else {
				bot.createMessage(msg.channel.id, cmd.helpMessage);
			}
		}
	}

	/**
	 * Reload or load a command.
	 * @arg {Client} bot The client.
	 * @arg {Message} msg The message that was sent.
	 * @arg {String} command The command to reload or load.
	 * @arg {Object} config The bot's config.
	 * @arg {settingsManager} settingsManager The bot's {@link settingsManager}
	 */
	reload(bot, msg, command, config, settingsManager) {
		fs.access(`${this.directory}${command}.js`, fs.R_OK | fs.F_OK, error => {
			if (error) {
				bot.createMessage(msg.channel.id, ':question: Command does not exist!').then(sentMsg => {
					setTimeout(() => { msg.delete(); sentMsg.delete(); }, 5000); // Delete messages after 5 seconds.
				});
			} else {
				try {
					if (this.commands.hasOwnProperty(command)) {
						this.commands[command].destroy();
					}
					this.commands[command] = new Command(command, this.prefix, reload(`${this.directory}${command}.js`), config, bot);
					bot.createMessage(msg.channel.id, `:white_check_mark: Command **${this.prefix}${command}** loaded!`).then(sentMsg => {
						setTimeout(() => { msg.delete(); sentMsg.delete(); }, 5000); // Delete messages after 5 seconds.
					});
					if (!settingsManager.commandList[this.prefix].includes(command)) {
						settingsManager.commandList[this.prefix].push(command);
					}
				} catch (error) {
					this.logger.error(error, 'Error reloading command ' + command);
					bot.createMessage(msg.channel.id, `:x: Error loading command: ${error}`).then(sentMsg => {
						setTimeout(() => { msg.delete(); sentMsg.delete(); }, 5000); // Delete messages after 5 seconds.
					});
				}
			}
		});
	}
}

module.exports = CommandManager;<|endoftext|>


<commit_before>'use strict';
Polymer({
  is: 'paper-steps',
  properties: {
    /**
     * Computed css class based on `_vertical` property.
     */
    _cssClass: {
      type: String,
      computed: '__cssClass(_vertical)'
    },
    /**
     * Computed boolean property based on `_vertical`, used for convenience.
     */
    _horizontal: {
      type: Boolean,
      computed: '__horizontal(_vertical)'
    },
    /**
     * If true, the `paper-steps` element is doing a little setup , please
     * wait until done.
     */
    _initializing: {
      type: Boolean,
      value: true
    },
    /**
     * Index of the currently selected `paper-step` element.
     */
    _selected: {
      type: Number,
      notify: true,
      value: 0
    },
    /**
     * Reference to the array of `paper-step` elements.
     */
    _steps: {
      type: Array
    },
    /**
     * Computed property with the total number of items in `_steps`.
     */
    _total: {
      type: Number,
      computed: '__total(_steps)'
    },
    /**
     * Determines if the `paper-steps` element is displayed vertically or
     * horizontally.
     */
    _vertical: {
      type: Boolean,
      value: false,
    },
    /**
     * If true, all of the submit buttons which are automatically
     * added for each `paper-step` element will be hidden.
     */
    actionsDisabled: {
      type: Boolean,
      value: false
    },
    /**
     * If set, and is within the valid range of selectable `paper-step`
     * elements, `paper-steps` will use this value as the beginning step.
     * `paper-steps` will also setp all previous steps to `complete` if
     * the form inputs are valid.
     *
     * Useful for saving progress and returning to it later, then jumping
     * immediately to this initial step value (Note: Zero based index of
     * step).
     */
    initialStep: {
      type: Number
    },
    /**
     * If true, requires the steps to be completed in ascending numerical order.
     * If false, the steps may be completed out of order.
     */
    linear: {
      type: Boolean,
      value: false
    },
    /**
     * Determines how the `paper-steps` element is displayed.
     * true: always display in vertical mode
     * false: display in horizontal mode if the screen is wide enough,
     *        otherwise display in vertical mode
     */
    vertical: {
      type: Boolean,
      value: false
    }
  },
  listeners: {
    'iron-activate': '_onActivate',
    'iron-deselect': '_onDeselect',
    'iron-select': '_onSelect',
    'iron-resize': '_onIronResize',
    'paper-step-next': '_onNext',
    'paper-step-complete': '_onComplete'
  },
  behaviors: [
    Polymer.IronResizableBehavior,
  ],

  // Element Lifecycle
  ready: function() {},

  attached: function() {
    var
      el, ruler,
      that = this
    ;

    this.async(function() {
      ruler = that.create('iron-selector', {id: 'ruler'});
      that.$.selector.getEffectiveChildren().forEach(
        function(s, index) {
          if (s.nodeName.toLowerCase() == 'paper-step') {
            //exclude any template or other elements.
            el = ruler.create('paper-step', {
              label: s.label,
              optional: s.optional,
              step: s.step,
              steps: s.steps,
              completed: s.completed,
              editable: s.editable,
              duplicate: true
            });
            Polymer.dom(ruler).appendChild(el);
          }
        }, this
      );

      Polymer.dom(that.root).appendChild(ruler);
      ruler.setAttribute('class', 'horizontal ' + ruler.getAttribute('class'));
      that.notifyResize, 1
      //trigger an initial update
      that._updateVertical();
    }, 250);
  },

  detached: function() {},

  // Element Behavior
  /**
   *
   */
  __cssClass: function(_vertical) {
    return (_vertical) ? 'vertical' : 'horizontal';
  },
  /**
   *
   */
  __horizontal: function(_vertical) {
    return !_vertical;
  },
  /**
   *
   */
  __total: function(steps) {
    var
      i, items, steps = steps && steps.length || 1,
      that = this
    ;
    items = this.$.steps_content.items;
    if (steps && items.length === steps && this.actionsDisabled) {
      for (i=0; i < steps; i++) {
        items[i].actionsDisabled = true;
      }
    }

    // Set initial step if assigned and value < total
    // Set any previous valid steps as completed.
    this.debounce('paper-steps-initialize', function() {
      var
        item, form,
        initial = parseInt(that.initialStep)
      ;

      if (initial !== NaN && initial < that._total) {
        initial = Math.max(initial, 0);

        for (i=0; i<initial; i++) {
          item = items[i];
          try {
            form = item._getForm();
            if (form && form.validate()) {
              item.completed = true;
            }
          } catch (e) {
            console.log('paper-steps-initialize error', e, typeof(form));
          }
        }
        that.$.steps_content.selected = initial;
      }
      that._initializing = false;
      that.fire('paper-steps-ready', that);
    }, 250);

    return steps;
  },
  /**
   *
   */
  _onActivate: function(e) {
    var
      item = e.detail && e.detail.item,
      selectable = item && item.selectable,
      selector = this.$.selector,
      items = selector && selector.items,
      previous = selector && item.step > 1 && items[selector.indexOf(item)-1],
      stop = function(evt) {
        evt.preventDefault();
        evt.stopPropagation();
        return false;
      }
    ;

    //if item is not selectable, disable click / touch event.
    if (selectable !== true) {
      return stop(e);

    } else if (this.linear) {
      //if linear, not first and previous item must be completed before advancing
      if (previous && previous.completed !== true) {
        return stop(e);
      }
    }
  },
  /**
   *
   */
  _onComplete: function(e) {
    this.$.selector.items[parseInt(e.detail) - 1].completed = true;
  },
  /**
   *
   */
  _onDeselect: function(e) {
    e.detail.item._selected = false;
  },
  /**
   *
   */
  _onNext: function(e) {
    if (this._initializing != true) {
      this.$.steps_content.selectNext();
    }
  },
  /**
   *
   */
  _onSelect: function(e) {
    e.detail.item._selected = true;
  },
  /**
   *
   */
  _onIronResize: function() {
    this._updateVertical();
  },
  /**
   *
   */
  _updateVertical: function() {
    if (this.vertical) {
      this._vertical = true;
    }
    else {
      //catch first few calls, iron-resize gets called initially
      //before ruler is ready.
      var ruler = this.querySelector('#ruler');
      if (ruler) {
        this._vertical = ruler.scrollWidth > ruler.offsetWidth;
      }
    }
  },
});
<commit_msg>enforce linear for both vertial and horizontal views<commit_after>'use strict';
Polymer({
  is: 'paper-steps',
  properties: {
    /**
     * Computed css class based on `_vertical` property.
     */
    _cssClass: {
      type: String,
      computed: '__cssClass(_vertical)'
    },
    /**
     * Computed boolean property based on `_vertical`, used for convenience.
     */
    _horizontal: {
      type: Boolean,
      computed: '__horizontal(_vertical)'
    },
    /**
     * If true, the `paper-steps` element is doing a little setup , please
     * wait until done.
     */
    _initializing: {
      type: Boolean,
      value: true
    },
    /**
     * Index of the currently selected `paper-step` element.
     */
    _selected: {
      type: Number,
      notify: true,
      value: 0
    },
    /**
     * Reference to the array of `paper-step` elements.
     */
    _steps: {
      type: Array
    },
    /**
     * Computed property with the total number of items in `_steps`.
     */
    _total: {
      type: Number,
      computed: '__total(_steps)'
    },
    /**
     * Determines if the `paper-steps` element is displayed vertically or
     * horizontally.
     */
    _vertical: {
      type: Boolean,
      value: false,
    },
    /**
     * If true, all of the submit buttons which are automatically
     * added for each `paper-step` element will be hidden.
     */
    actionsDisabled: {
      type: Boolean,
      value: false
    },
    /**
     * If set, and is within the valid range of selectable `paper-step`
     * elements, `paper-steps` will use this value as the beginning step.
     * `paper-steps` will also setp all previous steps to `complete` if
     * the form inputs are valid.
     *
     * Useful for saving progress and returning to it later, then jumping
     * immediately to this initial step value (Note: Zero based index of
     * step).
     */
    initialStep: {
      type: Number
    },
    /**
     * If true, requires the steps to be completed in ascending numerical order.
     * If false, the steps may be completed out of order.
     */
    linear: {
      type: Boolean,
      value: false
    },
    /**
     * Determines how the `paper-steps` element is displayed.
     * true: always display in vertical mode
     * false: display in horizontal mode if the screen is wide enough,
     *        otherwise display in vertical mode
     */
    vertical: {
      type: Boolean,
      value: false
    }
  },
  listeners: {
    'iron-activate': '_onActivate',
    'iron-deselect': '_onDeselect',
    'iron-select': '_onSelect',
    'iron-resize': '_onIronResize',
    'paper-step-next': '_onNext',
    'paper-step-complete': '_onComplete'
  },
  behaviors: [
    Polymer.IronResizableBehavior,
  ],

  // Element Lifecycle
  ready: function() {},

  attached: function() {
    var
      el, ruler,
      that = this
    ;

    this.async(function() {
      ruler = that.create('iron-selector', {id: 'ruler'});
      that.$.selector.getEffectiveChildren().forEach(
        function(s, index) {
          if (s.nodeName.toLowerCase() == 'paper-step') {
            //exclude any template or other elements.
            el = ruler.create('paper-step', {
              label: s.label,
              optional: s.optional,
              step: s.step,
              steps: s.steps,
              completed: s.completed,
              editable: s.editable,
              duplicate: true
            });
            Polymer.dom(ruler).appendChild(el);
          }
        }, this
      );

      Polymer.dom(that.root).appendChild(ruler);
      ruler.setAttribute('class', 'horizontal ' + ruler.getAttribute('class'));
      that.notifyResize, 1
      //trigger an initial update
      that._updateVertical();
    }, 250);
  },

  detached: function() {},

  // Element Behavior
  /**
   *
   */
  __cssClass: function(_vertical) {
    return (_vertical) ? 'vertical' : 'horizontal';
  },
  /**
   *
   */
  __horizontal: function(_vertical) {
    return !_vertical;
  },
  /**
   *
   */
  __total: function(steps) {
    var
      i, items, steps = steps && steps.length || 1,
      that = this
    ;
    items = this.$.steps_content.items;
    if (steps && items.length === steps && this.actionsDisabled) {
      for (i=0; i < steps; i++) {
        items[i].actionsDisabled = true;
      }
    }

    // Set initial step if assigned and value < total
    // Set any previous valid steps as completed.
    this.debounce('paper-steps-initialize', function() {
      var
        item, form,
        initial = parseInt(that.initialStep)
      ;

      if (initial !== NaN && initial < that._total) {
        initial = Math.max(initial, 0);

        for (i=0; i<initial; i++) {
          item = items[i];
          try {
            form = item._getForm();
            if (form && form.validate()) {
              item.completed = true;
            }
          } catch (e) {
            console.log('paper-steps-initialize error', e, typeof(form));
          }
        }
        that.$.steps_content.selected = initial;
      }
      that._initializing = false;
      that.fire('paper-steps-ready', that);
    }, 250);

    return steps;
  },
  /**
   *
   */
  _onActivate: function(e) {
    var
      item = e.detail && e.detail.item,
      selectable = item && item.selectable,
      selector = this.$.selector,
      items = selector && selector.items,
      previous = selector && item.step > 1 && items[item.step-2],
      stop = function(evt) {
        evt.preventDefault();
        evt.stopPropagation();
        return false;
      }
    ;

    //if item is not selectable, disable click / touch event.
    if (selectable !== true) {
      return stop(e);

    } else if (this.linear) {
      //if linear, not first and previous item must be completed before advancing
      if (previous && previous.completed !== true) {
        return stop(e);
      }
    }
  },
  /**
   *
   */
  _onComplete: function(e) {
    this.$.selector.items[parseInt(e.detail) - 1].completed = true;
  },
  /**
   *
   */
  _onDeselect: function(e) {
    e.detail.item._selected = false;
  },
  /**
   *
   */
  _onNext: function(e) {
    if (this._initializing != true) {
      this.$.steps_content.selectNext();
    }
  },
  /**
   *
   */
  _onSelect: function(e) {
    e.detail.item._selected = true;
  },
  /**
   *
   */
  _onIronResize: function() {
    this._updateVertical();
  },
  /**
   *
   */
  _updateVertical: function() {
    if (this.vertical) {
      this._vertical = true;
    }
    else {
      //catch first few calls, iron-resize gets called initially
      //before ruler is ready.
      var ruler = this.querySelector('#ruler');
      if (ruler) {
        this._vertical = ruler.scrollWidth > ruler.offsetWidth;
      }
    }
  },
});
<|endoftext|>