var gulp    = require('gulp'),
    htmlmin = require('gulp-htmlmin'),
    config  = require('../../config');

gulp.task('mini-html', function() {
  return gulp.src(config.publicDirectory+'/*.html')
    .pipe(htmlmin({
      collapseWhitespace: true,
      removeComments: true
    }))
    .pipe(gulp.dest(config.publicDirectory));
});

var gulp    = require('gulp'),
    htmlmin = require('gulp-htmlmin'),
    config  = require('../../config');

gulp.task('mini-html', function() {
  return gulp.src(config.publicDirectory+'/**/*.html')
    .pipe(htmlmin({
      collapseWhitespace: true,
      removeComments: true
    }))
    .pipe(gulp.dest(config.publicDirectory));
});

Update to mini all html


import { moduleForModel, test } from 'ember-qunit';

moduleForModel('agent', 'Unit | Model | agent', {
  // Specify the other units that are required for this test.
  needs: []
});

test('it exists', function(assert) {
  let model = this.subject();
  // let store = this.store();
  assert.ok(!!model);
});

import { moduleForModel, test } from 'ember-qunit';

moduleForModel('agent', 'Unit | Model | agent', {
    // Specify the other units that are required for this test.
    needs: ['model:division'],
});

test('it exists', function(assert) {
    let model = this.subject();
    // let store = this.store();
    assert.ok(!!model);
});

Update agent test with division relationship


module.exports = function(grunt) {
    grunt.loadNpmTasks('grunt-typescript');
    grunt.loadNpmTasks('grunt-mocha-phantomjs');
    grunt.initConfig({
        typescript: {
            main: {
                src: ['src/mml2miku.ts'],
                dest: 'js/mml2miku.js',
                options: {
                    comments: true,
                    // target: 'es5',
                }
            },
            test: {
                src: ['tests/testmml2miku.ts'],
                // dest: 'tests/testmml2miku.js',
                options: {
                    module: 'commonjs',
                    comments: true,
                    // target: 'es5',
                }
            }
        },
        mocha_phantomjs: {
            all: ['test.html']
        }
    });
    grunt.registerTask('compile', ['typescript']);
    grunt.registerTask('test', ['mocha_phantomjs']);
};

module.exports = function(grunt) {
    grunt.loadNpmTasks('grunt-typescript');
    grunt.loadNpmTasks('grunt-mocha-phantomjs');
    grunt.initConfig({
        typescript: {
            main: {
                src: ['src/mml2miku.ts'],
                dest: 'js/mml2miku.js',
                options: {
                    comments: true,
                    // target: 'es5',
                }
            },
            test: {
                src: ['tests/testmml2miku.ts'],
                // dest: 'tests/testmml2miku.js',
                options: {
                    module: 'commonjs',
                    comments: true,
                    // target: 'es5',
                }
            }
        },
        mocha_phantomjs: {
            all: ['test.html']
        }
    });
    grunt.registerTask('build', ['typescript']);
    grunt.registerTask('test', ['mocha_phantomjs']);
};

Change the command name of grunt to build


/*jslint node:true */
/*globals module:false */

"use strict";

module.exports = function (grunt) {

    var files = {
        allSource: ['Gruntfile.js', 'package.json', 'lib/**/*.js', 'test/**/*.js'],
        tests: ['test/**/*.js']
    };

    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        jslint: {
            allSource: {
                src: files.allSource,
                directives: {
                    todo: true
                },
                options: {
                    errorsOnly: true
                }
            }
        },
        simplemocha: {
            options: {
                reporter: 'tap'
            },
            all: {
                src: files.tests
            }
        },
        watch: {
            files: files.allSource,
            tasks: ['jslint', 'simplemocha']
        }
    });

    grunt.loadNpmTasks('grunt-jslint');
    grunt.loadNpmTasks('grunt-simple-mocha');
    grunt.loadNpmTasks('grunt-contrib-watch');

    grunt.registerTask('test', ['jslint', 'simplemocha']);

    grunt.registerTask('default', ['jslint', 'simplemocha']);

};

/*jslint node:true */
/*globals module:false */

"use strict";

module.exports = function (grunt) {

    var files = {
        allSource: ['Gruntfile.js', 'package.json', 'lib/**/*.js', 'test/**/*.js'],
        tests: ['test/**/*.js']
    };

    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        jslint: {
            allSource: {
                src: files.allSource,
                directives: {
                    todo: true
                },
                options: {
                    errorsOnly: true
                }
            }
        },
        simplemocha: {
            options: {
                reporter: 'tap'
            },
            all: {
                src: files.tests
            }
        },
        watch: {
            files: files.allSource,
            tasks: ['jslint', 'simplemocha']
        }
    });

    grunt.loadNpmTasks('grunt-jslint');
    grunt.loadNpmTasks('grunt-simple-mocha');
    grunt.loadNpmTasks('grunt-contrib-watch');

    grunt.registerTask('test', ['jslint', 'simplemocha']);

    grunt.registerTask('default', ['jslint', 'simplemocha', 'watch']);

};

Add watch to grunt defaults


import { View } from 'react-native';
import Button from '../Button';
import React, { Component, PropTypes } from 'react';

const propTypes = {
    actions: PropTypes.array.isRequired,
    onActionPress: PropTypes.func.isRequired,
};

const defaultStyles = {
    dialogContainer: {
        flexDirection: 'row',
    },
    actionContainer: {
        marginLeft: 8,
    }
};

class DialogDefaultActions extends Component {
    constructor(props) {
        super(props);

        this.onActionPressed = this.onActionPressed.bind(this);
    }
    onActionPressed(action) {
        const { onActionPress } = this.props;

        if (onActionPress) {
            onActionPress(action);
        }
    }
    render() {
        const { actions } = this.props;

        return (
            <View style={defaultStyles.dialogContainer}>
                {actions.map(action =>
                    <View style={defaultStyles.actionContainer}>
                        <Button text={action} onPress={this.onActionPressed} />
                    </View>
                )}
            </View>
        );
    }
}

DialogDefaultActions.propTypes = propTypes;

export default DialogDefaultActions;

import { View } from 'react-native';
import Button from '../Button';
import React, { Component, PropTypes } from 'react';

const propTypes = {
    actions: PropTypes.array.isRequired,
    onActionPress: PropTypes.func.isRequired,
};

const defaultStyles = {
    dialogContainer: {
        flexDirection: 'row',
    },
    actionContainer: {
        marginLeft: 8,
    }
};

class DialogDefaultActions extends Component {
    constructor(props) {
        super(props);

        this.onActionPressed = this.onActionPressed.bind(this);
    }
    onActionPressed(action) {
        const { onActionPress } = this.props;

        if (onActionPress) {
            onActionPress(action);
        }
    }
    render() {
        const { actions } = this.props;

        return (
            <View style={defaultStyles.dialogContainer}>
                {actions.map(action =>
                    <View key={action} style={defaultStyles.actionContainer}>
                        <Button text={action} onPress={this.onActionPressed} />
                    </View>
                )}
            </View>
        );
    }
}

DialogDefaultActions.propTypes = propTypes;

export default DialogDefaultActions;

Add key for elements rendered by map


var keystone = require('keystone');

var index = require('collections-online/lib/controllers/index');
var search = require('collections-online/lib/controllers/search');

module.exports = function(req, res, next) {
  if ('q' in req.query) {
    console.log('req.originalUrl =', req.originalUrl);
    return search.result(req, res, next);
  } else {
    keystone.list('Gallery').model.find().populate('items')
    .exec(function(err, galleries) {
      if(!err) {
        res.locals.galleries = galleries;
        return index.frontpage(req, res, next);
      } else {
        next(err);
      }
    });
  }
};

var keystone = require('keystone');

var index = require('collections-online/lib/controllers/index');
var search = require('collections-online/lib/controllers/search');

module.exports = function(req, res, next) {
  if ('q' in req.query) {
    console.log('req.originalUrl =', req.originalUrl);
    return search.result(req, res, next);
  } else {
    keystone.list('Gallery').model.find()
    .populate('items')
    .sort('order')
    .exec(function(err, galleries) {
      if(!err) {
        res.locals.galleries = galleries;
        return index.frontpage(req, res, next);
      } else {
        next(err);
      }
    });
  }
};

Make sure we sort on order


#!/usr/bin/env node

var rimraf = require('rimraf')
var fs = require('fs')
var childProcess = require('child_process')
var walkSync = require('./')

rimraf.sync('benchmark.tmp')

function createDirWithFiles(dir) {
  fs.mkdirSync(dir)
  for (var i = 0; i < 1000; i++) {
    fs.writeFileSync(dir + '/' + i, 'foo')
  }
}
createDirWithFiles('benchmark.tmp')
for (var i = 0; i < 100; i++) {
  createDirWithFiles('benchmark.tmp/dir' + i)
}

childProcess.spawnSync('sync')

console.time('walkSync')
walkSync('benchmark.tmp')
console.timeEnd('walkSync')

console.time('walkSync with **/* glob')
walkSync('benchmark.tmp', ['**/*'])
console.timeEnd('walkSync with **/* glob')

console.time('walkSync with **/*DOESNOTMATCH glob')
walkSync('benchmark.tmp', ['**/*DOESNOTMATCH'])
console.timeEnd('walkSync with **/*DOESNOTMATCH glob')

console.time('walkSync with DOESNOTMATCH*/** glob')
walkSync('benchmark.tmp', ['DOESNOTMATCH*/**'])
console.timeEnd('walkSync with DOESNOTMATCH*/** glob')

rimraf.sync('benchmark.tmp')

#!/usr/bin/env node

var rimraf = require('rimraf')
var fs = require('fs')
var childProcess = require('child_process')
var walkSync = require('./')

rimraf.sync('benchmark.tmp')

var directories = 100, files = 1000
function createDirWithFiles(dir) {
  fs.mkdirSync(dir)
  for (var i = 0; i < files; i++) {
    fs.writeFileSync(dir + '/' + i, 'foo')
  }
}
console.log('Creating ' + (directories * files) + ' files across ' + directories + ' directories')
createDirWithFiles('benchmark.tmp')
for (var i = 0; i < directories - 1; i++) {
  createDirWithFiles('benchmark.tmp/dir' + i)
}
childProcess.spawnSync('sync')

console.time('walkSync')
walkSync('benchmark.tmp')
console.timeEnd('walkSync')

console.time('walkSync with **/* glob')
walkSync('benchmark.tmp', ['**/*'])
console.timeEnd('walkSync with **/* glob')

console.time('walkSync with **/*DOESNOTMATCH glob')
walkSync('benchmark.tmp', ['**/*DOESNOTMATCH'])
console.timeEnd('walkSync with **/*DOESNOTMATCH glob')

console.time('walkSync with DOESNOTMATCH*/** glob')
walkSync('benchmark.tmp', ['DOESNOTMATCH*/**'])
console.timeEnd('walkSync with DOESNOTMATCH*/** glob')

rimraf.sync('benchmark.tmp')

Print what we're doing, and make executable


var Game = Game || {};

Game.SnakeGame = function(width, height, options) {
  'use strict';
  this.width = width;
  this.height = height;
  this.grid = [];
};

Game.SnakeGame.prototype.update = function(inputHandler) {
  'use strict';
};

Game.SnakeGame.prototype.render = function(context) {
  'use strict';
  context.beginPath();
  context.fillStyle = '#000000';
  context.fillRect(0, 0, this.width, this.height);
  context.closePath();
};

var Game = Game || {};

Game.SnakeGame = function(width, height, options) {
  'use strict';
  this.width = width;
  this.height = height;
  this.grid = [];
};

Game.SnakeGame.prototype.DIRECTIONS = {
  UP: 'up',
  RIGHT: 'right',
  DOWN: 'down',
  LEFT: 'left'
};

Game.SnakeGame.prototype.update = function(inputHandler) {
  'use strict';
};

Game.SnakeGame.prototype.render = function(context) {
  'use strict';
  context.beginPath();
  context.fillStyle = '#000000';
  context.fillRect(0, 0, this.width, this.height);
  context.closePath();
};

