"use strict";

// stdlib
var Fs = require('fs');

// nodeca
var NLib = require('nlib');

// 3rd-party
var StaticLulz = require('static-lulz');
var FsTools = NLib.Vendor.FsTools;
var Async = NLib.Vendor.Async;

module.exports = NLib.Application.create({
  root: __dirname,
  name: 'nodeca.core',
  bootstrap: function (nodeca, callback) {
    // empty bootstrap... for now..
    callback();
  }
});

global.nodeca.hooks.init.after('bundles', function (next) {
  nodeca.runtime.assets_server = new StaticLulz();

  FsTools.walk(nodeca.runtime.assets_path, function (file, stats, next_file) {
    // Fill in Static lulz with files and data
    Async.waterfall([
      Async.apply(Fs.readFile, file),
      function (buffer, callback) {
        var rel_path = file.replace(nodeca.runtime.assets_path, '');
        nodeca.runtime.assets_server.add(rel_path, buffer);
        callback();
      }
    ], next_file);
  }, next);
});

global.nodeca.hooks.init.after('initialization', function (next) {
  nodeca.runtime.http_server = connect.createServer();
  next();
});

////////////////////////////////////////////////////////////////////////////////
// vim:ts=2:sw=2
////////////////////////////////////////////////////////////////////////////////

"use strict";

// stdlib
var Fs = require('fs');

// nodeca
var NLib = require('nlib');

// 3rd-party
var StaticLulz = require('static-lulz');
var FsTools = NLib.Vendor.FsTools;
var Async = NLib.Vendor.Async;

module.exports = NLib.Application.create({
  root: __dirname,
  name: 'nodeca.core',
  bootstrap: function (nodeca, callback) {
    // empty bootstrap... for now..
    callback();
  }
});

global.nodeca.hooks.init.after('bundles', function (next) {
  nodeca.runtime.assets_server = new StaticLulz();

  FsTools.walk(nodeca.runtime.assets_path, function (file, stats, next_file) {
    // Fill in Static lulz with files and data
    Async.waterfall([
      Async.apply(Fs.readFile, file),
      function (buffer, callback) {
        var rel_path = file.replace(nodeca.runtime.assets_path, '');
        nodeca.runtime.assets_server.add(rel_path, buffer);
        callback();
      }
    ], next_file);
  }, next);
});

global.nodeca.hooks.init.after('initialization', function (next) {
  var http_server = connect.createServer();
  next();
});

////////////////////////////////////////////////////////////////////////////////
// vim:ts=2:sw=2
////////////////////////////////////////////////////////////////////////////////

Remove http_server from nodeca context.


var gulp = require('gulp'),
    gutil = require('gulp-util');

function loadTasks(config) {
    var tasks = Object.keys(config);
    tasks.forEach(function (name) {
        var taskConfig = config[name],
            task = require('./tasks/' + name)(taskConfig);

        gulp.task(name, task);
    });

    return gulp;
}

module.exports = function (config) {
    if (config) {
        return loadTasks(config);
    }
    return loadTasks;
};

var gulp = require('gulp'),
    gutil = require('gulp-util');

function loadTasks(config) {
    var tasks = Object.keys(config);
    tasks.forEach(function (name) {
        var taskConfig = config[name],
            task = require(taskConfig.def)(taskConfig);

        gulp.task(name, task);
    });

    return gulp;
}

module.exports = function (config) {
    if (config) {
        return loadTasks(config);
    }
    return loadTasks;
};

Use task property for require func


function neymar(type, props, ...children) {
    props = props || {}
    return {
        type,
        props,
        children: Array.prototype.concat.apply([], children)
    }
}

function createElement(node) {
    if (typeof node === 'string') {
        return document.createTextNode(node)
    }

    const el = document.createElement(node.type)
    node.children
        .map(createElement)
        .map(el.appendChild.bind(el))

    return el
}

function view() {
    return (

Neymar é fera

)
}

function render(node) {
    node.appendChild(createElement(view()))
}

function neymar(type, props, ...children) {
    props = props || {}
    return {
        type,
        props,
        children: Array.prototype.concat.apply([], children)
    }
}

function setProperties(node, props) {
    Object.keys(props).map(prop => {
        const propName = prop === 'className' ? 'class' : prop
        node.setAttribute(propName, props[prop])
    })

}

function createElement(node) {
    if (typeof node === 'string') {
        return document.createTextNode(node)
    }

    const el = document.createElement(node.type)
    setProperties(el, node.props)
    node.children
        .map(createElement)
        .map(el.appendChild.bind(el))

    return el
}

function view() {
    return (

O ousado chegou

Tô chegando com os refri rapazeada

)
}

function render(node) {
    node.appendChild(createElement(view()))
}

Add property assignments to the DOM node being created


var window = require('global/window');
var nodeCrypto = require('crypto');

function getRandomValues(buf) {
  if (window.crypto && window.crypto.getRandomValues) {
    window.crypto.getRandomValues(buf);
  }
  else if (typeof window.msCrypto === 'object' && typeof window.msCrypto.getRandomValues === 'function') {
    window.msCrypto.getRandomValues(buf);
  }
  else if (nodeCrypto) {
    if (buf.length > 65536) {
      var e = new Error();
      e.code = 22;
      e.message = 'Failed to execute \'getRandomValues\' on \'Crypto\': The ' +
        'ArrayBufferView\'s byte length (' + buf.length + ') exceeds the ' +
        'number of bytes of entropy available via this API (65536).';
      e.name = 'QuotaExceededError';
      throw e;
    }
    var bytes = nodeCrypto.randomBytes(buf.length);
    buf.set(bytes);
  }
  else {
    throw new Error('No secure random number generator available.');
  }
};

module.exports = getRandomValues;

var window = require('global/window');
var nodeCrypto = require('crypto');

function getRandomValues(buf) {
  if (window.crypto && window.crypto.getRandomValues) {
    window.crypto.getRandomValues(buf);
  }
  else if (typeof window.msCrypto === 'object' && typeof window.msCrypto.getRandomValues === 'function') {
    window.msCrypto.getRandomValues(buf);
  }
  else if (nodeCrypto.randomBytes) {
    if (buf.length > 65536) {
      var e = new Error();
      e.code = 22;
      e.message = 'Failed to execute \'getRandomValues\' on \'Crypto\': The ' +
        'ArrayBufferView\'s byte length (' + buf.length + ') exceeds the ' +
        'number of bytes of entropy available via this API (65536).';
      e.name = 'QuotaExceededError';
      throw e;
    }
    var bytes = nodeCrypto.randomBytes(buf.length);
    buf.set(bytes);
  }
  else {
    throw new Error('No secure random number generator available.');
  }
};

module.exports = getRandomValues;

Check for `randomBytes` before trying to use Node.js crypto


#!/usr/bin/env node

(function() {
  var Dependument = require('dependument').Dependument;

  var d = new Dependument({
    source: 'package.json',
    output: 'DEPENDENCIES.md'
  });

  d.process();
})();

Add declaration to top of script


const RunPluginScriptCommand = require('./src/commands/runPluginScript')
const CreateProjectCommand = require('./src/commands/createProject')
const AddPluginsCommand = require('./src/commands/addPlugins')
const RemovePluginsCommand = require('./src/commands/removePlugins')
const UpdatePluginsCommand = require('./src/commands/updatePlugins')
const GenerateCommand = require('./src/commands/generate')
const CommitCommand = require('./src/commands/commit')
const ReleaseCommand = require('./src/commands/release')
const AssembleCommand = require('./src/commands/assemble')

const commands = [
  RunPluginScriptCommand,
  CreateProjectCommand,
  AddPluginsCommand,
  RemovePluginsCommand,
  UpdatePluginsCommand,
  GenerateCommand,
  CommitCommand,
  AssembleCommand,
  ReleaseCommand,
]

const runCommand = (commandName, argv, params, options) => {
  const Command = commands.find(command => command.commandName === commandName)
  if (!Command) {
    throw new Error('Can\'t find command')
  }

  const command = new Command(argv, options)
  command.init()

  return command.run(params)
}

module.exports = {
  runCommand,
}

const RunPluginScriptCommand = require('./src/commands/runPluginScript')
const CreateProjectCommand = require('./src/commands/createProject')
const AddPluginsCommand = require('./src/commands/addPlugins')
const RemovePluginsCommand = require('./src/commands/removePlugins')
const UpdatePluginsCommand = require('./src/commands/updatePlugins')
const GenerateCommand = require('./src/commands/generate')
const CommitCommand = require('./src/commands/commit')
const ReleaseCommand = require('./src/commands/release')
const AssembleCommand = require('./src/commands/assemble')

const commands = [
  RunPluginScriptCommand,
  CreateProjectCommand,
  AddPluginsCommand,
  RemovePluginsCommand,
  UpdatePluginsCommand,
  GenerateCommand,
  CommitCommand,
  AssembleCommand,
  ReleaseCommand,
]

const runCommand = (commandName, argv, params, options) => {
  const Command = commands.find(command => command.commandName === commandName)
  if (!Command) {
    throw new Error('Can\'t find command')
  }

  const command = new Command(argv, options)
  return command.run(params)
}

module.exports = {
  runCommand,
}

Remove init in public api


'use strict'
const http = require('http')
const express = require('express')
const bodyParser = require('body-parser')
const Bot = require('messenger-bot')

let bot = new Bot({
  token: process.env.PAGE_TOKEN,
  verify: 'VERIFY_TOKEN',
  app_secret: 'APP_SECRET'
})

bot.on('error', (err) => {
  console.log(err.message)
})

bot.on('message', (payload, reply) => {
  let text = payload.message.text

  bot.getProfile(payload.sender.id, (err, profile) => {
    if (err) throw err

    reply({ text }, (err) => {
      if (err) throw err

      console.log(`Echoed back to ${profile.first_name} ${profile.last_name}: ${text}`)
    })
  })
})

let app = express()

app.use(bodyParser.json())
app.use(bodyParser.urlencoded({
  extended: true
}))

app.get('/', (req, res) => {
  return bot._verify(req, res)
})

app.post('/', (req, res) => {
  bot._handleMessage(req.body)
  res.end(JSON.stringify({ status: 'ok' }))
})

http.createServer(app).listen(3000)

'use strict'
const http = require('http')
const express = require('express')
const bodyParser = require('body-parser')
const Bot = require('messenger-bot')

let bot = new Bot({
  token: process.env.PAGE_TOKEN,
  verify: 'VERIFY_TOKEN',
  app_secret: 'APP_SECRET'
})

bot.on('error', (err) => {
  console.log(err.message)
})

bot.on('message', (payload, reply) => {
  let text = payload.message.text

  bot.getProfile(payload.sender.id, (err, profile) => {
    if (err) throw err

    reply({ text }, (err) => {
      if (err) throw err

      console.log(`Echoed back to ${profile.first_name} ${profile.last_name}: ${text}`)
    })
  })
})

let app = express()
app.set('port', process.env.PORT || 8080);

app.use(bodyParser.json())
app.use(bodyParser.urlencoded({
  extended: true
}))

app.get('/', (req, res) => {
  return bot._verify(req, res)
})

app.post('/', (req, res) => {
  bot._handleMessage(req.body)
  res.end(JSON.stringify({ status: 'ok' }))
})

app.listen(app.get('port'), () => {
  console.log('Node app is running on port', app.get('port'));
});

Add message for starting the server


var excludeMethods = [
  /^constructor$/,
  /^render$/,
  /^component[A-Za-z]+$/
];

var displayNameReg = /^function\s+([a-zA-Z]+)/;

function isExcluded(methodName) {
  return excludeMethods.some(function (reg) {
    return reg.test(methodName) === false;
  });
}

