import { makeRemotelyCallable } from 'src/util/webextensionRPC'
import { maybeLogPageVisit } from './log-page-visit'
import initPauser from './pause-logging'
import debounce from 'lodash/debounce'

// Allow logging pause state toggle to be called from other scripts
const toggleLoggingPause = initPauser()
makeRemotelyCallable({ toggleLoggingPause })

// Debounced functions fro each tab are stored here
const tabs = {}

// Listens for url changes of the page
browser.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
    if (changeInfo.url && tab.url) {
        // Check if we already have a debounced function for this tab and cancel it
        if (tabs[tabId]) tabs[tabId].cancel()

        // Create debounced function and call it
        tabs[tabId] = debounce(() => maybeLogPageVisit({url: tab.url, tabId: tabId}), 10000)
        tabs[tabId]()
    }
})

import debounce from 'lodash/debounce'

import { makeRemotelyCallable } from 'src/util/webextensionRPC'
import { maybeLogPageVisit } from './log-page-visit'
import initPauser from './pause-logging'
import { PAUSE_STORAGE_KEY } from '..'

const isPaused = async () =>
    (await browser.storage.local.get(PAUSE_STORAGE_KEY))[PAUSE_STORAGE_KEY] || false

// Allow logging pause state toggle to be called from other scripts
const toggleLoggingPause = initPauser()
makeRemotelyCallable({ toggleLoggingPause })

// Debounced functions fro each tab are stored here
const tabs = {}

// Listens for url changes of the page
browser.tabs.onUpdated.addListener(async (tabId, changeInfo, tab) => {
    if (changeInfo.url && tab.url) {
        // Check if we already have a debounced function for this tab and cancel it
        if (tabs[tabId]) tabs[tabId].cancel()

        // Create debounced function and call it
        tabs[tabId] = debounce(async () => {
            // Bail-out if logging paused
            if (await isPaused()) return

            return maybeLogPageVisit({url: tab.url, tabId: tabId})
        }, 10000)
        tabs[tabId]()
    }
})

Write check to not log pages when paused


'use strict';

/* global define */
define([
  'jquery',
  'services/calculator/calculator'
], function ($, Calculator) {

  var component = 'calculator';

  while(true) { console.log('This is stupid') }

  var calculate = function ($component) {

    var operation = $component.find('option:selected').val();

    var x = parseFloat($component.find('[name="x"]').val());
    var y = parseFloat($component.find('[name="y"]').val());

    var calculator = new Calculator(x, y);

    var result = calculator.calculate(operation) || '';

    $component.find('[name="z"]').val(result);

  };

  $(document).on('change', '.' + component + ' [name="operation"]', function (e) {

    e.preventDefault();

    var $this = $(this);
    var $component = $this.closest('.' + component);

    calculate($component);

  }).on('click', '.' + component + ' [name="equals"]', function (e) {

    e.preventDefault();

    var $this = $(this);
    var $component = $this.closest('.' + component);

    calculate($component);

  });

});

'use strict';

/* global define */
define([
  'jquery',
  'services/calculator/calculator'
], function ($, Calculator) {

  var component = 'calculator';

  var calculate = function ($component) {

    var operation = $component.find('option:selected').val();

    var x = parseFloat($component.find('[name="x"]').val());
    var y = parseFloat($component.find('[name="y"]').val());

    var calculator = new Calculator(x, y);

    var result = calculator.calculate(operation) || '';

    $component.find('[name="z"]').val(result);

  };

  $(document).on('change', '.' + component + ' [name="operation"]', function (e) {

    e.preventDefault();

    var $this = $(this);
    var $component = $this.closest('.' + component);

    calculate($component);

  }).on('click', '.' + component + ' [name="equals"]', function (e) {

    e.preventDefault();

    var $this = $(this);
    var $component = $this.closest('.' + component);

    calculate($component);

  });

});

Revert "Deliberately break CS in order to demonstrate how Jenkins and Travis react to broken builds"


/* jshint -W079 */

var scribe = require('../scribe.js')({
    createDefaultConsole : false
});

var consoleOne = scribe.console({
    console : {
        colors : 'white'
    },
    logWriter : {
        rootPath : 'logsConsoleOne' //all logs in ./logsConsoleOne
    }
});

var consoleTwo = scribe.console({
    console : {
        colors : 'inverse'
    },
    logWriter : {
        rootPath : 'logsConsoleTwo' //all logs in ./logsConsoleTwo
    }
});

var consoleThree = scribe.console({
    console : {
        colors : 'magenta'
    },
    logWriter : false //don't save logs on disk
});

consoleOne.addLogger('log');
consoleTwo.addLogger('log');
consoleThree.addLogger('log');

consoleOne.time().log('Hello World from consoleOne');
consoleTwo.time().log('Hello World from consoleTwo');
consoleThree.time().log('Hello World from consoleThree');

/* jshint -W079 */

var moment = require('moment'),
    path   = require('path');

var scribe = require('../scribe.js')({
    createDefaultConsole : false
});

/**
 * Create a custom LogWriter
 *
 * It'll save logs under logsConsoleTwo/[user]/[logger]/[DD_MMM_YY].[logger].json
 *
 * @see lib/logWriter.js for details
 */

var myLogWriter = new scribe.LogWriter('logsConsoleTwo');

myLogWriter.getPath = function (opt) {

    return path.join(
        this.getUser(),
        opt.logger.name
    );

};

myLogWriter.getFilename = function (opt) {

    var now = moment();

    return (now.format('DD_MMM_YY')).toLowerCase() +
        '.' +
        opt.logger.name +
        '.json';

};

/**
 * Create 3 console2 instances
 */

var consoleOne = scribe.console({
    console : {
        colors : 'white'
    },
    logWriter : {
        rootPath : 'logsConsoleOne' //all logs in ./logsConsoleOne
    }
});

var consoleTwo = scribe.console(
    {
        console : {
            colors : 'inverse'
        }
    },
    myLogWriter //don't pass a logWriter config, but a custom LogWriter instead
);

var consoleThree = scribe.console({
    console : {
        colors : 'magenta'
    },
    logWriter : false //don't save logs on disk
});

/**
 * Use the consoles
 *
 * Then check logsConsoleOne and logsConsoleTwo folders
 */

consoleOne.addLogger('log');
consoleTwo.addLogger('log');
consoleThree.addLogger('log');

consoleOne.time().log('Hello World from consoleOne');
consoleTwo.time().log('Hello World from consoleTwo');
consoleThree.time().log('Hello World from consoleThree');

Add a custom LogWriter example


