<commit_before>import {mount} from 'enzyme'
import React from 'react'
import {IntlStub} from 'tocco-test-util'
import MergeMatrix from './MergeMatrix'
import {HeaderRow, ToManyRelationRow, FieldRow, RelationRow} from './table-components'

describe('merge', () => {
  describe('components', () => {
    describe('MergeMatrix', () => {
      it('should load the matrix as a table with the desired amount of rows ', () => {
        const emptyFnc = () => {
        }

        const wrapper = mount(
          <MergeMatrix
            targetEntityPk="498"
            entities={testData.entities}
            model={testData.model}
            selections={{fields: {}, relations: {}, toManyRelations: {}}}
            changeTargetEntity={emptyFnc}
            selectSourceField={emptyFnc}
            selectSourceRelation={emptyFnc}
            toggleRelationMany={emptyFnc}
            intl={IntlStub}
          />
        )

        expect(wrapper.find('table')).to.have.length(1)
        expect(wrapper.find(HeaderRow)).to.have.length(1)
        expect(wrapper.find(FieldRow)).to.have.length(testData.model.fields.length)
        expect(wrapper.find(RelationRow)).to.have.length(testData.model.relations.filter(r => r.toMany).length)
        expect(wrapper.find(ToManyRelationRow)).to.have.length(testData.model.relations.filter(r => r.toMany).length)
      })
    })
  })
})

const testData = {
  entities: [
    {
      fields: {
        user_nr: {
          name: 'user_nr',
          type: 'counter',
          value: 62,
          writable: false
        },
        email: {
          name: 'email',
          type: 'email',
          value: 'mergi@test.ch',
          writable: true
        },
        callname: {
          name: 'callname',
          type: 'string',
          value: 'mergi',
          writable: true
        },
        birthdate: {
          name: 'birthdate',
          type: 'birthdate',
          value: '',
          writable: true
        },
        phone_company: {
          name: 'phone_company',
          type: 'phone',
          value: '+41445228899',
          writable: true
        },
        phone_mobile: {
          name: 'phone_mobile',
          type: 'phone',
          value: '+41796257841',
          writable: true
        },
        best_reached: {
          name: 'best_reached',
          type: 'string',
          value: 'Mittags',
          writable: true
        },
        c_address: {
          name: 'c_address',
          type: 'text',
          value: 'Tocco AG, Technology meets spirit\nRiedtlistrasse 27\n8006 Zürich',
          writable: false
        },
        b_address: {
          name: 'b_address',
          type: 'text',
          value: 'Tocco AG, Technology meets spirit\nRiedtlistrasse 27\n8006 Zürich',
          writable: false
        }
      },
      label: 'Test, Merge',
      pk: '498',
      relations: {
        relAcademic_title: {
          name: 'relAcademic_title',
          values: [
            {
              checked: true,
              label: 'Dr.',
              pk: '1'
            }
          ],
          writable: true
        },
        relUser_code2: {
          name: 'relUser_code2',
          values: [
            {
              checked: true,
              label: 'Lieferant',
              pk: '3'
            },
            {
              checked: false,
              label: 'Partner',
              pk: '4'
            },
            {
              checked: true,
              label: 'Mitarbeiter',
              pk: '2'
            }
          ],
          writable: true
        }
      }
    },
    {
      fields: {
        user_nr: {
          name: 'user_nr',
          type: 'counter',
          value: 68,
          writable: false
        },
        email: {
          name: 'email',
          type: 'email',
          value: 'test@test.merge.ch',
          writable: true
        },
        callname: {
          name: 'callname',
          type: 'string',
          value: '',
          writable: true
        },
        birthdate: {
          name: 'birthdate',
          type: 'birthdate',
          value: '1980-01-31T23:00:00.000Z',
          writable: true
        },
        phone_company: {
          name: 'phone_company',
          type: 'phone',
          value: '',
          writable: true
        },
        phone_mobile: {
          name: 'phone_mobile',
          type: 'phone',
          value: '',
          writable: true
        },
        best_reached: {
          name: 'best_reached',
          type: 'string',
          value: '',
          writable: true
        },
        c_address: {
          name: 'c_address',
          type: 'text',
          value: '',
          writable: false
        },
        b_address: {
          name: 'b_address',
          type: 'text',
          value: '',
          writable: false
        }
      },
      label: 'Test, Merge',
      pk: '504',
      relations: {
        relAcademic_title: {
          name: 'relAcademic_title',
          values: [],
          writable: true
        },
        relUser_code2: {
          name: 'relUser_code2',
          values: [
            {
              checked: false,
              label: 'Mitarbeiter',
              pk: '2'
            },
            {
              checked: false,
              label: 'Partner',
              pk: '4'
            },
            {
              checked: false,
              label: 'Lieferant',
              pk: '3'
            }
          ],
          writable: true
        }
      }
    },
    {
      fields: {
        user_nr: {
          name: 'user_nr',
          type: 'counter',
          value: 65,
          writable: false
        },
        email: {
          name: 'email',
          type: 'email',
          value: '',
          writable: true
        },
        callname: {
          name: 'callname',
          type: 'string',
          value: '',
          writable: true
        },
        birthdate: {
          name: 'birthdate',
          type: 'birthdate',
          value: null,
          writable: true
        },
        phone_company: {
          name: 'phone_company',
          type: 'phone',
          value: '',
          writable: true
        },
        phone_mobile: {
          name: 'phone_mobile',
          type: 'phone',
          value: '+41445239842',
          writable: true
        },
        best_reached: {
          name: 'best_reached',
          type: 'string',
          value: 'Mittags',
          writable: true
        },
        c_address: {
          name: 'c_address',
          type: 'text',
          value: 'Digitec Galaxus AG\nPfingstweidstrasse 60\n8005 Zürich',
          writable: false
        },
        b_address: {
          name: 'b_address',
          type: 'text',
          value: 'Digitec Galaxus AG\nPfingstweidstrasse 60\n8005 Zürich',
          writable: false
        }
      },
      label: 'Test, Merge',
      pk: '501',
      relations: {
        relAcademic_title: {
          name: 'relAcademic_title',
          values: [
            {
              checked: true,
              label: 'Prof. Dr.',
              pk: '3'
            }
          ],
          writable: true
        },
        relUser_code2: {
          name: 'relUser_code2',
          values: [
            {
              checked: true,
              label: 'Partner',
              pk: '4'
            },
            {
              checked: false,
              label: 'Mitarbeiter',
              pk: '2'
            },
            {
              checked: false,
              label: 'Lieferant',
              pk: '3'
            }
          ],
          writable: true
        }
      }
    }
  ],
  model: {
    fields: [
      {
        label: 'Personen-Nr.',
        name: 'user_nr'
      },
      {
        label: 'E-Mail',
        name: 'email'
      },
      {
        label: 'Rufname',
        name: 'callname'
      },
      {
        label: 'Geburtsdatum',
        name: 'birthdate'
      },
      {
        label: 'Telefon G',
        name: 'phone_company'
      },
      {
        label: 'Telefon M',
        name: 'phone_mobile'
      },
      {
        label: 'Beste Erreichbarkeit',
        name: 'best_reached'
      },
      {
        label: 'Korresp.-Adresse',
        name: 'c_address'
      },
      {
        label: 'Rg.-Adresse',
        name: 'b_address'
      }
    ],
    modelName: 'User',
    relations: [
      {
        label: 'Akad. Titel',
        name: 'relAcademic_title',
        toMany: false
      },
      {
        label: 'Personen-Code 2',
        name: 'relUser_code2',
        toMany: true
      }
    ]
  }
}
<commit_msg>test(merge): fix test after refactoring<commit_after>import {shallow} from 'enzyme'
import React from 'react'
import {IntlStub} from 'tocco-test-util'
import MergeMatrix from './MergeMatrix'
import {HeaderRow, ToManyRelationRow, FieldRow, RelationRow} from './table-components'

describe('merge', () => {
  describe('components', () => {
    describe('MergeMatrix', () => {
      it('should load the matrix as a table with the desired amount of rows ', () => {
        const emptyFnc = () => {
        }

        const wrapper = shallow(
          <MergeMatrix
            intl={IntlStub}
            targetEntityPk="498"
            entities={testData.entities}
            model={testData.model}
            selections={{fields: {}, relations: {}, toManyRelations: {}}}
            changeTargetEntity={emptyFnc}
            selectSourceField={emptyFnc}
            selectSourceRelation={emptyFnc}
            toggleRelationMany={emptyFnc}
          />
        )

        expect(wrapper.find('table')).to.have.length(1)
        expect(wrapper.find(HeaderRow)).to.have.length(1)
        expect(wrapper.find(FieldRow)).to.have.length(testData.model.fields.length)
        expect(wrapper.find(RelationRow)).to.have.length(testData.model.relations.filter(r => r.toMany).length)
        expect(wrapper.find(ToManyRelationRow)).to.have.length(testData.model.relations.filter(r => r.toMany).length)
      })
    })
  })
})