function bindToClass(scope, methods) {
  var componentName = scope.constructor.toString().match(displayNameReg)[1];
  methods = Array.isArray(methods) ? methods : [];

  methods.forEach(function(methodName) {
    if (methodName in scope) {
      scope[methodName] = scope[methodName].bind(scope);
    } else {
      throw new Error('Method "' + methodName + '" does not exists in "' + componentName + '"');
    }
  });

  // for debugging purposes only?
  // if (process.env.NODE_ENV != 'production') {
  if (Object.getOwnPropertyNames) {
    var proto = scope.constructor.prototype;

    Object.getOwnPropertyNames(proto).forEach(function(methodName) {
      var original = scope[methodName];
      if (typeof original === 'function' && isExcluded(methodName) && methods.indexOf(methodName) === -1) {
        scope[methodName] = function () {
          // test whether the scope is different
          if (this !== scope) {
            var e = 'Warning: Method "' + methodName + '" of "' + componentName + '" is not bound. Bad you!';
            console.error(e);
          }

          return original.apply(scope, arguments);
        };
      }
    });

  }
  // }
}

module.exports = bindToClass;

var excludeMethods = [
  /^constructor$/,
  /^render$/,
  /^component[A-Za-z]+$/,
  /^shouldComponentUpdate$/
];

var displayNameReg = /^function\s+([a-zA-Z]+)/;

function isExcluded(methodName) {
  return excludeMethods.some(function (reg) {
    return reg.test(methodName) === false;
  });
}

function bindToClass(scope, methods) {
  var componentName = scope.constructor.toString().match(displayNameReg)[1];
  methods = Array.isArray(methods) ? methods : [];

  methods.forEach(function(methodName) {
    if (methodName in scope) {
      scope[methodName] = scope[methodName].bind(scope);
    } else {
      throw new Error('Method "' + methodName + '" does not exists in "' + componentName + '"');
    }
  });

  // for debugging purposes only?
  // if (process.env.NODE_ENV != 'production') {
  if (Object.getOwnPropertyNames) {
    var proto = scope.constructor.prototype;

    Object.getOwnPropertyNames(proto).forEach(function(methodName) {
      var original = scope[methodName];
      if (typeof original === 'function' && isExcluded(methodName) && methods.indexOf(methodName) === -1) {
        scope[methodName] = function () {
          // test whether the scope is different
          if (this !== scope) {
            var e = 'Warning: Method "' + methodName + '" of "' + componentName + '" is not bound. Bad you!';
            console.error(e);
          }

          return original.apply(scope, arguments);
        };
      }
    });

  }
  // }
}

module.exports = bindToClass;

Add shouldComponentUpdate to excludeMethods array.


var isArray = require('isArray');

/**
 * Returns the maximum array depth of the array, meaning the maximum number of nests in the nested arrays.
 * @param  {Anything} array The array to find the dimensionality of
 * @param  {Boolean} shallowSearch If the search should *not* search all element
 * @return {Integer} The dimensionality of the array
 */
function dimensionality (array, shallowSearch) {
  var dimensions = 0;
  if (isArray(array)) {
    if (shallowSearch) {
      dimensions = 1 + dimensionality(array[0], shallowSearch);
    } else {
      dimensions = 1 + array.reduce(function (a, b) {
        return Math.max(dimensionality(a), dimensionality(b));
      }, 0);
    }
  }

  return dimensions;
}

module.exports = dimensionality;

var isArray = require('isarray');

/**
 * Returns the maximum array depth of the array, meaning the maximum number of nests in the nested arrays.
 * @param  {Anything} array The array to find the dimensionality of
 * @param  {Boolean} shallowSearch If the search should *not* search all element
 * @return {Integer} The dimensionality of the array
 */
function dimensionality (array, shallowSearch) {
  var dimensions = 0;
  if (isArray(array)) {
    if (shallowSearch) {
      dimensions = 1 + dimensionality(array[0], shallowSearch);
    } else {
      dimensions = 1 + array.reduce(function (a, b) {
        return Math.max(dimensionality(a), dimensionality(b));
      }, 0);
    }
  }

  return dimensions;
}

module.exports = dimensionality;

Fix casing of require('isarray') for cross-platform compatibility


var once = require('once');

module.exports = function () {

    var collections = [].slice.call(arguments);

    if(!Array.isArray(collections[0])) {

        collections = [ collections ];
    }

    return Promise.all(

    	collections.map(function(plugins) {

            var i = -1;

            return new Promise(function(resolve, reject){

                var next = function(data) {

                    var result, done;

                    if (++i < plugins.length) {

                        done = once(function(err, data){

                            if(err) {
                                reject(err);
                            }
                            else {
                                next(data);
                            }
                        });

                        result = plugins[i](data, done);

                        if(typeof result.then == 'function') {

                            result.then(function(data){

                                done(null, data);

                            }, done);
                        }
                    }
                    else {

                        resolve(data);
                    }
                };

                next([]);
            });
	    })
    );
};

var once = require('once');

module.exports = function () {

    var collections = [].slice.call(arguments);

    if(!Array.isArray(collections[0])) {

        collections = [ collections ];
    }

    return Promise.all(

    	collections.map(function(plugins) {

            var i = -1;

            return new Promise(function(resolve, reject){

                var next = function(data) {

                    var result, done;

                    if (++i < plugins.length) {

                        done = once(function(err, data){

                            if(err) {
                                reject(err);
                            }
                            else {
                                next(data);
                            }
                        });

                        result = plugins[i](data, done);

                        if(result && typeof result.then == 'function') {

                            result.then(function(data){

                                done(null, data);

                            }, done);
                        }
                    }
                    else {

                        resolve(data);
                    }
                };

                next([]);
            });
	    })
    );
};

Fix can not read property of undefined error


/*jslint indent: 2, vars: true */
var es = require('event-stream');
var gutil = require('gulp-util');
var Buffer = require('buffer').Buffer;

var cheerio = require('cheerio');

function modify(elem, attr, modifier) {
  "use strict";

  var val = elem.attr(attr);
  if (val) { elem.attr(attr, modifier(val)); }
}

module.exports = function (query, modifier) {
  "use strict";

  var queryHtml = function (file) {
    if (file.isNull()) { return this.emit('data', file); } // pass along
    if (file.isStream()) { return this.emit('error', new Error("gulp-coffee: Streaming not supported")); }

    var str = file.contents.toString('utf8');
    var $ = cheerio.load(str);

    $(query).each(function () {
      var elem = $(this);
      modify(elem, 'href', modifier);
      modify(elem, 'src', modifier);
    });

    file.contents = new Buffer($.root().html());
    this.emit('data', file);
  };

  return es.through(queryHtml);
};

/*jslint indent: 2, vars: true */
var es = require('event-stream');
var gutil = require('gulp-util');
var Buffer = require('buffer').Buffer;

var cheerio = require('cheerio');

function modify(file, elem, attr, modifier) {
  "use strict";

  var val = elem.attr(attr);
  if (val) { elem.attr(attr, modifier(val, file)); }
}

module.exports = function (query, modifier) {
  "use strict";

  var queryHtml = function (file) {
    if (file.isNull()) { return this.emit('data', file); } // pass along
    if (file.isStream()) { return this.emit('error', new Error("gulp-coffee: Streaming not supported")); }

    var str = file.contents.toString('utf8');
    var $ = cheerio.load(str);

    $(query).each(function () {
      var elem = $(this);
      modify(file, elem, 'href', modifier);
      modify(file, elem, 'src', modifier);
    });

    file.contents = new Buffer($.root().html());
    this.emit('data', file);
  };

  return es.through(queryHtml);
};

Add file as second param


'use strict'

var visit = require('unist-util-visit')
var toString = require('nlcst-to-string')
var posjs = require('pos')

module.exports = pos

var tagger = new posjs.Tagger()

function pos() {
  return transformer
}

function transformer(tree) {
  var queue = []

  visit(tree, 'WordNode', visitor)

  /* Gather a parent if not already gathered. */
  function visitor(node, index, parent) {
    if (parent && queue.indexOf(parent) === -1) {
      queue.push(parent)
      one(parent)
    }
  }

  /* Patch all words in `parent`. */
  function one(node) {
    var children = node.children
    var length = children.length
    var index = -1
    var values = []
    var words = []
    var child
    var tags

    while (++index < length) {
      child = children[index]

      if (child.type === 'WordNode') {
        values.push(toString(child))
        words.push(child)
      }
    }

    tags = tagger.tag(values)
    index = -1
    length = tags.length

    while (++index < length) {
      patch(words[index], tags[index][1])
    }
  }

  // Patch a `partOfSpeech` property on `node`s.
  function patch(node, tag) {
    var data = node.data || (node.data = {})
    data.partOfSpeech = tag
  }
}

'use strict'

var visit = require('unist-util-visit')
var toString = require('nlcst-to-string')
var posjs = require('pos')

module.exports = pos

var tagger = new posjs.Tagger()

function pos() {
  return transformer
}

function transformer(tree) {
  visit(tree, 'SentenceNode', visitor)

  // Patch all words in `parent`.
  function visitor(node) {
    var children = node.children
    var length = children.length
    var index = -1
    var values = []
    var words = []
    var child
    var tags

    // Find words.
    while (++index < length) {
      child = children[index]

      if (child.type === 'WordNode') {
        values.push(toString(child))
        words.push(child)
      }
    }

    // Apply tags if there are words.
    if (values.length !== 0) {
      tags = tagger.tag(values)
      length = tags.length
      index = -1

      while (++index < length) {
        patch(words[index], tags[index][1])
      }
    }

    // Don’t enter sentences.
    return visit.SKIP
  }

  // Patch a `partOfSpeech` property on `node`s.
  function patch(node, tag) {
    var data = node.data || (node.data = {})
    data.partOfSpeech = tag
  }
}

Refactor algorithm to perform better


/* Launches project gallery lightbox from button on project show pages */

$("#launch-gallery").on("click", function(event) {
  event.preventDefault();
  $("#project-gallery .image:first-child > img").trigger("click");
});

/* Launches project gallery lightbox from button on project show pages */

$("#launch-gallery").on("click", function(event) {
  event.preventDefault();
  document.getElementById('project-gallery').querySelector('.image').click();
});

Fix ordering issue when launching lightgallery from the button


let _ = require('underscore');

module.exports = function (ldapSettings, userData) {
    let searchDn = ldapSettings.searchDn || 'cn';

    // userData.mail may be a string with one mail address or an array.
    // Nevertheless we are only interested in the first mail address here - if there should be more...
    let tmpEMail = userData.mail;
    if (Array.isArray(userData.mail)) {
        tmpEMail = userData.mail[0];
    }
    let tmpEMailArray = [{
        address: tmpEMail,
        verified: true,
        fromLDAP: true
    }];
    let user = {
        createdAt: new Date(),
        emails: tmpEMailArray,
        username: userData[searchDn],
        profile: _.pick(userData, _.without(ldapSettings.whiteListedFields, 'mail'))
    };
    // copy over the LDAP user's long name from "cn" field to the meteor accounts long name field
    if (user.profile.cn) {
        user.profile.name = user.profile.cn;
        delete user.profile.cn;
    }
    if (userData.isInactive) {
        user.isInactive = true;
    }
    return user;
};

let _ = require('underscore');

module.exports = function (ldapSettings, userData) {
    let searchDn = ldapSettings.searchDn || 'cn';

    // userData.mail may be a string with one mail address or an array.
    // Nevertheless we are only interested in the first mail address here - if there should be more...
    let tmpEMail = userData.mail;
    if (Array.isArray(userData.mail)) {
        tmpEMail = userData.mail[0];
    }
    let tmpEMailArray = [{
        address: tmpEMail,
        verified: true,
        fromLDAP: true
    }];
    let user = {
        createdAt: new Date(),
        emails: tmpEMailArray,
        username: userData[searchDn].toLowerCase(),
        profile: _.pick(userData, _.without(ldapSettings.whiteListedFields, 'mail'))
    };
    // copy over the LDAP user's long name from "cn" field to the meteor accounts long name field
    if (user.profile.cn) {
        user.profile.name = user.profile.cn;
        delete user.profile.cn;
    }
    if (userData.isInactive) {
        user.isInactive = true;
    }
    return user;
};

