var StatsD = require('node-statsd').StatsD;
exports = module.exports = function(settings, logger) {
	var config = settings.get('statsd') || {};
	if (! config.host) {throw new Error('StatsD host not set in config')}
	var host = config.host;
	var port = config.port || 8125;
	var client = new StatsD({host: host, port: port, prefix: config.prefix, suffix: config.suffix });
	client.socket.on('error', function (error) {
		logger.error("StatsD Error " + error);
	})
	return client;
}

exports['@singleton'] = true;
exports['@require'] = ['settings', 'logger'];

var StatsD = require('node-statsd').StatsD;
exports = module.exports = function(settings, logger) {
	var config = settings.get('statsd') || {};
	if (! config.host) {throw new Error('StatsD host not set in config')}
	var host = config.host;
	var port = config.port || 8125;
	var client = new StatsD({host: host, port: port, prefix: config.prefix, suffix: config.suffix, mock: config.mock });
	client.socket.on('error', function (error) {
		logger.error("StatsD Error " + error);
	})
	return client;
}

exports['@singleton'] = true;
exports['@require'] = ['settings', 'logger'];

Add support for mock config flag.

Add support for mock config flag.


'use strict';

const validator = require('../util/validator');
const serverErrors = require('../util/server-errors');
const sendJson = require('../util/send-json');

module.exports = function({version, resource, controller}) {
  const {validations, plural_form, actions} = resource;

  const notAllowedMiddleware = [(req, res) => {
    res.status(serverErrors.notAllowed.code);
    sendJson(res, serverErrors.notAllowed.body());
  }];

  const postMiddleware = !actions.create ? notAllowedMiddleware : [
    validator(validations.create),
    controller.create
  ];
  const getManyMiddleware = !actions.readMany ? notAllowedMiddleware : [
    validator(validations.readMany),
    controller.read
  ];
  const getOneMiddleware = !actions.readOne ? notAllowedMiddleware : [
    validator(validations.readOne),
    controller.read
  ];
  const patchMiddleware = !actions.update ? notAllowedMiddleware : [
    validator(validations.update),
    controller.update
  ];
  const deleteMiddleware = !actions.delete ? notAllowedMiddleware : [
    validator(validations.delete),
    controller.delete
  ];

  return {
    // The root location of this resource
    location: `/v${version}/${plural_form}`,

    // Four routes for the four CRUD ops
    routes: {
      post: {'/': postMiddleware},
      get: {
        '/': getOneMiddleware,
        '/:id': getManyMiddleware
      },
      patch: {'/:id': patchMiddleware},
      delete: {'/:id': deleteMiddleware}
    }
  };
};

'use strict';

const validator = require('../util/validator');
const serverErrors = require('../util/server-errors');
const sendJson = require('../util/send-json');

module.exports = function({version, resource, controller}) {
  const {validations, plural_form, actions} = resource;

  const notAllowedMiddleware = [(req, res) => {
    res.status(serverErrors.notAllowed.code);
    sendJson(res, serverErrors.notAllowed.body());
  }];

  const postMiddleware = !actions.create ? notAllowedMiddleware : [
    validator(validations.create),
    controller.create
  ];
  const getManyMiddleware = !actions.readMany ? notAllowedMiddleware : [
    validator(validations.readMany),
    controller.read
  ];
  const getOneMiddleware = !actions.readOne ? notAllowedMiddleware : [
    validator(validations.readOne),
    controller.read
  ];
  const patchMiddleware = !actions.update ? notAllowedMiddleware : [
    validator(validations.update),
    controller.update
  ];
  const deleteMiddleware = !actions.delete ? notAllowedMiddleware : [
    validator(validations.delete),
    controller.delete
  ];

  return {
    // The root location of this resource
    location: `/v${version}/${plural_form}`,

    // Four routes for the four CRUD ops
    routes: {
      post: {'/': postMiddleware},
      get: {
        '/': getManyMiddleware,
        '/:id': getOneMiddleware
      },
      patch: {'/:id': patchMiddleware},
      delete: {'/:id': deleteMiddleware}
    }
  };
};

Fix read middleware to align with proper endpoint


import React, { Component } from 'react';
import './App.css';
import Nav from './components/Nav'
import Home from './components/Home'
import HouseholdsContainer from './components/HouseholdsContainer'
import Signup from './components/Signup'
import Login from './components/Login'
import { authenticate, authenticationFailure, logout } from './redux/modules/Auth/actions'
import { connect } from 'react-redux'
import {
  BrowserRouter as Router,
  Route,
  Switch
} from 'react-router-dom'
import { Container } from 'semantic-ui-react'
import NewHousehold from './components/NewHousehold'

class App extends Component {

  componentDidMount() {
    const token = localStorage.getItem('token')

    if (token) {
      this.props.authenticate()
    } else {
      this.props.authenticationFailure()
    }
  }

  render() {
    return (

        <Router>
          <div>
            <Container text>
            <Nav logout={this.props.logout} />
            </Container>
            <Container>
              <Switch>
                <Route exact path="/" component={Home} />
                <Route path="/households/new" component={NewHousehold} />
                <Route path="/households" component={HouseholdsContainer} />
                <Route path="/signup" component={Signup} />
                <Route path="/login" component={Login} />
              </Switch>
            </Container>
          </div>
        </Router>
    );
  }
}

export default connect(null, { authenticate, authenticationFailure, logout })(App);

import React, { Component } from 'react';
import './App.css';
import Nav from './components/Nav'
import Home from './components/Home'
import HouseholdsContainer from './components/HouseholdsContainer'
import Signup from './components/Signup'
import Login from './components/Login'
import { authenticate, authenticationFailure, logout } from './redux/modules/Auth/actions'
import { connect } from 'react-redux'
import {
  BrowserRouter as Router,
  Route,
  Switch
} from 'react-router-dom'
import { Container } from 'semantic-ui-react'
import NewHousehold from './components/NewHousehold'
import NewMeal from './components/NewMeal'

class App extends Component {

  componentDidMount() {
    const token = localStorage.getItem('token')

    if (token) {
      this.props.authenticate()
    } else {
      this.props.authenticationFailure()
    }
  }

  render() {
    return (

        <Router>
          <div>
            <Container text>
            <Nav logout={this.props.logout} />
            </Container>
            <Container>
              <Switch>
                <Route exact path="/" component={Home} />
                <Route path="/households/new" component={NewHousehold} />
                <Route path="/households" component={HouseholdsContainer} />
                <Route path="/meals/new" component={NewMeal} />
                <Route path="/signup" component={Signup} />
                <Route path="/login" component={Login} />
              </Switch>
            </Container>
          </div>
        </Router>
    );
  }
}

export default connect(null, { authenticate, authenticationFailure, logout })(App);

Create route for new meal form

Create route for new meal form


import React, { Component } from 'react'

export default class App extends Component {
  state = {
    listSize: 7776,
    passwordSize: 6
  }

  static bits (size) {
    return Math.floor(Math.log2(size)) + 1
  }

  numberSetter (key) {
    return (event) => this.setState({ [key]: parseInt(event.target.value, 10) })
  }

  render () {
    const size = this.state.listSize ** this.state.passwordSize
    return (

Password Entropy

Diceware

          <input value={this.state.passwordSize} onChange={this.numberSetter('passwordSize')} type="number" min="1" required/>
          words from a set of
          <input value={this.state.listSize} onChange={this.numberSetter('listSize')} type="number" min="1" required/>

Roughly {size.toPrecision(4)} passwords ({this.constructor.bits(size)} bits of entropy)

    )
  }
}

import React, { Component } from 'react'

export default class App extends Component {
  state = {
    length: 6,
    wordlistLength: 7776
  }

  static bits (size) {
    return Math.floor(Math.log2(size)) + 1
  }

  numberSetter (key) {
    return (event) => this.setState({ [key]: parseInt(event.target.value, 10) })
  }

  render () {
    const size = this.state.wordlistLength ** this.state.length
    return (
      <form>

Password Entropy

Length

          <input value={this.state.length} onChange={this.numberSetter('length')} type="number" min="1" required/>

Options

Wordlist Length

          <input value={this.state.wordlistLength} onChange={this.numberSetter('wordlistLength')} type="number" min="1" required/>

Possible Passwords

        Roughly {size.toPrecision(4)} ({this.constructor.bits(size)} bits of entropy)
      </form>
    )
  }
}

Clean up the form structure

Clean up the form structure


/*
 * Copyright (c) 2017 Bonnie Schulkin. All Rights Reserved.
 *
 * This file is part of BoxCharter.
 *
 * BoxCharter is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option)
 * any later version.
 *
 * BoxCharter is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with BoxCharter. If not, see <http://www.gnu.org/licenses/>.
 *
 */

/**
 * Written with help from Stephen Grider's Advanced React and Redux Udemy Course
 * @module
 * reducer_auth
 */

import { AUTH_USER, UNAUTH_USER, AUTH_ERROR } from '../actions/types'

export default (state = {}, action) => {
  switch(action.type) {
    case AUTH_USER:
      return { ...state, authenticated: true, userId: action.payload.userId }
    case UNAUTH_USER:
      return { ...state, authenticated: false }
    case AUTH_ERROR:
      return { ...state, error: action.payload }
    default:
      return { ...state }
  }
}

/*
 * Copyright (c) 2017 Bonnie Schulkin. All Rights Reserved.
 *
 * This file is part of BoxCharter.
 *
 * BoxCharter is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option)
 * any later version.
 *
 * BoxCharter is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with BoxCharter. If not, see <http://www.gnu.org/licenses/>.
 *
 */

/**
 * Written with help from Stephen Grider's Advanced React and Redux Udemy Course
 * @module
 * reducer_auth
 */

import { AUTH_USER, UNAUTH_USER, AUTH_ERROR } from '../actions/types'

export default (state = {}, action) => {
  switch(action.type) {
    case AUTH_USER:
      return { ...state, authenticated: true, userId: action.payload.userId, error: null }
    case UNAUTH_USER:
      return { ...state, authenticated: false }
    case AUTH_ERROR:
      return { ...state, error: action.payload }
    default:
      return { ...state }
  }
}

Clear error on successful login

Clear error on successful login


import fetch from 'isomorphic-fetch';
import { checkStatus, parseJSON } from './utils';
require('es6-promise').polyfill();

export const fetchTickets = projectId => {
  const headers = {
    Authorization: `Basic ${process.env.REACT_APP_API_KEY}`,
  };

  return fetch(`${process.env.REACT_APP_API_URL}/v1/tickets/${projectId}`, { headers }).then(checkStatus).then(parseJSON);
}

export const fetchTicketNotes = ticketId => {
  const headers = {
    Authorization: `Basic ${process.env.REACT_APP_API_KEY}`,
  };
  return fetch(`${process.env.REACT_APP_API_URL}/v1/ticket/${ticketId}/notes`, { headers }).then(checkStatus).then(parseJSON);
}

export const dispatchTickets = params => {
  const headers = {
    Authorization: `Basic ${process.env.REACT_APP_API_KEY}`,
    'Content-Type': 'application/json'
  };

  return fetch(`${process.env.REACT_APP_API_URL}/v1/dispatch`, {
    headers,
    method: 'POST',
    body: JSON.stringify(params),
  }).then(checkStatus).then(parseJSON);
}

import fetch from 'isomorphic-fetch';
import { checkStatus, parseJSON } from './utils';
require('es6-promise').polyfill();

export const fetchTickets = projectId => {
  const headers = {
    Authorization: `Basic ${process.env.REACT_APP_API_KEY}`,
  };

  return fetch(`${process.env.REACT_APP_API_URL}/v1/tickets/${projectId}`, { headers }).then(checkStatus).then(parseJSON);
}