const testData = {
  entities: [
    {
      fields: {
        user_nr: {
          name: 'user_nr',
          type: 'counter',
          value: 62,
          writable: false
        },
        email: {
          name: 'email',
          type: 'email',
          value: 'mergi@test.ch',
          writable: true
        },
        callname: {
          name: 'callname',
          type: 'string',
          value: 'mergi',
          writable: true
        },
        birthdate: {
          name: 'birthdate',
          type: 'birthdate',
          value: '',
          writable: true
        },
        phone_company: {
          name: 'phone_company',
          type: 'phone',
          value: '+41445228899',
          writable: true
        },
        phone_mobile: {
          name: 'phone_mobile',
          type: 'phone',
          value: '+41796257841',
          writable: true
        },
        best_reached: {
          name: 'best_reached',
          type: 'string',
          value: 'Mittags',
          writable: true
        },
        c_address: {
          name: 'c_address',
          type: 'text',
          value: 'Tocco AG, Technology meets spirit\nRiedtlistrasse 27\n8006 Zürich',
          writable: false
        },
        b_address: {
          name: 'b_address',
          type: 'text',
          value: 'Tocco AG, Technology meets spirit\nRiedtlistrasse 27\n8006 Zürich',
          writable: false
        }
      },
      label: 'Test, Merge',
      pk: '498',
      relations: {
        relAcademic_title: {
          name: 'relAcademic_title',
          values: [
            {
              checked: true,
              label: 'Dr.',
              pk: '1'
            }
          ],
          writable: true
        },
        relUser_code2: {
          name: 'relUser_code2',
          values: [
            {
              checked: true,
              label: 'Lieferant',
              pk: '3'
            },
            {
              checked: false,
              label: 'Partner',
              pk: '4'
            },
            {
              checked: true,
              label: 'Mitarbeiter',
              pk: '2'
            }
          ],
          writable: true
        }
      }
    },
    {
      fields: {
        user_nr: {
          name: 'user_nr',
          type: 'counter',
          value: 68,
          writable: false
        },
        email: {
          name: 'email',
          type: 'email',
          value: 'test@test.merge.ch',
          writable: true
        },
        callname: {
          name: 'callname',
          type: 'string',
          value: '',
          writable: true
        },
        birthdate: {
          name: 'birthdate',
          type: 'birthdate',
          value: '1980-01-31T23:00:00.000Z',
          writable: true
        },
        phone_company: {
          name: 'phone_company',
          type: 'phone',
          value: '',
          writable: true
        },
        phone_mobile: {
          name: 'phone_mobile',
          type: 'phone',
          value: '',
          writable: true
        },
        best_reached: {
          name: 'best_reached',
          type: 'string',
          value: '',
          writable: true
        },
        c_address: {
          name: 'c_address',
          type: 'text',
          value: '',
          writable: false
        },
        b_address: {
          name: 'b_address',
          type: 'text',
          value: '',
          writable: false
        }
      },
      label: 'Test, Merge',
      pk: '504',
      relations: {
        relAcademic_title: {
          name: 'relAcademic_title',
          values: [],
          writable: true
        },
        relUser_code2: {
          name: 'relUser_code2',
          values: [
            {
              checked: false,
              label: 'Mitarbeiter',
              pk: '2'
            },
            {
              checked: false,
              label: 'Partner',
              pk: '4'
            },
            {
              checked: false,
              label: 'Lieferant',
              pk: '3'
            }
          ],
          writable: true
        }
      }
    },
    {
      fields: {
        user_nr: {
          name: 'user_nr',
          type: 'counter',
          value: 65,
          writable: false
        },
        email: {
          name: 'email',
          type: 'email',
          value: '',
          writable: true
        },
        callname: {
          name: 'callname',
          type: 'string',
          value: '',
          writable: true
        },
        birthdate: {
          name: 'birthdate',
          type: 'birthdate',
          value: null,
          writable: true
        },
        phone_company: {
          name: 'phone_company',
          type: 'phone',
          value: '',
          writable: true
        },
        phone_mobile: {
          name: 'phone_mobile',
          type: 'phone',
          value: '+41445239842',
          writable: true
        },
        best_reached: {
          name: 'best_reached',
          type: 'string',
          value: 'Mittags',
          writable: true
        },
        c_address: {
          name: 'c_address',
          type: 'text',
          value: 'Digitec Galaxus AG\nPfingstweidstrasse 60\n8005 Zürich',
          writable: false
        },
        b_address: {
          name: 'b_address',
          type: 'text',
          value: 'Digitec Galaxus AG\nPfingstweidstrasse 60\n8005 Zürich',
          writable: false
        }
      },
      label: 'Test, Merge',
      pk: '501',
      relations: {
        relAcademic_title: {
          name: 'relAcademic_title',
          values: [
            {
              checked: true,
              label: 'Prof. Dr.',
              pk: '3'
            }
          ],
          writable: true
        },
        relUser_code2: {
          name: 'relUser_code2',
          values: [
            {
              checked: true,
              label: 'Partner',
              pk: '4'
            },
            {
              checked: false,
              label: 'Mitarbeiter',
              pk: '2'
            },
            {
              checked: false,
              label: 'Lieferant',
              pk: '3'
            }
          ],
          writable: true
        }
      }
    }
  ],
  model: {
    fields: [
      {
        label: 'Personen-Nr.',
        name: 'user_nr'
      },
      {
        label: 'E-Mail',
        name: 'email'
      },
      {
        label: 'Rufname',
        name: 'callname'
      },
      {
        label: 'Geburtsdatum',
        name: 'birthdate'
      },
      {
        label: 'Telefon G',
        name: 'phone_company'
      },
      {
        label: 'Telefon M',
        name: 'phone_mobile'
      },
      {
        label: 'Beste Erreichbarkeit',
        name: 'best_reached'
      },
      {
        label: 'Korresp.-Adresse',
        name: 'c_address'
      },
      {
        label: 'Rg.-Adresse',
        name: 'b_address'
      }
    ],
    modelName: 'User',
    relations: [
      {
        label: 'Akad. Titel',
        name: 'relAcademic_title',
        toMany: false
      },
      {
        label: 'Personen-Code 2',
        name: 'relUser_code2',
        toMany: true
      }
    ]
  }
}
<|endoftext|>


<commit_before>//Copyright (c) 2010 Nicholas C. Zakas. All rights reserved.
//MIT License

function EventTarget(){
    this._listeners = {};
}

EventTarget.prototype = {

    constructor: EventTarget,

    addListener: function(type, listener){
        if (typeof this._listeners[type] == "undefined"){
            this._listeners[type] = [];
        }

        this._listeners[type].push(listener);
    },

    fire: function(event){
        if (typeof event == "string"){
            event = { type: event };
        }
        if (!event.target){
            event.target = this;
        }

        if (!event.type){  //falsy
            throw new Error("Event object missing 'type' property.");
        }

        if (this._listeners[event.type] instanceof Array){
            var listeners = this._listeners[event.type];
            for (var i=0, len=listeners.length; i < len; i++){
                listeners[i].call(this, event);
            }
        }
    },

    removeListener: function(type, listener){
        if (this._listeners[type] instanceof Array){
            var listeners = this._listeners[type];
            for (var i=0, len=listeners.length; i < len; i++){
                if (listeners[i] === listener){
                    listeners.splice(i, 1);
                    break;
                }
            }
        }
    }
};<commit_msg>Got rid of EventTarget.js.  I think it's obsolete.<commit_after><|endoftext|>


<commit_before>/* eslint-disable no-undef */
module.exports = {
  entry: {
    bundle: ['./ui/polyfills.js', './ui/index.js'], // force polyfills to be first
    student_report_pdf: './ui/student_report_pdf.js',
    rollbar: './ui/rollbar.js',
    sign_in: ['./ui/polyfills.js', './ui/sign_in.js']
  },
  module: {
    rules: [
      // Process JS with Babel.
      {
        test: /\.js$/,
        exclude: /(node_modules)/,
        use: {
          loader: 'babel-loader',
          options: {
            // This is a feature of `babel-loader` for webpack (not Babel itself).
            // It enables caching results in ./node_modules/.cache/babel-loader/
            // directory for faster rebuilds.
            cacheDirectory: true,

            // define config for Babel itself and its plugins there, not
            // in here.
            babelrc: true
          }
        }
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      }
    ]
  }
};
/* eslint-disable no-undef */
<commit_msg>turns out it was webpack<commit_after>/* eslint-disable no-undef */
module.exports = {
  target: ['web', 'es5'],
  entry: {
    bundle: ['./ui/polyfills.js', './ui/index.js'], // force polyfills to be first
    student_report_pdf: './ui/student_report_pdf.js',
    rollbar: './ui/rollbar.js',
    sign_in: ['./ui/polyfills.js', './ui/sign_in.js']
  },
  module: {
    rules: [
      // Process JS with Babel.
      {
        test: /\.js$/,
        exclude: /(node_modules)/,
        use: {
          loader: 'babel-loader',
          options: {
            // This is a feature of `babel-loader` for webpack (not Babel itself).
            // It enables caching results in ./node_modules/.cache/babel-loader/
            // directory for faster rebuilds.
            cacheDirectory: true,

            // define config for Babel itself and its plugins there, not
            // in here.
            babelrc: true
          }
        }
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      }
    ]
  }
};
/* eslint-disable no-undef */
<|endoftext|>