Make sure the username is lower case when imported from ldap


/*! resol-vbus | Copyright (c) 2013-2014, Daniel Wippermann | MIT license */
'use strict';

var _ = require('lodash');
var Q = require('q');

var testUtils = {

    performAsyncTest: function(done, callback) {
        return Q.fcall(callback).then(function() {
            done();
        }).fail(function(reason) {
            done(reason);
        });
    },

    expectPromise: function(promise) {
        expect(promise).to.be.an('object');
        expect(promise).to.have.a.property('then').that.is.a('function');
        return promise;
    },

    expectRanges: function(ranges) {
        expect(ranges).a('array');

        var comparableRanges = _.map(ranges, function(range) {
            return {
                minTimestamp: range.minTimestamp.toISOString(),
                maxTimestamp: range.maxTimestamp.toISOString(),
            };
        });

        return expect(comparableRanges);
    },

};

module.exports = testUtils;

/*! resol-vbus | Copyright (c) 2013-2014, Daniel Wippermann | MIT license */
'use strict';

var _ = require('lodash');
var Q = require('q');

var testUtils = {

    performAsyncTest: function(done, callback) {
        return Q.fcall(callback).then(function() {
            done();
        }).fail(function(reason) {
            done(reason);
        });
    },

    expectPromise: function(promise) {
        expect(promise).to.be.an('object');
        expect(promise).to.have.a.property('then').that.is.a('function');
        return promise;
    },

    expectRanges: function(ranges) {
        expect(ranges).a('array');

        var comparableRanges = _.map(ranges, function(range) {
            return {
                minTimestamp: range.minTimestamp.toISOString(),
                maxTimestamp: range.maxTimestamp.toISOString(),
            };
        });

        return expect(comparableRanges);
    },

    adaptTimeout: function(timeout) {
        var factor = process.env.TRAVIS ? 1000 : 1;
        return timeout * factor;
    },

};

module.exports = testUtils;

Add `adaptTimeout` helper that changes timeouts during Travis test.


var oldContents = null;

var editForumPost = function(postId, postNumber) {
    var postSelector = $("#" + postNumber);

    // We grab the text element of the post and turn it into a textarea to make it editable
    oldContents = postSelector.find(".message")

    var postContents = oldContents.text();

    var editForm = $('<form>', {
                                id: 'post-edit-form-' + postNumber,
                                method: 'POST',
                                action:"/forum/post/" + postId
                                });

    var formTextArea = $('<textarea>', {id:'post-edit-area-' + postNumber, name:"changes", class:'edit-post-box'});
    formTextArea.text(postContents);

    var formSubmitButton = $('<input>', {type:'submit', value: 'Submit'});
    var formCancelButton = $('<a>', {'data-icon':'s', onclick:'cancelEdit(' + postNumber + ')' }).text("Cancel");

    editForm.append(formTextArea);
    editForm.append(formSubmitButton);
    editForm.append(formCancelButton);

    oldContents.replaceWith(editForm);

    $("#edit-button-" + postNumber).hide();
};

var cancelEdit = function(postNumber) {
    $('#post-edit-form-' + postNumber).replaceWith(oldContents);

    $("#edit-button-" + postNumber).show();
}

// Map of old post contents by post number in case the user clicks 'edit' on multiple posts
// on the same page (although rare.)
var oldContents = {};

var editForumPost = function(postId, postNumber) {
    var postSelector = $("#" + postNumber);

    // We grab the text element of the post and turn it into a textarea to make it editable
    oldContents[postNumber] = postSelector.find(".message");

    var old = oldContents[postNumber];

    var postContents = old.text();

    var editForm = $('<form>', {
                                id: 'post-edit-form-' + postNumber,
                                method: 'POST',
                                action:"/forum/post/" + postId
                                });

    var formTextArea = $('<textarea>', {id:'post-edit-area-' + postNumber, name:"changes", class:'edit-post-box'});
    formTextArea.text(postContents);

    var formSubmitButton = $('<input>', {type:'submit', value: 'Submit'});
    var formCancelButton = $('<a>', {'data-icon':'s', onclick:'cancelEdit(' + postNumber + ')' }).text("Cancel");

    editForm.append(formTextArea);
    editForm.append(formSubmitButton);
    editForm.append(formCancelButton);

    old.replaceWith(editForm);

    $("#edit-button-" + postNumber).hide();
};

var cancelEdit = function(postNumber) {
    $('#post-edit-form-' + postNumber).replaceWith(oldContents[postNumber]);

    $("#edit-button-" + postNumber).show();
}

Fix issue when edit is clicked on more than one post and then cancelled on both.


$('.close-modal').click(function () {
    $('#yt-player').hide();
    $('#yt-player iframe').attr("src", jQuery("#yt-player iframe").attr("src"));
});

jQuery(document).bind('hidden.bs.modal', function() {
    var vid = jQuery('#monologueForThree iframe[src*="youtube"]');
    if ( vid.length > 0 ){
        var src = vid.attr('src');
        vid.attr('src', '');
        vid.attr('src', src);
    }
});

Fix issue with 2 embed modals interferring with YouTube close


define([
	"../core",
	"../selector",
	"../effects",
], function( jQuery ) {
	jQuery.expr.filters.animated = function( elem ) {
		return jQuery.grep(jQuery.timers, function( fn ) {
			return elem === fn.elem;
		}).length;
	};
});

define([
	"../core",
	"../selector",
	"../effects"
], function( jQuery ) {
	jQuery.expr.filters.animated = function( elem ) {
		return jQuery.grep(jQuery.timers, function( fn ) {
			return elem === fn.elem;
		}).length;
	};
});

Remove trailing comma in define array. Close gh-1336.


module.exports.routes = {
  'post /register': 'UserController.create',
  'post /logout': 'AuthController.logout',

  'post /auth/local': 'AuthController.callback',
  'post /auth/local/:action': 'AuthController.callback',

  'post /auth/:provider/:action': 'AuthController.callback',

  'get /auth/:provider': 'AuthController.provider',
  'get /auth/:provider/callback': 'AuthController.callback',
  'get /auth/:provider/:action': 'AuthController.callback'
};

module.exports.routes = {
  'post /register': 'UserController.create',
  'post /logout': 'AuthController.logout',

  'post /auth/local': 'AuthController.callback',
  'post /auth/local/:action': 'AuthController.callback',

  'post /auth/:provider': 'AuthController.callback',
  'post /auth/:provider/:action': 'AuthController.callback',

  'get /auth/:provider': 'AuthController.provider',
  'get /auth/:provider/callback': 'AuthController.callback',
  'get /auth/:provider/:action': 'AuthController.callback'
};

Add new Delegated Authentication Protocol


#!/usr/bin/env babel-node

import assert from 'assert';

function getObj() {
  return {
    x: 2,
    y: 3
  };
}

let {
  x,
  y,
  z = 6
} = getObj();

assert.strictEqual(x, 2);
assert.strictEqual(y, 3);
assert.strictEqual(z, 6);

#!/usr/bin/env babel-node

import assert from 'assert';

//
// 分割代入内で代入ができる
//
function getObj() {
  return {
    x: 2,
    y: 3
  };
}

let {
  x,
  y,
  z = 6
} = getObj();

assert.strictEqual(x, 2);
assert.strictEqual(y, 3);
assert.strictEqual(z, 6);

//
// 別変数名で展開できる
//
function stringifyObj({ x: xVar, y: yVar }) {
  return xVar + yVar;
}
assert.strictEqual(stringifyObj({ x: 'foo', y: 'bar' }), 'foobar');

Update a sample of ES6


/**
 * Created by Zack Boman on 1/31/14.
 * http://www.zackboman.com or tennisgent@gmail.com
 */

(function(){

    var mod = angular.module('routeStyles', ['ngRoute']);

    mod.directive('head', ['$rootScope','$compile',
        function($rootScope, $compile){
            return {
                restrict: 'E',
                link: function(scope, elem){
                    var html = '<link rel="stylesheet" ng-repeat="(routeCtrl, cssUrl) in routeStyles" ng-href="{{cssUrl}}" >';
                    elem.append($compile(html)(scope));
                    scope.routeStyles = {};
                    $rootScope.$on('$routeChangeStart', function (e, next, current) {
                        if(current && current.$$route && current.$$route.css){
                            if(!Array.isArray(current.$$route.css)){
                                current.$$route.css = [current.$$route.css];
                            }
                            angular.forEach(current.$$route.css, function(sheet){
                                scope.routeStyles[sheet] = undefined;
                            });
                        }
                        if(next && next.$$route && next.$$route.css){
                            if(!Array.isArray(next.$$route.css)){
                                next.$$route.css = [next.$$route.css];
                            }
                            angular.forEach(next.$$route.css, function(sheet){
                                if (angular.isFunction(sheet)){
                                    sheet = sheet(next.params);
                                }
                                scope.routeStyles[sheet] = sheet;
                            });
                        }
                    });
                }
            };
        }
    ]);

})();

/**
 * Created by Zack Boman on 1/31/14.
 * http://www.zackboman.com or tennisgent@gmail.com
 */

'use strict';

(function(){

    var mod = angular.module('routeStyles', ['ngRoute']);

    mod.directive('head', ['$rootScope','$compile',
        function($rootScope, $compile){
            return {
                restrict: 'E',
                link: function(scope, elem){
                    var html = '<link rel="stylesheet" ng-repeat="(routeCtrl, cssUrl) in routeStyles" ng-href="{{cssUrl}}" >';
                    elem.append($compile(html)(scope));
                    scope.routeStyles = {};
                    $rootScope.$on('$routeChangeStart', function (e, next) {
                        if(next && next.$$route && next.$$route.css){
                            if(!Array.isArray(next.$$route.css)){
                                next.$$route.css = [next.$$route.css];
                            }
                            angular.forEach(next.$$route.css, function(sheet){
                                scope.routeStyles[sheet] = sheet;
                            });
                        }
                    });
                    $rootScope.$on('$routeChangeSuccess', function(e, current, previous) {
                        if (previous && previous.$$route && previous.$$route.css) {
                            if (!Array.isArray(previous.$$route.css)) {
                                previous.$$route.css = [previous.$$route.css];
                            }
                            angular.forEach(previous.$$route.css, function (sheet) {
                                scope.routeStyles[sheet] = undefined;
                            });
                        }
                    });
                }
            };
        }
    ]);

})();

Switch to use two separate events instead of the single event to prevent the CSS transition problem.


const fs = require('fs');
const glob = require('glob');
const yaml = require('js-yaml');
const extendify = require('extendify');

glob("templates/*.yml", function (er, files) {
  const contents = files.map(f => {
    return yaml.safeLoad(fs.readFileSync(f, 'utf8'));
  });
  const extend = extendify({
    inPlace: false,
    isDeep: true
  });
  const merged = contents.reduce(extend);
  console.log("Generating build/aws-stack.json");
  fs.existsSync("build") || fs.mkdirSync("build");
  fs.writeFileSync("build/aws-stack.yaml", yaml.safeDump(merged));
  fs.writeFileSync("build/aws-stack.json", JSON.stringify(merged, null, 2));
});

const fs = require('fs');
const glob = require('glob');
const yaml = require('js-yaml');
const extendify = require('extendify');
const proc = require('child_process');

const sortOrder = [
  'AWSTemplateFormatVersion',
  'Description',
  'Parameters',
  'Mappings',
  'Conditions',
  'Resources',
  'Metadata',
  'Outputs',
]