export const fetchTicketNotes = ticketId => {
  const headers = {
    Authorization: `Basic ${process.env.REACT_APP_API_KEY}`,
  };
  return fetch(`${process.env.REACT_APP_API_URL}/v1/ticket/${ticketId}/notes`, { headers }).then(checkStatus).then(parseJSON);
}

export const fetchTicketScheduleEntryIds = ticketId => {
  const headers = {
    Authorization: `Basic ${process.env.REACT_APP_API_KEY}`,
  };
  return fetch(`${process.env.REACT_APP_API_URL}/v1/ticket/${ticketId}/scheduleentries`, { headers }).then(checkStatus).then(parseJSON);
}

export const fetchScheduleEntryById = entryId => {
  const headers = {
    Authorization: `Basic ${process.env.REACT_APP_API_KEY}`,
  };
  return fetch(`${process.env.REACT_APP_API_URL}/v1/schedule/entries/${entryId}`, { headers }).then(checkStatus).then(parseJSON);
}

export const dispatchTickets = params => {
  const headers = {
    Authorization: `Basic ${process.env.REACT_APP_API_KEY}`,
    'Content-Type': 'application/json'
  };

  return fetch(`${process.env.REACT_APP_API_URL}/v1/dispatch`, {
    headers,
    method: 'POST',
    body: JSON.stringify(params),
  }).then(checkStatus).then(parseJSON);
}

Add schedule entry http requests

Add schedule entry http requests


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

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

Include skip-link-focus-fix.js in bundled js.


/**
*
* FunctionsList
*
*/

import React, { PropTypes } from 'react';
import LoadingIndicator from 'components/LoadingIndicator';
import FunctionListItem from 'containers/FunctionListItem';
// import styled from 'styled-components';
import { FormattedMessage } from 'react-intl';
import commonMessages from 'messages';

function FunctionsList({ loading, error, items, onRemove }) {
  if (loading) {
    return <LoadingIndicator />;
  }
  if (error !== false) {
    return
{ error }
;
  }
  return (

          <FormattedMessage {...commonMessages.name} />
          <FormattedMessage {...commonMessages.environment} />
          <FormattedMessage {...commonMessages.trigger} />
          <FormattedMessage {...commonMessages.action} />

        {
          items.map((item, index) => (
            <FunctionListItem item={item} key={`function-${index}`} onRemove={() => { onRemove(item); }} />
          ))
        }

  );
}

FunctionsList.propTypes = {
  loading: PropTypes.bool,
  error: PropTypes.any,
  items: PropTypes.oneOfType([
    PropTypes.object,
    PropTypes.array,
  ]),
  onRemove: PropTypes.func,
};

export default FunctionsList;

/**
*
* FunctionsList
*
*/

import React, { PropTypes } from 'react';
import LoadingIndicator from 'components/LoadingIndicator';
import ErrorIndicator from 'components/ErrorIndicator';
import FunctionListItem from 'containers/FunctionListItem';
// import styled from 'styled-components';
import { FormattedMessage } from 'react-intl';
import commonMessages from 'messages';

function FunctionsList({ loading, error, items, onRemove }) {
  if (loading) {
    return <LoadingIndicator />;
  }
  if (error !== false) {
    return <ErrorIndicator error={error} />;
  }
  return (

          <FormattedMessage {...commonMessages.name} />
          <FormattedMessage {...commonMessages.environment} />
          <FormattedMessage {...commonMessages.trigger} />
          <FormattedMessage {...commonMessages.action} />

        {
          items.map((item, index) => (
            <FunctionListItem item={item} key={`function-${index}`} onRemove={() => { onRemove(item); }} />
          ))
        }

  );
}

FunctionsList.propTypes = {
  loading: PropTypes.bool,
  error: PropTypes.any,
  items: PropTypes.oneOfType([
    PropTypes.object,
    PropTypes.array,
  ]),
  onRemove: PropTypes.func,
};

export default FunctionsList;

Fix react multi child error of FunctionsList

If an error occured in func edit page and then switch back to func list,
will cause an react multi child error


var combineTwoStrings = function (str1, str2, str3) {
  var pos1 = 0,
      pos2 = 0;

  if (str1 + str2 === str3) { return true; }

  for (var i = 0; i < str3.length; i++) {
    if (str3[i] === str1[pos1]) {
      pos1++;
    }
    else if (str3[i] === str2[pos2]) {
      pos2++;
    }
    else if (str3.length - i - 1 < str1.length - pos1 + str2.length - pos2) {
      return false;
    }
  }
  return true;
};

var combineTwoStrings = function (str1, str2, combined) {
  // Generate all the posible paths between `str1` and `str2`
  var paths = {};

  // Check the string lengths are the same to begin
  if ((str1 + str2).length !== combined.length) {
    return false;
  }

  // Finding paths is essentially the anagrams solution
  (function findPath (str1, str2, path) {
    if (path.length === combined.length) { return paths[path] = true; }

    // Find the next path from the first character of either strings
    str1 && findPath(str1.substr(1), str2, path + str1.substr(0, 1));
    str2 && findPath(str1, str2.substr(1), path + str2.substr(0, 1));
  })(str1, str2, '');

  return combined in paths;
};

Reimplement combine strings, much easier to precompute paths.


jQuery(function(){

	jQuery('button.link').click(function(){
		window.location.href = $(this).find('span.href').text();
	});

	jQuery('form.destroy_link_to').submit(function(){
		if( !confirm('Seriously?') ){
			return false;
		}
	});

	jQuery('p.flash').click(function(){$(this).remove();});

});

jQuery(function(){

	jQuery('button.link').click(function(){
		window.location.href = $(this).find('span.href').text();
	});

	jQuery('form.destroy_link_to').submit(function(){
		var message = "Destroy?  Seriously?"
		if( this.confirm && this.confirm.value ) {
			message = this.confirm.value
		}
		if( !confirm(message) ){
			return false;
		}
	});

	jQuery('p.flash').click(function(){$(this).remove();});

});

Check form for confirm field and use it if it exists.


'use strict';

/**
 * @ngdoc function
 * @name hmdaPilotApp.controller:selectFile
 * @description
 * # Select File
 * Controller for selecting a HMDA file and Reporting Year for verification.
 */
module.exports = function ($scope, RuleEngine) {
    var fiscalYears = RuleEngine.getFiscalYears();

    // Populate the $scope
    $scope.reportingYears = fiscalYears;

    // Set default values for any form fields
    $scope.hmdaData = {
        year: fiscalYears[1],
        file: ''
    };

    // Process the form submission
    $scope.submit = function(hmdaData) {
        // Nothing to hook up to yet...
    };
};

'use strict';

/**
 * @ngdoc function
 * @name hmdaPilotApp.controller:selectFile
 * @description
 * # Select File
 * Controller for selecting a HMDA file and Reporting Year for verification.
 */
module.exports = function ($scope, RuleEngine) {
    var fiscalYears = RuleEngine.getFiscalYears();

    // Populate the $scope
    $scope.reportingYears = fiscalYears;

    // Set default values for any form fields
    $scope.hmdaData = {
        year: fiscalYears[1],
        file: ''
    };

    // Process the form submission
    $scope.submit = function(hmdaData) {
        console.log('HMDA Data: ', hmdaData);
        // Nothing to hook up to yet...
    };
};

Add a little console.log to use the hmdaData or else jshint complains about the param never being used


$(function() {

  var form = $('form');
  var inputs = form.find(':input');
  var required = inputs.filter('[required]');
  var submit = inputs.filter('[type=submit]');
  var confirmation = form.find('.confirmation');

  // On touch devices, the links with drop-downs aren't normally hoverable, so
  // we need to disable their action to allow them to expand the drop-down only.
  if ('ontouchstart' in document.documentElement) {
    $('.menu-item:has(.menu-dropdown) > a').click(function() {
      return false;
    });
  }

  // Primitive form validation; inform immediately on missing a required field
  required.blur(function() {
    var input = $(this);
    input.toggleClass('invalid', !input.val());
  });

  // Keep the submit button disabled until everything is valid
  required.keyup(function() {
    var valid = true;
    required.each(function() {
      return valid &= !!$(this).val();
    });
    submit.prop('disabled', !valid);
  });

  // Submit asynchronously and show a confirmation message
  form.submit(function(event) {
    event.preventDefault();

    confirmation.hide();
    inputs.prop('disabled', true);

    var url = form.attr('action');
    $.post(url, form.serialize()).done(function() {
      inputs.prop('disabled', false);
      confirmation.show();
    });
    return false;
  });
});

$(function() {

  var form = $('form');
  var inputs = form.find(':input');
  var required = inputs.filter('[required]');
  var submit = inputs.filter('[type=submit]');
  var confirmation = form.find('.confirmation');

  // On touch devices, the links with drop-downs aren't normally hoverable, so
  // we need to disable their action to allow them to expand the drop-down only.
  if ('ontouchstart' in document.documentElement) {
    $('.menu-item:has(.menu-dropdown) > a').click(function() {
      return false;
    });
  }

  // Primitive form validation; inform immediately on missing a required field
  required.blur(function() {
    var input = $(this);
    input.toggleClass('invalid', !input.val());
  });

  // Keep the submit button disabled until everything is valid
  required.keyup(function() {
    var valid = true;
    required.each(function() {
      return valid &= !!$(this).val();
    });
    submit.prop('disabled', !valid);
  });

  // Submit asynchronously and show a confirmation message
  form.submit(function(event) {
    var url = form.attr('action');
    var data = form.serialize();

    confirmation.hide();
    inputs.prop('disabled', true);

    $.post(url, data).always(function() {
      window.console.log('You can ignore the error above.');
      inputs.prop('disabled', false);
      confirmation.show();
    });

    event.preventDefault();
    return false;
  });
});

Fix form submission to handle CORS errors.


const fs = require('fs')
const path = require('path')

const appDirectory = fs.realpathSync(process.cwd())

function resolveApp(relativePath) {
  return path.resolve(appDirectory, relativePath)
}

function resolveSelf(relativePath) {
  return path.resolve(__dirname, '../', relativePath)
}

module.exports = {
  root: appDirectory,
  dist: resolveApp('dist'),
  static: resolveApp('static'),
  src: resolveApp('src'),
  lib: resolveSelf('lib'),
  entrypoints: {
    scripts: resolveApp('src/assets/js/index.js'),
    static: resolveSelf('lib/static-files-glob.js')
  },
  assetsOutput: resolveApp('dist/assets'),
  userShopifyConfig: resolveApp('shopify.yml'),
  eslintrc: resolveSelf('./.eslintrc')
}

const fs = require('fs')
const path = require('path')

const appDirectory = fs.realpathSync(process.cwd())

function resolveApp(relativePath) {
  return path.resolve(appDirectory, relativePath)
}

function resolveSelf(relativePath) {
  return path.resolve(__dirname, '../', relativePath)
}

function getEslintrc() {
  const appEslintrc = resolveApp('./.eslintrc')

  if (fs.existsSync(appEslintrc)) {
    return appEslintrc
  }

  return resolveSelf('./.eslintrc')
}

module.exports = {
  root: appDirectory,
  dist: resolveApp('dist'),
  static: resolveApp('static'),
  src: resolveApp('src'),
  lib: resolveSelf('lib'),
  entrypoints: {
    scripts: resolveApp('src/assets/js/index.js'),
    static: resolveSelf('lib/static-files-glob.js')
  },
  assetsOutput: resolveApp('dist/assets'),
  userShopifyConfig: resolveApp('shopify.yml'),
  eslintrc: getEslintrc()
}

Return app eslintrc path if the file exists