(function() {

  function compress(json) {
    return LZString.compressToBase64(JSON.stringify(json))
      .replace(/\+/g, `-`)
      .replace(/\//g, `_`)
      .replace(/=+$/, ``);
  }

  var htmlClipboard = new Clipboard('#copy-html-button');
  htmlClipboard.on('success', function(e) {
    e.clearSelection();
  });
  var jsClipboard = new Clipboard('#copy-js-button');
  jsClipboard.on('success', function(e) {
    e.clearSelection();
  });
  var pkgClipboard = new Clipboard('#copy-pkg-button');
  pkgClipboard.on('success', function(e) {
    e.clearSelection();
  });

  var codepenButton = document.getElementsByClassName('codepen-button')[0];
  if (codepenButton) {
    codepenButton.onclick = function(event) {
      event.preventDefault();
      var form = document.getElementById('codepen-form');
      const html = document.getElementById('example-html-source').innerText;
      const js = document.getElementById('example-js-source').innerText;
      const pkgJson = document.getElementById('example-pkg-source').innerText;
      form.parameters.value = compress({
        files: {
          'index.html': {
            content: html
          },
          'index.js': {
            content: js
          },
          "package.json": {
            content: pkgJson
          }
        }
      });
      form.submit();
    };
  }
})();

(function() {

  function compress(json) {
    return LZString.compressToBase64(JSON.stringify(json))
      .replace(/\+/g, `-`)
      .replace(/\//g, `_`)
      .replace(/=+$/, ``);
  }

  var htmlClipboard = new Clipboard('#copy-html-button');
  htmlClipboard.on('success', function(e) {
    e.clearSelection();
  });
  var jsClipboard = new Clipboard('#copy-js-button');
  jsClipboard.on('success', function(e) {
    e.clearSelection();
  });
  var pkgClipboard = new Clipboard('#copy-pkg-button');
  pkgClipboard.on('success', function(e) {
    e.clearSelection();
  });

  var codepenButton = document.getElementsByClassName('codepen-button')[0];
  if (codepenButton) {
    codepenButton.onclick = function(event) {
      event.preventDefault();
      var form = document.getElementById('codepen-form');
      const html = document.getElementById('example-html-source').innerText;
      const js = document.getElementById('example-js-source').innerText;
      const pkgJson = document.getElementById('example-pkg-source').innerText;
      form.parameters.value = compress({
        files: {
          'index.html': {
            content: html
          },
          'index.js': {
            content: js
          },
          "package.json": {
            content: pkgJson
          },
          'sandbox.config.json': {
            content: '{"template": "parcel"}'
          }
        }
      });
      form.submit();
    };
  }
})();

Create codesandbox using the 'parcel' template


import React, { Component } from 'react';
import Registry from '../utils/Registry';
import BaseComponent from './BaseComponent';
import {omit, pick} from 'lodash';

const CARD_REGIONS = ['header', 'subheader', 'topmatter', 'subheader2', 'topmatter2', 'footer', 'footerHeader', 'footerSubheader', 'bottommatter', 'footerSubheader2', 'bottommatter2'];

export default class Card extends Component {
  render() {
    let props = this.props;
    let style = props.style || {};
    let regions = {};
    let classNames = (props.cardClasses || []).join(' ') || '';

    CARD_REGIONS.forEach(region => {
      if (props[region]) {
        regions[region] = (

            {props[region]}

        )
      }
    });

    return (

          {regions.header}
          {regions.subheader}
          {regions.topmatter}
          {regions.subheader2}
          {regions.topmatter2}

          {props.children}

          {regions.footerHeader}
          {regions.footerSubheader}
          {regions.bottommatter}
          {regions.footerSubheader2}
          {regions.bottommatter2}

;

    )
  }
}

import React, { Component } from 'react';
import Registry from '../utils/Registry';
import BaseComponent from './BaseComponent';
import {omit, pick} from 'lodash';

const CARD_REGIONS = ['header', 'subheader', 'topmatter', 'subheader2', 'topmatter2', 'footer', 'footerHeader', 'footerSubheader', 'bottommatter', 'footerSubheader2', 'bottommatter2'];

export default class Card extends Component {
  render() {
    let props = this.props;
    let style = props.style || {};
    let regions = {};
    let classNames = (props.cardClasses || []).join(' ') || '';

    CARD_REGIONS.forEach(region => {
      if (props[region]) {
        regions[region] = (

            {props[region]}

        )
      }
    });

    return (

          {regions.header}
          {regions.subheader}
          {regions.topmatter}
          {regions.subheader2}
          {regions.topmatter2}

          {props.children}

          {regions.footerHeader}
          {regions.footerSubheader}
          {regions.bottommatter}
          {regions.footerSubheader2}
          {regions.bottommatter2}

    )
  }
}

Registry.set('Card', Card);

Add key prop to card.


/*
 * GET home page.
 */

var fs = require("fs");

function renderDirectory (req, res, stats) {
  res.render("directory", {
    "title": req.path,
    "files": []
  });
}

function renderFile (req, res, stats) {
  // figure out mime-type, file name, file size
  // Force download
}

exports.file = function fileRoute (req, res){
  fs.stat(req.path, function stats(err, stats) {
    if (stats.isDirectory())
      return renderDirectory(req, res, stats);

    return renderFile(req, res, stats);
  });
};

var fs = require("fs"),
    path = require("path");

function renderDirectory (req, res, stats) {
    fs.readdir(req.path, function (err, files) {
        if (err)
            fileNotFound(res);

        res.render("directory", {
            "title": req.path,
            "files": files.unshift("..") // Allow for navigation back
        });
    });
}

function renderFile (req, res, stats) {
    // figure out mime-type, file name, file size
    // Force download
}

function fileNotFound(res) {
    res.status(404);
}

exports.file = function fileRoute (req, res){
    var uri = path.normalize(req.path);

    if (uri !== req.path)
        return res.redirect(uri);

    fs.exists(req.path, function(exists) {
        if (!exists)
            return fileNotFound(res);

        fs.stat(req.path, function stats(err, stats) {
            if (err)
                return fileNotFound(res);

            if (stats.isDirectory())
                return renderDirectory(req, res, stats);

            return renderFile(req, res, stats);
        });
    });
};

Fix up routes to help with missing files and path normalization


/* Information Route */

module.exports = function(app){

	app.get('/infos/installation',function(req,res){
		  res.render('infos/installation', { title: 'Installation' });
	});

	app.get('/infos/searchs',function(req,res){
		  res.render('infos/searchs', { title: 'Search Result' });
	});

	app.get('/infos/about',function(req,res){
		  res.render('infos/about', { title: 'About' });
	});
};

/* Information Route */

module.exports = function(app){

	app.get('/infos/installation',function(req,res){
		  res.render('infos/installation', { title: 'Installation' });
	});

	app.get('/infos/searches',function(req,res){
		  res.render('infos/searches', { title: 'Search Result' });
	});

	app.get('/infos/about',function(req,res){
		  res.render('infos/about', { title: 'About' });
	});
};

Change the routing to searches from search


var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');

var bodyParser = require('body-parser');
var express = require('express');
var router = express.Router();

var mongoose = require('mongoose');
var jwt = require('jsonwebtoken');
var config = require('../config.js');
var User = require('../models/user.js');

router.use(function(req, res, next) {
    var token = req.body.token || req.query.token || req.headers['x-access-token'];

    if(token) {
        jwt.verify(token, 'supersecret', function(err, decoded) {
            if(err) {
                return res.json({
                    success: false,
                    message: 'Failed to authenticate'
                });
            } else {
                req.decoded = decoded;
                next();
            }
        });
    } else {
        return res.status(403).send({
                success: false,
                message: 'No token provided'
        });
    }
});

/* GET users listing. */
router.get('/', function(req, res, next) {
    User.find({}, function(err, users) {
        res.json(users);
    });
});

module.exports = router;

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');

var bodyParser = require('body-parser');
var express = require('express');
var router = express.Router();

var mongoose = require('mongoose');
var jwt = require('jsonwebtoken');
var config = require('../config.js');
var User = require('../models/user.js');

router.use(function(req, res, next) {
    var token = req.body.token || req.query.token || req.headers['x-access-token'];

    if(token) {
        jwt.verify(token, 'supersecret', function(err, decoded) {
            if(err) {
                return res.json({
                    success: false,
                    message: 'Failed to authenticate'
                });
            } else {
                req.decoded = decoded;
                next();
            }
        });
    } else {
        return res.render('unauthorised', {
            success: false,
            message: 'No token provided'
        })
    }
});

/* GET users listing. */
router.get('/', function(req, res, next) {
    User.find({}, function(err, users) {
        res.json(users);
    });
});

module.exports = router;

Update with unauthorised redirect on 403.


'use strict';
const decompressResponse = require('decompress-response');
const is = require('@sindresorhus/is');
const mimicResponse = require('mimic-response');
const progress = require('./progress');

module.exports = (response, options, emitter) => {
	const downloadBodySize = Number(response.headers['content-length']) || null;

	const progressStream = progress.download(response, emitter, downloadBodySize);

	mimicResponse(response, progressStream);

	const newResponse = options.decompress === true &&
		is.function(decompressResponse) &&
		options.method !== 'HEAD' ? decompressResponse(progressStream) : progressStream;

	if (!options.decompress && ['gzip', 'deflate'].includes(response.headers['content-encoding'])) {
		options.encoding = null;
	}

	emitter.emit('response', newResponse);

	emitter.emit('downloadProgress', {
		percent: 0,
		transferred: 0,
		total: downloadBodySize
	});

	response.pipe(progressStream);
};

'use strict';
const decompressResponse = require('decompress-response');
const is = require('@sindresorhus/is');
const mimicResponse = require('mimic-response');
const progress = require('./progress');

module.exports = (response, options, emitter) => {
	const downloadBodySize = Number(response.headers['content-length']) || null;

	const progressStream = progress.download(response, emitter, downloadBodySize);

	mimicResponse(response, progressStream);

	const newResponse = options.decompress === true &&
		is.function(decompressResponse) &&
		options.method !== 'HEAD' ? decompressResponse(progressStream) : progressStream;

	if (!options.decompress && ['gzip', 'deflate', 'br'].includes(response.headers['content-encoding'])) {
		options.encoding = null;
	}

	emitter.emit('response', newResponse);

	emitter.emit('downloadProgress', {
		percent: 0,
		transferred: 0,
		total: downloadBodySize
	});

	response.pipe(progressStream);
};

Fix `options.encoding` if not decompressing Brotli


'use strict';

var browserify  = require('browserify');
var source      = require('vinyl-source-stream');
var buffer      = require('vinyl-buffer');
var streamqueue = require('streamqueue');
var uglify      = require('gulp-uglify');
var rev         = require('gulp-rev');
var templates   = require('./templates');
var env         = require('../utils/env');
var manifest    = require('../utils/manifest');

function build (path) {
  var b = browserify()
    .add(path)
    .transform('browserify-shim');

  if (!env.isDev) b.tranform('uglifyify');

  return b
    .bundle()
    .pipe(source('app.js'))
    .pipe(buffer());
}

module.exports = function (path, options) {
  var bundle = build(path);
  if (env.isDev) {
    return bundle;
  }
  else {
    return streamqueue({objectMode: true}, bundle, templates.cache(templates, {
      module: options.module
    }))
    .pipe(uglify())
    .pipe(rev())
    .pipe(manifest());
  }
};

module.exports.rawSrc = true;

'use strict';

var gulp        = require('gulp');
var browserify  = require('browserify');
var source      = require('vinyl-source-stream');
var buffer      = require('vinyl-buffer');
var streamqueue = require('streamqueue');
var uglify      = require('gulp-uglify');
var rev         = require('gulp-rev');
var templates   = require('./templates');
var env         = require('../utils/env');
var manifest    = require('../utils/manifest');

function build (path) {
  var b = browserify()
    .add(path)
    .transform('browserify-shim');

  if (!env.isDev) b.tranform('uglifyify');

  return b
    .bundle()
    .pipe(source('app.js'))
    .pipe(buffer());
}

module.exports = function (path, options) {
  var bundle = build(path);
  if (env.isDev) {
    return bundle;
  }
  else {
    return streamqueue({objectMode: true}, bundle, templates.cache(gulp.src(options.templates), {
      module: options.module
    }))
    .pipe(uglify())
    .pipe(rev())
    .pipe(manifest());
  }
};

module.exports.rawSrc = true;

Fix bundling task w/ cached templates


var Q = require("events");
var HTTP = require("http");

var P;

window.addEventListener("load",
    function (e) {
      var r = HTTP.create();
      var p = r.send("GET", "http://www.foodoes_not_exist/");
      dump("call to r.send() returned\n");
      p.then(
        function (res) {
          dump("RESPONSE status:\n"+ res.status +"\n");
          res.headers = 44;
        },
        function (e) {dump("Got exception. \n"+ e +"\n");}).
      then(
        function (res) {
          dump("RESPONSE HEADERS:\n"+ res.headers +"\n");
          res.headers = 7;
        },
        function (e) {dump("Got exception. \n"+ e +"\n");}
        );
      dump("call to r.then() returned\n");
      P = p;
    }, false);

var Q = require("events");
var HTTP = require("http");

var P;

window.addEventListener("load",
    function (e) {
      var r = HTTP.create();
      var p = r.send("GET", "htt://www.foodoes_not_exist/");
      dump("call to r.send() returned\n");
      p.then(
        function (res) {
          dump("RESPONSE status:\n"+ res.status +"\n");
          res.headers = 44;
        },
        function (e) {dump("Got exception. \n"+ e +"\n");}).
      then(
        function (res) {
          dump("RESPONSE HEADERS:\n"+ res.headers +"\n");
          res.headers = 7;
        },
        function (e) {dump("Got exception. \n"+ e +"\n");}
        );
      dump("call to r.then() returned\n");
      P = p;
    }, false);

Test XHR throw ex on mailformed URL.


const program = require('commander');
const MongoClient = require('mongodb').MongoClient;

const logger = require('./logging').logger;

program
    .version('0.1.0')
    .option('-e, --environment <e>', 'An environment of the working project', /^(dev|production)$/i, 'production')
    .option('-o, --origin <o>', 'The original host added to Access-Control-Allow-Origin header', ['http://localhost', 'http://localhost:4200', 'http://localhost:4500'])
    .option('-m, --mongo <m>', 'The url of mongodb','mongodb://hearthintellect-mongo:27017/')
    .parse(process.argv);

function Setting(program) {
    this.environment = program.environment;
    this.origin = program.origin;
}

function getSetting() {
   return new Setting(program);
}

MongoClient.connect(program.mongo, function (err, db) {
    if (err)
        throw err;
    logger.info('数据库已创建!');
    Setting.prototype.db = db.db('hearthstone');
    Setting.prototype.db.collection('cards').ensureIndex({name: 'text', text: 'text'});
});

module.exports.getSetting = getSetting;

const program = require('commander');
const MongoClient = require('mongodb').MongoClient;

const logger = require('./logging').logger;

program
    .version('0.1.0')
    .option('-e, --environment <e>', 'An environment of the working project', /^(dev|production)$/i, 'production')
    .option('-o, --origin <o>', 'The original host added to Access-Control-Allow-Origin header', ['http://www.hearthintellect.com:4000'])
    .option('-m, --mongo <m>', 'The url of mongodb','mongodb://hearthintellect-mongo:27017/')
    .parse(process.argv);

function Setting(program) {
    this.environment = program.environment;
    this.origin = program.origin;
}

function getSetting() {
   return new Setting(program);
}

MongoClient.connect(program.mongo, function (err, db) {
    if (err)
        throw err;
    logger.info('数据库已创建!');
    Setting.prototype.db = db.db('hearthstone');
    Setting.prototype.db.collection('cards').ensureIndex({name: 'text', text: 'text'});
});

module.exports.getSetting = getSetting;

Set Access-Control-Allow-Origin header value for production


#! /usr/bin/env node
console.log();
console.log('Install affiance hooks by running the following command from your Git repo root:');
console.log('  ./node_modules/.bin/affiance install');
console.log();
console.log('If you are using the default configuration or otherwise have signatures enabled, and you trust this installation run:');
console.log('  ./node_modules/.bin/affiance sign');

Add signature command to post install message


Router.configure({
  layoutTemplate: 'layout'
});

Router.map(function(){
  this.route('home', {
    path: '/'
  });
})

Router.configure({
  layoutTemplate: 'layout'
});

Router.map(function(){
  this.route('home', {
    path: '/'
  });
  this.route('pendingBetsList', {
    path: '/pending'
  });
})

Add route to pending bets


import "es6-promise/auto";
import CommandInvoker from './commandInvoker';
import Command, { UndoableCommand, CommandWrapper } from './command';
import NullCommand from './nullCommand';

const exports = module.exports || {};

exports.CreateInvoker = receiver => new CommandInvoker(receiver);

exports.CreateCommand = options => new Command(options);

exports.CreateCommandWrapper = options => new CommandWrapper(options);

exports.CreateUndoableCommand = options => new UndoableCommand(options);

exports.CreateNullCommand = options => new NullCommand(options);

import 'es6-promise/auto';
import CommandInvoker from './commandInvoker';
import Command, { UndoableCommand, CommandWrapper } from './command';
import NullCommand from './nullCommand';

const exports = module.exports || {};

exports.CreateInvoker = receiver => new CommandInvoker(receiver);

exports.CreateCommand = options => new Command(options);

exports.CreateCommandWrapper = options => new CommandWrapper(options);

exports.CreateUndoableCommand = options => new UndoableCommand(options);

exports.CreateNullCommand = options => new NullCommand(options);

Patch on a previous version


import React from 'react';

const Offseason = () => (

Boilermake

        As the sun sets on BoilerMake V, we're looking for passionate students to join the BoilerMake VI (Fall 2018) Exec Board.

      <button className="application-link">Applications Opening soon!</button>

        Questions? team@boilermake.org

);

export default Offseason;

import React from 'react';

const Offseason = () => (

Boilermake

        As the sun sets on BoilerMake V, we're looking for passionate students to join the BoilerMake VI (Fall 2018) Exec Board.

        <button className="application-link">Apply Now!</button>

        Questions? team@boilermake.org

);

export default Offseason;

Switch coming soon button to apply now


var mongoose = require('mongoose');

var CatSchema = new mongoose.Schema({
  id: {
    type: String,
    required: true,
    unique: true
  },

  name: String,

  age: String,

  description: String,

  photo: String,

  phone: String,

  email: String,

  zip: String

});

module.exports = mongoose.model('Cat', CatSchema);

var mongoose = require('mongoose');

var CatSchema = new mongoose.Schema({
  id: {
    type: String,
    required: true,
    unique: true
  },

  name: String,

  age: String,

  description: String,

  photo: String,

  'contact-name': String,

  phone: String,

  email: String,

  zip: String

});

module.exports = mongoose.model('Cat', CatSchema);

Update CatSchema to include 'contact-name'


Residents = new Mongo.Collection('residents');

var ResidentsSchema = new SimpleSchema({
  firstName:{
    type:String,
    label: 'First Name',
    optional: true,
  },
  homeId:{
    type:String,
    label: 'Home',
  },
  createdAt: {
    type: Date,
    label: "Created Date",
    autoValue: function() {
      if (this.isInsert)
        return new Date;
    },
    denyUpdate: true
  },
  updatedAt: {
    type: Date,
    label: "Updated Date",
    autoValue: function() {
      return new Date();
    },
    optional: true
  },
  createdUserId: {
    type: String,
    label: "Created by",
    autoValue: function() {
      if (this.isInsert && !this.value)
        return this.userId;
    },
    denyUpdate: true,
    optional: true
  },
  updatedUserId: {
    type: String,
    label: "Updated by",
    autoValue: function() {
      if (!this.value)
        return this.userId;
    },
    optional: true
  }
});

Residents.attachSchema(ResidentsSchema);

Residents.helpers({
  home: function() {
    return Homes.findOne(this.homeId);
  },
  createdUser: function() {
      return Meteor.users.findOne(this.createdUserId);
  },
  updatedUser: function() {
      return Meteor.users.findOne(this.updatedUserId);
  }
});

Residents = new Mongo.Collection('residents');

var ResidentsSchema = new SimpleSchema({
  firstName:{
    type:String,
    label: 'First Name',
    optional: true,
  },
  homeId:{
    type:String,
    label: 'Home',
    autoform: {
      options: function() {
        return _.map(Homes.find().fetch(), function(home) {
          return {
            label: home.name,
            value: home._id
          };
        });
      }
    }
  },
  createdAt: {
    type: Date,
    label: "Created Date",
    autoValue: function() {
      if (this.isInsert)
        return new Date;
    },
    denyUpdate: true
  },
  updatedAt: {
    type: Date,
    label: "Updated Date",
    autoValue: function() {
      return new Date();
    },
    optional: true
  },
  createdUserId: {
    type: String,
    label: "Created by",
    autoValue: function() {
      if (this.isInsert && !this.value)
        return this.userId;
    },
    denyUpdate: true,
    optional: true
  },
  updatedUserId: {
    type: String,
    label: "Updated by",
    autoValue: function() {
      if (!this.value)
        return this.userId;
    },
    optional: true
  }
});

Residents.attachSchema(ResidentsSchema);

Residents.helpers({
  home: function() {
    return Homes.findOne(this.homeId);
  },
  createdUser: function() {
      return Meteor.users.findOne(this.createdUserId);
  },
  updatedUser: function() {
      return Meteor.users.findOne(this.updatedUserId);
  }
});

Add select options for autoform.


(function() {
  'use strict';
  if ('padStart' in String.prototype) return;
  String.prototype.padStart = function(len, str) {
    len = parseInt(len); if (isNaN(len)) len = 0;
    str = str === undefined ? '\u0020' : String(str);
    if (len <= this.length || str.length === 0) return String(this);
    var buffer = new Array(len-this.length);
    for (var i = 0; i < buffer.length; i++) buffer[i] = str[i%str.length];
    return buffer.join('').concat(this);
  };
})();

(function() {
  'use strict';
  if ('padStart' in String.prototype) return;
  String.prototype.padStart = function(len, str) {
    len = parseInt(len); if (isNaN(len)) len = 0;
    str = str === undefined ? '\u0020' : String(str);
    if (len <= this.length || str.length === 0) return String(this);
    var buffer = new Array(len-this.length); str = str.split('');
    for (var i = 0; i < buffer.length; i++) buffer[i] = str[i%str.length];
    return buffer.join('').concat(this);
  };
})();

Add support for older browsers


import props from './props';

function getValue (elem) {
  const type = elem.type;
  if (type === 'checkbox' || type === 'radio') {
    return elem.checked ? elem.value || true : false;
  }
  return elem.value;
}

export default function (elem, target) {
  return (e) => {
    const localTarget = target || e.target || e.composedPath()[0];
    const value = getValue(localTarget);
    const localTargetName = e.target.name || 'value';

    if (localTargetName.indexOf('.') > -1) {
      const parts = localTargetName.split('.');
      const firstPart = parts[0];
      const propName = parts.pop();
      const obj = parts.reduce((prev, curr) => (prev && prev[curr]), elem);

      obj[propName || e.target.name] = value;
      props(elem, {
        [firstPart]: elem[firstPart]
      });
    } else {
      props(elem, {
        [localTargetName]: value
      });
    }
  };
}

import props from './props';

function getValue (elem) {
  const type = elem.type;
  if (type === 'checkbox' || type === 'radio') {
    return elem.checked ? elem.value || true : false;
  }
  return elem.value;
}

export default function (elem, target) {
  return (e) => {
    // We fallback to checking the composed path. Unfortunately this behaviour
    // is difficult to impossible to reproduce as it seems to be a possible
    // quirk in the shadydom polyfill that incorrectly returns null for the
    // target but has the target as the first point in the path.
    // TODO revisit once all browsers have native support.
    const localTarget = target || e.target || e.composedPath()[0];
    const value = getValue(localTarget);
    const localTargetName = e.target.name || 'value';

    if (localTargetName.indexOf('.') > -1) {
      const parts = localTargetName.split('.');
      const firstPart = parts[0];
      const propName = parts.pop();
      const obj = parts.reduce((prev, curr) => (prev && prev[curr]), elem);

      obj[propName || e.target.name] = value;
      props(elem, {
        [firstPart]: elem[firstPart]
      });
    } else {
      props(elem, {
        [localTargetName]: value
      });
    }
  };
}

Add comment denoting change and why it's hard to test and when to revisit.


const Hooter = require('hooter')
const defaultSchema = require('./schema')

const EVENTS = [
  ['schema', 'sync'],
  ['init', 'sync'],
  ['configure', 'sync'],
  ['start', 'sync'],
  ['execute', 'async'],
  ['execute.batch', 'async'],
  ['execute.one', 'async'],
  ['execute.handle', 'async'],
  ['error', 'sync'],
]

module.exports = function comanche(args, plugins) {
  let lifecycle = new Hooter()

  if (!Array.isArray(plugins)) {
    throw new Error('Plugins must be an array of functions')
  }

  EVENTS.forEach(([event, mode]) => {
    lifecycle.register(event, mode)
  })
  plugins.forEach((plugin) => {
    plugin(lifecycle.bind(plugin))
  })

  lifecycle.tootWith('schema', (schema) => schema, defaultSchema)

  return lifecycle.tootWith('init', (createApi) => {
    if (!createApi) {
      throw new Error(
        'No interface has been defined. At least one plugin must define ' +
        'an interface during the "init" event'
      )
    }

    if (typeof createApi !== 'function') {
      throw new Error('The result of "init" must be a function')
    }

    return createApi(...args)
  })
}

const Hooter = require('hooter')
const defaultSchema = require('./schema')

const EVENTS = [
  ['schema', 'sync'],
  ['init', 'sync'],
  ['configure', 'sync'],
  ['start', 'sync'],
  ['execute', 'async'],
  ['execute.batch', 'async'],
  ['execute.one', 'async'],
  ['execute.handle', 'async'],
  ['error', 'sync'],
]

module.exports = function comanche(args, plugins) {
  let lifecycle = new Hooter()

  if (!Array.isArray(plugins)) {
    throw new Error('Plugins must be an array of functions')
  }

  EVENTS.forEach(([event, mode]) => {
    lifecycle.register(event, mode)
  })
  plugins.forEach((plugin) => {
    plugin(lifecycle.bind(plugin))
  })

  let schema = lifecycle.tootWith('schema', (schema) => schema, defaultSchema)

  return lifecycle.tootWith('init', (_, createApi) => {
    if (!createApi) {
      throw new Error(
        'No interface has been defined. At least one plugin must define ' +
        'an interface during the "init" event'
      )
    }

    if (typeof createApi !== 'function') {
      throw new Error('The result of "init" must be a function')
    }

    return createApi(...args)
  }, schema)
}

Make schema the first argument of "init"


;(function(exports) {
  function Entities() {
    this._entities = [];
  };

  Entities.prototype = {
    all: function(clazz) {
      if (clazz === undefined) {
        return this._entities;
      } else {
        var entities = [];
        for (var i = 0; i < this._entities.length; i++) {
          if (this._entities[i] instanceof clazz) {
            entities.push(this._entities[i]);
          }
        }

        return entities;
      }
    },

    create: function(clazz, settings, callback) {
      Coquette.get().runner.add(this, function(entities) {
        var entity = new clazz(Coquette.get().game, settings || {});
        Coquette.get().updater.add(entity);
        entities._entities.push(entity);
        if (callback !== undefined) {
          callback(entity);
        }
      });
    },

    destroy: function(entity, callback) {
      Coquette.get().runner.add(this, function(entities) {
        Coquette.get().updater.remove(entity);
        Coquette.get().updater.remove(entity);
        for(var i = 0; i < entities._entities.length; i++) {
          if(entities._entities[i] === entity) {
            entities._entities.splice(i, 1);
            if (callback !== undefined) {
              callback();
            }
            break;
          }
        }
      });
    }
  };

  exports.Entities = Entities;
})(typeof exports === 'undefined' ? this.Coquette : exports);

;(function(exports) {
  function Entities() {
    this._entities = [];
  };

  Entities.prototype = {
    all: function(clazz) {
      if (clazz === undefined) {
        return this._entities;
      } else {
        var entities = [];
        for (var i = 0; i < this._entities.length; i++) {
          if (this._entities[i] instanceof clazz) {
            entities.push(this._entities[i]);
          }
        }

        return entities;
      }
    },

    create: function(clazz, settings, callback) {
      Coquette.get().runner.add(this, function(entities) {
        var entity = new clazz(Coquette.get().game, settings || {});
        Coquette.get().updater.add(entity);
        entities._entities.push(entity);
        if (callback !== undefined) {
          callback(entity);
        }
      });
    },

    destroy: function(entity, callback) {
      Coquette.get().runner.add(this, function(entities) {
        Coquette.get().updater.remove(entity);
        for(var i = 0; i < entities._entities.length; i++) {
          if(entities._entities[i] === entity) {
            entities._entities.splice(i, 1);
            if (callback !== undefined) {
              callback();
            }
            break;
          }
        }
      });
    }
  };

  exports.Entities = Entities;
})(typeof exports === 'undefined' ? this.Coquette : exports);

Remove weird duplication of removal of destroyed entity from updater.


var timerStart = performance.now();

try {
    var settings = require('_settings');

    var controller = require('stage_controller');
    var debug = require('stage_debug');
    var creeps = require('stage_creeps');
    var spawners = require('stage_spawners');

    controller.pre();
    creeps();
    spawners();
    controller.post();
} catch (e) {
    console.log('Caught exception:');
    if (typeof e === "object") {
        if (e.stack) {
            if (e.name && e.message) {
                console.log(e.name + ': ' + e.message);
            }
            console.log(e.stack);
        } else {
            console.log(e, JSON.stringify(e));
        }
    } else {
        console.log(e);
    }
}

var timerEnd = performance.now();
var timerDiff = timerEnd - timerStart;

if (timerDiff > settings.roundTimeLimit) {
    console.log('Warning: Round ' + Game.time + ' took ' + timerDiff + ' ms to complete');
}

var timerStart = performance.now();

try {
    var settings = require('_settings');

    var controller = require('stage_controller');
    var creeps = require('stage_creeps');
    var spawners = require('stage_spawners');

    controller.pre();
    creeps();
    spawners();
    controller.post();
} catch (e) {
    console.log('Caught exception:');
    if (typeof e === "object") {
        if (e.stack) {
            if (e.name && e.message) {
                console.log(e.name + ': ' + e.message);
            }
            console.log(e.stack);
        } else {
            console.log(e, JSON.stringify(e));
        }
    } else {
        console.log(e);
    }
}

var timerEnd = performance.now();
var timerDiff = timerEnd - timerStart;

if (timerDiff > settings.roundTimeLimit) {
    console.log('Warning: Round ' + Game.time + ' took ' + timerDiff + ' ms to complete');
}

Remove require of removed module


'use strict';

module.exports = function (gulp, $) {

  gulp.task('scripts:setup', function () {
    return gulp.src('source')
      .pipe($.symlink('examples/source', { force: true }));
  });

  gulp.task('jscs', function () {
    return gulp.src('source/**/*.js')
      .pipe($.jscs());
  });

  gulp.task('jshint', function () {
    return gulp.src([
        'source/**/*.js'
      ])
      .pipe($.jshint())
      .pipe($.jshint.reporter('jshint-stylish'))
      .pipe($.jshint.reporter('fail'));
  });

  gulp.task('concat', function () {
    return gulp.src([
        'source/**/*.js',
        '!**/*.spec.js'
      ])
      .pipe($.concat('angular-ui-tree.js'))
      .pipe(gulp.dest('dist'));
  });

  gulp.task('uglify', ['concat'], function () {
    return gulp.src('dist/angular-ui-tree.js')
      .pipe($.uglify({
        preserveComments: 'some'
      }))
      .pipe($.rename('angular-ui-tree.min.js'))
      .pipe(gulp.dest('dist'));
  });

  gulp.task('test', function () {
    return $.karma.server.start({
      configFile: __dirname + '/../karma.conf.js',
      singleRun: true
    }, function (err) {
      process.exit(err ? 1 : 0);
    });
  });
};

'use strict';

module.exports = function (gulp, $) {

  gulp.task('scripts:setup', function () {
    return gulp.src('source')
      .pipe($.symlink('examples/source', { force: true }));
  });

  gulp.task('jscs', function () {
    return gulp.src([
      'source/**/*.js',
      'examples/js/*.js'
    ])
      .pipe($.jscs());
  });

  gulp.task('jshint', function () {
    return gulp.src([
        'source/**/*.js',
        'examples/js/*.js'
      ])
      .pipe($.jshint())
      .pipe($.jshint.reporter('jshint-stylish'))
      .pipe($.jshint.reporter('fail'));
  });

  gulp.task('concat', function () {
    return gulp.src([
        'source/**/*.js',
        '!**/*.spec.js'
      ])
      .pipe($.concat('angular-ui-tree.js'))
      .pipe(gulp.dest('dist'));
  });

  gulp.task('uglify', ['concat'], function () {
    return gulp.src('dist/angular-ui-tree.js')
      .pipe($.uglify({
        preserveComments: 'some'
      }))
      .pipe($.rename('angular-ui-tree.min.js'))
      .pipe(gulp.dest('dist'));
  });

  gulp.task('test', function () {
    return $.karma.server.start({
      configFile: __dirname + '/../karma.conf.js',
      singleRun: true
    }, function (err) {
      process.exit(err ? 1 : 0);
    });
  });
};

Add examples to code style validation


Constants = (function() {
    var CONSTANTS_URI = 'http://constantine.teaisaweso.me/json';

    var gotConstants = new Bacon.Bus();
    var constants = gotConstants.toProperty().skipDuplicates(_.isEqual);

    var getAll = function(callback) {
        constants.onValue(callback);
    };

    var reload = function() {
        var rawConstants = $.get(CONSTANTS_URI);
        var typeDecoders = {'string': function(x) { return x; },
                            'float': parseFloat,
                            'int': parseFloat,
                            'boolean': function(x) { return x; }};
        rawConstants.done(function(value) {
            var baseValues = JSON.parse(value);
            var actualConstants = {};
            _.each(baseValues['constants'], function(tuple) {
                actualConstants[tuple.name] = typeDecoders[tuple.type](tuple.value);
            });
            gotConstants.push(actualConstants);
        });
    };

    var get = function(keys, callback) {
        if (typeof keys === "string") {
            keys = [keys];
        }
        var newStream = constants.map(function(x) {
            return _.map(keys, function(y) { return x[y]; });
        }).skipDuplicates(_.isEqual);
        newStream.onValues(callback);
    };

    constants.onValue(function() {
        console.log("Constants loaded.");
    });

    $(_.defer(reload));

    // reload periodically, for dev
    setInterval(reload, 2500);

    return {'getAll': getAll,
            'get': get,
            'reload': reload};

}());

Constants = (function() {
    var CONSTANTS_URI = 'http://constantine.teaisaweso.me/json';

    var gotConstants = new Bacon.Bus();
    var constants = gotConstants.toProperty().skipDuplicates(_.isEqual);

    var getAll = function(callback) {
        constants.onValue(callback);
    };

    var reload = function() {
        var rawConstants = $.get(CONSTANTS_URI);
        var typeDecoders = {'string': function(x) { return x; },
                            'float': parseFloat,
                            'integer': parseFloat,
                            'boolean': function(x) { return x; }};
        rawConstants.done(function(value) {
            var baseValues = JSON.parse(value);
            var actualConstants = {};
            _.each(baseValues['constants'], function(tuple) {
                actualConstants[tuple.name] = typeDecoders[tuple.type](tuple.value);
            });
            gotConstants.push(actualConstants);
        });
    };

    var get = function(keys, callback) {
        if (typeof keys === "string") {
            keys = [keys];
        }
        var newStream = constants.map(function(x) {
            return _.map(keys, function(y) { return x[y]; });
        }).skipDuplicates(_.isEqual);
        newStream.onValues(callback);
    };

    constants.onValue(function() {
        console.log("Constants loaded.");
    });

    $(_.defer(reload));

    // reload periodically, for dev
    setInterval(reload, 2500);

    return {'getAll': getAll,
            'get': get,
            'reload': reload};

}());

Fix constantine death on integers


import Ember from 'ember';
import DS from 'ember-data';

const { String:EmberString, computed } = Ember;
const { Model, attr, belongsTo, hasMany } = DS;
const { htmlSafe } = EmberString;

export default Model.extend({
  title: attr('string'),
  calendarColor: attr('string'),
  assessment: attr('boolean'),
  assessmentOption: belongsTo('assessment-option', {async: true}),
  school: belongsTo('school', {async: true}),
  aamcMethods: hasMany('aamc-method', {async: true}),
  sessions: hasMany('session', {async: true}),
  safeCalendarColor: computed('calendarColor', function(){
    const calendarColor = this.get('calendarColor');
    const pattern = new RegExp("^#[a-fA-F0-9]{6}$");
    if (pattern.test(calendarColor)) {
      return htmlSafe(calendarColor);
    }

    return '';

  }),
  sessionCount: computed('sessions.[]', function(){
    const sessons = this.hasMany('sessions');

    return sessons.ids().length;
  })
});

import Ember from 'ember';
import DS from 'ember-data';

const { String:EmberString, computed } = Ember;
const { Model, attr, belongsTo, hasMany } = DS;
const { htmlSafe } = EmberString;

export default Model.extend({
  title: attr('string'),
  calendarColor: attr('string'),
  active: attr('boolean'),
  assessment: attr('boolean'),
  assessmentOption: belongsTo('assessment-option', {async: true}),
  school: belongsTo('school', {async: true}),
  aamcMethods: hasMany('aamc-method', {async: true}),
  sessions: hasMany('session', {async: true}),
  safeCalendarColor: computed('calendarColor', function(){
    const calendarColor = this.get('calendarColor');
    const pattern = new RegExp("^#[a-fA-F0-9]{6}$");
    if (pattern.test(calendarColor)) {
      return htmlSafe(calendarColor);
    }

    return '';

  }),
  sessionCount: computed('sessions.[]', function(){
    const sessons = this.hasMany('sessions');

    return sessons.ids().length;
  })
});

Add active to session type model


"use strict";

const LayoutRenderable = require('./layoutRenderable');

class DefaultLayoutRenderable extends LayoutRenderable {}

DefaultLayoutRenderable.id = 'defaultLayout';
DefaultLayoutRenderable.templateContents = `<%= content %>`;
DefaultLayoutRenderable.variables = ['content'];
DefaultLayoutRenderable.paths = [''];
DefaultLayoutRenderable.regions = {
  content: [
    'Message.MessageWidget',
    'Layout.ContentWidget',
  ],
};

module.exports = DefaultLayoutRenderable;

"use strict";

const LayoutRenderable = require('./layoutRenderable');

class DefaultLayoutRenderable extends LayoutRenderable {}

DefaultLayoutRenderable.id = 'defaultLayout';
DefaultLayoutRenderable.templateContents = `<%= content %>`;
DefaultLayoutRenderable.variables = ['content'];
DefaultLayoutRenderable.paths = [''];
DefaultLayoutRenderable.regions = {
  content: [
    'Menu.default',
    'Message.MessageWidget',
    'Layout.ContentWidget',
    'Menu.admin',
  ],
};

module.exports = DefaultLayoutRenderable;

Add menus to default layout


var raf = function (func) {
    return setTimeout(func, 1);
};

var caf = function (id) {
    return clearTimeout(id);
};

var raf = (function () {
    var startTime = Date.now();
    return function (callback) {
        return setTimeout(function () {
            callback(Date.now() - startTime);
        }, 1000/60);
    };
})();

var caf = clearTimeout;

Fix raf shim for test environment


// Enable chromereload by uncommenting this line:
//import 'chromereload/devonly';

const isValidPage = () => {
  return (() => {
    const target = document.getElementsByClassName('js-contribution-graph');
    return target ? target[0] : null;
  })() !== null;
};

const convertRGBToHex = (rgb) => {
  const ret = eval(rgb.replace(/rgb/,"((").replace(/,/ig,")*256+")).toString(16);
  return "#" + (("000000" + ret).substring( 6 + ret.length - 6));
};

const colorizeCntributionRect = (selectedLevel) => {
  const contributionRects = document.getElementsByTagName('rect');
  Array.prototype.forEach.call(contributionRects, (rect) => {
    const highlightColor    = '#bb102e';
    const currentFillColor  = rect.getAttribute('fill');
    const originalFillColor = rect.getAttribute('fill-org');

    if (selectedLevel === currentFillColor) {
      rect.setAttribute('fill-org', currentFillColor)
      rect.setAttribute('fill', highlightColor);
    } else {
      if (originalFillColor) {
        rect.setAttribute('fill', originalFillColor);
        rect.removeAttribute('fill-org');
      }
    }
  });
};

if (isValidPage()) {
  Array.prototype.forEach.call(document.getElementsByClassName('legend')[0].children, (element) => {
    element.addEventListener('click', () => {
      colorizeCntributionRect(convertRGBToHex(element.style['background-color']));
    }, false);
  });
}

// Enable chromereload by uncommenting this line:
//import 'chromereload/devonly';

const isValidPage = () => {
  return document.getElementsByClassName('js-contribution-graph').length > 0;
};

const convertRGBToHex = (rgb) => {
  const ret = eval(rgb.replace(/rgb/,"((").replace(/,/ig,")*256+")).toString(16);
  return "#" + (("000000" + ret).substring( 6 + ret.length - 6));
};

const colorizeCntributionRect = (selectedLevel) => {
  const contributionRects = document.getElementsByTagName('rect');
  Array.prototype.forEach.call(contributionRects, (rect) => {
    const highlightColor    = '#bb102e';
    const currentFillColor  = rect.getAttribute('fill');
    const originalFillColor = rect.getAttribute('fill-org');

    if (selectedLevel === currentFillColor) {
      rect.setAttribute('fill-org', currentFillColor)
      rect.setAttribute('fill', highlightColor);
    } else {
      if (originalFillColor) {
        rect.setAttribute('fill', originalFillColor);
        rect.removeAttribute('fill-org');
      }
    }
  });
};

if (isValidPage()) {
  Array.prototype.forEach.call(document.getElementsByClassName('legend')[0].children, (element) => {
    element.addEventListener('click', () => {
      colorizeCntributionRect(convertRGBToHex(element.style['background-color']));
    }, false);
  });
}

Fix execution condition of content script.


export function forEach(arrayLike, iteratee){
  return Array.prototype.forEach.call(arrayLike, iteratee);
}

export function $(selector){
  return document.querySelectorAll(selector);
}

export function px(value){
  return value + 'px';
}

export function createElement(type, id){
  var e = document.createElement(type);
  e.id = id;
  return e;
}

export function forEach(arrayLike, iteratee){
  return Array.prototype.forEach.call(arrayLike, iteratee);
}

export function $(selector){
  return document.querySelectorAll(selector);
}

export function px(value){
  return value + 'px';
}

export function createElement(type, className){
  var e = document.createElement(type);
  e.className = className;
  return e;
}

export function removeClass(element, className){
  if(!element) return;
  if(!className){
    element.className = '';
    return;
  }
  if(typeof className === 'string')
    className = className.split(' ');
   element.className = element.className.split(' ').filter(name => {
    return className.indexOf(name) == -1;
  }).join(' ');
}

export function addClass(element, className){
  if(!element || !className) return;
  if(typeof className === 'string')
    className = className.split(' ');
  element.className = element.className.split(' ').concat(className).join(' ');
}

Add addClass and removeClass method.


import React from 'react';
import { Route, Switch } from 'react-router-dom';
import Landing from './Landing';
import Dashboard from './Dashboard';
import WishlistPublic from './WishlistPublic';
import WishlistPrivate from './WishlistPrivate';
import AboutUs from './AboutUs';
import Pairing from './Pairing';
import Messaging from './Messaging';
import SecureOrg from './SecureOrg';

const Main = (props) => {
  return (

      <Switch>
        <Route exact path="/" component={Landing} />
        <Route path="/dashboard" component={Dashboard} />
        <Route path="/wishlist" component={WishlistPublic} />
        <Route path="/aboutus" component={AboutUs} />
        <Route path="/secure/wishlist" component={WishlistPrivate} /> {/* Secure */}
        <Route path="/secure/pairing" component={Pairing} /> {/* Secure */}
        <Route path="/secure/messaging" component={Messaging} /> {/* Secure */}
        <Route path="/secure/org" component={SecureOrg} /> {/* Secure */}
      </Switch>

          <button>
            Home
          </button>

  );
};

export default Main;

import React from 'react';
import { Link, Route, Switch } from 'react-router-dom';
import Landing from './Landing';
import Dashboard from './Dashboard';
import WishlistPublic from './WishlistPublic';
import WishlistPrivate from './WishlistPrivate';
import AboutUs from './AboutUs';
import Pairing from './Pairing';
import Messaging from './Messaging';
import SecureOrg from './SecureOrg';

const Main = (props) => {
  return (
    <div>
      <Switch>
        <Route exact path="/" component={Landing} />
        <Route path="/dashboard" component={Dashboard} />
        <Route path="/wishlist" component={WishlistPublic} />
        <Route path="/aboutus" component={AboutUs} />
        <Route path="/secure/wishlist" component={WishlistPrivate} /> {/* Secure */}
        <Route path="/secure/pairing" component={Pairing} /> {/* Secure */}
        <Route path="/secure/messaging" component={Messaging} /> {/* Secure */}
        <Route path="/secure/org" component={SecureOrg} /> {/* Secure */}
      </Switch>
      <footer>
        <Link to="/">
          <button>
            Home
          </button>
        </Link>
      </footer>
    </div>
  );
};

export default Main;

Update anchor tag in footer to react Link component


/* Javascript eval */

mystructure = { a:39, b:3, addStuff : function(c,d) { return c+d; } };

mystring = JSON.stringify(mystructure, undefined);

mynewstructure = eval(mystring);

result = mynewstructure.addStuff(mynewstructure.a, mynewstructure.b);

/* Javascript eval */

mystructure = { a:39, b:3, addStuff : function(c,d) { return c+d; } };

mystring = JSON.stringify(mystructure, undefined);

mynewstructure_a = eval(mystring);
mynewstructure_b = eval("{ a:139, b:33, addStuff : function(c,d) { return c+d; } }");

result = mynewstructure_b.addStuff(mynewstructure_a.a, mynewstructure_a.b) == 42;

Test modified. 'JSON.stringify' does not serialize functions.


import React, { PureComponent } from 'react';
import Box from '../box';
import PropTypes from 'prop-types';
import cx from 'classnames';
import omit from 'lodash.omit';
import theme from './theme.css';

const factory = (baseType, type, defaultElement) => {
  class Text extends PureComponent {
    isSoft(color) {
      if (color !== 'white' && color !== 'teal') {
        return false;
      }

      return this.props.soft;
    }

    render() {
      const { children, className, color, element, ...others } = this.props;

      const isSoft = this.isSoft(color);

      const classNames = cx(
        theme[baseType],
        theme[type],
        theme[color],
        {
          [theme['soft']]: isSoft,
        },
        className,
      );

      const Element = element || defaultElement;

      const rest = omit(others, ['soft']);

      return (
        <Box className={classNames} data-teamleader-ui={baseType} element={Element} {...rest}>
          {children}
        </Box>
      );
    }
  }

  Text.propTypes = {
    children: PropTypes.node,
    className: PropTypes.string,
    color: PropTypes.oneOf(['white', 'neutral', 'mint', 'teal', 'violet', 'ruby', 'gold', 'aqua']),
    element: PropTypes.node,
    soft: PropTypes.bool,
  };

  Text.defaultProps = {
    element: null,
    soft: false,
  };

  return Text;
};

export { factory as textFactory };

import React, { PureComponent } from 'react';
import Box from '../box';
import PropTypes from 'prop-types';
import cx from 'classnames';
import omit from 'lodash.omit';
import theme from './theme.css';

const factory = (baseType, type, defaultElement) => {
  class Text extends PureComponent {
    isSoft(color) {
      if (color !== 'white' && color !== 'teal') {
        return false;
      }

      return this.props.soft;
    }

    render() {
      const { children, className, color, element, ...others } = this.props;

      const isSoft = this.isSoft(color);

      const classNames = cx(
        theme[baseType],
        theme[type],
        theme[color],
        {
          [theme['soft']]: isSoft,
        },
        className,
      );

      const Element = element || defaultElement;

      const rest = omit(others, ['soft']);

      return (
        <Box className={classNames} data-teamleader-ui={baseType} element={Element} {...rest}>
          {children}
        </Box>
      );
    }
  }

  Text.propTypes = {
    children: PropTypes.node,
    className: PropTypes.string,
    color: PropTypes.oneOf(['white', 'neutral', 'mint', 'teal', 'violet', 'ruby', 'gold', 'aqua']),
    element: PropTypes.node,
    soft: PropTypes.bool,
  };

  Text.defaultProps = {
    color: 'teal',
    element: null,
    soft: false,
  };

  return Text;
};

export { factory as textFactory };

Set the default color to teal


var loader = require("@loader");
var nodeRequire = loader._nodeRequire;

module.exports = nodeRequire("less");

var nodeRequire = require("@node-require");
module.exports = nodeRequire("less");

Use the new @node-require feature of steal


basePath = '../';

files = [
  ANGULAR_SCENARIO,
  ANGULAR_SCENARIO_ADAPTER,
  'test/e2e/**/*.coffee'
];

autoWatch = false;

browsers = ['Firefox', 'chromium'];

singleRun = true;

proxies = {
  '/': 'http://localhost:3333/'
};

urlRoot = '__test/';

module.exports = function(config) {
  config.set({
    basePath: '../',

    files: [
      'test/e2e/**/*.coffee'
    ],

    autoWatch: false,

    browsers: ['Firefox', 'Chrome'],

    frameworks: ['ng-scenario'],

    singleRun: true,

    proxies: {
      '/': 'http://localhost:3333/'
    },

    urlRoot: '__test/',

    plugins: [
      'karma-coffee-preprocessor',
      'karma-junit-reporter',
      'karma-chrome-launcher',
      'karma-firefox-launcher',
      'karma-jasmine',
      'karma-ng-scenario'
    ],

    junitReporter: {
      outputFile: 'test_out/e2e.xml',
      suite: 'e2e'
    }
  });
};

Update step-3 to Karma 0.10.


const MONGOOSE_CONSOLE_DEFAULT_PORT = 8080;
var express = require('express');
var app = express();

var path = __dirname + '';
var port = process.env.CONSOLE_PORT || MONGOOSE_CONSOLE_DEFAULT_PORT;

app.use(express.static(path));
app.get('*', function(req, res) {
    res.sendFile(path + '/index.html');
});

app.post('/savePrometheusConfiguration', function (req, res) {
    var file_name = "prometheus.yml"
    var file_content = req.body;

    var stream = fs.createWriteStream(file_name);
    stream.once('open', function () {
        stream.write(file_content);
        stream.end();
    });
});

app.listen(port);

const MONGOOSE_CONSOLE_DEFAULT_PORT = 8080;
const PROMETHEUS_CONFIGURATION_PATH = '/configuration/prometheus.yml';

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

var app = express();

var path = __dirname + '';
var port = process.env.CONSOLE_PORT || MONGOOSE_CONSOLE_DEFAULT_PORT;

app.use(express.static(path));
app.use(bodyParser.json()); // NOTE: Supporting JSON-encoded bodies
app.use(express.multipart()); // NOTE: We're saving Prometheus configuration via the server

// NOTE: Configurating server to serve index.html since during the production ...
// build Angular converts its html's to only one file.
app.get('*', function(req, res) {
    res.sendFile(path + '/index.html');
});

app.post('/savefile', function (req, res) {
    var fileName = req.body.fileName;
    var fileContent = req.body.fileContent;

    var stream = fs.createWriteStream(fileName);
    stream.once('open', function () {
        stream.write(fileContent);
        stream.end();
    });

    fs.writeFile(PROMETHEUS_CONFIGURATION_PATH, fileContent, function(err) {
        if(err) {
            return console.log(err);
        }

        console.log("Prometheus configuration has been updated.");
    });
});

app.listen(port);

Add saving file via NodeJS's fs.


const DEBUG = false;
const ctxHEIGHT = 100, ctxWIDTH = 280;
const DEFAULTS = {
	"blacklist":""
    ,"whitelist":""
    ,"timeout":30000
    ,"checkType":"HEAD"
    ,"ignore404":false
    ,"showStatusCode":true
    ,"ignoreHash":true
    ,"validateDomainOnly":false
    ,"maxQueueLength":2
    ,colour:{
        "success":["#080","#fff"]
        ,"failed":["#800","#fff"]
        ,"unknown":["#ff0","#000"]
        ,"content-match":["rgba(255,204,0,1)","#000"]
        ,"pending":["#cecece","#888"]
    }
}

const DEBUG = false;
const ctxHEIGHT = 100, ctxWIDTH = 280;
const DEFAULTS = {
    TESTVALUE:"ABC"
	,"blacklist":""
    ,"whitelist":""
    ,"timeout":30000
    ,"checkType":"HEAD"
    ,"ignore404":false
    ,"showStatusCode":true
    ,"ignoreHash":true
    ,"validateDomainOnly":false
    ,"maxQueueLength":2
    ,colour:{
        "success":["#080","#fff"]
        ,"failed":["#800","#fff"]
        ,"unknown":["#ff0","#000"]
        ,"content-match":["rgba(255,204,0,1)","#000"]
        ,"pending":["#cecece","#888"]
    }
}

Add Jasmine for basic unit testing.


'use strict';

(function () {

   // var addButton = document.querySelector('.btn-add');
   // var deleteButton = document.querySelector('.btn-delete');
   var clickNbr = document.querySelector('#click-nbr');
   // var loginButton = document.querySelector('.')
   var apiUrl = appUrl + '/api/:id/polls';

   function updateUserPolls (data) {
      var clicksObject = JSON.parse(data);
      clickNbr.innerHTML = clicksObject.clicks;
   }

   // ajaxFunctions.ready(ajaxFunctions.ajaxRequest('GET', apiUrl, updateUserPolls));

   addButton.addEventListener('click', function () {

      ajaxFunctions.ajaxRequest('POST', apiUrl, function () {
         ajaxFunctions.ajaxRequest('GET', apiUrl, updateUserPolls);
      });

   }, false);

   // deleteButton.addEventListener('click', function () {

   //    ajaxFunctions.ajaxRequest('DELETE', apiUrl, function () {
   //       ajaxFunctions.ajaxRequest('GET', apiUrl, updateUserPolls);
   //    });

   // }, false);

})();

'use strict';

(function () {

   // var addButton = document.querySelector('.btn-add');
   // var deleteButton = document.querySelector('.btn-delete');
   var userPolls = document.querySelector('#user-polls');
   // var loginButton = document.querySelector('.')
   var apiUrl = appUrl + '/api/:id/polls';

   function updateUserPolls (data) {
      var pollsObject = JSON.parse(data);
      // polls should be an array so fix this
      userPolls.innerHTML = pollsObject.polls;
   }

   // ajaxFunctions.ready(ajaxFunctions.ajaxRequest('GET', apiUrl, updateUserPolls));

   addButton.addEventListener('click', function () {

      ajaxFunctions.ajaxRequest('POST', apiUrl, function () {
         ajaxFunctions.ajaxRequest('GET', apiUrl, updateUserPolls);
      });

   }, false);

   // deleteButton.addEventListener('click', function () {

   //    ajaxFunctions.ajaxRequest('DELETE', apiUrl, function () {
   //       ajaxFunctions.ajaxRequest('GET', apiUrl, updateUserPolls);
   //    });

   // }, false);

})();

Change click number to user polls


import axios from 'axios'

const BLACKLIST_USER_API_BASE_URL = "http://localhost:8080/blacklist_user";

class BlacklistUserService {

    getBlacklistUsers() {
        return axios.get(BLACKLIST_USER_API_BASE_URL);
    }

    createBlacklistUser(blacklistUser) {
        return axios.post(BLACKLIST_USER_API_BASE_URL, blacklistUser);
    }

    getBlacklistUser(blacklistUserId) {
        return axios.get(BLACKLIST_USER_API_BASE_URL + '/' + blacklistUserId);
    }

    deleteBlacklistUser(blacklistUserId) {
        return axios.delete(BLACKLIST_USER_API_BASE_URL + '/' + blacklistUserId);
    }
}

export default new BlacklistUserService()

import axios from 'axios'

const BLACKLIST_USER_API_BASE_URL = "http://192.168.0.136:8080/blacklist_user";

class BlacklistUserService {

    getBlacklistUsers() {
        return axios.get(BLACKLIST_USER_API_BASE_URL);
    }

    createBlacklistUser(blacklistUser) {
        return axios.post(BLACKLIST_USER_API_BASE_URL, blacklistUser);
    }

    getBlacklistUser(blacklistUserId) {
        return axios.get(BLACKLIST_USER_API_BASE_URL + '/' + blacklistUserId);
    }

    deleteBlacklistUser(blacklistUserId) {
        return axios.delete(BLACKLIST_USER_API_BASE_URL + '/' + blacklistUserId);
    }
}

export default new BlacklistUserService()

Fix blacklist user api url.


var pythonMirror = process.env.npm_config_python_mirror || process.env.PYTHON_MIRROR || 'https://www.python.org/ftp/python/'

var buildTools = {
  installerName: 'BuildTools_Full.exe',
  installerUrl: 'http://download.microsoft.com/download/5/f/7/5f7acaeb-8363-451f-9425-68a90f98b238/visualcppbuildtools_full.exe',
  logName: 'build-tools-log.txt'
}

var python = {
  installerName: 'python-2.7.11.msi',
  installerUrl: pythonMirror.replace(/\/*$/, '/2.7.11/python-2.7.11.msi'),
  targetName: 'python27',
  logName: 'python-log.txt'
}

module.exports = {
  buildTools,
  python
}

var pythonMirror = process.env.npm_config_python_mirror || process.env.PYTHON_MIRROR || 'https://www.python.org/ftp/python/'

var buildTools = {
  installerName: 'BuildTools_Full.exe',
  installerUrl: 'https://download.microsoft.com/download/5/f/7/5f7acaeb-8363-451f-9425-68a90f98b238/visualcppbuildtools_full.exe',
  logName: 'build-tools-log.txt'
}

var python = {
  installerName: 'python-2.7.11.msi',
  installerUrl: pythonMirror.replace(/\/*$/, '/2.7.11/python-2.7.11.msi'),
  targetName: 'python27',
  logName: 'python-log.txt'
}

module.exports = {
  buildTools,
  python
}

Use HTTPS - Thanks to @grander


/*global describe, beforeEach, module, inject, it, spyOn, expect, $, console */

describe('dfAutocompleteDatalist', function() {
  'use strict';

  var scope, $compile;
  beforeEach(module('dataform.directives'));
  beforeEach(inject(function(_$rootScope_, _$compile_, _$window_) {
    scope = _$rootScope_.$new();
    $compile = _$compile_;
  }));

  describe('compiling this directive', function() {
    it('should throw an error if there is no df-list attribute value', function() {
      function compile() {
        $compile('<input df-autocomplete-datalist>')(scope);
      }
      expect(compile).toThrow();
    });
    it('should throw an error if the df-list attribute points to a nonexistent datalist', function() {
      function compile() {
        $compile('<input df-autocomplete-datalist=foo>')(scope);
      }
      expect(compile).toThrow();
    });
  });
});

describe('dfDatalist', function() {
  'use strict';

  var scope, $compile;
  beforeEach(module('dataform.directives'));
  beforeEach(inject(function(_$rootScope_, _$compile_, _$window_) {
    scope = _$rootScope_.$new();
    $compile = _$compile_;
  }));

  describe('compiling this directive', function() {
    it('should not throw an error', function() {
      function compile() {
        $compile('<datalist df-datalist id=foo></datalist>')(scope);
      }
      expect(compile).not.toThrow();
    });
  });
});

/*global describe, beforeEach, module, inject, it, spyOn, expect, $, console */

describe('dfAutocompleteDatalist', function() {
  'use strict';

  var scope, $compile;
  beforeEach(module('dataform.directives'));
  beforeEach(inject(function(_$rootScope_, _$compile_, _$window_) {
    scope = _$rootScope_.$new();
    $compile = _$compile_;
  }));

  describe('compiling this directive', function() {
    it('should throw an error if there is no df-list attribute value', function() {
      function compile() {
        $compile('<input df-autocomplete-datalist>')(scope);
      }
      expect(compile).toThrow();
    });
    it('should throw an error if the df-list attribute points to a nonexistent datalist', function() {
      function compile() {
        $compile('<input df-autocomplete-datalist=foo>')(scope);
      }
      expect(compile).toThrow();
    });
  });
});

describe('dfDatalist', function() {
  'use strict';

  var scope, $compile;
  beforeEach(module('dataform.directives'));
  beforeEach(inject(function(_$rootScope_, _$compile_, _$window_) {
    scope = _$rootScope_.$new();
    $compile = _$compile_;
  }));

  describe('compiling this directive', function() {
    it('should not throw an error', function() {
      function compile() {
        $compile('<ol df-datalist id=foo></ol>')(scope);
      }
      expect(compile).not.toThrow();
    });
  });
});

Update autocomplete spec to use <ol>


'use strict'

const jspm = require('gulp-jspm')
const sourcemaps = require('gulp-sourcemaps')
const filenames = require('gulp-filenames')
const flatten = require('gulp-flatten')
const hash = require('gulp-hash-filename')
const util = require('gulp-util')

exports.dep = ['bundle:tscompile']
exports.fn = function (gulp, paths, mode, done) {
  return gulp.src(paths.src + './app.js', { base: '.' })
    .pipe(!mode.production ? sourcemaps.init({loadMaps: true}) : util.noop())
    .pipe(jspm({selfExecutingBundle: true, minify: mode.production, mangle: mode.production, fileName: 'app-bundle'}))
    .pipe(mode.production ? hash({'format': '{hash}{ext}'}) : util.noop())
    .pipe(filenames('appbundle'))
    .pipe(!mode.production ? sourcemaps.write('.') : util.noop())
    .pipe(flatten())
    .pipe(gulp.dest('wwwroot'))
}

'use strict'

const jspm = require('gulp-jspm')
const sourcemaps = require('gulp-sourcemaps')
const filenames = require('gulp-filenames')
const flatten = require('gulp-flatten')
const hash = require('gulp-hash-filename')
const util = require('gulp-util')

exports.dep = ['bundle:tscompile']
exports.fn = function (gulp, paths, mode, done) {
  return gulp.src(paths.src + './main.js', { base: '.' })
    .pipe(!mode.production ? sourcemaps.init({loadMaps: true}) : util.noop())
    .pipe(jspm({selfExecutingBundle: true, minify: mode.production, mangle: mode.production, fileName: 'app-bundle'}))
    .pipe(mode.production ? hash({'format': '{hash}{ext}'}) : util.noop())
    .pipe(filenames('appbundle'))
    .pipe(!mode.production ? sourcemaps.write('.') : util.noop())
    .pipe(flatten())
    .pipe(gulp.dest('wwwroot'))
}

Use the correct entry point.


// assign dedent to shift-tab

var shift_tab_extension = (function() {

    var dedentKey = { "Shift-Tab":"indentLess" };
    var key   = IPython.keyboard.keycodes;

    /**
     * Concatenate associative array objects
     *
     * Source: http://stackoverflow.com/questions/2454295/javascript-concatenate-properties-from-multiple-objects-associative-array
     */
    function collect() {
    var ret = {};
    var len = arguments.length;
    for (var i=0; i<len; i++) {
        for (p in arguments[i]) {
            if (arguments[i].hasOwnProperty(p)) {
                ret[p] = arguments[i][p];
            }
        }
    }
    return ret;
}

    /**
     * Intercept codemirror onKeyEvent in codecell
     *
     * @return {Boolean} returns false if hotkey is found, otherwise call original function
     */
    var intercept_codemirror_keyevent = function (cm, event) {
        /* Dummy for shift+Tab, who knows why */
        if (event.type == 'keydown' && event.which == key.tab && event.shiftKey) {
            return false;
        };
        return this.handle_codemirror_keyevent(cm,event);
    }

    function assign_key(cell) {
        var keys = cell.code_mirror.getOption('extraKeys');
        cell.code_mirror.setOption('onKeyEvent',$.proxy(intercept_codemirror_keyevent,cell));
        cell.code_mirror.setOption('extraKeys', collect(keys, dedentKey ));
    }

    /**
     * Initialize newly created cell
     *
     * @param {Object} event
     * @param {Object} nbcell notebook cell
     */
    create_cell = function (event,nbcell,nbindex) {
        var cell = nbcell.cell;
        if ((cell instanceof IPython.CodeCell)) { assign_key(cell); }
    };

    /**
     * Initialize all cells
     *
     */
    var cells = IPython.notebook.get_cells();
    for(var i in cells){
        var cell = cells[i];
        if ((cell instanceof IPython.CodeCell)) { assign_key(cell); }
    };

    $([IPython.events]).on('create.Cell',create_cell);
})();

// map shift-tab to "indentLess" command in codemirror

"use strict";
var add_edit_shortcuts = {
        'Shift-tab' : {
            help    : 'indent less',
            help_index : 'eb',
            handler : function (event) {
                console.log("shift-tab")
                var cell = IPython.notebook.get_selected_cell();
                cell.code_mirror.execCommand('indentLess')
                return false;
         }
     },
};

IPython.keyboard_manager.edit_shortcuts.add_shortcuts(add_edit_shortcuts);

Use keyboard_manager instead of hack for 'indentLess' hotkey


var metaRouter = require('../');
var routesLoader = require('../lib/routes-loader');
var DataHolder = require('raptor-async/DataHolder');
var nodePath = require('path');

module.exports = function matchFactory(routes) {
    var matcher;
    var matcherDataHolder;

    if (typeof routes === 'string') {
        routes = nodePath.resolve(process.cwd(), routes);

        matcherDataHolder = new DataHolder();
        routesLoader.load(routes, function(err, routes) {
            if (err) {
                return matcherDataHolder.reject(err);
            }

            matcher = metaRouter.buildMatcher(routes);
            matcherDataHolder.resolve(matcher);
        });
    } else if (typeof routes.match === 'function') {
        // The provided routes are already a matcher
        matcher = routes;
    } else {
        matcher = metaRouter.buildMatcher(routes);
    }

    function go(matcher, req, res, next) {
        var match = matcher.match(req.path, req.method);
        if (match) {
            req.route = match;
        }

        next();
    }

    return function match(req, res, next) {
        if (matcher) {
            go(matcher, req, res, next);
        } else {
            matcherDataHolder.done(function(err, matcher) {
                if (err) {
                    return next(err);
                }

                go(matcher, req, res, next);
            });
        }

    };
};

var metaRouter = require('../');
var DataHolder = require('raptor-async/DataHolder');
var nodePath = require('path');

module.exports = function matchFactory(routes) {
    var matcher;
    var matcherDataHolder;

    if (typeof routes === 'string') {
        routes = nodePath.resolve(process.cwd(), routes);
        matcherDataHolder = new DataHolder();

        metaRouter.buildMatcher(routes, function(err, matcher) {
            if (err) {
                return matcherDataHolder.reject(err);
            }

            matcherDataHolder.resolve(matcher);
        });
    } else if (typeof routes.match === 'function') {
        // The provided routes are already a matcher
        matcher = routes;
    } else {
        matcher = metaRouter.buildMatcher(routes);
    }

    function go(matcher, req, res, next) {
        var match = matcher.match(req.path, req.method);
        if (match) {
            req.route = match;
        }

        next();
    }

    return function match(req, res, next) {
        if (matcher) {
            go(matcher, req, res, next);
        } else {
            matcherDataHolder.done(function(err, matcher) {
                if (err) {
                    return next(err);
                }

                go(matcher, req, res, next);
            });
        }

    };
};

Use the async version of buildMatcher for the middleware loading


#!/usr/bin/env node
'use strict';

var dokkuGitRemoteParser = require('dokku-git-remote-parser')
var dokkuAppSsh = require('dokku-app-ssh')
var cp = require('child_process')
var _ = require('highland')
var minimist = require('minimist');
var cleanHelpCommand = require('./clean-help-command')

var argv = minimist(process.argv.slice(2))
var command = argv._.join(' ')

dokkuGitRemoteParser(function (err, host, appName) {
  if (err) {
    console.error('Dokku Toolbelt Error:', err.message)
    process.exit()
  }

  try {
    var sshCommand = dokkuAppSsh(host, command, appName)
  } catch (err) {
    console.error('Dokku Toolbelt Error:', err.message)
    process.exit()
  }

  //run the command
  var commandArgs = sshCommand.split(' ')
  var cmd = commandArgs.shift()
  var dt = cp.spawn(cmd, commandArgs)

  _(dt.stdout)
    .map(function (buffer) {
      return buffer.toString()
    })
    .through(function (stream) {
      if (argv._[0] === 'help' || !argv._[0])
        return cleanHelpCommand(stream)

      return stream
    })
    .pipe(process.stdout)

  dt.stderr.pipe(process.stderr)
})

#!/usr/bin/env node
'use strict';

var dokkuGitRemoteParser = require('dokku-git-remote-parser')
var dokkuAppSsh = require('dokku-app-ssh')
var cp = require('child_process')
var _ = require('highland')
var minimist = require('minimist');
var cleanHelpCommand = require('./clean-help-command')

var argv = minimist(process.argv.slice(2))
Object.keys(argv)
  .filter(function (arg) {
    return arg !== '_'
  })
  .forEach(function (arg) {
    argv._.push('-' + arg)
  })
var command = argv._.join(' ')

dokkuGitRemoteParser(function (err, host, appName) {
  if (err) {
    console.error('Dokku Toolbelt Error:', err.message)
    process.exit()
  }

  try {
    var sshCommand = dokkuAppSsh(host, command, appName)
  } catch (err) {
    console.error('Dokku Toolbelt Error:', err.message)
    process.exit()
  }

  //run the command
  var commandArgs = sshCommand.split(' ')
  var cmd = commandArgs.shift()
  var dt = cp.spawn(cmd, commandArgs)

  _(dt.stdout)
    .map(function (buffer) {
      return buffer.toString()
    })
    .through(function (stream) {
      if (argv._[0] === 'help' || !argv._[0])
        return cleanHelpCommand(stream)

      return stream
    })
    .pipe(process.stdout)

  dt.stderr.pipe(process.stderr)
})

Handle missed command flags (-t)


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

var converters = {
    'distance': [
        'mil', 'in', 'ft', 'yd', 'mi', 'cp',
        'rd', 'A', 'n', 'µ', 'mm', 'cm', 'm',
        'km', 'ly', 'ld', 'lh', 'lm', 'ls'
    ],
    'weight': [
        'ct', 'pt', 'adr', 'tdr', 'mgr', 'tgr',
        'g', 'lhwt', 'shwt', 'kg', 'Mg', 'mg', 'aoz',
        'toz', 'dwt', 'alb', 'lb', 'tlb', 'slug',
        'st', 'lAT', 'sAT', 'ltn', 'stn', 't'
    ],
    'area': [
        'in2', 'ft2', 'yd2', 'mi2', 'ac', 'ha',
        'mm2', 'cm2', 'm2', 'km2'
    ],
    'computer_storage': [
        'b', 'kb', 'Mb', 'Gb', 'Tb', 'Pb',
        'Eb', 'Zb', 'Yb', 'nibble', 'kB',
        'MB', 'GB', 'TB', 'PB', 'EB', 'ZB',
        'YB', 'B'
    ]
};

module.exports = function(options) {

    /*
     * TODO Support for large numbers using big.js https://github.com/MikeMcl/big.js/
     * when options contains big: true
     */

    this.convert = function(value, unit) {
        var c = null;
        Object.keys(converters).forEach(function(key) {
            if (converters[key].indexOf(unit) != -1) {
                return c = new converter(key, value, unit);
            }
        });
        return c;
    }

};

var converter = require('./converter');
var getConverters = require('./utils/get_converters');

module.exports = function(options) {

    /*
     * TODO Support for large numbers using big.js https://github.com/MikeMcl/big.js/
     * when options contains big: true
     */

    this.convert = function(value, unit) {
        var converters = getConverters();
        var c = null;
        Object.keys(converters).forEach(function(key) {
            if (converters[key].indexOf(unit) != -1) {
                return c = new converter(key, value, unit);
            }
        });
        return c;
    }

};

Add dynamic loading of converters


import Model from 'components/Model'
import Settings from 'Settings'

export default class Organization extends Model {
	static resourceName = 'Organization'
	static listName = 'organizationList'

	static TYPE = {
		ADVISOR_ORG: 'ADVISOR_ORG',
		PRINCIPAL_ORG: 'PRINCIPAL_ORG'
	}

	static schema = {
		shortName: '',
		longName: '',
		identificationCode: null,
		type: '',
		parentOrg: null,
		childrenOrgs: [],
		approvalSteps: [],
		positions: [],
		tasks: []
	}

	isAdvisorOrg() {
		return this.type === Organization.TYPE.ADVISOR_ORG
	}

	static humanNameOfType(type) {

		if (type === Organization.TYPE.PRINCIPAL_ORG) {
			return Settings.fields.advisor.org.name
		}
		else {
			return Settings.fields.advisor.org.name
		} // TODO do not assume that if not of type TYPE.PRINCIPAL_ORG it is an advisor
	}

	humanNameOfType(type) {
		return Organization.humanNameOfType(this.type)
	}

	toString() {
		return this.shortName || this.longName || this.identificationCode
	}
}

import Model from 'components/Model'
import Settings from 'Settings'

export default class Organization extends Model {
	static resourceName = 'Organization'
	static listName = 'organizationList'

	static TYPE = {
		ADVISOR_ORG: 'ADVISOR_ORG',
		PRINCIPAL_ORG: 'PRINCIPAL_ORG'
	}

	static schema = {
		shortName: '',
		longName: '',
		identificationCode: null,
		type: '',
		parentOrg: null,
		childrenOrgs: [],
		approvalSteps: [],
		positions: [],
		tasks: []
	}

	isAdvisorOrg() {
		return this.type === Organization.TYPE.ADVISOR_ORG
	}

	static humanNameOfType(type) {

		if (type === Organization.TYPE.PRINCIPAL_ORG) {
			return Settings.fields.principal.org.name
		}
		else {
			return Settings.fields.advisor.org.name
		} // TODO do not assume that if not of type TYPE.PRINCIPAL_ORG it is an advisor
	}

	humanNameOfType(type) {
		return Organization.humanNameOfType(this.type)
	}

	toString() {
		return this.shortName || this.longName || this.identificationCode
	}
}

Fix use of wrong setting for organizations


AutoForm.hooks({
  submitPostForm: {

    before: {
      submitPost: function(doc) {

        this.template.$('button[type=submit]').addClass('loading');

        var post = doc;

        // ------------------------------ Checks ------------------------------ //

        if (!Meteor.user()) {
          Messages.flash(i18n.t('you_must_be_logged_in'), 'error');
          return false;
        }

        // ------------------------------ Callbacks ------------------------------ //

        // run all post submit client callbacks on properties object successively
        post = postSubmitClientCallbacks.reduce(function(result, currentFunction) {
            return currentFunction(result);
        }, post);

        return post;
      }
    },

    onSuccess: function(operation, post) {
      this.template.$('button[type=submit]').removeClass('loading');
      trackEvent("new post", {'postId': post._id});
      Router.go('post_page', {_id: post._id});
      if (post.status === STATUS_PENDING) {
        Messages.flash(i18n.t('thanks_your_post_is_awaiting_approval'), 'success');
      }
    },

    onError: function(operation, error) {
      this.template.$('button[type=submit]').removeClass('loading');
      Messages.flash(error.message.split('|')[0], 'error'); // workaround because error.details returns undefined
      Messages.clearSeen();
      // $(e.target).removeClass('disabled');
      if (error.error == 603) {
        var dupePostId = error.reason.split('|')[1];
        Router.go('post_page', {_id: dupePostId});
      }
    }

  }
});

AutoForm.hooks({
  submitPostForm: {

    before: {
      method: function(doc) {

        this.template.$('button[type=submit]').addClass('loading');

        var post = doc;

        // ------------------------------ Checks ------------------------------ //

        if (!Meteor.user()) {
          Messages.flash(i18n.t('you_must_be_logged_in'), 'error');
          return false;
        }

        // ------------------------------ Callbacks ------------------------------ //

        // run all post submit client callbacks on properties object successively
        post = postSubmitClientCallbacks.reduce(function(result, currentFunction) {
            return currentFunction(result);
        }, post);

        return post;
      }
    },

    onSuccess: function(operation, post) {
      this.template.$('button[type=submit]').removeClass('loading');
      trackEvent("new post", {'postId': post._id});
      Router.go('post_page', {_id: post._id});
      if (post.status === STATUS_PENDING) {
        Messages.flash(i18n.t('thanks_your_post_is_awaiting_approval'), 'success');
      }
    },

    onError: function(operation, error) {
      this.template.$('button[type=submit]').removeClass('loading');
      Messages.flash(error.message.split('|')[0], 'error'); // workaround because error.details returns undefined
      Messages.clearSeen();
      // $(e.target).removeClass('disabled');
      if (error.error == 603) {
        var dupePostId = error.reason.split('|')[1];
        Router.go('post_page', {_id: dupePostId});
      }
    }

  }
});

Fix a bug where the post submit autoform hook wasn't called


root.Chem = {
  Atom: Atom,
  Bond: Bond,
  Element: Element,
  Data: {
    Elements: Elements
  }
};
})
(this);

root.Chem = {
  Atom: Atom,
  Bond: Bond,
  Element: Element,
  Molecule: Molecule,
  Data: {
    Elements: Elements
  }
};
})
(this);