<commit_before>/* Croatian i18n for the jQuery UI date picker plugin. */
/* Written by Vjekoslav Nesek. */
jQuery(function($){
	$.datepicker.regional['hr'] = {
		closeText: 'Zatvori',
		prevText: '<',
		nextText: '>',
		currentText: 'Danas',
		monthNames: ['Siječanj','Veljača','Ožujak','Travanj','Svibanj','Lipani',
		'Srpanj','Kolovoz','Rujan','Listopad','Studeni','Prosinac'],
		monthNamesShort: ['Sij','Velj','Ožu','Tra','Svi','Lip',
		'Srp','Kol','Ruj','Lis','Stu','Pro'],
		dayNames: ['Nedjalja','Ponedjeljak','Utorak','Srijeda','Četvrtak','Petak','Subota'],
		dayNamesShort: ['Ned','Pon','Uto','Sri','Čet','Pet','Sub'],
		dayNamesMin: ['Ne','Po','Ut','Sr','Če','Pe','Su'],
		dateFormat: 'dd.mm.yy.', firstDay: 1,
		isRTL: false};
	$.datepicker.setDefaults($.datepicker.regional['hr']);
});<commit_msg>Datepicker: fixed #4362 Update Croation localization<commit_after>/* Croatian i18n for the jQuery UI date picker plugin. */
/* Written by Vjekoslav Nesek. */
jQuery(function($){
	$.datepicker.regional['hr'] = {
		closeText: 'Zatvori',
		prevText: '<',
		nextText: '>',
		currentText: 'Danas',
		monthNames: ['Siječanj','Veljača','Ožujak','Travanj','Svibanj','Lipanj',
		'Srpanj','Kolovoz','Rujan','Listopad','Studeni','Prosinac'],
		monthNamesShort: ['Sij','Velj','Ožu','Tra','Svi','Lip',
		'Srp','Kol','Ruj','Lis','Stu','Pro'],
		dayNames: ['Nedjelja','Ponedjeljak','Utorak','Srijeda','Četvrtak','Petak','Subota'],
		dayNamesShort: ['Ned','Pon','Uto','Sri','Čet','Pet','Sub'],
		dayNamesMin: ['Ne','Po','Ut','Sr','Če','Pe','Su'],
		dateFormat: 'dd.mm.yy.', firstDay: 1,
		isRTL: false};
	$.datepicker.setDefaults($.datepicker.regional['hr']);
});<|endoftext|>


<commit_before>/*
 * AdminPage
 *
 * This is the first thing users see of our AdminPage, at the '/' route
 *
 * NOTE: while this component should technically be a stateless functional
 * component (SFC), hot reloading does not currently support SFCs. If hot
 * reloading is not a neccessity for you then you can refactor it and remove
 * the linting exception.
 */

import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import { Switch, Route } from 'react-router-dom';
import { get, includes, isFunction, map, omit } from 'lodash';

import { pluginLoaded, updatePlugin } from 'containers/App/actions';
import { selectHasUserPlugin, selectPlugins } from 'containers/App/selectors';
import { hideNotification } from 'containers/NotificationProvider/actions';

// Design
import ComingSoonPage from 'containers/ComingSoonPage';
import Content from 'containers/Content';
import Header from 'components/Header/index';
import HomePage from 'containers/HomePage';
// import InstallPluginPage from 'containers/InstallPluginPage';
import LeftMenu from 'containers/LeftMenu';
import ListPluginsPage from 'containers/ListPluginsPage';
import Logout from 'components/Logout';
import NotFoundPage from 'containers/NotFoundPage';
import PluginPage from 'containers/PluginPage';

import auth from 'utils/auth';

import styles from './styles.scss';

export class AdminPage extends React.Component { // eslint-disable-line react/prefer-stateless-function
  state = { hasAlreadyRegistereOtherPlugins: false };

  getChildContext = () => (
    {
      plugins: this.props.plugins,
      updatePlugin: this.props.updatePlugin,
    }
  );

  componentDidMount() {
    this.checkLogin(this.props);
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.location.pathname !== this.props.location.pathname) {
      this.checkLogin(nextProps);
    }

    if (get(nextProps.plugins.toJS(), ['users-permissions', 'hasAdminUser']) !== get(this.props.plugins.toJS(), ['users-permissions', 'hasAdminUser'])) {
      this.checkLogin(nextProps);
    }
  }

  checkLogin = (props) => {
    if (props.hasUserPlugin && this.isUrlProtected(props) && !auth.getToken()) {
      const endPoint = this.hasAdminUser(props) ? 'login': 'register';
      this.props.history.push(`/plugins/users-permissions/auth/${endPoint}`);
    }

    if (!this.isUrlProtected(props) && includes(props.location.pathname, 'register') && this.hasAdminUser(props)) {
      this.props.history.push('/plugins/users-permissions/auth/login');
    }

    if (!props.hasUserPlugin || auth.getToken() && !this.state.hasAlreadyRegistereOtherPlugins) {
      map(omit(this.props.plugins.toJS(), ['users-permissions', 'email']), plugin => {
        switch (true) {
          case isFunction(plugin.bootstrap) && isFunction(plugin.pluginRequirements):
            plugin.pluginRequirements(plugin)
              .then(plugin => {
                return plugin.bootstrap(plugin);
              })
              .then(plugin => this.props.pluginLoaded(plugin));
            break;
          case isFunction(plugin.pluginRequirements):
            plugin.pluginRequirements(plugin).then(plugin => this.props.pluginLoaded(plugin));
            break;
          case isFunction(plugin.bootstrap):
            plugin.bootstrap(plugin).then(plugin => this.props.pluginLoaded(plugin));
            break;
          default:
        }
      });

      this.setState({ hasAlreadyRegistereOtherPlugins: true });
    }
  }

  hasAdminUser = (props) => get(props.plugins.toJS(), ['users-permissions', 'hasAdminUser']);

  isUrlProtected = (props) => !includes(props.location.pathname, get(props.plugins.toJS(), ['users-permissions', 'nonProtectedUrl']));

  showLeftMenu = () => !includes(this.props.location.pathname, 'users-permissions/auth/');

  render() {
    const leftMenu = this.showLeftMenu() ? <LeftMenu plugins={this.props.plugins} /> : '';
    const header = this.showLeftMenu() ?  : '';
    const style = this.showLeftMenu() ? {} : { width: '100%' };

    return (

        {leftMenu}
        { auth.getToken() && this.props.hasUserPlugin && this.isUrlProtected(this.props) ? (
          <Logout />
        ) : ''}

          {header}
          <Content {...this.props} showLeftMenu={this.showLeftMenu()}>
            <Switch>
              <Route path="/" component={HomePage} exact />
              <Route path="/plugins/:pluginId" component={PluginPage} />
              <Route path="/plugins" component={ComingSoonPage} />
              <Route path="/list-plugins" component={ListPluginsPage} exact />
              <Route path="/install-plugin" component={ComingSoonPage} exact />
              <Route path="/configuration" component={ComingSoonPage} exact />
              <Route path="" component={NotFoundPage} />
            </Switch>
          </Content>

    );
  }
}

AdminPage.childContextTypes = {
  plugins: PropTypes.object,
  updatePlugin: PropTypes.func,
};

AdminPage.contextTypes = {
  router: PropTypes.object.isRequired,
};

AdminPage.defaultProps = {
  hasUserPlugin: true,
};

AdminPage.propTypes = {
  hasUserPlugin: PropTypes.bool,
  history: PropTypes.object.isRequired,
  location: PropTypes.object.isRequired,
  pluginLoaded: PropTypes.func.isRequired,
  plugins: PropTypes.object.isRequired,
  updatePlugin: PropTypes.func.isRequired,
};

const mapStateToProps = createStructuredSelector({
  hasUserPlugin: selectHasUserPlugin(),
  plugins: selectPlugins(),
});