$(document).on("ready", function() {

  // $("a").click(".snowflake", function(event){
  //   event.preventDefault();
  //   event.stopPropagation();
  //   debugger
  //   // var target = $(event.target).parent().find("#vote_count")
  //   $.ajax({
  //     type: "POST",
  //     url : $(this).attr('href'),
  //     dataType: "json",
  //   }).done(function(response){
  //     debugger
  //     alert("Success!")
  //   }).fail(function(response){
  //     console.log(response);
  //   });
  // });

  $("#post").on("click", function(event){
    event.preventDefault();
    $.ajax({
      url : $(event.target).attr('href'),
    }).done(function(response){
      $(".comment-form").show();
    }).fail(function(response){
      alert("You are unable to post a comment.");
    });
  });

  $('.comment-form').on("submit", 'form', function(event){
    event.preventDefault();
    var id = $('#comment_trail_id').val()
    $.ajax({
      type: $(event.target).attr('method'),
      url : '/trails/' + id + '/comments',
      data: $(this).serialize()
    }).done(function(response){
      debugger;
      $(".river").append(response);
      $(".comment-form").remove();
      $(".comment-form").hide();
    }).fail(function(){
      alert("Your post has not been posted")
    });
  });

});

$(document).on("ready", function() {

  $("#post").on("click", function(event){
    event.preventDefault();
    $.ajax({
      url : $(event.target).attr('href'),
    }).done(function(response){
      $(".comment-form").show();
    }).fail(function(response){
      alert("You are unable to post a comment.");
    });
  });

  // $("a").click(".snowflake", function(event){
  //   event.preventDefault();
  //   event.stopPropagation();
  //   debugger
  //   // var target = $(event.target).parent().find("#vote_count")
  //   $.ajax({
  //     type: "POST",
  //     url : $(this).attr('href'),
  //     dataType: "json",
  //   }).done(function(response){
  //     debugger
  //     alert("Success!")
  //   }).fail(function(response){
  //     console.log(response);
  //   });
  // });

  // $('.comment-form').on("submit", 'form', function(event){
  //   event.preventDefault();
  //   var id = $('#comment_trail_id').val()
  //   // debugger;
  //   $.ajax({
  //     type: $(event.target).attr('method'),
  //     url : '/trails/' + id + '/comments',
  //     data: $(this).serialize()
  //   }).done(function(response){
  //     debugger;
  //     $(".river").toggle().toggle();
  //     $(".comment-form").remove();
  //     $(".comment-form").hide();
  //   }).fail(function(){
  //     alert("Your post has not been posted")
  //   });
  // });

});

Comment out AJAX data calls

Comment out AJAX data calls


function Scoreboard(options) {
  this.containers = {
    best: options.best,
    score: options.score
  };
  this.storage_key = 'personal_best'

  try {
    this.best = localStorage.getItem(this.storage_key) || 0;
  } catch (e) {
    this.best = 0;
  }

  this.score = 0;
  this.render();
};

Scoreboard.prototype.add = function(points) {
  this.score += points;
  if (this.best < this.score) {
    this.best = this.score;

    try {
      localStorage.setItem(this.storage_key, this.best);
    } catch (e) {
      // let's fail silently
    }
  }
  this.render();
};

Scoreboard.prototype.render = function() {
  this.containers.score.innerHTML = this.score;
  this.containers.best.innerHTML = this.best;
}

function Scoreboard(options) {
  this.containers = {
    best: options.best,
    score: options.score
  };
  this.storage_key = 'memchimp.personal_best'

  try {
    this.best = localStorage.getItem(this.storage_key) || 0;
  } catch (e) {
    this.best = 0;
  }

  this.score = 0;
  this.render();
};

Scoreboard.prototype.add = function(points) {
  this.score += points;
  if (this.best < this.score) {
    this.best = this.score;

    try {
      localStorage.setItem(this.storage_key, this.best);
    } catch (e) {
      // let's fail silently
    }
  }
  this.render();
};

Scoreboard.prototype.render = function() {
  this.containers.score.innerHTML = this.score;
  this.containers.best.innerHTML = this.best;
}

Change localStorage key for personal best


import Inferno from 'inferno'
import Component from 'inferno-component'
import styled from 'styled-components'
import { bind, debounce } from 'decko'

import { RaisedButton } from '@slup/buttons'
import { Slider } from '@slup/slider'
import { Logo } from '../components/logo'

const LogoContainer = styled.div`
  display: flex;
  flex: 1;
  height: 100%;
  align-items: center;
  justify-content: center;
  flex-direction: column;
`

export class Home extends Component {
  state = { value: 0, focus: false }

  @bind
  onChange(value) {
    this.setState({ value })
  }

  @bind
  onFocus() {
    this.setState({ focus: true })
  }

  @bind
  onBlur() {
    console.log('called')
    this.setState({ focus: false })
  }

  @bind
  @debounce(300)
  handleClick() {
    this.context.router.push('/components/buttons')
  }

  render() {
    return (
      <LogoContainer>
        {Logo}
        <RaisedButton onClick={this.handleClick} primary>Get started</RaisedButton>
        <Slider
          value={this.state.value}
          focus={this.state.focus}
          keyFocus={this.state.focus}
          max={100}
          onChange={this.onChange}
          onFocus={this.onFocus}
          onBlur={this.onBlur}
          discrete
          steps={10} />
      </LogoContainer>
    )
  }
}

import Inferno from 'inferno'
import Component from 'inferno-component'
import styled from 'styled-components'
import { bind, debounce } from 'decko'

import { RaisedButton } from '@slup/buttons'
import { Slider } from '@slup/slider'
import { Logo } from '../components/logo'

const LogoContainer = styled.div`
  display: flex;
  flex: 1;
  height: 100%;
  align-items: center;
  justify-content: center;
  flex-direction: column;
`

export class Home extends Component {
  @bind
  @debounce(300)
  handleClick() {
    this.context.router.push('/components/buttons')
  }

  render() {
    return (
      <LogoContainer>
        {Logo}
        <RaisedButton onClick={this.handleClick} primary>Get started</RaisedButton>
      </LogoContainer>
    )
  }
}

Revert "Revert ":fire: Removed useless sample code""

This reverts commit 81325bc686351ec5df5e38bf3ad04dfc71f16712.


/*!
 * debug
 * Copyright(c) 2012 TJ Holowaychuk <tj@vision-media.ca>
 * MIT Licensed
 */

/**
 * Previous debug() call.
 */

var prev = {};

/**
 * Create a debugger with the given `name`.
 *
 * @param {String} name
 * @return {Type}
 * @api public
 */

function debug(name) {
  if (!debug.enable) {
    throw new Error('You must set a function for `debug.enable`');
  }

  var enabled = debug.enable(name);

  if (!enabled) return function(){};

  function plain(fmt) {
    var curr = new Date;
    var ms = curr - (prev[name] || curr);
    prev[name] = curr;

    fmt = name
      + ' +' + ms + 'ms '
      + fmt;

    console.log.apply(console, arguments);
  }

  return plain;
}

/*!
 * debug
 * Copyright(c) 2012 TJ Holowaychuk <tj@vision-media.ca>
 * MIT Licensed
 */

/**
 * Previous debug() call.
 */

var prev = {};

/**
 * Create a debugger with the given `name`.
 *
 * @param {String} name
 * @return {Type}
 * @api public
 */

function debug(name) {
  if (!debug.enable) {
    throw new Error('You must set a function for `debug.enable`');
  }

  var enabled = debug.enable(name);

  if (!enabled) return function(){};

  function plain(fmt) {
    var curr = new Date;
    var ms = curr - (prev[name] || curr);
    prev[name] = curr;

    fmt = name
      + ' +' + ms + 'ms '
      + fmt;

    // This hackery is required for IE8, where `console.log` doesn't have 'apply'
    window.console && console.log &&
      Function.prototype.apply.call(console.log, console, arguments);
  }

  return plain;
}

Make it work in IE8.

Make it work in IE8.


import { quat_create, quat_set, quat_normalize, quat_multiply } from './quat';

var quat = quat_create();

export function controls_create(object) {
  var controls = {
    object: object,
    speed: 1,
    turnRate: Math.PI / 4,
    sensitivity: 0.002,
    enabled: false,

    onMouseMove: function(event) {
      if (!controls.enabled) {
        return;
      }

      var movementX = event.movementX || event.mozMovementX || 0;
      var movementY = event.movementY || event.mozMovementY || 0;

      var pitch = -movementY * controls.sensitivity;
      var yaw = -movementX * controls.sensitivity;

      quat_normalize(quat_set(quat, pitch, yaw, 0, 1));
      quat_multiply(object.quaternion, quat);
    },
  };

  document.addEventListener('mousemove', controls.onMouseMove);

  return controls;
}

export function controls_dispose(controls) {
  document.removeEventListener('mousemove', controls.onMouseMove);
}

import { quat_create, quat_copy, quat_set, quat_normalize, quat_multiply } from './quat';

var pitchQuat = quat_create();
var yawQuat = quat_create();

export function controls_create(object) {
  var controls = {
    object: object,
    speed: 1,
    turnRate: Math.PI / 4,
    sensitivity: 0.002,
    enabled: false,

    onMouseMove: function(event) {
      if (!controls.enabled) {
        return;
      }

      var movementX = event.movementX || event.mozMovementX || 0;
      var movementY = event.movementY || event.mozMovementY || 0;

      var pitch = -movementY * controls.sensitivity;
      var yaw = -movementX * controls.sensitivity;

      quat_normalize(quat_set(pitchQuat, pitch, 0, 0, 1));
      quat_normalize(quat_set(yawQuat, 0, yaw, 0, 1));

      // pitch * object * yaw
      quat_multiply(object.quaternion, pitchQuat);
      quat_multiply(yawQuat, object.quaternion);
      quat_copy(object.quaternion, yawQuat);
    },
  };

  document.addEventListener('mousemove', controls.onMouseMove);

  return controls;
}

export function controls_dispose(controls) {
  document.removeEventListener('mousemove', controls.onMouseMove);
}

Fix decomposition of camera quaternions

Fix decomposition of camera quaternions