glob("templates/*.yml", function (er, files) {
  const contents = files.map(f => {
    return yaml.safeLoad(fs.readFileSync(f, 'utf8'));
  });
  const extend = extendify({
    inPlace: false,
    isDeep: true
  });

  var merged = contents.reduce(extend);
  var sorted = {};

  // sort by a specific key order
  var keys = Object.keys(merged).sort(function(a,b){
    return sortOrder.indexOf(a) - sortOrder.indexOf(b);
  });

  for(var index in keys) {
    var key = keys[index];
    sorted[key] = merged[key];
  }

  const version = proc.execSync('git describe --tags --candidates=1');

  // set a description
  sorted.Description = "Buildkite stack " + String(version).trim();

  fs.existsSync("build") || fs.mkdirSync("build");
  console.log("Generating build/aws-stack.yml");
  fs.writeFileSync("build/aws-stack.yml", yaml.safeDump(sorted));

  console.log("Generating build/aws-stack.json");
  fs.writeFileSync("build/aws-stack.json", JSON.stringify(sorted, null, 2));
});

Sort output in build files


var Server = require("./server.js").Server;
var Config = require("./config.js").Config;

exports.nails = appRoot => {
  var cfg = new Config(appRoot + "/config.json");
  cfg.set('appRoot', appRoot);
  new Server(cfg).run();
};

var path = require("path");
var Server = require("./server.js").Server;
var Config = require("./config.js").Config;

exports.nails = (appRoot = path.dirname(require.main.filename)) => {
  var cfg = new Config(appRoot + "/config.json");
  cfg.set('appRoot', appRoot);
  new Server(cfg).run();
};

Make the appRoot param optional


import EventEmitter from 'eventemitter3';

class FlashesStore extends EventEmitter {
  constructor() {
    super(...arguments);
    this.ERROR = "error";
  }

  flash(type, message) {
    // See if the message is actually a GraphQL exception
    if(message.source && message.message) {
      if(message.source.errors[0] && message.source.errors[0].message) {
        message = message.source.errors[0].message;
      } else {
        message = "An unknown error occured";
      }
    }

    this.emit("flash", { id: (new Date()).valueOf(), type: type, message: message });
  }
}

export default new FlashesStore()

import EventEmitter from 'eventemitter3';

class FlashesStore extends EventEmitter {
  constructor() {
    super(...arguments);
    this.ERROR = "error";
  }

  flash(type, message) {
    // See if the message is actually a GraphQL exception
    if(message.source && message.message) {
      if(message.source.errors[0] && message.source.errors[0].message) {
        if(message.source.type == "unknown_error") {
          message = "Sorry, there’s been an unexpected error and our engineers have been notified";
        } else {
          message = message.source.errors[0].message;
        }
      } else {
        message = "An unknown error occured";
      }
    }

    this.emit("flash", { id: (new Date()).valueOf(), type: type, message: message });
  }
}

export default new FlashesStore()

Use a different message if GraphQL errors and we show a flash


/** @license
 * Copyright 2016 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

goog.provide('shaka.util.MediaReadyState');

shaka.util.MediaReadyState = class {
  /*
  * @param {!HTMLMediaElement} mediaElement
  * @param {HTMLMediaElement.ReadyState} readyState
  * @param {!function()} callback
  */
  static waitForReadyState(mediaElement, readyState, eventManager, callback) {
    if (readyState == HTMLMediaElement.HAVE_NOTHING ||
      mediaElement.readyState >= readyState) {
      callback();
    } else {
      const MediaReadyState = shaka.util.MediaReadyState;
      const eventName =
          MediaReadyState.READY_STATES_TO_EVENT_NAMES_.get(readyState);
      eventManager.listenOnce(mediaElement, eventName, callback);
    }
  }
};

/**
 * @const {!Map.<number, string>}
 * @private
 */
shaka.util.MediaReadyState.READY_STATES_TO_EVENT_NAMES_ = new Map([
  [HTMLMediaElement.HAVE_METADATA, 'loadedmetadata'],
  [HTMLMediaElement.HAVE_CURRENT_DATA, 'loadeddata'],
  [HTMLMediaElement.HAVE_FUTURE_DATA, 'canplay'],
  [HTMLMediaElement.HAVE_ENOUGH_DATA, 'canplaythrough'],
]);

/** @license
 * Copyright 2016 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

goog.provide('shaka.util.MediaReadyState');

goog.require('shaka.util.EventManager');

shaka.util.MediaReadyState = class {
  /**
   * @param {!HTMLMediaElement} mediaElement
   * @param {number} readyState
   * @param {shaka.util.EventManager} eventManager
   * @param {function()} callback
   */
  static waitForReadyState(mediaElement, readyState, eventManager, callback) {
    if (readyState == HTMLMediaElement.HAVE_NOTHING ||
      mediaElement.readyState >= readyState) {
      callback();
    } else {
      const MediaReadyState = shaka.util.MediaReadyState;
      const eventName =
          MediaReadyState.READY_STATES_TO_EVENT_NAMES_.get(readyState);
      eventManager.listenOnce(mediaElement, eventName, callback);
    }
  }
};

/**
 * @const {!Map.<number, string>}
 * @private
 */
shaka.util.MediaReadyState.READY_STATES_TO_EVENT_NAMES_ = new Map([
  [HTMLMediaElement.HAVE_METADATA, 'loadedmetadata'],
  [HTMLMediaElement.HAVE_CURRENT_DATA, 'loadeddata'],
  [HTMLMediaElement.HAVE_FUTURE_DATA, 'canplay'],
  [HTMLMediaElement.HAVE_ENOUGH_DATA, 'canplaythrough'],
]);

Fix annotations in MediaReadyState util


/**
 * Gets whether the AMP story's featured image has the right minimum dimensions.
 *
 * The featured image populates teh AMP story poster image.
 * The 3 minimum dimensions for that are 696px x 928px, 928px x 696px, or 928px x 928px.
 *
 * @param {Object} media A media object with width and height values.
 * @return {boolean} Whether the media has the minimum dimensions.
 */
export default ( media ) => {
	const largeDimension = 928;
	const smallDimension = 696;
	return (
		( media.width && media.height )	&&
		( media.width >= smallDimension && media.height >= smallDimension )	&&
		(
			( media.width >= largeDimension && media.height >= largeDimension ) ||
			( media.width < largeDimension && media.height >= largeDimension ) ||
			( media.height < largeDimension && media.width >= largeDimension )
		)
	);
};

Add a semicolon to address ESLint issue


/*!
 * Copyright 2012 Sakai Foundation (SF) Licensed under the
 * Educational Community License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may
 * obtain a copy of the License at
 *
 *     http://www.osedu.org/licenses/ECL-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an "AS IS"
 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

define(['jquery'], function (jQuery) {
    (function() {

        /**
         * Catch the keypress event for `enter` when an editable field has focus
         */
        $(document).on('focus', '.jeditable-field', function(ev) {
            $(this).keypress(function(ev) {
                if (ev.which == 13){
                    $(this).trigger('click.editable');
                }
            });
        });

    })();
});

/*!
 * Copyright 2012 Sakai Foundation (SF) Licensed under the
 * Educational Community License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may
 * obtain a copy of the License at
 *
 *     http://www.osedu.org/licenses/ECL-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an "AS IS"
 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

define(['jquery'], function (jQuery) {
    (function() {

        /**
         * Catch the keypress event for `enter` and `space` when an editable field has focus
         */
        $(document).on('focus', '.jeditable-field', function(ev) {
            $(this).keypress(function(ev) {
                if (ev.which == 13 || ev.which == 32){
                    ev.preventDefault();
                    $(this).trigger('click.editable');
                }
            });
        });

    })();
});

Make the spacebar usable for inline edit


var icalendar = require('icalendar');

exports.generateIcal = function(boards) {
    var ical = new icalendar.iCalendar();
    boards.each(function(board) {
        board.cards().each(function(card) {
            // no arm, no chocolate
            if (!card.get('badges').due) return;
            var event = new icalendar.VEvent();
            event.setSummary(card.get('name'));
            event.setDescription(card.get('description'));
            event.setDate(card.get('badges').due);
            ical.addComponent(event);
        });
    });
    return ical;
}

var icalendar = require('icalendar');

exports.generateIcal = function(boards) {
    var ical = new icalendar.iCalendar();
    boards.each(function(board) {
        board.cards().each(function(card) {
            // no arm, no chocolate
            if (!card.get('badges').due) return;
            var event = new icalendar.VEvent();
            event.setSummary(card.get('name'));
            event.setDescription(card.get('description'));
            event.setDate(card.get('badges').due);
            event.addProperty('ATTACH', card.get('url'));
            ical.addComponent(event);
        });
    });
    return ical;
}

Attach the URL to the event.


import BaseCommand from './BaseCommand';

/**
 * Simple command to ban a uaer by their ID.
 */
class IDBanCommand extends BaseCommand {
    constructor(bot) {
        super(bot);
    }

    /**
     * This event method we should listen for.
     *
     * @type {string}
     */
    method = 'message';

    /**
     * The pattern to match against. If the message matches this pattern then we will respond to it with the action method.
     *
     * @type {RegExp}
     */
    pattern = /^!idban/;

    /**
     * The function that should be called when the event is fired.
     *
     * @param {Message} message
     */
    action(action, message) {
        if (this.hasBypassRole(message)) {
            const ids = message.cleanContent.substr(7);

            ids.split(' ').forEach((id) => {
                message.guild.ban(id, 1);

                message.reply(`User with ID of '${id}' has been banned.`);
            });
        }

        message.delete();
    }
}

export default IDBanCommand;

import BaseCommand from './BaseCommand';

/**
 * Simple command to ban a uaer by their ID.
 */
class IDBanCommand extends BaseCommand {
    constructor(bot) {
        super(bot);
    }

    /**
     * This event method we should listen for.
     *
     * @type {string}
     */
    method = 'message';

    /**
     * The pattern to match against. If the message matches this pattern then we will respond to it with the action method.
     *
     * @type {RegExp}
     */
    pattern = /^!idban/;

    /**
     * The function that should be called when the event is fired.
     *
     * @param {Message} message
     */
    action(action, message) {
        message.delete();

        if (this.hasBypassRole(message)) {
            const ids = message.cleanContent.substr(7);

            if (!ids.length) {
                return;
            }

            if (ids.indexOf(' ') === -1) {
                return;
            }

            const splitIDs = ids.split(' ');

            splitIDs.forEach((id) => {
                message.guild.ban(id, 1);

                message.channel.send(`User with ID of '${id}' has been banned.`);
            });
        }
    }
}

export default IDBanCommand;

Upgrade IDBan command to do some more checking


export const FETCH_QUEUE = 'FETCH_QUEUE'
export const FETCH_QUEUE_DONE = 'FETCH_QUEUE_DONE'
export const FETCH_ACCOUNTS = 'FETCH_ACCOUNTS'
export const FETCH_TAGS = 'FETCH_TAGS'
export const ADD_TAG = 'ADD_TAG'
export const REMOVE_TAG = 'REMOVE_TAG'

export function fetchQueue() {
  return {
    type: FETCH_QUEUE
  }
}

export function fetchQueueDone(queue) {
  return {
    type: FETCH_QUEUE_DONE,
    queue
  }
}

export function fetchAccounts() {
  return {
    type: FETCH_ACCOUNTS,
  }
}

export function fetchTags() {
  return {
    type: FETCH_TAGS
  }
}

export function addTag(account, tag) {
  return {
    type: ADD_TAG,
    account: account,
    tag: tag
  }
}

export function removeTag(account, tag) {
  return {
    type: REMOVE_TAG,
    account: account,
    tag: tag
  }
}

export const FETCH_QUEUE = 'FETCH_QUEUE'
export const FETCH_QUEUE_DONE = 'FETCH_QUEUE_DONE'
export const FETCH_ACCOUNTS = 'FETCH_ACCOUNTS'
export const FETCH_TAGS = 'FETCH_TAGS'
export const ADD_TAG = 'ADD_TAG'
export const REMOVE_TAG = 'REMOVE_TAG'
export const SET_ACTIVE_DONATION = 'SET_ACTIVE_DONATION'

export function fetchQueue() {
  return {
    type: FETCH_QUEUE
  }
}

export function fetchQueueDone(queue) {
  return {
    type: FETCH_QUEUE_DONE,
    queue
  }
}

export function fetchAccounts() {
  return {
    type: FETCH_ACCOUNTS
  }
}

