<commit_before>/**
 * A higher order form component that handles syncing the state of a form
 * as well as validation, etc.
 *
 * @module higherform
 */

import React, { Component, createElement } from 'react';
import invariant from 'invariant';
import hoistStatics from 'hoist-non-react-statics';
import isPlainObject from 'lodash.isplainobject';
import { context as validationContext } from './validators';

function displayNameFor(WrappedComponent) {
    return WrappedComponent.displayName || WrappedComponent.name || 'Component';
}

/**
 * This is the default specification for a form.
 */
const DefaultFormSpec = {
    /**
     * Called on component creation to map the initial props to the form
     * values.
     *
     * @param {object} props The props passed to the component
     * @return {object|undefined} Return an object of {fieldName: value} pairs.
     *         May return a falsey value to decline doing anything
     */
    propsToForm: function (props) {
        return {};
    },

    /**
     * Invoked by the component in `componentWillReceiveProps`. This is
     * useful should you want to change the form values when the component
     * moves to a new entity or something.
     *
     * Return a falsy value here to decline the update.
     *
     * @param {object} props the components current props
     * @param {object} nextProps The components next props
     * @return {object|undefined} A new set of form data or a falsey value to
     *         decline doing an update.
     */
    nextPropsToForm: function (props, nextProps) {
        // noop
    },
}

/**
 * Ensure the spec from the user has the required methods.
 */
function createValidSpec(userSpec) {
    return Object.assign({}, DefaultFormSpec, userSpec);
}

function filterFormData(formData, fields) {
    invariant(isPlainObject(formData), 'propsToForm and nextPropsToForm must return plain objects');
    let nf = {};
    for (let field in fields) {
        nf[field] = fields[field].filterInput(formData[field] || '');
    }

    return nf;
}

/**
 * Creates a higher order component that decorates a form.
 *
 * @param {object|function} fieldSpec An array of string field names the form will track
 * @param {object|undefined} formSpec The form lifecycle spec. This includes
 *        allows you to hook in and modify how the form should behave on validaiton,
 *        setting up defualt values from the props, and handing updating props.
 *        If a function is given here it will be treated as the `validate`
 *        part of the spec.
 * @return {func} A decorator function.
 */
export default function higherform(fieldSpec, formSpec) {
    if (typeof formSpec === 'undefined') {
        formSpec = {};
    }

    invariant(isPlainObject(fieldSpec) || typeof fieldSpec === 'function',  'field spec must be an object or a function');
    invariant(isPlainObject(formSpec), 'The form spec must be a plain object');

    const finalFormSpec = createValidSpec(formSpec);

    return function (WrappedComponent) {

        class HigherForm extends Component {
            constructor(props, context) {
                super(props, context);
                this.fields = undefined;
                this._configureFields(props);
                let formData = filterFormData(finalFormSpec.propsToForm(props) || {}, this.fields);
                this.state = {
                    __errors: {},
                    ...formData,
                };
                this.submit = this.submit.bind(this);
            }

            componentWillReceiveProps(nextProps) {
                let fieldsChanged = this._configureFields(nextProps);
                let newFormData = finalFormSpec.nextPropsToForm(this.props, nextProps);
                if (!!newFormData) {
                    this.setState(filterFormData(newFormData, this.fields));
                } else if (fieldsChanged) {
                    // if the fields updated, we need to ensure the state that's
                    // tracked by this class is up to date with the fields
                    this.setState(filterFormData(this.state, this.fields));
                }
            }

            /**
             * Validate the form, and if it's good to go, call the `submit` callback
             * with the form data object.
             *
             * @param {func(formData)} submit the callback to "sumbit" the form data.
             * @return void
             */
            submit(callback) {
                let toSubmit = {};
                let errors = {};
                let hasErrors = false;
                for (let field in this.fields) {
                    let ctx = this.fields[field].validate(this.state[field], validationContext());
                    if (ctx.hasViolations()) {
                        hasErrors = true;
                        errors[field] = ctx.getViolations();
                    } else {
                        toSubmit[field] = this.fields[field].filterOutput(this.state[field]);
                    }
                }

                // always call `setState` here so previous errors get cleared
                // or new errors are udpated.
                this.setState({
                    __errors: errors,
                });

                if (!hasErrors) {
                    callback(toSubmit);
                }
            }

            buildFields() {
                let out = {};
                for (let field in this.fields) {
                    out[field] = this._buildFieldMethods(field);
                }

                return out;
            }

            buildForm() {
                return {
                    submit: this.submit,
                    errors: this.state.__errors,
                }
            }

            getWrappedInstance() {
                return this.instance;
            }

            render() {
                this.instance = createElement(WrappedComponent, {
                    fields: this.buildFields(),
                    form: this.buildForm(),
                    ref: f => this.instance = f,
                    ...this.props
                });

                return this.instance;
            }

            _buildFieldMethods(field) {
                return this.fields[field].toMethods(field, value => {
                    if (typeof value !== 'function') {
                        return this.setState({[field]: value});
                    }

                    return this.setState(function (nextState) {
                        return {[field]: value(nextState[field])};
                    });
                }, () => this.state[field]);
            }

            _configureFields(props) {
                var nf;
                if (typeof fieldSpec === 'function') {
                    nf = fieldSpec(props);
                    invariant(isPlainObject(nf), 'the fieldSpec function must return a plain object');
                } else {
                    nf = fieldSpec;
                }

                let updated = this.fields !== nf;

                this.fields = nf;

                return updated;
            }
        }

        HigherForm.displayName = `HigherForm(${displayNameFor(WrappedComponent)})`;
        HigherForm.WrappedComponent = WrappedComponent;

        return hoistStatics(HigherForm, WrappedComponent);
    };
}
<commit_msg>Don't Set the Default Value of a Field in Forms<commit_after>/**
 * A higher order form component that handles syncing the state of a form
 * as well as validation, etc.
 *
 * @module higherform
 */

import React, { Component, createElement } from 'react';
import invariant from 'invariant';
import hoistStatics from 'hoist-non-react-statics';
import isPlainObject from 'lodash.isplainobject';
import { context as validationContext } from './validators';

function displayNameFor(WrappedComponent) {
    return WrappedComponent.displayName || WrappedComponent.name || 'Component';
}

/**
 * This is the default specification for a form.
 */
const DefaultFormSpec = {
    /**
     * Called on component creation to map the initial props to the form
     * values.
     *
     * @param {object} props The props passed to the component
     * @return {object|undefined} Return an object of {fieldName: value} pairs.
     *         May return a falsey value to decline doing anything
     */
    propsToForm: function (props) {
        return {};
    },

    /**
     * Invoked by the component in `componentWillReceiveProps`. This is
     * useful should you want to change the form values when the component
     * moves to a new entity or something.
     *
     * Return a falsy value here to decline the update.
     *
     * @param {object} props the components current props
     * @param {object} nextProps The components next props
     * @return {object|undefined} A new set of form data or a falsey value to
     *         decline doing an update.
     */
    nextPropsToForm: function (props, nextProps) {
        // noop
    },
}

/**
 * Ensure the spec from the user has the required methods.
 */
function createValidSpec(userSpec) {
    return Object.assign({}, DefaultFormSpec, userSpec);
}

function filterFormData(formData, fields) {
    invariant(isPlainObject(formData), 'propsToForm and nextPropsToForm must return plain objects');
    let nf = {};
    for (let field in fields) {
        nf[field] = fields[field].filterInput(formData[field]);
    }

    return nf;
}

/**
 * Creates a higher order component that decorates a form.
 *
 * @param {object|function} fieldSpec An array of string field names the form will track
 * @param {object|undefined} formSpec The form lifecycle spec. This includes
 *        allows you to hook in and modify how the form should behave on validaiton,
 *        setting up defualt values from the props, and handing updating props.
 *        If a function is given here it will be treated as the `validate`
 *        part of the spec.
 * @return {func} A decorator function.
 */
export default function higherform(fieldSpec, formSpec) {
    if (typeof formSpec === 'undefined') {
        formSpec = {};
    }

    invariant(isPlainObject(fieldSpec) || typeof fieldSpec === 'function',  'field spec must be an object or a function');
    invariant(isPlainObject(formSpec), 'The form spec must be a plain object');

    const finalFormSpec = createValidSpec(formSpec);

    return function (WrappedComponent) {

        class HigherForm extends Component {
            constructor(props, context) {
                super(props, context);
                this.fields = undefined;
                this._configureFields(props);
                let formData = filterFormData(finalFormSpec.propsToForm(props) || {}, this.fields);
                this.state = {
                    __errors: {},
                    ...formData,
                };
                this.submit = this.submit.bind(this);
            }

            componentWillReceiveProps(nextProps) {
                let fieldsChanged = this._configureFields(nextProps);
                let newFormData = finalFormSpec.nextPropsToForm(this.props, nextProps);
                if (!!newFormData) {
                    this.setState(filterFormData(newFormData, this.fields));
                } else if (fieldsChanged) {
                    // if the fields updated, we need to ensure the state that's
                    // tracked by this class is up to date with the fields
                    this.setState(filterFormData(this.state, this.fields));
                }
            }

            /**
             * Validate the form, and if it's good to go, call the `submit` callback
             * with the form data object.
             *
             * @param {func(formData)} submit the callback to "sumbit" the form data.
             * @return void
             */
            submit(callback) {
                let toSubmit = {};
                let errors = {};
                let hasErrors = false;
                for (let field in this.fields) {
                    let ctx = this.fields[field].validate(this.state[field], validationContext());
                    if (ctx.hasViolations()) {
                        hasErrors = true;
                        errors[field] = ctx.getViolations();
                    } else {
                        toSubmit[field] = this.fields[field].filterOutput(this.state[field]);
                    }
                }

                // always call `setState` here so previous errors get cleared
                // or new errors are udpated.
                this.setState({
                    __errors: errors,
                });

                if (!hasErrors) {
                    callback(toSubmit);
                }
            }

            buildFields() {
                let out = {};
                for (let field in this.fields) {
                    out[field] = this._buildFieldMethods(field);
                }

                return out;
            }

            buildForm() {
                return {
                    submit: this.submit,
                    errors: this.state.__errors,
                }
            }

            getWrappedInstance() {
                return this.instance;
            }

            render() {
                this.instance = createElement(WrappedComponent, {
                    fields: this.buildFields(),
                    form: this.buildForm(),
                    ref: f => this.instance = f,
                    ...this.props
                });

                return this.instance;
            }

            _buildFieldMethods(field) {
                return this.fields[field].toMethods(field, value => {
                    if (typeof value !== 'function') {
                        return this.setState({[field]: value});
                    }

                    return this.setState(function (nextState) {
                        return {[field]: value(nextState[field])};
                    });
                }, () => this.state[field]);
            }

            _configureFields(props) {
                var nf;
                if (typeof fieldSpec === 'function') {
                    nf = fieldSpec(props);
                    invariant(isPlainObject(nf), 'the fieldSpec function must return a plain object');
                } else {
                    nf = fieldSpec;
                }

                let updated = this.fields !== nf;

                this.fields = nf;

                return updated;
            }
        }

        HigherForm.displayName = `HigherForm(${displayNameFor(WrappedComponent)})`;
        HigherForm.WrappedComponent = WrappedComponent;

        return hoistStatics(HigherForm, WrappedComponent);
    };
}
<|endoftext|>


