(function() {
  'use strict';

  angular
    .module('mendel')
    .config(routerConfig);

  /** @ngInject */
  function routerConfig($stateProvider, $urlRouterProvider) {
    $stateProvider
      .state('main', {
        url: '/main/?context',
        templateUrl: 'app/main/main.html',
        reloadOnSearch : false,
        controller: 'MainController',
        controllerAs: 'main'
      })

      .state('login', {
        url: '/login',
        templateUrl: 'app/login/login.html',
        controller: 'LoginController',
        controllerAs: 'login'
      })

      .state('about', {
        url: '/about',
        templateUrl: 'app/about/about.html',
        controller: 'AboutController',
        controllerAs: 'about'
      })

      ;

    $urlRouterProvider.otherwise('/main');
  }

})();

(function() {
  'use strict';

  angular
    .module('mendel')
    .config(routerConfig);

  /** @ngInject */
  function routerConfig($stateProvider, $urlRouterProvider) {
    $stateProvider
      .state('main', {
        url: '/main?context',
        templateUrl: 'app/main/main.html',
        reloadOnSearch : false,
        controller: 'MainController',
        controllerAs: 'main'
      })

      .state('login', {
        url: '/login',
        templateUrl: 'app/login/login.html',
        controller: 'LoginController',
        controllerAs: 'login'
      })

      .state('about', {
        url: '/about',
        templateUrl: 'app/about/about.html',
        controller: 'AboutController',
        controllerAs: 'about'
      })

      ;

    $urlRouterProvider.otherwise('/main');
  }

})();

Fix issue with extra slash in Main URL


$(document).ready(function() {

  var title = [],
    desc = [],
    link = [],
    val = $('#search-box'),
    url = 'http://en.wikipedia.org/w/api.php?action=opensearch&search=';

  $('#search-box').focus();

  $('.search-button').click(function() {
    $('.wiki-container').empty();
    $.getJSON(url + val.val() + '&callback=?').success(function(data) {

      for (i = 0; i < data[1].length; i++) {
        title = data[1][i];
        if (data[2][i] === '') {
          desc = "No description"
        } else {
          desc = data[2][i];
        }
        link = data[3][i];

        $('.wiki-container').append('

' + title + '

' + desc + '

');
      }
    });
  });

  $('#search-box').click(function() {
    $(this).val('');
  });

  //enables the "enter" key to perform search
  $('#search-box').keydown(function(e) {
    if (e.which === 13) {
      $('.search-button').click();
      $('#search-box').blur();
    }
  });
});

$(document).ready(function() {

  var title = [],
    desc = [],
    link = [],
    val = $('#search-box'),
    url = 'https://en.wikipedia.org/w/api.php?action=opensearch&search=';

  $('#search-box').focus();

  $('.search-button').click(function() {
    $('.wiki-container').empty();
    $.getJSON(url + val.val() + '&callback=?').success(function(data) {

      for (i = 0; i < data[1].length; i++) {
        title = data[1][i];
        if (data[2][i] === '') {
          desc = "No description"
        } else {
          desc = data[2][i];
        }
        link = data[3][i];

        $('.wiki-container').append('

' + title + '

' + desc + '

');
      }
    });
  });

  $('#search-box').click(function() {
    $(this).val('');
  });

  //enables the "enter" key to perform search
  $('#search-box').keydown(function(e) {
    if (e.which === 13) {
      $('.search-button').click();
      $('#search-box').blur();
    }
  });
});

Update to https from http


import moment from 'moment-timezone'
import PropTypes from 'prop-types'
import React, { Component } from 'react'

import { AnimalLink } from './'
import { asInfiniteListItem } from '../containers'

class MessageListItem extends Component {
  static propTypes = {
    item: PropTypes.object.isRequired,
  }

  renderDateAndSender() {
    const dateStr = moment(lastSlot.to).format(
      'DD.MM.YY HH:mm'
    )

    return (

        { dateStr }

        <AnimalLink animal={this.props.item.animal} />
        { this.props.item.isWrittenByMe ? ' (me)' : null }
        { !this.props.item.isRead ? ' *' : null }

    )
  }

  render() {
    return (

        { this.renderDateAndSender() }

          { this.props.item.text }

    )
  }
}

export default asInfiniteListItem(MessageListItem)

import moment from 'moment-timezone'
import PropTypes from 'prop-types'
import React, { Component } from 'react'

import { AnimalLink } from './'
import { asInfiniteListItem } from '../containers'

class MessageListItem extends Component {
  static propTypes = {
    item: PropTypes.object.isRequired,
  }

  renderDateAndSender() {
    const dateStr = moment(this.props.item.createdAt).format('DD.MM.YY HH:mm')

    return (

        { dateStr }

        <AnimalLink animal={this.props.item.animal} />
        { this.props.item.isWrittenByMe ? ' (me)' : null }
        { !this.props.item.isRead ? ' *' : null }

    )
  }

  render() {
    return (

        { this.renderDateAndSender() }

          { this.props.item.text }

    )
  }
}

export default asInfiniteListItem(MessageListItem)

Fix wrong variable name in message list items


'use strict'

const concat = require('gulp-concat')
const cleancss = require('gulp-clean-css')
const sourcemaps = require('gulp-sourcemaps')
const filenames = require('gulp-filenames')
const hash = require('gulp-hash-filename')
const util = require('gulp-util')

exports.fn = function (gulp, paths, mode, done) {
  var srcCss = [
    paths.src + '**/css/bootstrap.css',
    paths.src + '**/css/bootstrap-theme.css',
    paths.src + '**/css/font-awesome.css',
    paths.src + 'css/*'
  ]
  return gulp.src(srcCss)
    .pipe(!mode.production ? sourcemaps.init() : util.noop())
    .pipe(concat('app-bundle.css'))
    .pipe(cleancss({keepSpecialComments: 0, sourceMap: false}))
    .pipe(mode.production ? hash({'format': '{hash}{ext}'}) : util.noop())
    .pipe(filenames('cssbundle'))
    .pipe(!mode.production ? sourcemaps.write('.', {
      mapSources: function (sourcePath) {
        var prefix = ''
        if (sourcePath.indexOf('/') === -1) {
          prefix = 'css/'
        }
        var extendedSourcePath = paths.src.substr(2) + prefix + sourcePath
        util.log('SourcePath within source map extended to:', util.colors.cyan(extendedSourcePath))
        return extendedSourcePath
      }
    }) : util.noop())
    .pipe(gulp.dest(paths.wwwroot))
}

'use strict'

const concat = require('gulp-concat')
const cleancss = require('gulp-clean-css')
const sourcemaps = require('gulp-sourcemaps')
const filenames = require('gulp-filenames')
const hash = require('gulp-hash-filename')
const util = require('gulp-util')

exports.fn = function (gulp, paths, mode, done) {
  var srcCss = [
    paths.src + '**/css/bootstrap.css',
    paths.src + '**/css/bootstrap-theme.css',
    paths.src + '**/css/font-awesome.css',
    paths.src + 'css/*'
  ]
  return gulp.src(srcCss)
    .pipe(!mode.production ? sourcemaps.init({loadMaps: true}) : util.noop())
    .pipe(concat('app-bundle.css'))
    .pipe(cleancss({keepSpecialComments: 0, sourceMap: false}))
    .pipe(mode.production ? hash({'format': '{hash}{ext}'}) : util.noop())
    .pipe(filenames('cssbundle'))
    .pipe(!mode.production ? sourcemaps.write('.', {
      mapSources: function (sourcePath) {
        var prefix = ''
        if (sourcePath.indexOf('/') === -1) {
          prefix = 'css/'
        }
        var extendedSourcePath = paths.src.substr(2) + prefix + sourcePath
        util.log('SourcePath within source map extended to:', util.colors.cyan(extendedSourcePath))
        return extendedSourcePath
      }
    }) : util.noop())
    .pipe(gulp.dest(paths.wwwroot))
}

Load already existings source maps if available.


const jwt    = require('jsonwebtoken')
const config = require('config')

const authMiddleware = (req, res, next) => {
  if (req.path === '/auth') { return next() }

  const token = req.header('Authorization')

  console.log('[AUTHENTICATION] Authenticating...')

  jwt.verify(token, config.get('secret'), (error, decoded) => {
    if (error) {
      console.log('[AUTHENTICATION] Authentication failure :(')

      return res.send({
        user: {
          errors: {
            token: 'O token informado não é válido'
          }
        }
      })
    }

    console.log('[AUTHENTICATION] Authenticated :)')
    next()
  })
}

module.exports = authMiddleware

const jwt    = require('jsonwebtoken')
const config = require('config')

const isBypassed = (req) => {
  const bypassed = [
    {
      'path':   '/auth',
      'method': 'POST'
    },
    {
      'path':   '/users',
      'method': 'POST'
    }
  ]

  return bypassed.filter((item) => {
    return req.path === item.path && req.method === item.method
  }).length
}

const authMiddleware = (req, res, next) => {
  if (isBypassed(req)) { return next() }

  const token = req.header('Authorization')

  console.log('[AUTHENTICATION] Authenticating...')

  jwt.verify(token, config.get('secret'), (error, decoded) => {
    if (error) {
      console.log('[AUTHENTICATION] Authentication failure :(')

      return res.status(401).send({
        user: {
          errors: {
            token: 'O token informado não é válido'
          }
        }
      })
    }

    console.log('[AUTHENTICATION] Authenticated :)')
    next()
  })
}

module.exports = authMiddleware

Change bypass logic to be more flexible on authentication middleware


var dbm = require('db-migrate');
var type = dbm.dataType;

exports.up = function(db, callback) {
  db.createTable('users', {
		id: { type: 'int', primaryKey: true, autoIncrement: true },
    name: { type: 'string', notNull: true, unique: true },
    salt: { type: 'string', notNull: true },
    federation_tag: { type: 'string' },
    admin: { type: 'boolean', default: false },
    federation_name: { type: 'string' },
    password_hash: { type: 'string', notNull: true },
    bank_account_id: { type: 'int'},
    kyc_id: { type: 'int' },
    createdAt: { type: 'datetime', notNull: true },
    updatedAt: { type: 'datetime' },
    external_id: { type: string' }
  }, callback);
};

exports.down = function(db, callback) {
  db.dropTable('users', callback);
};

var dbm = require('db-migrate');
var type = dbm.dataType;

exports.up = function(db, callback) {
  db.createTable('users', {
		id: { type: 'int', primaryKey: true, autoIncrement: true },
    name: { type: 'string', notNull: true, unique: true },
    salt: { type: 'string', notNull: true },
    federation_tag: { type: 'string' },
    admin: { type: 'boolean', default: false },
    federation_name: { type: 'string' },
    password_hash: { type: 'string', notNull: true },
    bank_account_id: { type: 'int'},
    kyc_id: { type: 'int' },
    createdAt: { type: 'datetime', notNull: true },
    updatedAt: { type: 'datetime' },
    external_id: { type: 'string' }
  }, callback);
};

exports.down = function(db, callback) {
  db.dropTable('users', callback);
};

Fix string typo in migration


require('coffee-script');
require('should');
Redis = require('redis').createClient();
Streak = require('../lib/');
Streak.configure(function() {
  this.redis = Redis;
});

require('coffee-script');
require('should');
Redis = require('redis').createClient();
Redis.select(15);
Streak = require('../lib/');
Streak.configure(function() {
  this.redis = Redis;
});

Use DB 15 for test


var
  path     = require('path'),
  fs       = require('fs'),
  defaults = require('./defaults')
;

module.exports = {
  del: {
    silent : true
  },
  minify: {
    processImport       : false,
    keepSpecialComments : 0
  },
  uglify: {
    mangle : true
  },
  prefix: {
    browsers: [
      'last 2 version',
      '> 1%',
      'opera 12.1',
      'safari 6',
      'ie 9',
      'bb 10',
      'android 4'
    ]
  },
  header: {
    title      : defaults.title,
    repository : defaults.repository,
    url        : defaults.url
  },
  sourcemap: {
    includeContent : true,
    sourceRoot     : '/src'
  },
  rename: {
    json   : { extname : '.json' },
    minJS  : { extname : '.min.js' },
    minCSS : { extname : '.min.css' }
  },
  wrench: {
    recursive: {
      forceDelete       : false,
      excludeHiddenUnix : true,
      preserveFiles     : true
    }
  }
};

var
  path     = require('path'),
  fs       = require('fs'),
  defaults = require('./defaults')
;

module.exports = {
  del: {
    silent : true
  },
  minify: {
    processImport       : false,
    keepSpecialComments : 0
  },
  uglify: {
    mangle : true
  },
  prefix: {
    browsers: [
      'last 2 version',
      '> 1%',
      'opera 12.1',
      'safari 6',
      'ie 9',
      'bb 10',
      'android 4'
    ]
  },
  header: {
    title      : defaults.title,
    repository : defaults.repository,
    url        : defaults.url
  },
  sourcemap: {
    includeContent : true,
    sourceRoot     : '/src'
  },
  rename: {
    json   : { extname : '.json' },
    minJS  : { extname : '.min.js' },
    minCSS : { extname : '.min.css' }
  },
  wrench: {
    recursive: {
      forceDelete       : true,
      excludeHiddenUnix : true,
      preserveFiles     : true
    }
  }
};

Fix gulp install to add new files inside directories (was avoiding overwritin dir)


describe('hasVowels()', function() {
    it('should always return a boolean', function() {
        expect('example string'.hasVowels()).toEqual(jasmine.any(Boolean));
    });

    it('should return true if string has vowels', function() {
        expect('oreofe'.hasVowels()).toBe(true);
        expect(('AnDeLa').hasVowels()).toBe(true);
    });

    it('should return false if string has no vowels', function() {
        expect('why'.hasVowels()).toBe(false);
        expect(('twyndyllyngs').hasVowels()).toBe(false);
    });
});

describe('toUpper()', function() {
    it('should always return a string', function() {
        expect('example string'.toUpper()).toEqual(jasmine.any(String));
    });

    it('should return an upper case of the calling string', function() {
        expect('upper case'.toUpper()).toBe('UPPER CASE');
        expect('camelCase'.toUpper()).toBe('CAMELCASE');
        expect('MiXeD cAsE'.toUpper()).toBe('MIXED CASE');
    });
});

describe('toLower()', function() {
    it('should return a string', function() {
        expect('example string'.toLower()).toEqual(jasmine.any(String));
    });
});

Add spec to test the return type of toLower()


'use strict';

var matrix = require( 'dstructs-matrix' );
var isMatrix = require( './../lib' );

var mat = matrix( [10,10] );

console.log( isMatrix( mat ) );
// returns true

console.log( isMatrix( [] ) );
// returns false

console.log( isMatrix( {} ) );
// returns false

console.log( isMatrix({
	'data': new Int8Array( 10 ),
	'shape': [5,2],
	'strides': [2,1],
	'dtype': 'int8',
	'length': 10
}));
// returns false

console.log( isMatrix( null ) );
// returns false

'use strict';

// FIXME
return;

var matrix = require( 'dstructs-matrix' );
var isMatrix = require( './../lib' );

var mat = matrix( [10,10] );

console.log( isMatrix( mat ) );
// returns true

console.log( isMatrix( [] ) );
// returns false

console.log( isMatrix( {} ) );
// returns false

console.log( isMatrix({
	'data': new Int8Array( 10 ),
	'shape': [5,2],
	'strides': [2,1],
	'dtype': 'int8',
	'length': 10
}));
// returns false

console.log( isMatrix( null ) );
// returns false

Add a FIXME annotation to is-matrix example


// External
const mem = require('mem');

// Ours
const { merge } = require('../utils/structures');
const { getProjectConfig } = require('./project');

const PROJECT_TYPES_CONFIG = {
  preact: {
    plugins: [
      [
        require.resolve('@babel/plugin-transform-react-jsx'),
        {
          pragma: 'h'
        }
      ]
    ]
  },
  react: {
    presets: [require.resolve('@babel/preset-react')]
  }
};

module.exports.getBabelConfig = mem(({ isModernJS } = {}) => {
  const { babel: projectBabelConfig, pkg, type } = getProjectConfig();

  return merge(
    {
      presets: [
        [
          require.resolve('@babel/preset-env'),
          {
            targets: {
              browsers: isModernJS
                ? ['Chrome >= 60', 'Safari >= 10.1', 'iOS >= 10.3', 'Firefox >= 54', 'Edge >= 15']
                : pkg.browserslist || ['> 1% in au', '> 5%', 'Firefox ESR']
            },
            useBuiltIns: 'entry',
            corejs: 3,
            modules: process.env.NODE_ENV === 'test' ? 'commonjs' : false
          }
        ]
      ],
      plugins: [
        require.resolve('@babel/plugin-proposal-object-rest-spread'),
        require.resolve('@babel/plugin-syntax-dynamic-import'),
        require.resolve('@babel/plugin-proposal-class-properties')
      ]
    },
    PROJECT_TYPES_CONFIG[type],
    projectBabelConfig
  );
});

// External
const mem = require('mem');

// Ours
const { merge } = require('../utils/structures');
const { getProjectConfig } = require('./project');

const PROJECT_TYPES_CONFIG = {
  preact: {
    plugins: [
      [
        require.resolve('@babel/plugin-transform-react-jsx'),
        {
          pragma: 'h'
        }
      ]
    ]
  },
  react: {
    presets: [require.resolve('@babel/preset-react')]
  }
};

module.exports.getBabelConfig = mem(({ isModernJS } = {}) => {
  const { babel: projectBabelConfig, pkg, type } = getProjectConfig();

  return merge(
    {
      presets: [
        [
          require.resolve('@babel/preset-env'),
          {
            targets: {
              browsers: isModernJS
                ? ['Chrome >= 76', 'Safari >= 12.1', 'iOS >= 12.3', 'Firefox >= 69', 'Edge >= 18']
                : pkg.browserslist || ['> 1% in au', '> 5%', 'Firefox ESR']
            },
            useBuiltIns: 'entry',
            corejs: 3,
            modules: process.env.NODE_ENV === 'test' ? 'commonjs' : false
          }
        ]
      ],
      plugins: [
        require.resolve('@babel/plugin-proposal-object-rest-spread'),
        require.resolve('@babel/plugin-syntax-dynamic-import'),
        require.resolve('@babel/plugin-proposal-class-properties')
      ]
    },
    PROJECT_TYPES_CONFIG[type],
    projectBabelConfig
  );
});

Raise modern browser support bar


const merge = require('webpack-merge');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const baseConfig = require('./base.config.js');
const paths = require('./paths');

const devConfig = merge(baseConfig, {
  entry: {
    map: './src/map.js',
    bolg: './src/styles/post-index.scss',
    post: './src/post.js',
    gallery: './src/gallery.js',
  },
  output: {
    path: paths.public,
    filename: '[name].js',
    publicPath: paths.assets,
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        loader: ExtractTextPlugin.extract({ use: 'css-loader?importLoaders=1' }),
      },
      {
        test: /\.scss$/,
        exclude: /node_modules/,
        loader: ExtractTextPlugin.extract({
          fallback: 'style-loader',
          use: 'css-loader!sass-loader',
        }),
      },
    ],
  },
  plugins: [
    new CopyWebpackPlugin([
      {
        from: paths.static,
        to: paths.public,
        ignore: ['.*'],
      },
    ]),
    new FriendlyErrorsPlugin(),
  ],
  target: 'web',
  stats: 'minimal',
});
module.exports = devConfig;

const merge = require('webpack-merge');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const ManifestPlugin = require('webpack-manifest-plugin');
const baseConfig = require('./base.config.js');
const paths = require('./paths');

const devConfig = merge(baseConfig, {
  entry: {
    map: './src/map.js',
    bolg: './src/styles/post-index.scss',
    post: './src/post.js',
    gallery: './src/gallery.js',
  },
  output: {
    path: paths.public,
    publicPath: paths.assets,
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        loader: ExtractTextPlugin.extract({ use: 'css-loader?importLoaders=1' }),
      },
      {
        test: /\.scss$/,
        exclude: /node_modules/,
        loader: ExtractTextPlugin.extract({
          fallback: 'style-loader',
          use: 'css-loader!sass-loader',
        }),
      },
    ],
  },
  plugins: [
    new CopyWebpackPlugin([
      {
        from: paths.static,
        to: paths.public,
        ignore: ['.*'],
      },
    ]),
    new ManifestPlugin({
      fileName: paths.frontManifest,
      basePath: paths.assets,
    }),
  ],
  target: 'web',
  stats: 'minimal',
});
module.exports = devConfig;