export function fetchTags() {
  return {
    type: FETCH_TAGS
  }
}

export function addTag(account, tag) {
  return {
    type: ADD_TAG,
    account: account,
    tag: tag
  }
}

export function removeTag(account, tag) {
  return {
    type: REMOVE_TAG,
    account: account,
    tag: tag
  }
}

export function setActiveDonation() {
  return {
    type: SET_ACTIVE_DONATION
  }
}

Add action to set active donation


'use strict';

// Issue to improve: https://github.com/hashicorp/nomad/issues/7465
const proxyPath = '/v1';

module.exports = function(app, options) {
  // For options, see:
  // https://github.com/nodejitsu/node-http-proxy

  let proxyAddress = options.proxy;

  let server = options.httpServer;
  let proxy = require('http-proxy').createProxyServer({
    target: proxyAddress,
    ws: true,
    changeOrigin: true,
  });

  proxy.on('error', function(err, req) {
    // eslint-disable-next-line
    console.error(err, req.url);
  });

  app.use(proxyPath, function(req, res) {
    // include root path in proxied request
    req.url = proxyPath + req.url;
    proxy.web(req, res, { target: proxyAddress });
  });

  server.on('upgrade', function(req, socket, head) {
    if (req.url.startsWith('/v1/client/allocation') && req.url.includes('exec?')) {
      req.headers.origin = proxyAddress;
      proxy.ws(req, socket, head, { target: proxyAddress });
    }
  });
};

'use strict';

// Issue to improve: https://github.com/hashicorp/nomad/issues/7465
const proxyPath = '/v1';

module.exports = function(app, options) {
  // For options, see:
  // https://github.com/nodejitsu/node-http-proxy

  // This is probably not safe to do, but it works for now.
  let cacheKey = `${options.project.configPath()}|${options.environment}`;
  let config = options.project.configCache.get(cacheKey);

  // Disable the proxy completely when Mirage is enabled. No requests to the API
  // will be being made, and having the proxy attempt to connect to Nomad when it
  // is not running can result in socket max connections that block the livereload
  // server from reloading.
  if (config['ember-cli-mirage'].enabled !== false) {
    options.ui.writeInfoLine('Mirage is enabled. Not starting proxy');
    delete options.proxy;
    return;
  }

  let proxyAddress = options.proxy;

  let server = options.httpServer;
  let proxy = require('http-proxy').createProxyServer({
    target: proxyAddress,
    ws: true,
    changeOrigin: true,
  });

  proxy.on('error', function(err, req) {
    // eslint-disable-next-line
    console.error(err, req.url);
  });

  app.use(proxyPath, function(req, res) {
    // include root path in proxied request
    req.url = proxyPath + req.url;
    proxy.web(req, res, { target: proxyAddress });
  });

  server.on('upgrade', function(req, socket, head) {
    if (req.url.startsWith('/v1/client/allocation') && req.url.includes('exec?')) {
      req.headers.origin = proxyAddress;
      proxy.ws(req, socket, head, { target: proxyAddress });
    }
  });
};

Disable the proxy when Mirage is enabled


const fetch = require("node-fetch");
const qs = require("qs");
const { load } = require("js-yaml");

const { getInstallationToken } = require("./installation-token");

module.exports.GitHub = class GitHub {
  constructor (installationId) {
    this.installationId = installationId;
  }

  get (urlSegment) {
    return this.fetch("GET", urlSegment);
  }

  post (urlSegment, body) {
    return this.fetch("POST", urlSegment, body);
  }

  async fetch (method, urlSegment, body) {
    const installationToken = await getInstallationToken(this.installationId);

    const headers = {
      "User-Agent": "divmain/semver-as-a-service",
      "Accept": "application/vnd.github.machine-man-preview+json",
      "Authorization": `token ${installationToken}`
    };
    const opts = {
      method,
      headers
    };

    if (method === "POST") {
      headers["Content-Type"] = "application/json";
      opts.body = body;
    }

    return fetch(`https://api.github.com${urlSegment}`, opts);
  }
}

const fetch = require("node-fetch");
const qs = require("qs");
const { load } = require("js-yaml");

const { getInstallationToken } = require("./installation-token");

module.exports.GitHub = class GitHub {
  constructor (installationId) {
    this.installationId = installationId;
  }

  get (urlSegment) {
    return this.fetch("GET", urlSegment);
  }

  post (urlSegment, body) {
    return this.fetch("POST", urlSegment, body);
  }

  async fetch (method, urlSegment, body) {
    const installationToken = await getInstallationToken(this.installationId);

    const headers = {
      "User-Agent": "divmain/semver-as-a-service",
      "Accept": "application/vnd.github.machine-man-preview+json",
      "Authorization": `token ${installationToken}`
    };
    const opts = {
      method,
      headers
    };

    if (method === "POST") {
      headers["Content-Type"] = "application/json";
      opts.body = body;
    }

    return fetch(`https://api.github.com${urlSegment}`, opts);
  }

  getConfig (repoPath) {
    return this.get(`/repos/${repoPath}/contents/.maintainerd`)
      .then(response => response.json())
      .then(json => Buffer.from(json.content, "base64").toString())
      .then(yamlString => load(yamlString, "utf8"));
  }
}

Add GH method for retrieving maintainerd config file.


/* eslint-disable global-require */

'use strict';

const allRules = {
  'no-unused-styles': require('./lib/rules/no-unused-styles'),
  'no-inline-styles': require('./lib/rules/no-inline-styles'),
  'no-color-literals': require('./lib/rules/no-color-literals'),
  'split-platform-components': require('./lib/rules/split-platform-components'),
};

function configureAsError(rules) {
  const result = {};
  for (const key in rules) {
    if (!rules.hasOwnProperty(key)) {
      continue;
    }
    result['react-native/' + key] = 2;
  }
  return result;
}

const allRulesConfig = configureAsError(allRules);

module.exports = {
  deprecatedRules: {},
  rules: allRules,
  rulesConfig: {
    'no-unused-styles': 0,
    'no-inline-styles': 0,
    'no-color-literals': 0,
    'split-platform-components': 0,
  },
  configs: {
    all: {
      plugin: [
        'react-native',
      ],
      parserOptions: {
        ecmaFeatures: {
          jsx: true,
        },
      },
      rules: allRulesConfig,
    },
  },
};

/* eslint-disable global-require */

'use strict';

const allRules = {
  'no-unused-styles': require('./lib/rules/no-unused-styles'),
  'no-inline-styles': require('./lib/rules/no-inline-styles'),
  'no-color-literals': require('./lib/rules/no-color-literals'),
  'split-platform-components': require('./lib/rules/split-platform-components'),
};

function configureAsError(rules) {
  const result = {};
  for (const key in rules) {
    if (!rules.hasOwnProperty(key)) {
      continue;
    }
    result['react-native/' + key] = 2;
  }
  return result;
}

const allRulesConfig = configureAsError(allRules);

module.exports = {
  deprecatedRules: {},
  rules: allRules,
  rulesConfig: {
    'no-unused-styles': 0,
    'no-inline-styles': 0,
    'no-color-literals': 0,
    'split-platform-components': 0,
  },
  configs: {
    all: {
      plugins: [
        'react-native',
      ],
      parserOptions: {
        ecmaFeatures: {
          jsx: true,
        },
      },
      rules: allRulesConfig,
    },
  },
};

Fix all config for Eslint 4


const paths = require('./paths');
const fs = require('fs-extra');
const glob = require('glob');

/**
 * Build the breakpoint
 *
 * @param {*} breakpoint
 * @param {*} count
 */
function buildBreakpoint(breakpoint, count) {
	let html = `html { font-family: '${count}'; }`;

	return `@${breakpoint} { ${html} }\n`;
}

module.exports = breakpoints => {
	let count = 2,
		result = '';

	for (let breakpoint in breakpoints) {
		result += buildBreakpoint(breakpoint, count);
		count++;
	}

	fs.mkdirsSync(paths.temp);

	fs.writeFileSync(paths.temp + '/responsive.scss', result);
};

const paths = require('./paths');
const fs = require('fs-extra');
const glob = require('glob');

/**
 * Build the breakpoint
 *
 * @param {*} breakpoint
 * @param {*} count
 */
function buildBreakpoint(breakpoint, count) {
	let html = `html { font-family: '${count}'; }`;

	return `@${breakpoint} { ${html} }\n`;
}

module.exports = breakpoints => {
	let count = 2,
		result = '/* stylelint-disable */\n\n';

	for (let breakpoint in breakpoints) {
		result += buildBreakpoint(breakpoint, count);
		count++;
	}

	fs.mkdirsSync(paths.temp);

	fs.writeFileSync(paths.temp + '/responsive.scss', result);
};

Disable linting for generated file


Make sure the extractBody is set (not just body)


import React, { Component } from 'react'

//class-based component (ES6)
class SearchBar extends Component {
  render() { //method definition in ES6
    return <input onChange={event => console.log(event.target.value)} />;
  }

}

export default SearchBar;

import React, { Component } from 'react'

//class-based component (ES6)
class SearchBar extends Component {
  constructor(props) {
    super(props);

    this.state = { term: '' }
  }

  render() { //method definition in ES6
    return <input onChange={event => console.log(event.target.value)} />;
  }

}

export default SearchBar;

Initialize state in class-based component


module.exports = {
    js: {
        lintNatives: true,
        name: '<%= pkg.name %>',
        url: '<%= pkg.homepage %>',
        version: '<%= pkg.version %>',
        description: '<%= pkg.description %>',
        options: {
            nocode: true,
            paths: './src/',
            outdir: './build/documentation',
            themedir: './node_modules/yuidoc-theme-blue'
        }
    }
};

module.exports = {
    js: {
        lintNatives: true,
        name: '<%= pkg.name %> by <%= pkg.author.name %>',
        url: '<%= pkg.homepage %>',
        version: '<%= pkg.version %>',
        description: '<%= pkg.description %>',
        options: {
            nocode: true,
            paths: './src/',
            outdir: './build/documentation',
            themedir: './node_modules/yuidoc-theme-blue'
        }
    }
};

Add author information for SEO purposes


export default () => {
  return {
    test: /\.(png|jpg|gif|svg|eot|ttf|woff|woff2)$/i,
    loader: "file",
    query: { name: "assets/[name]-[hash].[ext]" }
  };
}

export default () => {
  return {
    test: /\.(png|jpg|gif|eot|ttf|woff|woff2)$/i,
    loader: "file",
    query: { name: "assets/[name]-[hash].[ext]" }
  };
}

Remove svg from file-loader test pattern


angular.module('shortly.shorten', [])

.controller('AccountsController', function ($scope, $window, $location, $http, Links) {

  // $scope.hasStripeConnectAccount = function() {

  //   var currentUser = sessionStorage.getItem('user');
  //   console.log('user',currentUser);

  // };

  $scope.authorize = function() {
    var currentUser = sessionStorage.getItem('user');

    return $http({
      method: 'POST',
      url: '/authorize',
      data: {
        username: currentUser,
      }
    })
    .then(function (resp) {
      console.log(resp);
      $window.location.href = resp.data;
    });
  };

  $scope.stripeCallback = function (code, result) {
    var currentUser = sessionStorage.getItem('user');

    if (result.error) {
        console.log('it failed! error: ' + result.error.message);
    } else {

      return $http({
        method: 'POST',
        url: '/stripe/debit-token',
        data: {
          username: currentUser,
          cardToken: result.id
        }
      })
      .then(function (resp) {
        console.log(resp);
      });

    }
  };
});

angular.module('shortly.shorten', [])