function mapDispatchToProps(dispatch) {
  return {
    onHideNotification: (id) => { dispatch(hideNotification(id)); },
    updatePlugin: (pluginId, updatedKey, updatedValue) => { dispatch(updatePlugin(pluginId, updatedKey, updatedValue)); },
    pluginLoaded: (plugin) => { dispatch(pluginLoaded(plugin)); },
    dispatch,
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(AdminPage);
<commit_msg>Handle Admin page 404<commit_after>/*
 * AdminPage
 *
 * This is the first thing users see of our AdminPage, at the '/' route
 *
 * NOTE: while this component should technically be a stateless functional
 * component (SFC), hot reloading does not currently support SFCs. If hot
 * reloading is not a neccessity for you then you can refactor it and remove
 * the linting exception.
 */

import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import { Switch, Route } from 'react-router-dom';
import { get, includes, isFunction, map, omit } from 'lodash';

import { pluginLoaded, updatePlugin } from 'containers/App/actions';
import { selectHasUserPlugin, selectPlugins } from 'containers/App/selectors';
import { hideNotification } from 'containers/NotificationProvider/actions';

// Design
import ComingSoonPage from 'containers/ComingSoonPage';
import Content from 'containers/Content';
import Header from 'components/Header/index';
import HomePage from 'containers/HomePage';
// import InstallPluginPage from 'containers/InstallPluginPage';
import LeftMenu from 'containers/LeftMenu';
import ListPluginsPage from 'containers/ListPluginsPage';
import Logout from 'components/Logout';
import NotFoundPage from 'containers/NotFoundPage';
import PluginPage from 'containers/PluginPage';

import auth from 'utils/auth';

import styles from './styles.scss';

export class AdminPage extends React.Component { // eslint-disable-line react/prefer-stateless-function
  state = { hasAlreadyRegistereOtherPlugins: false };

  getChildContext = () => (
    {
      plugins: this.props.plugins,
      updatePlugin: this.props.updatePlugin,
    }
  );

  componentDidMount() {
    this.checkLogin(this.props);
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.location.pathname !== this.props.location.pathname) {
      this.checkLogin(nextProps);
    }

    if (get(nextProps.plugins.toJS(), ['users-permissions', 'hasAdminUser']) !== get(this.props.plugins.toJS(), ['users-permissions', 'hasAdminUser'])) {
      this.checkLogin(nextProps);
    }
  }

  componentWillUpdate(nextProps) {
    if (includes(nextProps.location.pathname, 'plugins') && nextProps.location.pathname !== '/list-plugins') {
      const appPlugins = Object.keys(nextProps.plugins.toJS());
      const pluginPath = nextProps.location.pathname.split('/')[2];
      const shouldRedirect = appPlugins.filter(el => el === pluginPath).length === 0;

      if (shouldRedirect) {
        this.props.history.push('/404');
      }
    }
  }

  checkLogin = (props) => {
    if (props.hasUserPlugin && this.isUrlProtected(props) && !auth.getToken()) {
      const endPoint = this.hasAdminUser(props) ? 'login': 'register';
      this.props.history.push(`/plugins/users-permissions/auth/${endPoint}`);
    }

    if (!this.isUrlProtected(props) && includes(props.location.pathname, 'register') && this.hasAdminUser(props)) {
      this.props.history.push('/plugins/users-permissions/auth/login');
    }

    if (!props.hasUserPlugin || auth.getToken() && !this.state.hasAlreadyRegistereOtherPlugins) {
      map(omit(this.props.plugins.toJS(), ['users-permissions', 'email']), plugin => {
        switch (true) {
          case isFunction(plugin.bootstrap) && isFunction(plugin.pluginRequirements):
            plugin.pluginRequirements(plugin)
              .then(plugin => {
                return plugin.bootstrap(plugin);
              })
              .then(plugin => this.props.pluginLoaded(plugin));
            break;
          case isFunction(plugin.pluginRequirements):
            plugin.pluginRequirements(plugin).then(plugin => this.props.pluginLoaded(plugin));
            break;
          case isFunction(plugin.bootstrap):
            plugin.bootstrap(plugin).then(plugin => this.props.pluginLoaded(plugin));
            break;
          default:
        }
      });

      this.setState({ hasAlreadyRegistereOtherPlugins: true });
    }
  }

  hasAdminUser = (props) => get(props.plugins.toJS(), ['users-permissions', 'hasAdminUser']);

  isUrlProtected = (props) => !includes(props.location.pathname, get(props.plugins.toJS(), ['users-permissions', 'nonProtectedUrl']));

  showLeftMenu = () => !includes(this.props.location.pathname, 'users-permissions/auth/');

  render() {
    const leftMenu = this.showLeftMenu() ? <LeftMenu plugins={this.props.plugins} /> : '';
    const header = this.showLeftMenu() ?  : '';
    const style = this.showLeftMenu() ? {} : { width: '100%' };

    return (

        {leftMenu}
        { auth.getToken() && this.props.hasUserPlugin && this.isUrlProtected(this.props) ? (
          <Logout />
        ) : ''}

          {header}
          <Content {...this.props} showLeftMenu={this.showLeftMenu()}>
            <Switch>
              <Route path="/" component={HomePage} exact />
              <Route path="/plugins/:pluginId" component={PluginPage} />
              <Route path="/plugins" component={ComingSoonPage} />
              <Route path="/list-plugins" component={ListPluginsPage} exact />
              <Route path="/install-plugin" component={ComingSoonPage} exact />
              <Route path="/configuration" component={ComingSoonPage} exact />
              <Route path="" component={NotFoundPage} />
              <Route path="404" component={NotFoundPage} />
            </Switch>
          </Content>

    );
  }
}

AdminPage.childContextTypes = {
  plugins: PropTypes.object,
  updatePlugin: PropTypes.func,
};

AdminPage.contextTypes = {
  router: PropTypes.object.isRequired,
};

AdminPage.defaultProps = {
  hasUserPlugin: true,
};

AdminPage.propTypes = {
  hasUserPlugin: PropTypes.bool,
  history: PropTypes.object.isRequired,
  location: PropTypes.object.isRequired,
  pluginLoaded: PropTypes.func.isRequired,
  plugins: PropTypes.object.isRequired,
  updatePlugin: PropTypes.func.isRequired,
};

const mapStateToProps = createStructuredSelector({
  hasUserPlugin: selectHasUserPlugin(),
  plugins: selectPlugins(),
});

function mapDispatchToProps(dispatch) {
  return {
    onHideNotification: (id) => { dispatch(hideNotification(id)); },
    updatePlugin: (pluginId, updatedKey, updatedValue) => { dispatch(updatePlugin(pluginId, updatedKey, updatedValue)); },
    pluginLoaded: (plugin) => { dispatch(pluginLoaded(plugin)); },
    dispatch,
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(AdminPage);
<|endoftext|>


<commit_before>const crypto = require('crypto');

/**
 * Offers helper function for handling and verifying incoming requests
 * via the express web framework
 */
class WebhookHelperExpress {
  /**
   * Creates an instance of WebhookHelperExpress
   * @param {string} appSecret Facebook App Secret used to verify incoming
   *   webhook requests
   * @param {string} verifyToken used to verify Webhook setup()
  */
  constructor(appSecret, verifyToken) {
    this.appSecret = appSecret;
    this.verifyToken = verifyToken;
  }

  /**
   * Verify that GET request contains specified verifyToken
   * and responds with sent challenge
   * @param {object} req
   * @param {object} res
   * @param {string} verifyToken
   */
  verifyWebhookSetup(req, res, verifyToken) {
    if (
      req.query['hub.verify_token'] === (verifyToken || this.verifyToken) &&
      req.query['hub.mode'] === 'subscribe'
    ) {
      res.status(200).send(req.query['hub.challenge']);
    } else {
      res.sendStatus(403);
    }
  }

  /**
   * Verify if POST request really comes from Facebook.
   * The x-hub-signature header should contain a hash matching
   * a sha1 Hash created from the body with the Facebook App Secret as key
   * @param {object} req
   * @param {object} res
   * @param {buffer} buffer
   * @param {string} encoding
   */
  verifyWebhookRequest(req, res, buffer, encoding) {
    if (!this.appSecret) {
      return;
    } else {
      let expected = crypto
        .createHmac('sha1', this.appSecret)
        .update(buffer)
        .digest('hex');

      let received = req.headers['x-hub-signature'].split('sha1=')[1];
      if (received !== expected) {
        throw new Error('Failed verifying webhook request');
      }
    }
  }

  /**
   * Iterates over every entry and every message event in the request body and
   * dispatches the messageEventHandler for every message event
   * @param {object} req
   * @param {object} res
   * @param {function} messageEventHandler
   */
  handleWebhookEvent(req, res, messageEventHandler) {
    res.sendStatus(200);
    if (req.body) {
      req.body.entry.each(entry => {
        entry.messaging.each(messagingEvent => {
          messageEventHandler(messagingEvent);
        });
      });
    }
  }
}

module.exports = WebhookHelperExpress;
<commit_msg>replace .each with .forEach<commit_after>const crypto = require('crypto');

/**
 * Offers helper function for handling and verifying incoming requests
 * via the express web framework
 */
class WebhookHelperExpress {
  /**
   * Creates an instance of WebhookHelperExpress
   * @param {string} appSecret Facebook App Secret used to verify incoming
   *   webhook requests
   * @param {string} verifyToken used to verify Webhook setup()
  */
  constructor(appSecret, verifyToken) {
    this.appSecret = appSecret;
    this.verifyToken = verifyToken;
  }

  /**
   * Verify that GET request contains specified verifyToken
   * and responds with sent challenge
   * @param {object} req
   * @param {object} res
   * @param {string} verifyToken
   */
  verifyWebhookSetup(req, res, verifyToken) {
    if (
      req.query['hub.verify_token'] === (verifyToken || this.verifyToken) &&
      req.query['hub.mode'] === 'subscribe'
    ) {
      res.status(200).send(req.query['hub.challenge']);
    } else {
      res.sendStatus(403);
    }
  }

  /**
   * Verify if POST request really comes from Facebook.
   * The x-hub-signature header should contain a hash matching
   * a sha1 Hash created from the body with the Facebook App Secret as key
   * @param {object} req
   * @param {object} res
   * @param {buffer} buffer
   * @param {string} encoding
   */
  verifyWebhookRequest(req, res, buffer, encoding) {
    if (!this.appSecret) {
      return;
    } else {
      let expected = crypto
        .createHmac('sha1', this.appSecret)
        .update(buffer)
        .digest('hex');

      let received = req.headers['x-hub-signature'].split('sha1=')[1];
      if (received !== expected) {
        throw new Error('Failed verifying webhook request');
      }
    }
  }

  /**
   * Iterates over every entry and every message event in the request body and
   * dispatches the messageEventHandler for every message event
   * @param {object} req
   * @param {object} res
   * @param {function} messageEventHandler
   */
  handleWebhookEvent(req, res, messageEventHandler) {
    res.sendStatus(200);
    if (req.body.entry) {
      req.body.entry.forEach(entry => {
        entry.messaging.forEach(messagingEvent => {
          messageEventHandler(messagingEvent);
        });
      });
    }
  }
}

module.exports = WebhookHelperExpress;
<|endoftext|>


<commit_before>/*
 * jquery.simulate - simulate browser mouse and keyboard events
 *
 * Copyright (c) 2007 Eduardo Lundgren (eduardolundgren@gmail.com)
 * and Richard D. Worth (rdworth@gmail.com)
 *
 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
 *
 */

;(function($) {

$.fn.extend({
	simulate: function(type, options) {
		return this.each(function() {
			var opt = $.extend({}, $.simulate.defaults, options || {});
			new $.simulate(this, type, opt);
		});
	}
});

$.simulate = function(el, type, options) {
	this.target = el;
	this.options = options;

	if (/^drag$/i.test(type)) {
		this[type].apply(this, [this.target, options]);
	} else {
		this.simulateEvent(el, type, options);
	}
}

$.extend($.simulate.prototype, {
	simulateEvent: function(el, type, options) {
		// creates a individual option for each simulation inside this instance
		options = $.extend({}, this.options, options);
		var evt = this.createEvent(type, options);
		return this.dispatchEvent(el, type, evt, options);
	},
	createEvent: function(type, options) {
		if (/^mouse(over|out|down|up|move)|(dbl)?click$/i.test(type)) {
			return this.mouseEvent(type, options);
		} else if (/^key(up|down|press)$/i.test(type)) {
			return this.keyboardEvent(type, options);
		}
	},
	mouseEvent: function(type, options) {
		var evt;
		var e = $.extend({
			bubbles: true, cancelable: (type != "mousemove"), view: window, detail: 0,
			screenX: 0, screenY: 0, clientX: options.x || 0, clientY: options.y || 0,
			ctrlKey: false, altKey: false, shiftKey: false, metaKey: false,
			button: 0, relatedTarget: null
		}, options);

		var relatedTarget = $(e.relatedTarget)[0];

		if ($.isFunction(document.createEvent)) {
			evt = document.createEvent("MouseEvents");
			evt.initMouseEvent(type, e.bubbles, e.cancelable, e.view, e.detail,
				e.screenX, e.screenY, e.clientX, e.clientY,
				e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
				e.button, relatedTarget);

			// check to see if relatedTarget has been assigned
			if (relatedTarget && !evt.relatedTarget) {
				if (/^mouseout$/i.test(type)) evt.toElement = relatedTarget;
				else if (/^mouseover$/i.test(type)) evt.fromElement = relatedTarget;
			}

		} else if (document.createEventObject) {
			evt = document.createEventObject();
			$.extend(evt, e);

			// IE won't allow assignment to toElement or fromElement
			evt.relatedTarget = relatedTarget;

			// fix for 2 pixels bug from mousecords
			evt.pageX = options.x; evt.pageY = options.y;

			evt.button = { 0:1, 1:4, 2:2 }[evt.button] || evt.button;
		}
		return evt;
	},
	keyboardEvent: function(type, options) {
		var evt;

		var e = $.extend({ bubbles: true, cancelable: true, view: window,
			ctrlKey: false, altKey: false, shiftKey: false, metaKey: false,
			keyCode: 0, charCode: 0
		}, options);

		if ($.isFunction(document.createEvent)) {
			try {
				evt = document.createEvent("KeyEvents");
				evt.initKeyEvent(type, e.bubbles, e.cancelable, e.view,
					e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
					e.keyCode, e.charCode);
			} catch(err) {
				evt = document.createEvent("Events");
				evt.initEvent(type, e.bubbles, e.cancelable);
				$.extend(evt, { view: e.view,
					ctrlKey: e.ctrlKey, altKey: e.altKey, shiftKey: e.shiftKey, metaKey: e.metaKey,
					keyCode: e.keyCode, charCode: e.charCode
				});
			}
		} else if (document.createEventObject) {
			evt = document.createEventObject();
			$.extend(evt, e);
		}
		if ($.browser.msie || $.browser.opera) {
			evt.keyCode = (e.charCode > 0) ? e.charCode : e.keyCode;
			evt.charCode = undefined;
		}
		return evt;
	},

	dispatchEvent: function(el, type, evt, options) {
		if (el.dispatchEvent) {
			el.dispatchEvent(evt);
		} else if (el.fireEvent) {
			el.fireEvent('on' + type, evt);
		}
		// trigget complete for all events - not drag.
		this.triggerComplete(evt, options);
		return evt;
	},

	drag: function(el) {
		var self = this, center = this.findCenter(this.target),
			options = this.options,	x = center.x, y = center.y,
			dx = options.dx || 0, dy = options.dy || 0, target = this.target;

		var coord = { x: x, y: y, complete: null }, evt;
		this.simulateEvent(target, "mouseover", coord);
		this.simulateEvent(target, "mousedown", coord);

		var drag = function(x, y) {
			evt = self.simulateEvent(target, "mousemove", $.extend(coord, { x: x, y: y }));
			// triggering drag callback
			(self.options.drag && self.options.drag($.event.fix(evt)));
		};

		if (/^sync$/i.test(options.speed)) {
			// trigger synchronous simulation
			this.triggerSync(center, dx, dy, drag);
		}
		else {
			// trigger asynchronous simulation - animated
			this.triggerAsync(center, dx, dy, drag);
		}

		this.simulateEvent(target, "mouseup", coord);
		this.simulateEvent(target, "mouseout", coord);
		this.triggerComplete(evt, options);
	},

	triggerComplete: function(evt, options) {
		evt = $.event.fix(evt);
		(options.complete && options.complete(evt)); return evt;
	},

	triggerSync: function(center, dx, dy, fn) {
		var x = center.x, y = center.y, mdx = Math.abs(dx) || 0, mdy = Math.abs(dy) || 0;
		var range = Math.max(mdx, mdy), sigx = dx/mdx || 1, sigy = dy/mdy || 1;

		for (var dt = 1; dt <= range; dt++) {
			if (dt <= mdx) x = center.x + sigx*dt;
			if (dt <= mdy) y = center.y + sigy*dt;
			(fn && fn(x, y));
		}
	},

	triggerAsync: function(center, dx, dy, fn) {
		/*TODO*/
		// this method just have to call: (fn && fn(x, y));
		// return the x, y of the mousemove.
		// idea: $.animate({ step: function() { var x,y; (fn && fn(x, y)); } });
	},

	findCenter: function(el) {
		var el = $(this.target), o = el.offset();
		return {
			x: o.left + el.outerWidth() / 2,
			y: o.top + el.outerHeight() / 2
		};
	}
});

$.extend($.simulate, {
	defaults: {
		speed: 'sync'
	}
});

})(jQuery);
<commit_msg>Removed case insensitive checks: no method or event names are case insensitive<commit_after>/*
 * jquery.simulate - simulate browser mouse and keyboard events
 *
 * Copyright (c) 2007 Eduardo Lundgren (eduardolundgren@gmail.com)
 * and Richard D. Worth (rdworth@gmail.com)
 *
 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
 *
 */

;(function($) {

$.fn.extend({
	simulate: function(type, options) {
		return this.each(function() {
			var opt = $.extend({}, $.simulate.defaults, options || {});
			new $.simulate(this, type, opt);
		});
	}
});

$.simulate = function(el, type, options) {
	this.target = el;
	this.options = options;

	if (/^drag$/.test(type)) {
		this[type].apply(this, [this.target, options]);
	} else {
		this.simulateEvent(el, type, options);
	}
}

$.extend($.simulate.prototype, {
	simulateEvent: function(el, type, options) {
		// creates a individual option for each simulation inside this instance
		options = $.extend({}, this.options, options);
		var evt = this.createEvent(type, options);
		return this.dispatchEvent(el, type, evt, options);
	},
	createEvent: function(type, options) {
		if (/^mouse(over|out|down|up|move)|(dbl)?click$/.test(type)) {
			return this.mouseEvent(type, options);
		} else if (/^key(up|down|press)$/.test(type)) {
			return this.keyboardEvent(type, options);
		}
	},
	mouseEvent: function(type, options) {
		var evt;
		var e = $.extend({
			bubbles: true, cancelable: (type != "mousemove"), view: window, detail: 0,
			screenX: 0, screenY: 0, clientX: options.x || 0, clientY: options.y || 0,
			ctrlKey: false, altKey: false, shiftKey: false, metaKey: false,
			button: 0, relatedTarget: null
		}, options);

		var relatedTarget = $(e.relatedTarget)[0];

		if ($.isFunction(document.createEvent)) {
			evt = document.createEvent("MouseEvents");
			evt.initMouseEvent(type, e.bubbles, e.cancelable, e.view, e.detail,
				e.screenX, e.screenY, e.clientX, e.clientY,
				e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
				e.button, relatedTarget);

			// check to see if relatedTarget has been assigned
			if (relatedTarget && !evt.relatedTarget) {
				if (/^mouseout$/.test(type)) evt.toElement = relatedTarget;
				else if (/^mouseover$/.test(type)) evt.fromElement = relatedTarget;
			}

		} else if (document.createEventObject) {
			evt = document.createEventObject();
			$.extend(evt, e);

			// IE won't allow assignment to toElement or fromElement
			evt.relatedTarget = relatedTarget;

			// fix for 2 pixels bug from mousecords
			evt.pageX = options.x; evt.pageY = options.y;

			evt.button = { 0:1, 1:4, 2:2 }[evt.button] || evt.button;
		}
		return evt;
	},
	keyboardEvent: function(type, options) {
		var evt;

		var e = $.extend({ bubbles: true, cancelable: true, view: window,
			ctrlKey: false, altKey: false, shiftKey: false, metaKey: false,
			keyCode: 0, charCode: 0
		}, options);

		if ($.isFunction(document.createEvent)) {
			try {
				evt = document.createEvent("KeyEvents");
				evt.initKeyEvent(type, e.bubbles, e.cancelable, e.view,
					e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
					e.keyCode, e.charCode);
			} catch(err) {
				evt = document.createEvent("Events");
				evt.initEvent(type, e.bubbles, e.cancelable);
				$.extend(evt, { view: e.view,
					ctrlKey: e.ctrlKey, altKey: e.altKey, shiftKey: e.shiftKey, metaKey: e.metaKey,
					keyCode: e.keyCode, charCode: e.charCode
				});
			}
		} else if (document.createEventObject) {
			evt = document.createEventObject();
			$.extend(evt, e);
		}
		if ($.browser.msie || $.browser.opera) {
			evt.keyCode = (e.charCode > 0) ? e.charCode : e.keyCode;
			evt.charCode = undefined;
		}
		return evt;
	},

	dispatchEvent: function(el, type, evt, options) {
		if (el.dispatchEvent) {
			el.dispatchEvent(evt);
		} else if (el.fireEvent) {
			el.fireEvent('on' + type, evt);
		}
		// trigget complete for all events - not drag.
		this.triggerComplete(evt, options);
		return evt;
	},

	drag: function(el) {
		var self = this, center = this.findCenter(this.target),
			options = this.options,	x = center.x, y = center.y,
			dx = options.dx || 0, dy = options.dy || 0, target = this.target;

		var coord = { x: x, y: y, complete: null }, evt;
		this.simulateEvent(target, "mouseover", coord);
		this.simulateEvent(target, "mousedown", coord);

		var drag = function(x, y) {
			evt = self.simulateEvent(target, "mousemove", $.extend(coord, { x: x, y: y }));
			// triggering drag callback
			(self.options.drag && self.options.drag($.event.fix(evt)));
		};

		if (/^sync$/.test(options.speed)) {
			// trigger synchronous simulation
			this.triggerSync(center, dx, dy, drag);
		}
		else {
			// trigger asynchronous simulation - animated
			this.triggerAsync(center, dx, dy, drag);
		}

		this.simulateEvent(target, "mouseup", coord);
		this.simulateEvent(target, "mouseout", coord);
		this.triggerComplete(evt, options);
	},

	triggerComplete: function(evt, options) {
		evt = $.event.fix(evt);
		(options.complete && options.complete(evt)); return evt;
	},

	triggerSync: function(center, dx, dy, fn) {
		var x = center.x, y = center.y, mdx = Math.abs(dx) || 0, mdy = Math.abs(dy) || 0;
		var range = Math.max(mdx, mdy), sigx = dx/mdx || 1, sigy = dy/mdy || 1;

		for (var dt = 1; dt <= range; dt++) {
			if (dt <= mdx) x = center.x + sigx*dt;
			if (dt <= mdy) y = center.y + sigy*dt;
			(fn && fn(x, y));
		}
	},

	triggerAsync: function(center, dx, dy, fn) {
		/*TODO*/
		// this method just have to call: (fn && fn(x, y));
		// return the x, y of the mousemove.
		// idea: $.animate({ step: function() { var x,y; (fn && fn(x, y)); } });
	},

	findCenter: function(el) {
		var el = $(this.target), o = el.offset();
		return {
			x: o.left + el.outerWidth() / 2,
			y: o.top + el.outerHeight() / 2
		};
	}
});

$.extend($.simulate, {
	defaults: {
		speed: 'sync'
	}
});

})(jQuery);
<|endoftext|>


<commit_before>
export var canvas = d3.select("body").append("canvas")
  .attr("width", window.innerWidth)
  .attr("height", window.innerHeight)
  .style("opacity", 0)
  .style('background-color', '#EAEAEA');

canvas.node().getContext("webgl");

export var renderer = new THREE.WebGLRenderer({canvas: canvas.node(), antialias: true});

renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

export var camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 5000);
camera.position.z = 1000;

export var scene = new THREE.Scene();

export var light = new THREE.HemisphereLight('#ffffff', '#666666', 1.5);
scene.add(light);

export var stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.bottom = '45px';
document.body.appendChild(stats.domElement);

window.addEventListener('resize', onWindowResize, false);

function onWindowResize() {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize( window.innerWidth, window.innerHeight );
}

d3.select("#loading")
  .append("svg").attr({width: 100, height: 100})
  .style("margin-top", "-60px")
  .style("margin-left", "-20px")
  .append("circle").attr({cx: 50, cy: 50, r: 50}).style("opacity", 0)
  .transition().duration(6000).attr("r", 0).style("opacity", 1).remove();

d3.select("body")
  .append("div")
  .html('');

<commit_msg>re-position light<commit_after>
export var canvas = d3.select("body").append("canvas")
  .attr("width", window.innerWidth)
  .attr("height", window.innerHeight)
  .style("opacity", 0)
  .style('background-color', '#EAEAEA');

canvas.node().getContext("webgl");

export var renderer = new THREE.WebGLRenderer({canvas: canvas.node(), antialias: true});

renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

export var camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 5000);
camera.position.z = 1000;

export var scene = new THREE.Scene();

export var light = new THREE.HemisphereLight('#ffffff', '#666666', 1.5);
light.position.set(0, 3000, 0);
scene.add(light);

export var stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.bottom = '45px';
document.body.appendChild(stats.domElement);

window.addEventListener('resize', onWindowResize, false);

function onWindowResize() {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize( window.innerWidth, window.innerHeight );
}

d3.select("#loading")
  .append("svg").attr({width: 100, height: 100})
  .style("margin-top", "-60px")
  .style("margin-left", "-20px")
  .append("circle").attr({cx: 50, cy: 50, r: 50}).style("opacity", 0)
  .transition().duration(6000).attr("r", 0).style("opacity", 1).remove();

d3.select("body")
  .append("div")
  .html('');

<|endoftext|>


<commit_before>window.addEventListener('load', pageInit);

var container;
var camera, scene, renderer;

function pageInit() {

    container = document.createElement('div');
    document.body.appendChild(container);

    renderer = new THREE.WebGLRenderer();
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.shadowMap.enabled = true;
    renderer.shadowMap.type = THREE.PCFShadowMap;
    container.appendChild(renderer.domElement);

    camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 4500);
    camera.position.x = 200;
    camera.position.y = 200;
    camera.position.z = 200;
    // window.controls = new THREE.OrbitControls(camera, renderer.domElement);

    scene = new THREE.Scene();

    var size = 50;
    var hsize = size / 2;
    var positions = [
        [-hsize, -hsize, -hsize],
        [+hsize, -hsize, -hsize],
        [-hsize, +hsize, -hsize],
        [+hsize, +hsize, -hsize],

        [-hsize, -hsize, +hsize],
        [+hsize, -hsize, +hsize],
        [-hsize, +hsize, +hsize],
        [+hsize, +hsize, +hsize],
    ];

    for (var i = 0; i < positions.length; i++) {
        var geometry = new THREE.BoxGeometry(size, size, size);
        var mesh = new THREE.Mesh(geometry, new THREE.MeshPhongMaterial({ color: new THREE.Color(Math.random(), Math.random(), Math.random()) }));
        mesh.position.set(positions[i][0], positions[i][1], positions[i][2]);
        mesh.castShadow = true;
        mesh.receiveShadow = true;
        scene.add(mesh);
    }

    // adding light as the last object of this scene
    var light = new THREE.PointLight(0xffffff, 2, 1000);
    light.position.set(100, 100, 100);
    light.castShadow = true;
    light.shadow.mapSize.width  = 512;
    light.shadow.mapSize.height = 512;
    scene.add(light);

    var ambient = new THREE.AmbientLight( 0x222222 ); // soft white light
    scene.add( ambient );

    scene.background = new THREE.Color( 0x222222 );

    var obj = {
        t: 0,
        extension: 1.1,
        rotation: 2
    };

    window.addEventListener('keydown', function (e) {
        if (e.key == "k") {
            anime.remove(obj);
            anime({
                targets: obj,
                t: 1,
                elasticity: 650,
                duration: 2500,
                update: function (anim) {
                    for(var i = 0, l = scene.children.length - 2; i < l; i++) {
                        var t = obj.t;

                        var op = positions[i];
                        scene.children[i].position.set(op[0] + op[0] * t * obj.extension,
                                                       op[1] + op[1] * t * obj.extension,
                                                       op[2] + op[2] * t * obj.extension);

                        var axis = new THREE.Vector3(op[0], op[1], op[2]).normalize();
                        scene.children[i].setRotationFromAxisAngle(axis, obj.t * obj.rotation);
                    }
                }
            });
        }

        if (e.key == "l") {
            anime.remove(obj);
            anime({
                targets: obj,
                t: 0,
                easing: 'easeInOutCubic',
                duration: 400,
                update: function (anim) {
                    for(var i = 0, l = scene.children.length - 2; i < l; i++) {
                        var t = obj.t;

                        var op = positions[i];
                        scene.children[i].position.set(op[0] + op[0] * t * obj.extension,
                                                       op[1] + op[1] * t * obj.extension,
                                                       op[2] + op[2] * t * obj.extension);

                        var axis = new THREE.Vector3(op[0], op[1], op[2]).normalize();
                        scene.children[i].setRotationFromAxisAngle(axis, obj.t * obj.rotation);
                    }
                }
            });
        }
    });

    animate(0);
}