Remove friendly errors plugin (base) and add the manifest plugin


/*
Copyright 2017 New Vector Ltd

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.
*/

'use strict';

import React from 'react';
import PropTypes from 'prop-types';

export default class GenericElementContextMenu extends React.Component {
    static PropTypes = {
        element: PropTypes.element.isRequired,
    };

    render() {
        return <div>{ this.props.element }</div>;
    }
}

/*
Copyright 2017 New Vector Ltd

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.
*/

'use strict';

import React from 'react';
import PropTypes from 'prop-types';

export default class GenericElementContextMenu extends React.Component {
    static PropTypes = {
        element: PropTypes.element.isRequired,
    };

    componentDidMount() {
        this.resize = this.resize.bind(this);
        window.addEventListener("resize", this.resize.bind(this));
    }

    componentWillUnmount() {
        window.removeEventListener("resize", this.resize.bind(this));
    }

    resize() {
        if (this.props.onResize) {
            this.props.onResize();
        }
    }

    render() {
        return <div>{ this.props.element }</div>;
    }
}

Call for update on window resize.


const expect = require('chai').expect;
const app = mutil.getApp();
const AuthController = require(__root + 'auth/AuthController');

describe('AuthController', function () {
  it('should be a function', function () {
    expect(AuthController).to.be.a('function');
  });
  it('should take one parameter', function () {
    expect(AuthController.bind(this, app)).to.not.throw(Error);
  });
  it('should throw error if app parameter is missing', function () {
    expect(AuthController).to.throw(Error);
  });
  it('should return an express router function', function () {
    expect(AuthController(app)).to.be.a('function');
  });
});

Add AuthController tests similar to the NoteController


export default ({
  rules,
  ...eslintConfig
} = {
  extends: 'marlint',
  rules: {},
}) => ({
  ...eslintConfig,
  rules: {
    'react/react-in-jsx-scope': 'off',
    ...rules,
  },
});

export default ({
  rules,
  ...eslintConfig
} = {
  rules: {},
}) => ({
  extends: 'marlint',
  rules: {
    'react/react-in-jsx-scope': 'off',
    ...rules,
  },
  ...eslintConfig,
});

Fix custom configuration doesn't extend marlint


"use strict";

import { Constants as AssessmentConstants }  from "../actions/assessment";
import Immutable                             from "immutable";

const initialState = Immutable.fromJS({
  assessmentFormat: null
});

export default (state = initialState, action) => {

  switch(action.type){

    case AssessmentConstants.LOAD_ASSESSMENT_DONE:
      return state.set('assessmentFormat', action.payload.format);
      break;

    default:
      return state;
  }

};

"use strict";

import { Constants as AssessmentConstants }  from "../actions/assessment";
import Immutable                             from "immutable";

const initialState = Immutable.fromJS({
  assessmentFormat: null
});

export default (state = initialState, action) => {

  switch(action.type){

    case AssessmentConstants.LOAD_ASSESSMENT_DONE:
      return state.set('assessmentFormat', action.payload.standard);
      break;

    default:
      return state;
  }

};

Use 'standard' that is already provided.


import template from './resource-state.html';

const resourceState = {
  template: template,
  bindings: {
    resource: '<'
  },
  controller: class ResourceStateController {
    constructor(resourceUtils) {
      // @ngInject
      this.resourceUtils = resourceUtils;
    }

    $onInit() {
      this.refresh();
    }

    $onChange() {
      this.refresh();
    }

    refresh() {
      this.context = this.resourceUtils.getResourceState(this.resource);
    }
  }
};

