"use strict";

var View = require("substance-application").View;
var $$ = require("substance-application").$$;
var Data = require("substance-data");
var Index = Data.Graph.Index;
var _ = require("underscore");

// Substance.TOC.View
// ==========================================================================

var TOCView = function(docCtrl) {
  View.call(this);
  this.docCtrl = docCtrl;

  // Sniff into headings
  // --------
  //

  this.headings = _.filter(this.docCtrl.getNodes(), function(node) {
    return node.type === "heading";
  });

  this.$el.addClass("toc");
};

TOCView.Prototype = function() {

  // Renderer
  // --------

  this.render = function() {
    this.el.innerHTML = "";

    if (this.headings.length <= 2) return this;
    _.each(this.headings, function(heading) {
      this.el.appendChild($$('a.heading-ref.level-'+heading.level, {
        id: "toc_"+heading.id,
        text: heading.content,
        "sbs-click": "jumpToNode("+heading.id+")"
      }));
    }, this);

    return this;
  };

  // Renderer
  // --------
  //

  this.setActiveNode = function(nodeId) {
    this.$('.heading-ref.active').removeClass('active');
    this.$('#toc_'+nodeId).addClass('active');
  };

};

TOCView.Prototype.prototype = View.prototype;
TOCView.prototype = new TOCView.Prototype();

module.exports = TOCView;

"use strict";

var View = require("substance-application").View;
var $$ = require("substance-application").$$;
var Data = require("substance-data");
var Index = Data.Graph.Index;
var _ = require("underscore");

// Substance.TOC.View
// ==========================================================================

var TOCView = function(docCtrl) {
  View.call(this);
  this.docCtrl = docCtrl;

  this.$el.addClass("toc");
};

TOCView.Prototype = function() {

  // Renderer
  // --------

  this.render = function() {
    this.el.innerHTML = "";

    this.headings = _.filter(this.docCtrl.getNodes(), function(node) {
      return node.type === "heading";
    });

    if (this.headings.length <= 2) return this;
    _.each(this.headings, function(heading) {
      this.el.appendChild($$('a.heading-ref.level-'+heading.level, {
        id: "toc_"+heading.id,
        text: heading.content,
        "sbs-click": "jumpToNode("+heading.id+")"
      }));
    }, this);

    return this;
  };

  // Renderer
  // --------
  //

  this.setActiveNode = function(nodeId) {
    this.$('.heading-ref.active').removeClass('active');
    this.$('#toc_'+nodeId).addClass('active');
  };

};

TOCView.Prototype.prototype = View.prototype;
TOCView.prototype = new TOCView.Prototype();

module.exports = TOCView;

Read document structure dynamically to be able to react on structural changes.


//Winston Logger Module
const winston = require('winston');

let logLevel = "info"; //default
if (process.env.NODE_ENV === "production") {
    logLevel = "error"; //logs error
}
else if (process.env.NODE_ENV === "staging") {
    logLevel = "info"; //logs info and error
}

const logger = new winston.Logger({
    transports: [
        new winston.transports.Console({ //write prettified logs to console
            colorize: true,
            timestamp: true,
            level: logLevel
        }),
        new winston.transports.File({ //write logs to a file as well
            level: logLevel,
            name: 'policy_logs',
            filename: 'policy_logs.log'
        })
    ],
    exitOnError: false
});

module.exports = {
    //required functions to implement

    //normal message. log to file and to the console as an "info" message
    info: function (msg) {
        logger.info(msg);
    },
    //error message. log to file and to the console as an "error" message
    error: function (msg) {
        logger.error(msg);
    }
}

//Winston Logger Module
const winston = require('winston');

let logLevel = "info"; //default
if (process.env.NODE_ENV === "production") {
    logLevel = "error"; //logs error
}
else if (process.env.NODE_ENV === "staging") {
    logLevel = "info"; //logs info and error
}

const logger = new winston.Logger({
    transports: [
        new winston.transports.Console({ //write prettified logs to console
            colorize: true,
            timestamp: true,
            level: logLevel
        }),
        /*
        new winston.transports.File({ //write logs to a file as well
            level: logLevel,
            name: 'policy_logs',
            filename: 'policy_logs.log'
        })
        */
    ],
    exitOnError: false
});

module.exports = {
    //required functions to implement

    //normal message. log to file and to the console as an "info" message
    info: function (msg) {
        logger.info(msg);
    },
    //error message. log to file and to the console as an "error" message
    error: function (msg) {
        logger.error(msg);
    }
}

Disable file logging by default


'use strict';

var Y             = require('yuidocjs');
var rsvp          = require('rsvp');
var optsGenerator = require('../options');

module.exports = {
  name: 'ember-cli-yuidoc',

  description: 'Generates html documentation using YUIDoc',

  run: function(opts, rawArgs) {
    var options = optsGenerator.generate();

    var yuidocCompiler = new Y.YUIDoc(options);
    var json = yuidocCompiler.run();
    var builder = new Y.DocBuilder(options, json);

    return new rsvp.Promise(function(resolve) {
      builder.compile(function() { resolve(); });
    });
  }
}

'use strict';

module.exports = {
  name: 'ember-cli-yuidoc',

  description: 'Generates html documentation using YUIDoc',

  run: function(opts, rawArgs) {
    var Y             = require('yuidocjs');
    var rsvp          = require('rsvp');
    var optsGenerator = require('../options');
    var options       = optsGenerator.generate();

    var yuidocCompiler = new Y.YUIDoc(options);
    var json = yuidocCompiler.run();
    var builder = new Y.DocBuilder(options, json);

    return new rsvp.Promise(function(resolve) {
      builder.compile(function() { resolve(); });
    });
  }
}

Move require statements to the run command


import React from 'react';

import LoadingContainer from '../containers/LoadingContainer';
import PathResultsContainer from '../containers/PathResultsContainer';
import ErrorMessageContainer from '../containers/ErrorMessageContainer';
import SearchButtonContainer from '../containers/SearchButtonContainer';
import ToArticleInputContainer from '../containers/ToArticleInputContainer';
import FromArticleInputContainer from '../containers/FromArticleInputContainer';

import logo from '../images/logo.png';

import {P, Logo, InputFlexContainer, MainContent} from './Home.styles';

export default () => (

    <Logo src={logo} alt="Six Degrees of Wikipedia Logo" />
    <MainContent>

Find the shortest paths from

      <InputFlexContainer>
        <ToArticleInputContainer />

to

        <FromArticleInputContainer />
      </InputFlexContainer>

      <SearchButtonContainer />
      <LoadingContainer />
      <PathResultsContainer />
      <ErrorMessageContainer />
    </MainContent>

);

import React from 'react';

import LoadingContainer from '../containers/LoadingContainer';
import PathResultsContainer from '../containers/PathResultsContainer';
import ErrorMessageContainer from '../containers/ErrorMessageContainer';
import SearchButtonContainer from '../containers/SearchButtonContainer';
import ToArticleInputContainer from '../containers/ToArticleInputContainer';
import FromArticleInputContainer from '../containers/FromArticleInputContainer';

import logo from '../images/logo.png';

import {P, Logo, InputFlexContainer, MainContent} from './Home.styles';

export default () => (

    <Logo src={logo} alt="Six Degrees of Wikipedia Logo" />
    <MainContent>

Find the shortest paths from

      <InputFlexContainer>
        <FromArticleInputContainer />

to

        <ToArticleInputContainer />
      </InputFlexContainer>

      <SearchButtonContainer />
      <LoadingContainer />
      <PathResultsContainer />
      <ErrorMessageContainer />
    </MainContent>

);

Fix wrong input component order


"use strict";
var Codesearch = function() {
  return {
    socket: null,
    delegate: null,
    connect: function(delegate) {
      if (Codesearch.socket !== null)
        return;
      console.log("Connecting...");
      Codesearch.remote = null;
      Codesearch.delegate = delegate;
      var socket = io.connect(document.location.host.replace(/:\d+$/, '') + ":8910");
      socket.on('connect', function () {
                  Codesearch.socket = socket;
                  if (Codesearch.delegate.on_connect)
                    Codesearch.delegate.on_connect();
                });
      socket.on('regex_error', Codesearch.delegate.error);
      socket.on('match', Codesearch.delegate.match);
      socket.on('search_done', Codesearch.delegate.search_done);
    },
    new_search: function(re, file, id) {
      if (Codesearch.socket !== null)
        Codesearch.socket.emit('new_search', re, file, id);
    }
  };
}();

"use strict";
var Codesearch = function() {
  return {
    socket: null,
    delegate: null,
    connect: function(delegate) {
      if (Codesearch.socket !== null)
        return;
      console.log("Connecting...");
      Codesearch.remote = null;
      Codesearch.delegate = delegate;
      var socket = io.connect("http://" + document.location.host.replace(/:\d+$/, '') + ":8910");
      socket.on('connect', function () {
                  Codesearch.socket = socket;
                  if (Codesearch.delegate.on_connect)
                    Codesearch.delegate.on_connect();
                });
      socket.on('regex_error', Codesearch.delegate.error);
      socket.on('match', Codesearch.delegate.match);
      socket.on('search_done', Codesearch.delegate.search_done);
    },
    new_search: function(re, file, id) {
      if (Codesearch.socket !== null)
        Codesearch.socket.emit('new_search', re, file, id);
    }
  };
}();

Use a full URL for socket.io.


'use strict';

var exec = require('child_process').exec;

function fail() {
	process.stdout.write(
		'Style check failed (see the above output).\n' +
		'If you still wish to commit your code, run git commit -n to skip this check.\n'
	);
	process.exit(1);
}

exec('git diff --staged --name-status', function (error, stdout, stderr) {
	if (error) {
		process.stdout.write(stderr + '\nCould not get list of modified files: ' + error);
		fail();
	}
	var expression = /^[MA]\s+([\w-\\\/]+\.js)$/gm;
	var files = [];
	var match;

	while (match = expression.exec(stdout)) {
		files.push(match[1]);
	}
	if (files.length === 0) {
		process.exit(0);
	}

	var child = exec('node_modules/.bin/jshint --reporter=tools/jshint-reporter.js ' + files.join(' '));

	child.stdout.on('data', function (data) {
		process.stdout.write(data);
	});
	child.stderr.on('data', function (data) {
		process.stderr.write(data);
	});

	child.on('exit', function (code) {
		if (code !== 0) {
			fail();
		} else {
			process.exit(0);
		}
	});
});

'use strict';

var exec = require('child_process').exec;
var path = require('path');

function fail() {
	process.stdout.write(
		'Style check failed (see the above output).\n' +
		'If you still wish to commit your code, run git commit -n to skip this check.\n'
	);
	process.exit(1);
}

exec('git diff --staged --name-status', function (error, stdout, stderr) {
	if (error) {
		process.stdout.write(stderr + '\nCould not get list of modified files: ' + error);
		fail();
	}
	var expression = /^[MA]\s+([\w-\\\/]+\.js)$/gm;
	var files = [];
	var match;

	while (match = expression.exec(stdout)) {
		files.push(match[1]);
	}
	if (files.length === 0) {
		process.exit(0);
	}

	var command = path.resolve('./node_modules/.bin/jshint');
	var child = exec(command + ' --reporter=tools/jshint-reporter.js ' + files.join(' '));

	child.stdout.on('data', function (data) {
		process.stdout.write(data);
	});
	child.stderr.on('data', function (data) {
		process.stderr.write(data);
	});

	child.on('exit', function (code) {
		if (code !== 0) {
			fail();
		} else {
			process.exit(0);
		}
	});
});

Convert Unix path to Windows path in jshint command (in node_modules)


describe('layout tests', function () {

    it('Should have MaterialLayout globally available', function () {
      expect(MaterialLayout).to.be.a('function');
    });

    it('Should be upgraded to a MaterialLayout successfully', function () {
      var el = document.createElement('div');
      el.innerHTML = '

' +
      '

' +
      '

';

      var parent = document.createElement('div');
      parent.appendChild(el); // MaterialLayout.init() expects a parent

      componentHandler.upgradeElement(el, 'MaterialLayout');
      var upgraded = el.getAttribute('data-upgraded');
      expect(upgraded).to.contain('MaterialLayout');
    });
  });