function animate(now) {
    requestAnimationFrame(animate);

    camera.lookAt(scene.position);
    // controls.update();
    renderer.render(scene, camera);
}<commit_msg>code update<commit_after>window.addEventListener('load', pageInit);

var container;
var camera, scene, renderer;

function pageInit() {

    container = document.createElement('div');
    document.body.appendChild(container);

    renderer = new THREE.WebGLRenderer();
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.shadowMap.enabled = true;
    renderer.shadowMap.type = THREE.PCFShadowMap;
    container.appendChild(renderer.domElement);

    camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 4500);
    camera.position.x = 200;
    camera.position.y = 200;
    camera.position.z = 200;
    window.controls = new THREE.OrbitControls(camera, renderer.domElement);

    scene = new THREE.Scene();

    var size = 50;
    var hsize = size / 1.8;
    var positions = [
        [-hsize, -hsize, -hsize],
        [+hsize, -hsize, -hsize],
        [-hsize, +hsize, -hsize],
        [+hsize, +hsize, -hsize],

        [-hsize, -hsize, +hsize],
        [+hsize, -hsize, +hsize],
        [-hsize, +hsize, +hsize],
        [+hsize, +hsize, +hsize],
    ];

    for (var i = 0; i < positions.length; i++) {
        var r = Math.random();
        var gb = Math.random();
        r = r < gb ? gb : r;

        var geometry = new THREE.BoxGeometry(size, size, size);
        var mesh = new THREE.Mesh(geometry, new THREE.MeshPhongMaterial({ color: new THREE.Color(r, gb, gb), transparent: true, opacity: 1 }));
        mesh.position.set(positions[i][0], positions[i][1], positions[i][2]);
        mesh.castShadow = true;
        mesh.receiveShadow = true;
        scene.add(mesh);
    }

    // adding light as the last object of this scene
    var light = new THREE.PointLight(0xffffff, 2, 1000);
    light.position.set(100, 100, 100);
    light.castShadow = true;
    light.shadow.mapSize.width  = 512;
    light.shadow.mapSize.height = 512;
    scene.add(light);

    var ambient = new THREE.AmbientLight( 0x222222 ); // soft white light
    scene.add( ambient );

    scene.background = new THREE.Color( 0x222222 );

    var obj = {
        t: 0,
        extension: 1.1,
        rotation: 2
    };

    window.addEventListener('keydown', function (e) {
        if (e.key == "k") {
            anime.remove(obj);
            anime({
                targets: obj,
                t: 1,
                elasticity: 650,
                duration: 2500,
                update: function (anim) {
                    for(var i = 0, l = scene.children.length - 2; i < l; i++) {
                        var t = obj.t;

                        var op = positions[i];
                        scene.children[i].position.set(op[0] + op[0] * t * obj.extension,
                                                       op[1] + op[1] * t * obj.extension,
                                                       op[2] + op[2] * t * obj.extension);

                        var axis = new THREE.Vector3(op[0], op[1], op[2]).normalize();
                        scene.children[i].setRotationFromAxisAngle(axis, obj.t * obj.rotation);
                    }
                }
            });
        }

        if (e.key == "l") {
            anime.remove(obj);
            anime({
                targets: obj,
                t: 0,
                easing: 'easeInOutCubic',
                duration: 400,
                update: function (anim) {
                    for(var i = 0, l = scene.children.length - 2; i < l; i++) {
                        var t = obj.t;

                        var op = positions[i];
                        scene.children[i].position.set(op[0] + op[0] * t * obj.extension,
                                                       op[1] + op[1] * t * obj.extension,
                                                       op[2] + op[2] * t * obj.extension);

                        var axis = new THREE.Vector3(op[0], op[1], op[2]).normalize();
                        scene.children[i].setRotationFromAxisAngle(axis, obj.t * obj.rotation);
                    }
                }
            });
        }

        if (e.key == "c") {
            var y = 0;
            var vec = new THREE.Vector3();
            vec.copy(scene.children[3].position);
            console.log(vec);

            var lookAtVector = new THREE.Vector3(camera.matrix.elements[8], camera.matrix.elements[9], camera.matrix.elements[10]);
            lookAtVector.normalize();

            var cameraX = new THREE.Vector3(camera.matrix.elements[0], camera.matrix.elements[1], camera.matrix.elements[2]);
            cameraX.normalize();

            var cameray = new THREE.Vector3(camera.matrix.elements[4], camera.matrix.elements[5], camera.matrix.elements[6]);
            cameray.normalize();

            console.log(lookAtVector);
            console.log(cameraX);
            console.log(cameray);

            // vec.add(cameraX);
            scene.children[3].position.add(cameraX);
        }
    });

    animate(0);
}