.controller('AccountsController', function ($scope, $window, $location, $http, Links) {

  /**
   * Handles Stripe 'Connect' button submit.
   * Gets Connect account creation redirect url from server and manually sets href.
   */
  $scope.authorize = function() {
    var currentUser = sessionStorage.getItem('user');

    return $http({
      method: 'POST',
      url: '/authorize',
      data: {
        username: currentUser,
      }
    })
    .then(function (resp) {
      console.log(resp);
      $window.location.href = resp.data;
    });
  };

  /**
   * On form submit, card info is sent to Stripe for processing.
   * Stripe returns a one time use token to stripeCallback, which passes it to server for customerId creation and saving.
   */
  $scope.stripeCallback = function (code, result) {
    var currentUser = sessionStorage.getItem('user');

    if (result.error) {
        console.log('it failed! error: ' + result.error.message);
    } else {

      return $http({
        method: 'POST',
        url: '/stripe/debit-token',
        data: {
          username: currentUser,
          cardToken: result.id
        }
      })
      .then(function (resp) {
        console.log(resp);
      });

    }
  };
});

Add comments for functions that handle user account info submission


"use strict";

var fs = require('fs');
var path = require('path');
var extend = require('extend');
var template = require('lodash/template');

var errorHandler = require('./errorHandler');

var _fileNumber = 1;
var _config = {
    onEachFile: function() {},
    onCompleted: function() {}
};

var fileTemplaterApi = {};

fileTemplaterApi.run = function() {
    _config.files.forEach(function(element, index, array) {
        templateFile(element);
    });
}

fileTemplaterApi.setConfig = function(config) {
    _config = extend(_config, config);
}

function templateFile(filePaths) {
    var compiled;
    var output;

    fs.readFile(filePaths.src, 'utf8', function(err, fileContents) {
        if (err) errorHandler(err);

        compiled = template(fileContents);
        output = compiled(_config.data);

        fs.writeFile(filePaths.dest, output, 'utf8', function(err) {
            if (err) errorHandler(err);

            _config.onEachFile(filePaths.dest);

            if(_fileNumber === _config.files.length) {
                _config.onCompleted();
            }

            _fileNumber++;

        });

    });
}

module.exports = fileTemplaterApi;

"use strict";

var fs = require('fs');
var path = require('path');
var extend = require('extend');
var template = require('lodash/template');

var errorHandler = require('./errorHandler');

var _fileNumber = 1;
var _config = {
    onEachFile: function() {},
    onCompleted: function() {}
};

var fileTemplaterApi = {};

/**
 * Run file templating
 */
fileTemplaterApi.run = function() {
    _config.files.forEach(function(element, index, array) {
        templateFile(element);
    });
}

/**
 * Sets internal config object
 */
fileTemplaterApi.setConfig = function(config) {
    _config = extend(_config, config);
}

/**
 * Template a single file.
 * Get the contents of a file, template it and re-write to the same file
 * @param  {Object} filePaths File paths data, containing file src and dest
 */
function templateFile(filePaths) {
    var compiled;
    var output;

    fs.readFile(filePaths.src, 'utf8', function(err, fileContents) {
        if (err) errorHandler(err);

        compiled = template(fileContents);
        output = compiled(_config.data);

        fs.writeFile(filePaths.dest, output, 'utf8', function(err) {
            if (err) errorHandler(err);

            _config.onEachFile(filePaths.dest);

            if(_fileNumber === _config.files.length) {
                _config.onCompleted();
            }

            _fileNumber++;

        });

    });
}

module.exports = fileTemplaterApi;

Add doc comments for file templater


import ActiveModelAdapter from 'active-model-adapter';
import ActiveModelSerializer from 'active-model-adapter/active-model-serializer';

export default {
  name: 'active-model-adapter',
  initialize: function(applicationOrRegistry) {
    var registry, container;
    if (applicationOrRegistry.registry && applicationOrRegistry.container) {
      // initializeStoreService was registered with an
      // instanceInitializer. The first argument is the application
      // instance.
      registry = applicationOrRegistry.registry;
      container = applicationOrRegistry.container;
    } else {
      // initializeStoreService was called by an initializer instead of
      // an instanceInitializer. The first argument is a registy. This
      // case allows ED to support Ember pre 1.12
      registry = applicationOrRegistry;
      if (registry.container) { // Support Ember 1.10 - 1.11
        container = registry.container();
      } else { // Support Ember 1.9
        container = registry;
      }
    }

    registry.register('adapter:-active-model', ActiveModelAdapter);
    registry.register('serializer:-active-model', ActiveModelSerializer.extend({ isNewSerializerAPI: true }));
  }
};

import ActiveModelAdapter from 'active-model-adapter';
import ActiveModelSerializer from 'active-model-adapter/active-model-serializer';

export default {
  name: 'active-model-adapter',
  initialize: function(applicationOrRegistry) {
    var registry;
    if (applicationOrRegistry.registry) {
      // initializeStoreService was registered with an
      // instanceInitializer. The first argument is the application
      // instance.
      registry = applicationOrRegistry.registry;
    } else {
      // initializeStoreService was called by an initializer instead of
      // an instanceInitializer. The first argument is a registy. This
      // case allows ED to support Ember pre 1.12
      registry = applicationOrRegistry;
    }

    registry.register('adapter:-active-model', ActiveModelAdapter);
    registry.register('serializer:-active-model', ActiveModelSerializer.extend({ isNewSerializerAPI: true }));
  }
};

Remove unused container in instance initializer


/**
 * Sample React Native Calendar Strip
 * https://github.com/BugiDev/react-native-calendar-strip
 * @flow
 */

import React, { Component } from 'react';
import CalendarStrip from "react-native-calendar-strip";

export default class App extends Component<{}> {
  render() {
    return (
      <CalendarStrip
                    calendarAnimation={{type: 'sequence', duration: 30}}
                    daySelectionAnimation={{type: 'background', duration: 300, highlightColor: '#9265DC'}}
                    style={{height:100, paddingTop: 20, paddingBottom: 10}}
                    calendarHeaderStyle={{color: 'white'}}
                    calendarColor={'#7743CE'}
                    dateNumberStyle={{color: 'white'}}
                    dateNameStyle={{color: 'white'}}
                    iconLeft={require('./img/left-arrow.png')}
                    iconRight={require('./img/right-arrow.png')}
                    iconContainer={{flex: 0.1}}
                />
    );
  }
}

/**
 * Sample React Native Calendar Strip
 * https://github.com/BugiDev/react-native-calendar-strip
 * @flow
 */

import React, { Component } from 'react';
import CalendarStrip from "react-native-calendar-strip";
import moment from 'moment';