"use strict";
function createDialog(pageMantle, dialogHtml, dialogCss) {

	var dialog = {};

	// write dialog HTML to document
	var dialogDiv = jQuery('

');
	dialogDiv.appendTo(document.body);

	// create iframe
	var iframe = jQuery('');
	iframe.appendTo(dialogDiv);
	iframe.load(function() {

		var dialogDocument = iframe[0].contentWindow.document;
		dialogDocument.write(dialogHtml); // document.write() has the advantage to actually use the doctype from the given string.
		dialogDocument.close();

		// write dialog content CSS to dialog
		var dialogCssElement = jQuery('');
		dialogCssElement.appendTo(dialogDocument.head);

		var dialogContent = createDialogContent(dialogDocument, pageMantle);

		var keepPositionFixed = function(event) {
			$(event.target).parent().css('position', 'fixed');
		};
		$(dialogDiv).dialog({
			dialogClass : 'ftc_dialogDiv',
			/*TODO fix the vertical position. This seems especially difficult for pages without html5 doctype.
			 position : ['center', 'bottom'], //this works with the html5 doctype, but not in quirks mode
			 */
			width : 750,
			height : 270,
			create : keepPositionFixed,
			resize : keepPositionFixed,
			close : pageMantle.destroy
		});

	});

	return dialog;
}

"use strict";
function createDialog(pageMantle, dialogHtml, dialogCss) {

	var dialog = {};

	// write dialog HTML to document
	var dialogDiv = jQuery('

');
	dialogDiv.appendTo(document.body);

	// create iframe
	var iframe = jQuery('');
	iframe.appendTo(dialogDiv);
	iframe.load(function() {

		var dialogDocument = iframe[0].contentWindow.document;
		// document.write() has the advantage to actually use the doctype from the given string.
		dialogDocument.write(dialogHtml);
		dialogDocument.close();

		// write dialog content CSS to dialog
		var dialogCssElement = jQuery('');
		dialogCssElement.appendTo(dialogDocument.head);

		var dialogContent = createDialogContent(dialogDocument, pageMantle);

		var keepPositionFixed = function(event) {
			$(event.target).parent().css('position', 'fixed');
		};
		$(dialogDiv).dialog({
			dialogClass : 'ftc_dialogDiv',
			position : ['center', 'bottom'], // FIXME in quirks mode the vertical position is unpredictable and sometimes even invisible
			width : 750,
			height : 270,
			create : keepPositionFixed,
			resize : keepPositionFixed,
			close : pageMantle.destroy
		});

	});

	return dialog;
}

Change the vertical position to work correctly in standards mode.


// @flow
import React from 'react'

const Box = (props: any) => (
  <div {...props} />
)
export default Box

// @flow
import React, {Component} from 'react'

export default class Box extends Component {
  render () {
    return (
      <div {...this.props} />
    )
  }
}

Revert Box to a stateful component to fix refs usage


module.exports = {
    activeUserCount: require('./activeUserCount.js'),
    activeUsers: require('./activeUsers.js'),
    slackPostMessage: require('./slackPostMessage.js'),
    createDir: require('./createDir.js'),
    createDirQVD: require('./createDirQVD.js'),
    getDiskSpace: require('./getDiskSpace.js'),
    mqttPublishMessage: require('./mqttPublishMessage.js'),
    senseStartTask: require('./senseStartTask.js'),
    senseQRSPing: require('./senseQRSPing.js'),
    senseAppDump: require('./senseAppDump.js'),
    senseListApps: require('./senseListApps.js'),
    butlerPing: require('./butlerPing.js'),
    base62ToBase16: require('./baseConversion.js'),
    base16ToBase62: require('./baseConversion.js')
};

module.exports = {
    activeUserCount: require('./activeUserCount.js'),
    activeUsers: require('./activeUsers.js'),
    slackPostMessage: require('./slackPostMessage.js'),
    createDir: require('./createDir.js'),
    createDirQVD: require('./createDirQVD.js'),
    mqttPublishMessage: require('./mqttPublishMessage.js'),
    senseStartTask: require('./senseStartTask.js'),
    senseQRSPing: require('./senseQRSPing.js'),
    senseAppDump: require('./senseAppDump.js'),
    senseListApps: require('./senseListApps.js'),
    butlerPing: require('./butlerPing.js'),
    base62ToBase16: require('./baseConversion.js'),
    base16ToBase62: require('./baseConversion.js')
};
//getDiskSpace: require('./getDiskSpace.js'),

Remove disk space REST endpoint

Remove disk space REST endpoint


$(document).ready( function () {
  $(".upvote").click( function() {
    var commentId = $(".upvote").data("id");
    event.preventDefault();
    $.ajax({
      url: '/response/up_vote',
      method: 'POST',
      data: { id: commentId },
      dataType: 'JSON'
    }).done( function (voteCount) {

    }).fail( function (voteCount) {
        console.log("Failed. Here is the voteCount:");
        console.log(voteCount);
    })
  })
})

$(document).ready( function () {
  $(".upvote").click( function() {
    var commentId = $(".upvote").data("id");
    event.preventDefault();
    $.ajax({
      url: '/response/up_vote',
      method: 'POST',
      data: { id: commentId },
      dataType: 'JSON'
    }).done( function (voteCount) {
      if (voteCount == 1) {
        $("span[data-id=" + commentId + "]").html(voteCount + " vote");
      } else {
        $("span[data-id=" + commentId + "]").html(voteCount + " vote");
      }

    }).fail( function (voteCount) {
        console.log("Failed. Here is the voteCount:");
        console.log(voteCount);
    })
  })
})

Add inflection logic upon successful ajax upvote call.


angular.module('siyfion.sfTypeahead', [])
  .directive('sfTypeahead', function () {
    return {
      restrict: 'ACE',
      scope: {
        datasets: '=',
        ngModel: '='
      },
      link: function (scope, element) {
        element.typeahead(scope.datasets);

        // Updates the ngModel binding when a value is manually selected from the dropdown.
        // ToDo: Think about how the value could be updated on user entry...
        element.bind('typeahead:selected', function (object, datum) {
          scope.$apply(function() {
            scope.ngModel = datum;
          });
        });

        // Updates the ngModel binding when a query is autocompleted.
        element.bind('typeahead:autocompleted', function (object, datum) {
          scope.$apply(function() {
            scope.ngModel = datum;
          });
        });

        // Updates typeahead when ngModel changed.
        scope.$watch('ngModel', function (newVal) {
            var valueKey = scope.datasets.valueKey;
            if (newVal && valueKey && newVal.hasOwnProperty(valueKey)) {
                newVal = newVal[valueKey];
            }
            element.typeahead('setQuery', newVal || '');
        });
      }
    };
  });

angular.module('siyfion.sfTypeahead', [])
  .directive('sfTypeahead', function () {
    return {
      restrict: 'ACE',
      scope: {
        datasets: '=',
        ngModel: '='
      },
      link: function (scope, element) {
        element.typeahead(scope.datasets);

        // Updates the ngModel binding when a value is manually selected from the dropdown.
        // ToDo: Think about how the value could be updated on user entry...
        element.bind('typeahead:selected', function (object, datum, dataset) {
          scope.$apply(function() {
            scope.ngModel = datum;
            scope.selectedDataset = dataset
          });
        });

        // Updates the ngModel binding when a query is autocompleted.
        element.bind('typeahead:autocompleted', function (object, datum) {
          scope.$apply(function() {
            scope.ngModel = datum;
          });
        });

        // Updates typeahead when ngModel changed.
        scope.$watch('ngModel', function (newVal) {
            if ($.isArray(scope.datasets)) {
                for (var i=0;i<scope.datasets.length;i++) {
                    if (scope.datasets[i].name ==  scope.selectedDataset) {
                        var valueKey = scope.datasets[i].valueKey;
                        break;
                    }
                }

            } else {
                var valueKey = scope.datasets.valueKey;
            }

            if (newVal && valueKey && newVal.hasOwnProperty(valueKey)) {
                newVal = newVal[valueKey];
            }
            element.typeahead('setQuery', newVal || '');
        });
      }
    };
  });

Add support for multiple datasets

Add support for multiple datasets


'use strict'

module.exports = {
  presets: [
    require('babel-preset-es2015'),
    require('babel-preset-stage-2')
  ],
  plugins: [
    require('babel-plugin-transform-decorators-legacy'),
    [require('babel-plugin-transform-react-jsx'), {
      pragma: 'createElement'
    }]
  ],
  env: {
    production: {
      plugins: [
        require('babel-plugin-transform-react-constant-elements'),
        require('babel-plugin-transform-react-inline-elements')
      ]
    }
  }
}

'use strict'

module.exports = {
  presets: [
    require('babel-preset-es2015'),
    require('babel-preset-stage-2')
  ],
  plugins: [
    require('babel-plugin-transform-decorators-legacy').default,
    [require('babel-plugin-transform-react-jsx'), {
      pragma: 'createElement'
    }]
  ],
  env: {
    production: {
      plugins: [
        require('babel-plugin-transform-react-constant-elements'),
        require('babel-plugin-transform-react-inline-elements')
      ]
    }
  }
}

Fix bug when requiring babel-plugin-transform-decorators-legacy


#!/usr/bin/env node

var program = require('commander');
var prompt = require('inquirer');
var path = require('path');

var pkg = require(path.resolve(__dirname, '..', 'package.json'));

program.version(pkg.version)

program
    .command('new <name>')
    .description('Create a new project with the provided name')
    .action(function(name) {
        console.log('hits new command');
        console.log('project name is ->', name);
    });

program.parse(process.argv);

#!/usr/bin/env node

var program = require('commander');
var prompt = require('inquirer');
var path = require('path');
var chalk = require('chalk');

var pkg = require(path.resolve(__dirname, '..', 'package.json'));

program.version(pkg.version)

program
    .command('new <name>')
    .description('Create a new project with the provided name')
    .action(function(name) {
        console.log('Creating a new project: %s', chalk.underline(name));
        console.log('@TODO add this in!');
    });

program.parse(process.argv);

feat: Add user message outputting given project name


var FakeRedis = require('fakeredis');
var RedisCache = require('../../cache/RedisCache');
var Promise = require('bluebird');

var expect = require('expect');
var updateAddChunkPrefixes = require('../updateAddChunkPrefixes');
var sinon = require('sinon');

describe('expireChunkRanges', function() {
  var cache = new RedisCache(FakeRedis.createClient());
  var listName = 'list';

  it('should call methods for all specified chunks', function() {
    var ranges = [[1, 3], 5];
    var prefixes = ['a'];
    var mockCache = sinon.mock(cache);
    mockCache.expects('getChunkByID')
      .exactly(4).returns(Promise.resolve(['a']));
    mockCache.expects('dropPrefixes')
      .exactly(4).returns(Promise.resolve());
    mockCache.expects('dropPendingSubChunksByChunkID')
      .exactly(4).returns(Promise.resolve());
    mockCache.expects('dropChunkByID')
      .exactly(4).returns(Promise.resolve());
  });
});

var FakeRedis = require('fakeredis');
var RedisCache = require('../../cache/RedisCache');
var ChunkTypes = require('../ChunkTypes');
var Promise = require('bluebird');

var expect = require('expect');
var expireChunkRanges = require('../expireChunkRanges');
var sinon = require('sinon');

describe('expireChunkRanges', function() {
  var cache = new RedisCache(FakeRedis.createClient());
  var listName = 'list';

  it('should call methods for all specified chunks', function() {
    var ranges = [[1, 3], 5];
    var prefixes = ['a'];

    var mockCache = sinon.mock(cache);
    mockCache.expects('getChunkByID')
      .exactly(4).returns(Promise.resolve(['a']));
    mockCache.expects('dropPrefixes')
      .exactly(4).returns(Promise.resolve());
    mockCache.expects('dropPendingSubChunksByChunkID')
      .exactly(4).returns(Promise.resolve());
    mockCache.expects('dropChunkByID')
      .exactly(4).returns(Promise.resolve());

    return expireChunkRanges(cache, listName, ChunkTypes.ADD, ranges)
      .then(function() {
        mockCache.verify();
        mockCache.restore();
      });
  });
});

Fix expireChunkRanges test to actually work.


const config = {
  common: {
    outputDir: 'public',
    styles: [],
    scripts: [],
  },
  template: {
    common: {
      path: 'templates', // Path where the templates are
    },
    pug: {
      pretty: true,
    },
  },
  file: {
    html: {},
    json: {},
    markdown: {},
    yaml: {},
  },
  asset: {
    active: [],
    common: {
      path: 'assets', // path to assets
    },
    styles: {
      postcss: {
        plugins: {
          prod: [{ name: 'cssnano' }],
          dev: [],
        },
      },
    },
    scripts: {},
    fonts: {},
    images: {
      minify: {},
    },
  },
  content: {
    active: [],
    common: {
      path: 'content', // path to content
      urlWithExtension: false,
      htmlmin: {
        removeAttributeQuotes: true,
        collapseWhitespace: true,
      },
    },
    pages: {},
    posts: {
      urlFormat: '_blog/${year}/${month}/${date}/', // eslint-disable-line
    },
  },
  decorator: {
    active: [],
    common: {},
    sitemap: {},
    tags: {},
    pagination: {},
  },
};

module.exports = config;

const config = {
  common: {
    outputDir: 'public',
    styles: [],
    scripts: [],
    urlWithExtension: false,
  },
  template: {
    common: {
      path: 'templates', // Path where the templates are
    },
    pug: {
      pretty: true,
    },
  },
  file: {
    html: {},
    json: {},
    markdown: {},
    yaml: {},
  },
  asset: {
    active: [],
    common: {
      path: 'assets', // path to assets
    },
    styles: {
      postcss: {
        plugins: {
          prod: [{ name: 'cssnano' }],
          dev: [],
        },
      },
    },
    scripts: {},
    fonts: {},
    images: {
      minify: {},
    },
  },
  content: {
    active: [],
    common: {
      path: 'content', // path to content
      htmlmin: {
        removeAttributeQuotes: true,
        collapseWhitespace: true,
      },
    },
    pages: {},
    posts: {
      urlFormat: '_blog/${year}/${month}/${date}/', // eslint-disable-line
    },
  },
  decorator: {
    active: [],
    common: {},
    sitemap: {},
    tags: {},
    pagination: {},
  },
};

module.exports = config;

Move urlWithExtension to the global common

Remove from content common as it overwrites the global common defined by the user


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

const Path = require('path');
const server = require('../utils/test-metadata-server');

global.Config = require('nconf')
    .argv()
    .env()
    .file(Path.resolve(__dirname, '../data/config.json'))
    .defaults(require('../../config/defaults.json'));
global.Log = require('../../lib/logger').attach(global.Config);

server.start();

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

const Path = require('path');
const server = require('../utils/test-metadata-server');

global.Config = require('nconf')
    .argv()
    .env()
    .file(Path.resolve(__dirname, '../data/config.json'))
    .defaults(require('../../config/defaults.json'));
global.Log = require('../../lib/logger').attach(Config.get('log:level'));

server.start();

Allow the test metadata server to start by giving it a log level.


import Client from './state-client';

// Wrapper around global Twitch extension object.
class Ext {
  static onAuthorized(cb) {
    if (window.Twitch) {
      window.Twitch.ext.onAuthorized(cb);
    } else {
      Client.fetchTestAuth(this.testAppID, this.testChannelID)
        .then((auth) => {
          cb(auth);
        })
        .catch(() => {
          cb();
        });
    }
  }

  static onContext(cb) {
    if (window.Twitch) {
      window.Twitch.ext.onContext(cb);
    }
  }
}

export default Ext;

import Client from './state-client';

// Wrapper around global Twitch extension object.
class Ext {
  static fetchTestAuth(cb) {
    Client.fetchTestAuth(this.testAppID, this.testChannelID)
      .then((auth) => {
        cb(auth);
      })
      .catch(() => {
        cb();
      });
  }

  static onAuthorized(cb) {
    if (window.Twitch) {
      window.Twitch.ext.onAuthorized(cb);
    } else {
      Ext.fetchTestAuth(cb);
      setInterval(Ext.fetchTestAuth, 1000 * 60 * 25, cb);
    }
  }

  static onContext(cb) {
    if (window.Twitch) {
      window.Twitch.ext.onContext(cb);
    }
  }
}

export default Ext;

Refresh the test token every 25 minutes

Refresh the test token every 25 minutes


import React from 'react';
import {render} from 'react-dom';
import Rx from 'rx';
import {actionIs} from './utils.js';

/*
update is an Array of trios: [actionName, actionHandler, effect]
*/

export default function(model, update, View, container) {
  let eventStream = new Rx.Subject();

  let dispatcherArray = update.map(([actionName, actionHandler]) =>
    eventStream.filter(actionIs(actionName)).map(actionHandler))

  return Rx.Observable
    .merge(...dispatcherArray)
    .scan(((state, action) => action(state)), model)
    .startWith([model])
    .do(([state, _]) => render(<View {...state} eventStream={eventStream} />, container))
    .flatMap(([state, effect]) => effect ? effect(state, eventStream) : Rx.Observable.empty() )
    .subscribe(
      function(){},
      console.error.bind(console)
    );
}

import React from 'react';
import {render} from 'react-dom';
import Rx from 'rx';
import {actionIs} from './utils.js';

/*
update is an Array of trios: [actionName, actionHandler, effect]
*/

export default function(model, update, View, container) {
  let eventStream = new Rx.Subject();

  let dispatcherArray = update.map(([actionName, actionHandler]) =>
    eventStream.filter(actionIs(actionName)).map(actionHandler))

  return Rx.Observable
    .merge(...dispatcherArray)
    .scan((([state, _], action) => action(state)), [model])
    .startWith([model])
    .do(([state, _]) => render(<View {...state} eventStream={eventStream} />, container))
    .flatMap(([state, effect]) => effect ? effect(state, eventStream) : Rx.Observable.empty() )
    .subscribe(
      function(){},
      console.error.bind(console)
    );
}

Correct passing of state to state mutators


import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import styled from 'styled-components'

import Text from 'v2/components/UI/Text'

const Copy = styled(Text)`
  a {
    font-weight: bold;
  }
  a:hover {
    color: ${x => x.theme.colors.gray.base};
  }
`

export default class SearchEmptyMessage extends PureComponent {
  static propTypes = {
    term: PropTypes.string.isRequired,
  }

  render() {
    const { term } = this.props

    return (
      <Copy f={6} my={8} color="gray.medium" lineHeight={2}>
        {`No results for '${term}'`}
      </Copy>
    )
  }
}

import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import styled from 'styled-components'

import Text from 'v2/components/UI/Text'

const Copy = styled(Text)`
  a {
    font-weight: bold;
  }
  a:hover {
    color: ${x => x.theme.colors.gray.base};
  }
`

export default class SearchEmptyMessage extends PureComponent {
  static propTypes = {
    term: PropTypes.string.isRequired,
  }

  render() {
    const { term } = this.props

    return (
      <Copy f={6} my={8} color="gray.medium" lineHeight={2}>
        {term
          ? `No results for '${term}'`
          : `No results found, try changing your search filters.`}
      </Copy>
    )
  }
}

Fix no results message when there is no term


/*
 * Swagger UI and Specs Servers
*/
var path = require('path')
var createServer = require('http-server').createServer;

var dist = path.join(__dirname, '..', '..', 'dist');
var specs = path.join(__dirname, '..', '..', 'test', 'specs');
var DOCS_PORT = 8080;
var SPEC_SERVER_PORT = 8081;

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

var headers = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept'
};

var swaggerUI;
var specServer;

module.exports.start = function (specsLocation, done) {
  swaggerUI = createServer({ root: dist, headers: headers });
  specServer = createServer({ root: specs, headers: headers });

  swaggerUI.listen(DOCS_PORT);
  specServer.listen(SPEC_SERVER_PORT);

  var swaggerSpecLocation = encodeURIComponent('http://localhost:' + SPEC_SERVER_PORT + specsLocation)
  var url = 'http://localhost:' + DOCS_PORT + '/index.html?url=' + swaggerSpecLocation;

  setTimeout(function(){
    driver.get(url);
    done();
  }, 3000);
};

module.exports.close = function(){
  swaggerUI.close();
  specServer.close();
}

/*
 * Swagger UI and Specs Servers
*/
var path = require('path')
var createServer = require('http-server').createServer;

var dist = path.join(__dirname, '..', '..', 'dist');
var specs = path.join(__dirname, '..', '..', 'test', 'specs');
var DOCS_PORT = 8080;
var SPEC_SERVER_PORT = 8081;

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

var headers = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept'
};