describe('layout tests', function () {

    it('Should have MaterialLayout globally available', function () {
      expect(MaterialLayout).to.be.a('function');
    });

    it('Should be upgraded to a MaterialLayout successfully', function () {
      var el = document.createElement('div');
      el.innerHTML = '

' +
      '

' +
      '

';

      var parent = document.createElement('div');
      parent.appendChild(el); // MaterialLayout.init() expects a parent

      componentHandler.upgradeElement(el, 'MaterialLayout');
      expect($(el)).to.have.data('upgraded', ',MaterialLayout');
    });
  });

Update MaterialLayout unit test with chai-jquery


/**
 * Return a truncated version of a string
 * @param  {string} input
 * @param  {integer} length
 * @param  {boolean} killwords
 * @param  {string} end
 * @return {string}
 */
PolymerExpressions.prototype.truncate = function (input, length, killwords, end) {
    var orig = input;
    length = length || 255;

    if (input.length <= length)
        return input;

    if (killwords) {
        input = input.substring(0, length);
    } else {
        var idx = input.lastIndexOf(' ', length);
        if (idx === -1) {
            idx = length;
        }

        input = input.substring(0, idx);
    }

    input += (end !== undefined && end !== null) ? end : '...';
    return input;
};

/**
 * Return a truncated version of a string
 * @param  {string} input
 * @param  {integer} length
 * @param  {boolean} killwords
 * @param  {string} end
 * @return {string}
 */
PolymerExpressions.prototype.truncate = function (input, length, killwords, end) {
    var orig = input;
    length = length || 255;
    if(!input) {
        return;
    }
    if (input.length <= length)
        return input;

    if (killwords) {
        input = input.substring(0, length);
    } else {
        var idx = input.lastIndexOf(' ', length);
        if (idx === -1) {
            idx = length;
        }

        input = input.substring(0, idx);
    }

    input += (end !== undefined && end !== null) ? end : '...';
    return input;
};

Add check to ensure truncate does not error


import React, { Component } from 'react';

class RowSizeControls extends Component {
  render() {
    return (

        Images per row
        <input
          style={{
            display: 'block',
            margin: '0 auto',
          }}
          type="range"
          min="1"
          max="10"
          value={this.props.value}
          onChange={this.props.onChange} />
        {this.props.value}

    );
  }
}

export default RowSizeControls;

import React, { Component } from 'react';

class RowSizeControls extends Component {
  render() {
    return (

        {this.props.value} images per row
        <input
          style={{
            display: 'block',
            margin: '0 auto',
          }}
          type="range"
          min="1"
          max="10"
          value={this.props.value}
          onChange={this.props.onChange} />

    );
  }
}

export default RowSizeControls;

Add rowLength to slider label


'use strict';

module.exports = function(html, attachments) {
  attachments.forEach(function(attachment) {
    attachment.applied = false;

    var regexps = [
      (attachment.fileName) ? new RegExp('<img[^>]*cid:' + attachment.fileName + '[@"\'][^>]*>', 'ig') : undefined,
      (attachment.contentId) ? new RegExp('<img[^>]*cid:' + attachment.contentId + '[@"\'][^>]*>', 'ig') : undefined,
    ];

    var content = Buffer.isBuffer(attachment.content) ? attachment.content.toString('base64') : attachment.content;
    var extension = attachment.fileName.substr(attachment.fileName.lastIndexOf('.') + 1);
    if(extension === 'jpeg') {
      extension = 'jpg';
    }

    regexps.forEach(function(regexp) {
      if(regexp && regexp.test(html)) {
        attachment.applied = true;

        html = html.replace(regexp, '<img src="data:image/' + extension + ';base64,' + content + '" />');
      }
    });
  });

  return html.replace(/<img[^>]*cid[^>]*>/ig, '');
};

'use strict';

module.exports = function(html, attachments) {
  attachments.forEach(function(attachment) {
    attachment.applied = false;

    var regexps = [
      (attachment.fileName) ? new RegExp('<img[^>]*cid:' + attachment.fileName.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&") + '[@"\'][^>]*>', 'ig') : undefined,
      (attachment.contentId) ? new RegExp('<img[^>]*cid:' + attachment.contentId.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&") + '[@"\'][^>]*>', 'ig') : undefined,
    ];

    var content = Buffer.isBuffer(attachment.content) ? attachment.content.toString('base64') : attachment.content;
    var extension = attachment.fileName.substr(attachment.fileName.lastIndexOf('.') + 1);
    if(extension === 'jpeg') {
      extension = 'jpg';
    }

    regexps.forEach(function(regexp) {
      if(regexp && regexp.test(html)) {
        attachment.applied = true;

        html = html.replace(regexp, '<img src="data:image/' + extension + ';base64,' + content + '" />');
      }
    });
  });

  return html.replace(/<img[^>]*cid:[^>]*>/ig, '');
};

Fix a bug in removing cid img without attachments


'use strict'

const _ = require('lodash')

const hasLoop = (flow) => {
  if (Array.isArray(flow)) {
    return _validate(flow)
  } else if (flow.states) {
    return _validate(flow.states)
  }
  return {
    status: "error",
    message: "Flow not found!"
  }
}

const _validate = (states) => {

  const paths = states.reduce((acc, state) => {
    if (state.automatic) {
      acc.push({ from: state.name, to: state.transitions[0].to })
      return acc
    }
  }, [])

  while (paths.length > 0) {
    if (validatePath(paths.pop(), paths)) {
      return {
        status: ERROR,
        message: "LOOP"
      }
    }
  }

  return {
    status: SUCCESS,
    message: "OK"
  }
}

const validatePath = (root, path, stack = []) => {
  stack.push(root)
  if (_.find(stack, { from: root.to })) {
    return true
  }
  const next = _.find(path, { from: root.to })
  const newPath = _.remove(path, { from: root.to })
  if (!next) {
    return false
  }
  return validatePath(next, newPath, stack)
}

const ERROR = "error"
const SUCCESS = "sucess"

module.exports = {
  hasLoop,
  ERROR,
  SUCCESS
}

'use strict'

const _ = require('lodash')

const hasLoop = (flow) => {
  if (Array.isArray(flow)) {
    return _validate(flow)
  } else if (flow.states) {
    return _validate(flow.states)
  }
  return {
    status: "error",
    message: "Flow not found!"
  }
}

const _validate = (states) => {

  const paths = states.reduce((acc, state) => {
    if (state.automatic) {
      acc.push({ from: state.name, to: state.transitions[0].to })
    }
    return acc
  }, [])

  while (paths.length > 0) {
    if (validatePath(paths.pop(), paths)) {
      return {
        status: ERROR,
        message: "LOOP"
      }
    }
  }

  return {
    status: SUCCESS,
    message: "OK"
  }
}

const validatePath = (root, path, stack = []) => {
  stack.push(root)
  if (_.find(stack, { from: root.to })) {
    return true
  }
  const next = _.find(path, { from: root.to })
  const newPath = _.remove(path, { from: root.to })
  if (!next) {
    return false
  }
  return validatePath(next, newPath, stack)
}

const ERROR = "error"
const SUCCESS = "sucess"

module.exports = {
  hasLoop,
  ERROR,
  SUCCESS
}

Fix return acc when not push a state


var debug = require('debug')('metalsmith-copy'),
    path = require('path'),
    cloneDeep = require('lodash').cloneDeep;
    minimatch = require('minimatch');

module.exports = plugin;

function plugin(options) {
  return function(files, metalsmith, done) {
    if (!options.directory && !options.extension && !options.transform) return done(new Error('metalsmith-copy: "directory" or "extension" option required'));

    var matcher = minimatch.Minimatch(options.pattern);

    Object.keys(files).forEach(function (file) {
      debug('checking file: ' + file);
      if (!matcher.match(file)) return;

      var newName = file;

      // transform filename
      if (options.transform) {
        newName = options.transform(file);
      } else {
        if (options.extension) {
          var currentExt = path.extname(file);
          newName = path.join(path.dirname(file), path.basename(file, currentExt) + options.extension);
        }
        if (options.directory) {
          newName = path.join(options.directory, path.basename(newName));
        }
      }

      if (files[newName]) return done(new Error('metalsmith-copy: copying ' + file + ' to ' + newName + ' would overwrite file'));

      debug('copying file: ' + newName);
      files[newName] = cloneDeep(files[file], function(value) {
        if (value instanceof Buffer) {
          return new Buffer(value);
        }
      });
      if (options.move) {
        delete files[file];
      }
    });

    done();
  }
}

var debug = require('debug')('metalsmith-copy'),
    path = require('path'),
    cloneDeep = require('lodash').cloneDeep,
    minimatch = require('minimatch');

module.exports = plugin;

function plugin(options) {
  return function(files, metalsmith, done) {
    if (!options.directory && !options.extension && !options.transform) return done(new Error('metalsmith-copy: "directory" or "extension" option required'));

    var matcher = minimatch.Minimatch(options.pattern);

    Object.keys(files).forEach(function (file) {
      debug('checking file: ' + file);
      if (!matcher.match(file)) return;

      var newName = file;

      // transform filename
      if (options.transform) {
        newName = options.transform(file);
      } else {
        if (options.extension) {
          var currentExt = path.extname(file);
          newName = path.join(path.dirname(file), path.basename(file, currentExt) + options.extension);
        }
        if (options.directory) {
          newName = path.join(options.directory, path.basename(newName));
        }
      }

      if (files[newName]) return done(new Error('metalsmith-copy: copying ' + file + ' to ' + newName + ' would overwrite file'));

      debug('copying file: ' + newName);
      files[newName] = cloneDeep(files[file], function(value) {
        if (value instanceof Buffer) {
          return new Buffer(value);
        }
      });
      if (options.move) {
        delete files[file];
      }
    });

    done();
  }
}

Fix imports that broke when running in strict mode


var easymongo = require('easymongo');

module.exports = function(options) {
  var mongo = new easymongo(options);

  return {
    find: function(collection, params, options) {
      return function(fn) {
        mongo.find(collection, params, options, fn);
      };
    },

    save: function(collection, params) {
      return function(fn) {
        mongo.save(collection, params, fn);
      };
    },

    remove: function(collection, id) {
      return function(fn) {
        mongo.removeById(collection, id, fn);
      };
    }
  };
};

var EasyMongo = require('easymongo');

module.exports = function(server, options) {
  var mongo = new EasyMongo(server, options);

  return {
    find: function(table, params, options) {
      return function(fn) {
        mongo.find(table, params, options, fn);
      };
    },

    findById: function(table, id) {
      return function(fn) {
        mongo.findById(table, id, fn);
      };
    },

    save: function(table, params) {
      return function(fn) {
        mongo.save(table, params, fn);
      };
    },

    remove: function(table, params) {
      return function(fn) {
        mongo.remove(table, params, fn);
      };
    },

    removeById: function(table, id) {
      return function(fn) {
        mongo.removeById(table, id, fn);
      };
    },

    count: function(table, params) {
      return function(fn) {
        mongo.count(table, params, fn);
      };
    }
  };
};

Add constructor arguments. Fix deprecated API. Add findById, removeById and count methods.


seajs.on('compiled', function(mod) {
  if (mod.uri.indexOf('jquery.js') > -1) {
    window.jQuery = window.$ = mod.exports
  }
})

seajs.on('compile', function(mod) {
  if (mod.uri.indexOf('cookie.js') > -1) {
    mod.exports = $.cookie
  }
})

seajs.config({
  preload: ['./auto-transport/jquery']
}).use('./auto-transport/init')

seajs.on('compiled', function(mod) {
  if (mod.uri.indexOf('jquery.js') > -1) {
    global.jQuery = global.$ = mod.exports
  }
})