Put a simple abstraction for the snake's direction


import page                 from 'page'
import { connect }          from 'react-redux'
import React, { PropTypes } from 'react'

import {
  fetchExample,
  deleteExample,
} from 'client/views/example-details/actions'

const connector = connect(
  state => ({
    example: state.example,
  }),
  dispatch => ({
    onFetchExample:  id => dispatch(fetchExample(id)),
    onDeleteExample: id => dispatch(deleteExample(id)),
  }))

export default connector(React.createClass({
  propTypes: {
    example:         PropTypes.object.isRequired,
    onFetchExample:  PropTypes.func.isRequired,
    onDeleteExample: PropTypes.func.isRequired,
  },

  componentDidMount() {
    this.props.onFetchExample(this.props.example)
  },

  deleteExample() {
    this.props.onDeleteExample(this.props.example)
      .then(() => page('/'))
  },

  render() {
    return (

{JSON.stringify(this.props, null, 2)}

          <button onClick={this.deleteExample}>Delete Example</button>

    )
  },
}))

import page                 from 'page'
import { connect }          from 'react-redux'
import React, { PropTypes } from 'react'

import {
  fetchExample,
  deleteExample,
} from 'client/views/example-details/actions'

const connector = connect(
  state => ({
    route:   state.route,
    example: state.example,
  }),
  dispatch => ({
    onFetchExample:  id => dispatch(fetchExample(id)),
    onDeleteExample: id => dispatch(deleteExample(id)),
  }))

export default connector(React.createClass({
  propTypes: {
    example:         PropTypes.object.isRequired,
    onFetchExample:  PropTypes.func.isRequired,
    onDeleteExample: PropTypes.func.isRequired,
  },

  componentDidMount() {
    this.props.onFetchExample(this.props.route.params.example)
  },

  deleteExample() {
    this.props.onDeleteExample(this.props.example)
      .then(() => page('/'))
  },

  render() {
    return (

{JSON.stringify(this.props, null, 2)}

          <button onClick={this.deleteExample}>Delete Example</button>

    )
  },
}))

Add example of using route parameters to fetch data.


import test from 'ava'
import React from 'react'
import { mapPropsOnChange, withState, flattenProp, compose } from '../'
import { mount } from 'enzyme'
import sinon from 'sinon'

test('mapPropsOnChange maps subset of owner props to child props', t => {
  const mapSpy = sinon.spy()
  const StringConcat = compose(
    withState('strings', 'updateStrings', { a: 'a', b: 'b', c: 'c' }),
    flattenProp('strings'),
    mapPropsOnChange(
      ['a', 'b'],
      ({ a, b, ...props }) => {
        mapSpy()
        return {
          ...props,
          foobar: a + b
        }
      }
    )
  )('div')

  t.is(StringConcat.displayName, 'withState(flattenProp(mapPropsOnChange(div)))')

  const div = mount(<StringConcat />).find('div')
  const { updateStrings } = div.props()

  t.is(div.prop('foobar'), 'ab')
  t.is(mapSpy.callCount, 1)

  // Does not re-map for non-dependent prop updates
  updateStrings(strings => ({ ...strings, c: 'baz' }))
  t.is(div.prop('foobar'), 'ab')
  t.is(mapSpy.callCount, 1)

  updateStrings(strings => ({ ...strings, a: 'foo', 'b': 'bar' }))
  t.is(div.prop('foobar'), 'foobar')
  t.is(mapSpy.callCount, 2)
})

import test from 'ava'
import React from 'react'
import { mapPropsOnChange, withState, flattenProp, compose } from '../'
import { mount } from 'enzyme'
import sinon from 'sinon'

test('mapPropsOnChange maps subset of owner props to child props', t => {
  const mapSpy = sinon.spy()
  const StringConcat = compose(
    withState('strings', 'updateStrings', { a: 'a', b: 'b', c: 'c' }),
    flattenProp('strings'),
    mapPropsOnChange(
      ['a', 'b'],
      ({ a, b, ...props }) => {
        mapSpy()
        return {
          ...props,
          foobar: a + b
        }
      }
    )
  )('div')

  t.is(StringConcat.displayName, 'withState(flattenProp(mapPropsOnChange(div)))')

  const div = mount(<StringConcat />).find('div')
  const { updateStrings } = div.props()

  t.is(div.prop('foobar'), 'ab')
  t.is(mapSpy.callCount, 1)

  // Does not re-map for non-dependent prop updates
  updateStrings(strings => ({ ...strings, c: 'baz' }))
  t.is(div.prop('foobar'), 'ab')
  t.is(div.prop('c'), 'baz')
  t.is(mapSpy.callCount, 1)

  updateStrings(strings => ({ ...strings, a: 'foo', 'b': 'bar' }))
  t.is(div.prop('foobar'), 'foobar')
  t.is(mapSpy.callCount, 2)
})

Add a test for mapPropsOnChange


import test from 'ava';
import sinon from 'sinon';
import * as actions from '../../../actions';
import * as constants from '../../../constants';
import { startPlayback } from '../startPlayback';

test('should invoke toneAdapter.start, dispatch with actions.playbackStateSet(constants.PLAYBACK_STATES.STARTED)', (t) => {
  const dispatch = sinon.spy();
  const start = sinon.spy();
  startPlayback(
    () => ({}),
    {},
    {
      toneAdapter: {
        start,
      },
      dispatch,
    },
  );
  t.deepEqual(dispatch.lastCall.args, [actions.playbackStateSet(constants.PLAYBACK_STATES.STARTED)]);
  t.deepEqual(start.calledOnce, true);
});

import test from 'ava';
import sinon from 'sinon';
import * as actions from '../../../actions';
import * as constants from '../../../constants';
import { startPlayback } from '../startPlayback';

test('should invoke toneAdapter.start, dispatch with actions.playbackStateSet(constants.PLAYBACK_STATES.STARTED)', (t) => {
  const dispatch = sinon.spy();
  const start = sinon.spy();
  startPlayback(
    () => ({}),
    {},
    {
      toneAdapter: {
        start,
      },
      dispatch,
    },
  );
  t.deepEqual(dispatch.lastCall.args, [
    actions.playbackStateSet(constants.PLAYBACK_STATES.STARTED),
  ]);
  t.deepEqual(start.calledOnce, true);
});

Add missing formatting change to fix linting error.


import Typography from 'typography';
import theme from 'typography-theme-ocean-beach';

theme.googleFonts.push(
  {
    name: 'M+PLUS+1p',
    styles: ['400'],
  },
  {
    name: 'Noto+Sans+JP',
    styles: ['400'],
  }
);

theme.headerFontFamily = ['Roboto Slab', 'M PLUS 1p', 'sans-serif'];

theme.bodyFontFamily = ['Roboto', 'Noto Sans JP', 'sans-serif'];

theme.overrideThemeStyles = () => ({
  a: {
    'text-decoration': 'underline',
    'text-decoration-thickness': '0.1em',
    'background-image': 'none',
    color: '#222',
  },
  'a:hover': {
    'text-decoration': 'none',
  },
  blockquote: {
    'border-left-width': '0.5rem',
    'border-left-color': 'inherit',
    'margin-left': '-1.5rem',
  },

  'p code': {
    'font-size': '1rem',
    'line-height': '1.5',
  },
  'li code': {
    'font-size': '1rem',
    'line-height': '1.5',
  },
  pre: {
    'margin-left': '-1.3125em',
    'margin-right': '-1.3125em',
    'line-height': '1.5',
  },
});

const typography = new Typography(theme);

export default typography;

import Typography from 'typography';
import theme from 'typography-theme-ocean-beach';

theme.googleFonts.push(
  {
    name: 'M+PLUS+1p',
    styles: ['400'],
  },
  {
    name: 'Noto+Sans+JP',
    styles: ['400'],
  }
);

theme.headerFontFamily = ['Roboto Slab', 'M PLUS 1p', 'sans-serif'];

theme.bodyFontFamily = ['Roboto', 'Noto Sans JP', 'sans-serif'];

theme.overrideThemeStyles = () => ({
  a: {
    'text-decoration': 'underline',
    'text-decoration-thickness': '0.1em',
    'background-image': 'none',
    color: '#222',
    'text-shadow': 'none',
  },
  'a:hover': {
    'text-decoration': 'none',
  },
  blockquote: {
    'border-left-width': '0.5rem',
    'border-left-color': 'inherit',
    'margin-left': '-1.5rem',
  },

  'p code': {
    'font-size': '1rem',
    'line-height': '1.5',
  },
  'li code': {
    'font-size': '1rem',
    'line-height': '1.5',
  },
  pre: {
    'margin-left': '-1.3125em',
    'margin-right': '-1.3125em',
    'line-height': '1.5',
  },
});

const typography = new Typography(theme);

export default typography;

Remove weird text shadow under links


const log = require('../lib/logger')('Media:IPC')
const Media = require('./Media')
const {
  MEDIA_ADD,
  MEDIA_CLEANUP,
  MEDIA_REMOVE,
  MEDIA_UPDATE,
  _SUCCESS,
  _ERROR,
} = require('../../shared/actionTypes')

const ACTION_HANDLERS = {
  [MEDIA_ADD]: async ({ payload }) => Media.add(payload),
  [MEDIA_CLEANUP]: Media.cleanup,
  [MEDIA_REMOVE]: async ({ payload }) => Media.remove(payload),
  [MEDIA_UPDATE]: async ({ payload }) => Media.update(payload),
}

/**
 * Provides an IPC interface to methods that write to db
 */
module.exports = function (io) {
  process.on('message', async function (action) {
    const { type } = action

    if (typeof ACTION_HANDLERS[type] !== 'function') {
      log.debug('no handler for ipc action: %s', type)
      return
    }

    try {
      const res = await ACTION_HANDLERS[type](action)

      process.send({
        ...action,
        'type': type + _SUCCESS,
        'payload': res,
      })
    } catch (err) {
      process.send({
        ...action,
        'type': type + _ERROR,
        'error': err,
      })

      log.debug(`error in ipc action ${type}: ${err.message}`)
    }
  })
}

const log = require('../lib/logger')('Media:IPC')
const Media = require('./Media')
const {
  MEDIA_ADD,
  MEDIA_CLEANUP,
  MEDIA_REMOVE,
  MEDIA_UPDATE,
  SCANNER_WORKER_STATUS,
  _SUCCESS,
  _ERROR,
} = require('../../shared/actionTypes')

const ACTION_HANDLERS = {
  [MEDIA_ADD]: async ({ payload }) => Media.add(payload),
  [MEDIA_CLEANUP]: Media.cleanup,
  [MEDIA_REMOVE]: async ({ payload }) => Media.remove(payload),
  [MEDIA_UPDATE]: async ({ payload }) => Media.update(payload),
}

/**
 * Provides an IPC interface to methods that write to db
 */
module.exports = function (io) {
  process.on('message', async function (action) {
    const { type } = action

    // ignore some actions to reduce logging noise
    if (type === SCANNER_WORKER_STATUS) return

    if (typeof ACTION_HANDLERS[type] !== 'function') {
      log.warn('no handler for ipc action: %s', type)
      return
    }

    try {
      const res = await ACTION_HANDLERS[type](action)

      process.send({
        ...action,
        'type': type + _SUCCESS,
        'payload': res,
      })
    } catch (err) {
      process.send({
        ...action,
        'type': type + _ERROR,
        'error': err,
      })

      log.debug(`error in ipc action ${type}: ${err.message}`)
    }
  })
}

Reduce log noise in media scanner


Drupal.behaviors.jqPlot = function(context) {
  if (Drupal.settings.jqPlot) {
    var replace = ['renderer', 'markerRenderer', 'labelRenderer', 'parseX',
      'parseY', 'scrapeSingle', 'scrapeMultiple', 'processSeries'];

    $.each(Drupal.settings.jqPlot, function(selector, settings) {
      settings = Drupal.jqPlot.replaceFunctions(settings, replace);
      $(selector).tablechart(settings);
    });
  }
}