var swaggerUI;
var specServer;

module.exports.start = function (specsLocation, done) {
  swaggerUI = createServer({ root: dist, headers: headers });
  specServer = createServer({ root: specs, headers: headers });

  swaggerUI.listen(DOCS_PORT);
  specServer.listen(SPEC_SERVER_PORT);

  var swaggerSpecLocation = encodeURIComponent('http://localhost:' + SPEC_SERVER_PORT + specsLocation)
  var url = 'http://localhost:' + DOCS_PORT + '/index.html?url=' + swaggerSpecLocation;

  setTimeout(function(){
    driver.get(url);
    done();
  }, process.env.TRAVIS ? 6000 : 3000);
};

module.exports.close = function(){
  swaggerUI.close();
  specServer.close();
}

Increase timeout for server launch in TravisCI


define([], function () {
  return {
    hasLoggedInUser: function() {
        return !!(this.profile && this.profile.get('selected_identity'));
    },
    profile: null
  }
});

import { hasLoggedInUser } from 'profilePredicate';

export default {
    hasLoggedInUser: function() {
        return hasLoggedInUser(this.profile);
    },
    profile: null
}

Use utility predicate in logged in user check


const path = require('path');
const should = require('should');

const pluginsLoader = require('../../lib/plugins');

// Caching fixture plugin, but redefining its properties for testing plugin load.
const pluginName = 'express-gateway-plugin-test';
const pluginDirectory = path.join(__dirname, '../fixtures', pluginName);
const testPlugin = require(pluginDirectory);
testPlugin.schema = {
  '$id': `http://express-gateway.io/schemas/plugin/${pluginName}.json`,
  'required': ['schema-test-param']
};
testPlugin.init = (pluginContext) => {
  pluginContext.registerPolicy('schema-test-policy');
};

describe('Plugin schema validation on load', () => {
  it('fails loading when parameter undefined', () => {
    const missingParameterConfig = {
      'config': {
        'systemConfig': {
          'plugins': {
            'test': {
              'package': pluginDirectory
            }
          }
        }
      }
    };

    const loadedPlugins = pluginsLoader.load(missingParameterConfig);
    should(loadedPlugins).a.property('policies').be.empty();
  });

  it('loads plugin and registers policy successfully', () => {
    const config = {
      'config': {
        'systemConfig': {
          'plugins': {
            'test': {
              'package': pluginDirectory,
              'schema-test-param': 'defined'
            }
          }
        }
      }
    };

    const loadedPlugins = pluginsLoader.load(config);
    should(loadedPlugins).have.a.property('policies', ['schema-test-policy']);
  });
});

const path = require('path');
const should = require('should');

const pluginsLoader = require('../../lib/plugins');

// Caching fixture plugin, but redefining its properties for testing plugin load.
const pluginName = 'express-gateway-plugin-test';
const pluginDirectory = path.join(__dirname, '../fixtures', pluginName);
const testPlugin = require(pluginDirectory);

testPlugin.schema = {
  '$id': `http://express-gateway.io/schemas/plugin/${pluginName}.json`,
  'required': ['schema-test-param']
};

testPlugin.init = (pluginContext) => {
  pluginContext.registerPolicy('schema-test-policy');
};

describe('Plugin schema validation on load', () => {
  it('fails loading when parameter undefined', () => {
    const missingParameterConfig = {
      'config': {
        'systemConfig': {
          'plugins': {
            'test': {
              'package': pluginDirectory
            }
          }
        }
      }
    };

    const loadedPlugins = pluginsLoader.load(missingParameterConfig);
    should(loadedPlugins).have.property('policies').empty();
  });

  it('loads plugin and registers policy successfully', () => {
    const config = {
      'config': {
        'systemConfig': {
          'plugins': {
            'test': {
              'package': pluginDirectory,
              'schema-test-param': 'defined'
            }
          }
        }
      }
    };

    const loadedPlugins = pluginsLoader.load(config);
    should(loadedPlugins).have.property('policies', ['schema-test-policy']);
  });
});

Put new lines here and there

Put new lines here and there


(function(previousMethods){

if (typeof previousMethods === 'undefined') {
	// Defining previously that object allows you to use that plugin even if you have overridden L.map
	previousMethods = {
		getTiledPixelBounds: L.GridLayer.prototype._getTiledPixelBounds
	};
}

L.GridLayer.include({

  _getTiledPixelBounds : function(center, zoom, tileZoom) {
    var pixelBounds = previousMethods.getTiledPixelBounds.call(this, center, zoom, tileZoom);

    if (this.options.edgeBufferTiles > 0) {
      var pixelEdgeBuffer = this.options.edgeBufferTiles * this._getTileSize();
      pixelBounds = new L.Bounds(pixelBounds.min.subtract([pixelEdgeBuffer, pixelEdgeBuffer]), pixelBounds.max.add([pixelEdgeBuffer, pixelEdgeBuffer]));
    }

    return pixelBounds;
  }
});

})(window.leafletEdgeBufferPreviousMethods);