<commit_before>import React from 'react'
import _ from './utils'

const noop = () => {}
const reop = d => d

export const FormDefaultProps = {
  loadState: noop,
  defaultValues: {},
  preValidate: reop,
  validate: () => null,
  onValidationFail: noop,
  onChange: noop,
  saveState: noop,
  willUnmount: noop,
  preSubmit: reop,
  onSubmit: noop,
  postSubmit: noop,
  component: 'div'
}

export default React.createClass({
  displayName: 'Form',
  childContextTypes: {
    formAPI: React.PropTypes.object
  },
  getChildContext () {
    return {
      formAPI: this.getAPI()
    }
  },
  // Lifecycle
  getDefaultProps () {
    return FormDefaultProps
  },
  getInitialState () {
    const {
      defaultValues,
      values,
      loadState
    } = this.props
    const mergedValues = {
      ..._.clone(defaultValues),
      ..._.clone(values)
    }

    return loadState(this.props, this) || {
      values: mergedValues,
      touched: {},
      errors: this.validate(mergedValues),
      nestedErrors: {}
    }
  },
  componentWillMount () {
    this.emitChange(this.state, true)
  },
  componentWillReceiveProps (props) {
    if (props.values === this.props.values) {
      return
    }

    this.setFormState({
      values: _.clone(props.values) || {}
    }, true)
  },
  componentWillUnmount () {
    this.props.willUnmount(this.state, this.props, this)
  },

  // API
  setAllValues (values, noTouch) {
    if (noTouch) {
      return this.setFormState({values})
    }
    this.setFormState({values, touched: {}})
  },
  setValue (field, value, noTouch) {
    const state = this.state
    const values = _.set(state.values, field, value)
    // Also set touched since the value is changing
    if (noTouch) {
      return this.setFormState({values})
    }
    const touched = _.set(state.touched, field)
    this.setFormState({values, touched})
  },
  getValue (field, fallback) {
    const state = this.state
    const val = _.get(state.values, field)
    return typeof val !== 'undefined' ? val : fallback
  },
  setNestedError (field, value = true) {
    const nestedErrors = _.set(this.state.nestedErrors, field, value)
    this.setFormState({nestedErrors})
  },
  getError (field) {
    return _.get(this.state.errors, field)
  },
  setTouched (field, value = true) {
    const touched = _.set(this.state.touched, field, value)
    this.setFormState({touched})
  },
  getTouched (field) {
    const state = this.state
    if (this.state.dirty === true || this.props.touched === true) {
      return true
    }
    return _.get(state.touched, field)
  },
  addValue (field, value) {
    const state = this.state
    const values = _.set(state.values, field, [
      ..._.get(state.values, field, []),
      value
    ])
    this.setFormState({values})
  },
  removeValue (field, index) {
    const state = this.state
    const fieldValue = _.get(state.values, field, [])
    const values = _.set(state.values, field, [
      ...fieldValue.slice(0, index),
      ...fieldValue.slice(index + 1)
    ])
    this.setFormState({values})
  },
  swapValues (field, index, destIndex) {
    const state = this.state
    const fieldValues = _.get(state.values, field, [])
    const values = _.set(state.values, field, [
      ...fieldValues.slice(0, index),
      fieldValues[destIndex],
      ...fieldValues.slice(index + 1, destIndex),
      fieldValues[index],
      ...fieldValues.slice(destIndex + 1)
    ])
    this.setFormState({values})
  },
  setAllTouched (dirty = true, state) {
    this.setFormState({
      ...state,
      dirty: !!dirty
    })
  },
  resetForm () {
    return this.setFormState(this.getInitialState())
  },
  submitForm (e) {
    e && e.preventDefault && e.preventDefault(e)
    const state = this.state
    const errors = this.validate(state.values, state, this.props)
    if (errors) {
      if (!state.dirty) {
        this.setAllTouched(true, {errors})
      }
      return this.props.onValidationFail(state.values, state, this.props, this)
    }
    const preSubmitValues = this.props.preSubmit(state.values, state, this.props, this)
    this.props.onSubmit(preSubmitValues, state, this.props, this)
    this.props.postSubmit(preSubmitValues, state, this.props, this)
  },

  // Utils
  getAPI () {
    return {
      setAllValues: this.setAllValues,
      setValue: this.setValue,
      getValue: this.getValue,
      setNestedError: this.setNestedError,
      getError: this.getError,
      setTouched: this.setTouched,
      getTouched: this.getTouched,
      addValue: this.addValue,
      removeValue: this.removeValue,
      swapValues: this.swapValues,
      setAllTouched: this.setAllTouched,
      resetForm: this.resetForm,
      submitForm: this.submitForm
    }
  },
  setFormState (newState, silent) {
    if (newState && newState.values && !newState.errors) {
      newState.values = this.props.preValidate(newState.values, newState, this.props, this)
      newState.errors = this.validate(newState.values, newState, this.props)
    }
    this.setState(newState, () => {
      this.props.saveState(this.state, this.props, this)
      if (!silent) {
        this.emitChange(this.state, this.props)
      }
    })
  },
  emitChange (state, initial) {
    this.props.onChange(state, this.props, initial, this)
  },
  validate (values, state, props) {
    const errors = this.props.validate(
      removeNestedErrorValues(values, this.state ? this.state.nestedErrors : {}),
      state,
      props,
      this
    )
    return cleanErrors(errors)
  },
  // Render
  render () {
    const props = {
      ...this.props,
      ...this.state,
      ...this.getAPI()
    }
    const { component, children, ...rest } = props
    const resolvedChild = typeof children === 'function' ? children(rest) : children
    const RootEl = component
    if (!RootEl) {
      return resolvedChild
    }
    return (
      <RootEl className='ReactForm'>{resolvedChild}</RootEl>
    )
  }
})

// Utils

function cleanErrors (err) {
  if (_.isObject(err)) {
    const resolved = _.mapValues(err, cleanErrors)
    const found = _.pickBy(resolved, d => d)
    return Object.keys(found).length ? resolved : undefined
  }
  if (_.isArray(err)) {
    const resolved = err.map(cleanErrors)
    const found = resolved.find(d => d)
    return found ? resolved : undefined
  }
  return err
}

function removeNestedErrorValues (value, nestedErrors) {
  const recurse = (value, path = []) => {
    if (_.get(nestedErrors, path)) {
      return undefined
    }
    if (_.isObject(value)) {
      return _.mapValues(value, (d, i) => {
        return recurse(d, [...path, i])
      })
    }
    if (_.isArray(value)) {
      return value.map((d, key) => {
        return recurse(d, [...path, key])
      })
    }
    return value
  }
  return recurse(value)
}
<commit_msg>Fixed: Deeply nested form fields now validate properly<commit_after>import React from 'react'
import _ from './utils'

const noop = () => {}
const reop = d => d

export const FormDefaultProps = {
  loadState: noop,
  defaultValues: {},
  preValidate: reop,
  validate: () => null,
  onValidationFail: noop,
  onChange: noop,
  saveState: noop,
  willUnmount: noop,
  preSubmit: reop,
  onSubmit: noop,
  postSubmit: noop,
  component: 'div'
}