export default class App extends Component<{}> {
  render() {
    let customDatesStyles = [];
    let markedDates = [];
    let startDate = moment();
    for (let i=0; i<6; i++) {
      let _date = startDate.clone().add(i, 'days');
      customDatesStyles.push({
        startDate: _date, // Single date since no endDate provided
        dateNameStyle: {color: 'blue'},
        dateNumberStyle: {color: 'purple'},
        // Random color...
        dateContainerStyle: { backgroundColor: `#${(`#00000${(Math.random() * (1 << 24) | 0).toString(16)}`).slice(-6)}` },
      });
      markedDates.push({
        date: _date,
        dots: [
          {
            key: i,
            color: 'red',
            selectedDotColor: 'yellow',
          },
        ],
      });
    }

    return (
      <CalendarStrip
                    calendarAnimation={{type: 'sequence', duration: 30}}
                    daySelectionAnimation={{type: 'background', duration: 300, highlightColor: '#9265DC'}}
                    style={{height:200, paddingTop: 20, paddingBottom: 10}}
                    calendarHeaderStyle={{color: 'white'}}
                    calendarColor={'#7743CE'}
                    dateNumberStyle={{color: 'white'}}
                    dateNameStyle={{color: 'white'}}
                    iconLeft={require('./img/left-arrow.png')}
                    iconRight={require('./img/right-arrow.png')}
                    iconContainer={{flex: 0.1}}
                    customDatesStyles={customDatesStyles}
                    markedDates={markedDates}
                />
    );
  }
}

Update example with customDatesStyles and markedDates.


module.exports = {
  "replace": {
    "_i18n/template.en-US.json": {
      "author": /docprofsky/g,
      "info-name": "info.name",
      "info-use": "info.use",
      "info-detail": "info.detail"
    },

    "package.json": {
      "author": /docprofsky/g,
      "name": /template(?!_)/g
    },

    "template.js": {
      "name": /template/g
    },

    "template.ps.js": {
      "name": /template/g
    },

    "template.html": {
      "name": /template/g
    },

    "template.css": {
      "name": /template/g
    }
  },
  "files": {
    "rename": [
      "_i18n/template.en-US.json",
      "template.css",
      "template.html",
      "template.js",
      "template.ps.js"
    ],
    "delete": [
      ".git"
    ]
  }
}

module.exports = {
  "replace": {
    "_i18n/template.en-US.json": {
      "author": /docprofsky/g,
      "info-name": "info.name",
      "info-use": "info.use",
      "info-detail": "info.detail"
    },

    "package.json": {
      "author": /docprofsky/g,
      "name": /template(?!_)/g
    },

    "template.js": {
      "name": /template/g
    },

    "template.ps.js": {
      "name": /template/g
    },

    "template.html": {
      "name": /template/g
    },

    "template.css": {
      "name": /template/g
    }
  },
  "files": {
    "rename": [
      "_i18n/template.en-US.json",
      "template.css",
      "template.html",
      "template.js",
      "template.ps.js"
    ],
    "delete": [
      ".git",
      "replace-values.js"
    ]
  }
}

Remove template file when done configuring


var gulp        = require('gulp')
  , gutil       = require('gulp-util')
  , help        = require('gulp-help')
  , path        = require('path')
  , packageJson = require(path.resolve(__dirname, 'package.json'))
  , noop        = function(){}

help(gulp, { aliases: ['h'] })

gulp.task('version', 'Prints the version number.', [], function() {
  console.log(packageJson.version)
}, {
  aliases: ['v']
})

gulp.task('init', 'Initializes the project.', function() {

})

gulp.task('migrate', 'Run pending migrations.', function() {

})

gulp.task('migrate:undo', 'Undo the last migration.', function() {

})

gulp.task('migration:create', 'Creates a new migration.', function() {

})

var gulp        = require('gulp')
  , path        = require('path')
  , fs          = require('fs')
  , helpers     = require(path.resolve(__dirname, 'lib', 'helpers'))

require('gulp-help')(gulp, { aliases: ['h'] })

fs
  .readdirSync(path.resolve(__dirname, 'lib', 'tasks'))
  .filter(function(file) { return file.indexOf('.') !== 0 })
  .map(function(file) {
    return require(path.resolve(__dirname, 'lib', 'tasks', file))
  })
  .forEach(function(tasks) {
    Object.keys(tasks).forEach(function(taskName) {
      helpers.addTask(gulp, taskName, tasks[taskName])
      helpers.addHelp(gulp, taskName, tasks[taskName])
    })
  })

Load the tasks from lib/tasks


"use strict";

var _ = require("lodash"); // devDependency
var base = require("./webpack.config.dev");

// Clone our own module object.
var mod = _.cloneDeep(base.module);
var firstLoader = mod.loaders[0];

// Update loaders array. First loader needs react-hot-loader.
firstLoader.loaders = [require.resolve("react-hot-loader")]
  .concat(firstLoader.loader ? [firstLoader.loader] : [])
  .concat(firstLoader.loaders || []);

// Remove single loader if any.
firstLoader.loader = null;

module.exports = _.merge({}, _.omit(base, "entry", "module"), {
  entry: {
    app: [
      require.resolve("webpack/hot/dev-server"),
      "./demo/app.jsx"
    ]
  },

  module: mod
});

"use strict";

var _ = require("lodash"); // devDependency
var base = require("./webpack.config.dev");

// Clone our own module object.
var mod = _.cloneDeep(base.module);
var firstLoader = mod.loaders[0];

// Update loaders array. First loader needs react-hot-loader.
firstLoader.loaders = [require.resolve("react-hot-loader")]
  .concat(firstLoader.loader ? [firstLoader.loader] : [])
  .concat(firstLoader.loaders || []);

// Remove single loader if any.
firstLoader.loader = null;

module.exports = _.merge({}, _.omit(base, "entry", "module"), {
  entry: {
    app: [
      require.resolve("webpack/hot/only-dev-server"),
      "./demo/app.jsx"
    ]
  },

  module: mod
});

Use only-dev-server to not reload on syntax errors


/**
 * Checks if 'value' is a number.
 * Note: NaN is considered a number.
 * @param   {*}       value The value to check.
 * @returns {Boolean} Returns true if 'value' is a number, else false.
 */
function isNumber(value) {
    'use strict';

    return value &&
        Object.prototype.toString.call(value) === '[object Number]' &&
        typeof value === 'number' ||
        false;
}

/**
 * Checks if 'value' is a number.
 * Note: NaN is considered a number.
 * @param {*} value The value to check.
 * @return {Boolean} Returns true if 'value' is a number, else false.
 */
function isNumber(value) {
    'use strict';

    return value != null && // NOTE: Using non strict equality to check against null and undefined.
        Object.prototype.toString.call(value) === '[object Number]' &&
        typeof value === 'number' ||
        false;
}

Fix to return true for NaN


import m from 'mithril';
import attributes from './attributes';

export let Card = {
	view(ctrl, args, ...children) {
		args = args || {};
		let attr = attributes(args);
		let {shadow} = args;

		attr.class.push('mdl-card');
		if(shadow) attr.class.push(`mdl-shadow--${shadow}dp`);

		return
{children}
;
	}
};

export let CardTitle = {
	view(ctrl, args, ...children) {
		args = args || {};
		let attr = attributes(args);
		let {expand, size} = args;

		attr.class.push('mdl-card__title');
		if(expand) attr.class.push('mdl-card--expand');

		size = size || 2;

		return

{children}

;
	}
};

export let CardSupportingText = {
	view(ctrl, args, ...children) {
		args = args || {};
		let attr = attributes(args);
		let {border, expand} = attr;

		attr.class.push('mdl-card__title-text');
		if(border) attr.class.push('mdl-shadow--border');
		if(expand) attr.class.push('mdl-card--expand');

		return
{children}
;
	}
};

import m from 'mithril';
import attributes from './attributes';

export let Card = {
	view(ctrl, args, ...children) {
		args = args || {};
		let attr = attributes(args);
		let {shadow} = args;

		attr.class.push('mdl-card');
		if(shadow) attr.class.push(`mdl-shadow--${shadow}dp`);

		return
{children}
;
	}
};

export let CardTitle = {
	view(ctrl, args, ...children) {
		args = args || {};
		let attr = attributes(args);
		let {expand, size} = args;

		attr.class.push('mdl-card__title');
		if(expand) attr.class.push('mdl-card--expand');

		size = size || 2;

		return

{children}

;
	}
};

export let CardSupportingText = {
	view(ctrl, args, ...children) {
		args = args || {};
		let attr = attributes(args);
		let {border, expand} = attr;

		attr.class.push('mdl-card__title-text');
		if(border) attr.class.push('mdl-card--border');
		if(expand) attr.class.push('mdl-card--expand');

		return
{children}
;
	}
};

Fix wrong class name in CardSupportingText


var _ = require('lodash');
var stylelint = require('stylelint');
var STYLELINT_CONFIG = require('./config/stylelint');
var glob = require('glob');

var ruleUtils = require('./rule_utils');

var customRules = {};

var runLinter = function(contents, file, context) {
	var customRules = context.customRules || {};

	_.merge(stylelint.rules, customRules);

	var config = context.lintConfig;

	var configs = [{}, STYLELINT_CONFIG];

	if (_.isObject(config)) {
		configs.push(config);
	}

	config = _.merge.apply(_, configs);

	return stylelint.lint(
		{
			code: contents,
			codeFileName: file,
			config: config,
			formatter: 'json',
			syntax: 'scss'
		}
	);
};

var globOptions = {
	cwd: __dirname
};

module.exports = function(contents, file, context) {
	context.customRules = customRules;

	glob.sync(
		'./lint_css_rules/*.js',
		globOptions
	).forEach(
		function(item, index) {
			var id = ruleUtils.getRuleId(item);

			customRules[id] = require(item);
		}
	);

	return runLinter(contents, file, context);
};

module.exports.stylelint = stylelint;
module.exports.linter = stylelint.linter;
module.exports.runLinter = runLinter;

var _ = require('lodash');
var path = require('path');
var stylelint = require('stylelint');
var STYLELINT_CONFIG = require('./config/stylelint');
var glob = require('glob');

var ruleUtils = require('./rule_utils');

var customRules = {};

var runLinter = function(contents, file, context) {
	var customRules = context.customRules || {};

	_.merge(stylelint.rules, customRules);

	var config = context.lintConfig;

	var configs = [{}, STYLELINT_CONFIG];

	if (_.isObject(config)) {
		configs.push(config);
	}

	config = _.merge.apply(_, configs);

	return stylelint.lint(
		{
			code: contents,
			codeFileName: file,
			config: config,
			configBasedir: path.resolve(__dirname, '..'),
			formatter: 'json',
			syntax: 'scss'
		}
	);
};

var globOptions = {
	cwd: __dirname
};

module.exports = function(contents, file, context) {
	context.customRules = customRules;

	glob.sync(
		'./lint_css_rules/*.js',
		globOptions
	).forEach(
		function(item, index) {
			var id = ruleUtils.getRuleId(item);

			customRules[id] = require(item);
		}
	);

	return runLinter(contents, file, context);
};

module.exports.stylelint = stylelint;
module.exports.linter = stylelint.linter;
module.exports.runLinter = runLinter;

Add configBasedir to properly look up the plugins


var gulp = require('gulp'),
    sass = require('gulp-sass'),
    rename = require('gulp-rename'),
    minifyCss = require('gulp-minify-css'),
    autoprefixer = require('gulp-autoprefixer'),
    browserSync = require('browser-sync').create();

gulp.task('dependencies', function() {
  gulp.src('bower_components/normalize-css/normalize.css')
  .pipe(rename('_normalize.scss'))
  .pipe(gulp.dest('scss/libs'));
});

gulp.task('sass', function() {
    gulp.src('scss/style.scss')
    .pipe(sass().on('error', sass.logError))
    .pipe(autoprefixer({
        browsers: ['last 20 versions'],
        cascade: false
    }))
    .pipe(gulp.dest('./css/'))
    .pipe(minifyCss())
    .pipe(rename('style.min.css'))
    .pipe(gulp.dest('./css/'));
});

gulp.task('dist', function() {
  gulp.src(['index.html'])
  .pipe(gulp.dest('./'));
});

gulp.task('watch', function() {
    browserSync.init({
        server: "./"
    });
    gulp.watch('scss/**/*.scss', ['sass']).on('change', browserSync.reload);
    gulp.watch('index.html', ['dist']).on('change', browserSync.reload);
});

gulp.task('default', ['clean', 'dependencies', 'sass'], function() {
  gulp.start('dist');
});

var gulp = require('gulp'),
    sass = require('gulp-sass'),
    rename = require('gulp-rename'),
    minifyCss = require('gulp-minify-css'),
    autoprefixer = require('gulp-autoprefixer'),
    browserSync = require('browser-sync').create();

gulp.task('dependencies', function() {
  gulp.src('bower_components/normalize-css/normalize.css')
  .pipe(rename('_normalize.scss'))
  .pipe(gulp.dest('scss/libs'));
});

gulp.task('sass', function() {
    gulp.src('scss/style.scss')
    .pipe(sass().on('error', sass.logError))
    .pipe(autoprefixer({
        browsers: ['last 20 versions'],
        cascade: false
    }))
    .pipe(gulp.dest('./css/'))
    .pipe(minifyCss())
    .pipe(rename('style.min.css'))
    .pipe(gulp.dest('./css/'));
});

gulp.task('dist', function() {
  gulp.src(['index.html'])
  .pipe(gulp.dest('./'));
});

gulp.task('watch', function() {
    browserSync.init({
        server: "./"
    });
    gulp.watch('scss/**/*.scss', ['sass']).on('change', browserSync.reload);
    gulp.watch('index.html', ['dist']).on('change', browserSync.reload);
    gulp.watch('js/*.js', ['dist']).on('change', browserSync.reload);

});

gulp.task('default', ['clean', 'dependencies', 'sass'], function() {
  gulp.start('dist');
});

Update to gulp watch js folder


import {List} from 'immutable';
import {CompiledProject} from '../records';

const initialState = new List();

function trimRight(list, maxLength) {
  if (list.size <= maxLength) {
    return list;
  }

  return list.splice(0, list.size - maxLength);
}

export default function compiledProjects(stateIn, action) {
  let state = stateIn;
  if (state === undefined) {
    state = initialState;
  }

  switch (action.type) {
    case 'PROJECT_COMPILED':
      return trimRight(
        state.push(new CompiledProject({
          source: action.payload.source,
          title: action.payload.title,
          timestamp: action.meta.timestamp,
        })),
        2,
      );

    case 'USER_DONE_TYPING':
      return trimRight(state, 1);

    case 'VALIDATED_SOURCE':
      if (action.payload.errors.length) {
        return initialState;
      }
      return state;
  }

  return state;
}

import {List} from 'immutable';
import {CompiledProject} from '../records';

const initialState = new List();

function trimRight(list, maxLength) {
  if (list.size <= maxLength) {
    return list;
  }

  return list.splice(0, list.size - maxLength);
}

export default function compiledProjects(stateIn, action) {
  let state = stateIn;
  if (state === undefined) {
    state = initialState;
  }

  switch (action.type) {
    case 'PROJECT_CREATED':
      return initialState;

    case 'CHANGE_CURRENT_PROJECT':
      return initialState;

    case 'PROJECT_COMPILED':
      return trimRight(
        state.push(new CompiledProject({
          source: action.payload.source,
          title: action.payload.title,
          timestamp: action.meta.timestamp,
        })),
        2,
      );

    case 'USER_DONE_TYPING':
      return trimRight(state, 1);

    case 'VALIDATED_SOURCE':
      if (action.payload.errors.length) {
        return initialState;
      }
      return state;
  }

  return state;
}

Clear compiled projects when a project is created or switched to


var express = require('express');

var bodyParser = require('body-parser');

var app = express();

// host static client files on server
// app.use(express.static(__dirname + '/client'/* TODO: insert/change client folder path here */));

app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());

// configure server with routes
require('./config/routes')(app, express);

//start listening on given port
var port = 3000;
app.listen(port, function() {
  console.log('Listening on port', port);
});

module.exports = app;

var express = require('express');

var bodyParser = require('body-parser');

var app = express();

// host static client files on server
// app.use(express.static(__dirname + '/client'/* TODO: insert/change client folder path here */));

app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());

// configure server with routes
require('./config/routes')(app, express);

//start listening on given port
var port = process.env.PORT || 3000;
app.listen(port, function() {
  console.log('Listening on port', port);
});

module.exports = app;

Remove hardcoding of port number for Heroku


import {exec} from 'node-promise-es6/child-process';
import fs from 'node-promise-es6/fs';

async function run() {
  const {linkDependencies = {}} = await fs.readJson('package.json');
  for (const dependencyName of Object.keys(linkDependencies)) {
    const dependencyPath = linkDependencies[dependencyName];

    const {stdout: diff} = await exec(
      'git diff-index HEAD', {cwd: dependencyPath});
    if (diff.trim().indexOf('package.json') === -1) { continue; }

    await exec(
      [
        'git commit',
        `--author 'Vinson Chuong <vinsonchuong@gmail.com>'`,
        `-m 'Automatically update dependencies'`,
        'package.json'
      ].join(' '),
      {cwd: dependencyPath}
    );
    await exec(
      [
        'git push',
        `https://${process.env.GITHUB_TOKEN}@github.com/vinsonchuong/${dependencyName}`,
        'master'
      ].join(' '),
      {cwd: dependencyPath}
    );
  }
}

run().catch(e => {
  process.stderr.write(`${e.stack}\n`);
  process.exit(1);
});