seajs.on('compile', function(mod) {
  if (mod.uri.indexOf('cookie.js') > -1) {
    mod.exports = $.cookie
  }
})

seajs.config({
  preload: ['./auto-transport/jquery']
}).use('./auto-transport/init')

Change window to global for Node.js


/*=include modules/accessor.js */
/*=include modules/ajax.js */
/*=include modules/calculation_colums.js */
/*=include modules/clipboard.js */
/*=include modules/download.js */
/*=include modules/edit.js */
/*=include modules/filter.js */
/*=include modules/format.js */
/*=include modules/frozen_columns.js */
/*=include modules/frozen_rows.js */
/*=include modules/group_rows.js */
/*=include modules/history.js */
/*=include modules/html_table_import.js */
/*=include modules/Keybindings.js */
/*=include modules/moveable_columns.js */
/*=include modules/moveable_rows.js */
/*=include modules/mutator.js */
/*=include modules/page.js */
/*=include modules/persistence.js */
/*=include modules/resize_columns.js */
/*=include modules/resize_rows.js */
/*=include modules/resize_table.js */
/*=include modules/responsive_layout.js */
/*=include modules/select_row.js */
/*=include modules/sort.js */
/*=include modules/validate.js */

/*=include modules/accessor.js */
/*=include modules/ajax.js */
/*=include modules/calculation_colums.js */
/*=include modules/clipboard.js */
/*=include modules/download.js */
/*=include modules/edit.js */
/*=include modules/filter.js */
/*=include modules/format.js */
/*=include modules/frozen_columns.js */
/*=include modules/frozen_rows.js */
/*=include modules/group_rows.js */
/*=include modules/history.js */
/*=include modules/html_table_import.js */
/*=include modules/keybindings.js */
/*=include modules/moveable_columns.js */
/*=include modules/moveable_rows.js */
/*=include modules/mutator.js */
/*=include modules/page.js */
/*=include modules/persistence.js */
/*=include modules/resize_columns.js */
/*=include modules/resize_rows.js */
/*=include modules/resize_table.js */
/*=include modules/responsive_layout.js */
/*=include modules/select_row.js */
/*=include modules/sort.js */
/*=include modules/validate.js */

Fix improper case from 'KeyBindings' to keybindings causing build failures on linux.


var tools = module.exports = exports = {},
    debug = require('debug')('turnkey');

tools.default_cfg = function() {
  var cfg = {
    // Required Options --------------------------------------------------------
    router: undefined,       /* Express JS Router */
    model: undefined,        /* Mongoose model */

    // Options with defaults ---------------------------------------------------
    hash_length: 256,
    hash_iterations: 12288,
    logger: console.log.bind(console),
    username_key: 'username',
    min_length: 8,
    forgot_limit: 1000 * 60 * 60,  // 1 hour

    deserialize: function(id, cb) {
      cfg.model.findOne({ _id: id }, function(e, d) {
        d = (d && d.toJSON) ? d.toJSON({ getters: true }) : d;
        cb(e, d);
      });
    },

    serialize: function(u, cb) {
      return cb(null, String(u._id));
    },

    find_user: function(body, cb) {
      var uname = body && body[cfg.username_key] &&
                  body[cfg.username_key].toLowerCase(),
          q = {};

      debug('find user: %s', uname);

      q[cfg.username_key] = uname;
      cfg.model.findOne(q, function(e, d) {
        d = (d && d.toJSON) ? d.toJSON({ getters: true }) : d;
        cb(e, d);
      });
    },

    // Optionals ---------------------------------------------------------------
    forgot_mailer: undefined     /* Mailer when forgot password */
  };

  return cfg;
}

var tools = module.exports = exports = {},
    debug = require('debug')('turnkey');

tools.default_cfg = function() {
  var cfg = {
    // Required Options --------------------------------------------------------
    router: undefined,       /* Express JS Router */
    model: undefined,        /* Mongoose model */

    // Options with defaults ---------------------------------------------------
    hash_length: 256,
    hash_iterations: 12288,
    logger: console.log.bind(console),
    username_key: 'username',
    min_length: 8,
    forgot_limit: 1000 * 60 * 60,  // 1 hour

    deserialize: function(id, cb) {
      cfg.model.findById(id).lean().exec(cb);
    },

    serialize: function(u, cb) {
      return cb(null, String(u._id));
    },

    find_user: function(body, cb) {
      var uname = body && body[cfg.username_key] &&
                  body[cfg.username_key].toLowerCase(),
          q = {};

      debug('find user: %s', uname);

      q[cfg.username_key] = uname;
      cfg.model.findOne(q, function(e, d) {
        d = (d && d.toJSON) ? d.toJSON({ getters: true }) : d;
        cb(e, d);
      });
    },

    // Optionals ---------------------------------------------------------------
    forgot_mailer: undefined     /* Mailer when forgot password */
  };

  return cfg;
}

Modify default deserialize user function


'use strict';

const gulp = require('gulp');
const helper = require('gulp/helper');

gulp.task('configure:sandbox', cb => {
  const isNotEmpty = helper.validators.isNotEmpty;
  const questions = [
    {
      type: 'input',
      name: 'url',
      message: 'What is your Profile URL address?',
      default: 'https://webtask.it.auth0.com',
      validate: isNotEmpty
    },
    {
      type: 'input',
      name: 'container',
      message: 'What is your container name?',
      validate: isNotEmpty
    },
    {
      type: 'input',
      name: 'token',
      message: 'What is your token value?',
      validate: isNotEmpty
    }
  ];

  helper.prompt(questions).then(input => {
    helper.updateConfig('sandbox', input).then(cb).catch(cb);
  }).catch(cb);
});

'use strict';

const gulp = require('gulp');
const helper = require('gulp/helper');

gulp.task('configure:sandbox', cb => {
  const isNotEmpty = helper.validators.isNotEmpty;
  const questions = [
    {
      type: 'input',
      name: 'url',
      message: 'What is your Profile URL address?',
      default: 'https://webtask.it.auth0.com',
      validate: isNotEmpty
    },
    {
      type: 'input',
      name: 'container',
      message: 'What is your container name?',
      validate: isNotEmpty
    },
    {
      type: 'password',
      name: 'token',
      message: 'What is your token value?',
      validate: isNotEmpty
    }
  ];

  helper.prompt(questions).then(input => {
    helper.updateConfig('sandbox', input).then(cb).catch(cb);
  }).catch(cb);
});

Use 'password' type for a field with a token value.


export class Machete {
    // TODO: Implement polyfills

    /**
     * Utility function to get an element from the DOM.
     * It is needed because everytime we get an element from the DOM,
     * we reset its style to make sure code works as needed.
     * @param  string selector      CSS selector
     * @return Element              Selected element
     */
    static getDOMElement(selector) {
        let element = document.querySelector(selector),
            style = window.getComputedStyle(element);
        element.style.left = style.left;
        element.style.top = style.top;
        element.style.width = style.width;
        element.style.height = style.height;
        element.style.transform = style.transform;
        return element;
    }
}

export class Machete {
    // TODO: Implement polyfills

    /**
     * Utility function to get an element from the DOM.
     * It is needed because everytime we get an element from the DOM,
     * we reset its style to make sure code works as needed.
     * @param  string selector      CSS selector
     * @return Element              Selected element
     */
    static getDOMElement(selector) {
        return Machete.resetStyles(document.querySelector(selector));
    }

    static resetStyles(element) {
        let style = window.getComputedStyle(element);
        element.style.left = style.left;
        element.style.top = style.top;
        element.style.width = style.width;
        element.style.height = style.height;
        element.style.transform = style.transform;
        return element;
    }
}

Split DOM element selection and style reset


/* eslint-env node */

module.exports = {
  root: true,
  parser: '@typescript-eslint/parser',
  parserOptions: {
    ecmaFeatures: {
      jsx: true,
    },
    ecmaVersion: 2020,
    sourceType: 'module',
  },
  plugins: ['@typescript-eslint', 'jest', 'react-hooks', 'prettier'],
  extends: [
    'eslint:recommended',
    'plugin:react/recommended',
    'plugin:@typescript-eslint/eslint-recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:prettier/recommended',
    'prettier/react',
  ],
  settings: {
    react: {
      version: 'detect',
    },
  },
  rules: {
    'react/prop-types': 'off',
    '@typescript-eslint/explicit-function-return-type': 'off',
    '@typescript-eslint/explicit-module-boundary-types': 'off',
    '@typescript-eslint/no-unused-vars': 'off',
    '@typescript-eslint/member-delimiter-style': 'off',
    '@typescript-eslint/no-explicit-any': 'off',

    'react-hooks/rules-of-hooks': 'error',
    'react-hooks/exhaustive-deps': 'warn',
  },
  overrides: [
    {
      files: ['*.test.ts', '*.test.tsx'],
      env: {
        'jest/globals': true,
      },
    },
    {
      files: ['*.tsx'],
      rules: {
        'react/react-in-jsx-scope': 'error',
      },
    },
  ],
}

/* eslint-env node */

module.exports = {
  root: true,
  parser: '@typescript-eslint/parser',
  parserOptions: {
    ecmaFeatures: {
      jsx: true,
    },
    ecmaVersion: 2020,
    sourceType: 'module',
  },
  plugins: ['@typescript-eslint', 'jest', 'react-hooks', 'prettier'],
  extends: [
    'eslint:recommended',
    'plugin:react/recommended',
    'plugin:@typescript-eslint/eslint-recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:prettier/recommended',
    'prettier',
  ],
  settings: {
    react: {
      version: 'detect',
    },
  },
  rules: {
    'react/prop-types': 'off',
    '@typescript-eslint/explicit-function-return-type': 'off',
    '@typescript-eslint/explicit-module-boundary-types': 'off',
    '@typescript-eslint/no-unused-vars': 'off',
    '@typescript-eslint/member-delimiter-style': 'off',
    '@typescript-eslint/no-explicit-any': 'off',

    'react-hooks/rules-of-hooks': 'error',
    'react-hooks/exhaustive-deps': 'warn',
  },
  overrides: [
    {
      files: ['*.test.ts', '*.test.tsx'],
      env: {
        'jest/globals': true,
      },
    },
    {
      files: ['*.tsx'],
      rules: {
        'react/react-in-jsx-scope': 'error',
      },
    },
  ],
}

Use updated prettier ESLint config


const theme = {
  color: {
    grey: '#DDDDDD',
    black: '#111111',
  },
  font: {
    sans: `'Lora', sans-serif`,
    serif: `'Raleway', serif`,
  },
};

export default theme;

const theme = {
  color: {
    grey: '#DDDDDD',
    black: '#111111',
  },
  font: {
    sans: `'Lora', sans-serif`,
    serif: `'Raleway', serif`,
  },
  sizes: {
    mobileS: '320px',
    mobileM: '375px',
    mobileL: '425px',
    tablet: '768px',
    laptop: '1024px',
    laptopL: '1440px',
    desktop: '2560px',
  },
};

export default theme;

Add sizes to make it easier @medias


const departments = new Map(Object.entries({
	AR: 'ART',
	AS: 'ASIAN',
	BI: 'BIO',
	CH: 'CHEM',
	CS: 'CSCI',
	EC: 'ECON',
	ES: 'ENVST',
	HI: 'HIST',
	MU: 'MUSIC',
	PH: 'PHIL',
	PS: 'PSCI',
	RE: 'REL',
	SA: 'SOAN',
}))

export default function expandDepartment(dept) {
	if (departments.has(dept)) {
		return departments.get(dept)
	}
	else {
		throw new TypeError(`expandDepartment(): "${dept}" is not a valid department shorthand`)
	}
}

const departments = new Map(Object.entries({
	AR: 'ART',
	AS: 'ASIAN',
	BI: 'BIO',
	CH: 'CHEM',
	CS: 'CSCI',
	EC: 'ECON',
	EN: 'ENGL',
	ES: 'ENVST',
	HI: 'HIST',
	MU: 'MUSIC',
	PH: 'PHIL',
	PS: 'PSCI',
	RE: 'REL',
	SA: 'SOAN',
}))