export default React.createClass({
  displayName: 'Form',
  childContextTypes: {
    formAPI: React.PropTypes.object
  },
  getChildContext () {
    return {
      formAPI: this.getAPI()
    }
  },
  // Lifecycle
  getDefaultProps () {
    return FormDefaultProps
  },
  getInitialState () {
    const {
      defaultValues,
      values,
      loadState
    } = this.props
    const mergedValues = {
      ..._.clone(defaultValues),
      ..._.clone(values)
    }

    return loadState(this.props, this) || {
      values: mergedValues,
      touched: {},
      errors: this.validate(mergedValues),
      nestedErrors: {}
    }
  },
  componentWillMount () {
    this.emitChange(this.state, true)
  },
  componentWillReceiveProps (props) {
    if (props.values === this.props.values) {
      return
    }

    this.setFormState({
      values: _.clone(props.values) || {}
    }, true)
  },
  componentWillUnmount () {
    this.props.willUnmount(this.state, this.props, this)
  },

  // API
  setAllValues (values, noTouch) {
    if (noTouch) {
      return this.setFormState({values})
    }
    this.setFormState({values, touched: {}})
  },
  setValue (field, value, noTouch) {
    const state = this.state
    const values = _.set(state.values, field, value)
    // Also set touched since the value is changing
    if (noTouch) {
      return this.setFormState({values})
    }
    const touched = _.set(state.touched, field)
    this.setFormState({values, touched})
  },
  getValue (field, fallback) {
    const state = this.state
    const val = _.get(state.values, field)
    return typeof val !== 'undefined' ? val : fallback
  },
  setNestedError (field, value = true) {
    const nestedErrors = _.set(this.state.nestedErrors, field, value)
    this.setFormState({nestedErrors})
  },
  getError (field) {
    return _.get(this.state.errors, field)
  },
  setTouched (field, value = true) {
    const touched = _.set(this.state.touched, field, value)
    this.setFormState({touched})
  },
  getTouched (field) {
    const state = this.state
    if (this.state.dirty === true || this.props.touched === true) {
      return true
    }
    return _.get(state.touched, field)
  },
  addValue (field, value) {
    const state = this.state
    const values = _.set(state.values, field, [
      ..._.get(state.values, field, []),
      value
    ])
    this.setFormState({values})
  },
  removeValue (field, index) {
    const state = this.state
    const fieldValue = _.get(state.values, field, [])
    const values = _.set(state.values, field, [
      ...fieldValue.slice(0, index),
      ...fieldValue.slice(index + 1)
    ])
    this.setFormState({values})
  },
  swapValues (field, index, destIndex) {
    const state = this.state
    const fieldValues = _.get(state.values, field, [])
    const values = _.set(state.values, field, [
      ...fieldValues.slice(0, index),
      fieldValues[destIndex],
      ...fieldValues.slice(index + 1, destIndex),
      fieldValues[index],
      ...fieldValues.slice(destIndex + 1)
    ])
    this.setFormState({values})
  },
  setAllTouched (dirty = true, state) {
    this.setFormState({
      ...state,
      dirty: !!dirty
    })
  },
  resetForm () {
    return this.setFormState(this.getInitialState())
  },
  submitForm (e) {
    e && e.preventDefault && e.preventDefault(e)
    const state = this.state
    const errors = this.validate(state.values, state, this.props)
    if (errors) {
      if (!state.dirty) {
        this.setAllTouched(true, {errors})
      }
      return this.props.onValidationFail(state.values, state, this.props, this)
    }
    const preSubmitValues = this.props.preSubmit(state.values, state, this.props, this)
    this.props.onSubmit(preSubmitValues, state, this.props, this)
    this.props.postSubmit(preSubmitValues, state, this.props, this)
  },

  // Utils
  getAPI () {
    return {
      setAllValues: this.setAllValues,
      setValue: this.setValue,
      getValue: this.getValue,
      setNestedError: this.setNestedError,
      getError: this.getError,
      setTouched: this.setTouched,
      getTouched: this.getTouched,
      addValue: this.addValue,
      removeValue: this.removeValue,
      swapValues: this.swapValues,
      setAllTouched: this.setAllTouched,
      resetForm: this.resetForm,
      submitForm: this.submitForm
    }
  },
  setFormState (newState, silent) {
    if (newState && newState.values && !newState.errors) {
      newState.values = this.props.preValidate(newState.values, newState, this.props, this)
      newState.errors = this.validate(newState.values, newState, this.props)
    }
    this.setState(newState, () => {
      this.props.saveState(this.state, this.props, this)
      if (!silent) {
        this.emitChange(this.state, this.props)
      }
    })
  },
  emitChange (state, initial) {
    this.props.onChange(state, this.props, initial, this)
  },
  validate (values, state, props) {
    const errors = this.props.validate(
      removeNestedErrorValues(values, this.state ? this.state.nestedErrors : {}),
      state,
      props,
      this
    )
    return cleanErrors(errors)
  },
  // Render
  render () {
    const props = {
      ...this.props,
      ...this.state,
      ...this.getAPI()
    }
    const { component, children, ...rest } = props
    const resolvedChild = typeof children === 'function' ? children(rest) : children
    const RootEl = component
    if (!RootEl) {
      return resolvedChild
    }
    return (
      <RootEl className='ReactForm'>{resolvedChild}</RootEl>
    )
  }
})

// Utils

function cleanErrors (err) {
  if (_.isObject(err)) {
    const resolved = _.mapValues(err, cleanErrors)
    const found = _.pickBy(resolved, d => d)
    return Object.keys(found).length ? resolved : undefined
  }
  if (_.isArray(err)) {
    const resolved = err.map(cleanErrors)
    const found = resolved.find(d => d)
    return found ? resolved : undefined
  }
  return err
}

function removeNestedErrorValues (value, nestedErrors) {
  const recurse = (value, path = []) => {
    if (_.isObject(value)) {
      return _.mapValues(value, (d, i) => {
        return recurse(d, [...path, i])
      })
    }
    if (_.isArray(value)) {
      return value.map((d, key) => {
        return recurse(d, [...path, key])
      })
    }
    if (_.get(nestedErrors, path)) {
      return undefined
    }
    return value
  }
  return recurse(value)
}
<|endoftext|>


<commit_before>//------------------------------------------------------
// Company: Valentys Ltda.
// Author: dmunozgaete@gmail.com
//
// Description: Angular Gale Implementation
//
// URL: https://github.com/dmunozgaete/angular-gale
//
// Documentation:
//      http://gale-docs.azurewebsites.net/
//------------------------------------------------------
angular.manifiest('gale', [
    'gale.templates',
    'gale.directives',
    'gale.components',
    'gale.filters',
    'gale.services',
    'gale.services.security',
    'gale.services.configuration',
    'gale.services.rest',
    'gale.services.storage'
], [
    'ui.router', //NG ROUTE
    'ngMaterial' //MATERIAL DESIGN DIRECTIVES
])

.run(['$Configuration', '$LocalStorage', '$log', 'CONFIGURATION', function($Configuration, $LocalStorage, $log, CONFIGURATION)
{
    var stored_key = "$_application";
    var app_conf = CONFIGURATION.application;
    var app_stored = $LocalStorage.getObject(stored_key);

    if (app_stored)
    {

        //check version configuration , if old , broadcast a changeVersion event;
        if (app_stored.version !== app_conf.version || app_stored.environment !== app_conf.environment)
        {

            $log.debug("a new configuration version is available, calling [on_build_new_version] if exist's !", app_conf.version); //Show only in debug mode

            if (angular.isFunction(CONFIGURATION.on_build_new_version))
            {
                try
                {
                    CONFIGURATION.on_build_new_version(app_conf.version, app_stored.version);
                }
                catch (e)
                {

                    $log.debug("failed to execute [on_build_new_version] function defined in config.js", e);
                    throw e;
                }
            }
        }

    }

    //Update
    $LocalStorage.setObject(stored_key, app_conf);

}])

//------------------------------------------------------------
//ADD DEFAULT THEME STYLE'S in DOM Selected By Angular-Material
//Example Use:
//
 Change the color to primary palette
//
 Change the background color to primary palette
.config(['$mdThemingProvider', function($mdThemingProvider)
{
    setTimeout(function()
    {
        //------------------------------------------------------------
        var style = angular.element('');
        document.head.appendChild(style[0]);
        stylesheet = style[0].sheet;

        var defaultTheme = (function()
        {
            for (var name in $mdThemingProvider._THEMES)
            {
                return name;
            }
        })();

        var palettes = ["primary", "accent", "warn"];
        for (var i in palettes)
        {
            var theme = palettes[i];
            var palette = $mdThemingProvider._THEMES[defaultTheme].colors[theme].name;
            var color = $mdThemingProvider._PALETTES[palette][500]; //Default color is 500

            stylesheet.insertRule(".md-" + theme + ".text { color: " + color + " }", 0);
            stylesheet.insertRule(".md-" + theme + ".background { background-color: " + color + " }", 0);
        }
        //------------------------------------------------------------

    }, 100);
}]);
<commit_msg>toRGB fgunction<commit_after>//------------------------------------------------------
// Company: Valentys Ltda.
// Author: dmunozgaete@gmail.com
//
// Description: Angular Gale Implementation
//
// URL: https://github.com/dmunozgaete/angular-gale
//
// Documentation:
//      http://gale-docs.azurewebsites.net/
//------------------------------------------------------
angular.manifiest('gale', [
    'gale.templates',
    'gale.directives',
    'gale.components',
    'gale.filters',
    'gale.services',
    'gale.services.security',
    'gale.services.configuration',
    'gale.services.rest',
    'gale.services.storage'
], [
    'ui.router', //NG ROUTE
    'ngMaterial' //MATERIAL DESIGN DIRECTIVES
])

.run(['$Configuration', '$LocalStorage', '$log', 'CONFIGURATION', function($Configuration, $LocalStorage, $log, CONFIGURATION)
{
    var stored_key = "$_application";
    var app_conf = CONFIGURATION.application;
    var app_stored = $LocalStorage.getObject(stored_key);

    if (app_stored)
    {

        //check version configuration , if old , broadcast a changeVersion event;
        if (app_stored.version !== app_conf.version || app_stored.environment !== app_conf.environment)
        {

            $log.debug("a new configuration version is available, calling [on_build_new_version] if exist's !", app_conf.version); //Show only in debug mode

            if (angular.isFunction(CONFIGURATION.on_build_new_version))
            {
                try
                {
                    CONFIGURATION.on_build_new_version(app_conf.version, app_stored.version);
                }
                catch (e)
                {

                    $log.debug("failed to execute [on_build_new_version] function defined in config.js", e);
                    throw e;
                }
            }
        }

    }

    //Update
    $LocalStorage.setObject(stored_key, app_conf);

}])

//------------------------------------------------------------
//ADD DEFAULT THEME STYLE'S in DOM Selected By Angular-Material
//Example Use:
//
 Change the color to primary palette
//
 Change the background color to primary palette
.config(['$mdThemingProvider', function($mdThemingProvider)
{
    setTimeout(function()
    {
        //------------------------------------------------------------
        var toRGB = function(color)
        {
            if (typeof(color) !== "string")
            {
                return "rgb(" + color[0] + "," + color[1] + "," + color[2] + ")";
            }
            else
            {
                return color;
            }
        };
        //------------------------------------------------------------
        var style = angular.element('');
        document.head.appendChild(style[0]);
        stylesheet = style[0].sheet;

        var defaultTheme = (function()
        {
            for (var name in $mdThemingProvider._THEMES)
            {
                return name;
            }
        })();

        var palettes = ["primary", "accent", "warn"];
        for (var i in palettes)
        {
            var theme = palettes[i];
            var palette = $mdThemingProvider._THEMES[defaultTheme].colors[theme].name;
            var color = $mdThemingProvider._PALETTES[palette][500]; //Default color is 500

            stylesheet.insertRule(".md-" + theme + ".text { color: " + toRGB(color) + " }", 0);
            stylesheet.insertRule(".md-" + theme + ".background { background-color: " + toRGB(color) + " }", 0);
        }
        //------------------------------------------------------------

    }, 100);
}]);
<|endoftext|>