function animate(now) {
    requestAnimationFrame(animate);

    camera.lookAt(scene.position);
    controls.update();
    renderer.render(scene, camera);
}<|endoftext|>


<commit_before>(function ($, window, undefined) {

/**
 * @module Assets-tools
 */

/**
 * Implements an input field that send search string to Amazon Product store and retrieve top ten relevant products
 * @class Assets Amazon tool
 * @constructor
 * @param {Object} [options] options to pass to this tool, so far nothing
 * @return {Q.Tool}
 */

Q.Tool.define("Assets/amazon/preview", "Streams/preview", function(options, preview) {

  if (!Q.Users.loggedInUser) {
      tool.element.style.display = 'none';
      console.warn("Assets/amazon: Don't render tool when user is not logged in");
      return;
  }

  var ps = preview.state;
  this.preview = preview;

  // proceed to construct the tool
  var tool = this;
  var state = tool.state;
  var $te = $(tool.element);

  // draw the tool
  ps.onRefresh.add(this.refresh.bind(this));

  $te.on(Q.Pointer.fastclick, '.Assets_amazon_results_item', tool, function () {

      var stream = tool.preview.stream;

      var asin = $(this).attr('data-asin');
      var title = $(this).attr('data-title');

      stream.fields.title = title;
      stream.set({'asin': asin, 'title': title});
      stream.save();

      console.log(stream);

      tool.filter = Q.Tool.from(tool.$('.Q_filter_tool'), 'Q/filter');
      tool.filter.input = tool.filter.$('.Q_filter_input');
      tool.filter.input.attr('value', '');
      tool.filter.input.next().show();
      tool.filter.end();
  }
  );

},

{ // DEFAULT OPTIONS
  onChoose: new Q.Event(),
  onSelect: new Q.Event(),
  onFilter: new Q.Event(),
  onCreate: new Q.Event(),
  onUpdate: new Q.Event(),
  onRefresh: new Q.Event()
},

{ // YOUR TOOL'S METHODS

  refresh: function (callback) {
    var tool = this;
    var state = tool.state;
    var $te = $(tool.element);

    var stream = tool.stream = tool.preview.stream;

    $items = tool.$('[data-assets-amazon], .Assets_amazon_preview_tool');

    Q.Template.render(
        'Assets/amazon/response/wishlist',
        $items,
        function (err, html) {

            if (err) {
                return;
            }

            var options = {};

            var filterAmazon = Q.Tool.setUpElement(
                'div',
                'Q/filter',
                options,
                tool.prefix + 'filter'
            );

            Q.replace(tool.element, null);

            $(filterAmazon)
                .appendTo($te)
                .activate(
                    function () {

                        var tool = this;
                        var state = tool.state;

                        state.onFilter.set(
                          function (query, element) {

                              var latest = Q.latest(tool);

                              Q.req("Assets/amazon", 'results',
                                function (err, response) {

                                  if (Q.latest(tool, latest)) {

                                    var msg = Q.firstErrorMessage(err, response && response.errors);
                                    if (msg) {
                                        return console.warn(msg);
                                    }

                                    var results = response.slots.results;
                                    var fields = {results: ''};
                                    if (results) {
                                        fields = {results: results};
                                    }

                                    Q.Template.render(
                                        'Assets/amazon/response/results',
                                        fields,
                                        function (err, html) {
                                            $(element).html(html);
                                        }
                                    );
                                  }
                                },
                                {
                                  fields: {
                                      input: query
                                  }
                              });

                          }, tool);
                });

            $te.append(html);
            });
  },

  // optional methods for your tool
  // that would be called by Qbix
  Q: {
    onInit: function () {
      // occurs after onInit
      // of all child tools
    },
    beforeRemove: function () {
      // clean up anything you've attached
      // to the elements, such as
      // jQuery event handlers, data, etc.
    },
    onRetain: function (newOptions) {
      // compare newOptions to this.state
      // and update the tool's appearance.
      // after this event, the tool's
      // state will be extended with
      // the new options.
    },
    onLayout: function (elem, container) {
      // Occurs if the layout is being
      // updated for this tool's element.
      // If you want more fine-grained control
      // then use Q.onLayout(element) instead.
    }
  }
});

Q.Template.set('Assets/amazon/response/wishlist',
      ''
    + '{{&tool "Streams/inplace" stream=stream inplaceType="text" inplace-placeholder="Name of the good" attribute="asin"}}'

);

Q.Template.set('Assets/amazon/response/results',
    '{{#if results}}'
    + '
'
    + '{{#each results}}'
    + '
'
    + ''
    + '
'
    + '
{{title}}
'
    + '{{price}}'
    + '
'
    + '
'
    + '
'
    + '{{/each}}'
    + '
'
    + '{{/if}}'
);

})(window.jQuery, window);<commit_msg>debug version for check template render options for Sreams/inplace<commit_after>(function ($, window, undefined) {

/**
 * @module Assets-tools
 */

/**
 * Implements an input field that send search string to Amazon Product store and retrieve top ten relevant products
 * @class Assets Amazon tool
 * @constructor
 * @param {Object} [options] options to pass to this tool, so far nothing
 * @return {Q.Tool}
 */

Handlebars.registerHelper("debug", function(optionalValue) {
  console.log("Current Context");
  console.log("====================");
  console.log(this);

  if (optionalValue) {
    console.log("Value");
    console.log("====================");
    console.log(optionalValue);
  }
});

Q.Tool.define("Assets/amazon/preview", "Streams/preview", function(options, preview) {

  if (!Q.Users.loggedInUser) {
      tool.element.style.display = 'none';
      console.warn("Assets/amazon: Don't render tool when user is not logged in");
      return;
  }

  var ps = preview.state;
  this.preview = preview;

  // proceed to construct the tool
  var tool = this;
  var state = tool.state;
  var $te = $(tool.element);

  // draw the tool
  ps.onRefresh.add(this.refresh.bind(this));

  $te.on(Q.Pointer.fastclick, '.Assets_amazon_results_item', tool, function () {

      var stream = tool.preview.stream;

      var asin = $(this).attr('data-asin');
      var title = $(this).attr('data-title');

      stream.fields.title = title;
      stream.set({'asin': asin, 'title': title});
      stream.save();

      console.log(stream);

      tool.filter = Q.Tool.from(tool.$('.Q_filter_tool'), 'Q/filter');
      tool.filter.input = tool.filter.$('.Q_filter_input');
      tool.filter.input.attr('value', '');
      tool.filter.input.next().show();
      tool.filter.end();
  }
  );

},

{ // DEFAULT OPTIONS
  onChoose: new Q.Event(),
  onSelect: new Q.Event(),
  onFilter: new Q.Event(),
  onCreate: new Q.Event(),
  onUpdate: new Q.Event(),
  onRefresh: new Q.Event()
},

{ // YOUR TOOL'S METHODS

  refresh: function (callback) {
    var tool = this;
    var state = tool.state;
    var $te = $(tool.element);

    var stream = tool.stream = tool.state.stream = tool.preview.stream;

    $items = tool.$('[data-assets-amazon], .Assets_amazon_preview_tool');

    Q.Template.render(
        'Assets/amazon/response/wishlist',
        $items,
        function (err, html) {

            if (err) {
                return;
            }

            var options = {};

            var filterAmazon = Q.Tool.setUpElement(
                'div',
                'Q/filter',
                options,
                tool.prefix + 'filter'
            );

            Q.replace(tool.element, null);

            $(filterAmazon)
                .appendTo($te)
                .activate(
                    function () {

                        var tool = this;
                        var state = tool.state;

                        state.onFilter.set(
                          function (query, element) {

                              var latest = Q.latest(tool);

                              Q.req("Assets/amazon", 'results',
                                function (err, response) {

                                  if (Q.latest(tool, latest)) {

                                    var msg = Q.firstErrorMessage(err, response && response.errors);
                                    if (msg) {
                                        return console.warn(msg);
                                    }

                                    var results = response.slots.results;
                                    var fields = {results: ''};
                                    if (results) {
                                        fields = {results: results};
                                    }

                                    Q.Template.render(
                                        'Assets/amazon/response/results',
                                        fields,
                                        function (err, html) {
                                            $(element).html(html);
                                        }
                                    );
                                  }
                                },
                                {
                                  fields: {
                                      input: query
                                  }
                              });

                          }, tool);
                });

            $te.append(html);
            });
  },

  // optional methods for your tool
  // that would be called by Qbix
  Q: {
    onInit: function () {
      // occurs after onInit
      // of all child tools
    },
    beforeRemove: function () {
      // clean up anything you've attached
      // to the elements, such as
      // jQuery event handlers, data, etc.
    },
    onRetain: function (newOptions) {
      // compare newOptions to this.state
      // and update the tool's appearance.
      // after this event, the tool's
      // state will be extended with
      // the new options.
    },
    onLayout: function (elem, container) {
      // Occurs if the layout is being
      // updated for this tool's element.
      // If you want more fine-grained control
      // then use Q.onLayout(element) instead.
    }
  }
});

Q.Template.set('Assets/amazon/response/wishlist',
      ''
    + '
{{debug this}}
'
    + '{{&tool "Streams/inplace" publisherId=tool.stream.publisherId streamName=tool.stream.streamName inplaceType="text" inplace-placeholder="Name of the good" attribute="asin"}}'

//    + '{{&tool "Streams/inplace" inplaceType="text" inplace-placeholder="Name of the wish" attribute="title"}}
'
//    + '{{&tool "Streams/inplace" publisherId=publisherId streamName=streamName inplaceType="text" inplace-placeholder="Name of the good"}}'
//    + '
{{&tool "Streams/inplace" publisherId=publisherId streamName=streamName inplaceType="text" inplace-placeholder="Name of the good" field="asin"}}
'
);

Q.Template.set('Assets/amazon/response/results',
    '{{#if results}}'
    + '
'
    + '{{#each results}}'
    + '
'
    + ''
    + '
'
    + '
{{title}}
'
    + '{{price}}'
    + '
'
    + '
'
    + '
'
    + '{{/each}}'
    + '
'
    + '{{/if}}'
);

})(window.jQuery, window);<|endoftext|>