export default resourceState;

import template from './resource-state.html';

const resourceState = {
  template: template,
  bindings: {
    resource: '<'
  },
  controller: function ResourceStateController($scope, resourceUtils) {
    $scope.$watch(() => this.resource, () =>
      this.context = resourceUtils.getResourceState(this.resource));
  }
};

export default resourceState;

Fix watcher for resource state.


// @flow
import * as React from 'react'
import {StyleSheet, Platform} from 'react-native'
import Icon from 'react-native-vector-icons/Ionicons'

const styles = StyleSheet.create({
	icon: {
		fontSize: Platform.select({
			ios: 30,
			android: 24,
		}),
	},
})

type Props = {
	tintColor: string,
	focused: boolean,
}

export const platformPrefixIcon = (name: string) => {
	let isAvailable = Icon.hasIcon(name)
	let isAvailableOnBothPlatforms =
		Icon.hasIcon(`ios-${name}`) && Icon.hasIcon(`md-${name}`)

	if (isAvailable && !isAvailableOnBothPlatforms) {
		return name
	}

	return Platform.OS === 'ios' ? `ios-${name}` : `md-${name}`
}

export const TabBarIcon = (icon: string) => ({tintColor}: Props) => (
	<Icon name={`ios-${icon}`} style={[styles.icon, {color: tintColor}]} />
)

// @flow
import * as React from 'react'
import {StyleSheet, Platform} from 'react-native'
import Icon from 'react-native-vector-icons/Ionicons'

const styles = StyleSheet.create({
	icon: {
		fontSize: Platform.select({
			ios: 30,
			android: 24,
		}),
	},
})

type Props = {
	tintColor: string,
	focused: boolean,
}

export const platformPrefixIcon = (name: string) => {
	let isAvailable = Icon.hasIcon(name)
	let isAvailableOnBothPlatforms =
		Icon.hasIcon(`ios-${name}`) && Icon.hasIcon(`md-${name}`)

	if (isAvailable && !isAvailableOnBothPlatforms) {
		return name
	}

	return Platform.OS === 'ios' ? `ios-${name}` : `md-${name}`
}

export const TabBarIcon = (icon: string) => ({tintColor}: Props) => (
	<Icon
		name={platformPrefixIcon(icon)}
		style={[styles.icon, {color: tintColor}]}
	/>
)

Use platformPrefixIcon to smartly prefix the icon


'use strict';

const dateHelper = require(`${__dirname}/../helpers/dateHelper`);
const requestHelper = require(`${__dirname}/../helpers/requestHelper`);

exports.getRowRequest = (field, req, res) => {
  if (dateHelper.isValidDate(req.query.from) && dateHelper.isValidDate(req.query.to)) {
    requestHelper.makeAllRowRequest(field, req.query.from, req.query.to, res);
  } else {
    res.json({ success: false, message: 'Bad date formatting' });
  }
};

'use strict';

const dateHelper = require(`${__dirname}/../helpers/dateHelper`);
const requestHelper = require(`${__dirname}/../helpers/requestHelper`);

exports.getRowRequest = (field, size, req, res) => {
  if (dateHelper.isValidDate(req.query.from) && dateHelper.isValidDate(req.query.to)) {
    requestHelper.makeAllRowRequest(field, size, req.query.from, req.query.to, res);
  } else {
    res.json({ success: false, message: 'Bad date formatting' });
  }
};

Change controller function to support custom size


'use strict';

/* https://github.com/angular/protractor/blob/master/docs/toc.md */

describe('my app', function() {

  browser.get('index.html');

  it('should automatically redirect to /view1 when location hash/fragment is empty', function() {
    expect(browser.getLocationAbsUrl()).toMatch("/view1");
  });

  describe('view1', function() {

    beforeEach(function() {
      browser.get('index.html#/view1');
    });

    it('should render view1 when user navigates to /view1', function() {
      expect(element.all(by.css('[ng-view] p')).first().getText()).
        toMatch(/partial for view 1/);
    });

  });

  describe('view2', function() {

    beforeEach(function() {
      browser.get('index.html#/view2');
    });

    it('should render view2 when user navigates to /view2', function() {
      expect(element.all(by.css('[ng-view] p')).first().getText()).
        toMatch(/partial for view 2/);
    });

  });
});

'use strict';

/* https://github.com/angular/protractor/blob/master/docs/toc.md */

describe('my app', function() {
  var homeUrl = "/home";

  describe('page routing', function() {
    it('should redirect index.html to '+homeUrl, function() {
      browser.get('index.html');
      browser.getLocationAbsUrl().then(function(url) {
          expect(url.split('#')[1]).toBe(homeUrl);
        });
    });

    it('should redirect empty hash fragment to '+homeUrl, function() {
      browser.get('');
      browser.getLocationAbsUrl().then(function(url) {
        expect(url.split('#')[1]).toBe(homeUrl);
      });
    })

    it('should redirect #/asdf to '+homeUrl, function() {
      browser.get('#/asdf');
      browser.getLocationAbsUrl().then(function(url) {
        expect(url.split('#')[1]).toBe(homeUrl);
      });
    })
  });
});

Add e2e tests for the routing system.


/*
 * Suite-global initialization that should occur before any other files are required. It's probably
 * a code smell that I have to do this.
 */

var config = require('../../src/config');

function reconfigure () {
  if (process.env.INTEGRATION) {
    console.log('Integration test mode active.');

    config.configure(process.env);

    console.log('NOTE: This will leave files uploaded in Cloud Files containers.');
    console.log('Be sure to clear these containers after:');
    console.log('[' + config.contentContainer() + '] and [' + config.assetContainer() + ']');
  } else {
    config.configure({
      STORAGE: 'memory',
      RACKSPACE_USERNAME: 'me',
      RACKSPACE_APIKEY: '12345',
      RACKSPACE_REGION: 'space',
      ADMIN_APIKEY: '12345',
      CONTENT_CONTAINER: 'the-content-container',
      ASSET_CONTAINER: 'the-asset-container',
      MONGODB_URL: 'mongodb-url',
      CONTENT_LOG_LEVEL: process.env.CONTENT_LOG_LEVEL || 'error'
    });
  }
}

reconfigure();

exports.reconfigure = reconfigure;

/*
 * Suite-global initialization that should occur before any other files are required. It's probably
 * a code smell that I have to do this.
 */

var config = require('../../src/config');

function reconfigure () {
  if (process.env.INTEGRATION) {
    console.log('Integration test mode active.');

    config.configure(process.env);

    console.log('NOTE: This will leave files uploaded in Cloud Files containers.');
    console.log('Be sure to clear these containers after:');
    console.log('[' + config.contentContainer() + '] and [' + config.assetContainer() + ']');
  } else {
    config.configure({
      STORAGE: 'memory',
      RACKSPACE_USERNAME: 'me',
      RACKSPACE_APIKEY: '12345',
      RACKSPACE_REGION: 'space',
      ADMIN_APIKEY: process.env.ADMIN_APIKEY || '12345',
      CONTENT_CONTAINER: 'the-content-container',
      ASSET_CONTAINER: 'the-asset-container',
      MONGODB_URL: 'mongodb-url',
      CONTENT_LOG_LEVEL: process.env.CONTENT_LOG_LEVEL || 'error'
    });
  }
}

reconfigure();

exports.reconfigure = reconfigure;

Use ADMIN_APIKEY from the env in tests.


var ForgePlugins = ForgePlugins || {};

/**
 */
ForgePlugins.EditorHUD = function(editor)
{
    this._editor = editor;

    this._canvas = null;

    this._options = { cross: true };

    this._boot();
};

ForgePlugins.EditorHUD.prototype =
{

    _boot: function()
    {
        this._canvas = this._editor.plugin.create.canvas();
        this._canvas.maximize(true);
        this._editor.plugin.container.addChild(this._canvas);
    },

    update: function()
    {
        var ctx = this._canvas.context2D;
        ctx.clearRect(0, 0, this._canvas.pixelWidth, this._canvas.pixelHeight);

        if(this._options.cross === true)
        {
            var center = { x: this._canvas.pixelWidth / 2, y: this._canvas.pixelHeight / 2 };

            ctx.lineWidth = 2;
            ctx.strokeStyle = "white";
            ctx.beginPath();
            ctx.moveTo(center.x - 10, center.y);
            ctx.lineTo(center.x + 10, center.y);
            ctx.moveTo(center.x, center.y - 10);
            ctx.lineTo(center.x, center.y + 10);
            ctx.stroke();
        }
    },

    destroy: function()
    {
        this._canvas = null;
    }
};

var ForgePlugins = ForgePlugins || {};

/**
 */
ForgePlugins.EditorHUD = function(editor)
{
    this._editor = editor;

    this._canvas = null;

    this._options = { cross: true };

    this._boot();
};

ForgePlugins.EditorHUD.prototype =
{

    _boot: function()
    {
        this._canvas = this._editor.plugin.create.canvas();
        this._canvas.maximize(true);
        this._editor.plugin.container.addChild(this._canvas);
    },

    update: function()
    {
        var ctx = this._canvas.context2D;
        ctx.clearRect(0, 0, this._canvas.pixelWidth, this._canvas.pixelHeight);

        if(this._options.cross === true)
        {
            var center = { x: this._canvas.pixelWidth / 2, y: this._canvas.pixelHeight / 2 };

            ctx.lineWidth = 4;
            ctx.strokeStyle = "black";
            ctx.beginPath();
            ctx.moveTo(center.x - 11, center.y);
            ctx.lineTo(center.x + 11, center.y);
            ctx.moveTo(center.x, center.y - 11);
            ctx.lineTo(center.x, center.y + 11);
            ctx.stroke();

            ctx.lineWidth = 2;
            ctx.strokeStyle = "white";
            ctx.beginPath();
            ctx.moveTo(center.x - 10, center.y);
            ctx.lineTo(center.x + 10, center.y);
            ctx.moveTo(center.x, center.y - 10);
            ctx.lineTo(center.x, center.y + 10);
            ctx.stroke();
        }
    },

    destroy: function()
    {
        this._canvas = null;
    }
};

Add black lines to hud cross


export default function(sequelize, DataTypes) {
  let BSDCallAssignment = sequelize.define('BSDCallAssignment', {
    name: DataTypes.STRING
  }, {
    underscored: true,
    tableName: 'bsd_call_assignments',
    classMethods: {
      associate: (models) => {
        BSDCallAssignment.belongsTo(models.BSDSurvey, {foreignKey: 'bsd_survey_id', as: 'survey'})
        BSDCallAssignment.belongsTo(models.BSDGroup, {as: 'intervieweeGroup'});
        BSDCallAssignment.belongsTo(models.BSDGroup, {as: 'callerGroup'});
        BSDCallAssignment.hasMany(models.BSDAssignedCall, {
          as: 'assignedCalls',
          foreignKey: {
            name: 'call_assignment_id'
          }
        })
      }
    }
  })
  return BSDCallAssignment;
}

export default function(sequelize, DataTypes) {
  let BSDCallAssignment = sequelize.define('BSDCallAssignment', {
    name: DataTypes.STRING
  }, {
    underscored: true,
    tableName: 'bsd_call_assignments',
    classMethods: {
      associate: (models) => {
        BSDCallAssignment.belongsTo(models.BSDSurvey, {foreignKey: 'signup_form_id', as: 'survey'})
        BSDCallAssignment.belongsTo(models.BSDGroup, {as: 'intervieweeGroup'});
        BSDCallAssignment.belongsTo(models.BSDGroup, {as: 'callerGroup'});
        BSDCallAssignment.hasMany(models.BSDAssignedCall, {
          as: 'assignedCalls',
          foreignKey: {
            name: 'call_assignment_id'
          }
        })
      }
    }
  })
  return BSDCallAssignment;
}

Use underlying table name in foreign key


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