import {exec} from 'node-promise-es6/child-process';
import fs from 'node-promise-es6/fs';

async function run() {
  const {linkDependencies = {}} = await fs.readJson('package.json');
  for (const dependencyName of Object.keys(linkDependencies)) {
    const dependencyPath = linkDependencies[dependencyName];

    const {stdout: diff} = await exec(
      'git diff-index HEAD', {cwd: dependencyPath});
    if (diff.trim().indexOf('package.json') === -1) { continue; }

    await exec(
      `git config user.email 'vinsonchuong@gmail.com'`,
      {cwd: dependencyPath}
    );
    await exec(
      `git config user.name 'Vinson Chuong'`,
      {cwd: dependencyPath}
    );
    await exec(
      [
        'git commit',
        `-m 'Automatically update dependencies'`,
        'package.json'
      ].join(' '),
      {cwd: dependencyPath}
    );
    await exec(
      [
        'git push',
        `https://${process.env.GITHUB_TOKEN}@github.com/vinsonchuong/${dependencyName}`,
        'master'
      ].join(' '),
      {cwd: dependencyPath}
    );
  }
}

run().catch(e => {
  process.stderr.write(`${e.stack}\n`);
  process.exit(1);
});

Use alternate way of setting git author


export default class ColumnLayout {
    constructor (options) {
        this._options = {
            columns: 5
        };
        Object.assign(this._options, options);

        this._tileWidth = 0;
        this.height = 0;
    }

    layout (layoutParams) {
        const row = Math.floor(layoutParams.position / this._options.columns);
        const col = layoutParams.position - row * this._options.columns;
        const translateX = col * this._tileWidth;
        const translateY = row * this._tileWidth;

        this.height = Math.max(this.height, (row + 1) * this._tileWidth);

        return {
            x: translateX,
            y: translateY,
            width: this._tileWidth,
            height: this._tileWidth
        };
    }

    onWidthChanged (width) {
        this._tileWidth = width / this._options.columns;
        this.height = 0;
    }
}

export default class ColumnLayout {
    constructor (options) {
        this._options = {
            columns: 5,
            margin: 8,
            outerMargin: true
        };
        Object.assign(this._options, options);

        this._tileWidth = 0;
        this.height = 0;
    }

    layout (layoutParams) {
        const row = Math.floor(layoutParams.position / this._options.columns);
        const col = layoutParams.position - row * this._options.columns;
        let translateX = col * (this._tileWidth + this._options.margin);
        let translateY = row * (this._tileWidth + this._options.margin);
        if (this._options.outerMargin) {
            translateX += this._options.margin;
            translateY += this._options.margin;
        }

        this.height = Math.max(this.height, (row + 1) * (this._tileWidth + this._options.margin) + (this._options.outerMargin ? this._options.margin : -this._options.margin));

        return {
            x: translateX,
            y: translateY,
            width: this._tileWidth,
            height: this._tileWidth
        };
    }

    onWidthChanged (width) {
        if (this._options.outerMargin) {
            this._tileWidth = (width - (this._options.columns + 1) * this._options.margin) / this._options.columns;
        } else {
            this._tileWidth = (width - (this._options.columns - 1) * this._options.margin) / this._options.columns;
        }

        this.height = 0;
    }
}

Add support for margins in column layout.


let argv = require('yargs').argv;

class Paths {
    /**
     * Create a new Paths instance.
     */
    constructor() {
        if (argv['$0'].includes('ava')) {
            this.rootPath = path.resolve(__dirname, '../');
        } else {
            this.rootPath = process.cwd();
        }
    }

    /**
     * Set the root path to resolve webpack.mix.js.
     *
     * @param {string} path
     */
    setRootPath(path) {
        this.rootPath = path;

        return this;
    }

    /**
     * Determine the path to the user's webpack.mix.js file.
     */
    mix() {
        return this.root(
            argv.env && argv.env.mixfile ? argv.env.mixfile : 'webpack.mix'
        );
    }

    /**
     * Determine the project root.
     *
     * @param {string|null} append
     */
    root(append = '') {
        return path.resolve(this.rootPath, append);
    }
}

module.exports = Paths;

let argv = require('yargs').argv;

class Paths {
    /**
     * Create a new Paths instance.
     */
    constructor() {
        if (argv['$0'].includes('ava')) {
            this.rootPath = path.resolve(__dirname, '../');
        } else {
            this.rootPath = process.cwd();
        }
    }

    /**
     * Set the root path to resolve webpack.mix.js.
     *
     * @param {string} path
     */
    setRootPath(path) {
        this.rootPath = path;

        return this;
    }

    /**
     * Determine the path to the user's webpack.mix.js file.
     */
    mix() {
        return this.root(
            process.env && process.env.MIX_FILE
                ? process.env.MIX_FILE
                : 'webpack.mix'
        );
    }

    /**
     * Determine the project root.
     *
     * @param {string|null} append
     */
    root(append = '') {
        return path.resolve(this.rootPath, append);
    }
}

module.exports = Paths;

Use real env var for custom mix file


'use strict';

var gulp = require( 'gulp' );
var g = require( 'gulp-load-plugins' )();

var allTestFiles = './tests/**/*.js';

gulp.task( 'assets', function() {
  gulp.src([
    'node_modules/gulp-mocha/node_modules/mocha/mocha.{js,css}',
    'node_modules/chai/chai.js'
  ])
  .pipe( gulp.dest( 'public' ) );
});

gulp.task( 'test', function( done ) {
  gulp.src([ allTestFiles ])
    .pipe( g.istanbul() )
    .on( 'finish', function() {
      gulp.src([ 'src/ajax.js', allTestFiles ])
      .pipe( g.istanbul.writeReports() )
      .on( 'end', done );
    });
});

gulp.task( 'default', [ 'assets' ], function() {
  require( './api/app' );
  gulp.watch([ allTestFiles, 'src/ajax.js' ], [ 'test' ]);
});

'use strict';

var gulp = require( 'gulp' );
var g = require( 'gulp-load-plugins' )();
var exec = require( 'child_process' ).exec;

var allTestFiles = './tests/**/*.js';

gulp.task( 'assets', function() {
  gulp.src([
    'node_modules/gulp-mocha/node_modules/mocha/mocha.{js,css}',
    'node_modules/chai/chai.js'
  ])
  .pipe( gulp.dest( 'public' ) );
});

gulp.task( 'test', function( done ) {
  gulp.src([ allTestFiles ])
    .pipe( g.istanbul() )
    .on( 'finish', function() {
      gulp.src([ 'src/ajax.js', allTestFiles ])
      .pipe( g.istanbul.writeReports() )
      .on( 'end', done );
    });
});

gulp.task( 'python', function() {
  exec( 'python -m SimpleHTTPServer 9001' );
});

gulp.task( 'default', [ 'assets' ], function() {
  require( './api/app' );
  gulp.watch([ allTestFiles, 'src/ajax.js' ], [ 'test' ]);
});

Add Python server to localhost


var elixir = require('laravel-elixir');

/*
 |--------------------------------------------------------------------------
 | Elixir Asset Management
 |--------------------------------------------------------------------------
 |
 | Elixir provides a clean, fluent API for defining some basic Gulp tasks
 | for your Laravel application. By default, we are compiling the Less
 | file for our application, as well as publishing vendor resources.
 |
 */

elixir(function(mix) {
    mix.less('app.less');
    mix.scriptsIn("resources/assets/js", "public/js/app.js");
});

require('laravel-elixir-browser-sync');

elixir(function(mix) {
	mix.browserSync([
	    'public/**/*',
	    'resources/views/**/*'
		], {
		proxy: 'netsocadmin.dev',
		reloadDelay: 500
	});
});

var elixir = require('laravel-elixir');

/*
 |--------------------------------------------------------------------------
 | Elixir Asset Management
 |--------------------------------------------------------------------------
 |
 | Elixir provides a clean, fluent API for defining some basic Gulp tasks
 | for your Laravel application. By default, we are compiling the Less
 | file for our application, as well as publishing vendor resources.
 |
 */

elixir(function(mix) {
    mix.less('app.less');
    mix.scriptsIn("resources/assets/js", "public/js/app.js");
});

require('laravel-elixir-browser-sync');

elixir(function(mix) {
	mix.browserSync([
	    'public/**/*',
	    'resources/**/*'
		], {
		proxy: 'netsocadmin.dev',
		reloadDelay: 500
	});
});

Change browsersync to listen for changes to JS/LESS as well


/* @flow */

const useLSP = atom.config.get('flow-ide.useLSP')

// possible improvement: check if flow really supports lsp

if (useLSP === true) {
  // eslint-disable-next-line global-require
  const { FlowLanguageClient } = require('./language-client')
  module.exports = new FlowLanguageClient()
} else {
  // eslint-disable-next-line global-require
  const { Flow } = require('./index')
  module.exports = new Flow()
}

/* @flow */

const useLSP = atom.config.get('flow-ide.useLSP')

// notify to restart if the value changes
atom.config.onDidChange('flow-ide.useLSP', () => {
  const buttons = [{
    text: 'Restart',
    onDidClick () { return atom.restartApplication() },
  }]
  atom.notifications.addInfo('Changing this value requires a restart of atom.', { dismissable: true, buttons })
})

// possible improvement: check if flow really supports lsp

if (useLSP === true) {
  // eslint-disable-next-line global-require
  const { FlowLanguageClient } = require('./language-client')
  module.exports = new FlowLanguageClient()
} else {
  // eslint-disable-next-line global-require
  const { Flow } = require('./index')
  module.exports = new Flow()
}

Add notification to restart atom when switching to/from LSP


var NwBuilder = require("node-webkit-builder");
var asap = require("pdenodeify");
var path = require("path");
var rimraf = require("rimraf");

module.exports = function(options, buildResult){
	var nwOptions = {};

	// Convert the options to camelcase
	Object.keys(options).forEach(function(optName){
		nwOptions[toCamelcase(optName)] = options[optName];
	});

	// Set the bundlesPath as a file to use.
	var files = nwOptions.files || nwOptions.glob || [];
	files = Array.isArray(files) ? files : [files];
	if(buildResult && buildResult.configuration &&
	   buildResult.configuration.bundlesPath) {
		var bundlesPath = buildResult.configuration.bundlesPath;
		files.unshift(path.join(bundlesPath, "**/*"));
	}
	nwOptions.files = files;

	var nw = new NwBuilder(nwOptions);

	nw.on("log", function(msg){
		console.error(msg);
	});

	var buildPromise = asap(rimraf)(nwOptions.buildDir);

	var builder = asap(nw.build.bind(nw));
	return buildPromise.then(builder);
};

function toCamelcase(str) {
	return str.replace(/\_+([a-z])/g, function (x, chr) { return chr.toUpperCase(); });
}

var NwBuilder = require("node-webkit-builder");
var asap = require("pdenodeify");
var path = require("path");
var rimraf = require("rimraf");

module.exports = function(options, buildResult){
	var nwOptions = {};

	// Convert the options to camelcase
	Object.keys(options).forEach(function(optName){
		nwOptions[toCamelcase(optName)] = options[optName];
	});

	// Set the bundlesPath as a file to use.
	var files = nwOptions.files || nwOptions.glob || [];
	files = Array.isArray(files) ? files : [files];
	if(buildResult && buildResult.configuration &&
	   buildResult.configuration.bundlesPath) {
		var bundlesPath = buildResult.configuration.bundlesPath;
		var globPath = path.relative(process.cwd(), bundlesPath) + "/**/*";
		files.unshift(globPath);
	}
	nwOptions.files = files;

	var nw = new NwBuilder(nwOptions);

	nw.on("log", function(msg){
		console.error(msg);
	});

	var buildPromise = asap(rimraf)(nwOptions.buildDir);

	var builder = asap(nw.build.bind(nw));
	return buildPromise.then(builder);
};

function toCamelcase(str) {
	return str.replace(/\_+([a-z])/g, function (x, chr) { return chr.toUpperCase(); });
}

Use path.relative to create the bundles glob