<commit_before>// module.exports = function() {
window.onload = function()
{
    gameTime = 0;
    canvas = document.getElementById("monsters");
    canvas.width = 590;
    canvas.height = 560;
    Hero = require('./hero.js'),
    EntityManager = require('./entity_manager.js'),
    ShopManager = require('./shop_manager.js'),
    StatsManager = require('./stats_manager.js'),
    AudioManager = require('./AudioManager.js')
    ctx = canvas.getContext("2d");

  var load = function(sm) {

    var statemanager = sm;
    // The width & height of the screen
    SCREEN_WIDTH = 1280;
    SCREEN_HEIGHT = 720;

    EntityManager.add_gold = ShopManager.AddGold;
    // Module variables

    StatsManager.SetSpawnDelegate(EntityManager.spawn_monster);

    ShopManager.SetStatsManagerDelegates(
      StatsManager.IncreaseAttackCap,
      StatsManager.IncreaseDefenseCap,
      StatsManager.IncreaseHealthCap,
      StatsManager.AddSpecial,
      EntityManager.open_door,
      EntityManager.upgrade_boss
    );

    EntityManager.initialize();
    canvas = document.getElementById("monsters");
    ctx = canvas.getContext("2d");

    AudioManager.playIdleMusic();
    EntityManager.initialize();

    window.requestAnimationFrame(loop);
  }

  var update = function(elapsedTime) {
    EntityManager.update(elapsedTime);
  };

  var keyUp = function(e) {
    // Do nothing
  };

  var keyDown = function(e) {
    // Do nothing
  };

  var exit = function() {
    // Any exit logic
  }
  var render = function() {
    //TODO
    EntityManager.render(ctx);
  };

  var loop = function(newTime)
  {
    var elapsedTime = (newTime - gameTime) / 1000;
    gameTime = newTime;
    update(elapsedTime);
    render();
    window.requestAnimationFrame(loop);
  }
  load();
  window.requestAnimationFrame(loop);
}
//   return {
//     load: load,
//     update: update,
//     render: render,
//     keyUp: keyUp,
//     keyDown: keyDown,
//     exit: exit
//   }

// }();
<commit_msg>shop manager adds gold to ease debugging<commit_after>// module.exports = function() {
window.onload = function()
{
    gameTime = 0;
    canvas = document.getElementById("monsters");
    canvas.width = 590;
    canvas.height = 560;
    Hero = require('./hero.js'),
    EntityManager = require('./entity_manager.js'),
    ShopManager = require('./shop_manager.js'),
    StatsManager = require('./stats_manager.js'),
    AudioManager = require('./AudioManager.js')
    ctx = canvas.getContext("2d");

  var load = function(sm) {

    var statemanager = sm;
    // The width & height of the screen
    SCREEN_WIDTH = 1280;
    SCREEN_HEIGHT = 720;

    EntityManager.add_gold = ShopManager.AddGold;
    // Module variables

    StatsManager.SetSpawnDelegate(EntityManager.spawn_monster);

    ShopManager.SetStatsManagerDelegates(
      StatsManager.IncreaseAttackCap,
      StatsManager.IncreaseDefenseCap,
      StatsManager.IncreaseHealthCap,
      StatsManager.AddSpecial,
      EntityManager.open_door,
      EntityManager.upgrade_boss
    );

    ShopManager.AddGold(15000);

    EntityManager.initialize();
    canvas = document.getElementById("monsters");
    ctx = canvas.getContext("2d");

    AudioManager.playIdleMusic();
    EntityManager.initialize();

    window.requestAnimationFrame(loop);
  }

  var update = function(elapsedTime) {
    EntityManager.update(elapsedTime);
  };

  var keyUp = function(e) {
    // Do nothing
  };

  var keyDown = function(e) {
    // Do nothing
  };

  var exit = function() {
    // Any exit logic
  }
  var render = function() {
    //TODO
    EntityManager.render(ctx);
  };

  var loop = function(newTime)
  {
    var elapsedTime = (newTime - gameTime) / 1000;
    gameTime = newTime;
    update(elapsedTime);
    render();
    window.requestAnimationFrame(loop);
  }
  load();
  window.requestAnimationFrame(loop);
}
//   return {
//     load: load,
//     update: update,
//     render: render,
//     keyUp: keyUp,
//     keyDown: keyDown,
//     exit: exit
//   }

// }();
<|endoftext|>


<commit_before>/**
 * @module Lyria
 */
define('lyria/game', ['eventmap', 'mixer', 'jquery', 'lyria/viewport', 'lyria/scene/director', 'lyria/preloader', 'lyria/loop', 'lyria/world'], function(EventMap, mixer, $, Viewport, Director, Preloader, Loop, World) {'use strict';

  /**
   * Game class which has a viewport, scene director and preloader by
   * default.
   *
   * @class Game
   */
  return (function() {

    /**
     * @constructor
     */
    var Game = function(options) {
      var self = this;

      options = $.extend(options, {
        startLoop: true
      });

      mixer(Game.prototype, new EventMap());

      /**
       * @property viewport
       * @type {Viewport}
       */
      // Set up a viewport
      this.viewport = new Viewport();
      this.viewport.parent = this;

      /**
       * @property director
       * @type {Director}
       */
      // Add a scene director
      this.director = new Director(this.viewport);
      this.director.parent = this;

      /**
       * @property preloader
       * @type {Preloader}
       */
      // Add a preloader
      this.preloader = new Preloader();
      this.preloader.parent = this;

      // Bind the scene director to the preloader reference
      this.preloader.sceneDirector = this.director;

      this.paused = false;

      // World reference
      this.world = new World();
      this.world.parent = this;

      // Add an update task to the loop with updates the scene director on each
      // frame
      Game.Loop.on('update', function(dt) {
        self.director.trigger('update', dt);
      });

      // Run game loop (if it should start by default)
      // In most cases, you'll need the loop (animation/physics/etc.)
      // In a pure event-based game - like a turn-based-strategy game -
      // you might want turn startLoop off
      if (options.startLoop) {
        Game.Loop.run();
      }

      $(document).ready(function() {
        $(window).blur(self.pause);
        $(window).focus(self.resume);
      });
    };

    Game.prototype.pause = function() {
      this.paused = true;
      this.trigger('pause');
    };

    Game.prototype.resume = function() {
      this.paused = false;
      this.trigger('resume');
    };

    /**
     * @property Loop
     * @static
     * @type {Loop}
     */
    // Store the reference to the Lyria Loop at the Game object
    Game.Loop = Loop;

    return Game;

  })();

}); <commit_msg>Using event shorthand functions + binding to blur and focus<commit_after>/**
 * @module Lyria
 */
define('lyria/game', ['eventmap', 'mixer', 'jquery', 'lyria/viewport', 'lyria/scene/director', 'lyria/preloader', 'lyria/loop', 'lyria/world'], function(EventMap, mixer, $, Viewport, Director, Preloader, Loop, World) {'use strict';

  /**
   * Game class which has a viewport, scene director and preloader by
   * default.
   *
   * @class Game
   */
  return (function() {

    /**
     * @constructor
     */
    var Game = function(options) {
      var self = this;

      options = $.extend(options, {
        startLoop: true
      });

      mixer(Game.prototype, new EventMap());

      /**
       * @property viewport
       * @type {Viewport}
       */
      // Set up a viewport
      this.viewport = new Viewport();
      this.viewport.parent = this;

      /**
       * @property director
       * @type {Director}
       */
      // Add a scene director
      this.director = new Director(this.viewport);
      this.director.parent = this;

      /**
       * @property preloader
       * @type {Preloader}
       */
      // Add a preloader
      this.preloader = new Preloader();
      this.preloader.parent = this;

      // Bind the scene director to the preloader reference
      this.preloader.sceneDirector = this.director;

      this.paused = false;

      // World reference
      this.world = new World();
      this.world.parent = this;

      // Add an update task to the loop with updates the scene director on each
      // frame
      Game.Loop.on('update', function(dt) {
        self.director.trigger('update', dt);
      });

      // Run game loop (if it should start by default)
      // In most cases, you'll need the loop (animation/physics/etc.)
      // In a pure event-based game - like a turn-based-strategy game -
      // you might want turn startLoop off
      if (options.startLoop) {
        Game.Loop.run();
      }

      this.on('pause', function() {
        self.paused = true;
      });

      this.on('resume', function() {
        self.paused = false;
      });

      $(document).ready(function() {
        $(window).blur(self.pause.bind(self));
        $(window).focus(self.resume.bind(self));
      });
    };

    /**
     * @property Loop
     * @static
     * @type {Loop}
     */
    // Store the reference to the Lyria Loop at the Game object
    Game.Loop = Loop;

    return Game;

  })();

}); <|endoftext|>


<commit_before>/*
 * gioc
 * https://github.com/goliatone/gioc
 *
 * TODO: Refactor processor related methods
 *     addSolver
 *     addPost
 *     addProvider
 *     Those should all use the same signature
 *     and work in the same way, then have one method
 *     that creates dynamically them.
 *
 * TODO: prepare and post could be one method, also
 *       dynamically attached.
 *
 * Copyright (c) 2013 goliatone
 * Licensed under the MIT license.
 */