export default function expandDepartment(dept) {
	if (departments.has(dept)) {
		return departments.get(dept)
	}
	else {
		throw new TypeError(`expandDepartment(): "${dept}" is not a valid department shorthand`)
	}
}

Add "EN" as a valid department shorthand


/*
 * style-select.js
 * https://github.com/savetheinternet/Tinyboard/blob/master/js/style-select.js
 *
 * Changes the stylesheet chooser links to a <select>
 *
 * Released under the MIT license
 * Copyright (c) 2013 Michael Save <savetheinternet@tinyboard.org>
 *
 * Usage:
 *   $config['additional_javascript'][] = 'js/jquery.min.js';
 *   $config['additional_javascript'][] = 'js/style-select.js';
 *
 */

onready(function(){
	var stylesDiv = $('div.styles');
	var stylesSelect = $('<select></select>');

	var i = 1;
	stylesDiv.children().each(function() {
		stylesSelect.append(
			$('<option></option>')
				.text(this.innerText.replace(/(^\[|\]$)/g, ''))
				.val(i)
		);
		$(this).attr('id', 'style-select-' + i);
		i++;
	});

	stylesSelect.change(function() {
		$('#style-select-' + $(this).val()).click();
	});

	stylesDiv.hide();

	stylesDiv.after(
		$('

')
			.text(_('Style: '))
			.append(stylesSelect)
	);
});

/*
 * style-select.js
 * https://github.com/savetheinternet/Tinyboard/blob/master/js/style-select.js
 *
 * Changes the stylesheet chooser links to a <select>
 *
 * Released under the MIT license
 * Copyright (c) 2013 Michael Save <savetheinternet@tinyboard.org>
 *
 * Usage:
 *   $config['additional_javascript'][] = 'js/jquery.min.js';
 *   $config['additional_javascript'][] = 'js/style-select.js';
 *
 */

onready(function(){
	var stylesDiv = $('div.styles');
	var stylesSelect = $('<select></select>');

	var i = 1;
	stylesDiv.children().each(function() {
		var opt = $('<option></option>')
			.text(this.innerText.replace(/(^\[|\]$)/g, ''))
			.val(i);
		if ($(this).hasClass('selected'))
			opt.attr('selected', true);
		stylesSelect.append(opt);
		$(this).attr('id', 'style-select-' + i);
		i++;
	});

	stylesSelect.change(function() {
		$('#style-select-' + $(this).val()).click();
	});

	stylesDiv.hide();

	stylesDiv.after(
		$('

')
			.text(_('Style: '))
			.append(stylesSelect)
	);
});

Select chosen stylesheet on load


(() => {
  const $input = document.querySelector('#pull_request_body');
  if (!$input || $input.textLength > 0) {
    return;
  }

  jiraLinks = [];
  document.querySelectorAll('.commit-message').forEach((commitMessage) => {
    commitMessage.textContent.match(/BSD-\d+/g).forEach((jiraId) => {
      jiraLinks.push(`[${jiraId}](https://buildout.atlassian.net/browse/${jiraId})`);
    })
  });

  if (jiraLinks.length > 0) {
    $input.value = jiraLinks.join("\n") + "\n\n"
  }
})();

(() => {
  const $title = document.querySelector('#pull_request_title');
  const $description = document.querySelector('#pull_request_body');
  if (!$title || !$description) {
    return;
  }

  const idSet = new Set()
  document.querySelectorAll('.commit-message').forEach((commitMessage) => {
    match = commitMessage.textContent.match(/BSD-\d+/g);
    if (match) {
      match.forEach((id) => idSet.add(id));
    }
  });

  if (idSet.size > 0) {
    const ids = [];

    let anyIdsInTitle = false;
    let description = "";

    idSet.forEach((id) => {
      ids.push(id);
      description += `[${id}](https://buildout.atlassian.net/browse/${id})\n`;

      if ($title.value.indexOf(id) > -1) {
        anyIdsInTitle = true;
      }
    });

    if (!anyIdsInTitle) {
      $title.value = `${ids.join(", ")}: `;
    }

    if ($description.textLength === 0) {
      $description.value = `${description}\n`;
    }
  }
})();

Fix title handling for multiple tags


import React from 'react'

export const Checkbox = ({ id, nodes, onToggle }) => {
  const node = nodes[id]
  const { key, childIds, checked } = node

  const handleChange = () => {
    onToggle(id)
  }

  return (
    <React.Fragment key={id}>
      {key &&

          <input type='checkbox' checked={checked} className='pointer'
            onChange={handleChange} />
          {key}

}
      {childIds.length ?
        (

          {childIds.map((childId) => {
            return (<Checkbox key={childId} id={childId}
              nodes={nodes} onToggle={onToggle} />)
          })}

)
        :
        null}
    </React.Fragment>
  )
}

import React from 'react'

export const Checkbox = ({ id, nodes, onToggle }) => {
  const node = nodes[id]
  const { key, childIds, checked } = node

  const handleChange = () => onToggle(id)

  return (
    <React.Fragment key={id}>
      {key &&

          <input type='checkbox' checked={checked} className='pointer'
            onChange={handleChange} />
          {key}

}
      {childIds.length ?
        (

          {childIds.map((childId) => {
            return (<Checkbox key={childId} id={childId}
              nodes={nodes} onToggle={onToggle} />)
          })}

)
        :
        null}
    </React.Fragment>
  )
}

Remove braces for one line functions


$(document).ready(function() {
  $('#upload_form').change(function () {
    $('#upload_form_label').hide();
    $('#upload_form').hide();
    $('#upload_progress').show();
    $('#upload_form').submit();
  });

  $('#propagate_races').live('ajax:beforeSend', function() {
    $('#propagate_races').hide();
    $('#propagate_races_progress').show();
  });

  $('#destroy_races').live('ajax:beforeSend', function() {
    $('#destroy_races').hide();
    $('#destroy_races_progress').show();
  });

  $('#edit_promoter_link').click(function() {
    window.location.href = '/admin/people/' + $('#event_promoter_id').val() + '/edit?event_id=' + $('#edit_promoter_link').attr('data-event-id');
    return false;
  });
});

$(document).ready(function() {
  $('#upload_form').change(function () {
    $('#upload_form_label').hide();
    $('#upload_form').hide();
    $('#upload_progress').show();
    $('#upload_form').submit();
  });

  $('#propagate_races').bind('ajax:beforeSend', function() {
    $('#propagate_races').hide();
    $('#propagate_races_progress').show();
  });

  $('#destroy_races').bind('ajax:beforeSend', function() {
    $('#destroy_races').hide();
    $('#destroy_races_progress').show();
  });

  $('#edit_promoter_link').click(function() {
    window.location.href = '/admin/people/' + $('#event_promoter_id').val() + '/edit?event_id=' + $('#edit_promoter_link').attr('data-event-id');
    return false;
  });
});

Use jquery-rails bind() in place of live()


import reducer from './';

describe('Modal reducer', () => {
  let initialState;
  let state;
  let actionType;
  let payload;

  const callReducer = () => reducer(state, { type: actionType, payload });

  beforeEach(() => {
    initialState = {
      isOpen: false,
      modalName: '',
      modalOptions: {}
    };
  });

  it('returns initial state', () => {
    expect(callReducer()).toEqual(initialState);
  });

  context('when state is present', () => {
    beforeEach(() => {
      state = initialState;

      payload = {
        name: 'Awesome Modal',
        someOption: 'Some option value'
      };
    });

    describe('OPEN_MODAL', () => {
      beforeEach(() => {
        actionType = 'OPEN_MODAL';
      });

      it('returns new state', () => {
        expect(callReducer()).toEqual({
          isOpen: true,
          modalName: 'Awesome Modal',
          modalOptions: {
            someOption: 'Some option value'
          }
        });
      });
    });

    describe('CLOSE_MODAL', () => {
      beforeEach(() => {
        actionType = 'CLOSE_MODAL';

        state = {
          isOpen: true,
          modalName: 'Awesome Modal',
          modalOptions: {
            someOption: 'Some option value'
          }
        };
      });

      it('returns initial state', () => {
        expect(callReducer()).toEqual({
          isOpen: false,
          modalName: '',
          modalOptions: {}
        });
      });
    });
  });
});

import reducer from './';

describe('Modal reducer', () => {
  let initialState;
  let state;
  let actionType;
  let payload;

  const callReducer = () => reducer(state, { type: actionType, payload });

  beforeEach(() => {
    initialState = {
      isOpen: false,
      modalName: '',
      modalOptions: {}
    };
  });

  it('returns initial state', () => {
    expect(callReducer()).toEqual(initialState);
  });

  context('when state is present', () => {
    beforeEach(() => {
      state = initialState;

      payload = {
        name: 'Awesome Modal',
        someOption: 'Some option value'
      };
    });

    describe('OPEN_MODAL', () => {
      beforeEach(() => {
        actionType = 'OPEN_MODAL';
      });

      it('returns new state', () => {
        expect(callReducer()).toEqual({
          isOpen: true,
          modalName: 'Awesome Modal',
          modalOptions: {
            someOption: 'Some option value'
          }
        });
      });
    });

    describe('CLOSE_MODAL', () => {
      beforeEach(() => {
        actionType = 'CLOSE_MODAL';

        state = {
          isOpen: true,
          modalName: 'Awesome Modal',
          modalOptions: {
            someOption: 'Some option value'
          }
        };
      });

      it('returns initial state', () => {
        expect(callReducer()).toEqual(initialState);
      });
    });
  });
});

Use initialState in Modal reducer spec


// webpack config file for develop sugar.js

module.exports = {
	'entry': './src/main/index',
	'output': {
		'path': './bundle',
		'library': 'Sugar',
		'libraryTarget': 'umd',
		'filename': 'sugar.js'
	},
	'module': {
		'loaders': [
			{
				'test': /\.js$/,
				'exclude': /(node_modules|bower_components)/,
				'loader': 'babel', // 'babel-loader' is also a legal name to reference
				'query': {
					'presets': ['es2015']
				}
			}
		]
	},
	'devtool': 'inline-source-map'
}

// webpack config file for develop sugar.js

module.exports = {
	'entry': './src/main/index',
	'output': {
		'path': './bundle',
		'library': 'Sugar',
		'libraryTarget': 'umd',
		'filename': 'sugar.js'
	},
	'module': {
		'loaders': [
			{
				'test': /\.js$/,
				'exclude': /(node_modules|bower_components)/,
				'loader': 'babel', // 'babel-loader' is also a legal name to reference
				'query': {
					'presets': ['es2015']
				}
			}
		]
	},
	'devtool': 'source-map'
}

Change webpack devtool to source-map.


daysUntilExpiration = function() {
    var daysToWait = 1;
    var daysAgo = new Date();
    daysAgo.setDate(daysAgo.getDate()-daysToWait);
    return daysAgo;
}

daysUntilExpiration = function() {
    var daysToWait = 90;
    var daysAgo = new Date();
    daysAgo.setDate(daysAgo.getDate()-daysToWait);
    return daysAgo;
}

Change expiration to 90 days


/**
 * Created by ogiba on 20.08.2017.
 */

window.addEventListener("DOMContentLoaded", function () {
    setupScrollListener();
    setupAlertListener();
});

function setupScrollListener() {
    var navBar = $('#nav-bar');
    var titleBar = $('.app-title');
    var distance = navBar.offset().top;
    var titleBarDistane = titleBar.offset().top;

    $(window).scroll(function () {
        if ($(this).scrollTop() >= distance) {
            navBar.addClass("fixed-top");
        } else {
            navBar.removeClass("fixed-top");
        }
    });
}

function setupAlertListener() {
    $("#cookieInfo").on("close.bs.alert", function () {
        updateCookieInfo();
    });
}

function selectPage(pageNumber) {
    console.log(pageNumber);
    $.get("/?postPage=" + pageNumber, function (data) {
        $("#postsContainer").html("").html(data);
    });
}

function updateCookieInfo() {
    setCookies("cookieInfo", "displayed");
}

function getCookie(key) {
    let cookies = document.cookie;

    if (!cookies.includes(key)) {
        return null;
    }

    let values = cookies.split(";");
    let cookieValue = values.find(t => t.trim().startsWith(key));
    return cookieValue.replace(key, "");
}

function setCookies(key, value) {
    document.cookie = `${key}=${value}`;

    return document.cookie.includes(key);
}

/**
 * Created by ogiba on 20.08.2017.
 */

window.addEventListener("DOMContentLoaded", function () {
    setupScrollListener();
    setupAlertListener();
});

function setupScrollListener() {
    var navBar = $('#nav-bar');
    var titleBar = $('.app-title');
    var distance = navBar.offset().top;
    var titleBarDistane = titleBar.offset().top;

    $(window).scroll(function () {
        if ($(this).scrollTop() >= distance) {
            navBar.addClass("fixed-top");
        } else {
            navBar.removeClass("fixed-top");
        }
    });
}

function setupAlertListener() {
    let cookieALert = $("#cookieInfo");

    if (cookieALert.length) {
        cookieALert.on("close.bs.alert", function () {
            updateCookieInfo();
        });
    }
}

function selectPage(pageNumber) {
    console.log(pageNumber);
    $.get("/?postPage=" + pageNumber, function (data) {
        $("#postsContainer").html("").html(data);
    });
}

function updateCookieInfo() {
    setCookies("cookieInfo", "displayed");
}

function getCookie(key) {
    let cookies = document.cookie;

    if (!cookies.includes(key)) {
        return null;
    }

    let values = cookies.split(";");
    let cookieValue = values.find(t => t.trim().startsWith(key));
    return cookieValue.replace(key, "");
}

function setCookies(key, value) {
    document.cookie = `${key}=${value}`;

    return document.cookie.includes(key);
}

Fix setting close event error that ocurred when alert was not placed in view


angular.module( 'moviematch.match', [] )

.controller( 'MatchController', function( $scope, Match ) {
  $scope.movie = {};
  $scope.session = {};
  $scope.user = {};

  $scope.user.name = "Julie";

  $scope.session.name = "Girls Night Out";

  $scope.movie = {
    name: "Gone With The Wind",
    year: "1939",
    rating: "G",
    runtime: "3h 58m",
    genres: [ "Drama", "Romance", "War" ],
    country: "USA",
    poster_path: "https://www.movieposter.com/posters/archive/main/30/MPW-15446",
    summary: "A manipulative southern belle carries on a turbulent affair with a blockade runner during the American Civil War.",
    director: "Victor Fleming",
    cast: "Clark Cable, Vivian Leigh, Thomas Mitchell"
  }

  $scope.yes = Match.sendVote( $scope.session.id, $scope.user.id, $scope.movie.id, true );
  $scope.no = Match.sendVote( $scope.session.id, $scope.user.id, $scope.movie.id, false );
} );

angular.module( 'moviematch.match', [] )

.controller( 'MatchController', function( $scope, Match ) {
  $scope.movie = {};
  $scope.session = {};
  $scope.user = {};

  $scope.user.name = "Julie";

  $scope.session.name = "Girls Night Out";

  $scope.movie = {
    name: "Gone With The Wind",
    year: "1939",
    rating: "G",
    runtime: "3h 58m",
    genres: [ "Drama", "Romance", "War" ],
    country: "USA",
    poster_path: "https://www.movieposter.com/posters/archive/main/30/MPW-15446",
    summary: "A manipulative southern belle carries on a turbulent affair with a blockade runner during the American Civil War.",
    director: "Victor Fleming",
    cast: "Clark Cable, Vivian Leigh, Thomas Mitchell"
  }

  $scope.yes = function() { Match.sendVote( $scope.session.id, $scope.user.id, $scope.movie.id, true ); }
  $scope.no = function() { Match.sendVote( $scope.session.id, $scope.user.id, $scope.movie.id, false ); }
} );

Make yes/no function definitions rather than function executions


// relay_force_routing

// documentation via: haraka -h plugins/relay_force_routing

exports.register = function() {
    this.register_hook('get_mx', 'force_routing');
    this.domain_ini = this.config.get('relay_dest_domains.ini', 'ini');
};

exports.force_routing = function (next, hmail, domain) {
    var force_route = lookup_routing(this, this.domain_ini['domains'], domain);
    if (force_route != "NOTFOUND" ){
        this.logdebug(this, 'using ' + force_route + ' for ' + domain);
        next(OK, force_route);
    } else {
        this.logdebug(this, 'using normal MX lookup' + ' for ' + domain);
        next(CONT);
    }
};

/**
 * @return {string}
 */

function lookup_routing (plugin, domains_ini, domain) {
    if (domain in domains_ini) {
        var config = JSON.parse(domains_ini[domain]);
        plugin.logdebug(plugin, 'found config for' + domain + ': ' + domains_ini['nexthop']);
        return config['nexthop'];
    }
    return "NOTFOUND";
}

// relay_force_routing

// documentation via: haraka -h plugins/relay_force_routing

exports.hook_get_mx = function (next, hmail, domain) {
    var domain_ini = this.config.get('relay_dest_domains.ini', 'ini');
    var force_route = lookup_routing(domain_ini['domains'], domain);
    if (force_route != "NOTFOUND" ){
        this.logdebug('using ' + force_route + ' for: ' + domain);
        next(OK, force_route);
    } else {
        this.logdebug('using normal MX lookup for: ' + domain);
        next(CONT);
    }
}

/**
 * @return {string}
 */

function lookup_routing (domains_ini, domain) {
    if (domain in domains_ini) {
        var config = JSON.parse(domains_ini[domain]);
        return config['nexthop'];
    }
    return "NOTFOUND";
}

Change around so config is loaded when changed


function AudioButton() {
  var that = this;
  this.position = {
    x: 15.5,
    y: 0.5
  };

  if (typeof localStorage.soundActivated === 'undefined') {
    localStorage.soundActivated = "1";
  }

  this.on = !!localStorage.soundActivated;
  setTimeout(function() {
    createjs.Sound.setMute(!that.on);
    mm.music.volume = !!that.on * 0.4;
  }, 10);
}

AudioButton.prototype.render = function() {
  var sprite = this.on ? this.sprite_on : this.sprite_off;
  ctx.save();
  var scaler = sprite.width * GU * 0.00025;
  ctx.translate(this.position.x * GU, this.position.y * GU);
  ctx.scale(scaler, scaler);
  ctx.drawImage(sprite, -sprite.width / 2, -sprite.height / 2);
  ctx.restore();
};

AudioButton.prototype.pause = function() {
  this.musicElement.pause && this.musicElement.pause();
  localStorage.soundActivated = "";
};

AudioButton.prototype.toggleActivated = function() {
  this.on = !this.on;
  createjs.Sound.setMute(!this.on);
  mm.music.volume = !!this.on * 0.4;

  localStorage.soundActivated = this.on ? "1" : "";
};

function AudioButton() {
  var that = this;
  this.position = {
    x: 15.5,
    y: 0.4
  };

  if (typeof localStorage.soundActivated === 'undefined') {
    localStorage.soundActivated = "1";
  }

  this.on = !!localStorage.soundActivated;
  setTimeout(function() {
    createjs.Sound.setMute(!that.on);
    mm.music.volume = !!that.on * 0.4;
  }, 10);
}

AudioButton.prototype.render = function() {
  var sprite = this.on ? this.sprite_on : this.sprite_off;
  ctx.save();
  var scaler = sprite.width * GU * 0.00015;
  ctx.translate(this.position.x * GU, this.position.y * GU);
  ctx.scale(scaler, scaler);
  ctx.drawImage(sprite, -sprite.width / 2, -sprite.height / 2);
  ctx.restore();
};

AudioButton.prototype.pause = function() {
  this.musicElement.pause && this.musicElement.pause();
  localStorage.soundActivated = "";
};

AudioButton.prototype.toggleActivated = function() {
  this.on = !this.on;
  createjs.Sound.setMute(!this.on);
  mm.music.volume = !!this.on * 0.4;

  localStorage.soundActivated = this.on ? "1" : "";
};

Adjust audio button position, size


export const baseStyles = {};

export const centerStyles = {
  display: 'flex',
  alignItems: 'center',
  justifyContent: 'center'
};

export const baseStyles = {
};

export const centerStyles = {
  margin: '24px auto',
  maxWidth: '90%',
};

Modify baseStyles & centerStyles for a better centering of the content without flexbox


import React, { PropTypes } from 'react';
import { Card } from '../UI';
import styles from './ImageCard.css';

export default class ImageCard extends React.Component {

  render() {
    const { onClick, onImageLoaded, image, text, description } = this.props;
    return (
      <Card onClick={onClick} className={styles.root}>

{text}

        {description ?
{description}
 : null}
      </Card>
    );
  }
}

ImageCard.propTypes = {
  image: PropTypes.string,
  onClick: PropTypes.func,
  onImageLoaded: PropTypes.func,
  text: PropTypes.string.isRequired,
  description: PropTypes.string
};

ImageCard.defaultProps = {
  onClick: function() {},
  onImageLoaded: function() {}
};

Make cards have h2s not h1s


'use strict';

module.exports = function (err, data) {

  if (err) {
    return 'Debug output: %j' + data + '\n' + JSON.stringify(err);
  }

  if (!data.length) {
    return;
  }

  var returnString = '';
  for (var i = 0, il = data.length; i < il; ++i) {
    returnString += JSON.stringify({
      type: 'issue',
      check_name: 'Vulnerable module "' + data[i].module + '" identified',
      description: '`' + data[i].module + '` ' + data[i].title,
      categories: ['Security'],
      content: {
        body: data[i].content
      },
      location: {
        path: 'npm-shrinkwrap.json',
        lines: {
          begin: data[i].line.start,
          end: data[i].line.end
        }
      }
    }) + '\0\n';
  }

  return returnString;
};

'use strict';

module.exports = function (err, data) {

  if (err) {
    return 'Debug output: %j' + data + '\n' + JSON.stringify(err);
  }

  if (!data.length) {
    return;
  }

  var returnString = '';
  for (var i = 0, il = data.length; i < il; ++i) {
    returnString += JSON.stringify({
      type: 'issue',
      check_name: 'Vulnerable module "' + data[i].module + '" identified',
      description: '`' + data[i].module + '` ' + data[i].title,
      categories: ['Security'],
      remediation_points: 300000,
      content: {
        body: data[i].content
      },
      location: {
        path: 'npm-shrinkwrap.json',
        lines: {
          begin: data[i].line.start,
          end: data[i].line.end
        }
      }
    }) + '\0\n';
  }

  return returnString;
};

Add Remediation Points to issues


var AppWorkerInfo = function(appSpec, listenPort, logFilePath, exitPromise) {
  this.appSpec = appSpec;
  this.listenPort = listenPort;
  this.logFilePath = logFilePath;
  this.exitPromise = exitPromise;
};
module.exports = AppWorkerInfo;

(function() {

}).call(AppWorkerInfo.prototype);

var events = require('events');
var util = require('util');

var AppWorkerInfo = function(appSpec, listenPort, logFilePath, exitPromise) {
  events.EventEmitter.call(this);
  this.appSpec = appSpec;
  this.listenPort = listenPort;
  this.logFilePath = logFilePath;
  this.exitPromise = exitPromise;

  this.$refCount = 0;
};
module.exports = AppWorkerInfo;

util.inherits(AppWorkerInfo, events.EventEmitter);

(function() {

  /**
   * Increment reference count.
   *
   * Call when a client is using this worker. This prevents the worker from
   * being reaped. Call release() when the client is done using the worker.
   */
  this.acquire = function() {
    this.$refCount++;
    this.emit('acquire', this.$refCount);
  };

  /**
   * Decrement reference count.
   *
   * Call when a client is done using this worker. This allows the worker to
   * potentially be reaped if the refcount is zero.
   */
  this.release = function() {
    this.$refCount--;
    this.emit('release', this.$refCount);
  };

}).call(AppWorkerInfo.prototype);

Add acquire/release to worker info


module.exports = function(robot){

	var pugBombReplies = [
		'There will be no more of that.  Take your {{number}} pugs elsewhere.',
		'Nope.  Nobody needs to see that many pugs.',
		'Why do you think we all need to see {{number}} pugs?',
		'No pugs for you!',
		'{{number}} pugs... {{number}} PUGS??  Absolutely not.'
	];

	robot.respond(/pug bomb (.*)/i, function(res){
		var number = res.match[1],
			index = Math.floor(Math.random() * pugBombReplies.length),
			reply = pugBombReplies[index].replace(/{{number}}/g, number.trim());

		res.reply(reply);
	});

}

module.exports = function(robot){

	var pugBombReplies = [
		'There will be no more of that.  Take your {{number}} pugs elsewhere.',
		'Nope.  Nobody needs to see that many pugs.',
		'Why do you think we all need to see {{number}} pugs?',
		'No pugs for you!',
		'{{number}} pugs... {{number}} PUGS??  Absolutely not.',
		'http://i.imgur.com/cUJd5aO.jpg'
	];

	robot.respond(/pug bomb (.*)/i, function(res){
		var number = res.match[1],
			index = Math.floor(Math.random() * pugBombReplies.length),
			reply = pugBombReplies[index].replace(/{{number}}/g, number.trim());

		res.reply(reply);
	});

}

Add lumberg meme to pug bomb replies


const { APIMessage } = require('discord.js');
const { Extendable } = require('klasa');
const { TextChannel, DMChannel, GroupDMChannel, User } = require('discord.js');

module.exports = class extends Extendable {

	constructor(...args) {
		super(...args, { appliesTo: [TextChannel, DMChannel, GroupDMChannel, User] });
	}

	sendCode(code, content, options = {}) {
		return this.send(APIMessage.transformOptions(content, options, { code }));
	}

	sendEmbed(embed, content, options = {}) {
		return this.send(APIMessage.transformOptions(content, options, { embed }));
	}

	sendFile(attachment, name, content, options = {}) {
		return this.send(APIMessage.transformOptions(content, options, { files: [{ attachment, name }] }));
	}

	sendFiles(files, content, options = {}) {
		return this.send(APIMessage.transformOptions(content, options, { files }));
	}

	sendLocale(key, args = [], options = {}) {
		if (!Array.isArray(args)) [options, args] = [args, []];
		const language = this.guild ? this.guild.language : this.client.languages.default;
		return this.send({ content: language.get(key, ...args), ...options });
	}

	sendMessage(content, options) {
		return this.send(content, options);
	}

};

const { APIMessage } = require('discord.js');
const { Extendable } = require('klasa');
const { TextChannel, DMChannel, GroupDMChannel, User } = require('discord.js');

module.exports = class extends Extendable {

	constructor(...args) {
		super(...args, { appliesTo: [TextChannel, DMChannel, GroupDMChannel, User] });
	}

	sendCode(code, content, options = {}) {
		return this.send(APIMessage.transformOptions(content, options, { code }));
	}

	sendEmbed(embed, content, options = {}) {
		return this.send(APIMessage.transformOptions(content, options, { embed }));
	}

	sendFile(attachment, name, content, options = {}) {
		return this.send(APIMessage.transformOptions(content, options, { files: [{ attachment, name }] }));
	}

	sendFiles(files, content, options = {}) {
		return this.send(APIMessage.transformOptions(content, options, { files }));
	}

	sendLocale(key, args = [], options = {}) {
		if (!Array.isArray(args)) [options, args] = [args, []];
		const language = this.guild ? this.guild.language : this.client.languages.default;
		return this.send(language.get(key, ...args), options);
	}

	sendMessage(content, options) {
		return this.send(content, options);
	}

};

Fix [object Object] in sendLocale extendable


/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.menu',
  name: 'SubMenu',
  extends: 'foam.nanos.menu.AbstractMenu',

  requires: [ 'foam.nanos.menu.SubMenuView' ],

  methods: [
    // Code below is for conventional style menus which pop-up,
    // which no longer happens as opening a menu just opens the tree view.
    /*
    function createView(X, menu, parent) {
      return this.SubMenuView.create({menu: menu, parent: parent}, X);
    },

    function launch(X, menu, parent) {
      var view = this.createView(X, menu, parent);
      view.open();
    }
    */
  ]
});