<commit_before>var exercise      = require('workshopper-exercise')()
  , filecheck     = require('workshopper-exercise/filecheck')
  , execute       = require('workshopper-exercise/execute')
  , comparestdout = require('workshopper-exercise/comparestdout')
  , path          = require('path')
  , fs = require('fs')

// checks that the submission file actually exists
exercise = filecheck(exercise)

// add setup.
exercise.addSetup(function (mode, callback) {

    this.solutionModule = require(getSolutionPath() + 'solution.js');
    this.submissionModule = require([process.cwd(), this.args[0]].join('/'));
    process.nextTick(callback);
});

// add a processor.
exercise.addProcessor(function (mode, callback) {
    var pass = true;
    var update = function(name, age, tShirtSize){
        this.name = name;
        this.age = age;
        this.tShirtSize = tShirtSize;
    };
    var personForSolutionCall = {name: "Brij", age: 28, tShirtSize: 'L'};
    var personForSubmissionCall = {name: "Brij", age: 28, tShirtSize: 'L'};

    var callerSolutionResult = this.solutionModule.caller(personForSolutionCall, update, 'Kishor', 29, 'XL');
    var callerSubmissionResult = this.submissionModule.caller(personForSubmissionCall, update, 'Kishor', 29, 'XL');

    if (personForSubmissionCall.name !== personForSolutionCall.name || personForSubmissionCall.age !== personForSolutionCall.age || personForSubmissionCall.tShirtSize !== personForSolutionCall.tShirtSize) {
        exercise.emit('fail', 'Call method result in error. \nExpected result: ' + personForSolutionCall + ' \nActual result: '+ personForSubmissionCall);
        pass = false;
    }

    var personForSolutionApply = {name: "Kishor", age: 24, tShirtSize: 'S'};
    var personForSubmissionApply = {name: "Kishor", age: 24, tShirtSize: 'S'};

    var applierSolutionResult = this.solutionModule.applier(personForSolutionApply, update, ['Brij', 26, 'M']);
    var applierSubmissionResult = this.submissionModule.applier(personForSubmissionApply, update, ['Brij', 26, 'M']);
    if (personForSubmissionApply.name !== personForSolutionApply.name || personForSubmissionApply.age !== personForSolutionApply.age || personForSubmissionApply.tShirtSize !== personForSolutionApply.tShirtSize) {
        exercise.emit('fail', 'Apply method result in error. \nExpected result: ' + applierSolutionResult + ' \nActual result: '+ applierSubmissionResult);
        pass = false;
    }
    process.nextTick(function () {
        callback(null, pass)
    });
});