elixir(function(mix) {
    mix.less('style.less');

    mix.scripts([
        '../assets/bower_components/d3/d3.js',
        'coordinate_system.js',
        'helpers.js',
        'chart_data.js',
        'graph.js',
        'progress_graph.js',
        'bar_chart.js',
        'main_chart.js',
        'pie_charts.js',
        '../assets/bower_components/list.js/dist/list.js',
        'sprint_backlog.js'
    ], 'public/js/sprint_overview.js');

    mix.scripts([
        '../assets/bower_components/jquery/dist/jquery.js',
        '../assets/bower_components/bootstrap/dist/js/bootstrap.js'
    ], 'public/js/main.js');

    mix.scripts(
        '../assets/bower_components/bootstrap-datepicker/js/bootstrap-datepicker.js',
        'public/js/datepicker.js'
    ).styles(
        '../assets/bower_components/bootstrap-datepicker/css/datepicker3.css',
        'public/css/datepicker.css'
    );
});

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

elixir.config.sourcemaps = false;

elixir(function(mix) {
    mix.less('style.less', 'public/css/style.css');

    mix.scripts([
        '../assets/bower_components/d3/d3.js',
        'coordinate_system.js',
        'helpers.js',
        'chart_data.js',
        'graph.js',
        'progress_graph.js',
        'bar_chart.js',
        'main_chart.js',
        'pie_charts.js',
        '../assets/bower_components/list.js/dist/list.js',
        'sprint_backlog.js'
    ], 'public/js/sprint_overview.js', 'resources/js');

    mix.scripts([
        '../assets/bower_components/jquery/dist/jquery.js',
        '../assets/bower_components/bootstrap/dist/js/bootstrap.js'
    ], 'public/js/main.js', 'resources/js');

    mix.scripts(
        '../assets/bower_components/bootstrap-datepicker/js/bootstrap-datepicker.js',
        'public/js/datepicker.js', 'resources/js'
    ).styles(
        '../assets/bower_components/bootstrap-datepicker/css/datepicker3.css',
        'public/css/datepicker.css', 'resources/js'
    );
});

Add missing parameters and settings to Elixir functions


import { put, select } from 'redux-saga/effects'
import TemperatureActions from '../Redux/TemperatureRedux'
import { is } from 'ramda'

// exported to make available for tests
export const selectTemperature = (state) => state.temperature.temperature

// process STARTUP actions
export function * startup (action) {
  if (__DEV__) {
    // straight-up string logging
    console.tron.log('Hello, I\'m an example of how to log via Reactotron.')

    // logging an object for better clarity
    console.tron.log({
      message: 'pass objects for better logging',
      someGeneratorFunction: selectTemperature
    })

    // fully customized!
    const subObject = { a: 1, b: [1, 2, 3], c: true }
    subObject.circularDependency = subObject // osnap!
    console.tron.display({
      name: '🔥 IGNITE 🔥',
      preview: 'You should totally expand this',
      value: {
        '💃': 'Welcome to the future!',
        subObject,
        someInlineFunction: () => true,
        someGeneratorFunction: startup,
        someNormalFunction: selectTemperature
      }
    })
  }
  const temp = yield select(selectTemperature)
  // only fetch new temps when we don't have one yet
  if (!is(Number, temp)) {
    yield put(TemperatureActions.temperatureRequest('San Francisco'))
  }
}

import { put, select } from 'redux-saga/effects'
import TemperatureActions from '../Redux/TemperatureRedux'
import { is } from 'ramda'

// exported to make available for tests
export const selectTemperature = (state) => state.temperature.temperature

// process STARTUP actions
export function * startup (action) {
  if (__DEV__ && console.tron) {
    // straight-up string logging
    console.tron.log('Hello, I\'m an example of how to log via Reactotron.')

    // logging an object for better clarity
    console.tron.log({
      message: 'pass objects for better logging',
      someGeneratorFunction: selectTemperature
    })

    // fully customized!
    const subObject = { a: 1, b: [1, 2, 3], c: true }
    subObject.circularDependency = subObject // osnap!
    console.tron.display({
      name: '🔥 IGNITE 🔥',
      preview: 'You should totally expand this',
      value: {
        '💃': 'Welcome to the future!',
        subObject,
        someInlineFunction: () => true,
        someGeneratorFunction: startup,
        someNormalFunction: selectTemperature
      }
    })
  }
  const temp = yield select(selectTemperature)
  // only fetch new temps when we don't have one yet
  if (!is(Number, temp)) {
    yield put(TemperatureActions.temperatureRequest('San Francisco'))
  }
}

Fix the console.tron call in a saga when running via ava.


"use strict";

var jsdom = require("../..").jsdom;

exports["should put errors in the window.document.errors array"] = function (t) {
  var window = jsdom("<!DOCTYPE html>Hi").createWindow();

  window.console.log("foo");
  window.console.info("bar");
  window.console.warn("baz");
  window.console.error("qux");

  t.deepEqual(window.document.errors, [{ type: "error", message: "qux", data: null }]);
  t.done();
};

exports["should send errors to the correct window when multiple are in play (GH-658)"] = function (t) {
  var window1 = jsdom("<!DOCTYPE html>Hi").createWindow();
  var window2 = jsdom("<!DOCTYPE html>Hi").createWindow();

  window1.console.error("foo");
  window2.console.error("bar");

  t.notEqual(window1.console, window2.console);
  t.deepEqual(window1.document.errors, [{ type: "error", message: "foo", data: null }]);
  t.deepEqual(window2.document.errors, [{ type: "error", message: "bar", data: null }]);
  t.done();
};

"use strict";

var jsdom = require("../..").jsdom;

exports["should put errors in the window.document.errors array"] = function (t) {
  var window = jsdom("<!DOCTYPE html>Hi").parentWindow;

  window.console.log("foo");
  window.console.info("bar");
  window.console.warn("baz");
  window.console.error("qux");

  t.deepEqual(window.document.errors, [{ type: "error", message: "qux", data: null }]);
  t.done();
};

exports["should send errors to the correct window when multiple are in play (GH-658)"] = function (t) {
  var window1 = jsdom("<!DOCTYPE html>Hi").parentWindow;
  var window2 = jsdom("<!DOCTYPE html>Hi").parentWindow;

  window1.console.error("foo");
  window2.console.error("bar");

  t.notEqual(window1.console, window2.console);
  t.deepEqual(window1.document.errors, [{ type: "error", message: "foo", data: null }]);
  t.deepEqual(window2.document.errors, [{ type: "error", message: "bar", data: null }]);
  t.done();
};

Use parentWindow instead of createWindow() in these tests.


import NodeGarden from './nodegarden'

var pixelRatio = window.devicePixelRatio
var $container = document.getElementById('container')
var $moon = document.getElementsByClassName('moon')[0]

var nodeGarden = new NodeGarden($container)

// start simulation
nodeGarden.start()

// trigger nightMode automatically
var date = new Date()
if (date.getHours() > 18 || date.getHours() < 6) {
  nodeGarden.toggleNightMode()
}

var resetNode = 0

$container.addEventListener('click', function (e) {
  resetNode++
  if (resetNode > nodeGarden.nodes.length - 1) {
    resetNode = 1
  }
  nodeGarden.nodes[resetNode].reset({x: e.pageX * pixelRatio, y: e.pageY * pixelRatio, vx: 0, vy: 0})
})

$moon.addEventListener('click', () => { nodeGarden.toggleNightMode() })
window.addEventListener('resize', () => { nodeGarden.resize() })

import NodeGarden from './nodegarden';

const pixelRatio = window.devicePixelRatio;
const $container = document.getElementById('container');
const $moon = document.getElementsByClassName('moon')[0];

const nodeGarden = new NodeGarden($container);

// start simulation
nodeGarden.start();

// trigger nightMode automatically
const date = new Date();

if (date.getHours() > 18 || date.getHours() < 6) {
  nodeGarden.toggleNightMode();
}

let resetNode = 0;

$container.addEventListener('click', (e) => {
  const bcr = $container.getBoundingClientRect();
  resetNode++;
  if (resetNode > nodeGarden.nodes.length - 1) {
    resetNode = 1;
  }
  nodeGarden.nodes[resetNode].reset({
    x: (e.pageX - bcr.left) * pixelRatio,
    y: (e.pageY - bcr.top) * pixelRatio,
    vx: 0,
    vy: 0
  });
});

$moon.addEventListener('click', () => {
  nodeGarden.toggleNightMode();
});

window.addEventListener('resize', () => {
  nodeGarden.resize();
});

Fix relative positioning issue, use classes and other sugar, ...


import { gql } from 'react-apollo'

export const createMentorship = gql`
  mutation {
    createMentorship(input: {
      mentorship: {
        subjectId: "314ebf89-b463-4a70-812e-9222e797cfb8"
      }
    })
  }
`

import { gql } from 'react-apollo'

export const createMentorship = gql`
  mutation {
    createMentorship(input: {
      mentorship: {
        subjectId: "199bfcda-eae2-4b9b-8cbc-e02520b426f6",
        userId: "03cb6404-bd2d-4532-afab-fa72abe17a28"
      }
    })
  }
`

Add create mentorship example query


import test from 'ava';

test.todo('Write test for inView');

import test from 'ava';
import inView from '../src/in-view';
import Registry from '../src/registry';

test('inView is a function', t => {
    t.true(typeof inView === 'function');
});

test('inView returns a registry', t => {
    t.true(inView('body').__proto__ === Registry([]).__proto__);
});

test('inView returns existing registries', t => {
    let registry = inView('body');
    t.true(registry === inView('body'));
});

test('inView updates existing registry elements', t => {

    const addDiv = () => {
        document.body.appendChild(
            document.createElement('div')
        );
    };

    t.true(inView('div').elements.length === 0);

    addDiv();
    t.true(inView('div').elements.length === 1);

});

test.after(initBrowserEnv);

Add initial tests for inView interface