/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.menu',
  name: 'SubMenu',
  extends: 'foam.nanos.menu.AbstractMenu',

  requires: [ 'foam.nanos.menu.SubMenuView' ],

  methods: [
    function launch() { /** NOP **/ }
    // Code below is for conventional style menus which pop-up,
    // which no longer happens as opening a menu just opens the tree view.
    /*
    function createView(X, menu, parent) {
      return this.SubMenuView.create({menu: menu, parent: parent}, X);
    },

    function launch(X, menu, parent) {
      var view = this.createView(X, menu, parent);
      view.open();
    }
    */
  ]
});

Fix for my last menu fix.


const webpack = require('webpack')
const merge = require('webpack-merge')
const base = require('./webpack.base.config')
const path = require('path')
const VueSSRClientPlugin = require('vue-server-renderer/client-plugin')

const config = merge(base, {
  output: {
    path: path.resolve(__dirname, '../../dist'),
    publicPath: '/dist/',
    filename: '[name].js'
  },
  optimization: {
    splitChunks:  {
      cacheGroups: {
        commons: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendor',
          chunks: 'all',
        },
      },
    },
    runtimeChunk: {
      name: "manifest",
    }
  },
  mode: 'development',
  resolve: {
    alias: {
      'create-api': './create-api-client.js'
    }
  },
  plugins: [
    // strip dev-only code in Vue source
    new webpack.DefinePlugin({
      'process.env.VUE_ENV': '"client"'
    }),
    new VueSSRClientPlugin()
  ]
})