/*global define:true*/
/* jshint strict: false */
define('gioc', function() {

////////////////////////////////////////
/// PRIVATE METHODS
////////////////////////////////////////

    var _slice = Array.prototype.slice;

    var _isFactory = function(bean){
        if(this.factoryKey in bean.config) return bean.config[this.factoryKey];
        return typeof bean.load === 'function';
    };

////////////////////////////////////////
/// CONSTRUCTOR
////////////////////////////////////////

    /**
     * Gioc constructor.
     *
     * @param  {Object} config Optional config object.
     */
    var Gioc = function Gioc(config){
        //Store all bean info.
        this.beans = {};
        this.graph = {};

        this.solvers = {};
        this.editors = [];

        this.providers = [];

        this.configure(config);

        //Solvers methods should have a common signature:
        //id, target, options (which should be similar throught all methods)

        this.addSolver(this.propKey, this.extend);
        this.addSolver(this.depsKey, this.solveDependencies);

        //TODO: Should addPost and addProvider have the
        //same signature as addSolver?
        this.addPost(this.resetGraph);

        this.addProvider(this.extend);
    };

////////////////////////////////////////
/// STATIC VARS
////////////////////////////////////////
    //TODO: Add support for chain props.
    //TODO: If we modify this, ie unit testing,
    //      then we cant reset. REMOVE STATIC or have
    //      a default src.
    Gioc.config = {
        attributes:[
            'depsKey', 'propKey', 'postKey',
            'postArgs', 'modKey', 'factoryKey',
            'strictErrors'
        ],
        defaults:{
            depsKey: 'deps',
            propKey: 'props',
            postKey: 'after',
            modKey:   'modifier',
            postArgs: 'pargs',
            factoryKey:'construct',
            strictErrors: false
        }
    };
////////////////////////////////////////
/// PUBLIC METHODS
////////////////////////////////////////
    /**
     * Handles configuration.
     * @param  {Object} config Configuration object
     * @return {this}
     */
    Gioc.prototype.configure = function(config){
        //TODO: This should be configurable.
        /* Default supported directives:
         * - deps: dependencies
         * - post: post wire execution
         * - props: add props to instantiated value.
         */
        config = this.extend({}, Gioc.config.defaults, config);
        Gioc.config.attributes.map(function(ckey){
            this[ckey] = config[ckey];
        }, this);

        return this;
    };

    /**
     * Stores a definition, with configuration
     * options, to be *solved* later.
     * The payload can be either a literal value
     * or a factory method.
     *
     * @param  {String} key      String ID.
     * @param  {Object|Function} payload Value to be map.
     * @param  {Object} config   Options for current mapping.
     * @return {Gioc}
     */
    Gioc.prototype.map = function(key, payload, config){
        //Store basic information of our payload.
        var bean = {key:key, load:payload, config:(config || {})};

        //Is this a factory or a literal value?
        bean.construct =
        bean.isFactory = _isFactory.call(this, bean);

        //TODO: How do we want to handle collision? We are overriding.
        if(this.mapped(key)) this.logger.warn(key, 'key is already mapped, overriding.');

        this.beans[key] = bean;

        return this;
    };

    /**
     * Solve for the provided *key*, it returns a value
     * and solves all dependencies, etc.
     * The cycle to solve for a key is the
     * result of runing the methods in order
     * `prepare` as a pre process, `build` to
     * generate the payload, `wire` to solve
     * dependencies, and `post` as a post process.
     *
     * @param  {String} key      String ID we are solving
     *                           for
     * @param  {Object} options   Options object.
     * @return {Object|undefined} Solved value
     *                            for the given key.
     */
    Gioc.prototype.solve = function(key, options){
        //TODO: If we try to solve an unregistered key, what should
        //we do? Throw or return undefined?
        if (!this.mapped(key)) return undefined;

        var value  = null,
            bean   = this.beans[key],
            config = {};
        //pre-process
        this.prepare(key, config, bean.config, options);

        this.logger.log('==> solve, generated config ', config);
        //build our value.
        value = this.build(key, config);

        //configure our value
        value = this.wire(key, value, config);

        //do all post 'construct' operations.
        this.post(key, value, config);

        return value;
    };

    /**
     * Pre process to consolidate the configuration
     * object for a given key.
     * It will loop over all providers in order the order
     * they were added and call the provider with the
     * Gioc instance as scope.
     * The default provider is the `extend` method.
     *
     * @param  {String} key      String ID we are solving
     *                           for
     * @param  {Object} target   Resulting object.
     * @param  {Object} config   Conf object stored with the
     *                           key.
     * @param  {Object} options  Conf object passed in the
     *                           method call.
     * @return {Gioc}
     */
    Gioc.prototype.prepare = function(key, target, config, options){
        (this.providers).map(function(provider){
            provider.call(this, key, target, config, options);
        }, this);
        return this;
    };

    /**
     * Retrieve the payload value for the given *key*.
     * If the payload is a literal value, we just return
     * it without any further steps. If the payload is a
     * function then we execute it with the **scope** and
     * **args** provided in the config object.
     *
     * @param  {String} key      String ID we are solving
     *                           for
     * @param  {Object} options  Conf object passed in the
     *                           method call.
     * @return {Primitive|Object} Raw stored value for key.
     */
    Gioc.prototype.build = function(key, options){
        //TODO: If we try to solve an unregistered key, what should
        //we do? Throw or return undefined?
        if (!this.mapped(key)) return undefined;

        var bean   = this.beans[key],
            value  = bean.load;

        //TODO: REFACTOR, CLEAN UP!!
        if(options &&
           this.factoryKey in options &&
           !options[this.factoryKey]) return value;

        if(!bean.construct) return value;

        var config = this.extend(key, {scope:this}, bean.config, options),
            args   = config.args,
            scope  = config.scope;

        value = value.apply(scope, args);

        return value;
    };

    /**
     * Goes over the `config` object's keys
     * and for any of its keys that has a related solver
     * it will apply the solver method to the
     * target.
     * Meant to be overridden with use.
     * Default solvers are the `extend` method
     * for the key `props` and `solveDependencies`
     * for the `deps` key.
     *
     * @param  {String} key      String ID we are solving
     *                           for
     * @param  {Primitive|Object} target Solved value for the
     *                                   provided key.
     * @param  {Object} config  Configuration object.
     * @return {Primitive|object}        Solved value.
     */
    Gioc.prototype.wire = function(key, target, config){
        config = config || {};

        //We have a literal value. We might want to modify it?
        if(typeof target !== 'object' && this.modKey in config)
            return config[this.modKey](target);

        /*
         * solve is the intersection between all keys in the
         * config object and the solvers.
         */
        var keys  = Object.keys(this.solvers);
        var solve = Object.keys(config).filter(function(k){ return keys.indexOf(k) !== -1;});
        solve.map(function(ckey){
            (this.solvers[ckey]).map(function(solver){
                solver.call(this, key, target, config[ckey]);
            }, this);
        }, this);

        return target;
    };

    /**
     * Injects a dependency into the provided `scope`
     *
     * @param  {String} key     String ID we are solving
     *                          for
     * @param  {Object} scope   Target to be injected
     * @param  {Object} options Options object.
     * @return {this}
     */
    Gioc.prototype.inject = function (key, scope, options) {
        if(! this.mapped(key)) return this.logger.error(key, 'Provided key is not mapped.');

        var setter = options.setter || key,
            value  = this.solve(key, options);

        //It could be that we were unable to solve for key, how do
        //we handle it? Do we break the whole chain?
        if(value === undefined) return this;

        if( typeof setter === 'function') setter.call(scope, value, key);
        else if( typeof setter === 'string') scope[setter] = value;

        //TODO: We should treat this as an array
        //TODO: This should be the 'initialize' phase!
        if(this.postKey in options) options[this.postKey].apply(scope, options[this.postArgs]);

        return this;
    };

    /**
     * During the solve cycle, the post process applied
     * after the payload has been configured, built, and
     * wired.
     * @param  {String} key     [description]
     * @param  {Primitive|Object} target  Result from solving key.
     * @param  {Object} options Options object.
     * @return {Gioc}
     */
    Gioc.prototype.post = function(key, target, options){
        this.logger.log('post ', this.editors);
        (this.editors).map(function(editor){
            this.logger.log('editor ', editor, ' key ', key, ' target ', target, ' options ', options);
            editor.call(this, key, target, options);
        }, this);
        return this;
    };

    /**
     * Checks to see if *key* is currently
     * mapped.
     *
     * @param  {String} key Definition id.
     * @return {Boolean}    Does a definition
     *                      with this key exist?
     */
    Gioc.prototype.mapped = function(key){
        return (key in this.beans);
    };

    /**
     * Solve an array of dependencies.
     *
     * @param  {Object} scope    Scope to which dependencies
     *                           will be applied to.
     * @param  {[type]} mappings Array contained deps.
     *                           definitions
     * @return {this}
     */
    Gioc.prototype.solveDependencies = function(key, scope, mappings){

        (this.graph = this.graph || {}) && ( this.graph[key] = key );

        /*
         * Try to add a dependency solver based on requirejs
         * We might have to keep track of dependencies, and only
         * execute next solver if the previous one failed, so we
         * might want to modify the array as we go, to remove the
         * key from the next loop.
         * We should use `every` or `some` instead of map.
         */
        mappings.map(function(bean){
            //Normalize bean def, if string, we ride on defaults.
            if(typeof bean === 'string') {
                bean = {id: bean, options: {setter: bean}};
            }

            //Catch circular dependency
            if(bean.id in this.graph){
                return this.error(key, 'ERROR: Circular Dependency detected.');
            }

            //store current bean id for CD.
            this.graph[bean.id] = key;

            //We should try catch this.
            this.inject(bean.id, scope, bean.options);

            //Bean resolved, move on.
            delete this.graph[bean.id];

        }, this);

        this.logger.log(key, this.graph);

        return this;
    };

    /**
     * Error handler. If `strictErrors` is true
     * it will throw the error. Else it will be
     * handled by `logger`.
     * @param  {String} key     Error key
     * @param  {String} message Message
     * @return {void}
     */
    Gioc.prototype.error = function(key, message){
        if(this.strictErrors === true) throw new Error(key, message);
        this.logger.error(key, message);
    };

    /**
     * Solvers are functions that handle
     * specific `key`s in the configuration
     * bean.
     * We add solvers by key.
     *
     * @param {String} key    Solver ID
     * @param {Function} solver Solver implementation.
     */
    Gioc.prototype.addSolver = function(key, solver){
        (this.solvers[key] || (this.solvers[key] = [])).push(solver);
    };

    /**
     * Add a post solver function.
     * @param {Function} editor Post editor
     */
    Gioc.prototype.addPost = function(editor){
        if(this.editors.indexOf(editor) === -1){
            this.editors.push(editor);
        }

        return this;
    };

    Gioc.prototype.addProvider = function(provider){
        if(this.providers.indexOf(provider) === -1){
            this.providers.push(provider);
        }

        return this;
    };

    Gioc.prototype.resetGraph = function(key, target, options){
        this.logger.log('=== DELETE GRAPH');
        if(key in this.graph) delete this.graph[key];
    };

    /**
     * Extend method.
     * @param  {Object} target Source object
     * @return {Object}        Resulting object from
     *                         meging target to params.
     */
    Gioc.prototype.extend = function(key, target, options, config){
        var i = 2, length = arguments.length, source;
        for ( ; i < length; i++ ) {
            // Only deal with defined values
            if ((source = arguments[i]) != undefined ){
                Object.getOwnPropertyNames(source).forEach(function(k){
                    var d = Object.getOwnPropertyDescriptor(source, k) || {value:source[k]};
                    if (d.get) {
                        target.__defineGetter__(k, d.get);
                        if (d.set) target.__defineSetter__(k, d.set);
                    } else if (target !== d.value) target[k] = d.value;
                });
            }
        }
        return target;
    };

    /**
     * Stub method for logger.
     * By default is mapped to console.
     */
    Gioc.prototype.logger = console;

    return Gioc;
});<commit_msg>TODO: When adding external solvers- requirejs- we need also to check on different context during mapped calls.<commit_after>/*
 * gioc
 * https://github.com/goliatone/gioc
 *
 * TODO: Refactor processor related methods
 *     addSolver
 *     addPost
 *     addProvider
 *     Those should all use the same signature
 *     and work in the same way, then have one method
 *     that creates dynamically them.
 *
 * TODO: prepare and post could be one method, also
 *       dynamically attached.
 *
 * Copyright (c) 2013 goliatone
 * Licensed under the MIT license.
 */