// Print out the suggested solution when the student passes. This is copied from
// workshopper-exercise/execute because the rest of execute is not relevant to
// the way this is tested.
exercise.getSolutionFiles = function (callback) {
    var solutionDir = getSolutionPath();

    fs.readdir(solutionDir, function (err, list) {
        if (err) {
            return callback(err);
        }

        list = list
            .filter(function (f) { return (/\.js$/).test(f) })
            .map(function (f) { return path.join(solutionDir, f)});

        callback(null, list);
    });
};

function getSolutionPath() {
    return path.join(exercise.dir, './solution/');
}

module.exports = exercise
<commit_msg>Print out object key, value pairs for error message<commit_after>var exercise      = require('workshopper-exercise')()
  , filecheck     = require('workshopper-exercise/filecheck')
  , execute       = require('workshopper-exercise/execute')
  , comparestdout = require('workshopper-exercise/comparestdout')
  , path          = require('path')
  , fs = require('fs')
  , util = require('util')

// checks that the submission file actually exists
exercise = filecheck(exercise)

// add setup.
exercise.addSetup(function (mode, callback) {

    this.solutionModule = require(getSolutionPath() + 'solution.js');
    this.submissionModule = require([process.cwd(), this.args[0]].join('/'));
    process.nextTick(callback);
});

// add a processor.
exercise.addProcessor(function (mode, callback) {
    var pass = true;
    var update = function(name, age, tShirtSize){
        this.name = name;
        this.age = age;
        this.tShirtSize = tShirtSize;
    };

    var personForSolutionCall = {name: "Brij", age: 28, tShirtSize: 'L'};
    var personForSubmissionCall = {name: "Brij", age: 28, tShirtSize: 'L'};

    var callerSolutionResult = this.solutionModule.caller(personForSolutionCall, update, 'Kishor', 29, 'XL');
    var callerSubmissionResult = this.submissionModule.caller(personForSubmissionCall, update, 'Kishor', 29, 'XL');

    if (personForSubmissionCall.name !== personForSolutionCall.name || personForSubmissionCall.age !== personForSolutionCall.age || personForSubmissionCall.tShirtSize !== personForSolutionCall.tShirtSize) {
        exercise.emit('fail', 'Call method result in error. \nExpected result: ' + showObjectInLog(personForSolutionCall) + ' \nActual result: '+ showObjectInLog(personForSubmissionCall));
        pass = false;
    }

    var personForSolutionApply = {name: "Kishor", age: 24, tShirtSize: 'S'};
    var personForSubmissionApply = {name: "Kishor", age: 24, tShirtSize: 'S'};

    var applierSolutionResult = this.solutionModule.applier(personForSolutionApply, update, ['Brij', 26, 'M']);
    var applierSubmissionResult = this.submissionModule.applier(personForSubmissionApply, update, ['Brij', 26, 'M']);
    if (personForSubmissionApply.name !== personForSolutionApply.name || personForSubmissionApply.age !== personForSolutionApply.age || personForSubmissionApply.tShirtSize !== personForSolutionApply.tShirtSize) {
        exercise.emit('fail', 'Apply method result in error. \nExpected result: ' + showObjectInLog(personForSolutionApply) + ' \nActual result: '+ showObjectInLog(personForSubmissionApply));
        pass = false;
    }
    process.nextTick(function () {
        callback(null, pass)
    });
});

// Print out the suggested solution when the student passes. This is copied from
// workshopper-exercise/execute because the rest of execute is not relevant to
// the way this is tested.
exercise.getSolutionFiles = function (callback) {
    var solutionDir = getSolutionPath();

    fs.readdir(solutionDir, function (err, list) {
        if (err) {
            return callback(err);
        }

        list = list
            .filter(function (f) { return (/\.js$/).test(f) })
            .map(function (f) { return path.join(solutionDir, f)});

        callback(null, list);
    });
};

function getSolutionPath() {
    return path.join(exercise.dir, './solution/');
}

// Print out an object's key value pairs when that object is used in `console.log`.
// Normally, would output: `[object Object]`.
function showObjectInLog(obj) {
    return util.inspect(obj);
}

module.exports = exercise
<|endoftext|>