module.exports = config;

const webpack = require('webpack')
const merge = require('webpack-merge')
const base = require('./webpack.base.config')
const path = require('path')
const VueSSRClientPlugin = require('vue-server-renderer/client-plugin')

const config = merge(base, {
  optimization: {
    splitChunks:  {
      cacheGroups: {
        commons: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendor',
          chunks: 'all',
        },
      },
    },
    runtimeChunk: {
      name: "manifest",
    }
  },
  mode: 'development',
  resolve: {
    alias: {
      'create-api': './create-api-client.js'
    }
  },
  plugins: [
    // strip dev-only code in Vue source
    new webpack.DefinePlugin({
      'process.env.VUE_ENV': '"client"'
    }),
    new VueSSRClientPlugin()
  ]
})

module.exports = config;

Revert "Minor - webpack client filename change"


const axios = require('axios');
const chalk = require('chalk');

const helpers = require('../helpers');

exports.listArticles = function (argv) {
  const articleCount = argv.limit > 1 ? argv.limit : 1;

  axios.get('https://api.spaceflightnewsapi.net/articles/', {
    params: {
      limit: articleCount
    }
  }).then((response) => {
    const articles = response.data;

    const articlesByDate = articles.reduce((byDate, article) => {
      const title = chalk`${article.title}`;

      const publishedAt = new Date(article.date_published * 1000).toLocaleDateString();
      const articleDetails = chalk`{yellow ${article.news_site_long}}`;

      const readSource = `Read on ${article.url}`;

      const dayArticles = byDate[publishedAt] = byDate[publishedAt] || [];
      dayArticles.push(`${articleDetails} | ${title}\n${readSource}\n`);

      return byDate;
    }, {});

    for (const date in articlesByDate) {
      const dayArticles = articlesByDate[date];

      helpers.printMessage(`${date}\n----------`);
      dayArticles.forEach(article => helpers.printMessage(article));
    }
  }).catch(error => {
    const errorMessage = `${error.code}: ${error.message}`;
    helpers.printError(errorMessage);
  });
};

const axios = require('axios');
const chalk = require('chalk');

const helpers = require('../helpers');

exports.listArticles = function (argv) {
  const articleCount = argv.limit > 1 ? argv.limit : 1;

  axios.get('https://api.spaceflightnewsapi.net/articles/', {
    params: {
      limit: articleCount
    }
  }).then((response) => {
    const articles = response.data;

    const articlesByDate = articles.reduce((byDate, article) => {
      const title = chalk`${article.title}`;

      const publishedAt = new Date(article.date_published * 1000).toLocaleDateString();
      const articleDetails = chalk`{yellow ${article.news_site_long}}`;

      const readSource = `Read on ${article.url}`;

      const dayArticles = byDate[publishedAt] = byDate[publishedAt] || [];
      dayArticles.push(`${articleDetails} | ${title}\n${readSource} \n`);

      return byDate;
    }, {});

    for (const date in articlesByDate) {
      const dayArticles = articlesByDate[date];

      helpers.printMessage(`${date}\n----------`);
      dayArticles.forEach(article => helpers.printMessage(article));
    }
  }).catch(error => {
    const errorMessage = `${error.code}: ${error.message}`;
    helpers.printError(errorMessage);
  });
};

Fix new line being included in an anchor


import 'react-native-browser-polyfill';
import App from './app/App.react';
import Component from 'react-pure-render/component';
import React, { AppRegistry, Platform } from 'react-native';
import config from './config';
import configureStore from '../common/configureStore';
import createEngine from 'redux-storage-engine-reactnativeasyncstorage';
import messages from './messages';
import { Provider } from 'react-redux';

require('../server/intl/polyfillLocales')(self, config.locales);

export default function index() {
  const initialState = {
    config: {
      appName: config.appName,
      firebaseUrl: config.firebaseUrl
    },
    intl: {
      // TODO: Detect native current locale.
      currentLocale: config.defaultLocale,
      locales: config.locales,
      messages
    },
    device: {
      platform: Platform.OS
    }
  };
  const store = configureStore({ createEngine, initialState });

  class Root extends Component {
    render() {
      return (
        <Provider store={store}>
          <App />
        </Provider>
      );
    }
  }

  AppRegistry.registerComponent('Este', () => Root);
}

import 'react-native-browser-polyfill';
import App from './app/App.react';
import Component from 'react-pure-render/component';
import React, { AppRegistry, Platform } from 'react-native';
import config from './config';
import configureStore from '../common/configureStore';
import createEngine from 'redux-storage-engine-reactnativeasyncstorage';
import messages from './messages';
import { Provider } from 'react-redux';

require('../server/intl/polyfillLocales')(self, config.locales);

export default function index() {
  const initialState = {
    config: {
      appName: config.appName,
      firebaseUrl: config.firebaseUrl
    },
    intl: {
      // TODO: Detect native current locale.
      currentLocale: config.defaultLocale,
      initialNow: Date.now(),
      locales: config.locales,
      messages
    },
    device: {
      platform: Platform.OS
    }
  };
  const store = configureStore({ createEngine, initialState });

  class Root extends Component {
    render() {
      return (
        <Provider store={store}>
          <App />
        </Provider>
      );
    }
  }

  AppRegistry.registerComponent('Este', () => Root);
}

Add missing initialNow for native


import options from './options';
import { defer } from './util';
import { renderComponent } from './vdom/component';

/** Managed queue of dirty components to be re-rendered */

// items/itemsOffline swap on each rerender() call (just a simple pool technique)
let items = [],
	itemsOffline = [];

export function enqueueRender(component) {
	if (!component._dirty && (component._dirty = true) && items.push(component)==1) {
		(options.debounceRendering || defer)(rerender);
	}
}

export function rerender() {
	if (!items.length) return;

	let currentItems = items,
		p;

	// swap online & offline
	items = itemsOffline;
	itemsOffline = currentItems;

	while ( (p = currentItems.pop()) ) {
		if (p._dirty) renderComponent(p);
	}
}