/*global define:true*/
/* jshint strict: false */
define('gioc', function() {

////////////////////////////////////////
/// PRIVATE METHODS
////////////////////////////////////////

    var _slice = Array.prototype.slice;

    var _isFactory = function(bean){
        if(this.factoryKey in bean.config) return bean.config[this.factoryKey];
        return typeof bean.load === 'function';
    };

////////////////////////////////////////
/// CONSTRUCTOR
////////////////////////////////////////

    /**
     * Gioc constructor.
     *
     * @param  {Object} config Optional config object.
     */
    var Gioc = function Gioc(config){
        //Store all bean info.
        this.beans = {};
        this.graph = {};

        this.solvers = {};
        this.editors = [];

        this.providers = [];

        this.configure(config);

        //Solvers methods should have a common signature:
        //id, target, options (which should be similar throught all methods)

        this.addSolver(this.propKey, this.extend);
        this.addSolver(this.depsKey, this.solveDependencies);

        //TODO: Should addPost and addProvider have the
        //same signature as addSolver?
        this.addPost(this.resetGraph);

        this.addProvider(this.extend);
    };

////////////////////////////////////////
/// STATIC VARS
////////////////////////////////////////
    //TODO: Add support for chain props.
    //TODO: If we modify this, ie unit testing,
    //      then we cant reset. REMOVE STATIC or have
    //      a default src.
    Gioc.config = {
        attributes:[
            'depsKey', 'propKey', 'postKey',
            'postArgs', 'modKey', 'factoryKey',
            'strictErrors'
        ],
        defaults:{
            depsKey: 'deps',
            propKey: 'props',
            postKey: 'after',
            modKey:   'modifier',
            postArgs: 'pargs',
            factoryKey:'construct',
            strictErrors: false
        }
    };
////////////////////////////////////////
/// PUBLIC METHODS
////////////////////////////////////////
    /**
     * Handles configuration.
     * @param  {Object} config Configuration object
     * @return {this}
     */
    Gioc.prototype.configure = function(config){
        //TODO: This should be configurable.
        /* Default supported directives:
         * - deps: dependencies
         * - post: post wire execution
         * - props: add props to instantiated value.
         */
        config = this.extend({}, Gioc.config.defaults, config);
        Gioc.config.attributes.map(function(ckey){
            this[ckey] = config[ckey];
        }, this);

        return this;
    };

    /**
     * Stores a definition, with configuration
     * options, to be *solved* later.
     * The payload can be either a literal value
     * or a factory method.
     *
     * @param  {String} key      String ID.
     * @param  {Object|Function} payload Value to be map.
     * @param  {Object} config   Options for current mapping.
     * @return {Gioc}
     */
    Gioc.prototype.map = function(key, payload, config){
        //Store basic information of our payload.
        var bean = {key:key, load:payload, config:(config || {})};

        //Is this a factory or a literal value?
        bean.construct =
        bean.isFactory = _isFactory.call(this, bean);

        //TODO: How do we want to handle collision? We are overriding.
        if(this.mapped(key)) this.logger.warn(key, 'key is already mapped, overriding.');

        this.beans[key] = bean;

        return this;
    };

    /**
     * Solve for the provided *key*, it returns a value
     * and solves all dependencies, etc.
     * The cycle to solve for a key is the
     * result of runing the methods in order
     * `prepare` as a pre process, `build` to
     * generate the payload, `wire` to solve
     * dependencies, and `post` as a post process.
     *
     * @param  {String} key      String ID we are solving
     *                           for
     * @param  {Object} options   Options object.
     * @return {Object|undefined} Solved value
     *                            for the given key.
     */
    Gioc.prototype.solve = function(key, options){
        //TODO: If we try to solve an unregistered key, what should
        //we do? Throw or return undefined?
        if (!this.mapped(key)) return undefined;

        var value  = null,
            bean   = this.beans[key],
            config = {};
        //pre-process
        this.prepare(key, config, bean.config, options);

        this.logger.log('==> solve, generated config ', config);
        //build our value.
        value = this.build(key, config);

        //configure our value
        value = this.wire(key, value, config);

        //do all post 'construct' operations.
        this.post(key, value, config);

        return value;
    };

    /**
     * Pre process to consolidate the configuration
     * object for a given key.
     * It will loop over all providers in order the order
     * they were added and call the provider with the
     * Gioc instance as scope.
     * The default provider is the `extend` method.
     *
     * @param  {String} key      String ID we are solving
     *                           for
     * @param  {Object} target   Resulting object.
     * @param  {Object} config   Conf object stored with the
     *                           key.
     * @param  {Object} options  Conf object passed in the
     *                           method call.
     * @return {Gioc}
     */
    Gioc.prototype.prepare = function(key, target, config, options){
        (this.providers).map(function(provider){
            provider.call(this, key, target, config, options);
        }, this);
        return this;
    };

    /**
     * Retrieve the payload value for the given *key*.
     * If the payload is a literal value, we just return
     * it without any further steps. If the payload is a
     * function then we execute it with the **scope** and
     * **args** provided in the config object.
     *
     * @param  {String} key      String ID we are solving
     *                           for
     * @param  {Object} options  Conf object passed in the
     *                           method call.
     * @return {Primitive|Object} Raw stored value for key.
     */
    Gioc.prototype.build = function(key, options){
        //TODO: If we try to solve an unregistered key, what should
        //we do? Throw or return undefined?
        if (!this.mapped(key)) return undefined;

        var bean   = this.beans[key],
            value  = bean.load;

        //TODO: REFACTOR, CLEAN UP!!
        if(options &&
           this.factoryKey in options &&
           !options[this.factoryKey]) return value;

        if(!bean.construct) return value;

        var config = this.extend(key, {scope:this}, bean.config, options),
            args   = config.args,
            scope  = config.scope;

        value = value.apply(scope, args);

        return value;
    };

    /**
     * Goes over the `config` object's keys
     * and for any of its keys that has a related solver
     * it will apply the solver method to the
     * target.
     * Meant to be overridden with use.
     * Default solvers are the `extend` method
     * for the key `props` and `solveDependencies`
     * for the `deps` key.
     *
     * @param  {String} key      String ID we are solving
     *                           for
     * @param  {Primitive|Object} target Solved value for the
     *                                   provided key.
     * @param  {Object} config  Configuration object.
     * @return {Primitive|object}        Solved value.
     */
    Gioc.prototype.wire = function(key, target, config){
        config = config || {};

        //We have a literal value. We might want to modify it?
        if(typeof target !== 'object' && this.modKey in config)
            return config[this.modKey](target);

        /*
         * solve is the intersection between all keys in the
         * config object and the solvers.
         */
        var keys  = Object.keys(this.solvers);
        var solve = Object.keys(config).filter(function(k){ return keys.indexOf(k) !== -1;});
        solve.map(function(ckey){
            (this.solvers[ckey]).map(function(solver){
                solver.call(this, key, target, config[ckey]);
            }, this);
        }, this);

        return target;
    };

    /**
     * Injects a dependency into the provided `scope`
     *
     * @param  {String} key     String ID we are solving
     *                          for
     * @param  {Object} scope   Target to be injected
     * @param  {Object} options Options object.
     * @return {this}
     */
    Gioc.prototype.inject = function (key, scope, options) {
        if(! this.mapped(key)) return this.logger.error(key, 'Provided key is not mapped.');

        var setter = options.setter || key,
            value  = this.solve(key, options);

        //It could be that we were unable to solve for key, how do
        //we handle it? Do we break the whole chain?
        if(value === undefined) return this;

        if( typeof setter === 'function') setter.call(scope, value, key);
        else if( typeof setter === 'string') scope[setter] = value;

        //TODO: We should treat this as an array
        //TODO: This should be the 'initialize' phase!
        if(this.postKey in options) options[this.postKey].apply(scope, options[this.postArgs]);

        return this;
    };

    /**
     * During the solve cycle, the post process applied
     * after the payload has been configured, built, and
     * wired.
     * @param  {String} key     [description]
     * @param  {Primitive|Object} target  Result from solving key.
     * @param  {Object} options Options object.
     * @return {Gioc}
     */
    Gioc.prototype.post = function(key, target, options){
        this.logger.log('post ', this.editors);
        (this.editors).map(function(editor){
            this.logger.log('editor ', editor, ' key ', key, ' target ', target, ' options ', options);
            editor.call(this, key, target, options);
        }, this);
        return this;
    };

    /**
     * Checks to see if *key* is currently
     * mapped.
     * TODO: If we add external solvers, then
     *       this check might be outdated. We
     *       need to add solver+mapper!
     *
     * @param  {String} key Definition id.
     * @return {Boolean}    Does a definition
     *                      with this key exist?
     */
    Gioc.prototype.mapped = function(key){
        return  this.beans.hasOwnProperty(key);
    };

    /**
     * Solve an array of dependencies.
     *
     * @param  {Object} scope    Scope to which dependencies
     *                           will be applied to.
     * @param  {[type]} mappings Array contained deps.
     *                           definitions
     * @return {this}
     */
    Gioc.prototype.solveDependencies = function(key, scope, mappings){

        (this.graph = this.graph || {}) && ( this.graph[key] = key );

        /*
         * Try to add a dependency solver based on requirejs
         * We might have to keep track of dependencies, and only
         * execute next solver if the previous one failed, so we
         * might want to modify the array as we go, to remove the
         * key from the next loop.
         * We should use `every` or `some` instead of map.
         */
        mappings.map(function(bean){
            //Normalize bean def, if string, we ride on defaults.
            if(typeof bean === 'string') {
                bean = {id: bean, options: {setter: bean}};
            }

            //Catch circular dependency
            if(bean.id in this.graph){
                return this.error(key, 'ERROR: Circular Dependency detected.');
            }

            //store current bean id for CD.
            this.graph[bean.id] = key;

            //We should try catch this.
            this.inject(bean.id, scope, bean.options);

            //Bean resolved, move on.
            delete this.graph[bean.id];

        }, this);

        this.logger.log(key, this.graph);

        return this;
    };

    /**
     * Error handler. If `strictErrors` is true
     * it will throw the error. Else it will be
     * handled by `logger`.
     * @param  {String} key     Error key
     * @param  {String} message Message
     * @return {void}
     */
    Gioc.prototype.error = function(key, message){
        if(this.strictErrors === true) throw new Error(key, message);
        this.logger.error(key, message);
    };

    /**
     * Solvers are functions that handle
     * specific `key`s in the configuration
     * bean.
     * We add solvers by key.
     *
     * @param {String} key    Solver ID
     * @param {Function} solver Solver implementation.
     */
    Gioc.prototype.addSolver = function(key, solver){
        (this.solvers[key] || (this.solvers[key] = [])).push(solver);
    };

    /**
     * Add a post solver function.
     * @param {Function} editor Post editor
     */
    Gioc.prototype.addPost = function(editor){
        if(this.editors.indexOf(editor) === -1){
            this.editors.push(editor);
        }

        return this;
    };

    Gioc.prototype.addProvider = function(provider){
        if(this.providers.indexOf(provider) === -1){
            this.providers.push(provider);
        }

        return this;
    };

    Gioc.prototype.resetGraph = function(key, target, options){
        this.logger.log('=== DELETE GRAPH');
        if(key in this.graph) delete this.graph[key];
    };

    /**
     * Extend method.
     * @param  {Object} target Source object
     * @return {Object}        Resulting object from
     *                         meging target to params.
     */
    Gioc.prototype.extend = function(key, target, options, config){
        var i = 2, length = arguments.length, source;
        for ( ; i < length; i++ ) {
            // Only deal with defined values
            if ((source = arguments[i]) != undefined ){
                Object.getOwnPropertyNames(source).forEach(function(k){
                    var d = Object.getOwnPropertyDescriptor(source, k) || {value:source[k]};
                    if (d.get) {
                        target.__defineGetter__(k, d.get);
                        if (d.set) target.__defineSetter__(k, d.set);
                    } else if (target !== d.value) target[k] = d.value;
                });
            }
        }
        return target;
    };

    /**
     * Stub method for logger.
     * By default is mapped to console.
     */
    Gioc.prototype.logger = console;

    return Gioc;
});<|endoftext|>