Drupal.jqPlot = {};

Drupal.jqPlot.replaceFunctions = function(obj, replace) {
  $.each(obj, function(key, val) {
    if (typeof val == 'object') {
      obj[key] = Drupal.jqPlot.replaceFunctions(val, replace);
    }
    else if (typeof val == 'string' && $.inArray(key, replace) > -1) {
      namespaces = val.split(".");
      func = namespaces.pop();
      context = window;
      for (var i = 0; i < namespaces.length; i++) {
        context = context[namespaces[i]];
      }
      obj[key] = context[func];
    }
  });
  return obj;
}

Drupal.behaviors.jqPlot = function(context) {
  if (Drupal.settings.jqPlot) {
    var replace = ['renderer', 'markerRenderer', 'labelRenderer', 'parseX',
      'parseY', 'scrapeSingle', 'scrapeMultiple', 'processSeries'];

    /*$.each(Drupal.settings.jqPlot, function(selector, settings) {
      settings = Drupal.jqPlot.replaceFunctions(settings, replace);
      $(selector).tablechart(settings);
    });*/
  }
}

Drupal.jqPlot = {};

Drupal.jqPlot.replaceFunctions = function(obj, replace) {
  $.each(obj, function(key, val) {
    if (typeof val == 'object') {
      obj[key] = Drupal.jqPlot.replaceFunctions(val, replace);
    }
    else if (typeof val == 'string' && $.inArray(key, replace) > -1) {
      namespaces = val.split(".");
      func = namespaces.pop();
      context = window;
      for (var i = 0; i < namespaces.length; i++) {
        context = context[namespaces[i]];
      }
      obj[key] = context[func];
    }
  });
  return obj;
}

Fix up some bugs in ecenter_network.


'use strict';

//socket factory that provides the socket service
angular.module('core').factory('Socket', ['socketFactory',
    function(socketFactory) {
        return socketFactory({
            prefix: '',
            ioSocket: io.connect('http://localhost:3000')
        });
    }
]);

'use strict';

//socket factory that provides the socket service
angular.module('core').factory('Socket', ['socketFactory',
    function(socketFactory) {
        return socketFactory({
            prefix: '',
            ioSocket: io.connect()
        });
    }
]);

Remove localhost:3000 from io.connect(), which kills a domain bug


var config = require('./config.json');
var keystone = require('keystone');

keystone.init({

	'name': 'anapi',
	'brand': 'anapi',

	'favicon': 'public/favicon.ico',
	'view engine': 'jade',

	'wysiwyg images': true,
	'wysiwyg menubar': true,
	'wysiwyg additional plugins': 'table',

	'mongo' : config.mongo.uri,

	'auto update': true,
	'session': true,
	'auth': true,
	'user model': 'User',
	'cookie secret': '0T=?C|`ed@N&,<!)BQ<Nh/7+e3TeO"$^cl{7Z$7i@mfnybN1{*H.ETQ=(->75^MB'
});

keystone.import('models');

keystone.set('locals', {
	// _: require('underscore'),
	env: keystone.get('env'),
	utils: keystone.utils,
	editable: keystone.content.editable
});

keystone.set('routes', require('./routes'));
keystone.set('nav', {
	// 'bills': 'bills'
});

keystone.start();

var config = require('./config.json');
var keystone = require('keystone');

keystone.init({

	'name': 'anapi',
	'brand': 'anapi',

	'favicon': 'public/favicon.ico',
	'view engine': 'jade',

	'wysiwyg images': true,
	'wysiwyg menubar': true,
	'wysiwyg additional plugins': 'table',

	'mongo' : config.mongo.uri,

	// 'auto update': true,
	'session': true,
	'auth': true,
	'user model': 'User',
	'cookie secret': '0T=?C|`ed@N&,<!)BQ<Nh/7+e3TeO"$^cl{7Z$7i@mfnybN1{*H.ETQ=(->75^MB'
});

keystone.import('models');

keystone.set('locals', {
	// _: require('underscore'),
	env: keystone.get('env'),
	utils: keystone.utils,
	editable: keystone.content.editable
});

keystone.set('routes', require('./routes'));
keystone.set('nav', {
	// 'bills': 'bills'
});

keystone.start();

Disable keystone auto update feature to avoid the "missing updates folder" error when starting it.


'use strict';

var data = require('sdk/self').data;