import options from './options';
import { defer } from './util';
import { renderComponent } from './vdom/component';

/** Managed queue of dirty components to be re-rendered */

// items/itemsOffline swap on each rerender() call (just a simple pool technique)
let items = [],
	itemsOffline = [];

export function enqueueRender(component) {
	if (!component._dirty && (component._dirty = true) && items.push(component)==1) {
		(options.debounceRendering || defer)(rerender);
	}
}

export function rerender() {

	let currentItems = items,
		p;

	// swap online & offline
	items = itemsOffline;
	itemsOffline = currentItems;

	while ( (p = currentItems.pop()) ) {
		if (p._dirty) renderComponent(p);
	}
}

Remove unnecessary early exit for a condition that rarely/never occurs.


/* eslint-disable no-unused-vars*/
import React from 'react';
/* eslint-enable no-unused-vars*/
import { Router, Route, IndexRoute } from 'react-router';
import createBrowserHistory from 'history/lib/createBrowserHistory';

import App from 'containers/App/';
import Home from 'containers/Home/';

const Routes = () => (
  <Router history={createBrowserHistory()}>
    <Route path="/" component={App}>
      <IndexRoute component={Home} />
    </Route>
  </Router>
);

export default Routes;

/* eslint-disable no-unused-vars*/
import React from 'react';
/* eslint-enable no-unused-vars*/
import { Router, Route, IndexRoute } from 'react-router';
import createBrowserHistory from 'history/lib/createBrowserHistory';

import App from './App/';
import Home from './Home/';

const Routes = () => (
  <Router history={createBrowserHistory()}>
    <Route path="/" component={App}>
      <IndexRoute component={Home} />
    </Route>
  </Router>
);

export default Routes;

Change import path of routes


var TODO_COMPONENT = `
var TodoList = React.createClass({
  render: function() {
    var createItem = function(itemText, index) {
      return
{itemText}
;
    };
    return
{this.props.items.map(createItem)}
;
  }
});
var TodoApp = React.createClass({
  getInitialState: function() {
    return {items: [], text: ''};
  },
  onChange: function(e) {
    this.setState({text: e.target.value});
  },
  handleSubmit: function(e) {
    e.preventDefault();
    var nextItems = this.state.items.concat([this.state.text]);
    var nextText = '';
    this.setState({items: nextItems, text: nextText});
  },
  render: function() {
    return (

TODO

        <TodoList items={this.state.items} />
        <form onSubmit={this.handleSubmit}>
          <input onChange={this.onChange} value={this.state.text} />
          <button>{'Add #' + (this.state.items.length + 1)}</button>
        </form>

    );
  }
});

ReactDOM.render(<TodoApp />, mountNode);
`;

ReactDOM.render(
  <ReactPlayground codeText={TODO_COMPONENT} />,
  document.getElementById('todoExample')
);

var TODO_COMPONENT = `
var TodoList = React.createClass({
  render: function() {
    var createItem = function(item) {
      return
{item.text}
;
    };
    return
{this.props.items.map(createItem)}
;
  }
});
var TodoApp = React.createClass({
  getInitialState: function() {
    return {items: [], text: ''};
  },
  onChange: function(e) {
    this.setState({text: e.target.value});
  },
  handleSubmit: function(e) {
    e.preventDefault();
    var nextItems = this.state.items.concat([{text: this.state.text, id: Date.now()}]);
    var nextText = '';
    this.setState({items: nextItems, text: nextText});
  },
  render: function() {
    return (

TODO

        <TodoList items={this.state.items} />
        <form onSubmit={this.handleSubmit}>
          <input onChange={this.onChange} value={this.state.text} />
          <button>{'Add #' + (this.state.items.length + 1)}</button>
        </form>

    );
  }
});

ReactDOM.render(<TodoApp />, mountNode);
`;

ReactDOM.render(
  <ReactPlayground codeText={TODO_COMPONENT} />,
  document.getElementById('todoExample')
);

Use id for TodoApp example


import ObjectProxy from '@ember/object/proxy';
import { merge } from '@ember/polyfills';

/**
 * @class Row
 * @extends Ember.ObjectProxy
 * @namespace SemanticUI
 */
const Row = ObjectProxy.extend({
  /**
   * Whether the row is currently selected.
   *
   * @property selected
   * @type Boolean
   * @default false
   * @public
   */
  selected: false,

  /**
   * Whether the row is currently expanded.
   *
   * @property expanded
   * @type Boolean
   * @default false
   * @public
   */
  expanded: false,

  /**
   * Whether thw row is currently being edited.
   *
   * @property editing
   * @type Boolean
   * @default false
   * @public
   */
  editing: false,

  /**
   * Content for this row. See {{#crossLink "Ember.ObjectProxy"}}{{/crossLink}}.
   *
   * @property content
   * @type Object
   * @public
   */
  content: null
});

Row.reopenClass({
  /**
   * @method create
   * @param {Object} content Content for this row.
   * @param {Object} options Properties to initialize in this object.
   * @return Row
   * @public
   * @static
   */
  create(content, options = {}) {
    let _options = merge({ content }, options);
    return this._super(_options);
  }
});

export default Row;

import ObjectProxy from '@ember/object/proxy';
import { assign } from '@ember/polyfills';

/**
 * @class Row
 * @extends Ember.ObjectProxy
 * @namespace SemanticUI
 */
const Row = ObjectProxy.extend({
  /**
   * Whether the row is currently selected.
   *
   * @property selected
   * @type Boolean
   * @default false
   * @public
   */
  selected: false,

  /**
   * Whether the row is currently expanded.
   *
   * @property expanded
   * @type Boolean
   * @default false
   * @public
   */
  expanded: false,

  /**
   * Whether thw row is currently being edited.
   *
   * @property editing
   * @type Boolean
   * @default false
   * @public
   */
  editing: false,

  /**
   * Content for this row. See {{#crossLink "Ember.ObjectProxy"}}{{/crossLink}}.
   *
   * @property content
   * @type Object
   * @public
   */
  content: null
});

Row.reopenClass({
  /**
   * @method create
   * @param {Object} content Content for this row.
   * @param {Object} options Properties to initialize in this object.
   * @return Row
   * @public
   * @static
   */
  create(content, options = {}) {
    const _options = assign({ content }, options);
    return this._super(_options);
  }
});

export default Row;

Replace usage of merge with assign


/**
 * A simple Error type to represent an error that occured while loading a
 * resource.
 *
 * @class LoadError
 * @extends Error
 */
export default class LoadError extends Error {
  /**
   * Constructs a new LoadError with a supplied error message and an instance
   * of the AssetLoader service to use when retrying a load.
   *
   * @param {String} message
   * @param {AssetLoader} assetLoader
   */
  constructor(message, assetLoader) {
    super(...arguments);
    this.name = 'LoadError';
    this.message = message;
    this.loader = assetLoader;
  }

  /**
   * An abstract hook to define in a sub-class that specifies how to retry
   * loading the errored resource.
   */
  retryLoad() {
    throw new Error(`You must define a behavior for 'retryLoad' in a subclass.`);
  }

  /**
   * Invokes a specified method on the AssetLoader service and caches the
   * result. Should be used in implementations of the retryLoad hook.
   *
   * @protected
   */
  _invokeAndCache(method, ...args) {
    return this._retry || (this._retry = this.loader[method](...args));
  }
}

function ExtendBuiltin(klass) {
  function ExtendableBuiltin() {
    klass.apply(this, arguments);
  }

  ExtendableBuiltin.prototype = Object.create(klass.prototype);
  ExtendableBuiltin.prototype.constructor = ExtendableBuiltin;
  return ExtendableBuiltin;
}

/**
 * A simple Error type to represent an error that occured while loading a
 * resource.
 *
 * @class LoadError
 * @extends Error
 */
export default class LoadError extends ExtendBuiltin(Error) {
  /**
   * Constructs a new LoadError with a supplied error message and an instance
   * of the AssetLoader service to use when retrying a load.
   *
   * @param {String} message
   * @param {AssetLoader} assetLoader
   */
  constructor(message, assetLoader) {
    super(...arguments);
    this.name = 'LoadError';
    this.message = message;
    this.loader = assetLoader;
  }

  /**
   * An abstract hook to define in a sub-class that specifies how to retry
   * loading the errored resource.
   */
  retryLoad() {
    throw new Error(`You must define a behavior for 'retryLoad' in a subclass.`);
  }

  /**
   * Invokes a specified method on the AssetLoader service and caches the
   * result. Should be used in implementations of the retryLoad hook.
   *
   * @protected
   */
  _invokeAndCache(method, ...args) {
    return this._retry || (this._retry = this.loader[method](...args));
  }
}

Make it possible to extend from `Error` in Babel 6.


$(document).ready(function() {
    $('#voterloginbox').submit(function(event) {
        var formData = {
            'voter_token'          : $('input[name=voter_token]').val(),
            '_csrf'                : $('input[name=_csrf]').val(),
            'g-recaptcha-response' : $('textarea[name=g-recaptcha-response]').val()
        };

        $('#voterloginbox').hide("slow");

        var formsub = $.ajax({
            type        : 'POST',
            url         : '/endpoints/process_voter_login.php',
            data        : formData,
            dataType    : 'json',
            encode      : true }
        });

        formsub.done(function(data) {
            if (data.success) {
              setTimeout(function() { window.location.href = "/vote"; }, 2000);
            }
        });

        event.preventDefault();
    });
});

$(document).ready(function() {
    $("#voter_token").keyup(function(event){
        if(event.keyCode == 13){
            $("#next_btn").click();
        }
    });

    $('#voterloginbox').submit(function(event) {
        var formData = {
            'voter_token'          : $('input[name=voter_token]').val(),
            '_csrf'                : $('input[name=_csrf]').val(),
            'g-recaptcha-response' : $('textarea[name=g-recaptcha-response]').val()
        };

        $('#voterloginbox').hide("slow");

        var formsub = $.ajax({
            type        : 'POST',
            url         : '/endpoints/process_voter_login.php',
            data        : formData,
            dataType    : 'json',
            encode      : true }
        });

        formsub.done(function(data) {
            if (data.success) {
              setTimeout(function() { window.location.href = "/vote"; }, 2000);
            }
        });

        event.preventDefault();
    });
});

Test out having enter press bring up modal


module.exports = {
  "extends": [
    "./index.js",
  ],

  plugins: ["fp"],

  rules: {
    "fp/no-arguments": "error",
    "fp/no-class": "warn",
    "fp/no-delete": "error",
    "fp/no-events": "error",
    "fp/no-get-set": "error",
    "fp/no-let": "error",
    "fp/no-loops": "error",
    "fp/no-mutating-assign": "error",
    "fp/no-mutating-methods": "error",
    "fp/no-mutation": ["error", {commonjs:true}],
    "fp/no-nil": "error",
    "fp/no-proxy": "error",
    "fp/no-rest-parameters": "warn",
    "fp/no-this": "warn",
    "fp/no-throw": "warn",
    "fp/no-unused-expression": "warn",
    "fp/no-valueof-field": "error",
    "no-new-object": "warn",
    "no-magic-numbers": [2, {  "ignore": [0,1] }],
    "no-unused-expressions": 2, // disallow usage of expressions in statement position
    "no-lonely-if": 2, // disallow if as the only statement in an else block
  },
}