(function(previousMethods){

if (typeof previousMethods === 'undefined') {
	// Defining previously that object allows you to use that plugin even if you have overridden L.map
	previousMethods = {
		getTiledPixelBounds: L.GridLayer.prototype._getTiledPixelBounds
	};
}

L.GridLayer.include({

  _getTiledPixelBounds : function(center, zoom, tileZoom) {
    var pixelBounds = previousMethods.getTiledPixelBounds.call(this, center, zoom, tileZoom);

    if (this.options.edgeBufferTiles > 0) {
      var pixelEdgeBuffer = this.options.edgeBufferTiles * this.options.tileSize;
      pixelBounds = new L.Bounds(pixelBounds.min.subtract([pixelEdgeBuffer, pixelEdgeBuffer]), pixelBounds.max.add([pixelEdgeBuffer, pixelEdgeBuffer]));
    }

    return pixelBounds;
  }
});

})(window.leafletEdgeBufferPreviousMethods);

Use tileSize direct from options

Use tileSize direct from options


// eslint-disable-next-line no-unused-vars
function dega_bed_parser(line, index) {
    // Here is where we convert the content of a specific BED file format to the desired JSON structure

    // In Parul's file, there are two entities- a cis regulatory element (the position column
    //  used for tabix queries) and a promoter that affects a gene (a string column with an ID)
    var fields = line.split('\t');
    var chrom = fields[0].replace('chr', '');

    // assume format chr1:123-456_GENE or chr1:123-456 (chr prefix optional)
    var second_item = fields[3].match(/^(?:chr)?(\d+):(\d+)-(\d+)(?:_)?(\w+)?$/);
    var score = fields[4];
    score = (score === '.') ? null : +score;
    return {
        // Note: The query is based on position of element 1, not element 2. It's possible for a loop to be in the
        //   region, but the tabix query to not find it.
        chrom: chrom,
        start1: +fields[1],
        end1: +fields[2],
        start2: +second_item[2],
        end2: +second_item[3],
        // Optional field: chromatin interaction files have it; coaccessibility files do not
        target: second_item[4] || null,
        score: score,
        id: index,  // Dummy ID: position in the list of lines TODO: create a better unique ID
    };
}

// eslint-disable-next-line no-unused-vars
function dega_bed_parser(line, index) {
    // Here is where we convert the content of a specific BED file format to the desired JSON structure

    // In Parul's file, there are two entities- a cis regulatory element (the position column
    //  used for tabix queries) and a promoter that affects a gene (a string column with an ID)
    var fields = line.split('\t');
    var chrom = fields[0].replace('chr', '');

    // assume format chr1:123-456_GENE or chr1:123-456 (chr prefix optional)
    var second_item = fields[3].match(/^(?:chr)?(\d+):(\d+)-(\d+)(?:_)?(\S+)?$/);
    var score = fields[4];
    score = (score === '.') ? null : +score;
    return {
        // Note: The query is based on position of element 1, not element 2. It's possible for a loop to be in the
        //   region, but the tabix query to not find it.
        chrom: chrom,
        start1: +fields[1],
        end1: +fields[2],
        start2: +second_item[2],
        end2: +second_item[3],
        // Optional field: chromatin interaction files have it; coaccessibility files do not
        target: second_item[4] || null,
        score: score,
        id: index,  // Dummy ID: position in the list of lines TODO: create a better unique ID
    };
}

Fix gene regex per parul suggestion

Fix gene regex per parul suggestion

[ci skip]


const moment = require('moment');
const weather = require('./weather');

const actions = {
  getWeather: (data) => new Promise((resolve, reject) => {
    const context = data.context;
    const entities = data.entities;

    const missingLocation = entities.location === undefined;
    const location = entities.location ? entities.location[0].value : null;
    const datetime = entities.datetime ? entities.datetime[0].value : null;

    if (missingLocation) {
      const contextData = Object.assign({}, context, { missingLocation });
      resolve(contextData);
    } else if (datetime) {
      weather.forecastByLocationName(location, datetime)
        .then((weatherData) => {
          const contextData = Object.assign({}, context, weatherData);
          if (datetime) {
            Object.assign(contextData, { datetime: moment(datetime).calendar().toLowerCase() });
          }
          resolve(contextData);
        })
        .catch(reject);
    } else {
      weather.weatherByLocationName(location)
        .then((weatherData) => {
          const contextData = Object.assign({}, context, weatherData);
          if (datetime) {
            Object.assign(contextData, { datetime: moment(datetime).calendar().toLowerCase() });
          }
          resolve(contextData);
        })
        .catch(reject);
    }
  })
}

module.exports = actions;

const moment = require('moment');
const weather = require('./weather');

const actions = {
  getWeather: (data) => new Promise((resolve, reject) => {
    const context = data.context;
    const entities = data.entities;

    const missingLocation = entities.location === undefined;
    const location = entities.location ? entities.location[0].value : null;
    const datetime = entities.datetime ? entities.datetime[0].value : null;

    if (missingLocation) {
      const contextData = Object.assign({}, context, { missingLocation });
      resolve(contextData);
    } else if (datetime) {
      weather.forecastByLocationName(location, datetime)
        .then((weatherData) => {
          const contextData = Object.assign({}, context, weatherData);
          if (datetime) {
            Object.assign(contextData, { datetime: moment(datetime).calendar().toLowerCase() });
          }
          resolve(contextData);
        })
        .catch(reject);
    } else {
      weather.weatherByLocationName(location)
        .then((weatherData) => {
          const contextData = Object.assign({}, context, weatherData);
          if (datetime) {
            Object.assign(contextData, { datetime: moment(datetime).calendar().toLowerCase() });
          }
          resolve(contextData);
        })
        .catch(reject);
    }
  }),
  getTime: (data) => new Promise((resolve, reject) => {
    const context = data.context;
    const contextData = {};
    const datetime = new Date();

    Object.assign(contextData, { datetime: moment(datetime).calendar().toLowerCase() });
    resolve(contextData);
  })
}

module.exports = actions;

Add getTime as default wit action in boilerplate


function normalizeName(name) {
  if (name.charAt(0) === '-') {
    name = name.substr(1)
  }

  if (name.charAt(0) === '-') {
    name = name.substr(1)
  }

  return name
}

class Option {
  constructor(name, parent) {
    if (!parent) {
      throw new Error('An option must have a parent command')
    }

    if (!name || typeof name !== 'string') {
      throw new Error('The first argument of the Option constructor must be a its name (a non-empty string)')
    }

    name = normalizeName(name)
    this.parent = parent
    this.config = {
      name,
      id: `${parent.config.id}#${name}`,
    }
  }

  aliases(...aliases) {
    this.config.aliases = aliases.map((a) => normalizeName(a))
    return this
  }

  shared() {
    this.parent.sharedOptions.push(this.config.id)
    return this
  }

  command(...args) {
    return this.parent.command(...args)
  }

  option(...args) {
    return this.parent.option(...args)
  }

  handle(...args) {
    return this.parent.handle(...args)
  }

  end() {
    return this.parent
  }
}

module.exports = Option

function normalizeName(name) {
  if (name.charAt(0) === '-') {
    name = name.substr(1)
  }

  if (name.charAt(0) === '-') {
    name = name.substr(1)
  }

  return name
}

class Option {
  constructor(name, parent) {
    if (!parent) {
      throw new Error('An option must have a parent command')
    }

    if (!name || typeof name !== 'string') {
      throw new Error('The first argument of the Option constructor must be its name (a non-empty string)')
    }

    name = normalizeName(name)
    this.parent = parent
    this.config = {
      name,
      id: `${parent.config.id}#${name}`,
    }
  }

  aliases(...aliases) {
    this.config.aliases = aliases.map((a) => normalizeName(a))
    return this
  }

  shared() {
    this.parent.sharedOptions.push(this.config.id)
    return this
  }

  command(...args) {
    return this.parent.command(...args)
  }

  option(...args) {
    return this.parent.option(...args)
  }

  handle(...args) {
    return this.parent.handle(...args)
  }

  end() {
    return this.parent
  }
}

module.exports = Option

Fix a typo in an error

Fix a typo in an error


function prepareCSVLink()
{
    var pokemonCells = document.getElementsByClassName('pokemonCell');

    var csvString = 'Number, Name, Caught\n'

    for(var i = 0; i < pokemonCells.length; i++)
    {
        var pokemonCell = pokemonCells[i];
        var number = pokemonCell.getElementsByClassName("pokemonCellNumber")[0].innerHTML;
        var name = pokemonCell.getElementsByClassName("pokemonCellName")[0].innerHTML;
        var caughtState = pokemonCell.className.split(/\s+/)[1];

        csvString+=number;
        csvString+=', ';
        csvString+=name;
        csvString+=', ';
        csvString+=caughtState;
        csvString+='\n';

        break;
    }

    var downloadLink = document.createElement("a");
    var csvURI = 'data:text/csv;charset=utf-8,' + csvString;
    downloadLink.href = csvString;
    downloadLink.download = 'dex.csv';
    downloadLink.innerHTML = "Download as CSV (experimental)";

    var container = document.getElementsByClassName("csvDownloadLinkContainer")[0];
    container.appendChild(downloadLink);
}

function prepareCSVLink()
{
    var pokemonCells = document.getElementsByClassName('pokemonCell');

    var csvString = 'Number, Name, Caught\n'

    for(var i = 0; i < pokemonCells.length; i++)
    {
        var pokemonCell = pokemonCells[i];
        var number = pokemonCell.getElementsByClassName("pokemonCellNumber")[0].innerHTML;
        var name = pokemonCell.getElementsByClassName("pokemonCellName")[0].innerHTML;
        var caughtState = pokemonCell.className.split(/\s+/)[1];

        csvString+=number;
        csvString+=', ';
        csvString+=name;
        csvString+=', ';
        csvString+=caughtState;
        csvString+='\n';

        break;
    }

    var downloadLink = document.createElement("a");
    var csvURI = 'data:text/csv;charset=utf-8,' + csvString;
    downloadLink.href = csvURI;
    downloadLink.download = 'dex.csv';
    downloadLink.innerHTML = "Download as CSV (experimental)";

    var container = document.getElementsByClassName("csvDownloadLinkContainer")[0];
    container.appendChild(downloadLink);
}

Fix CSV download link to include content type


import React, { Component } from 'react'
import { connect } from 'react-redux'
import { addPattern } from '../actions'
import TextField from 'material-ui/TextField'
import RaisedButton from 'material-ui/RaisedButton'
import Subheader from 'material-ui/Subheader'
import Paper from 'material-ui/Paper'
import * as styles from './styles'

const mapStateToProps = function(state) {
  return {
    patternList: state.patternList
  }
}

class AddPattern extends Component {
  render() {
    return (
      <div>
        <Subheader style={styles.headingStyle}>Add Pattern</Subheader>
        <Paper style={styles.paperStyle} zDepth={2} rounded={true}>
          <TextField ref="name" style={styles.inputStyle} hintText="Name" />
          <TextField ref="pattern" style={styles.inputStyle} hintText="Pattern" fullWidth={true} />
          <RaisedButton label="Add Pattern" primary={true} style={styles.buttonStyle} onClick={ () => this.props.addPattern(this.refs.name.input.value, this.refs.pattern.state.searchText) } />
        </Paper>
      </div>
    )
  }
}