<commit_before>// @flow

import type { Vector3 } from './vector.js'
import type { Side } from './mesh.js'
import type { Chunk } from './chunk.js'

import {
  append,
  concat,
  filter,
  find,
  findIndex,
  forEach,
  map,
  reduce,
  update,
} from 'ramda'

const vector = require('./vector');
const chunk = require('./chunk');

const grid = {};

export type Grid<T> = Array<Array<Array<T>>>
export type GetterFn<T> = (emptyCell: T, g: Grid<T>, pos: Vector3) => T
export type GridCell<T> = {
  position: Vector3,
  value: T,
}

grid.getIdentity = <T>(emptyCell: T): Grid<T> => {
  return grid.create({
    size: {x: 0, y: 0, z: 0},
    fillEmptyWith: emptyCell,
    values: [],
  })
}

type GridArgs<T> = {
  size: Vector3,
  // voxelTypes?: Array,
  fillEmptyWith: T,
  values: Array<GridCell<T>>,
}

grid.create = <T>(args: GridArgs<T>): Grid<T> => {
  const size = args.size
  if(size == null) {
    return grid.getIdentity(args.fillEmptyWith);
  }
  if(!vector.isValid(args.size)) {
    return grid.getIdentity(args.fillEmptyWith)
  }
  const values = args.values
  var g = new Array(size.x);
  for(var x = 0; x < g.length; ++x) {
    var yArray = g[x] = new Array(size.y);
    for(var y = 0; y < yArray.length; ++y) {
      var zArray = yArray[y] = new Array(size.z);
      for(var z = 0; z < zArray.length; ++z) {
        zArray[z] = args.fillEmptyWith
      }
    }
  }

  forEach(function(tuple) {
    var position = tuple.position;
    var value = tuple.value;
    g[position.x][position.y][position.z] = value;
  }, values);
  return g;
}

grid.set = <T>(
  emptyCell: T,
  g: Grid<T>,
  p: Vector3,
  v: T
): Grid<T> => {
  if(!grid.isInBounds(g, p)) {
    return g;
  }
  const cells = grid.flatten(emptyCell, g);
  // TODO: Handle out of bounds
  const removed = filter(({position, value}) => p != position, cells);
  const added = append({position: p, value: v}, removed);
  return grid.create({
    size: grid.getSize(g),
    values: added,
    fillEmptyWith: emptyCell,
  });
};

grid.isInBounds = <T>(g: Grid<T>, p: Vector3): bool => {
  const size = grid.getSize(g);
  if(p.x < 0 || p.y < 0 || p.z < 0) {
    return false;
  }
  else if(size.x > p.x && size.y > p.y && size.z > p.z) {
    return true;
  }
  else {
    return false;
  }
};

grid.get = <T>(emptyCell: T, g: Grid<T>, position: Vector3): T => {
  if(!grid.isInBounds(g, position)) return emptyCell;
  var xArray = g[position.x];
  if(xArray === null) return emptyCell;
  var yArray = xArray[position.y];
  if(yArray === null) return emptyCell;
  return yArray[position.z];
}

grid.getSize = <T>(g: Grid<T>): Vector3 => {
  if(g != null && g[0] != null && g[0][0] != null) {
    var x = g.length;
    var y = g[0].length;
    var z = g[0][0].length;
    return {x: x, y: y, z: z};
  }
  else {
    return { x: 0, y: 0, z: 0 };
  }
}

grid.generate = <T>(
  matIndex: (p: Vector3, s: Side, v: T) => number,
  emptyCell: T,
  g: Grid<T>,
  chunkSize: Vector3,
  voxelSize: number,
): Grid<Chunk> => {
  var gridSize = grid.getSize(g);
  var chunkXLength = parseInt(gridSize.x / chunkSize.x);
  var chunkYLength = parseInt(gridSize.y / chunkSize.y);
  var chunkZLength = parseInt(gridSize.z / chunkSize.z);

  if(gridSize.x % chunkSize.x != 0) ++chunkXLength;
  if(gridSize.y % chunkSize.y != 0) ++chunkYLength;
  if(gridSize.z % chunkSize.z != 0) ++chunkZLength;

  var chunkDimensions = {x: chunkXLength, y: chunkYLength, z: chunkZLength};
  var chunks = [];
  for(var x = 0; x < chunkXLength; ++x) {
    for(var y = 0; y < chunkYLength; ++y) {
      for(var z = 0; z < chunkZLength; ++z) {
        var position = {x: x, y: y, z: z};
        var chunkMeshData = chunk.generate(
          matIndex,
          emptyCell,
          g,
          grid.get,
          chunkSize,
          position,
          voxelSize
        );
        chunks.push({position: position, value: chunkMeshData});
      }
    }
  }
  var chunkGrid = grid.create({
    size: chunkDimensions,
    values: chunks,
    fillEmptyWith: chunk.empty,
  })
  return chunkGrid;
};

export type GridIteratorResult<T> = {
  done: bool,
  value: GridCell<T>,
}

export type GridIterator<T> = {
  next: () => GridIteratorResult<T>,
}

grid.getIterator = <T>(emptyCell: T, g: Grid<T>): GridIterator<T> => {
  let x = 0
  let y = 0
  let z = 0
  const gridSize = grid.getSize(g)
  let lastCell = false

  const iterator = {
    next: (): GridIteratorResult<T> => {
      let done = lastCell
      if(done) {
        return {
          done: true,
          value: { position: {x: 0, y: 0, z: 0}, value: emptyCell},
        }
      }
      else {
        var position = {x: x, y: y, z: z};
        ++x;
        if(x >= gridSize.x) {
          x = 0
          ++y
          if(y >= gridSize.y) {
            y = 0
            ++z
            if(z >= gridSize.z) {
              lastCell = true
            }
          }
        }
        const cellValue = grid.get(emptyCell, g, position)
        const iteration = {
          done: done,
          value: {position: position, value: cellValue},
        };
        return iteration
      }
    },
    // TODO: Polyfill this or something
    [Symbol.iterator]: () => iterator,
  }
  return iterator
}