Add `Molecule` in export object.


'use strict';

/* eslint-env node */

module.exports = {
  extends: 'recommended',

  rules: {
    'block-indentation': false,
    'img-alt-attributes': false,
    'triple-curlies': false,
    'html-comments': false,
  },
};

'use strict';

/* eslint-env node */

module.exports = {
  extends: 'recommended',

  rules: {
    'img-alt-attributes': false,
    'triple-curlies': false,
    'html-comments': false,
  },
};

Enable indentation rule for templates


const MySql = require("./MySql.js");
const entities = require("entities");

class Meet{
  constructor(obj){
    Object.keys(obj).forEach(k=>this[k]=entities.decodeHTML(obj[k]));
  }

  asMarkdown(){
    return `*${this.post_title}* ${this.meet_start_time}\n${this.guid}`;
  }

}
Meet.fromObjArray = function(objArray){
  return objArray.map(o=>new Meet(o));
};

function getAllMeets(){
  return MySql.query(`SELECT * FROM wp_posts WHERE post_type = 'meet'`)
    .then(results=>Meet.fromObjArray(results.results));
}

function getUpcomingMeets(){
  return MySql.query(`
    SELECT CAST(meta_value AS DATE) AS meet_start_time, wp_posts.*
    FROM wp_postmeta LEFT JOIN wp_posts ON post_id=ID
    WHERE meta_key = 'meet_start_time' AND CAST(meta_value AS DATE) > CURDATE()
  `).then(results=>Meet.fromObjArray(results.results));
}