function extractJSON(rawJson) {
  return rawJson
    .replace(/\s*while\((1|true)\)\s*;?/, '')
    .replace(/\s*for\(;;\)\s*;?/, '')
    .replace(/^[^{\[].+\({/, '{')
    .replace(/}\s?\);?\s*$/, '}');
}

module.exports = extractJSON;

function extractJSON(rawJson) {
  return rawJson
    .replace(/\s*while\((1|true)\)\s*;?/, '')
    .replace(/\s*for\(;;\)\s*;?/, '')
    .replace(/^[^{\[].+\(\s?{/, '{')
    .replace(/}\s?\);?\s*$/, '}');
}

module.exports = extractJSON;

Fix the regular expression at the beginning and end of raw jsonp


suite('NumberValue', function() {
  test('NumberValue is a NumberValue and a StyleValue', function() {
    var nv = new NumberValue(3);
    assert.instanceOf(nv, NumberValue);
    assert.instanceOf(nv, StyleValue);
  });
});

Add newline to end of test


var config = require('tresdb-config');
var db = require('tresdb-db');
var bcrypt = require('bcryptjs');

exports.createUser = function (username, email, password, callback) {
  // Create non-admin active user. Does not check if user exists.
  //
  // Parameters:
  //   username
  //     string
  //   email
  //     string
  //   password
  //     string
  //   callback
  //     function (err)

  var users = db.collection('users');

  var r = config.bcrypt.rounds;

  bcrypt.hash(password, r, function (berr, pwdHash) {

    if (berr) {
      return callback(berr);
    }

    users.insert({
      name: username,
      email: email,
      hash: pwdHash,
      admin: false,
      status: 'active',  // 'deactivated'
    }, function (qerr) {

      if (qerr) {
        return callback(qerr);
      }

      // User inserted successfully
      return callback();
    });
  });
};

var config = require('tresdb-config');
var db = require('tresdb-db');
var bcrypt = require('bcryptjs');

exports.createUser = function (username, email, password, callback) {
  // Create non-admin active user. Does not check if user exists.
  //
  // Parameters:
  //   username
  //     string
  //   email
  //     string
  //   password
  //     string
  //   callback
  //     function (err)

  var users = db.collection('users');

  var r = config.bcrypt.rounds;

  bcrypt.hash(password, r, function (berr, pwdHash) {
    if (berr) {
      return callback(berr);
    }

    users.insert({
      name: username,
      email: email,
      hash: pwdHash,
      admin: false,
      status: 'active',  // options. 'active' | 'deactivated',
      createdAt: (new Date()).toISOString(),
      loginAt: (new Date()).toISOString(),
    }, function (qerr) {
      if (qerr) {
        return callback(qerr);
      }
      // User inserted successfully
      return callback();
    });
  });
};

Create new users with createdAt and loginAt props


var myArray = [];
$("#submit").on('click', function() {
	if ( $('#box').val() || $('#box').text() ) {
		// myArray variable below checks for period, comma, apostrophe, hashtag and forward-slash and removes them.
		myArray = $('#box').val().replace(/\.|,|'|#|/g, '').split('\n');
		if ( $('#output').length === 0) {
			$('form').append('<textarea id="output" rows="20" cols="50"></textarea>');
		}
		for( var i = 0; i<myArray.length; i++ ) {
			var newValue = myArray[i].toLowerCase().replace(/ |\//g, "-");
				if ( i === myArray.length - 1) {
					$('#output').append(newValue);
					$('#submit').attr('disabled', 'disabled');
					$('#clipboard').removeAttr('disabled');
				} else {
					$('#output').append(newValue + '\n');
				}
			}
		} else {
			alert('This is a required field.');
		}
	}
);

// copy to clipboard code

$('#clipboard').click(function(){
    var output = document.getElementById('output');
    output.focus();
    document.execCommand('SelectAll');
    document.execCommand("Copy", false, null);
    output.blur();
});

$("#reset_button").on('click', function(value) {
	$('#submit').removeAttr('disabled');
	$('#output').remove();
	$('#clipboard').attr('disabled', 'disabled');
}
);

var myArray = [];
$("#submit").on('click', function() {
	if ( $('#box').val() || $('#box').text() ) {
		// myArray variable below checks for period, comma, apostrophe, hashtag and forward-slash and removes them.
		myArray = $('#box').val().replace(/\.|,|'|&|#|/g, '').split('\n');
		if ( $('#output').length === 0) {
			$('form').append('<textarea id="output" rows="20" cols="50"></textarea>');
		}
		for( var i = 0; i<myArray.length; i++ ) {
			var newValue = myArray[i].toLowerCase().replace(/ |\//g, "-");
				if ( i === myArray.length - 1) {
					$('#output').append(newValue);
					$('#submit').attr('disabled', 'disabled');
					$('#clipboard').removeAttr('disabled');
				} else {
					$('#output').append(newValue + '\n');
				}
			}
		} else {
			alert('This is a required field.');
		}
	}
);

// copy to clipboard code

$('#clipboard').click(function(){
    var output = document.getElementById('output');
    output.focus();
    document.execCommand('SelectAll');
    document.execCommand("Copy", false, null);
    output.blur();
});

$("#reset_button").on('click', function(value) {
	$('#submit').removeAttr('disabled');
	$('#output').remove();
	$('#clipboard').attr('disabled', 'disabled');
}
);

Remove '&' sign from url slugs.


import React, { Component, PropTypes } from 'react'
import { Button } from 'react-bootstrap'
import { connect } from 'react-redux'

import { switchLocations } from '../../actions/map'

class SwitchButton extends Component {
  static propTypes = {
    onClick: PropTypes.func
  }
  _onClick = () => {
    this.props.switchLocations()
  }
  render () {
    return (
      <Button
        onClick={this._onClick || this.props.onClick}
        >Switch</Button>
    )
  }
}

const mapStateToProps = (state, ownProps) => {
  return {}
}

const mapDispatchToProps = (dispatch, ownProps) => {
  return {
    switchLocations: () => { dispatch(switchLocations()) }
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(SwitchButton)

import React, { Component, PropTypes } from 'react'
import { Button } from 'react-bootstrap'
import { connect } from 'react-redux'

import { switchLocations } from '../../actions/map'

class SwitchButton extends Component {
  static propTypes = {
    onClick: PropTypes.func,
    switchLocations: PropTypes.func
  }

  static defaultProps = {
    content: 'Switch'
  }

  _onClick = () => {
    this.props.switchLocations()
  }

  render () {
    const { content } = this.props
    return (
      <Button className='switch-button'
        onClick={this._onClick || this.props.onClick}
        >{content}</Button>
    )
  }
}

const mapStateToProps = (state, ownProps) => {
  return {}
}

const mapDispatchToProps = (dispatch, ownProps) => {
  return {
    switchLocations: () => { dispatch(switchLocations()) }
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(SwitchButton)

Allow custom content for switch button


import {
  SET_EXPENSE_VALUE,
  SET_EXPENSE_CATEGORY,
  SET_EXPENSE_ITEM,
  RESET_ADD_EXPENSE_FORM,
  NEW_EXPENSE,
  SET_EXPENSE_DATE,
} from '../constants/ActionTypes';

export const setExpenseValue = (value) => {
  return {
    type: SET_EXPENSE_VALUE,
    value: value * (-1)
  };
};

export const setExpenseCategory = (categoryId) => {
  return {
    type: SET_EXPENSE_CATEGORY,
    categoryId
  };
};

export const setExpenseItem = (itemId) => {
  return {
    type: SET_EXPENSE_ITEM,
    itemId
  };
};

export const setExpenseDate = (date) => {
  return {
    type: SET_EXPENSE_DATE,
    date
  };
};

export const resetAddExpenseForm = () => {
  return {
    type: RESET_ADD_EXPENSE_FORM,
  };
};

export const addNewExpense = (expense) => {
  return {
    type: NEW_EXPENSE,
    expense
  };
};

import {
  SET_EXPENSE_VALUE,
  SET_EXPENSE_CATEGORY,
  SET_EXPENSE_ITEM,
  RESET_ADD_EXPENSE_FORM,
  NEW_EXPENSE,
  SET_EXPENSE_DATE,
} from '../constants/ActionTypes';

export const setExpenseValue = (value) => {
  return {
    type: SET_EXPENSE_VALUE,
    value: Math.abs(value) * (-1)
  };
};

export const setExpenseCategory = (categoryId) => {
  return {
    type: SET_EXPENSE_CATEGORY,
    categoryId
  };
};

export const setExpenseItem = (itemId) => {
  return {
    type: SET_EXPENSE_ITEM,
    itemId
  };
};

export const setExpenseDate = (date) => {
  return {
    type: SET_EXPENSE_DATE,
    date
  };
};

export const resetAddExpenseForm = () => {
  return {
    type: RESET_ADD_EXPENSE_FORM,
  };
};

export const addNewExpense = (expense) => {
  return {
    type: NEW_EXPENSE,
    expense
  };
};

Fix setting the expense value in add expense form


'use strict';

define(
  [
    'flight/lib/component'
  ],

  function (defineComponent) {

    return defineComponent(spanName);

    function spanName() {
      this.updateSpans = function(ev, data) {
        var html =
          "<option value='all'>all</option>" +
          $.map(data.spans, function(span) {
            return "<option value='"+span+"'>"+span+"</option>";
          }).join("");
        this.$node.html(html);
        this.trigger('chosen:updated');
      };

      this.after('initialize', function() {
        this.$node.chosen();
        this.on(document, 'dataSpanNames', this.updateSpans);
      });
    }

  }
);

'use strict';

define(
  [
    'flight/lib/component'
  ],

  function (defineComponent) {

    return defineComponent(spanName);

    function spanName() {
      this.updateSpans = function(ev, data) {
        var html =
          "<option value='all'>all</option>" +
          $.map(data.spans, function(span) {
            return "<option value='"+span+"'>"+span+"</option>";
          }).join("");
        this.$node.html(html);
        this.trigger('chosen:updated');
      };

      this.after('initialize', function() {
        this.$node.chosen(
        {
          search_contains: true
        });
        this.on(document, 'dataSpanNames', this.updateSpans);
      });
    }

  }
);

Allow partial maches in span names


chrome.bookmarks.getTree(function ( bookmarks ) {
	var list = [];
	readFolder(bookmarks[0], list);
	console.log(list);
});

function readFolder(node, list) {
	var children = node.children;
	for (var i = 0; i < children.length; i++) {
		var newNode = children[i];
		if (isBookmark(newNode)) {
			list.push(newNode);
		} else {
			readFolder(newNode, list);
		}
	};
}

function isFolder( node ) {
	return !!(node.children);
}

function isBookmark( node ) {
	return !(node.url === undefined);
}

Read bookmarks and save as list


"use strict";

class Game {
    rollAll(framesListWithScores) {
        var framesArray = JSON.parse(framesListWithScores);
        return Array.isArray(framesArray);
    }

    roll(pins) {
        return true;
    }

    score() {
        return 0;
    }
}

module.exports = Game;

"use strict";

class Game {
    constructor() {
        this.totalPins = 0;
    }
    rollAll(framesListWithScores) {
        var framesArray = JSON.parse(framesListWithScores);
        return Array.isArray(framesArray);
    }

    roll(pins) {
        this.totalPins += pins;
        return true;
    }

    score() {
        return this.totalPins;
    }
}

module.exports = Game;

Test in which we expect the score to be that of the roll if we have only rolled once is passing


import { connect } from 'react-redux';
import Poll from 'mastodon/components/poll';

const mapStateToProps = (state, { pollId }) => ({
  poll: state.getIn(['polls', pollId]),
});

export default connect(mapStateToProps)(Poll);

import { connect } from 'react-redux';
import Poll from 'flavours/glitch/components/poll';

const mapStateToProps = (state, { pollId }) => ({
  poll: state.getIn(['polls', pollId]),
});

export default connect(mapStateToProps)(Poll);

Use glitch-soc's poll component instead of upstream's


const CONTACTS_URL = 'http://moz.fruux.net';

function reqListener() {
  var result = document.getElementById('result');
  result.textContent = this.responseText;
}

function getCredentials() {
  navigator.id.get(function (assertion) {
    var data = {
      debugaudience: 'http://localhost',
      assertion: assertion
    };

    var oReq = new XMLHttpRequest();
    oReq.onload = reqListener;
    oReq.open('POST', CONTACTS_URL + '/browserid/login', true);
    oReq.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
    oReq.send(JSON.stringify(data));
  });
}

var getcreds_button = document.getElementById('getcreds-button');
getcreds_button.onclick = getCredentials;

const CONTACTS_URL = 'http://moz.fruux.net';

function reqListener() {
  var result = document.getElementById('result');
  result.textContent = this.responseText;
}

function getCredentials() {
  navigator.id.get(function (assertion) {
    var data = {
      audience: 'http://localhost',
      assertion: assertion
    };

    var oReq = new XMLHttpRequest();
    oReq.onload = reqListener;
    oReq.open('POST', CONTACTS_URL + '/browserid/login', true);
    oReq.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
    oReq.send(JSON.stringify(data));
  });
}

var getcreds_button = document.getElementById('getcreds-button');
getcreds_button.onclick = getCredentials;

Fix the name of the audience field


import initialState from './initialState';
import {
    HEADER_SHRINKING,
    HEADER_EXPANDING,
    HEADER_ANIMATION_ENDED,
    LOGO_SPIN_STARTED,
    LOGO_SPIN_ENDED,
    SCROLL_STARTED,
    SCROLL_ENDED,
    APP_INIT
} from '../constants';

export default function (state = initialState.status, action) {
    switch (action.type) {

        case APP_INIT:
            return Object.assign({}, state, {
                isMainPage: true
            })

        case HEADER_EXPANDING:
        case HEADER_SHRINKING:
            return Object.assign({}, state, {
                isAnimating: true
            })

        case HEADER_ANIMATION_ENDED:
            return Object.assign({}, state, {
                isAnimating: false,
                isMainPage: !state.isMainPage
            })

        case SCROLL_STARTED:
            return Object.assign({}, state, {
                isScrolling: false,
                loadingMessage: action.type
            })

        case SCROLL_ENDED:
            return Object.assign({}, state, {
                isScrolling: false,
            })

        case LOGO_SPIN_STARTED:
            return Object.assign({}, state, {
                isLogoSpinning: true,
                loadingMessage: action.payload
            })

        case LOGO_SPIN_ENDED:
            return Object.assign({}, state, {
                isLogoSpinning: false,
                loadingMessage: null
            })

        default:
            return state
    }
}

import initialState from './initialState';
import {
    LOGO_SPIN_STARTED,
    LOGO_SPIN_ENDED,
    PAGE_SCROLL_STARTED,
    SCROLL_ENDED,
    APP_INIT,
    STATUS_UPDATE,
    PROVIDER_CHANGE,
    BOARD_CHANGE,

    THREAD_REQUESTED,
    BOARD_REQUESTED,
} from '../constants';

export default function (state = initialState.status, action) {
    switch (action.type) {

        case APP_INIT:
            return Object.assign({}, state, {
                isMainPage: true
            })

        case PAGE_SCROLL_STARTED:
            return Object.assign({}, state, {
                isScrolling: true
            })

        case SCROLL_ENDED:
            return Object.assign({}, state, {
                isScrolling: false,
            })

        case LOGO_SPIN_STARTED:
            return Object.assign({}, state, {
                isLogoSpinning: true
            })

        case LOGO_SPIN_ENDED:
            return Object.assign({}, state, {
                isLogoSpinning: false
            })

        case PROVIDER_CHANGE:
            return Object.assign({}, state, {
                provider: action.payload
            })

        case BOARD_REQUESTED:
            return Object.assign({}, state, {
                boardID: action.payload
            })

        case THREAD_REQUESTED:
            return Object.assign({}, state, {
                threadID: action.payload
            })

        case STATUS_UPDATE:
            return Object.assign({}, state, {
                statusMessage: action.payload
            })

        default:
            return state
    }
}

Add STATUS_UPDATE, remove header actions from status reducer


'use strict';

// this Babel plugin removes configuration comments such as:
//   <!-- template-lint triple-curlies=false -->
//   <!-- template-lint enabled=false -->

var AstNodeInfo = require('ember-template-lint/lib/helpers/ast-node-info');

module.exports = function() {
  function RemoveConfigurationHtmlCommentsPlugin() {}

  RemoveConfigurationHtmlCommentsPlugin.prototype.transform = function(ast) {
    var walker = new this.syntax.Walker();
    var bodyEntry;

    walker.visit(ast, function(node) {
      if (node.type === 'Program') {
        for (var i = 0; i < node.body.length; i++) {
          bodyEntry = node.body[i];

          if(AstNodeInfo.isConfigurationHtmlComment(bodyEntry)) {
            // remove the entry
            node.body.splice(i, 1);
          }
        }
      }
    });

    return ast;
  };

  return RemoveConfigurationHtmlCommentsPlugin;
};

'use strict';

// this Babel plugin removes configuration comments such as:
//   <!-- template-lint triple-curlies=false -->
//   <!-- template-lint enabled=false -->

module.exports = function() {
  function RemoveConfigurationHtmlCommentsPlugin() {}

  RemoveConfigurationHtmlCommentsPlugin.prototype.transform = function(ast) {
    this.syntax.traverse(ast, {
      CommentStatement: function(node) {
        if (node.value.trim().indexOf('template-lint ') === 0) {
          // remove the entry
          return null;
        }
      }
    });

    return ast;
  };

  return RemoveConfigurationHtmlCommentsPlugin;
};

Use simpler traverse API for removing config comments.


import callApi, {callApiWithFiles} from '../../../../util/apiCaller';

export function throwError(message){
	return {type:"error", error: message};
}

export function resetError(){
	return {type:"error", error:""};
}

export function submitForm(formData) {
	var data;
  	data = {student_email: formData.student_email, course_role: formData.course_role};

  	return function (dispatch) {
  		dispatch(resetError());
  		if(formData.course_num == ""){
  			return dispatch(throwError("Course not found. How did you even get here?"));
  		}
  		if(formData.student_email == ""){
  			return dispatch(throwError("Please provide a user email."));
  		}
  		if(formData.course_role == ""){
  			return dispatch(throwError("Please provide a valid course role (Defaults are 'instructor' and 'student')"));
  		}
  		if(formData.course_role != 'instructor' && formData.course_role != 'student'){
  			return dispatch(throwError("Custom roles are currently unsupported. Please type either student or instructor."));
  		}
	    dispatch(() => {return {type:"wait"};});
	    return callApi("courses/" + formData.course_num + "/enroll", 'post', data).then(data => {
	       	console.log(data);
	       	if(data.Status == "404"){
	       		return dispatch(throwError(JSON.stringify(data)));
	       	}

	    })
  	}
}

import callApi, {callApiWithFiles} from '../../../../util/apiCaller';

export function throwError(message){
	return {type:"error", error: message};
}

export function resetError(){
	return {type:"error", error:""};
}

export function submitForm(formData) {
	var data;
  	data = {student_email: formData.student_email, course_role: formData.course_role};

  	return function (dispatch) {
  		dispatch(resetError());
  		if(formData.course_num == ""){
  			return dispatch(throwError("Course not found. How did you even get here?"));
  		}
  		if(formData.student_email == ""){
  			return dispatch(throwError("Please provide a user email."));
  		}
  		if(formData.course_role == ""){
  			return dispatch(throwError("Please provide a valid course role (Defaults are 'instructor' and 'student')"));
  		}
  		if(formData.course_role != 'instructor' && formData.course_role != 'student'){
  			return dispatch(throwError("Custom roles are currently unsupported. Please type either student or instructor."));
  		}
	    dispatch(() => {return {type:"wait"};});
	    return callApi("courses/" + formData.course_num + "/enroll", 'post', data).then(data => {
	       	console.log(data);
	       	if(data.Status == "404"){
	       		return dispatch(throwError(JSON.stringify(data)));
	       	}
	       	return dispatch(throwError("User added successfully. Don't mind the scary red color, our green one is in the shop."));
	    })
  	}
}

Add user page now tells you when a user has been added successfully


var vm = new Vue({
  el: '#homeselling',
  data: {
    heading: "What's My Profit?",
    homeprice: "Home Price",
    realtorfee: "Realtor fee (in percent)",
    mortgage: "Mortgage amount",
  },
  methods: {
    currencyFormat: function(num) {
      // As seen at https://blog.tompawlak.org/number-currency-formatting-javascript.
      return "$" + num.toFixed(2).replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,")
    },
    calculateProfit: function(num) {
      var price = document.getElementById('price').value;
      var fee = document.getElementById('fee').value / 100;
      var mortgage = document.getElementById('mortgage').value;

      var takehome = (price - (price * fee));
      var profit = takehome - mortgage;
      document.getElementById('takehome').innerHTML = '
Your takehome money after the Realtor fee will be ' + vm.currencyFormat(takehome) + '.
';
      document.getElementById('result').innerHTML = '
Your profit after paying off your mortgage will be ' + vm.currencyFormat(profit) + '.
';
      return false;
    }
  }
});

var vm = new Vue({
  el: '#homeselling',
  data: {
    heading: "What's My Profit?",
    homeprice: "Home Price",
    realtorfee: "Realtor fee (in percent)",
    mortgage: "Mortgage amount",
  },
  methods: {
    // Format the number for currency
    currencyFormat: function(num) {
      // As seen at https://blog.tompawlak.org/number-currency-formatting-javascript.
      return "$" + num.toFixed(2).replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,")
    },
    // Calculate the profit from the sale
    calculateProfit: function(num) {
      var price = document.getElementById('price').value;
      var fee = document.getElementById('fee').value / 100;
      var mortgage = document.getElementById('mortgage').value;

      var takehome = (price - (price * fee));
      var profit = takehome - mortgage;
      document.getElementById('takehome').innerHTML = '
Your takehome money after the Realtor fee will be ' + vm.currencyFormat(takehome) + '.
';
      document.getElementById('result').innerHTML = '
Your profit after paying off your mortgage will be ' + vm.currencyFormat(profit) + '.
';
      return false;
    }
  }
});

Add code comments for clarity


console.log('loaded play.js');

var playPauseButton = function() {
  return document.querySelector("button[data-id='play-pause']");
};

var currentTrackInfo = function() {
  return {
    song: document.getElementById('playerSongTitle').innerHTML,
    artist: document.getElementById('player-artist').innerHTML,
    album: document.getElementsByClassName('player-album')[0].innerHTML,
    state: playPauseButton().className.indexOf('playing') === -1 ? 'paused' : 'playing'
  };
};

var commands = {
  play: function() {
    playPauseButton().click();
  },
  next: function() {
    document.querySelector("button[data-id='forward']").click();
  },
  previous: function() {
    document.querySelector("button[data-id='rewind']").click();
  }
};

chrome.runtime.onMessage.addListener(
  function(request, sender, sendResponse) {
    console.log(request.command);
    commands[request.command]();
    var response = {
      track: currentTrackInfo()
    };

    sendResponse(response);
});

console.log('loaded play.js');

var playPauseButton = function() {
  return document.querySelector("button[data-id='play-pause']");
};

var currentTrackInfo = function() {
  return {
    song: document.getElementById('playerSongTitle').innerHTML,
    artist: document.getElementById('player-artist').innerHTML,
    album: document.getElementsByClassName('player-album')[0].innerHTML,
    state: playPauseButton().className.indexOf('playing') === -1 ? 'paused' : 'playing'
  };
};

var commands = {
  play: function() {
    playPauseButton().click();
  },
  next: function() {
    document.querySelector("button[data-id='forward']").click();
  },
  previous: function() {
    document.querySelector("button[data-id='rewind']").click();
  },
  shuffle: function() {
    document.querySelector("li[data-type='artists']").click();
    document.querySelector("button[data-id='shuffle-my-library']").click();
  }
};

chrome.runtime.onMessage.addListener(
  function(request, sender, sendResponse) {
    console.log(request.command);
    commands[request.command]();
    var response = {
      track: currentTrackInfo()
    };

    sendResponse(response);
});

Add support for shuffling the library.


'use strict';

var Component = require('../../ui/Component');
var $$ = Component.$$;

function ImageComponent() {
  Component.apply(this, arguments);
}

ImageComponent.Prototype = function() {

  this.initialize = function() {
    var doc = this.props.doc;
    doc.on(this, { 'document:changed': this.handleDocumentChange });
  };

  this.dispose = function() {
    var doc = this.props.doc;
    doc.off(this);
  };

  this.render = function() {
    return $$('img')
      .addClass('sc-image')
      .attr({
        "data-id": this.props.node.id,
        contentEditable: false,
        src: this.props.node.src,
      });
  };

  this.handleDocumentChange = function(change) {
    if (change.isAffected([this.props.node.id, "src"])) {
      this.rerender();
    }
  };
};

Component.extend(ImageComponent);

module.exports = ImageComponent;

'use strict';

var Component = require('../../ui/Component');
var $$ = Component.$$;

function ImageComponent() {
  Component.apply(this, arguments);
}

ImageComponent.Prototype = function() {

  this.initialize = function() {
    var doc = this.props.doc;
    doc.on('document:changed', this.handleDocumentChange, this);
  };

  this.dispose = function() {
    var doc = this.props.doc;
    doc.off(this);
  };

  this.render = function() {
    return $$('img')
      .addClass('sc-image')
      .attr({
        "data-id": this.props.node.id,
        contentEditable: false,
        src: this.props.node.src,
      });
  };

  this.handleDocumentChange = function(change) {
    if (change.isAffected([this.props.node.id, "src"])) {
      this.rerender();
    }
  };
};

Component.extend(ImageComponent);

module.exports = ImageComponent;

Use the actual new EventEmitter API.


module.exports = function (config) {
    config.module.rules.push({
        resource: {
            test: /\.glsl$/,
            include: [/node_modules(\/|\\)vtk\.js(\/|\\)/]
        },
        use: [
            'shader-loader'
        ]
    });
    config.module.rules.push({
        resource: {
            test: /\.js$/,
            include: [/node_modules(\/|\\)vtk\.js(\/|\\)/]
        },
        use: [
            {
                loader: 'babel-loader',
                options: {
                    presets: ['es2015']
                }
            }
        ]
    });
    return config;
};

module.exports = function (config) {
    config.module.rules.push({
        resource: {
            test: /node_modules(\/|\\)vtk\.js(\/|\\).*.glsl$/,
            include: [/node_modules(\/|\\)vtk\.js(\/|\\)/]
        },
        use: [
            'shader-loader'
        ]
    });
    config.module.rules.push({
        resource: {
            test: /node_modules(\/|\\)vtk\.js(\/|\\).*.js$/,
            include: [/node_modules(\/|\\)vtk\.js(\/|\\)/]
        },
        use: [
            {
                loader: 'babel-loader',
                options: {
                    presets: ['es2015']
                }
            }
        ]
    });
    return config;
};

Make the babel-loader test more explicit in dicom_viewer


import React, { Component } from 'react';
import { connect } from 'react-redux';

export default function(ComposedComponent) {

 function mapStateToProps(state) {
    return { authenticated: state.auth.authenticated };
  }

  class Authentication extends Component {
    // static contextTypes = {
    //   router: React.PropTypes.object
    // }

    componentWillMount() {
      if (!this.props.authenticated) {
        this.props.history.push('/');
      }
    }

    componentWillUpdate(nextProps) {
      if (!nextProps.authenticated) {
        this.context.router.push('/');
      }
    }

    render() {
      return <ComposedComponent {...this.props} />
    }
  }

  return connect(mapStateToProps)(Authentication);
}

import React, { Component } from 'react';
import { connect } from 'react-redux';

export default function(ComposedComponent) {

 function mapStateToProps(state) {
    return { authenticated: state.auth.authenticated };
  }

  class Authentication extends Component {
    // static contextTypes = {
    //   router: React.PropTypes.object
    // }

    componentWillMount() {
      if (!this.props.authenticated) {
        this.props.history.push('/');
      }
    }

    componentWillUpdate(nextProps) {
      if (!nextProps.authenticated) {
        this.props.history.push('/');
      }
    }

    render() {
      return <ComposedComponent {...this.props} />
    }
  }

  return connect(mapStateToProps)(Authentication);
}

Update for React Router 4


import { Dispatcher } from 'flux';

let AppDispatcher = new Dispatcher();

AppDispatcher.handleAction = (action) => {
  this.dispatch({
    source: 'VIEW_ACTION',
    action: action
  });
};

export default AppDispatcher;

import { Dispatcher } from 'flux';

let AppDispatcher = new Dispatcher();

AppDispatcher.handleAction = function (action)  {
  this.dispatch({
    source: 'VIEW_ACTION',
    action: action
  });
};

export default AppDispatcher;

Fix broken scope in Dispatcher.


import React, { Component, PropTypes } from 'react';

class FavoriteStores extends Component {
  static defaultProps = {
    previewStore: {}
  };

  static propTypes = {
    previewStore: PropTypes.object.isRequired
  };

  renderPreviewStore = () => {
    return (

          {this.props.previewStore.Name}

          {this.props.previewStore.Address}

    );
  };

  render() {
    return (

        {this.props.previewStore.Name ? this.renderPreviewStore() : ''}

    );
  }
}

export default FavoriteStores;

import React, { Component, PropTypes } from 'react';

class FavoriteStores extends Component {
  constructor(props) {
    super(props);
    this.state = {
      favoriteStores: []
    };
  }
  static defaultProps = {
    previewStore: {}
  };

  static propTypes = {
    previewStore: PropTypes.object.isRequired
  };

  addStoreToFavorites = () => {
    this.setState((prevState, props) => ({
      favoriteStores: [...prevState.favoriteStores, props.previewStore]
    }));
  };

  renderPreviewStore = () => {
    return (

          {this.props.previewStore.Name}

          {this.props.previewStore.Address}

        <button onClick={this.addStoreToFavorites}>Add to favorites</button>

    );
  };

  renderStore(store, i) {
    return (

          {store.Name}

          {store.Address}

    );
  }

  render() {
    return (

        {this.props.previewStore.Name ? this.renderPreviewStore() : ''}
        {this.state.favoriteStores.map(this.renderStore)}

    );
  }
}

export default FavoriteStores;

Add previewStore to favorite stores list


'use strict';

angular.module('Teem')
  .directive('needWidget', function() {
    return {
      scope: {},
      controller: [
      '$scope',
      function($scope) {
        $scope.toggleCompleted = function (need) {
          event.stopPropagation();
          // Needed by the magic of material design
          event.preventDefault();

          $scope.project.toggleNeedCompleted(need);
        };
      }],
      templateUrl: 'pad/need-widget.html'
    };
  });

'use strict';

angular.module('Teem')
  .directive('needWidget', function() {
    return {
      scope: {},
      controller: [
      '$scope',
      function($scope) {
        $scope.toggleCompleted = function (need, event) {
          event.stopPropagation();
          // Needed by the magic of material design
          event.preventDefault();

          $scope.project.toggleNeedCompleted(need);
        };
      }],
      templateUrl: 'pad/need-widget.html'
    };
  });

Add missing reference to event


import Ember from 'ember';

var proxyGenerator = function (name) {
    return function (msg = '', title = '', options = {}) {
        window.toastr[name](msg.toString(), title.toString(), options);
    };
};

export default Ember.Service.extend({
    success: proxyGenerator('success'),
    info: proxyGenerator('info'),
    warning: proxyGenerator('warning'),
    error: proxyGenerator('error')
});

Use 2 spaces instead of 4


L.controlCredits = function(t) {
    return new L.CreditsControl(t)
}, L.CreditsControl = L.Control.extend({
    options: {
        position: "bottomright"
    },
    initialize: function(t) {
        if (!t.text) throw "L.CreditsControl missing required option: text";
        if (!t.image) throw "L.CreditsControl missing required option: image";
        if (!t.link) throw "L.CreditsControl missing required option: link";
        L.setOptions(this, t)
    },
    onAdd: function(t) {
        this._map = t;
        var i = L.DomUtil.create("div", "leaflet-credits-control", i);
        i.style.backgroundImage = "url(" + this.options.image + ")", this.options.width && (i.style.paddingRight = this.options.width + "px"), this.options.height && (i.style.height = this.options.height + "px");
        var o = L.DomUtil.create("a", "", i);
        L.DomUtil.addClass(o,'leaflet-credits-showlink');
        i.onclick = function(){
          window.open(o.href, "_blank");
        }
        return o.target = "_blank", o.innerHTML = this.options.text, i.link = o,
        this._container = i, this._link = o, i
    },
    setText: function(t) {
        this._link.innerHTML = t
    }
});

L.controlCredits = function(t) {
    return new L.CreditsControl(t)
}, L.CreditsControl = L.Control.extend({
    options: {
        position: "bottomright"
    },
    initialize: function(t) {
        if (!t.text) throw "L.CreditsControl missing required option: text";
        if (!t.image) throw "L.CreditsControl missing required option: image";
        if (!t.link) throw "L.CreditsControl missing required option: link";
        L.setOptions(this, t)
    },
    onAdd: function(t) {
        this._map = t;
        var i = L.DomUtil.create("div", "leaflet-credits-control", i);
        i.style.backgroundImage = "url(" + this.options.image + ")", this.options.width && (i.style.paddingRight = this.options.width + "px"), this.options.height && (i.style.height = this.options.height + "px");
        var o = L.DomUtil.create("a", "", i);
        L.DomUtil.addClass(o,'leaflet-credits-showlink');
        var lurl = this.options.link;
        i.onclick = function(){
          window.open(lurl, "_blank");
        }
        return o.target = "_blank", o.innerHTML = this.options.text,
        this._container = i, this._link = o, i
    },
    setText: function(t) {
        this._link.innerHTML = t
    }
});

Store local url in a variable and display it once for both text and image.


/**
* @license Apache-2.0
*
* Copyright (c) 2018 The Stdlib Authors.
*
* 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.
*/

'use strict';

// MODULES //

var logger = require( 'debug' );
var isnan = require( '@stdlib/assert/is-nan' ).isPrimitive;

// VARIABLES //

var debug = logger( 'plot:accessor:is-defined' );

// MAIN //

/**
* Accessor function which determines whether a datum is defined.
*
* @private
* @param {number} d - datum
* @returns {boolean} boolean indicating whether a datum is defined
*/
function isDefined( d ) {
	var bool = !isnan( d );
	debug( 'Datum: %s. Defined: %s.', JSON.stringify( d ), bool );
	return bool;
}

// EXPORTS //

module.exports = isDefined;

/**
* @license Apache-2.0
*
* Copyright (c) 2018 The Stdlib Authors.
*
* 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.
*/

'use strict';

// MODULES //

var logger = require( 'debug' );
var isnan = require( '@stdlib/assert/is-nan' ).isPrimitive;

// VARIABLES //

var debug = logger( 'plot:accessor:is-defined' );

// MAIN //

/**
* Accessor function which determines whether a datum is defined.
*
* @private
* @param {number} d - datum
* @param {integer} i - index
* @returns {boolean} boolean indicating whether a datum is defined
*/
function isDefined( d ) {
	var bool = !isnan( d );
	debug( 'Datum: %s. Defined: %s.', JSON.stringify( d ), bool );
	return bool;
}

// EXPORTS //

module.exports = isDefined;

Add param description for index argument


Meteor.publish("post_list", function(page_num){
  const posts_per_page = 10;

  //validating the page number
  var page_number = page_num;
  if(!/^[0-9]+$/gi.test(page_num)){
    return [];
  } else if(page_num <= 0) {
    return [];
  }

  return Posts.find({},{
    limit: posts_per_page,
    skip: (parseInt(page_number) - 1) * posts_per_page,
    sort: {
      createdAt: -1
    }
  })
})

Meteor.publish("get_post", function(post_id){
  //validating the id
  if(!post_id.match(/^[0-9A-Za-z]{17}(?=(#[0-9a-zA-Z_-]+)?$)/gi)){
    return [];
  }

  return Posts.find(post_id, {
    limit: 1,
  })
})

Meteor.publish("metadata", function(type){
  if(type === "posts" || type === "works"){
    return MetaData.find({
      type: type
    });
  } else {
    return [];
  }
})

Meteor.publish("post_list", function(page_num){
  const posts_per_page = MetaData.findOne({type: "posts"}).posts_per_page;

  //validating the page number
  var page_number = page_num;
  if(!/^[0-9]+$/gi.test(page_num)){
    return [];
  } else if(page_num <= 0) {
    return [];
  }

  return Posts.find({},{
    limit: posts_per_page,
    skip: (parseInt(page_number) - 1) * posts_per_page,
    sort: {
      createdAt: -1
    }
  })
})

Meteor.publish("get_post", function(post_id){
  //validating the id
  if(!post_id.match(/^[0-9A-Za-z]{17}(?=(#[0-9a-zA-Z_-]+)?$)/gi)){
    return [];
  }

  return Posts.find(post_id, {
    limit: 1,
  })
})

Meteor.publish("metadata", function(type){
  if(type === "posts" || type === "works"){
    return MetaData.find({
      type: type
    });
  } else {
    return [];
  }
})

Read posts per page from metadata collection


import { combineReducers } from 'redux';
import NavigationReducer from './NavigationReducer';
import SyncReducer from './SyncReducer';
import { PagesReducer } from './PagesReducer';
import { ModulesReducer } from './ModulesReducer';
import { UserReducer } from './UserReducer';
import { PrescriptionReducer } from './PrescriptionReducer';

export default combineReducers({
  user: UserReducer,
  nav: NavigationReducer,
  sync: SyncReducer,
  pages: PagesReducer,
  modules: ModulesReducer,
  prescription: PrescriptionReducer,
});

import { combineReducers } from 'redux';
import NavigationReducer from './NavigationReducer';
import SyncReducer from './SyncReducer';
import { PagesReducer } from './PagesReducer';
import { ModulesReducer } from './ModulesReducer';
import { UserReducer } from './UserReducer';
import { PrescriptionReducer } from './PrescriptionReducer';
import { PatientReducer } from './PatientReducer';

export default combineReducers({
  user: UserReducer,
  nav: NavigationReducer,
  sync: SyncReducer,
  pages: PagesReducer,
  modules: ModulesReducer,
  prescription: PrescriptionReducer,
  patient: PatientReducer,
});

Add the use of PatientReducer in redux store


#!/usr/bin/env node

var repl = require('repl');
var ChromeInterface = require('../');

ChromeInterface(function (chrome) {
    var chromeRepl = repl.start({
        'prompt': 'chrome> '
    });

    chromeRepl.on('exit', function () {
        chrome.close();
    });

    for (var domain in chrome) {
        chromeRepl.context[domain] = chrome[domain];
    }
});

#!/usr/bin/env node

var repl = require('repl');
var Chrome = require('../');

Chrome(function (chrome) {
    var chromeRepl = repl.start({
        'prompt': 'chrome> '
    });

    chromeRepl.on('exit', function () {
        chrome.close();
    });

    for (var domain in chrome) {
        chromeRepl.context[domain] = chrome[domain];
    }
});

Fix naming convention in REPL


'use strict';

const { gracefulExit } = require('./graceful_exit');

// Make sure the server stops when graceful exits are possible
// Also send related events
const setupGracefulExit = function ({ servers, dbAdapters, runOpts }) {
  const gracefulExitA = gracefulExit.bind(
    null,
    { servers, dbAdapters, runOpts },
  );

  process.on('SIGINT', gracefulExitA);
  process.on('SIGTERM', gracefulExitA);

  return { gracefulExit: gracefulExitA };
};

module.exports = {
  setupGracefulExit,
};

'use strict';

const { gracefulExit } = require('./graceful_exit');

// Make sure the server stops when graceful exits are possible
// Also send related events
const setupGracefulExit = function ({ servers, dbAdapters, runOpts }) {
  const gracefulExitA = gracefulExit.bind(
    null,
    { servers, dbAdapters, runOpts },
  );

  process.on('SIGINT', gracefulExitA);
  process.on('SIGTERM', gracefulExitA);
  // For Nodemon
  process.on('SIGUSR2', gracefulExitA);

  return { gracefulExit: gracefulExitA };
};

module.exports = {
  setupGracefulExit,
};

Use graceful exits with Nodemon


import sendChat from '../actions/sendChat'
import bot from '../lib/bot'

function getInitialState() {
  return [{
    message : bot.getInitial(),
    user    : 'Eliza',
    time    : new Date()
  }]
}

function add(state, message) {
  return state.concat(message)
}

function register() {
  return {
    [sendChat] : add
  }
}

export default { getInitialState, register }

import sendChat from '../actions/sendChat'
import bot from '../lib/bot'
import uid from 'uid'

function getInitialState() {
  return [{
    id      : uid(),
    message : bot.getInitial(),
    user    : 'Eliza',
    time    : new Date()
  }]
}

function add(state, message) {
  return state.concat(message)
}

function register() {
  return {
    [sendChat] : add
  }
}

export default { getInitialState, register }

Add a unique key to initial message


import React from 'react';
import { c } from 'ttag';
import { Alert, Loader } from 'react-components';

const Upgrading = () => {
    return (
        <>
            <Alert>{c('Info').t`Your account is being upgraded, this may take up to 30 seconds.`}</Alert>
            <Loader />
        </>
    );
};

export default Upgrading;

import React from 'react';
import { c } from 'ttag';
import { Alert, Loader } from 'react-components';

const Upgrading = () => {
    return (
        <>
            <Alert>{c('Info').t`Your account is being upgraded, this may take up to 30 seconds.`}</Alert>
            <Loader size="medium" />
        </>
    );
};

export default Upgrading;

Use medium loader when upgrading account


const anchorJS = require('anchor-js')
const Prism = require('./prism')

window.anchors = new anchorJS()
window.Vue = require('vue')

Vue.component('responsive-code-sample', require('./components/ResponsiveCodeSample.vue'))

const app = new Vue({
    el: '#app'
})

Prism.highlightAll()
anchors.options = { placement: 'left', class: 'text-slate-light' };
anchors.add();

document.onkeyup = function (e) {
    if (e.which == 191) {
        document.getElementById('docsearch').focus();
    }
};

const anchorJS = require('anchor-js')
const Prism = require('./prism')

window.anchors = new anchorJS()
window.Vue = require('vue')

Vue.component('responsive-code-sample', require('./components/ResponsiveCodeSample.vue'))

const app = new Vue({
    el: '#app'
})

Prism.highlightAll()
anchors.options = { placement: 'left', class: 'text-slate-light' };
anchors.add();

// Add shortcut to search input when pressing the "/" key
document.onkeyup = function (e) {
    if (e.which == 191) {
        document.getElementById('docsearch').focus();
    }
};

Add comment to search input shortcut code


const http = require('http');
const url  = require('url');

const port = Number( process.argv[2] );
if ( isNaN(port) ) {
    // bail immediately
    return;
}

http.createServer( function( request, response ) {
    if ( 'GET' != request.method ) {
        // ignore request
        return;
    }

    const urlParsed = url.parse( request.url, true );
    const iso       = urlParsed.query.iso;
    if ( undefined === iso  ) {
        response.writeHead( 400, "Bad request" );
        response.end();
        return;
    }
    const date = new Date( iso );
    if ( isNaN(date.getTime()) ) {
        response.writeHead( 400, "Bad request" );
        response.end();
        return;
    }

    var jsonTime = {};
    if ( '/api/parsetime' == urlParsed.pathname ) {
        jsonTime.hour   = date.getHours();
        jsonTime.minute = date.getMinutes();
        jsonTime.second = date.getSeconds();
    }
    else if ( '/api/unixtime'  == urlParsed.pathname ) {
        jsonTime.unixtime = date.getTime();
    }
    else {
        response.writeHead( 404, "Page not found" );
        response.end();
    }
    response.writeHead( 200, { 'Content-Type': 'application/json' } );
    response.write( JSON.stringify(jsonTime) );
    response.end();
}).listen( port );

const http = require('http');
const url  = require('url');

const port = Number( process.argv[2] );
if ( isNaN(port) ) {
    // bail immediately
    return;
}

http.createServer( function( request, response ) {
    if ( 'GET' != request.method ) {
        // ignore request
        return;
    }

    const urlParsed = url.parse( request.url, true );
    const iso       = urlParsed.query.iso;
    const date      = new Date( iso );
    if ( undefined === iso || isNaN(date.getTime()) ) {
        response.writeHead( 400, "Bad request" );
        response.end();
        return;
    }

    var jsonTime = {};
    if ( '/api/parsetime' == urlParsed.pathname ) {
        jsonTime.hour   = date.getHours();
        jsonTime.minute = date.getMinutes();
        jsonTime.second = date.getSeconds();
    }
    else if ( '/api/unixtime'  == urlParsed.pathname ) {
        jsonTime.unixtime = date.getTime();
    }
    else {
        response.writeHead( 404, "Page not found" );
        response.end();
    }
    response.writeHead( 200, { 'Content-Type': 'application/json' } );
    response.write( JSON.stringify(jsonTime) );
    response.end();
}).listen( port );

Join bad request checks into one


// Validation for bookmark payloads.
'use strict';

var Hoek = require('hoek');
var Joi = require('joi');

var validators = {};

validators.bookmarkID = Joi.string().guid();

// Bookmark without ID. Used for the update and save payloads.
validators.bookmarkWithoutID = {
  id: Joi.string().allow(null),
  url: Joi.string()
    .required()
    .max(200, 'utf-8'),
  title: Joi.string()
    .max(200, 'utf-8')
    .allow(null),
  description: Joi.string().allow(null),
  added_at: Joi.string().allow(null),
  created_at: Joi.string().allow(null),
  updated_at: Joi.string().allow(null),
};

// The full bookmark payload, requiring the ID.
validators.bookmark = Hoek.clone(validators.bookmarkWithoutID);
validators.bookmark.id = validators.bookmark.id.required();

module.exports = validators;

// Validation for bookmark payloads.
'use strict';

var Hoek = require('hoek');
var Joi = require('joi');

var validators = {};

validators.bookmarkID = Joi.string().guid();

// Bookmark without ID. Used for the update and save payloads.
validators.bookmarkWithoutID = {
  id: validators.bookmarkID.allow(null),
  url: Joi.string()
    .required()
    .max(200, 'utf-8'),
  title: Joi.string()
    .max(200, 'utf-8')
    .allow(null),
  description: Joi.string().allow(null),
  added_at: Joi.string().allow(null),
  created_at: Joi.string().allow(null),
  updated_at: Joi.string().allow(null),
};

// The full bookmark payload, requiring the ID.
validators.bookmark = Hoek.clone(validators.bookmarkWithoutID);
validators.bookmark.id = validators.bookmarkID.required();

module.exports = validators;

Validate UUIDs in bookmark objects.


(function() {
  'use strict';

  angular.module('app.config')
    .constant('API_BASE', 'http://localhost:3000')
    .constant('API_LOGIN', 'admin')
    .constant('API_PASSWORD', 'smartvm');
})();

(function() {
  'use strict';

  angular.module('app.config')
    .constant('API_BASE', 'http://localhost:4000')
    .constant('API_LOGIN', 'admin')
    .constant('API_PASSWORD', 'smartvm');
})();

Change port for the API 3000 -> 4000


define([
    'libs/bean',
    'libs/bonzo',
    'libs/qwery',
    'utils/loadJSON',
    'sc'
], function(
    bean,
    bonzo,
    qwery,
    loadJSON
) {
    var sound;

    return {
        init: function() {
            loadJSON('/soundcloud-keys.json', function(data) {
                SC.initialize({
                    client_id: data.id
                });
            });

            this.bindEvents();
        },

        bindEvents: function() {
            bean.on(document.body, 'click', '.track', function(e) {
                this.playTrack(e.currentTarget.dataset.trackId);
            }.bind(this));
        },

        playTrack: function(trackId) {
            el = qwery('#track-' + trackId);

            if (sound) {
                if (bonzo(el).hasClass('is-playing')) {
                    sound.pause();
                    bonzo(el).removeClass('is-playing');
                } else {
                    sound.play();
                    bonzo(el).addClass('is-playing');
                }
            } else {
                SC.stream('/tracks/' + trackId, function(obj){
                    obj.play();
                    sound = obj;
                    bonzo(el).addClass('is-playing');
                });
            }
        }
    }
});

define([
    'libs/bean',
    'libs/bonzo',
    'libs/qwery',
    'utils/loadJSON',
    'sc'
], function(
    bean,
    bonzo,
    qwery,
    loadJSON
) {
    var sound;

    return {
        init: function() {
            loadJSON('/soundcloud-keys.json', function(data) {
                SC.initialize({
                    client_id: data.id
                });
            });

            this.bindEvents();
        },

        bindEvents: function() {
            bean.on(document.body, 'click', '.track', function(e) {
                this.playTrack(e.currentTarget.dataset.trackId);
            }.bind(this));
        },

        playTrack: function(trackId) {
            el = bonzo(qwery('#track-' + trackId));
            current = bonzo(qwery('.is-playing'));

            if (sound) {
                // Check if it's the same track
                if (sound.url.split('/')[4] == trackId) {
                    if (el.hasClass('is-playing')) {
                        sound.pause();
                        el.removeClass('is-playing');
                    } else {
                        sound.play();
                        el.addClass('is-playing');
                    }
                // If not, destory old track and start again
                } else {
                    sound.stop();
                    current.removeClass('is-playing');
                    sound = undefined;
                    this.playTrack(trackId);
                }
            // First time playing of new track
            } else {
                SC.stream('/tracks/' + trackId, function(obj){
                    obj.play();
                    sound = obj;
                    el.addClass('is-playing');
                });
            }
        }
    }
});

Handle playback of multiple tracks