export default connect(mapStateToProps,
  (dispatch) => {
    return {
      addPattern: (name, pattern) => {
        dispatch(addPattern(name, pattern))
      }
    }
  })(AddPattern)

import React, { Component } from 'react'
import { connect } from 'react-redux'
import { addPattern } from '../actions'
import TextField from 'material-ui/TextField'
import RaisedButton from 'material-ui/RaisedButton'
import Subheader from 'material-ui/Subheader'
import Paper from 'material-ui/Paper'
import * as styles from './styles'

const mapStateToProps = function(state) {
  return {
    patternList: state.patternList
  }
}

class AddPattern extends Component {
  render() {
    return (
      <div>
        <Subheader style={styles.headingStyle}>Add Pattern</Subheader>
        <Paper style={styles.paperStyle} zDepth={2} rounded={true}>
          <TextField ref="name" style={styles.inputStyle} hintText="Name" />
          <TextField ref="pattern" style={styles.inputStyle} hintText="Pattern" fullWidth={true} />
          <RaisedButton label="Add Pattern" primary={true} style={styles.buttonStyle} onClick={ () => this.props.addPattern(this.refs.name.input.value, this.refs.pattern.input.value) } />
        </Paper>
      </div>
    )
  }
}

export default connect(mapStateToProps,
  (dispatch) => {
    return {
      addPattern: (name, pattern) => {
        dispatch(addPattern(name, pattern))
      }
    }
  })(AddPattern)

Fix adding pattern to actually use the pattern text.


function quotemeta(str) {
  // From: http://phpjs.org/functions
  // +   original by: Paulo Freitas
  // *     example 1: quotemeta(". + * ? ^ ( $ )");
  // *     returns 1: '\. \+ \* \? \^ \( \$ \)'
  return (str + '').replace(/([\.\\\+\*\?\[\^\]\$\(\)])/g, '\\$1');
}

Fix example (to give proper response and as expected by Travis)


"use strict";

// Program that sorts a stack such that smallest items are on top

// create Stack class
function Stack() {
	this.top = null;
}

// push value into stack
Stack.prototype.push = function(val) {
	this.top = {
		data: val,
		next: this.top
	};
};

// pop value from stack
Stack.prototype.pop = function() {
	var top = this.top;
	if(top) {
		var popData = top.data;
		top = top.next;
		return popData;
	}
	return;
};

// sort stack in ascending order (main function in exercise)
Stack.prototype.sort = function() {
	// create output stack
	var stackTwo = new Stack();

	while(this.top) {
		var placeHolder = this.pop();

		while(stackTwo.top && stackTwo.top.data > placeHolder) {
			// push the top element in output stack (larger value) into original stack
			stackOne.push(stackTwo.pop());
		}
		// push element in placeholder into output stack
		stackTwo.push(placeHolder);
	}
	console.log(stackTwo);
};

// test cases
var testStack = new Stack();
testStack.push(1);
testStack.push(2);
testStack.push(3);
testStack.push(4);
testStack.push(5);
// expect 5 to be at top of stack
console.log(testStack);
testStack.sort();

Add test case for sort stack program

Add test case for sort stack program


(function () {
    function showMessage(message, type) {
        $('#check-update-info a.close').trigger('click.fndtn.alert');
        var alertBox = '
' + message +
            '× ' + '
';
            $(".large-10").prepend(alertBox).foundation();
    }

    function showLoading() {
        var loading = '

';
        $(".large-10").prepend(loading);
    }

    function hideLoading() {
        $('#loading').remove();
    }

    $("#checkupdate").bind('click', function (event) {
        $.ajax({
            url: "/api/checkupdate",
            type: "get",
            dataType: "json",
            beforeSend: function (xhr) {
                $('#check-update-info a.close').trigger('click.fndtn.alert');
                showLoading();
            },
            success: function (res, status, xhr) {
                console.log(res);
                var message = res.message + '点此查看升级方法。'
                showMessage(message, 'success');
            },
            complete: function (xhr, status) {
                hideLoading();
            },
            error: function (xhr, status, thrown) {
                var err = xhr.responseJSON;
                console.log(err);
                showMessage(err.message, 'alert');
            }
        });
    });
})();

(function () {
    function showMessage(message, type) {
        $('#check-update-info a.close').trigger('click.fndtn.alert');
        var alertBox = '
' + message +
            '× ' + '
';
            $(".large-10").prepend(alertBox).foundation();
    }

    function showLoading() {
        var loading = '

';
        $(".large-10").prepend(loading);
    }

    function hideLoading() {
        $('#loading').remove();
    }

    $("#checkupdate").bind('click', function (event) {
        $.ajax({
            url: "/api/checkupdate",
            type: "get",
            dataType: "json",
            beforeSend: function (xhr) {
                $('#check-update-info a.close').trigger('click.fndtn.alert');
                showLoading();
            },
            success: function (res, status, xhr) {
                console.log(res);
                var message = res.message + '点此查看升级方法。'
                showMessage(message, 'success');
            },
            complete: function (xhr, status) {
                hideLoading();
            },
            error: function (xhr, status, thrown) {
                var err = xhr.responseJSON;
                console.log(err);
                showMessage(err ? err.message : '检测更新失败！', 'alert');
            }
        });
    });
})();

Update check update error handler.

Update check update error handler.


Template.latestActivitiesByType.created = function () {
  // Create 'instance' variable for use througout template logic
  var instance = this;

  // Create variable to hold activity type selection
  instance.activityTypeSelection = new ReactiveVar();

  // Create a reactive var to hold data containing latest activities by type
  instance.latestActivityIdsByTypeData = new ReactiveVar();

  Meteor.call('getAllResidentsLatestActivityIdsByType', function (error, latestResidentActivityIdsByType) {
    // Set latest activities by type
    instance.latestActivityIdsByTypeData.set(latestResidentActivityIdsByType);
  });
};

Template.latestActivitiesByType.helpers({
  'latestActivitiesByType': function () {
    // Get a reference to template instance
    var instance = Template.instance();

    var latestResidentActivityIdsByType = instance.latestActivityIdsByTypeData.get();

    //console.log(latestResidentActivitiesByType);
    if (latestResidentActivityIdsByType) {
      console.log(latestResidentActivityIdsByType);
      return latestResidentActivityIdsByType;
    } else {
      return [];
    }
  }
});

Template.latestActivitiesByType.created = function () {
  // Create 'instance' variable for use througout template logic
  var instance = this;

  // Create variable to hold activity type selection
  instance.activityTypeSelection = new ReactiveVar();

  // Create a reactive var to hold data containing latest activities by type
  instance.latestActivityIdsByTypeData = new ReactiveVar();

  Meteor.call('getAllResidentsLatestActivityIdsByType', function (error, latestResidentActivityIdsByType) {
    // Set latest activities by type
    instance.latestActivityIdsByTypeData.set(latestResidentActivityIdsByType);
  });
};

Template.latestActivitiesByType.helpers({
  'latestActivitiesByType': function () {
    // Get a reference to template instance
    var instance = Template.instance();

    var latestResidentActivityIdsByType = instance.latestActivityIdsByTypeData.get();

    // Create an array of activities
    var activities = _.map(latestResidentActivityIdsByType, function (activityId) {
      // Find a single activity by ID
      var activity = Activities.findOne(activityId);

      return activity;
    });

    // If activities exist
    if (activities) {
      // Return the activities to template
      return activities;
    } else {
      // Return an empty array
      return [];
    }
  }
});

Return activity objects to template

Return activity objects to template


import { isFunction } from 'lodash';
import { setHeader } from 'focus-core/application';

module.exports = {

    /**
     * Updates the cartridge using the cartridgeConfiguration.
     */
    _registerCartridge(props = this.props) {
        const cartridgeConfiguration = this.cartridgeConfiguration || props.cartridgeConfiguration;
        const hasCartridge = isFunction(cartridgeConfiguration);
        if (!hasCartridge) {
            console.warn(`
                Your detail page does not have any cartrige configuration, this is not mandarory but recommended.
                It should be a component attribute return by a function.
                function cartridgeConfiguration(){
                    var cartridgeConfiguration = {
                    summary: {component: "A React Component", props: {id: this.props.id}},
                    cartridge: {component: "A React Component"},
                    actions: {components: "react actions"}
                    };
                    return cartridgeConfiguration;
                }
            `);
        }
        const config = hasCartridge ? cartridgeConfiguration() : {};
        setHeader(config);
    },

    /**
     * Registers the cartridge upon mounting.
     */
    componentWillMount() {
        this._registerCartridge();
    },

    componentWillReceiveProps(nextProps) {
        this._registerCartridge(nextProps);
    }
};

import { isFunction } from 'lodash';
import { setHeader } from 'focus-core/application';

const cartridgeBehaviour = {

    /**
     * Updates the cartridge using the cartridgeConfiguration.
     */
    _registerCartridge(props = this.props) {
        const cartridgeConfiguration = this.cartridgeConfiguration || props.cartridgeConfiguration;
        const hasCartridge = isFunction(cartridgeConfiguration);
        if (!hasCartridge) {
            console.warn(`
                Your detail page does not have any cartrige configuration, this is not mandarory but recommended.
                It should be a component attribute return by a function.
                function cartridgeConfiguration(){
                    var cartridgeConfiguration = {
                    summary: {component: "A React Component", props: {id: this.props.id}},
                    cartridge: {component: "A React Component"},
                    actions: {components: "react actions"}
                    };
                    return cartridgeConfiguration;
                }
            `);
        }
        const config = hasCartridge ? cartridgeConfiguration() : {};
        setHeader(config);
    },

    /**
     * Registers the cartridge upon mounting.
     */
    componentWillMount() {
        this._registerCartridge();
    },

    componentWillReceiveProps(nextProps) {
        this._registerCartridge(nextProps);
    }
};

export default cartridgeBehaviour;

Change module.exports to export default

Change module.exports to export default


import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';

moduleForComponent('ics-feed', 'Integration | Component | ics feed', {
  integration: true
});

test('it show instructions', function(assert) {
  assert.expect(1);
  let instructions = 'SOME TEST INS';
  this.set('instructions', instructions);

  this.render(hbs`{{ics-feed instructions=instructions}}`);

  assert.equal(this.$().text().trim(), instructions);
});

test('it show url', function(assert) {
  assert.expect(1);
  let url = 'http://example.com/url';
  this.set('url', url);

  this.render(hbs`{{ics-feed url=url}}`);

  assert.equal(this.$('input').val().trim(), url);
});

test('refresh calls action', function(assert) {
  assert.expect(1);
  let url = 'http://example.com/url';
  this.set('url', url);
  // Set any properties with this.set('myProperty', 'value');
  // Handle any actions with this.on('myAction', function(val) { ... });
  this.on('refresh', function(){
    assert.ok(true);
  });
  this.render(hbs`{{ics-feed refresh='refresh'}}`);

  this.$('button:eq(1)').click();
});

import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';

moduleForComponent('ics-feed', 'Integration | Component | ics feed', {
  integration: true
});

test('it show instructions', function(assert) {
  assert.expect(1);
  let instructions = 'SOME TEST INS';
  this.set('instructions', instructions);

  this.render(hbs`{{ics-feed instructions=instructions}}`);

  assert.equal(this.$().text().trim(), instructions);
});

test('it show url', function(assert) {
  assert.expect(1);
  let url = 'http://example.com/url';
  this.set('url', url);

  this.render(hbs`{{ics-feed url=url}}`);

  assert.equal(this.$('input').val().trim(), url);
});

// test('refresh calls action', function(assert) {
//   assert.expect(1);
//   let url = 'http://example.com/url';
//   this.set('url', url);
//   // Set any properties with this.set('myProperty', 'value');
//   // Handle any actions with this.on('myAction', function(val) { ... });
//   this.on('refresh', function(){
//     assert.ok(true);
//   });
//   this.render(hbs`{{ics-feed refresh='refresh'}}`);
//
//   this.$('button:eq(1)').click();
// });