module.exports = {getAllMeets,getUpcomingMeets};

const MySql = require("./MySql.js");
const entities = require("entities");

class Meet{
  constructor(obj){
    Object.keys(obj).forEach(k=>this[k]=entities.decodeHTML(obj[k]));
  }

  asMarkdown(){
    return `[${this.post_title}](${this.guid}) _${this.meet_start_time}_`;
  }

}
Meet.fromObjArray = function(objArray){
  return objArray.map(o=>new Meet(o));
};

function getAllMeets(){
  return MySql.query(`SELECT * FROM wp_posts WHERE post_type = 'meet'`)
    .then(results=>Meet.fromObjArray(results.results));
}

function getUpcomingMeets(){
  return MySql.query(`
    SELECT CAST(meta_value AS DATE) AS meet_start_time, wp_posts.*
    FROM wp_postmeta LEFT JOIN wp_posts ON post_id=ID
    WHERE meta_key = 'meet_start_time' AND CAST(meta_value AS DATE) > CURDATE()
  `).then(results=>Meet.fromObjArray(results.results));
}

module.exports = {getAllMeets,getUpcomingMeets};

FIX formatting on meet markdown


$(document).ready(function () {
    $(document).on('keyloaded', function () {
        var hostname = $('#hostname').val();
        $.ajax({
            url: appConfig.getUrl('/log/' + hostname),
            dataType: 'jsonp',
            method: 'GET'
        }).done(function (data) {

            var hasHeader = false;
            var $table = $('

');
            var $tbody = $('

');
            var $trh = $('

');
            for (var i in data) {
                var log = data[i]._source;

                var $tr = $('

').addClass(log['level']);
                for (var prop in log) {
                    if (hasHeader == false) {
                        $trh.append('' + prop + '	');
                    }
                    var value = (prop === 'date' ? moment(log[prop]).format('YYYY/MM/DD HH:MM:SS') : log[prop]);
                    $tr.append('' + value + '	');
                }
                if (hasHeader == false) {
                    var $thead = $('

').append($trh);
                    $table.append($thead);
                    hasHeader = true;
                }
                $tbody.append($tr);
            }
            $table.append($tbody);
            $('#dtContainer').append($table);
            $('#dtContainer table:eq(0)').dataTable();
        });
    });
});

$(document).ready(function () {
    $(document).on('keyloaded', function () {
        var hostname = $('#hostname').val();
        $.ajax({
            url: appConfig.getUrl('/log/' + hostname),
            dataType: 'jsonp',
            method: 'GET'
        }).done(function (data) {

            var hasHeader = false;
            var $table = $('

');
            var $tbody = $('

');
            var $trh = $('

');
            var nbCol = 0;
            var createdAtIndex = 0;
            for (var i in data) {
                var log = data[i]._source;

                var $tr = $('

').addClass(log['level']);
                for (var prop in log) {
                    if (hasHeader == false) {
                        $trh.append('' + prop + '	');
                    }
                    if (prop === 'createdAt' || prop === 'date') {
                        createdAtIndex = nbCol;
                        $tr.append('' + moment(log[prop]).format('YYYY/MM/DD HH:MM:SS') + '	');
                    }
                    else {
                        $tr.append('' + log[prop] + '	');
                    }
                    nbCol++;
                }
                if (hasHeader == false) {
                    var $thead = $('

').append($trh);
                    $table.append($thead);
                    hasHeader = true;
                }
                $tbody.append($tr);
            }
            $table.append($tbody);
            $('#dtContainer').append($table);
            $('#dtContainer table:eq(0)').dataTable({
                "order": [[createdAtIndex, "desc"]]
            });
        });
    });
});

Add createdAt when logging a message and sort results by createdAt date


// runtime fixes for ugly mantisbt elements
/*
$("input[type=text]").addClass("form-control lwidth");
$("input[type=password]").addClass("form-control lwidth");
$("input[type=file]").addClass("btn btn-default");
$("textarea").addClass("form-control");
$(".button").addClass("btn btn-default btn-sm");
$(".button-small").addClass("btn btn-default btn-sm");
$("select").addClass("btn btn-default btn-xs lwidth");
*/
/*var pageTitle = $(document).attr("title");
if (pageTitle === "Report Issue - FreeCAD Tracker") {
  console.log('it works');
}*/

// runtime fixes for ugly mantisbt elements
/*
$("input[type=text]").addClass("form-control lwidth");
$("input[type=password]").addClass("form-control lwidth");
$("input[type=file]").addClass("btn btn-default");
$("textarea").addClass("form-control");
$(".button").addClass("btn btn-default btn-sm");
$(".button-small").addClass("btn btn-default btn-sm");
$("select").addClass("btn btn-default btn-xs lwidth");
*/
// update #summary, #description, #steps_to_reproduce, #additional_info
function addPlaceholderText(el, txt) {
  $(el).attr('placeholder', txt);
}

function addReportTemplate() {
  var summaryText = `Short description about the issue`;

  var descText = `Explain the details of the bug/feature/patch. Make sure to \
explain what you were doing when it happened, what you expected to happen, and \
what actually was the result.`;

  var stepsText = `Please produce a step-by-step list (as detailed as possible) \
to help the devs and testers reproduce your issue.
Ex:
1. Launch FreeCAD
2. Open Sketcher through the dropdown
3. Open a New Document: File > New
4. etc...
Result: Summarize the result you see happening in FreeCAD`;

  var infoText = `Please paste the contents of Help > About FreeCAD > "Copy to \
clipboard"

If you are running a debug release you can also publish a debugging backtrace. \
Please see: http://www.freecadweb.org/wiki/Debugging

If you have a file to share that is larger than the FreeCAD tracker allows, you \
can upload the file to a cloud-based 3rd party service (like NextCloud, Dropbox, \
etc.) and paste the link here.`;

  addPlaceholderText('#summary', summaryText);
  addPlaceholderText('#description', descText);
  addPlaceholderText('#steps_to_reproduce', stepsText);
  addPlaceholderText('#additional_info', infoText);
}

$(document).ready(function () {
  var pageTitle = $(document).attr("title");
  if (pageTitle === "Report Issue - FreeCAD Tracker") {
    addReportTemplate();
  }
});

Add report issue placeholder text script


// Note: You must restart bin/webpack-dev-server for changes to take effect

const { resolve } = require('path')
const merge = require('webpack-merge')
const devConfig = require('./development.js')
const { devServer, publicPath, paths } = require('./configuration.js')

module.exports = merge(devConfig, {
  devServer: {
    host: devServer.host,
    port: devServer.port,
    compress: true,
    headers: { "Access-Control-Allow-Origin": "*" },
    historyApiFallback: true,
    contentBase: resolve(paths.output, paths.entry),
    publicPath
  }
})

// Note: You must restart bin/webpack-dev-server for changes to take effect

const { resolve } = require('path')
const merge = require('webpack-merge')
const devConfig = require('./development.js')
const { devServer, publicPath, paths } = require('./configuration.js')

module.exports = merge(devConfig, {
  devServer: {
    host: devServer.host,
    port: devServer.port,
    compress: true,
    headers: { 'Access-Control-Allow-Origin': '*' },
    historyApiFallback: true,
    contentBase: resolve(paths.output, paths.entry),
    publicPath
  }
})

Change the " to '


$(function() {
  /*
   * Functions
   **/

  // Function to clear the table and insert a specific placeholder
  var insertPlaceholder = function(msg) {
    $('table.certificates tbody').html(
      '
' +
      '' + msg + '	' +
      '
'
    );
  };

  // Reload certificates and populate table
  var reloadCertificates = function() {
    insertPlaceholder('loading ...');
    $.ajax({
      type: 'GET'
      , url: 'http://' + window.location.host + '/api/certificates'
      , cache: false
      , success: function(data) {
        // Clear placeholder
        $('table.certificates tbody').html('');
        // Populate
 with host
's
        data.data.hosts.forEach(function(certificate) {
          $('table.certificates tbody').append(
            '
' +
            '' + certificate.id + '	' +
            '' + certificate.hostname + '	' +
            '' + certificate.expiration + '	' +
            '' + certificate.customer + ' (#300123)	' +
            '' + certificate.usage + '	' +
            '<button disabled type="button" class="btn btn-xs btn-default"></button> <button disabled type="button" class="btn btn-xs btn-danger"></button>	' +
            '
'
          );
        });
      }
    });
  };

  /*
   * Main
   **/

  // Call reloadCertificates()
  reloadCertificates();
});

$(function() {
  /*
   * Functions
   **/

  // Function to clear the table and insert a specific placeholder
  var insertPlaceholder = function(msg) {
    $('table.certificates tbody').html(
      '
' +
      '' + msg + '	' +
      '
'
    );
  };

  // Reload certificates and populate table
  var reloadCertificates = function() {
    insertPlaceholder('loading ...');
    $.ajax({
      type: 'GET'
      , url: window.location.protocol + '//' + window.location.host + '/api/certificates'
      , cache: false
      , success: function(data) {
        // Clear placeholder
        $('table.certificates tbody').html('');
        // Populate
 with host
's
        data.data.hosts.forEach(function(certificate) {
          $('table.certificates tbody').append(
            '
' +
            '' + certificate.id + '	' +
            '' + certificate.hostname + '	' +
            '' + certificate.expiration + '	' +
            '' + certificate.customer + ' (#300123)	' +
            '' + certificate.usage + '	' +
            '<button disabled type="button" class="btn btn-xs btn-default"></button> <button disabled type="button" class="btn btn-xs btn-danger"></button>	' +
            '
'
          );
        });
      }
    });
  };

  /*
   * Main
   **/

  // Call reloadCertificates()
  reloadCertificates();
});

Use window.location.protocol instead of hardcoded one