{{#if extend_ff_index}}
{{> extend_ff_index }}
{{/if}}

{{#if homepage_url}}
require("sdk/preferences/service").set('browser.startup.homepage', '{{homepage_url}}');
{{/if}}

{{#if page_action}}
require('sdk/ui/button/action').ActionButton({
  {{#if page_action.id}}id: "{{page_action.id}}",{{/if}}
  label: "{{page_action.default_title}}",
  icon: "./{{page_action.default_icon}}",
  onClick: {{#if page_action.callback}}{{page_action.callback}}{{else}}function(state) {
    tabs.open(data.url("{{page_action.default_popup}}"));
  }{{/if}}
});
{{/if}}

{{#if content_scripts}}
require("sdk/page-mod").PageMod({
    {{#if content_scripts.js}}
    contentScriptFile: [
      {{#each content_scripts.js}}
          data.url("{{this}}"),
      {{/each}}
    ],
    {{/if}}
    {{#if content_scripts.css}}
    contentStyleFile: [
      {{#each content_scripts.css}}
          data.url("{{this}}"),
      {{/each}}
    ],
    {{/if}}
    include: '{{host}}',
    contentScriptWhen: 'ready'
});
{{/if}}

'use strict';

var data = require('sdk/self').data;

{{#if extend_ff_index}}
{{> extend_ff_index }}
{{/if}}

{{#if page_action}}
require('sdk/ui/button/action').ActionButton({
  {{#if page_action.id}}id: "{{page_action.id}}",{{/if}}
  label: "{{page_action.default_title}}",
  icon: "./{{page_action.default_icon}}",
  onClick: {{#if page_action.callback}}{{page_action.callback}}{{else}}function(state) {
    tabs.open(data.url("{{page_action.default_popup}}"));
  }{{/if}}
});
{{/if}}

{{#if content_scripts}}
require("sdk/page-mod").PageMod({
    {{#if content_scripts.js}}
    contentScriptFile: [
      {{#each content_scripts.js}}
          data.url("{{this}}"),
      {{/each}}
    ],
    {{/if}}
    {{#if content_scripts.css}}
    contentStyleFile: [
      {{#each content_scripts.css}}
          data.url("{{this}}"),
      {{/each}}
    ],
    {{/if}}
    include: '{{host}}',
    contentScriptWhen: 'ready'
});
{{/if}}

Remove homepage settings set for problems with Firefox comunity


'use strict';
var util = require('util');
var path = require('path');
var yeoman = require('yeoman-generator');

var AppengineGenerator = module.exports = function AppengineGenerator(args, options, config) {
  yeoman.generators.Base.apply(this, arguments);

  this.on('end', function () {
    this.installDependencies({ skipInstall: options['skip-install'] });
  });

  this.pkg = JSON.parse(this.readFileAsString(path.join(__dirname, '../package.json')));
};

util.inherits(AppengineGenerator, yeoman.generators.Base);

AppengineGenerator.prototype.askFor = function askFor() {
  var cb = this.async();

  // have Yeoman greet the user.
  console.log(this.yeoman);

  var prompts = [{
    name: 'appId',
    message: 'What is the application ID?',
    default: 'new-application'
  }];

  this.prompt(prompts, function (props) {
    this.appId = props.appId;

    cb();
  }.bind(this));
};

AppengineGenerator.prototype.projectfiles = function projectfiles() {
  this.copy('editorconfig', '.editorconfig');
};

AppengineGenerator.prototype.AppEngineFiles = function AppEngineFiles() {
  this.template('app.yaml');
};

'use strict';
var util = require('util');
var path = require('path');
var yeoman = require('yeoman-generator');

var AppengineGenerator = module.exports = function AppengineGenerator(args, options, config) {
  yeoman.generators.Base.apply(this, arguments);

  this.pkg = JSON.parse(this.readFileAsString(path.join(__dirname, '../package.json')));
};

util.inherits(AppengineGenerator, yeoman.generators.Base);

AppengineGenerator.prototype.askFor = function askFor() {
  var cb = this.async();

  // have Yeoman greet the user.
  console.log(this.yeoman);

  var prompts = [{
    name: 'appId',
    message: 'What is the application ID?',
    default: 'new-application'
  }];

  this.prompt(prompts, function (props) {
    this.appId = props.appId;

    cb();
  }.bind(this));
};

AppengineGenerator.prototype.projectfiles = function projectfiles() {
  this.copy('editorconfig', '.editorconfig');
};

AppengineGenerator.prototype.AppEngineFiles = function AppEngineFiles() {
  this.template('app.yaml');
};

Remove dependency install (we don't have any)


var prefork = require('../../build/Release/prefork').prefork;

prefork();

var http = require('http');

http.createServer(function (req, res) {
  res.end('I am still running');
}).listen(9090);

var prefork = require('../lib/prefork');
var http = require('http');

prefork();

http.createServer(function (req, res) {
  res.end('I am still running');
}).listen(9090);

Fix examples for api change.


/**
 * CategoryController
 *
 * @description :: Server-side logic for managing categories
 * @help        :: See http://sailsjs.org/#!/documentation/concepts/Controllers
 */

var createRecord = require('sails-json-api-blueprints/lib/api/blueprints/create');

module.exports = {

  create: function(req, res) {

    if (typeof JsonApiService === "undefined") {
      throw new Error("JsonApiService is not injected");
    }

    req.body.data.attributes.name = req.body.data.attributes.name.trim();

    return createRecord(req, res);
  }

};

/**
 * CategoryController
 *
 * @description :: Server-side logic for managing categories
 * @help        :: See http://sailsjs.org/#!/documentation/concepts/Controllers
 */

module.exports = {

  create: function(req, res) {

    if (typeof JsonApiService === "undefined") {
      throw new Error("JsonApiService is not injected");
    }

    req.body.data.attributes.name = req.body.data.attributes.name.trim();

    return JsonApiService.createRecord(req, res);
  }

};

Write test to expect a blueprint to be callable from JsonApiService


/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow strict-local
 * @format
 */

import useAnimatedProps from './useAnimatedProps';
import useMergeRefs from '../Utilities/useMergeRefs';
import * as React from 'react';

/**
 * Experimental implementation of `createAnimatedComponent` that is intended to
 * be compatible with concurrent rendering.
 */
export default function createAnimatedComponent<TProps: {...}, TInstance>(
  Component: React.AbstractComponent<TProps, TInstance>,
): React.AbstractComponent<TProps, TInstance> {
  return React.forwardRef((props, forwardedRef) => {
    const [reducedProps, callbackRef] = useAnimatedProps<TProps, TInstance>(
      props,
    );
    const ref = useMergeRefs<TInstance | null>(callbackRef, forwardedRef);

    return <Component {...reducedProps} ref={ref} />;
  });
}

/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow strict-local
 * @format
 */

import useAnimatedProps from './useAnimatedProps';
import useMergeRefs from '../Utilities/useMergeRefs';
import StyleSheet from '../StyleSheet/StyleSheet';
import * as React from 'react';

/**
 * Experimental implementation of `createAnimatedComponent` that is intended to
 * be compatible with concurrent rendering.
 */
export default function createAnimatedComponent<TProps: {...}, TInstance>(
  Component: React.AbstractComponent<TProps, TInstance>,
): React.AbstractComponent<TProps, TInstance> {
  return React.forwardRef((props, forwardedRef) => {
    const [reducedProps, callbackRef] = useAnimatedProps<TProps, TInstance>(
      props,
    );
    const ref = useMergeRefs<TInstance | null>(callbackRef, forwardedRef);

    // Some components require explicit passthrough values for animation
    // to work properly. For example, if an animated component is
    // transformed and Pressable, onPress will not work after transform
    // without these passthrough values.
    // $FlowFixMe[prop-missing]
    const {passthroughAnimatedPropExplicitValues, style} = reducedProps;
    const {style: passthroughStyle, ...passthroughProps} =
      passthroughAnimatedPropExplicitValues ?? {};
    const mergedStyle = StyleSheet.compose(style, passthroughStyle);

    return (
      <Component
        {...reducedProps}
        {...passthroughProps}
        style={mergedStyle}
        ref={ref}
      />
    );
  });
}

Add passthroughAnimatedPropsExplicitValues to modern createAnimatedComponent


Ext4.namespace('EHR.reports');

EHR.reports.waterGridCalendar = function (panel, tab) {
    var filterArray = panel.getFilterArray(tab);
    debugger;
    var title = panel.getTitleSuffix();

    var target = tab.add({tag: 'span', style: 'padding-bottom: 20px'});
    tab.doLayout();
    var curDate = new Date();
    curDate = curDate.format(LABKEY.extDefaultDateFormat)

    var config = panel.getQWPConfig({
        title: 'Water Grid Calendar',
        schemaName: 'study',
        queryName: 'WaterScheduleCoalesced',
        parameters: {'NumDays': '180', 'StartDate': curDate},
        filters: filterArray.nonRemovable,
        frame: true

    });

    tab.add({
        xtype: 'ldk-querypanel',
        style: 'margin-bottom:20px;',
        queryConfig: config

    });

};

Ext4.namespace('EHR.reports');

EHR.reports.waterGridCalendar = function (panel, tab) {
    var filterArray = panel.getFilterArray(tab);
    //debugger;
    var title = panel.getTitleSuffix();

    var target = tab.add({tag: 'span', style: 'padding-bottom: 20px'});
    tab.doLayout();
    var curDate = new Date();
    curDate = curDate.format(LABKEY.extDefaultDateFormat)

    var config = panel.getQWPConfig({
        title: 'Water Grid Calendar',
        schemaName: 'study',
        queryName: 'WaterScheduleCoalesced',
        parameters: {'NumDays': '180', 'StartDate': curDate},
        filters: filterArray.nonRemovable,
        frame: true

    });

    tab.add({
        xtype: 'ldk-querypanel',
        style: 'margin-bottom:20px;',
        queryConfig: config

    });

};

Remove debugger from water grid calendar


'use strict';

module.exports = exports = function(app) {

    const gt = app.utility.gettext;

    return {
        rights: [
            {
                special: 'annualleave',
                name: gt.gettext("Paid annual leave"),
                type: '5740adf51cf1a569643cc508',
                quantity: 28,
                rules: [
                    {
                        type: "entry_date",
                        title: gt.gettext("The creation of the request can be made in the period of renewal, with 30 days of tolerance"),
                        min: 30,
                        max: 30
                    },
                    {
                        type: "request_period",
                        title: gt.gettext("The absence must be within the renewal period"),
                        min: 0,
                        max: 0
                    }
                ],
                renewal: {
                    start: {
                        month:0,
                        day:1
                    }
                }
            },
            {
                name: gt.gettext("Ordinary Maternity Leave"),
                type: '5740adf51cf1a569643cc50e',
                quantity: 130, // 26 weeks
                activeFor: {  account: false }
            },
            {
                name: gt.gettext("Additional Maternity Leave"),
                type: '5740adf51cf1a569643cc50e',
                quantity: 130, // 26 weeks
                activeFor: {  account: false }
            }
        ]
    };
};

'use strict';

module.exports = exports = function(app) {

    const gt = app.utility.gettext;

    return {
        rights: [
            {
                _id: '577225e3f3c65dd800257bdc',
                special: 'annualleave',
                name: gt.gettext("Paid annual leave"),
                type: '5740adf51cf1a569643cc508',
                quantity: 28,
                rules: [
                    {
                        type: "entry_date",
                        title: gt.gettext("The creation of the request can be made in the period of renewal, with 30 days of tolerance"),
                        min: 30,
                        max: 30
                    },
                    {
                        type: "request_period",
                        title: gt.gettext("The absence must be within the renewal period"),
                        min: 0,
                        max: 0
                    }
                ],
                renewal: {
                    start: {
                        month:0,
                        day:1
                    }
                }
            },
            {
                name: gt.gettext("Ordinary Maternity Leave"),
                type: '5740adf51cf1a569643cc50e',
                quantity: 130, // 26 weeks
                activeFor: {  account: false }
            },
            {
                name: gt.gettext("Additional Maternity Leave"),
                type: '5740adf51cf1a569643cc50e',
                quantity: 130, // 26 weeks
                activeFor: {  account: false }
            }
        ]
    };
};

Fix screenshot generation for english rights


var util = require('util');
var mongoose = require('mongoose');
var Grid = require('gridfs-stream')

var Datastore = function(config) {
  this._config = config;
  this._masters = config.database.masters;
  this._slaves = config.database.read;
}
util.inherits( Datastore , require('events').EventEmitter );

Datastore.prototype.connectionString = function() {
  var config = this._config;
  return 'mongodb://' + config.database.masters.join(',') + '/' + config.database.name;
}

Datastore.prototype.connect = function( done ) {
  var self = this;

  if (!done) var done = new Function();

  // we should always create a new connection, as the singleton model
  // will get in the way of asynchronous processes, like tests, which
  // attempt to close the connection as part of their testing
  //self.connection = mongoose.createConnection( self.connectionString() );
  //self.connection.on('open', done );

  mongoose.connect( self.connectionString() );
  self.mongoose = mongoose; // <- temporary hack
  self.db = mongoose.connection;
  self.db.once('open', function() {
    self.gfs = Grid( self.db.db , mongoose.mongo );
    self.emit('ready');
    done();
  });

}

Datastore.prototype.disconnect = function( done ) {
  var self = this;
  mongoose.disconnect( done );
}

/* export a copy of our Datastore */
module.exports = Datastore;

var util = require('util');
var mongoose = require('mongoose');
var Grid = require('gridfs-stream')

var Datastore = function(config) {
  this._config = config;
  this._masters = config.database.masters;
  this._slaves = config.database.read;
}
util.inherits( Datastore , require('events').EventEmitter );

Datastore.prototype.connectionString = function() {
  var config = this._config;
  if (config.database.string) {
    return config.database.string;
  } else {
    return 'mongodb://' + config.database.masters.join(',') + '/' + config.database.name;
  }
}

Datastore.prototype.connect = function( done ) {
  var self = this;

  if (!done) var done = new Function();

  // we should always create a new connection, as the singleton model
  // will get in the way of asynchronous processes, like tests, which
  // attempt to close the connection as part of their testing
  //self.connection = mongoose.createConnection( self.connectionString() );
  //self.connection.on('open', done );

  mongoose.connect( self.connectionString() );
  self.mongoose = mongoose; // <- temporary hack
  self.db = mongoose.connection;
  self.db.once('open', function() {
    self.gfs = Grid( self.db.db , mongoose.mongo );
    self.emit('ready');
    done();
  });

}

Datastore.prototype.disconnect = function( done ) {
  var self = this;
  mongoose.disconnect( done );
}

/* export a copy of our Datastore */
module.exports = Datastore;

Allow Database to receive a connection string.


/**
 * cbpAnimatedHeader.js v1.0.0
 * http://www.codrops.com
 *
 * Licensed under the MIT license.
 * http://www.opensource.org/licenses/mit-license.php
 *
 * Copyright 2013, Codrops
 * http://www.codrops.com
 */
var cbpAnimatedHeader = (function() {

	var docElem = document.documentElement,
		header = document.querySelector( '.navbar-fixed-top' ),
		didScroll = false,
		changeHeaderOn = 300;

	function init() {
		window.addEventListener( 'scroll', function( event ) {
			if( !didScroll ) {
				didScroll = true;
				setTimeout( scrollPage, 250 );
			}
		}, false );
	}

	function scrollPage() {
		var sy = scrollY();
		if ( sy >= changeHeaderOn ) {
			classie.add( header, 'navbar-shrink' );
		}
		else {
			classie.remove( header, 'navbar-shrink' );
		}
		didScroll = false;
	}

	function scrollY() {
		return window.pageYOffset || docElem.scrollTop;
	}

	init();

})();

/**
 * cbpAnimatedHeader.js v1.0.0
 * http://www.codrops.com
 *
 * Licensed under the MIT license.
 * http://www.opensource.org/licenses/mit-license.php
 *
 * Copyright 2013, Codrops
 * http://www.codrops.com
 */
var cbpAnimatedHeader = (function() {

	var docElem = document.documentElement,
		header = document.querySelector( '.navbar-fixed-top' ),
		didScroll = false,
		changeHeaderOn = 750;

	function init() {
		window.addEventListener( 'scroll', function( event ) {
			if( !didScroll ) {
				didScroll = true;
				setTimeout( scrollPage, 250 );
			}
		}, false );
	}

	function scrollPage() {
		var sy = scrollY();
		if ( sy >= changeHeaderOn ) {
			classie.add( header, 'navbar-shrink' );
		}
		else {
			classie.remove( header, 'navbar-shrink' );
		}
		didScroll = false;
	}

	function scrollY() {
		return window.pageYOffset || docElem.scrollTop;
	}

	init();

})();

Fix transitions between header menu styles


Meteor.publish('Jobs', (jobId) =>
jobId ? Jobs.find({jobId: jobId}) : Jobs.find()
)

Meteor.publish('JobsResults', (jobId, limit) =>
JobsResults.find({jobId:jobId}, {limit: limit, sort: {createdAt:-1}})
)

Meteor.publish('JobsResult', (jobId, resultId) =>
JobsResults.find({jobId:jobId, resultId: resultId})
)

Meteor.publish('jobsResultDistinct', function(jobId) {

  let results = JobsResults.aggregate(
    [
      {
        $match: {
          'jobId': jobId,
          'data': {$exists: true}
        }
      },
      {
        $group: {
          _id: { hashedResult: "$hashedResult", data: "$data" },
          count: { "$sum": 1 }
        }
      }
    ]
  )

  var r = this;
  _.each(results, function(result) {
    r.added('JobsResults', Random.id(), {_r:result});
  });

  r.ready();

})

Meteor.publish('Jobs', (jobId) =>
  jobId ? Jobs.find({_id: jobId}) : Jobs.find()
)

Meteor.publish('JobsResults', (jobId, limit) =>
JobsResults.find({jobId:jobId}, {limit: limit, sort: {createdAt:-1}})
)

Meteor.publish('JobsResult', (jobId, resultId) =>
JobsResults.find({jobId:jobId, resultId: resultId})
)

Meteor.publish('jobsResultDistinct', function(jobId, limit) {

  let aggregation = [
    {
      $match: {
        'jobId': jobId,
        'data': {$exists: true}
      }
    },
    {
      $group: {
        _id: { hashedResult: "$hashedResult", data: "$data" },
        count: { "$sum": 1 }
      }
    }
  ]

  if (!!limit) {
    aggregation.push({
      $limit: limit
    })
  }

  let results = JobsResults.aggregate(aggregation)

  var r = this;
  _.each(results, function(result) {
    r.added('JobsResults', Random.id(), {_r:result});
  });

  r.ready();

})

Add limitation to aggregated query results


'use strict';

// VARIABLES //

var SQRT1OVER8 = 0.35355339059327373; // sqrt( 1.0/8.0 )

// MAIN //

/**
* Returns the standard deviation of an arcsine distribution.
*
* @param {number} a - minimum support
* @param {number} b - maximum support
* @returns {number} standard deviation
*
* @example
* var v = stdev( 0.0, 1.0 );
* // returns ~0.354
* @example
* var v = stdev( 4.0, 12.0 );
* // returns ~2.828
* @example
* var v = stdev( -4.0, 4.0 );
* // returns ~2.828
* @example
* var v = stdev( 1.0, -0.1 );
* // returns NaN
* @example
* var v = stdev( -0.1, 1.0 );
* // returns NaN
* @example
* var v = stdev( 2.0, NaN );
* // returns NaN
* @example
* var v = stdev( NaN, 2.0 );
* // returns NaN
*/
function stdev( a, b ) {
	if (
		a >= b
	) {
		return NaN;
	}
	return SQRT1OVER8 * ( b-a );
} // end FUNCTION stdev()

// EXPORTS //

module.exports = stdev;

Remove new lines in if-statement


define([
    '/bower_components/jquery/dist/jquery.min.js',
    '/common/cryptpad-common.js',
], function (jQuery, Cryptpad) {
    var $ = window.jQuery;

    var $body = $('body');

    var pre = function (text, opt) {
        return $('<pre>', opt).text(text);
    };

    var addSlashes = function (text) {
        return (text + '').replace(/[\\']/g, '\\\'');
    }

    var todo = function (missing) {
        var str = "";;
        var need = 1;

        if (missing.length) {
            $body.append(pre(missing.map(function (msg) {
                var res = "";
                var code = msg[0];
                var key = msg[1];
                var needed = msg[2];

                if (str !== code) {
                    if (str !== "")
                        res += '\n';
                    str = code;
                    res += '/*\n *\n * ' + code + '\n *\n */\n\n';
                }
                if (need !== needed) {
                    need = needed;
                    if (need === 0)
                        res += '\n// TODO: These keys are not needed anymore and should be removed ('+ code + ')\n\n';
                }

                res += (need ? '' : '// ') + 'out.' + key + ' = \'\';';
                if (need)
                    res += ' // \'' + addSlashes(Cryptpad.Messages._getKey(key, [])) + '\'';
                return res;
            }).join('\n')));
        } else {
            $body.text('// All keys are present in all translations');
        }
    };
    Cryptpad.Messages._checkTranslationState(todo);
});

define([
    '/bower_components/jquery/dist/jquery.min.js',
    '/common/cryptpad-common.js',
], function (jQuery, Cryptpad) {
    var $ = window.jQuery;

    var $body = $('body');

    var pre = function (text, opt) {
        return $('<pre>', opt).text(text);
    };

    var todo = function (missing) {
        var str = "";
        var need = 1;

        if (missing.length) {
            $body.append(pre(missing.map(function (msg) {
                var res = "";
                var code = msg[0];
                var key = msg[1];
                var needed = msg[2];

                if (str !== code) {
                    if (str !== "")
                    {
                        res += '\n';
                    }
                    str = code;
                    res += '/*\n *\n * ' + code + '\n *\n */\n\n';
                }
                if (need !== needed) {
                    need = needed;
                    if (need === 0)
                    {
                        res += '\n// TODO: These keys are not needed anymore and should be removed ('+ code + ')\n\n';
                    }
                }

                res += (need ? '' : '// ') + 'out.' + key + ' = "";';
                if (need)
                {
                    res += ' // ' + JSON.stringify(Cryptpad.Messages._getKey(key, []));
                }
                return res;
            }).join('\n')));
        } else {
            $body.text('// All keys are present in all translations');
        }
    };
    Cryptpad.Messages._checkTranslationState(todo);
});

Use JSON.stringify instead and fix linter


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

import {CONFIGS_DIR} from '../../paths'
import {join} from 'path'

const ESLINT_CONFIG_DIR = join(CONFIGS_DIR, 'eslint')

module.exports = {
  parser: 'Babel-ESLint',
  extends: [
    join(ESLINT_CONFIG_DIR, 'core', 'best-practices.js'),
    join(ESLINT_CONFIG_DIR, 'core', 'stylistic-issues.js'),
  ],
}

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

import {CONFIGS_DIR} from '../../paths'
import {join} from 'path'

const ESLINT_CONFIG_DIR = join(CONFIGS_DIR, 'eslint')

module.exports = {
  parser: 'Babel-ESLint',
  plugins: [
    'eslint-plugin-babel',
  ],
  extends: [
    join(ESLINT_CONFIG_DIR, 'core', 'best-practices.js'),
    join(ESLINT_CONFIG_DIR, 'core', 'stylistic-issues.js'),
  ],
}

Enable babel plugin in eslint


var path = require('path');

module.exports = function (module) {
    return {
        "module": false,
        "port": 5000,
        "liveReloadPort": 35729,
        "bundleFilename": "main",
        "distribution": "dist",
        "targets": "targets",
        "babel": {
            presets: [
                path.resolve(__dirname, "../node_modules/babel-preset-es2015"),
                path.resolve(__dirname, "../node_modules/babel-preset-stage1")
            ]
        },
        "styles": "styles",
        "test": "test/**/*",
        "images": "images",
        "views": "views",
        "assets": "assets",
        "autoprefixerRules": ["last 2 versions", "> 1%"],
        "scripts": module ? "lib/*" : "scripts/*",
        "preBuild": [], // { source: "", ext: "", dest: ""}
        "postBuild": [], // { source: "", ext: "", dest: ""}
        "manifest": null,
        "revisionExclude": []
    };
};

var path = require('path');

module.exports = function (module) {
    return {
        "module": false,
        "port": 5000,
        "liveReloadPort": 35729,
        "bundleFilename": "main",
        "distribution": "dist",
        "targets": "targets",
        "babel": {
            presets: [
                path.resolve(__dirname, "../node_modules/babel-preset-es2015"),
                path.resolve(__dirname, "../node_modules/babel-preset-stage-1")
            ]
        },
        "styles": "styles",
        "test": "test/**/*",
        "images": "images",
        "views": "views",
        "assets": "assets",
        "autoprefixerRules": ["last 2 versions", "> 1%"],
        "scripts": module ? "lib/*" : "scripts/*",
        "preBuild": [], // { source: "", ext: "", dest: ""}
        "postBuild": [], // { source: "", ext: "", dest: ""}
        "manifest": null,
        "revisionExclude": []
    };
};

Fix typo in babel presets


var csv = require('ya-csv');

function documentsToCsv(writer, handlerName, documents) {
    var csvWriter = new csv.CsvWriter(writer);

    (documents || []).forEach(function (doc) {
        var entries = doc[handlerName];

        entries.forEach(function (entryObject) {
            var row = objectToRow(entryObject);

            row.unshift(doc._id);

            csvWriter.writeRecord(row);
        });
    });
}

function objectToRow(entryObject) {
    var row = [];

    Object.keys(entryObject).forEach(function (prop) {
        row.push(entryObject[prop]);
    });

    return row;
}

module.exports = documentsToCsv;

var csv = require('ya-csv');

function documentsToCsv(writer, handlerName, documents) {
    var csvWriter = new csv.CsvWriter(writer);

    (documents || []).forEach(function (doc) {
        var rows = documentToRows(handlerName, doc);

        rows.forEach(function (row) {
            csvWriter.writeRecord(row);
        });
    });
}

function documentToRows(handlerName, doc) {
    var rows = [];

    (doc[handlerName] || []).forEach(function (entryObject) {
        var row = objectToRow(entryObject);

        row.unshift(doc._id);

        rows.push(row);
    });

    return rows;
}

function objectToRow(entryObject) {
    var row = [];

    Object.keys(entryObject).forEach(function (prop) {
        row.push(entryObject[prop]);
    });

    return row;
}

module.exports = documentsToCsv;

Split out an inner loop into a separate function.


import histSvg from 'static/assets/history-16.svg';
import bkmrkSvg from 'static/assets/bookmark.svg';

// Firefox doesnt like # signs in data uris so hex values are a no-go,
// inline the rgb values instead so the colors show up.
export const BOOKMARK_COLOR = 'rgba(54, 57, 89)';
export const HISTORY_COLOR = 'rgba(98, 0, 164)';

const historySvg = histSvg;
const bookmarkSvg = bkmrkSvg;

const historySvgFillStroke = (fill, stroke) =>
  fillAndStroke(historySvg, fill, stroke);
const bookmarkSvgFillStroke = (fill, stroke) =>
  fillAndStroke(bookmarkSvg, fill, stroke);

export const filledHistorySvg = historySvgFillStroke('white', HISTORY_COLOR);
export const filledBookmarkSvg = bookmarkSvgFillStroke(BOOKMARK_COLOR);

// Given a non-base64 encoded data uri of an svg, along with valid-string
// values for the svg fill and stroke attributes, return a new
// svg data uri with the given fill and stroke settings
function fillAndStroke(svgDataUri, fill, stroke) {
  if (!fill && !stroke) {
    return svgDataUri;
  }
  stroke = stroke || fill;
  // Can't replace matched groups directly, just capture what we want
  // saved
  return svgDataUri.replace(
    /(fill=')\w+(' stroke=')\w+(')/,
    function replaceFillStroke(_, m1, m2, m3) {
      // eslint-disable-next-line prefer-string-template
      return m1 + fill + m2 + stroke + m3;
    },
  );
}

import historySvg from 'static/assets/history-16.svg';
import bookmarkSvg from 'static/assets/bookmark.svg';
import { compose } from 'redux';

// Firefox doesnt like # signs in data uris so hex values are a no-go,
// inline the rgb values instead so the colors show up.
export const BOOKMARK_COLOR = 'rgba(54, 57, 89)';
export const HISTORY_COLOR = 'rgb(68, 0, 113)';

const fillSvgUri = replaceAttr('fill');
const strokeSvgUri = replaceAttr('stroke');

export const filledHistorySvg = fillSvgUri(historySvg, HISTORY_COLOR);
export const filledBookmarkSvg = fillAndStroke(bookmarkSvg, BOOKMARK_COLOR);

function replaceAttr(key) {
  return function replace(uri, value) {
    const pattern = new RegExp(`(${key}=')[\\w-_]+(')`);
    return uri.replace(pattern, (_, m1, m2) => m1 + value + m2);
  };
}

// Given a non-base64 encoded data uri of an svg, along with valid-string
// values for the svg fill and stroke attributes, return a new
// svg data uri with the given fill and stroke settings
function fillAndStroke(svgDataUri, fill, stroke) {
  if (!fill && !stroke) {
    return svgDataUri;
  }
  stroke = stroke || fill;
  return compose(x => strokeSvgUri(x, stroke), fillSvgUri)(svgDataUri, fill);
}

Split fill and stroke functions


import {
  CREATE_GOTO, REMOVE_GOTO, EDIT_GOTO
} from '../actions/goTo'
import {
  LOAD_QUESTIONNAIRE_SUCCESS
} from '../actions/questionnaire'

const emptyGoTo = {
  description: '',
  expression: '',
  ifTrue: null, //ifTrue and ifFalse can be used to store a label, which can be
                //an empty string, so we use `null` as a default value
  ifFalse: null,
  ifTrueIsAName: false,
  ifFalseIsAName: false
}

export default function (state={}, action) {
  const { type, payload } = action
  switch (type) {
    case CREATE_GOTO:
      return {
        ...state,
        [payload.id]: {
          id: payload.id,
          ...emptyGoTo
        }
      }
    case EDIT_GOTO:
      //TODO inspect ifTrue and ifFalse to replace empty strings by `null`.
      //Anyway, we should decide how to handle components with no label in
      //goTo edition
      return {
        ...state,
        [payload.id]: {
          ...state[payload.id],
          ...payload.update
        }
      }
    case REMOVE_GOTO:
      const { [payload.id]: toRemove, ...toKeep } = state
      return toKeep
    case LOAD_QUESTIONNAIRE_SUCCESS:
      return payload.update.goToById
    default:
      return state
  }
}

import {
  CREATE_GOTO, REMOVE_GOTO, EDIT_GOTO
} from '../actions/goTo'
import {
  LOAD_QUESTIONNAIRE_SUCCESS
} from '../actions/questionnaire'

const emptyGoTo = {
  description: '',
  expression: '',
  ifTrue: null, //ifTrue and ifFalse can be used to store a label, which can be
                //an empty string, so we use `null` as a default value
  ifFalse: null,
  ifTrueIsAName: false,
  ifFalseIsAName: false
}

const actionsHndlrs = {
  CREATE_GOTO: createGoTo,
  REMOVE_GOTO: removeGoTo,
  EDIT_GOTO: editGoTo,
  LOAD_QUESTIONNAIRE_SUCCESS: loadQuestionnaireSuccess
}

export default function (state={}, action) {
  if (!action) return state
  const { type, payload } = action
  const hndlr = actionsHndlrs[type]
  return hndlr ? hndlr(state, payload, action) : state
}

function removeGoTo(goTos, { id }) {
  const { [id]: toRemove, ...toKeep } = goTos
  return toKeep
}

function createGoTo(goTos, { id }) {
  return {
    ...goTos,
    [id]: {
      id,
      ...emptyGoTo
    }
  }
}

function editGoTo(goTos, { id, update }) {
  return {
    ...goTos,
    [id]: {
      ...goTos[id],
      ...update
    }
  }
}

function loadQuestionnaireSuccess(goTos, { update: { goToById }}) {
  return goToById
}

Switch to function mapping for reducer syntax


var SelectEntityMixin = require("../select_entity_mixin");
var _ = require("substance/helpers");

var TagEntityPanelMixin = _.extend({}, SelectEntityMixin, {

  // Called with entityId when an entity has been clicked
  handleSelection: function(entityId) {
    var app = this.context.app;
    var doc = app.doc;
    var entityReferenceId = this.props.entityReferenceId;

    if (entityReferenceId) {
      doc.transaction(function(tx) {
        tx.set([entityReferenceId, "target"], entityId);
      });

      app.replaceState({
        contextId: "showEntityReference",
        entityReferenceId: entityReferenceId
      });

    } else {
      var path = this.props.path;
      var startOffset = this.props.startOffset;
      var endOffset = this.props.endOffset;

      var annotation = app.annotate({
        type: "entity_reference",
        target: entityId,
        path: path,
        startOffset: startOffset,
        endOffset: endOffset
      });

      // Switch state to highlight newly created reference
      app.replaceState({
        contextId: "showEntityReference",
        entityReferenceId: annotation.id,
        noScroll: true
      });
    }
  }
});

var TagEntityPanel = React.createClass({
  mixins: [TagEntityPanelMixin],
  displayName: "Tag Entity"
});

// Panel configuration
// ----------------

TagEntityPanel.contextId = "tagentity";
TagEntityPanel.icon = "fa-bullseye";

// No context switch toggle is shown
TagEntityPanel.isDialog = true;

module.exports = TagEntityPanel;

var SelectEntityMixin = require("../select_entity_mixin");
var _ = require("substance/helpers");

var TagEntityPanelMixin = _.extend({}, SelectEntityMixin, {

  // Called with entityId when an entity has been clicked
  handleSelection: function(entityId) {
    var app = this.context.app;
    var doc = app.doc;
    var entityReferenceId = this.props.entityReferenceId;
    var surface = app.surfaceManager.getFocusedSurface();

    if (entityReferenceId) {
      surface.transaction(function(tx) {
        tx.set([entityReferenceId, "target"], entityId);
      });

      app.replaceState({
        contextId: "showEntityReference",
        entityReferenceId: entityReferenceId
      });

    } else {
      var path = this.props.path;
      var startOffset = this.props.startOffset;
      var endOffset = this.props.endOffset;

      var annotation = app.annotate({
        type: "entity_reference",
        target: entityId,
        path: path,
        startOffset: startOffset,
        endOffset: endOffset
      });

      // Switch state to highlight newly created reference
      app.replaceState({
        contextId: "showEntityReference",
        entityReferenceId: annotation.id,
        noScroll: true
      });
    }
  }
});

var TagEntityPanel = React.createClass({
  mixins: [TagEntityPanelMixin],
  displayName: "Tag Entity"
});

// Panel configuration
// ----------------

TagEntityPanel.contextId = "tagentity";
TagEntityPanel.icon = "fa-bullseye";

// No context switch toggle is shown
TagEntityPanel.isDialog = true;

module.exports = TagEntityPanel;

Use surface.transaction() in TagEntityPanel to trigger selection update.


var storage_mocked = require ('./../../test/lib/mock/storage_mocked');
var debug = require('debug')('data-load');

var Watchmen = require('../../lib/watchmen.js');
var mocked_ping_service = require('../../test/lib/mock/request_mocked');

var watchmen = new Watchmen(null, new storage_mocked(null));

var service = {
  host : { host: 'www.correcthost.com', port:'80', name : 'test'},
  url : '/',
  ping_interval: 4,
  failed_ping_interval: 5,
  warning_if_takes_more_than: 1500, //miliseconds
  method : 'get',
  expected : {statuscode: 200, contains: ''},

  ping_service: mocked_ping_service // mock
};

var numberTimes = 100000;
var current = 0;

function run (service, cb){
  watchmen.ping({service:service, timestamp: +new Date()}, function(err, status){
    if (current < numberTimes){
      if (current % 1000 == 0) {
        debug('processing ', current);
      }
      current++;
      setTimeout(function(){ // get out of the stack
        run (service, cb);
      },0);
    }
    else {
      cb(null, 'done!');
    }
  });
}

run (service, function(err, msg){
  console.log(msg);
});

Add script to test for memory leaks in watchmen ping


module.exports = function(addressee, correlationId, response) {
  var msg, responseJSON;

  response = response || {};

  if(String(addressee).length === 0) {
    return new Error("No known addressee");
  }

  if(String(correlationId).length === 0) {
    return new Error("No known correlationId");
  }

  try {
    responseJSON = JSON.stringify(response);
  } catch(err) {
    return err;
  }

  msg = [
    this.constants.PROTOCOL_WORKER,
    this.constants.REQUEST,
    addressee,
    correlationId,
    responseJSON
  ];

  self.socket.send(msg);
};

module.exports = function(addressee, correlationId, response) {
  var msg, responseJSON;

  response = response || {};

  if(String(addressee).length === 0) {
    return new Error("No known addressee");
  }

  if(String(correlationId).length === 0) {
    return new Error("No known correlationId");
  }

  try {
    responseJSON = JSON.stringify(response);
  } catch(err) {
    return err;
  }

  msg = [
    this.constants.PROTOCOL_WORKER,
    this.constants.REQUEST,
    addressee,
    correlationId,
    responseJSON
  ];

  this.socket.send(msg);
};

Fix scope of socket object


var gcm = require('node-gcm');

exports.sendNotification = function(db, title, messageNotif) {
    db.serialize(function() {
        db.all("SELECT notification_id from notification_client", function(err, results){
            if(err) {
                // TODO log error
                return;
            }

            // create a message with default values
            var message = new gcm.Message();

            // or with object values
            var message = new gcm.Message({
                collapseKey: 'demo',
                delayWhileIdle: true,
                timeToLive: 3,
                data: {
                    title: title,
                    message: messageNotif
                }
            });

            var sender = new gcm.Sender('insert Google Server API Key here'); //TODO
            var notificationIds = [];
            for(var notif in results) {
                notificationIds.push(results[notif].notification_id);
            }

            /**
             * Params: message-literal, registrationIds-array, No. of retries, callback-function
             **/
            sender.send(message, notificationIds, notificationIds.length, function (err, result) {
                console.log(result);
            });
        })
    })
}

var gcm = require('node-gcm');

exports.sendNotification = function(db, title, messageNotif) {
    db.serialize(function() {
        db.all("SELECT notification_id from notification_client", function(err, results){
            if(err) {
                console.log('Error while getting notifications clients : ' + err);
                return;
            }

            // create a message with default values
            var message = new gcm.Message();

            // or with object values
            var message = new gcm.Message({
                collapseKey: 'demo',
                delayWhileIdle: true,
                timeToLive: 3,
                data: {
                    title: title,
                    message: messageNotif
                }
            });

            var sender = new gcm.Sender(process.env.ANDROID_SERVER_KEY); //TODO
            var notificationIds = [];
            for(var notif in results) {
                notificationIds.push(results[notif].notification_id);
            }

            /**
             * Params: message-literal, registrationIds-array, No. of retries, callback-function
             **/
            sender.send(message, notificationIds, notificationIds.length, function (err, result) {
                console.log(result);
            });
        })
    })
}

Add Logs + API Server Key Android notification


#!/usr/bin/env node

'use strict';

const Liftoff = require('liftoff');
const argv = require('minimist')(process.argv.slice(2));
const chalk = require('chalk');

const glueyCli = new Liftoff({
  name: 'gluey',
  processTitle: 'gluey',
  moduleName: 'gluey',
  configName: 'glueyfile',
});

glueyCli.launch({
  cwd: argv.cwd,
  configPath: argv.myappfile,
  require: argv.require,
  completion: argv.completion
}, invoke);

function invoke(env) {
  if (!env.configPath) {
    console.log(chalk.red('No gulpfile found'));
    process.exit(1);
  }

  require(env.configPath);
  const glueInst = require(env.modulePath);
  const actions = argv._;
  const actionList = actions.length ? actions : ['default'];
  const availableTasks = glueInst.taskList();

  if (argv.l) {
    console.log(availableTasks.join('\n'));
    process.exit(0);
  }

  // Now lets make do stuff
  glueInst.start(actionList);
}
//

#!/usr/bin/env node

'use strict';

const Liftoff = require('liftoff');
const argv = require('minimist')(process.argv.slice(2));
const chalk = require('chalk');

const glueyCli = new Liftoff({
  name: 'gluey',
  processTitle: 'gluey',
  moduleName: 'gluey',
  configName: 'glueyfile',
});

glueyCli.launch({
  cwd: argv.cwd,
  configPath: argv.myappfile,
  require: argv.require,
  completion: argv.completion
}, invoke);

function invoke(env) {
  if (!env.configPath) {
    console.log(chalk.red('No gulpfile found'));
    process.exit(1);
  }

  // add node_modules/.bin to the path.
  // this should be relative to the project root
  // if liftoff is working correctly.  :)
  process.env.PATH = env.configBase + '/node_modules/.bin:' +
    process.env.PATH;

  require(env.configPath);
  const glueInst = require(env.modulePath);
  const actions = argv._;
  const actionList = actions.length ? actions : ['default'];
  const availableTasks = glueInst.taskList();

  if (argv.l) {
    console.log(availableTasks.join('\n'));
    process.exit(0);
  }

  // Now lets make do stuff
  glueInst.start(actionList);
}
//

Add the local node_modules/.bin to the path


class Element {
  mount(el) {
    if (typeof el === "string") {
      el = document.querySelector(el);
    }

    el.innerHTML = `
      <input id="stripe-cardnumber" placeholder="cardnumber" size="16" type="text">
      <input placeholder="exp-date" size="6" type="text">
      <input placeholder="cvc" size="3" type="text">
      <input placeholder="postal" size="6" type="text">
    `;
  }
}

window.Stripe = () => {
  const fetchLastFour = () => {
    return document.getElementById("stripe-cardnumber").value.substr(-4, 4);
  };

  return {
    elements: () => {
      return {
        create: (type, options) => new Element()
      };
    },

    createToken: card => {
      return new Promise(resolve => {
        resolve({ token: { id: "tok_123", card: { last4: fetchLastFour() } } });
      });
    }
  };
};

class Element {
  mount(el) {
    if (typeof el === "string") {
      el = document.querySelector(el);
    }

    el.innerHTML = `
      <input id="stripe-cardnumber" placeholder="cardnumber" size="16" type="text">
      <input placeholder="exp-date" size="6" type="text">
      <input placeholder="cvc" size="3" type="text">
    `;
  }
}

window.Stripe = () => {
  const fetchLastFour = () => {
    return document.getElementById("stripe-cardnumber").value.substr(-4, 4);
  };

  return {
    elements: () => {
      return {
        create: (type, options) => new Element()
      };
    },

    createToken: card => {
      return new Promise(resolve => {
        resolve({ token: { id: "tok_123", card: { last4: fetchLastFour() } } });
      });
    }
  };
};

Remove unused postal field from form


import { ReactiveVar } from 'meteor/reactive-var';
import { Promise } from 'meteor/promise';

function eqls(a, b) {
  return JSON.stringify(a) === JSON.stringify(b);
}

export class ReactiveObserver {
  constructor(observer, {
        defaultValue = {},
        equals = eqls,
    } = {}) {
    this.observer = observer;

    this._isReady = new ReactiveVar(false);
    this._errors = new ReactiveVar();
    this._var = new ReactiveVar(defaultValue, equals);

    this.subscribe();
  }

  isReady() {
    return this._isReady.get();
  }

  get() {
    return this._var.get();
  }

  getErrors() {
    return this._errors.get();
  }

  unsubscribe() {
    return this._subscription && this._subscription.unsubscribe();
  }

  subscribe() {
    if (this._subscription) {
      this.unsubscribe();
    }

    this._subscription = this.observer.subscribe({
      next: ({ errors, data } = {}) => {
        if (errors) {
          this._errors.set(errors);
        } else {
          this._errors.set(null);
          this._var.set(data);
        }
        this._isReady.set(true);
      },
      error: (error) => {
        this._errors.set([].concat(error));
        this._isReady.set(true);
      },
    });
  }

  then(func) {
    return this.observer
        .result()
        .then(({ data } = {}) => Promise.resolve(data).then(func));
  }
}

import { ReactiveVar } from 'meteor/reactive-var';
import { Promise } from 'meteor/promise';

function eqls(a, b) {
  // Because Apollo client data is immutable, we can simply check equality by using ===
  return a === b;
}

export class ReactiveObserver {
  constructor(observer, {
        defaultValue = {},
        equals = eqls,
    } = {}) {
    this.observer = observer;

    this._isReady = new ReactiveVar(false);
    this._errors = new ReactiveVar();
    this._var = new ReactiveVar(defaultValue, equals);

    this.subscribe();
  }

  isReady() {
    return this._isReady.get();
  }

  get() {
    return this._var.get();
  }

  getErrors() {
    return this._errors.get();
  }

  unsubscribe() {
    return this._subscription && this._subscription.unsubscribe();
  }

  subscribe() {
    if (this._subscription) {
      this.unsubscribe();
    }

    this._subscription = this.observer.subscribe({
      next: ({ errors, data } = {}) => {
        if (errors) {
          this._errors.set(errors);
        } else {
          this._errors.set(null);
          this._var.set(data);
        }
        this._isReady.set(true);
      },
      error: (error) => {
        this._errors.set([].concat(error));
        this._isReady.set(true);
      },
    });
  }

  then(func) {
    return this.observer
        .result()
        .then(({ data } = {}) => Promise.resolve(data).then(func));
  }
}

Simplify default equals function based on immutability


'use strict';

<%= angularAppName %>
    .config(function ($routeProvider, $httpProvider, $translateProvider, USER_ROLES) {
            $routeProvider
                .when('/<%= entityInstance %>', {
                    templateUrl: 'views/<%= entityInstance %>s.html',
                    controller: '<%= entityClass %>Controller',
                    resolve:{
                        resolved<%= entityClass %>: ['<%= entityClass %>', function (<%= entityClass %>) {
                            return <%= entityClass %>.query();
                        }]<% for (relationshipId in relationships) {
                            var relationshipClass = relationships[relationshipId].otherEntityNameCapitalized;%>,
                        resolved<%=relationshipClass%>: ['<%=relationshipClass%>', function (<%=relationshipClass%>) {
                            return <%=relationshipClass%>.query();
                        }]<% } %>
                    },
                    access: {
                        authorizedRoles: [USER_ROLES.all]
                    }
                })
        });

'use strict';

<%= angularAppName %>
    .config(function ($routeProvider, $httpProvider, $translateProvider, USER_ROLES) {
            $routeProvider
                .when('/<%= entityInstance %>', {
                    templateUrl: 'views/<%= entityInstance %>s.html',
                    controller: '<%= entityClass %>Controller',
                    resolve:{
                        resolved<%= entityClass %>: ['<%= entityClass %>', function (<%= entityClass %>) {
                            return <%= entityClass %>.query().$promise;
                        }]<% for (relationshipId in relationships) {
                            var relationshipClass = relationships[relationshipId].otherEntityNameCapitalized;%>,
                        resolved<%=relationshipClass%>: ['<%=relationshipClass%>', function (<%=relationshipClass%>) {
                            return <%=relationshipClass%>.query().$promise;
                        }]<% } %>
                    },
                    access: {
                        authorizedRoles: [USER_ROLES.all]
                    }
                })
        });

Fix unresolved data with huge result set


export default {
  title: 'Core|Errors',
};

export const exception = () => {
  throw new Error('error');
};
exception.title = 'story throws exception';
exception.parameters = {
  storyshots: { disabled: true },
};

export const errors = () => null;
errors.title = 'story errors';
errors.parameters = {
  notes: 'Story does not return something react can render',
  storyshots: { disabled: true },
};

export default {
  title: 'Core|Errors',
};

// Comment this story out until Chromatic supports skipping stories via parameters (soon I promise!)
// export const exception = () => {
//   throw new Error('error');
// };
// exception.title = 'story throws exception';
// exception.parameters = {
//   storyshots: { disabled: true },
// };

export const errors = () => null;
errors.title = 'story errors';
errors.parameters = {
  notes: 'Story does not return something react can render',
  storyshots: { disabled: true },
};

Comment out story that fails in Chromatic


$(function() {

  // Pre-populate values from localStorage
  $('#tweet_url').val(localStorage.getItem('tweetUrl') || '');
  $('#usernames').val(localStorage.getItem('usernames') || '');

  // User large version of jstree
  $.jstree.defaults.core.themes.variant = "large";

  $('#view').click(function() {
    $('#loading').show();
    $('#error').hide();
    var tweetUrl = $('#tweet_url').val();
    var usernames = $('#usernames').val();
    localStorage.setItem('tweetUrl', tweetUrl);
    localStorage.setItem('usernames', usernames);

    $.post('/get', {
      tweetUrl: tweetUrl,
      usernames: usernames
    }, function(data) {
      console.log(data);
      if (typeof data === 'string') {
        $('#error').show();
        $('#loading').hide();
        return;
      }

      $('#loading').hide();
      $('#tree').jstree({ 'core' : data });
    });
  });

  $('#clear').click(function() {
    $('#tweet_url').val('');
    $('#usernames').val('');
  });

});

$(function() {

  var loadedOnce = false;

  // Pre-populate values from localStorage
  $('#tweet_url').val(localStorage.getItem('tweetUrl') || '');
  $('#usernames').val(localStorage.getItem('usernames') || '');

  // User large version of jstree
  $.jstree.defaults.core.themes.variant = "large";

  $('#view').click(function() {
    $('#loading').show();
    $('#error').hide();
    var tweetUrl = $('#tweet_url').val();
    var usernames = $('#usernames').val();
    localStorage.setItem('tweetUrl', tweetUrl);
    localStorage.setItem('usernames', usernames);

    $.post('/get', {
      tweetUrl: tweetUrl,
      usernames: usernames
    }, function(res) {
      if (typeof res === 'string') {
        $('#error').show();
        $('#loading').hide();
        return;
      }

      $('#loading').hide();
      if (loadedOnce) {
        $('#tree').jstree(true).settings.core.data = res.data;
        $('#tree').jstree(true).refresh();
      } else {
        $('#tree').jstree({ 'core' : {
          check_callback: true,
          data: res.data
        }});
        loadedOnce = true;
      }
    });
  });

  $('#clear').click(function() {
    $('#tweet_url').val('');
    $('#usernames').val('');
  });

});

Fix tree not being drawn after first time


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

module.exports = Command.extend({
    commandName: 'time',
    commandAliases: ['now'],
    moment: null,
    Discord: null,
    dependencies: {
        'moment': 'moment',
        'Discord': 'Discord',
    },
    processMessage: function (message, tokens) {
        const now = this.moment().locale(this.i18n.getLocale());
        const cdt = now.tz('America/Chicago');
        const jst = now.clone().tz('Asia/Tokyo');
        const gmt = now.clone().tz('Europe/London');

        const embed = new this.Discord.RichEmbed();
        embed.addField('Chicago', cdt.format('lll'));
        embed.addField('London', gmt.format('lll'));
        embed.addField('東京', jst.format('lll'));
        embed.setTimestamp(now);

        message.channel.send(embed);
    }
});

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

module.exports = Command.extend({
    commandName: 'time',
    commandAliases: ['now'],
    moment: null,
    Discord: null,
    dependencies: {
        'moment': 'moment',
        'Discord': 'Discord',
    },
    processMessage: function (message, tokens) {
        const now = this.moment().locale(this.i18n.getLocale());
        const cdt = now.tz('America/Chicago');
        const jst = now.clone().tz('Asia/Tokyo');
        const gmt = now.clone().tz('Europe/London');

        const embed = new this.Discord.RichEmbed();
        embed.addField('Dallas (Shotbow Time)', cdt.format('lll'));
        embed.addField('London', gmt.format('lll'));
        embed.addField('東京', jst.format('lll'));
        embed.setTimestamp(now);

        message.channel.send(embed);
    }
});

Update label for US CT


'use strict';

// module Web.Firebase.Authentication.Eff

exports._onAuth = function (callback, firebase) {
  return function() {
    return firebase.onAuth(callback);
  };
};

exports._authWithOAuthRedirect(provider, errorCallback, ref) {
  var errorCbEffect = function() {
    return errorCallback()(); // ensure effect gets used
  }
  ref.authWithOAuthRedirect(provider, errorCbEffect);
}

'use strict';

// module Web.Firebase.Authentication.Eff

exports._onAuth = function (callback, firebase) {
  return function() {
    return firebase.onAuth(callback);
  };
};

exports._authWithOAuthRedirect(provider, errorCallback, ref) {
  var errorCbEffect = function(error) {
    return errorCallback(error)(); // ensure effect gets used
  }
  ref.authWithOAuthRedirect(provider, errorCbEffect);
}

Fix pass error to callback


// @flow strict-local
import React, { Fragment } from 'react'
import vt323 from './vt323/VT323-Regular.ttf'
import { Global, css } from '@emotion/core'

function GlobalStyles() {
  return (
    <Fragment>
      <Global
        styles={css`
          @font-face {
            font-family: 'VT323-Webfont';
            src: url('${vt323}');
          }
        `}
      />
      <Global
        styles={css`
          html,
          body {
            font-family: 'VT323', 'VT323-Webfont', monospace;
            font-size: 100%;
            margin: 0;
            padding: 0;
          }
        `}
      />
      <Global
        styles={css`
          * {
            box-sizing: border-box;
          }
        `}
      />
    </Fragment>
  )
}

export default GlobalStyles

// @flow strict-local
import React from 'react'
import vt323 from './vt323/VT323-Regular.ttf'
import { Global, css } from '@emotion/core'

function GlobalStyles() {
  return (
    <>
      <Global
        styles={css`
          @font-face {
            font-family: 'VT323-Webfont';
            src: url('${vt323}');
          }
        `}
      />
      <Global
        styles={css`
          html,
          body {
            font-family: 'VT323', 'VT323-Webfont', monospace;
            font-size: 100%;
            margin: 0;
            padding: 0;
          }
        `}
      />
      <Global
        styles={css`
          * {
            box-sizing: border-box;
          }
        `}
      />
    </>
  )
}

export default GlobalStyles

Use fragment JSX syntax instead


let bracketPattern = /\[([^\]]+)\]/g
let inputText = "To [sleep], [per]chance []to [dream]"

let match
let startingIndex
let matchLength
let quillOperations = []

while (match = bracketPattern.exec(inputText)) {
  startingIndex = match.index + 1   // Have to advance past the opening square bracket
  matchLength = bracketPattern.lastIndex - startingIndex - 1  // and remove the closing bracket
  console.log(
    `starting index: ${startingIndex}, matchLength: ${matchLength}
     ${inputText.slice(startingIndex, startingIndex + matchLength)}
     ${match}
    `
  )
}

const bracketPattern = /\[([^\]]+)\]/g
const inputText = "To [sleep], [per]chance []to [dream]"

let match
let startingIndex
let matchLength

while (match = bracketPattern.exec(inputText)) {
  startingIndex = match.index + 1   // Have to advance past the opening square bracket
  matchLength = bracketPattern.lastIndex - startingIndex - 1  // and remove the closing bracket
  console.log(
    `starting index: ${startingIndex}, matchLength: ${matchLength}
     ${inputText.slice(startingIndex, startingIndex + matchLength)}
     ${match}
    `
  )
}

Make bracket regexp a constant


var self = module.exports = {
  __module: {
    provides: {
      "resources/use_scripts": {},
      "resources/use_stylesheets": {},
      "resources/register_assets_dir": {after: ['resources/bootstrap', 'resources/hadron']},
      "resources/register_views_dir": {}
    },
    properties: {
      fontAwesome: 'resources/font-awesome'
    }
  },

  register_assets_dir: function() {
    return __dirname + "/../../assets";
  },

  register_views_dir: function() {
    return __dirname + "/../../views";
  },

  use_scripts: function() {
  },

  use_stylesheets: function() {
    return {
      default: [
        self.fontAwesome.exports.stylesheets.all,
        "http://fonts.googleapis.com/css?family=Lato:300,400,700",
        "styles/main.less"
      ],
      admin: [
        "styles/admin.less"
      ]
    };
  }
};

var self = module.exports = {
  __module: {
    provides: {
      "resources/use_scripts": {},
      "resources/use_stylesheets": {},
      "resources/register_assets_dir": {after: ['resources/bootstrap', 'resources/hadron']},
      "resources/register_views_dir": {}
    },
    properties: {
      fontAwesome: 'resources/font-awesome'
    }
  },

  register_assets_dir: function() {
    return __dirname + "/../../assets";
  },

  register_views_dir: function() {
    return __dirname + "/../../views";
  },

  use_scripts: function() {
  },

  use_stylesheets: function() {
    return {
      default: [
        self.fontAwesome.exports.stylesheets.all,
        "https://fonts.googleapis.com/css?family=Lato:300,400,700",
        "styles/main.less"
      ],
      admin: [
        "styles/admin.less"
      ]
    };
  }
};

Use https for load font files


import reactTransform from 'babel-plugin-react-transform'
import es2015 from 'babel-preset-es2015'
import stage0 from 'babel-preset-stage-0'
import react from 'babel-preset-react'

export default {
  name: 'webpack-babel',
  configure ({ buildTarget }) {
    const hmrEnv = {
      development: {
        plugins: [reactTransform],
        extra: {
          'react-transform': {
            transforms: [{
              transform: 'react-transform-hmr',
              imports: ['react'],
              locals: ['module']
            }]
          }
        }
      }
    }

    return {
      babel: {
        presets: [es2015, stage0, react],
        env: buildTarget === 'develop' ? hmrEnv : {}
      },

      module: {
        loaders: [
          {
            test: /\.jsx?$/,
            exclude: /node_modules/,
            loader: 'babel'
          }
        ]
      },

      resolve: {
        extensions: ['', '.js', '.jsx']
      }
    }
  }
}

import reactTransform from 'babel-plugin-react-transform'
import es2015 from 'babel-preset-es2015'
import stage0 from 'babel-preset-stage-0'
import react from 'babel-preset-react'

export default {
  name: 'webpack-babel',
  configure ({ buildTarget }) {
    const hmrEnv = {
      development: {
        plugins: [
          [reactTransform, {
            transforms: [{
              transform: 'react-transform-hmr',
              imports: ['react'],
              locals: ['module']
            }]
          }]
        ]
      }
    }

    return {
      babel: {
        presets: [es2015, stage0, react],
        env: buildTarget === 'develop' ? hmrEnv : {}
      },

      module: {
        loaders: [
          {
            test: /\.jsx?$/,
            exclude: /node_modules/,
            loader: 'babel'
          }
        ]
      },

      resolve: {
        extensions: ['', '.js', '.jsx']
      }
    }
  }
}

Fix development environment broken after Babel 6 update


/**
 * Created by shellus on 2016-03-16.
 */
angular.module('ngMQTT', [])
    .config(['$provide', function($provide){
        $provide.provider('MQTT', function(){

            var settings = {
                href: ""
            };

            this.setHref = function(href){
                settings.href = href;
            };
            this.$get = function() {
                return settings;
            };
        });
    }])

    .service('MQTTService',
        ['$q', '$rootScope', 'MQTT', function($q, $rootScope, MQTT) {
            var Service = {};
            var callbacks = {};

            var client = mqtt.connect(MQTT.href); // you add a ws:// url here

            client.on("message", function(topic, payload) {
                try {
                    var data = JSON.parse(payload.toString());
                }catch (e){
                    throw new Error("received data can not parse for JSON !");
                }
                angular.forEach(callbacks,function(callback, name){
                    var regexpStr = name.replace(new RegExp('(#)|(\\*)'),function(str){
                        if(str=="#"){
                            return ".*?"
                        }else if(str=="*"){
                            return ".*?"
                        }
                    });
                    if(topic.match(regexpStr)){
                        $rootScope.$apply(function() {
                            callback(data);
                        });
                    }
                })
            });

            client.publish("time", (new Date()).getDate());

            Service.on = function(name, callback){
                callbacks[name] = callback;
                client.subscribe(name);
            };
            Service.send = function(name, data){
                client.publish(name, JSON.stringify(data));
            };
            return Service;
        }]);

/**
 * Created by shellus on 2016-03-16.
 */
angular.module('ngMQTT', [])
    .config(['$provide', function($provide){
        $provide.provider('MQTT', function(){

            var settings = {
                href: ""
            };

            this.setHref = function(href){
                settings.href = href;
            };
            this.$get = function() {
                return settings;
            };
        });
    }])

    .service('MQTTService',
        ['$q', '$rootScope', 'MQTT', function($q, $rootScope, MQTT) {
            var Service = {};
            var callbacks = {};

            var client = mqtt.connect(MQTT.href); // you add a ws:// url here

            client.on("message", function(topic, payload) {
                try {
                    var data = JSON.parse(payload.toString());
                }catch (e){
                    var data = payload.toString();
                }
                angular.forEach(callbacks,function(callback, name){
                    var regexpStr = name.replace(new RegExp('(#)|(\\*)'),function(str){
                        if(str=="#"){
                            return ".*?"
                        }else if(str=="*"){
                            return ".*?"
                        }
                    });
                    if(topic.match(regexpStr)){
                        $rootScope.$apply(function() {
                            callback(data);
                        });
                    }
                })
            });

            client.publish("time", (new Date()).getDate());

            Service.on = function(name, callback){
                callbacks[name] = callback;
                client.subscribe(name);
            };
            Service.send = function(name, data){
                client.publish(name, JSON.stringify(data));
            };
            return Service;
        }]);

Remove throw when payload is not an Object


import Ember  from 'ember'
import layout from '../templates/components/sy-loading-dots'

export default Ember.Component.extend({
  layout,

  classNames: [ 'loading-dots' ]
})

import Ember  from 'ember'
import layout from '../templates/components/sy-loading-dots'

export default Ember.Component.extend({
  layout,

  tagName: 'span',
  classNames: [ 'loading-dots' ]
})

Set tagName of loading dots to 'span'


define([

], function () {

    function removeElement (node) {
        var parent = node.parentNode;
        while (node.hasChildNodes()) {
            node.removeChild(node.lastChild);
        }
        if (parent) {
            parent.removeChild(node);
        }
        return parent;
    }

    return removeElement;
});

define([

], function () {

    function removeElement (node, removeParent) {
        var parent = node.parentNode;
        while (node.hasChildNodes()) {
            node.removeChild(node.lastChild);
        }
        if (removeParent && parent) {
            parent.removeChild(node);
        }
        return parent;
    }

    return removeElement;
});

Swap action and replace action for components


let TwitterTimeline = {
  init(socket, tweets_container) {
    // Now that you are connected, you can join channels with a topic:
    this.channel = socket.channel("twitter_timeline", {})
    this.container = $(tweets_container)
    this.tweets = $(tweets_container).find(".tweets")

    this.channel.join()
      .receive("ok", resp => { console.log("Joined successfully", resp) })
      .receive("error", resp => { console.log("Unable to join", resp) })

    this.channel.onError(e => console.log("something went wrong", e))
    this.channel.onClose(e => console.log("channel closed", e))

    this.channel.on("updated_list", msg => {
      this._refreshAll(msg.tweets)
    })

    this.channel.on("new_tweet", tweet => {
      this._newTweet(tweet)
    })
  },

  _refreshAll(tweets) {
    this.tweets.empty()
    for(var tweet of tweets.slice(0,3)) {
      this.tweets.append(`<li>${tweet.text}<br><i>${tweet.timestamp}</i></li>`)
    }
    this.container.scrollTop(this.container[0].scrollHeight)
  },

  _newTweet(tweet) {
    this.tweets.prepend(`<li>${tweet.text}<br><i>${tweet.timestamp}</i></li>`)
    this.container.scrollTop(this.container[0].scrollHeight)
  }
}

export default TwitterTimeline

let TwitterTimeline = {
  init(socket, tweets_container) {
    // Now that you are connected, you can join channels with a topic:
    this.channel = socket.channel("twitter_timeline", {})
    this.container = $(tweets_container)
    this.tweets = $(tweets_container).find(".tweets")

    this.channel.join()
      .receive("ok", resp => { console.log("Joined successfully", resp) })
      .receive("error", resp => { console.log("Unable to join", resp) })

    this.channel.onError(e => console.log("something went wrong", e))
    this.channel.onClose(e => console.log("channel closed", e))

    this.channel.on("updated_list", msg => {
      this._refreshAll(msg.tweets)
    })

    this.channel.on("new_tweet", tweet => {
      this._newTweet(tweet)
    })
  },

  _refreshAll(tweets) {
    this.tweets.empty()
    for(var tweet of tweets.slice(0,3)) {
      var text = tweet.text.autoLink({target: "_blank"})
      this.tweets.append(`<li>${text}<br><i>${tweet.timestamp}</i></li>`)
    }
    this.container.scrollTop(this.container[0].scrollHeight)
  },

  _newTweet(tweet) {
    var text = tweet.text.autoLink({target: "_blank"})
    this.tweets.prepend(`<li>${text}<br><i>${tweet.timestamp}</i></li>`)
    this.container.scrollTop(this.container[0].scrollHeight)
  }
}

export default TwitterTimeline

Transform links to clickable links in tweets


var Hexo = require('hexo');
var hexo = new Hexo(process.cwd(), {});

var dot = require('dot');

hexo.extend.renderer.register('dot', 'html', function(data, locals){
  return dot.template(data.text)(locals);
}, true);

Add hexo requires for hexo3 compliance


window.onload = function() {
  sectionShow();
};

function sectionShow() {
  document.getElementById('about_btn').addEventListener('click', function(event) {
    event.preventDefault();
    document.getElementById('about').style.display = 'inline'
  })
}

Add sectionShow function to control menu behavior


'use strict';
var es = require('event-stream');
var Mocha = require('mocha');

module.exports = function (options) {
	var mocha = new Mocha(options);

	return es.through(function (file) {
		mocha.addFile(file.path);
		this.emit('data', file);
	}, function () {
		mocha.run(function (errCount) {
			if (errCount > 0) {
				return this.emit('error', new Error('gulp-mocha: ' + errCount + ' ' + (errCount === 1 ? 'test' : 'tests') + ' failed.'));
			}

			this.emit('end');
		}.bind(this));
	});
};

'use strict';
var es = require('event-stream');
var Mocha = require('mocha');
var path = require('path');

module.exports = function (options) {
	var mocha = new Mocha(options);

	return es.through(function (file) {
		delete require.cache[require.resolve(path.resolve(file.path))];
		mocha.addFile(file.path);
		this.emit('data', file);
	}, function () {
		mocha.run(function (errCount) {
			if (errCount > 0) {
				return this.emit('error', new Error('gulp-mocha: ' + errCount + ' ' + (errCount === 1 ? 'test' : 'tests') + ' failed.'));
			}

			this.emit('end');
		}.bind(this));
	});
};

Make gulp-mocha comatible with watch