Remove test for refresh button that is currently not enabled


!function(PIXI, exports) {
  'use strict';

  var Point = exports.Point,
      Rect = exports.Rect;

  function Frame(position, url, options) {
    this.pos = position;
    this.dir = options.direction || new Point(1, 1);
    this.offset = options.offset || new Point(0, 0);

    var base = PIXI.BaseTexture.fromImage(url),
        slice = options.crop || new Rect(0, 0, base.width, base.height),
        tex = new PIXI.Texture(base, slice);

    this.sprite = new PIXI.Sprite(tex);

    this.render();
  }

  Frame.prototype.render = function() {
    var gPos = this.sprite.position,
        gScale = this.sprite.scale;

    gPos.x = Math.round(this.pos.x + this.offset.x);
    gPos.y = Math.round(this.pos.y + this.offset.y);

    gScale.x = this.dir.x;
    gScale.y = this.dir.y;

    /*
     * Flipping will result in the sprite appearing to jump (flips on the 0,
     * rather than mid-sprite), so subtract the sprite's size from its position
     * if it's flipped.
     */

    if(gScale.x < 0) gPos.x -= this.sprite.width;
    if(gScale.y < 0) gPos.y -= this.sprite.height;
  };

  exports.Frame = Frame;

}(PIXI, demo);

!function(PIXI, exports) {
  'use strict';

  var Point = exports.Point,
      Rect = exports.Rect;

  function Frame(position, url, options) {
    this.pos = position;
    this.dir = options.direction || new Point(1, 1);
    this.offset = options.offset || new Point(0, 0);

    var base = PIXI.BaseTexture.fromImage(url, false, PIXI.scaleModes.NEAREST),
        slice = options.crop || new Rect(0, 0, base.width, base.height),
        tex = new PIXI.Texture(base, slice);

    this.sprite = new PIXI.Sprite(tex);

    this.render();
  }

  Frame.prototype.render = function() {
    var gPos = this.sprite.position,
        gScale = this.sprite.scale;

    gPos.x = Math.round(this.pos.x + this.offset.x);
    gPos.y = Math.round(this.pos.y + this.offset.y);

    gScale.x = this.dir.x;
    gScale.y = this.dir.y;

    /*
     * Flipping will result in the sprite appearing to jump (flips on the 0,
     * rather than mid-sprite), so subtract the sprite's size from its position
     * if it's flipped.
     */

    if(gScale.x < 0) gPos.x -= this.sprite.width;
    if(gScale.y < 0) gPos.y -= this.sprite.height;
  };

  exports.Frame = Frame;

}(PIXI, demo);

Use nearest neighbor scaling in demo renderer.


Meteor.methods({
  addContactUsMessage: function(message) {

    try {
      // synchronous (to the server) insert
      ContactUsMessages.insert({
        name: message.name,
        email: message.email,
        zipcode: message.zipcode,
        text: message.text,
        createdAt: new Date(),
        authorId: Meteor.userId(), // can be null, meaning a message from someone not logged in
      });

      // send an email to ourselves containing the message
      var text = 'name: ' + message.name +
        '\nemail: ' + message.email +
        '\nzipcode: ' + message.zipcode +
        '\ntext: ' + message.text;
      var email = {
        to: "GrowSocial Pilot Coordinator <growsocial.org@gmail.com>",
        from: "GrowSocial Pilot Website <email@growsocial.org>",
        subject: "GrowSocial pilot message - Contact Us",
        text: text,
      };
      // no need to do anything with email errors here, so pass empty callback
      Meteor.call('sendEmail', email, function() {});
      return; // undefined, i.e. a positive response to client/caller

    } catch (err) {
      console.log('caught err from insert, will add to error log, then rethrow to server & client');
      Meteor.call('addErrorLog', {
        tag: 'ContactUsMessages.insert',
        message: err.message + ', message: ' + EJSON.stringify(message).substring(1,300),
      });
      throw(err);
    }
  },
});

Meteor.methods({
  addContactUsMessage: function(message) {

    try {
      // synchronous (to the server) insert
      ContactUsMessages.insert({
        name: message.name,
        email: message.email,
        zipcode: message.zipcode,
        text: message.text,
        createdAt: new Date(),
        authorId: Meteor.userId(), // can be null, meaning a message from someone not logged in
      });

      // send an email to ourselves containing the message
      var text = 'name: ' + message.name +
        '\nemail: ' + message.email +
        '\nzipcode: ' + message.zipcode +
        '\ntext: ' + message.text;
      var email = {
        to: "GrowSocial Pilot Coordinator <growsocial.org@gmail.com>",
        from: "GrowSocial Pilot Website <growsocial.org@gmail.com>",
        subject: "GrowSocial pilot message - Contact Us",
        text: text,
      };
      // no need to do anything with email errors here, so pass empty callback
      Meteor.call('sendEmail', email, function() {});
      return; // undefined, i.e. a positive response to client/caller

    } catch (err) {
      console.log('caught err from insert, will add to error log, then rethrow to server & client');
      Meteor.call('addErrorLog', {
        tag: 'ContactUsMessages.insert',
        message: err.message + ', message: ' + EJSON.stringify(message).substring(1,300),
      });
      throw(err);
    }
  },
});

Use a real email in contactUs email from-field


module.exports = function(question, callback) {

	var apiai = require('apiai');
	var app = apiai("76ccb7c7acea4a6884834f6687475222", "8b3e68f16ac6430cb8c40d49c315aa7a");

		var request = app.textRequest(question);

		request.on('response', function(response) {
		    var kantSucks = require('./deontologyYo')(response);
		    if(kantSucks == 0)
		    	if(response.result.metadata.speech)
		    		callback(response.result.metadata.speech)
		    	else
		    		callback("Unfortunately, I'm not smart enough to answer that question yet.")
		    else if(kantSucks == 1)
		    	if(response.result.metadata.speech)
		    	callback("I'll check with that after I pass through my utility ethic function. Heres what I would say otherwise: " + response.result.metadata.speech);
		    else
		    	callback(kantSucks)
		});

		request.on('error', function(error) {
		    callback(error);
		});

		request.end()
};

module.exports = function(question, callback) {

	var apiai = require('apiai');
	var app = apiai("76ccb7c7acea4a6884834f6687475222", "8b3e68f16ac6430cb8c40d49c315aa7a");

		var request = app.textRequest(question);

		request.on('response', function(response) {
		    var kantSucks = require('./deontologyYo')(response);
		    if(kantSucks == 0)
		    	if(response.result.metadata.speech)
		    		callback(response.result.metadata.speech)
		    	else
		    		callback("Unfortunately, I'm not smart enough to answer that question yet.")
		    else if(kantSucks == 1)
		    	if(response.result.metadata.speech && response.result.metadata.speech != "undefined")
		    	callback("I'll check with that after I pass through my utility ethic function. Heres what I would say otherwise: " + response.result.metadata.speech);
		    else
		    	callback(kantSucks)
		});

		request.on('error', function(error) {
		    callback(error);
		});

		request.end()
};

Solve issue of blank/undefined answers.

Solve issue of blank/undefined answers.


// jshint camelcase: false
(function () {
	'use strict';

	// Make async
	window.__karma__.loaded = function () {};

	require.config({

		// Set baseUrl for Karma
		baseUrl: 'http://localhost:9876/base/js/',

		deps: ['config']
	});

	// Load the example spec, add your own specifications here
	require([
		'../test/specs/example.spec'
	], window.__karma__.start);
}());

// jshint camelcase: false
(function () {
	'use strict';

	// Make async
	window.__karma__.loaded = function () {};

	// Set the flag for test environment
	window.__test = true;

	// RequireJS Configuration
	require.config({

		// Set baseUrl for Karma
		baseUrl: 'http://localhost:9876/base/js/',

		deps: ['config']
	});

	// Load the example spec, add your own specifications here
	require([
		'../test/specs/example.spec'
	], window.__karma__.start);
}());

Set a `__test` flag when running tests with karma


import React from 'react';
import { storiesOf } from '@storybook/react';
import { select } from '@storybook/addon-knobs/react';
import { LoadingSpinner } from '../components';

const colors = ['neutral', 'mint', 'violet', 'ruby', 'gold', 'aqua', 'teal'];
const sizes = ['small', 'medium'];
const tints = ['lightest', 'light', 'normal', 'dark', 'darkest'];

storiesOf('Loading spinners', module).add('Basic', () => (
  <LoadingSpinner
    color={select('Color', colors, 'neutral')}
    size={select('Size', sizes, 'medium')}
    tint={select('Tint', tints, 'lightest')}
  />
));

import React from 'react';
import { storiesOf } from '@storybook/react';
import { select } from '@storybook/addon-knobs/react';
import { LoadingSpinner } from '../components';

const colors = ['neutral', 'mint', 'violet', 'ruby', 'gold', 'aqua', 'teal'];
const sizes = ['small', 'medium'];
const tints = ['lightest', 'light', 'normal', 'dark', 'darkest'];

storiesOf('Loading spinners', module).add('Basic', () => (
  <LoadingSpinner
    color={select('Color', colors, 'teal')}
    size={select('Size', sizes, 'medium')}
    tint={select('Tint', tints, 'darkest')}
  />
));

Update the default story props so they reflect the components default props and the spinner is not initially white


(function() {
  'use strict';

  var ApiConfig;

  ApiConfig = {
    'API_LOCALHOST': 'http://localhost:3000',
    'API_URL': 'https://minhaagenda-api.herokuapp.com'
  };

  angular
  .module('Core.config', [])
  .constant('ApiConfig', ApiConfig);

})();

(function() {
  'use strict';

  var ApiConfig;

  ApiConfig = {
    'API_URL': 'https://minhaagenda-api.herokuapp.com'
  };

  angular
  .module('Core.config', [])
  .constant('ApiConfig', ApiConfig);

})();

Remove a constante criada para api_localhost.


require( 'wTesting' );
const _ = require( 'wTools' );
_.include( 'wProcess' );
let c = 0;

//

_.process.on( 'exit', () =>
{
  debugger;
  c++;
  console.log( 'onExit :', c )
})

//

function onSuiteEnd()
{
  debugger;
  c++;
  console.log( 'onSuiteEnd :', c )
}

//

function routine1( test )
{
  test.identical( 1, 1 );
  return _.time.out( 200 );
}

//

function routine2( test )
{
  process.exit( 0 );
}

//

var Self1 =
{
  name : 'manualTermination',
  onSuiteEnd,
  tests :
  {
    routine1,
    routine2,
  }
}

//

Self1 = wTestSuite( Self1 );
if( typeof module !== 'undefined' && !module.parent )
wTester.test( Self1.name );

require( 'wTesting' );
// const _ = require( 'wTools' );
const _ = _globals_.testing.wTools;
_.include( 'wProcess' );
let c = 0;

//

_.process.on( 'exit', () =>
{
  debugger;
  c++;
  console.log( 'onExit :', c )
})

//

function onSuiteEnd()
{
  debugger;
  c++;
  console.log( 'onSuiteEnd :', c )
}

//

function routine1( test )
{
  test.identical( 1, 1 );
  return _.time.out( 200 );
}

//

function routine2( test )
{
  process.exit( 0 );
}

//

var Self1 =
{
  name : 'manualTermination',
  onSuiteEnd,
  tests :
  {
    routine1,
    routine2,
  }
}

//

Self1 = wTestSuite( Self1 );
if( typeof module !== 'undefined' && !module.parent )
wTester.test( Self1.name );

Improve asset `manualTermination`, use valid global instance of `wTools`