module.exports = {
  "extends": [
    "./index.js",
  ],

  plugins: ["fp"],

  rules: {
    "fp/no-arguments": "error",
    "fp/no-class": "warn",
    "fp/no-delete": "error",
    "fp/no-events": "error",
    "fp/no-get-set": "error",
    "fp/no-let": "error",
    "fp/no-loops": "error",
    "fp/no-mutating-assign": "error",
    "fp/no-mutating-methods": "error",
    "fp/no-mutation": ["error", {commonjs:true}],
    "fp/no-nil": "error",
    "fp/no-proxy": "error",
    "fp/no-rest-parameters": "warn",
    "fp/no-this": "warn",
    "fp/no-throw": "warn",
    "fp/no-unused-expression": "warn",
    "fp/no-valueof-field": "error",
    "no-new-object": "warn",
    "no-magic-numbers": [2, {  "ignore": [0,1] }],
    "no-unused-expressions": 2, // disallow usage of expressions in statement position
    "no-lonely-if": 2, // disallow if as the only statement in an else block
    "sort-keys": [1, "asc", {"caseSensitive": true, "natural": false}],
  },
}

Add a warning for sorting keys


// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery
//= require jquery_ujs
//= require jquery.kinetic
//= require_tree .

// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery
//= require jquery-ui
//= require jquery_ujs
//= require jquery.kinetic
//= require_tree .

Add jquery ui to app.js


import Em from 'ember';

let get = Em.get;
let defaultFilter = function(value) { return value; };

export default function(dependentKey, filter) {
  filter = filter || defaultFilter;

  let computed = Em.computed(function handler(key) {
    let lastValueKey = `__changeGate${key}LastValue`;

    let isFirstRun = !this.hasOwnProperty(lastValueKey);
    if (isFirstRun) { //setup an observer which is responsible for notifying property changes
      this[lastValueKey] = filter.call(this, get(this, dependentKey));

      this.addObserver(dependentKey, function() {
        let newValue = filter.call(this, get(this, dependentKey));
        let lastValue = this[lastValueKey];

        if(newValue !== lastValue) {
          this[lastValueKey] = newValue;
          this.notifyPropertyChange(key);
        }
      });
    }

    return this[lastValueKey];
  });

  return computed;
}

import Em from 'ember';

let get = Em.get;
let defaultFilter = function(value) { return value; };

export default function(dependentKey, filter) {
  filter = filter || defaultFilter;

  let computed = Em.computed(function handler(key) {
    let lastValueKey = `__changeGate${key}LastValue`;

    let isFirstRun = !this.hasOwnProperty(lastValueKey);
    if (isFirstRun) { //setup an observer which is responsible for notifying property changes
      this[lastValueKey] = filter.call(this, get(this, dependentKey));

      this.addObserver(dependentKey, function() {
        let newValue = filter.call(this, get(this, dependentKey));
        let lastValue = this[lastValueKey];

        if(!Em.isEqual(newValue, lastValue)) {
          this[lastValueKey] = newValue;
          this.notifyPropertyChange(key);
        }
      });
    }

    return this[lastValueKey];
  });

  return computed;
}

Use Ember.isEqual instead of direct equality


import CoreObject from 'fantasy-weather/models/_core/core-object';
import Team from 'fantasy-weather/models/team';

let League = CoreObject.extend({

  // Set defaults and stub out properties
  _defaults() {
    return {
      name: "",
      teams: []
    };
  },

  addTeam(team) {
    let teams = this.get('teams');
    teams.push(Team.create(team));
    return teams;
  },

  addTeams(teams) {
    _.forEach(teams, this.addTeam.bind(this));
    return teams;
  }

});

League.reopenClass({
});

export default League;

import CoreObject from 'fantasy-weather/models/_core/core-object';
import Team from 'fantasy-weather/models/team';

let League = CoreObject.extend({

  // Set defaults and stub out properties
  _defaults() {
    return {
      name: "",
      teams: []
    };
  },

  addTeam(team) {
    let teams = this.get('teams');
    teams.push(Team.create(team));
    return teams;
  },

  addTeams(teams) {
    _.forEach(teams, this.addTeam.bind(this));
    return teams;
  },

  findTeamByAbbr(abbr) {
    let teams = this.get('teams');
    return _.find(teams, { abbr });
  },

  findStadiumByAbbr(abbr) {
    let team = this.findTeamByAbbr(abbr);
    return _.get(team, 'stadium.location');
  }

});

League.reopenClass({
});

export default League;

Add methods to find team/stadium by abbreviation


#!/usr/bin/env node
var recast = require('recast');
var messages = require('./transform/messages');
var exec = require('child_process').exec;
var fs = require('fs');
var glob = require('glob');
var usage = [
	'Usage: assertion-messages.js "globbing expression"',
	'To generate assertion messages for a set of test files, provide a valid glob expression.',
	'File search begins at `../test262/test/`'
].join('\n');

var gexpr = process.argv[2];

if (!gexpr) {
	console.log(usage);
	return;
}

glob('../test262/test/' + gexpr, function(err, list) {

	list.forEach(function(file) {
		var source = fs.readFileSync(file, 'utf8');

		try {
			var ast = recast.parse(source);
			var result = messages(ast);
			fs.writeFileSync(file, recast.print(ast).code, 'utf8');
		} catch (e) {
			console.log("failed: ", file);
		}
	});
});

#!/usr/bin/env node
var recast = require('recast');
var messages = require('./transform/messages');
var exec = require('child_process').exec;
var fs = require('fs');
var glob = require('glob');
var usage = [
	'Usage: assertion-messages.js "globbing expression"',
	'To generate assertion messages for a set of test files, provide a valid glob expression.',
	'File search is relative to the `test/` sub-directory of the local',
	'Test262 repository'
].join('\n');

var gexpr = process.argv[2];

if (!gexpr) {
	console.log(usage);
	return;
}

glob(__dirname + '/../test262/test/' + gexpr, function(err, list) {

	list.forEach(function(file) {
		var source = fs.readFileSync(file, 'utf8');

		try {
			var ast = recast.parse(source);
			var result = messages(ast);
			fs.writeFileSync(file, recast.print(ast).code, 'utf8');
		} catch (e) {
			console.log("failed: ", file);
		}
	});
});

Allow script to be invoke from any directory


import React from "react";
import portal from "react-portal-hoc";

const MentionMenu = props => {
  const {
    active,
    className,
    item: Item,
    options,
    top,
    left,
    hoverItem,
    selectItem,
    style = {}
  } = props;
  const menuStyle = {
    ...style,
    left,
    top,
    position: "absolute"
  };
  return (

      {options.map((option, idx) => {
        return (

            <Item active={active === idx} {...option} />

        );
      })}

  );
};

export default portal({ clickToClose: true, escToClose: true })(MentionMenu);

import React from "react";
import portal from "react-portal-hoc";

class MentionMenu extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      left: props.left,
      top: props.top
    };
  }
  componentWillReceiveProps(nextProps) {
    if (nextProps.left != this.state.left || nextProps.top != this.state.top) {
      this.setState({ left: nextProps.left, right: nextProps.right });
    }
  }
  componentDidMount() {
    //prevent menu from going off the right of the screen
    if (this.node && this.props.left + this.node.offsetWidth > window.innerWidth) {
      this.setState({ left: window.innerWidth - (this.node.offsetWidth + 10) });
    }
    //prevent menu from going off bottom of screen
    if (this.node && this.props.top + this.node.offsetHeight > window.innerHeight) {
      this.setState({ top: window.innerHeight - (this.node.offsetHeight + 10) });
    }
  }
  render() {
    const {
      active,
      className,
      item: Item,
      options,
      hoverItem,
      selectItem,
      style = {}
    } = this.props;

    const {
      top,
      left
    } = this.state;

    const menuStyle = {
      ...style,
      left,
      top,
      position: "absolute"
    };
    return (

 this.node = node}>
        {options.map((option, idx) => {
          return (

              <Item active={active === idx} {...option} />

          );
        })}

    );
  }
}

export default portal({ clickToClose: true, escToClose: true })(MentionMenu);

Improve menu positioning to keep it from going off screen


'use strict';

var opbeat = require('../../');
var inquirer = require('inquirer');

var questions = [
  { name: 'app_id', message: 'App ID' },
  { name: 'organization_id', message: 'Organization ID' },
  { name: 'secret_token', message: 'Secret token' }
];

var test = function (options) {
  options.env = 'production';
  options.level = 'fatal';
  options.handleExceptions = false;
  var client = opbeat.createClient(options);

  client.handleUncaughtExceptions(function (err) {
    console.log('Handled uncaught exception correctly');
    process.exit();
  });

  console.log('Capturing error...');
  client.captureError(new Error('captureError()'), function (err, url) {
    if (err) console.log('Something went wrong:', err.message);
    console.log('The error have been logged at:', url);

    console.log('Capturing message...');
    client.captureError('captureError()', function (err, url) {
      if (err) console.log('Something went wrong:', err.message);
      console.log('The message have been logged at:', url);

      console.log('Throwing exception...');
      throw new Error('throw');
    });
  });
};

inquirer.prompt(questions, function (answers) {
  // inquirer gives quite a long stack-trace, so let's do this async
  process.nextTick(test.bind(null, answers));
});

'use strict';

var opbeat = require('../../');
var inquirer = require('inquirer');

var questions = [
  { name: 'app_id', message: 'App ID' },
  { name: 'organization_id', message: 'Organization ID' },
  { name: 'secret_token', message: 'Secret token' }
];

var test = function (options) {
  options.env = 'production';
  options.level = 'fatal';
  options.handleExceptions = false;
  var client = opbeat.createClient(options);

  client.handleUncaughtExceptions(function (err) {
    console.log('Handled uncaught exception correctly');
    process.exit();
  });

  console.log('Capturing error...');
  client.captureError(new Error('This is an Error object'), function (err, url) {
    if (err) console.log('Something went wrong:', err.message);
    console.log('The error have been logged at:', url);

    console.log('Capturing message...');
    client.captureError('This is a string', function (err, url) {
      if (err) console.log('Something went wrong:', err.message);
      console.log('The message have been logged at:', url);

      console.log('Throwing exception...');
      throw new Error('This Error was thrown');
    });
  });
};

inquirer.prompt(questions, function (answers) {
  // inquirer gives quite a long stack-trace, so let's do this async
  process.nextTick(test.bind(null, answers));
});

Improve error names when running integration test


import React, { Component } from 'react'
import { Link } from 'react-router'

import Header from 'Components/Header'
import Footer from 'Components/Footer'
import zhcn from '../../../intl/zh-cn'
import en from '../../../intl/en'
import 'styles/pages/index.scss'

const format = (locale) => {
  return (key) => {
    let v = null;
    if (locale === 'zh-cn') {
      v = zhcn[key]
    } else {
      v = en[key]
    }
    return v || key;
  }
}

export default class Index extends Component {

  changeLang(lang) {
    let {pathname, query} = this.props.location
    query.locale = lang
    this.context.router.replace({pathname: location.pathname, query})
  }

  render() {
    const {query} = this.props.location
    const __ = format(query.locale);
    return (
      <div id='container'>
        <Header __={__} query={query} changeLang={this.changeLang.bind(this)} />
        {React.cloneElement(this.props.children, {__, query})}
        <Footer __={__} query={query} />
      </div>
    )
  }
}

Index.contextTypes = {
  router: React.PropTypes.object.isRequired
}

import React, { Component } from 'react'
import { Link } from 'react-router'

import Header from 'Components/Header'
import Footer from 'Components/Footer'
import zhcn from '../../../intl/zh-cn'
import en from '../../../intl/en'
import 'styles/pages/index.scss'

const format = (locale) => {
  return (key) => {
    let v = null;
    if (locale === 'zh-cn') {
      v = zhcn[key]
    } else {
      v = en[key]
    }
    return v || key;
  }
}

export default class Index extends Component {

  changeLang(lang) {
    let {pathname, query} = this.props.location
    query.locale = lang
    this.context.router.replace({pathname: pathname, query})
  }

  render() {
    const {query} = this.props.location
    const __ = format(query.locale);
    return (
      <div id='container'>
        <Header __={__} query={query} changeLang={this.changeLang.bind(this)} />
        {React.cloneElement(this.props.children, {__, query})}
        <Footer __={__} query={query} />
      </div>
    )
  }
}

Index.contextTypes = {
  router: React.PropTypes.object.isRequired
}

Fix redirect when switch language.