// Gives back a flat list of {position, value} for each cell.
grid.flatten = <T>(emptyCell: T, g: Grid<T>): Array<GridCell<T>> => {
  var values = [];
  var iterator = grid.getIterator(emptyCell, g);
  while(true) {
    const value = iterator.next()
    if(value.done) {
      break;
    }
    values.push(value.value);
  }
  return values;
};

var logged = false;
grid.merge = <T>(
  emptyCell: T,
  finalGrid: Grid<T>,
  currentGrid: Grid<T>
): Grid<T> => {
  const values = map(function(cell) {
    const position = cell.position
    const value = cell.value
    const currentValue = grid.get(emptyCell, currentGrid, position);
    const newValue = currentValue === emptyCell ? value : currentValue
    return {position: position, value: newValue};
  }, grid.flatten(emptyCell, finalGrid));
  const newGrid = grid.create({
    size: grid.getSize(finalGrid),
    values: values,
    fillEmptyWith: emptyCell,
  })
  return newGrid
}

grid.blit = <T>(
  emptyCell: T,
  target: Grid<T>,
  offset: Vector3,
  source: Grid<T>,
): Grid<T> => {
  const originalValues = grid.flatten(emptyCell, target)
  const sourceValues = map((cell) => {
    const offsetCellPosition = vector.plus(cell.position, offset)
    const newCell = { value: cell.value, position: offsetCellPosition }
    return newCell
  }, filter(c => {
    return c.value != emptyCell
  }, grid.flatten(emptyCell, source)))

  const combinedValues = reduce((acc, cell) => {
    const i = findIndex(c => c.position == cell.position, acc)
    if(i > -1) {
      return update(i, cell, acc)
    } else {
      return append(cell, acc)
    }
  }, originalValues, sourceValues)

  const newGrid = grid.create({
    size: grid.getSize(target),
    fillEmptyWith: emptyCell,
    // values,
    values: combinedValues,
  })
  return newGrid
}

module.exports = grid
<commit_msg>provide better info when getting a cube that is out of bounds<commit_after>// @flow

import type { Vector3 } from './vector.js'
import type { Side } from './mesh.js'
import type { Chunk } from './chunk.js'

import {
  append,
  concat,
  filter,
  find,
  findIndex,
  forEach,
  map,
  reduce,
  update,
} from 'ramda'

const vector = require('./vector');
const chunk = require('./chunk');

const grid = {};

export type Grid<T> = Array<Array<Array<T>>>
export type GetterFn<T> = (emptyCell: T, g: Grid<T>, pos: Vector3) => T
export type GridCell<T> = {
  position: Vector3,
  value: T,
}

grid.getIdentity = <T>(emptyCell: T): Grid<T> => {
  return grid.create({
    size: {x: 0, y: 0, z: 0},
    fillEmptyWith: emptyCell,
    values: [],
  })
}

type GridArgs<T> = {
  size: Vector3,
  // voxelTypes?: Array,
  fillEmptyWith: T,
  values: Array<GridCell<T>>,
}

grid.create = <T>(args: GridArgs<T>): Grid<T> => {
  const size = args.size
  if(size == null) {
    return grid.getIdentity(args.fillEmptyWith);
  }
  if(!vector.isValid(args.size)) {
    return grid.getIdentity(args.fillEmptyWith)
  }
  const values = args.values
  var g = new Array(size.x);
  for(var x = 0; x < g.length; ++x) {
    var yArray = g[x] = new Array(size.y);
    for(var y = 0; y < yArray.length; ++y) {
      var zArray = yArray[y] = new Array(size.z);
      for(var z = 0; z < zArray.length; ++z) {
        zArray[z] = args.fillEmptyWith
      }
    }
  }

  forEach(function(tuple) {
    var position = tuple.position;
    var value = tuple.value;
    try {
      g[position.x][position.y][position.z] = value;
    } catch(e) {
      throw new Error(`Position ${JSON.stringify(position)} is not valid for ${JSON.stringify(grid.getSize(g))}`)
    }
  }, values);
  return g;
}

grid.set = <T>(
  emptyCell: T,
  g: Grid<T>,
  p: Vector3,
  v: T
): Grid<T> => {
  if(!grid.isInBounds(g, p)) {
    return g;
  }
  const cells = grid.flatten(emptyCell, g);
  // TODO: Handle out of bounds
  const removed = filter(({position, value}) => p != position, cells);
  const added = append({position: p, value: v}, removed);
  return grid.create({
    size: grid.getSize(g),
    values: added,
    fillEmptyWith: emptyCell,
  });
};

grid.isInBounds = <T>(g: Grid<T>, p: Vector3): bool => {
  const size = grid.getSize(g);
  if(p.x < 0 || p.y < 0 || p.z < 0) {
    return false;
  }
  else if(size.x > p.x && size.y > p.y && size.z > p.z) {
    return true;
  }
  else {
    return false;
  }
};

grid.get = <T>(emptyCell: T, g: Grid<T>, position: Vector3): T => {
  if(!grid.isInBounds(g, position)) return emptyCell;
  var xArray = g[position.x];
  if(xArray === null) return emptyCell;
  var yArray = xArray[position.y];
  if(yArray === null) return emptyCell;
  return yArray[position.z];
}

grid.getSize = <T>(g: Grid<T>): Vector3 => {
  if(g != null && g[0] != null && g[0][0] != null) {
    var x = g.length;
    var y = g[0].length;
    var z = g[0][0].length;
    return {x: x, y: y, z: z};
  }
  else {
    return { x: 0, y: 0, z: 0 };
  }
}

grid.generate = <T>(
  matIndex: (p: Vector3, s: Side, v: T) => number,
  emptyCell: T,
  g: Grid<T>,
  chunkSize: Vector3,
  voxelSize: number,
): Grid<Chunk> => {
  var gridSize = grid.getSize(g);
  var chunkXLength = parseInt(gridSize.x / chunkSize.x);
  var chunkYLength = parseInt(gridSize.y / chunkSize.y);
  var chunkZLength = parseInt(gridSize.z / chunkSize.z);

  if(gridSize.x % chunkSize.x != 0) ++chunkXLength;
  if(gridSize.y % chunkSize.y != 0) ++chunkYLength;
  if(gridSize.z % chunkSize.z != 0) ++chunkZLength;

  var chunkDimensions = {x: chunkXLength, y: chunkYLength, z: chunkZLength};
  var chunks = [];
  for(var x = 0; x < chunkXLength; ++x) {
    for(var y = 0; y < chunkYLength; ++y) {
      for(var z = 0; z < chunkZLength; ++z) {
        var position = {x: x, y: y, z: z};
        var chunkMeshData = chunk.generate(
          matIndex,
          emptyCell,
          g,
          grid.get,
          chunkSize,
          position,
          voxelSize
        );
        chunks.push({position: position, value: chunkMeshData});
      }
    }
  }
  var chunkGrid = grid.create({
    size: chunkDimensions,
    values: chunks,
    fillEmptyWith: chunk.empty,
  })
  return chunkGrid;
};

export type GridIteratorResult<T> = {
  done: bool,
  value: GridCell<T>,
}

export type GridIterator<T> = {
  next: () => GridIteratorResult<T>,
}

grid.getIterator = <T>(emptyCell: T, g: Grid<T>): GridIterator<T> => {
  let x = 0
  let y = 0
  let z = 0
  const gridSize = grid.getSize(g)
  let lastCell = false

  const iterator = {
    next: (): GridIteratorResult<T> => {
      let done = lastCell
      if(done) {
        return {
          done: true,
          value: { position: {x: 0, y: 0, z: 0}, value: emptyCell},
        }
      }
      else {
        var position = {x: x, y: y, z: z};
        ++x;
        if(x >= gridSize.x) {
          x = 0
          ++y
          if(y >= gridSize.y) {
            y = 0
            ++z
            if(z >= gridSize.z) {
              lastCell = true
            }
          }
        }
        const cellValue = grid.get(emptyCell, g, position)
        const iteration = {
          done: done,
          value: {position: position, value: cellValue},
        };
        return iteration
      }
    },
    // TODO: Polyfill this or something
    [Symbol.iterator]: () => iterator,
  }
  return iterator
}

// Gives back a flat list of {position, value} for each cell.
grid.flatten = <T>(emptyCell: T, g: Grid<T>): Array<GridCell<T>> => {
  var values = [];
  var iterator = grid.getIterator(emptyCell, g);
  while(true) {
    const value = iterator.next()
    if(value.done) {
      break;
    }
    values.push(value.value);
  }
  return values;
};

var logged = false;
grid.merge = <T>(
  emptyCell: T,
  finalGrid: Grid<T>,
  currentGrid: Grid<T>
): Grid<T> => {
  const values = map(function(cell) {
    const position = cell.position
    const value = cell.value
    const currentValue = grid.get(emptyCell, currentGrid, position);
    const newValue = currentValue === emptyCell ? value : currentValue
    return {position: position, value: newValue};
  }, grid.flatten(emptyCell, finalGrid));
  const newGrid = grid.create({
    size: grid.getSize(finalGrid),
    values: values,
    fillEmptyWith: emptyCell,
  })
  return newGrid
}

grid.blit = <T>(
  emptyCell: T,
  target: Grid<T>,
  offset: Vector3,
  source: Grid<T>,
): Grid<T> => {
  const originalValues = grid.flatten(emptyCell, target)
  const sourceValues = map((cell) => {
    const offsetCellPosition = vector.plus(cell.position, offset)
    const newCell = { value: cell.value, position: offsetCellPosition }
    return newCell
  }, filter(c => {
    return c.value != emptyCell
  }, grid.flatten(emptyCell, source)))

  const combinedValues = reduce((acc, cell) => {
    const i = findIndex(c => c.position == cell.position, acc)
    if(i > -1) {
      return update(i, cell, acc)
    } else {
      return append(cell, acc)
    }
  }, originalValues, sourceValues)

  const newGrid = grid.create({
    size: grid.getSize(target),
    fillEmptyWith: emptyCell,
    // values,
    values: combinedValues,
  })
  return newGrid
}

module.exports = grid
<|endoftext|>