<commit_before>/*
 A teapot with an orbiting directional light source that you can rotate with the mouse.

 The white dot is not the actual position of the light - it just indicates its direction
 relative to the teapot.

 Lindsay S. Kay,
 lindsay.kay@xeolabs.com

 https://github.com/xeolabs/scenejs/wiki/light

 */

SceneJS.createScene({
    type: "scene",
    id:"theScene",
    canvasId: "theCanvas",
    loggingElementId: "theLoggingDiv",

    nodes: [
        {
            type: "lookAt",
            eye : { x: 0.0, y: 10.0, z: 35 },
            look : { y:1.0 },
            up : { y: 1.0 },

            nodes: [
                {
                    type: "camera",
                    optics: {
                        type: "perspective",
                        fovy : 55.0,
                        aspect : 1.47,
                        near : 0.10,
                        far : 300.0
                    },

                    nodes: [

                        /*---------------------------------------------------------------------------------
                         * Our animated light source is rotated using a Quaternion node which
                         * receives rotation updates through configs injected into the scene when
                         * it is rendered
                         * -------------------------------------------------------------------------------*/

                        {
                            type: "quaternion",
                            id: "myQuaternion",

                            nodes: [
                                {
                                    type: "light",

                                    mode: "dir",

                                    /* Colour of our light
                                     */
                                    color: { r: 1.0, g: 1.0, b: 0.0 },

                                    /* Our light will contribute to both the quantities of
                                     * specular and diffuse light that will hit our teapot.
                                     */
                                    diffuse: true,
                                    specular: true,

                                    /* The directional light's direction, a vector from the
                                     * origin of this coordinate system (which is in this
                                     * case the view coordinate system, since our light is
                                     * not within any no modelling transform nodes).
                                     *
                                     * The direction vector is calculated by mouse handlers and
                                     * injected into the scene's render method.
                                     */
                                    dir: {  x: 0, y: 0, z: -1.0 }

                                    /* Note the absence of attenuation properties;
                                     * unlike a point light, a directional light has no
                                     * position, and is therefore not subject to attenuation
                                     * since it is at an infinite distance.
                                     */
                                },

                                /*----------------------------------------------------------
                                 * A sphere that marks the light's direction - not the focus
                                 * of this example
                                 * -------------------------------------------------------*/
                                {
                                    type: "translate",
                                    z: 10 ,
                                    nodes: [
                                        {
                                            type: "material",
                                            baseColor:      { r: .6, g: .6, b: 0.6 },
                                            specularColor:  { r: 0.9, g: 0.9, b: 0.9 },
                                            emit: 0.5,
                                            specular:       0.9,
                                            shine:          6.0,
                                            nodes: [
                                                {
                                                    type: "scale",
                                                    x: 0.5,
                                                    y: 0.5,
                                                    z: 0.5,
                                                    nodes: [
                                                        {
                                                            type: "sphere"
                                                        }
                                                    ]
                                                }
                                            ]
                                        }
                                    ]
                                }
                            ]
                        },

                        /*--------------------------------------------------------------------------
                         * Teapot, rotated and scaled into position within model-space, coloured
                         * with some material properties
                         * ------------------------------------------------------------------------*/

                        {
                            type: "rotate",
                            angle: -20,
                            x : 1.0,
                            nodes: [

                                {
                                    type: "rotate",
                                    angle: 30.0,
                                    y : 1.0,

                                    nodes: [

                                        {
                                            type: "scale",
                                            x: 2,
                                            y: 2,
                                            z: 2,

                                            nodes: [
                                                {
                                                    type: "material",
                                                    baseColor:      { r: 0.9, g: 0.2, b: 0.2 },
                                                    specularColor:  { r: 0.9, g: 0.9, b: 0.2 },
                                                    emit:           0.0,
                                                    specular:       0.9,
                                                    shine:          6.0,

                                                    nodes: [
                                                        {
                                                            type: "teapot"
                                                        }
                                                    ]
                                                }
                                            ]
                                        }
                                    ]
                                }
                            ]
                        }
                    ]
                }
            ]
        }
    ]
});

/*---------------------------------------------------------------------------------------------------------------------
 * Scene rendering loop and mouse handler stuff
 *--------------------------------------------------------------------------------------------------------------------*/

var rotx = 0;
var roty = 0;
var lastX;
var lastY;
var dragging = false;

var canvas = document.getElementById("theCanvas");

function mouseDown(event) {
    lastX = event.clientX;
    lastY = event.clientY;
    dragging = true;
}

function mouseUp() {
    dragging = false;
}

var scene = SceneJS.scene("theScene");

/* On a mouse drag, we'll re-render the scene, passing in
 * incremented angles in each time.
 */
function mouseMove(event) {
    if (dragging) {
        roty = (event.clientX - lastX);
        rotx = (event.clientY - lastY);

        if (Math.abs(roty) > Math.abs(rotx)) {

            scene.findNode("myQuaternion").add({
                rotation:{
                    y: 1,
                    angle: roty
                }
            });

        } else {

            scene.findNode("myQuaternion").add({
                rotation:{
                    x: 1,
                    angle: rotx
                }
            });
        }
        lastX = event.clientX;
        lastY = event.clientY;
    }
}

canvas.addEventListener('mousedown', mouseDown, true);
canvas.addEventListener('mousemove', mouseMove, true);
canvas.addEventListener('mouseup', mouseUp, true);

SceneJS.bind("error", function(e) {
    alert(e.exception.message);
});

scene.start();
<commit_msg>Support for touch devices<commit_after>/*
 A teapot with an orbiting directional light source that you can rotate with the mouse.

 The white dot is not the actual position of the light - it just indicates its direction
 relative to the teapot.

 Lindsay S. Kay,
 lindsay.kay@xeolabs.com

 https://github.com/xeolabs/scenejs/wiki/light

 */

SceneJS.createScene({
    type: "scene",
    id:"theScene",
    canvasId: "theCanvas",
    loggingElementId: "theLoggingDiv",

    nodes: [
        {
            type: "lookAt",
            eye : { x: 0.0, y: 10.0, z: 35 },
            look : { y:1.0 },
            up : { y: 1.0 },

            nodes: [
                {
                    type: "camera",
                    optics: {
                        type: "perspective",
                        fovy : 55.0,
                        aspect : 1.47,
                        near : 0.10,
                        far : 300.0
                    },

                    nodes: [

                        /*---------------------------------------------------------------------------------
                         * Our animated light source is rotated using a Quaternion node which
                         * receives rotation updates through configs injected into the scene when
                         * it is rendered
                         * -------------------------------------------------------------------------------*/

                        {
                            type: "quaternion",
                            id: "myQuaternion",

                            nodes: [
                                {
                                    type: "light",

                                    mode: "dir",

                                    /* Colour of our light
                                     */
                                    color: { r: 1.0, g: 1.0, b: 0.0 },

                                    /* Our light will contribute to both the quantities of
                                     * specular and diffuse light that will hit our teapot.
                                     */
                                    diffuse: true,
                                    specular: true,

                                    /* The directional light's direction, a vector from the
                                     * origin of this coordinate system (which is in this
                                     * case the view coordinate system, since our light is
                                     * not within any no modelling transform nodes).
                                     *
                                     * The direction vector is calculated by mouse handlers and
                                     * injected into the scene's render method.
                                     */
                                    dir: {  x: 0, y: 0, z: -1.0 }

                                    /* Note the absence of attenuation properties;
                                     * unlike a point light, a directional light has no
                                     * position, and is therefore not subject to attenuation
                                     * since it is at an infinite distance.
                                     */
                                },

                                /*----------------------------------------------------------
                                 * A sphere that marks the light's direction - not the focus
                                 * of this example
                                 * -------------------------------------------------------*/
                                {
                                    type: "translate",
                                    z: 10 ,
                                    nodes: [
                                        {
                                            type: "material",
                                            baseColor:      { r: .6, g: .6, b: 0.6 },
                                            specularColor:  { r: 0.9, g: 0.9, b: 0.9 },
                                            emit: 0.5,
                                            specular:       0.9,
                                            shine:          6.0,
                                            nodes: [
                                                {
                                                    type: "scale",
                                                    x: 0.5,
                                                    y: 0.5,
                                                    z: 0.5,
                                                    nodes: [
                                                        {
                                                            type: "sphere"
                                                        }
                                                    ]
                                                }
                                            ]
                                        }
                                    ]
                                }
                            ]
                        },

                        /*--------------------------------------------------------------------------
                         * Teapot, rotated and scaled into position within model-space, coloured
                         * with some material properties
                         * ------------------------------------------------------------------------*/

                        {
                            type: "rotate",
                            angle: -20,
                            x : 1.0,
                            nodes: [

                                {
                                    type: "rotate",
                                    angle: 30.0,
                                    y : 1.0,

                                    nodes: [

                                        {
                                            type: "scale",
                                            x: 2,
                                            y: 2,
                                            z: 2,

                                            nodes: [
                                                {
                                                    type: "material",
                                                    baseColor:      { r: 0.9, g: 0.2, b: 0.2 },
                                                    specularColor:  { r: 0.9, g: 0.9, b: 0.2 },
                                                    emit:           0.0,
                                                    specular:       0.9,
                                                    shine:          6.0,

                                                    nodes: [
                                                        {
                                                            type: "teapot"
                                                        }
                                                    ]
                                                }
                                            ]
                                        }
                                    ]
                                }
                            ]
                        }
                    ]
                }
            ]
        }
    ]
});

/*---------------------------------------------------------------------------------------------------------------------
 * Scene rendering loop and mouse handler stuff
 *--------------------------------------------------------------------------------------------------------------------*/

var rotx = 0;
var roty = 0;
var lastX;
var lastY;
var dragging = false;

var canvas = document.getElementById("theCanvas");

function mouseDown(event) {
    lastX = event.clientX;
    lastY = event.clientY;
    dragging = true;
}

function touchStart(event) {
    lastX = event.targetTouches[0].clientX;
    lastY = event.targetTouches[0].clientY;
    dragging = true;
}

function mouseUp() {
    dragging = false;
}

function touchEnd() {
    dragging = false;
}

var scene = SceneJS.scene("theScene");

/* On a mouse or touch drag, we'll re-render the scene, passing in
 * incremented angles in each time.
 */
function mouseMove(event) {
    var posX = event.clientX;
    var posY = event.clientY;
    actionMove(posX,posY);
}

function touchMove(event) {
    var posX = event.targetTouches[0].clientX;
    var posY = event.targetTouches[0].clientY;
    actionMove(posX,posY);
}

function actionMove(posX, posY) {
    if (dragging) {

        roty = posX - lastX;
        rotx = posY - lastY;

        if (Math.abs(roty) > Math.abs(rotx)) {

            scene.findNode("myQuaternion").add({
                rotation:{
                    y: 1,
                    angle: roty
                }
            });

        } else {

            scene.findNode("myQuaternion").add({
                rotation:{
                    x: 1,
                    angle: rotx
                }
            });
        }
        lastX = posX;
        lastY = posY;
    }
}

canvas.addEventListener('mousedown', mouseDown, true);
canvas.addEventListener('mousemove', mouseMove, true);
canvas.addEventListener('mouseup', mouseUp, true);
canvas.addEventListener('touchstart', touchStart, true);
canvas.addEventListener('touchmove', touchMove, true);
canvas.addEventListener('touchend', touchEnd, true);

SceneJS.bind("error", function(e) {
    alert(e.exception.message);
});

scene.start();
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import { browserHistory } from 'react-router';
import { Card, BaseComponent, Dataset, DataHandler, DataHandlers, Registry, EventDispatcher } from '../ReactDashboard';
import { isArray, isEqual, pick} from 'lodash';
import { appliedFiltersToQueryString } from '../utils/utils';

export default class Dashboard extends BaseComponent {

  constructor(props) {
    super(props);
  }

  componentWillMount() {
    super.componentWillMount();

    // if doFilterRouting flag is present, get url filters
    if (this.props.doFilterRouting !== false) {
      let appliedFilters = this.getUrlFilters();
      this.state.appliedFilters = appliedFilters;
    }

    // initialize data
    this.state.isFetching = true;
    this.getDashboardData();
  }

  shouldComponentUpdate(nextProps, nextState) {
    if (nextState.isFetching) return false; // wait for all data before updating.
    return true;
  }

  componentDidUpdate(nextProps, nextState) {
    if (!isEqual(nextState.appliedFilters, this.state.appliedFilters)) {
      this.setState({isFetching: true});
      this.getDashboardData();
    }
  }

  /**
   * Apply datahandlers in sequence, passing data returned to subsequent handler
   *    makes global leve data and @@TODO event object available to ahndler
   **/
  _applyDataHandlers(datahandlers, componentData=[]) {
    let _handlers = datahandlers;
    let _appliedFilters = this.state.appliedFilters || {};
    let _data = DataHandler.handle.call(this, _handlers, componentData, this.state.data, {e:'foo'}, _appliedFilters);
    return _data;
  }

  getUrlFilters() {
    let q = this.props.location.query;
    let appliedFilters = {};

    Object.keys(q).forEach(key => {
      let payload = {}; // mock url filter as regular Dashboard filter
      payload.field = key;
      payload.value = q[key].split(',');
      payload.vals = payload.value.map(v => {
        if (!isNaN(v)) parseInt(v);
        return v;
      });

      appliedFilters = this.getUpdatedAppliedFilters(payload, appliedFilters);
    });

    return appliedFilters;
  }

  /**
   * Override this method in your application and insert data fetching stuff here!
   *    API integrations
   *    Flat file Loading
   *    Whatever you want
   **/
  getDashboardData() {
    console.log('Warning. getDashboardData should be defined in your application which extends this dashboard component. getDashboardData should return an object with dataKeys. See @@LINK');
  }

  /**
   * Maps data to components based on component settings
   **/
  getChildData(component) {
    let data = [];

    if (component.dataHandlers) {
      data = this._applyDataHandlers(component.dataHandlers, component.data);
    } else if (component.data) {
      if (component.data.length > 0) {
        data = component.data;
      }
    }

    return data;
  }

  /**
   * Figure out which appliedFilters apply to which dataKeys
   **/
  getFilters(key, appliedFilters) {
    let filters = [];
    //let appliedFilters = Object.assign({}, this.state.appliedFilters);
    return Object.keys(appliedFilters).map(k => {
      let next = appliedFilters[k];
      if (next && next.willFilter && next.willFilter.length > 0 ) {
        let will = next.willFilter.indexOf(key);
        if (will >= 0) return appliedFilters[k];
      }
    });
  }

  getFilterByField(field) {
    let filter;

    this.props.regions.forEach(region => {
      if (region.children) {
        return region.children.forEach(child => {
          if (child.field === field) filter = child;
        })
      } else if (region.elements) { // drill down through multi-component sections
        Object.keys(region.elements).forEach(k => {
          region.elements[k].forEach(el => {
            if (el.field === field) filter = el;
          });
        })
      }
    });

    return filter;
  }

  /**
   * Handle actions here.
   *    Update appliedFilters on state triggers re-render
   *    App parses appliedFilters and updates dash accordingly
   **/
  onAction(payload) {
    switch(payload.actionType) {
      case 'AUTOCOMPLETE_CHANGE':
        let appliedFilters = Object.assign({}, this.state.appliedFilters);
        let updatedAppliedFilters = this.getUpdatedAppliedFilters(payload, appliedFilters);
        let q = appliedFiltersToQueryString(updatedAppliedFilters);
        let basePath = this.props.basePath || '';

        if (this.props.doFilterRouting !== false) {
          browserHistory.push(basePath + '?' + q);
        }

        this.setState({appliedFilters: updatedAppliedFilters, isFetching: true});
        this.getDashboardData(updatedAppliedFilters);
        break;

      case 'MULTICHECKBOX_CHANGE':
        console.log('xyz', payload);
        break;

      default:
        console.warn('Actions should define an actionType. See docs @@LINK');
    }
  }

  getUpdatedAppliedFilters(_payload, appliedFilters) {
    let field = _payload.field;
    let filter = this.getFilterByField(field);
    let payload = Object.assign(_payload, filter);

    // value is a non-empty array of values
    if (isArray(payload.value) && payload.value.length > 0) {
      payload.vals = payload.value.map(row => {
        if (!isNaN(row)) return parseInt(row); // the row is just a numeric value
        if (!isNaN(row.value)) return parseInt(row.value);  // ints are easier
        return row.value;
      });
      appliedFilters[field] = payload;
    } else if (payload.value && payload.value.value) { // payload value is an object with a value attribute
      if (!isNaN(payload.value.value)) payload.value.value =  parseInt(payload.value.value);  // ints are easier
      payload.value = [payload.value]
      appliedFilters[field] = payload;
    } else if (payload.value && typeof payload.value === 'string' || typeof payload.value === 'number') { // payload value is a scalar value
      payload.value = [payload.value];
      appliedFilters[field] = payload;
    } else { // if there is no value, remove this filter from appliedFilters
      delete appliedFilters[field];
    }
    return appliedFilters;
  }

  getRegions() {
    let regions;
    if (this.props.regions) {
      regions = this.props.regions.map( (region, key) => {

      if (region.multi) {
        let multiRegionKey = this.getChildData(region);
        region.key = key;
        region.children = region.elements[multiRegionKey];
      }

      return (

          {region.children.map( (element, key) => {
            let isReactEl = React.isValidElement(element);
            let output;
            let el;
            // if it isn't a react element, the element is a settings object
            let _props = (isReactEl) ? element.props : element;
            let props = Object.assign({}, _props);

            props.data = this.getChildData(element) || [];
            props.globalData = Object.assign({}, this.state.data || {});
            props.appliedFilters = Object.assign({}, this.state.appliedFilters || {});
            props.vars = Object.assign({}, this.props.vars || {});
            props.routeParams = routeParams;
            props.key = 'el_' + key;

             el = (isReactEl) ? element : React.createElement(Registry.get(element.type), props);

            if (props.cardStyle) {
              output =
                <Card key={'card_'+key} {...props}>
                  {el}
                </Card>
            } else {
              output = el;
            }

            return output;
          })}

      )
    })} else {
      regions = this.props.children;
    }

    return regions;
  }

  render() {
    let markup;
    let routeParams = pick(this.props, ['history', 'location', 'params', 'route', 'routeParams', 'routes']);
    let regions = this.getRegions();

    console.log('DASH RENDER', this);
      return (

            <link rel="stylesheet" type="text/css" href={this.props.faPath} />

{this.props.title}

            {regions}

      );
  }
}
<commit_msg>Add routeParams to getRegions helper in Dashboard<commit_after>import React, { Component } from 'react';
import { browserHistory } from 'react-router';
import { Card, BaseComponent, Dataset, DataHandler, DataHandlers, Registry, EventDispatcher } from '../ReactDashboard';
import { isArray, isEqual, pick} from 'lodash';
import { appliedFiltersToQueryString } from '../utils/utils';

export default class Dashboard extends BaseComponent {

  constructor(props) {
    super(props);
  }

  componentWillMount() {
    super.componentWillMount();

    // if doFilterRouting flag is present, get url filters
    if (this.props.doFilterRouting !== false) {
      let appliedFilters = this.getUrlFilters();
      this.state.appliedFilters = appliedFilters;
    }

    // initialize data
    this.state.isFetching = true;
    this.getDashboardData();
  }

  shouldComponentUpdate(nextProps, nextState) {
    if (nextState.isFetching) return false; // wait for all data before updating.
    return true;
  }

  componentDidUpdate(nextProps, nextState) {
    if (!isEqual(nextState.appliedFilters, this.state.appliedFilters)) {
      this.setState({isFetching: true});
      this.getDashboardData();
    }
  }

  /**
   * Apply datahandlers in sequence, passing data returned to subsequent handler
   *    makes global leve data and @@TODO event object available to ahndler
   **/
  _applyDataHandlers(datahandlers, componentData=[]) {
    let _handlers = datahandlers;
    let _appliedFilters = this.state.appliedFilters || {};
    let _data = DataHandler.handle.call(this, _handlers, componentData, this.state.data, {e:'foo'}, _appliedFilters);
    return _data;
  }

  getUrlFilters() {
    let q = this.props.location.query;
    let appliedFilters = {};

    Object.keys(q).forEach(key => {
      let payload = {}; // mock url filter as regular Dashboard filter
      payload.field = key;
      payload.value = q[key].split(',');
      payload.vals = payload.value.map(v => {
        if (!isNaN(v)) parseInt(v);
        return v;
      });

      appliedFilters = this.getUpdatedAppliedFilters(payload, appliedFilters);
    });

    return appliedFilters;
  }

  /**
   * Override this method in your application and insert data fetching stuff here!
   *    API integrations
   *    Flat file Loading
   *    Whatever you want
   **/
  getDashboardData() {
    console.log('Warning. getDashboardData should be defined in your application which extends this dashboard component. getDashboardData should return an object with dataKeys. See @@LINK');
  }

  /**
   * Maps data to components based on component settings
   **/
  getChildData(component) {
    let data = [];

    if (component.dataHandlers) {
      data = this._applyDataHandlers(component.dataHandlers, component.data);
    } else if (component.data) {
      if (component.data.length > 0) {
        data = component.data;
      }
    }

    return data;
  }

  /**
   * Figure out which appliedFilters apply to which dataKeys
   **/
  getFilters(key, appliedFilters) {
    let filters = [];
    //let appliedFilters = Object.assign({}, this.state.appliedFilters);
    return Object.keys(appliedFilters).map(k => {
      let next = appliedFilters[k];
      if (next && next.willFilter && next.willFilter.length > 0 ) {
        let will = next.willFilter.indexOf(key);
        if (will >= 0) return appliedFilters[k];
      }
    });
  }

  getFilterByField(field) {
    let filter;

    this.props.regions.forEach(region => {
      if (region.children) {
        return region.children.forEach(child => {
          if (child.field === field) filter = child;
        })
      } else if (region.elements) { // drill down through multi-component sections
        Object.keys(region.elements).forEach(k => {
          region.elements[k].forEach(el => {
            if (el.field === field) filter = el;
          });
        })
      }
    });

    return filter;
  }

  /**
   * Handle actions here.
   *    Update appliedFilters on state triggers re-render
   *    App parses appliedFilters and updates dash accordingly
   **/
  onAction(payload) {
    switch(payload.actionType) {
      case 'AUTOCOMPLETE_CHANGE':
        let appliedFilters = Object.assign({}, this.state.appliedFilters);
        let updatedAppliedFilters = this.getUpdatedAppliedFilters(payload, appliedFilters);
        let q = appliedFiltersToQueryString(updatedAppliedFilters);
        let basePath = this.props.basePath || '';

        if (this.props.doFilterRouting !== false) {
          browserHistory.push(basePath + '?' + q);
        }

        this.setState({appliedFilters: updatedAppliedFilters, isFetching: true});
        this.getDashboardData(updatedAppliedFilters);
        break;

      case 'MULTICHECKBOX_CHANGE':
        console.log('xyz', payload);
        break;

      default:
        console.warn('Actions should define an actionType. See docs @@LINK');
    }
  }

  getUpdatedAppliedFilters(_payload, appliedFilters) {
    let field = _payload.field;
    let filter = this.getFilterByField(field);
    let payload = Object.assign(_payload, filter);

    // value is a non-empty array of values
    if (isArray(payload.value) && payload.value.length > 0) {
      payload.vals = payload.value.map(row => {
        if (!isNaN(row)) return parseInt(row); // the row is just a numeric value
        if (!isNaN(row.value)) return parseInt(row.value);  // ints are easier
        return row.value;
      });
      appliedFilters[field] = payload;
    } else if (payload.value && payload.value.value) { // payload value is an object with a value attribute
      if (!isNaN(payload.value.value)) payload.value.value =  parseInt(payload.value.value);  // ints are easier
      payload.value = [payload.value]
      appliedFilters[field] = payload;
    } else if (payload.value && typeof payload.value === 'string' || typeof payload.value === 'number') { // payload value is a scalar value
      payload.value = [payload.value];
      appliedFilters[field] = payload;
    } else { // if there is no value, remove this filter from appliedFilters
      delete appliedFilters[field];
    }
    return appliedFilters;
  }

  getRegions() {
    let routeParams = pick(this.props, ['history', 'location', 'params', 'route', 'routeParams', 'routes']);
    let regions;
    if (this.props.regions) {
      regions = this.props.regions.map( (region, key) => {

      if (region.multi) {
        let multiRegionKey = this.getChildData(region);
        region.key = key;
        region.children = region.elements[multiRegionKey];
      }

      return (

          {region.children.map( (element, key) => {
            let isReactEl = React.isValidElement(element);
            let output;
            let el;
            // if it isn't a react element, the element is a settings object
            let _props = (isReactEl) ? element.props : element;
            let props = Object.assign({}, _props);

            props.data = this.getChildData(element) || [];
            props.globalData = Object.assign({}, this.state.data || {});
            props.appliedFilters = Object.assign({}, this.state.appliedFilters || {});
            props.vars = Object.assign({}, this.props.vars || {});
            props.routeParams = routeParams;
            props.key = 'el_' + key;

             el = (isReactEl) ? element : React.createElement(Registry.get(element.type), props);

            if (props.cardStyle) {
              output =
                <Card key={'card_'+key} {...props}>
                  {el}
                </Card>
            } else {
              output = el;
            }

            return output;
          })}

      )
    })} else {
      regions = this.props.children;
    }

    return regions;
  }

  render() {
    let markup;
    let regions = this.getRegions();

    console.log('DASH RENDER', this);
      return (

            <link rel="stylesheet" type="text/css" href={this.props.faPath} />

{this.props.title}

            {regions}

      );
  }
}
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import { Link } from 'react-router-dom'

import bitcoin from 'bitcoin'
import crypto from 'crypto'
import keyto from "@trust/keyto"

import claims from '../../data/claims.json';
import documents from '../../data/documents.json';
import payloads from '../../data/payload.json';

import './Documents.css';

export default class Documents extends Component {
  render(){
    let payload = documents.payload;
    console.log(payload);

    payload.map((document, i) => console.log(document))
    payload.map((claim, i) => console.log(claim))

        return (

                            Documents

                            Find all documents for transactions below.

                                    File Name
                                    Date Added
                                    Date Signed
                                    Signed
                                    Actions

  {
    payload.map((document, i) => (

                                        <Link to={`/document/${i}`} rel="nofollow noopener noreferrer">
        {document.title}
                                        </Link>

        {document.date}
                                    {document.deadline}
                                    {document.signed ? 'Yes' : 'No'}

        ))
      }

                            Claims

                            <Link className="ti-plus icon-yes" to="/claims">

                            </Link>

                            Find all claims for transactions below.

                                    File Name
                                    Date Added
                                    Date Signed
                                    Signed
                                    Verified
                                    Actions

  {
    payload.map((claim, i) => (

                                        <Link to={`/claim/${i}`} rel="nofollow noopener noreferrer">
        {claim.title}
                                        </Link>

        {claim.date}
                                    {claim.deadline}
                                    {claim.signed ? 'Yes' : 'No'}
                                    {claim.verified ? 'Yes' : 'No'}

        ))
      }

                { this.renderDocuments() }

        );
    }

    signDocument(document){
      console.log(document)
      console.log(JSON.stringify(document))
      var client = new bitcoin.Client({
        host: 'localhost',
        port: '8332',
        user: 'fred',
        pass: 'fred'
      })
      var address = "mjH9KTBnNyzdLifkr6xch5FkXnGdbf27mq"
      var message = JSON.stringify(document.payload)
      var signaturePayload = {}

      console.log(message)
      client.signMessage(address, message,function(err,signature){
        if(err){
          return console.error(err);
        }
        signaturePayload["address"] = address
        signaturePayload["signature"] = signature
        console.log(signature)
        console.log(address)
      })

    }

    renderDocument (document) {

        return (

 <button type="submit" className="btn btn-wide btn-o btn-primary" onClick={this.signDocument.bind(this,document)}>Sign</button> {JSON.stringify(document)}

        )
    }

    renderDocuments () {
        return (

                            Signing Validation

                { payloads.entries.map((document) => this.renderDocument(document)) }

        )
    }

}
<commit_msg>Remove Claims table from Documents section<commit_after>import React, { Component } from 'react';
import { Link } from 'react-router-dom'

import bitcoin from 'bitcoin'
import crypto from 'crypto'
import keyto from "@trust/keyto"

import claims from '../../data/claims.json';
import documents from '../../data/documents.json';
import payloads from '../../data/payload.json';

import './Documents.css';

export default class Documents extends Component {
  render(){
    let payload = documents.payload;
    console.log(payload);

    payload.map((document, i) => console.log(document))
    payload.map((claim, i) => console.log(claim))

        return (

                            Documents

                            Find all documents for transactions below.

                                    File Name
                                    Date Added
                                    Date Signed
                                    Signed
                                    Actions

  {
    payload.map((document, i) => (

                                        <Link to={`/document/${i}`} rel="nofollow noopener noreferrer">
        {document.title}
                                        </Link>

        {document.date}
                                    {document.deadline}
                                    {document.signed ? 'Yes' : 'No'}

        ))
      }

                { this.renderDocuments() }

        );
    }

    signDocument(document){
      console.log(document)
      console.log(JSON.stringify(document))
      var client = new bitcoin.Client({
        host: 'localhost',
        port: '8332',
        user: 'fred',
        pass: 'fred'
      })
      var address = "mjH9KTBnNyzdLifkr6xch5FkXnGdbf27mq"
      var message = JSON.stringify(document.payload)
      var signaturePayload = {}

      console.log(message)
      client.signMessage(address, message,function(err,signature){
        if(err){
          return console.error(err);
        }
        signaturePayload["address"] = address
        signaturePayload["signature"] = signature
        console.log(signature)
        console.log(address)
      })

    }

    renderDocument (document) {

        return (

 <button type="submit" className="btn btn-wide btn-o btn-primary" onClick={this.signDocument.bind(this,document)}>Sign</button> {JSON.stringify(document)}

        )
    }

    renderDocuments () {
        return (

                            Signing Validation

                { payloads.entries.map((document) => this.renderDocument(document)) }

        )
    }

}
<|endoftext|>


<commit_before>/*
Copyright 2020 The Matrix.org Foundation C.I.C.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import EventEmitter from 'events';
import { MatrixClientPeg } from '../MatrixClientPeg';
import { accessSecretStorage, AccessCancelledError } from '../SecurityManager';
import { PHASE_DONE as VERIF_PHASE_DONE } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";

export const PHASE_INTRO = 0;
export const PHASE_BUSY = 1;
export const PHASE_DONE = 2;    //final done stage, but still showing UX
export const PHASE_CONFIRM_SKIP = 3;
export const PHASE_FINISHED = 4; //UX can be closed

export class SetupEncryptionStore extends EventEmitter {
    static sharedInstance() {
        if (!global.mx_SetupEncryptionStore) global.mx_SetupEncryptionStore = new SetupEncryptionStore();
        return global.mx_SetupEncryptionStore;
    }

    start() {
        if (this._started) {
            return;
        }
        this._started = true;
        this.phase = PHASE_BUSY;
        this.verificationRequest = null;
        this.backupInfo = null;

        // ID of the key that the secrets we want are encrypted with
        this.keyId = null;
        // Descriptor of the key that the secrets we want are encrypted with
        this.keyInfo = null;

        const cli = MatrixClientPeg.get();
        cli.on("crypto.verification.request", this.onVerificationRequest);
        cli.on('userTrustStatusChanged', this._onUserTrustStatusChanged);

        const requestsInProgress = cli.getVerificationRequestsToDeviceInProgress(cli.getUserId());
        if (requestsInProgress.length) {
            // If there are multiple, we take the most recent. Equally if the user sends another request from
            // another device after this screen has been shown, we'll switch to the new one, so this
            // generally doesn't support multiple requests.
            this._setActiveVerificationRequest(requestsInProgress[requestsInProgress.length - 1]);
        }

        this.fetchKeyInfo();
    }

    stop() {
        if (!this._started) {
            return;
        }
        this._started = false;
        if (this.verificationRequest) {
            this.verificationRequest.off("change", this.onVerificationRequestChange);
        }
        if (MatrixClientPeg.get()) {
            MatrixClientPeg.get().removeListener("crypto.verification.request", this.onVerificationRequest);
            MatrixClientPeg.get().removeListener('userTrustStatusChanged', this._onUserTrustStatusChanged);
        }
    }

    async fetchKeyInfo() {
        const cli = MatrixClientPeg.get();
        const keys = await cli.isSecretStored('m.cross_signing.master', false);
        if (keys === null || Object.keys(keys).length === 0) {
            this.keyId = null;
            this.keyInfo = null;
        } else {
            // If the secret is stored under more than one key, we just pick an arbitrary one
            this.keyId = Object.keys(keys)[0];
            this.keyInfo = keys[this.keyId];
        }

        // do we have any other devices which are E2EE which we can verify against?
        const dehydratedDevice = await cli.getDehydratedDevice();
        this.hasDevicesToVerifyAgainst = cli.getStoredDevicesForUser(cli.getUserId()).some(
            device =>
                device.getIdentityKey() &&
                (!dehydratedDevice || (device.deviceId != dehydratedDevice.device_id)),
        );

        if (!this.hasDevicesToVerifyAgainst && !this.keyInfo) {
            // skip before we can even render anything.
            // XXX: this causes a dialog box flash
            this.phase = PHASE_FINISHED;
        } else {
            this.phase = PHASE_INTRO;
        }
        this.emit("update");
    }

    async usePassPhrase() {
        this.phase = PHASE_BUSY;
        this.emit("update");
        const cli = MatrixClientPeg.get();
        try {
            const backupInfo = await cli.getKeyBackupVersion();
            this.backupInfo = backupInfo;
            this.emit("update");
            // The control flow is fairly twisted here...
            // For the purposes of completing security, we only wait on getting
            // as far as the trust check and then show a green shield.
            // We also begin the key backup restore as well, which we're
            // awaiting inside `accessSecretStorage` only so that it keeps your
            // passphase cached for that work. This dialog itself will only wait
            // on the first trust check, and the key backup restore will happen
            // in the background.
            await new Promise((resolve, reject) => {
                try {
                    accessSecretStorage(async () => {
                        await cli.checkOwnCrossSigningTrust();
                        resolve();
                        if (backupInfo) {
                            // A complete restore can take many minutes for large
                            // accounts / slow servers, so we allow the dialog
                            // to advance before this.
                            await cli.restoreKeyBackupWithSecretStorage(backupInfo);
                        }
                    }).catch(reject);
                } catch (e) {
                    console.error(e);
                    reject(e);
                }
            });

            if (cli.getCrossSigningId()) {
                this.phase = PHASE_DONE;
                this.emit("update");
            }
        } catch (e) {
            if (!(e instanceof AccessCancelledError)) {
                console.log(e);
            }
            // this will throw if the user hits cancel, so ignore
            this.phase = PHASE_INTRO;
            this.emit("update");
        }
    }

    _onUserTrustStatusChanged = (userId) => {
        if (userId !== MatrixClientPeg.get().getUserId()) return;
        const publicKeysTrusted = MatrixClientPeg.get().getCrossSigningId();
        if (publicKeysTrusted) {
            this.phase = PHASE_DONE;
            this.emit("update");
        }
    }

    onVerificationRequest = (request) => {
        this._setActiveVerificationRequest(request);
    }

    onVerificationRequestChange = () => {
        if (this.verificationRequest.cancelled) {
            this.verificationRequest.off("change", this.onVerificationRequestChange);
            this.verificationRequest = null;
            this.emit("update");
        } else if (this.verificationRequest.phase === VERIF_PHASE_DONE) {
            this.verificationRequest.off("change", this.onVerificationRequestChange);
            this.verificationRequest = null;
            // At this point, the verification has finished, we just need to wait for
            // cross signing to be ready to use, so wait for the user trust status to
            // change (or change to DONE if it's already ready).
            const publicKeysTrusted = MatrixClientPeg.get().getCrossSigningId();
            this.phase = publicKeysTrusted ? PHASE_DONE : PHASE_BUSY;
            this.emit("update");
        }
    }

    skip() {
        this.phase = PHASE_CONFIRM_SKIP;
        this.emit("update");
    }

    skipConfirm() {
        this.phase = PHASE_FINISHED;
        this.emit("update");
    }

    returnAfterSkip() {
        this.phase = PHASE_INTRO;
        this.emit("update");
    }

    done() {
        this.phase = PHASE_FINISHED;
        this.emit("update");
        // async - ask other clients for keys, if necessary
        MatrixClientPeg.get()._crypto.cancelAndResendAllOutgoingKeyRequests();
    }

    async _setActiveVerificationRequest(request) {
        if (request.otherUserId !== MatrixClientPeg.get().getUserId()) return;

        if (this.verificationRequest) {
            this.verificationRequest.off("change", this.onVerificationRequestChange);
        }
        this.verificationRequest = request;
        await request.accept();
        request.on("change", this.onVerificationRequestChange);
        this.emit("update");
    }
}
<commit_msg>remove errand whitespace<commit_after>/*
Copyright 2020 The Matrix.org Foundation C.I.C.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import EventEmitter from 'events';
import { MatrixClientPeg } from '../MatrixClientPeg';
import { accessSecretStorage, AccessCancelledError } from '../SecurityManager';
import { PHASE_DONE as VERIF_PHASE_DONE } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";

export const PHASE_INTRO = 0;
export const PHASE_BUSY = 1;
export const PHASE_DONE = 2;    //final done stage, but still showing UX
export const PHASE_CONFIRM_SKIP = 3;
export const PHASE_FINISHED = 4; //UX can be closed

export class SetupEncryptionStore extends EventEmitter {
    static sharedInstance() {
        if (!global.mx_SetupEncryptionStore) global.mx_SetupEncryptionStore = new SetupEncryptionStore();
        return global.mx_SetupEncryptionStore;
    }

    start() {
        if (this._started) {
            return;
        }
        this._started = true;
        this.phase = PHASE_BUSY;
        this.verificationRequest = null;
        this.backupInfo = null;

        // ID of the key that the secrets we want are encrypted with
        this.keyId = null;
        // Descriptor of the key that the secrets we want are encrypted with
        this.keyInfo = null;

        const cli = MatrixClientPeg.get();
        cli.on("crypto.verification.request", this.onVerificationRequest);
        cli.on('userTrustStatusChanged', this._onUserTrustStatusChanged);

        const requestsInProgress = cli.getVerificationRequestsToDeviceInProgress(cli.getUserId());
        if (requestsInProgress.length) {
            // If there are multiple, we take the most recent. Equally if the user sends another request from
            // another device after this screen has been shown, we'll switch to the new one, so this
            // generally doesn't support multiple requests.
            this._setActiveVerificationRequest(requestsInProgress[requestsInProgress.length - 1]);
        }

        this.fetchKeyInfo();
    }

    stop() {
        if (!this._started) {
            return;
        }
        this._started = false;
        if (this.verificationRequest) {
            this.verificationRequest.off("change", this.onVerificationRequestChange);
        }
        if (MatrixClientPeg.get()) {
            MatrixClientPeg.get().removeListener("crypto.verification.request", this.onVerificationRequest);
            MatrixClientPeg.get().removeListener('userTrustStatusChanged', this._onUserTrustStatusChanged);
        }
    }

    async fetchKeyInfo() {
        const cli = MatrixClientPeg.get();
        const keys = await cli.isSecretStored('m.cross_signing.master', false);
        if (keys === null || Object.keys(keys).length === 0) {
            this.keyId = null;
            this.keyInfo = null;
        } else {
            // If the secret is stored under more than one key, we just pick an arbitrary one
            this.keyId = Object.keys(keys)[0];
            this.keyInfo = keys[this.keyId];
        }

        // do we have any other devices which are E2EE which we can verify against?
        const dehydratedDevice = await cli.getDehydratedDevice();
        this.hasDevicesToVerifyAgainst = cli.getStoredDevicesForUser(cli.getUserId()).some(
            device =>
                device.getIdentityKey() &&
                (!dehydratedDevice || (device.deviceId != dehydratedDevice.device_id)),
        );

        if (!this.hasDevicesToVerifyAgainst && !this.keyInfo) {
            // skip before we can even render anything.
            // XXX: this causes a dialog box flash
            this.phase = PHASE_FINISHED;
        } else {
            this.phase = PHASE_INTRO;
        }
        this.emit("update");
    }

    async usePassPhrase() {
        this.phase = PHASE_BUSY;
        this.emit("update");
        const cli = MatrixClientPeg.get();
        try {
            const backupInfo = await cli.getKeyBackupVersion();
            this.backupInfo = backupInfo;
            this.emit("update");
            // The control flow is fairly twisted here...
            // For the purposes of completing security, we only wait on getting
            // as far as the trust check and then show a green shield.
            // We also begin the key backup restore as well, which we're
            // awaiting inside `accessSecretStorage` only so that it keeps your
            // passphase cached for that work. This dialog itself will only wait
            // on the first trust check, and the key backup restore will happen
            // in the background.
            await new Promise((resolve, reject) => {
                try {
                    accessSecretStorage(async () => {
                        await cli.checkOwnCrossSigningTrust();
                        resolve();
                        if (backupInfo) {
                            // A complete restore can take many minutes for large
                            // accounts / slow servers, so we allow the dialog
                            // to advance before this.
                            await cli.restoreKeyBackupWithSecretStorage(backupInfo);
                        }
                    }).catch(reject);
                } catch (e) {
                    console.error(e);
                    reject(e);
                }
            });

            if (cli.getCrossSigningId()) {
                this.phase = PHASE_DONE;
                this.emit("update");
            }
        } catch (e) {
            if (!(e instanceof AccessCancelledError)) {
                console.log(e);
            }
            // this will throw if the user hits cancel, so ignore
            this.phase = PHASE_INTRO;
            this.emit("update");
        }
    }

    _onUserTrustStatusChanged = (userId) => {
        if (userId !== MatrixClientPeg.get().getUserId()) return;
        const publicKeysTrusted = MatrixClientPeg.get().getCrossSigningId();
        if (publicKeysTrusted) {
            this.phase = PHASE_DONE;
            this.emit("update");
        }
    }

    onVerificationRequest = (request) => {
        this._setActiveVerificationRequest(request);
    }

    onVerificationRequestChange = () => {
        if (this.verificationRequest.cancelled) {
            this.verificationRequest.off("change", this.onVerificationRequestChange);
            this.verificationRequest = null;
            this.emit("update");
        } else if (this.verificationRequest.phase === VERIF_PHASE_DONE) {
            this.verificationRequest.off("change", this.onVerificationRequestChange);
            this.verificationRequest = null;
            // At this point, the verification has finished, we just need to wait for
            // cross signing to be ready to use, so wait for the user trust status to
            // change (or change to DONE if it's already ready).
            const publicKeysTrusted = MatrixClientPeg.get().getCrossSigningId();
            this.phase = publicKeysTrusted ? PHASE_DONE : PHASE_BUSY;
            this.emit("update");
        }
    }

    skip() {
        this.phase = PHASE_CONFIRM_SKIP;
        this.emit("update");
    }

    skipConfirm() {
        this.phase = PHASE_FINISHED;
        this.emit("update");
    }

    returnAfterSkip() {
        this.phase = PHASE_INTRO;
        this.emit("update");
    }

    done() {
        this.phase = PHASE_FINISHED;
        this.emit("update");
        // async - ask other clients for keys, if necessary
        MatrixClientPeg.get()._crypto.cancelAndResendAllOutgoingKeyRequests();
    }

    async _setActiveVerificationRequest(request) {
        if (request.otherUserId !== MatrixClientPeg.get().getUserId()) return;

        if (this.verificationRequest) {
            this.verificationRequest.off("change", this.onVerificationRequestChange);
        }
        this.verificationRequest = request;
        await request.accept();
        request.on("change", this.onVerificationRequestChange);
        this.emit("update");
    }
}
<|endoftext|>


<commit_before>// import { h, Component } from 'preact';
import React, { Component, PropTypes } from 'react';

type PropsType = {};

type StateType = {};

type ResponseType = {};

export default class Game extends Component {
  state = {
    level: 0,
    experience: 1000, // business experience... maybe equal to level

    discipline: 1000,
    charisma: 1000,

    talent: 1000, // is not upgradeable (level of difficulty)
    vision: 1000, // may be done in one parameter firstly

  };

  componentWillMount() {}

  render(props: PropsType) {
    const state = this.state;
    console.log('state is', state);
    return (

        {JSON.stringify(state)}

    );
  }
}
<commit_msg>more data...<commit_after>// import { h, Component } from 'preact';
import React, { Component, PropTypes } from 'react';

type PropsType = {};

type StateType = {};

type ResponseType = {};

export default class Game extends Component {
  state = {
    player: {
      skills: {
        level: 0,
        experience: 1000, // business experience... maybe equal to level

        discipline: 1000, // ability to perform well even in low morale. Suffers less morale if fails
        charisma: 1000, // коммуникабельность + способность управлять людьми

        talent: 1000, // is not upgradeable (level of difficulty)
        // vision: 1000, // may be done in one parameter firstly

        // профильный навык
        programming: 1000,
        marketing: 1000,
        ux: 1000,
        design: 1000,
        SEO: 1000,
      },

      // temporaries
      morale: 1000,
      energy: 1000,

      friends: 100, // NEED MORE INFO.

      reputation: 50,
      money: 20000,
    },

    products: [{
      rating: 0,
      idea: 'IDEA_WEB_STUDIO',

      features: {
        offer: {},
        programming: {
          debt: 0,
        },
        support: {},
        marketing: {},
      }
    }],
    team: []
  };

  componentWillMount() {}

  render(props: PropsType) {
    const state = this.state;
    console.log('state is', state);
    return (

        {JSON.stringify(state)}

    );
  }
}
<|endoftext|>


<commit_before>pc.extend(pc, function () {
    /**
     * @component
     * @name pc.ModelComponent
     * @constructor Create a new ModelComponentSystem
     * @class Enables an Entity to render a model or a primitive shape. This Component attaches additional model geometry in to the scene graph below the Entity.
     * @param {pc.ModelComponentSystem} system The ComponentSystem that created this Component
     * @param {pc.Entity} entity The Entity that this Component is attached to.
     * @extends pc.Component
     * @property {String} type The type of the model, which can be one of the following values:
     *

     *
asset: The component will render a model asset

     *
box: The component will render a box

     *
capsule: The component will render a capsule

     *
cone: The component will render a cone

     *
cylinder: The component will render a cylinder

     *
sphere: The component will render a sphere

     *

     * @property {Boolean} enabled Enable or disable rendering of the Model
     * @property {pc.Asset} asset The asset for the model (only applies to models of type 'asset') - can also be an asset id.
     * @property {Boolean} castShadows If true, this model will cast shadows for lights that have shadow casting enabled.
     * @property {Boolean} receiveShadows If true, shadows will be cast on this model
     * @property {Number} materialAsset The material {@link pc.Asset} that will be used to render the model (not used on models of type 'asset')
     * @property {pc.Model} model The model that is added to the scene graph.
     * @property {Object} mapping A dictionary that holds material overrides for each mesh instance. Only applies to model components of type 'asset'. The mapping contains pairs of mesh instance index - material asset id.
     */
    var ModelComponent = function ModelComponent (system, entity)   {
        this.on("set_type", this.onSetType, this);
        this.on("set_asset", this.onSetAsset, this);
        this.on("set_castShadows", this.onSetCastShadows, this);
        this.on("set_model", this.onSetModel, this);
        this.on("set_receiveShadows", this.onSetReceiveShadows, this);
        this.on("set_material", this.onSetMaterial, this);
        this.on("set_mapping", this.onSetMapping, this);

        // override materialAsset property to return a pc.Asset instead
        Object.defineProperty(this, 'materialAsset', {
            set: this.setMaterialAsset.bind(this),
            get: this.getMaterialAsset.bind(this)
        });

        this._assetOld = 0;

        this._materialEvents = null;
    };
    ModelComponent = pc.inherits(ModelComponent, pc.Component);

    pc.extend(ModelComponent.prototype, {
        setVisible: function (visible) {
            console.warn("WARNING: setVisible: Function is deprecated. Set enabled property instead.");
            this.enabled = visible;
        },

        _onAssetLoad: function(asset) {
            if (asset.resource)
                this._onModelLoaded(asset.resource.clone());
        },

        _onAssetChange: function(asset, attribute, newValue, oldValue) {
            if (attribute === 'data') {
                // reset mapping
                this.mapping = this.data.mapping;
            }
        },

        _onAssetRemove: function (asset) {
            if (this.asset === asset.id)
                this.asset = null;
        },

        _setModelAsset: function (id) {
            var assets = this.system.app.assets;
            var asset = id !== null ? assets.get(id) : null;

            this._onModelAsset(asset || null);

            if (! asset && id !== null)
                assets.once("add:" + id, this._onModelAsset, this);
        },

        _onModelAsset: function(asset) {
            var assets = this.system.app.assets;

            // clear old assets bindings
            if (this._assetOld) {
                assets.off("add:" + this._assetOld, this._onModelAsset, this);

                var assetOld = assets.get(this._assetOld);
                if (assetOld) {
                    assetOld.off('load', this._onAssetLoad, this);
                    assetOld.off('change', this._onAssetChange, this);
                    assetOld.off('remove', this._onAssetRemove, this);
                }
            }

            // remember new asset id
            this._assetOld = asset ? asset.id : 0;

            if (asset) {
                // subscribe to asset events
                asset.on('load', this._onAssetLoad, this);
                asset.on('change', this._onAssetChange, this);
                asset.on('remove', this._onAssetRemove, this);

                if (asset.resource) {
                    this._onModelLoaded(asset.resource.clone());
                } else {
                    assets.load(asset);
                }
            }
        },

        remove: function() {
            this._onModelAsset(null);
        },

        _onModelLoaded: function (model) {
            if (this.data.type === 'asset')
                this.model = model;
        },

        /**
         * @function
         * @private
         * @name pc.ModelComponent#onSetType
         * @description Handle changes to the 'type' variable
         */
        onSetType: function (name, oldValue, newValue) {
            var data = this.data;

            if (newValue) {
                var mesh = null;

                if (newValue === 'asset') {
                    if (this.data.asset !== null) {
                        this._setModelAsset(this.data.asset);
                    } else {
                        this.model = null;
                    }
                } else {
                    switch (newValue) {
                        case 'box':
                            mesh = this.system.box;
                            break;
                        case 'capsule':
                            mesh = this.system.capsule;
                            break;
                        case 'sphere':
                            mesh = this.system.sphere;
                            break;
                        case 'cone':
                            mesh = this.system.cone;
                            break;
                        case 'cylinder':
                            mesh = this.system.cylinder;
                            break;
                        case 'plane':
                            mesh = this.system.plane;
                            break;
                        default:
                            throw new Error("Invalid model type: " + newValue);
                    }

                    var node = new pc.GraphNode();

                    var model = new pc.Model();
                    model.graph = node;

                    model.meshInstances = [ new pc.MeshInstance(node, mesh, data.material) ];

                    if (this.system._inTools) {
                        model.generateWireframe();
                    }

                    this.model = model;
                    this.asset = null;
                }
            }
        },

        onSetAsset: function (name, oldValue, newValue) {
            var id = null;
            if (this.data.type === 'asset') {
                if (newValue !== null) {
                    id = newValue;

                    if (newValue instanceof pc.Asset) {
                        this.data.asset = newValue.id;
                        id = newValue.id;
                    }
                } else {
                    this.model = null;
                }
            }

            if (id === null)
                this.data.asset = null;

            this._setModelAsset(id);
        },

        onSetCastShadows: function (name, oldValue, newValue) {
            var model = this.data.model;
            if (model) {
                var scene = this.system.app.scene;
                var inScene = scene.containsModel(model);
                if (inScene) {
                    scene.removeModel(model);
                }

                var meshInstances = model.meshInstances;
                for (var i = 0; i < meshInstances.length; i++) {
                    meshInstances[i].castShadow = newValue;
                }

                if (inScene) {
                    scene.addModel(model);
                }
            }
        },

        onSetModel: function (name, oldValue, newValue) {
            if (oldValue) {
                this.system.app.scene.removeModel(oldValue);
                this.entity.removeChild(oldValue.getGraph());
                delete oldValue._entity;
            }

            if (newValue) {
                var componentData = this.data;
                var meshInstances = newValue.meshInstances;
                for (var i = 0; i < meshInstances.length; i++) {
                    meshInstances[i].castShadow = componentData.castShadows;
                    meshInstances[i].receiveShadow = componentData.receiveShadows;
                }

                this.entity.addChild(newValue.graph);

                if (this.enabled && this.entity.enabled) {
                    this.system.app.scene.addModel(newValue);
                }

                // Store the entity that owns this model
                newValue._entity = this.entity;

                // Update any animation component
                if (this.entity.animation) {
                    this.entity.animation.setModel(newValue);
                }

                // trigger event handler to load mapping
                // for new model
                if (this.data.type === 'asset') {
                    this.mapping = this.data.mapping;
                } else {
                    this._unsetMaterialEvents();
                }
            } else {
                this._unsetMaterialEvents();
            }
        },

        _onMaterialAssetRemove: function(asset) {
            var assets = this.system.app.assets;
            var id = isNaN(asset) ? asset.id : asset;

            if (asset && isNaN(asset) && asset.resource === this.material)
                this.material = pc.ModelHandler.DEFAULT_MATERIAL;

            assets.off('add:' + id, this._onMaterialAsset, this);
            assets.off('load:' + id, this._onMaterialAsset, this);
            assets.off('remove:' + id, this._onMaterialAssetRemove, this);
        },

        _onMaterialAsset: function(asset) {
            var assets = this.system.app.assets;

            if (asset.resource) {
                this.material = asset.resource;
            } else {
                assets.load(asset);
            }
        },

        setMaterialAsset: function (value) {
            // if the type of the value is not a number assume it is an pc.Asset
            var id = typeof value === 'number' || !value ? value : value.id;

            // var material;
            var assets = this.system.app.assets;
            var self = this;

            // unsubscribe
            if (this.data.materialAsset !== id) {
                if (this.data.materialAsset)
                    this._onMaterialAssetRemove(this.data.materialAsset);

                if (id) {
                    assets.on('load:' + id, this._onMaterialAsset, this);
                    assets.once('remove:' + id, this._onMaterialAssetRemove, this);
                }
            }

            // try to load the material asset
            if (id !== undefined && id !== null) {
                var asset = assets.get(id);
                if (asset)
                    this._onMaterialAsset(asset);

                // subscribe for adds
                assets.once('add:' + id, this._onMaterialAsset, this);
            } else if (id === null) {
                self.material = pc.ModelHandler.DEFAULT_MATERIAL;
            }

            var valueOld = this.data.materialAsset;
            this.data.materialAsset = id;
            this.fire('set', 'materialAsset', valueOld, id);
        },

        getMaterialAsset: function () {
            return this.system.app.assets.get(this.data.materialAsset);
        },

        onSetMaterial: function (name, oldValue, newValue) {
            if (newValue !== oldValue) {
                this.data.material = newValue;
                if (this.data.model && this.data.type !== 'asset') {
                    var meshInstances = this.data.model.meshInstances;
                    for (var i=0; i<meshInstances.length; i++) {
                        meshInstances[i].material = newValue;
                    }
                }
            }
        },

        onSetMapping: function (name, oldValue, newValue) {
            if (this.data.type !== 'asset' || !this.data.model) {
                return;
            }

            // unsubscribe from old events
            if (oldValue) {
                this._unsetMaterialEvents();
            }

            if (! newValue) {
                newValue = {};
            }

            var meshInstances = this.data.model.meshInstances;
            var modelAsset = this.asset ? this.system.app.assets.get(this.asset) : null;
            var assetMapping = modelAsset ? modelAsset.data.mapping : null;

            for (var i = 0, len = meshInstances.length; i < len; i++) {
                if (newValue[i] !== undefined) {
                    if (newValue[i]) {
                        this._loadAndSetMeshInstanceMaterial(newValue[i], meshInstances[i], i);
                    } else {
                        meshInstances[i].material = pc.ModelHandler.DEFAULT_MATERIAL;
                    }
                } else if (assetMapping) {
                    if (assetMapping[i] && (assetMapping[i].material || assetMapping[i].path)) {
                        var idOrPath = assetMapping[i].material || assetMapping[i].path;
                        this._loadAndSetMeshInstanceMaterial(idOrPath, meshInstances[i], i);
                    } else {
                        meshInstances[i].material = pc.ModelHandler.DEFAULT_MATERIAL;
                    }
                }
            }
        },

        _setMaterialEvent: function (index, event, id, handler) {
            this.system.app.assets.once(event + ':' + id, handler, this);

            if (!this._materialEvents) this._materialEvents = [];

            if (!this._materialEvents[index]) this._materialEvents[index] = {};

            this._materialEvents[index][event] = {
                id: id,
                handler: handler
            };
        },

        _unsetMaterialEvents: function () {
            var assets = this.system.app.assets;
            var events = this._materialEvents;
            if (! events)
                return;

            for (var i = 0, len = events.length; i < len; i++) {
                if (! events[i]) continue;
                var evt = events[i];
                for (var key in evt) {
                    assets.off(key, evt[key].handler, this);
                }
            }

            this._materialEvents = null;
        },

        _loadAndSetMeshInstanceMaterial: function (idOrPath, meshInstance, index) {
            var self = this;
            var asset;
            var assets = this.system.app.assets;

            var isPath = isNaN(parseInt(idOrPath, 10));

            // get asset by id or url
            if (!isPath) {
                asset = assets.get(idOrPath);
            } else if (self.asset) {
                var url = self._getMaterialAssetUrl(idOrPath);
                if (!url) return;

                asset = assets.getByUrl(url);
            }

            var handleMaterial = function (asset) {
                if (asset.resource) {
                    meshInstance.material = asset.resource;

                    self._setMaterialEvent(index, 'remove', asset.id, function () {
                        meshInstance.material = pc.ModelHandler.DEFAULT_MATERIAL;
                    });
                } else {
                    self._setMaterialEvent(index, 'load', asset.id, function (asset) {
                        meshInstance.material = asset.resource;

                        self._setMaterialEvent(index, 'remove', asset.id, function () {
                            meshInstance.material = pc.ModelHandler.DEFAULT_MATERIAL;
                        });
                    });

                    assets.load(asset);
                }
            };

            if (asset) {
                handleMaterial(asset);
            } else {
                self._setMaterialEvent(index, isPath ? 'add:url' : 'add', idOrPath, handleMaterial);
            }
        },

        _getMaterialAssetUrl: function (path) {
            if (!this.asset) return null;

            var modelAsset = this.system.app.assets.get(this.asset);
            if (!modelAsset) return null;

            var fileUrl = modelAsset.getFileUrl();
            var dirUrl = pc.path.getDirectory(fileUrl);
            return pc.path.join(dirUrl, path);
        },

        onSetReceiveShadows: function (name, oldValue, newValue) {
            if (newValue !== undefined) {
                var componentData = this.data;
                if (componentData.model) {
                    var meshInstances = componentData.model.meshInstances;
                    for (var i = 0; i < meshInstances.length; i++) {
                        meshInstances[i].receiveShadow = newValue;
                    }
                }
            }
        },

        onEnable: function () {
            ModelComponent._super.onEnable.call(this);

            var model = this.data.model;
            if (model) {
                var inScene = this.system.app.scene.containsModel(model);
                if (!inScene) {
                    this.system.app.scene.addModel(model);
                }
            }
        },

        onDisable: function () {
            ModelComponent._super.onDisable.call(this);

            var model = this.data.model;
            if (model) {
                var inScene = this.system.app.scene.containsModel(model);
                if (inScene) {
                    this.system.app.scene.removeModel(model);
                }
            }
        }
    });

    return {
        ModelComponent: ModelComponent
    };
}());
<commit_msg>Fix removing material event handlers in model component<commit_after>pc.extend(pc, function () {
    /**
     * @component
     * @name pc.ModelComponent
     * @constructor Create a new ModelComponentSystem
     * @class Enables an Entity to render a model or a primitive shape. This Component attaches additional model geometry in to the scene graph below the Entity.
     * @param {pc.ModelComponentSystem} system The ComponentSystem that created this Component
     * @param {pc.Entity} entity The Entity that this Component is attached to.
     * @extends pc.Component
     * @property {String} type The type of the model, which can be one of the following values:
     *

     *
asset: The component will render a model asset

     *
box: The component will render a box

     *
capsule: The component will render a capsule

     *
cone: The component will render a cone

     *
cylinder: The component will render a cylinder

     *
sphere: The component will render a sphere

     *

     * @property {Boolean} enabled Enable or disable rendering of the Model
     * @property {pc.Asset} asset The asset for the model (only applies to models of type 'asset') - can also be an asset id.
     * @property {Boolean} castShadows If true, this model will cast shadows for lights that have shadow casting enabled.
     * @property {Boolean} receiveShadows If true, shadows will be cast on this model
     * @property {Number} materialAsset The material {@link pc.Asset} that will be used to render the model (not used on models of type 'asset')
     * @property {pc.Model} model The model that is added to the scene graph.
     * @property {Object} mapping A dictionary that holds material overrides for each mesh instance. Only applies to model components of type 'asset'. The mapping contains pairs of mesh instance index - material asset id.
     */
    var ModelComponent = function ModelComponent (system, entity)   {
        this.on("set_type", this.onSetType, this);
        this.on("set_asset", this.onSetAsset, this);
        this.on("set_castShadows", this.onSetCastShadows, this);
        this.on("set_model", this.onSetModel, this);
        this.on("set_receiveShadows", this.onSetReceiveShadows, this);
        this.on("set_material", this.onSetMaterial, this);
        this.on("set_mapping", this.onSetMapping, this);

        // override materialAsset property to return a pc.Asset instead
        Object.defineProperty(this, 'materialAsset', {
            set: this.setMaterialAsset.bind(this),
            get: this.getMaterialAsset.bind(this)
        });

        this._assetOld = 0;

        this._materialEvents = null;
    };
    ModelComponent = pc.inherits(ModelComponent, pc.Component);

    pc.extend(ModelComponent.prototype, {
        setVisible: function (visible) {
            console.warn("WARNING: setVisible: Function is deprecated. Set enabled property instead.");
            this.enabled = visible;
        },

        _onAssetLoad: function(asset) {
            if (asset.resource)
                this._onModelLoaded(asset.resource.clone());
        },

        _onAssetChange: function(asset, attribute, newValue, oldValue) {
            if (attribute === 'data') {
                // reset mapping
                this.mapping = this.data.mapping;
            }
        },

        _onAssetRemove: function (asset) {
            if (this.asset === asset.id)
                this.asset = null;
        },

        _setModelAsset: function (id) {
            var assets = this.system.app.assets;
            var asset = id !== null ? assets.get(id) : null;

            this._onModelAsset(asset || null);

            if (! asset && id !== null)
                assets.once("add:" + id, this._onModelAsset, this);
        },

        _onModelAsset: function(asset) {
            var assets = this.system.app.assets;

            // clear old assets bindings
            if (this._assetOld) {
                assets.off("add:" + this._assetOld, this._onModelAsset, this);

                var assetOld = assets.get(this._assetOld);
                if (assetOld) {
                    assetOld.off('load', this._onAssetLoad, this);
                    assetOld.off('change', this._onAssetChange, this);
                    assetOld.off('remove', this._onAssetRemove, this);
                }
            }

            // remember new asset id
            this._assetOld = asset ? asset.id : 0;

            if (asset) {
                // subscribe to asset events
                asset.on('load', this._onAssetLoad, this);
                asset.on('change', this._onAssetChange, this);
                asset.on('remove', this._onAssetRemove, this);

                if (asset.resource) {
                    this._onModelLoaded(asset.resource.clone());
                } else {
                    assets.load(asset);
                }
            }
        },

        remove: function() {
            this._onModelAsset(null);
        },

        _onModelLoaded: function (model) {
            if (this.data.type === 'asset')
                this.model = model;
        },

        /**
         * @function
         * @private
         * @name pc.ModelComponent#onSetType
         * @description Handle changes to the 'type' variable
         */
        onSetType: function (name, oldValue, newValue) {
            var data = this.data;

            if (newValue) {
                var mesh = null;

                if (newValue === 'asset') {
                    if (this.data.asset !== null) {
                        this._setModelAsset(this.data.asset);
                    } else {
                        this.model = null;
                    }
                } else {
                    switch (newValue) {
                        case 'box':
                            mesh = this.system.box;
                            break;
                        case 'capsule':
                            mesh = this.system.capsule;
                            break;
                        case 'sphere':
                            mesh = this.system.sphere;
                            break;
                        case 'cone':
                            mesh = this.system.cone;
                            break;
                        case 'cylinder':
                            mesh = this.system.cylinder;
                            break;
                        case 'plane':
                            mesh = this.system.plane;
                            break;
                        default:
                            throw new Error("Invalid model type: " + newValue);
                    }

                    var node = new pc.GraphNode();

                    var model = new pc.Model();
                    model.graph = node;

                    model.meshInstances = [ new pc.MeshInstance(node, mesh, data.material) ];

                    if (this.system._inTools) {
                        model.generateWireframe();
                    }

                    this.model = model;
                    this.asset = null;
                }
            }
        },

        onSetAsset: function (name, oldValue, newValue) {
            var id = null;
            if (this.data.type === 'asset') {
                if (newValue !== null) {
                    id = newValue;

                    if (newValue instanceof pc.Asset) {
                        this.data.asset = newValue.id;
                        id = newValue.id;
                    }
                } else {
                    this.model = null;
                }
            }

            if (id === null)
                this.data.asset = null;

            this._setModelAsset(id);
        },

        onSetCastShadows: function (name, oldValue, newValue) {
            var model = this.data.model;
            if (model) {
                var scene = this.system.app.scene;
                var inScene = scene.containsModel(model);
                if (inScene) {
                    scene.removeModel(model);
                }

                var meshInstances = model.meshInstances;
                for (var i = 0; i < meshInstances.length; i++) {
                    meshInstances[i].castShadow = newValue;
                }

                if (inScene) {
                    scene.addModel(model);
                }
            }
        },

        onSetModel: function (name, oldValue, newValue) {
            if (oldValue) {
                this.system.app.scene.removeModel(oldValue);
                this.entity.removeChild(oldValue.getGraph());
                delete oldValue._entity;
            }

            if (newValue) {
                var componentData = this.data;
                var meshInstances = newValue.meshInstances;
                for (var i = 0; i < meshInstances.length; i++) {
                    meshInstances[i].castShadow = componentData.castShadows;
                    meshInstances[i].receiveShadow = componentData.receiveShadows;
                }

                this.entity.addChild(newValue.graph);

                if (this.enabled && this.entity.enabled) {
                    this.system.app.scene.addModel(newValue);
                }

                // Store the entity that owns this model
                newValue._entity = this.entity;

                // Update any animation component
                if (this.entity.animation) {
                    this.entity.animation.setModel(newValue);
                }

                // trigger event handler to load mapping
                // for new model
                if (this.data.type === 'asset') {
                    this.mapping = this.data.mapping;
                } else {
                    this._unsetMaterialEvents();
                }
            } else {
                this._unsetMaterialEvents();
            }
        },

        _onMaterialAssetRemove: function(asset) {
            var assets = this.system.app.assets;
            var id = isNaN(asset) ? asset.id : asset;

            if (asset && isNaN(asset) && asset.resource === this.material)
                this.material = pc.ModelHandler.DEFAULT_MATERIAL;

            assets.off('add:' + id, this._onMaterialAsset, this);
            assets.off('load:' + id, this._onMaterialAsset, this);
            assets.off('remove:' + id, this._onMaterialAssetRemove, this);
        },

        _onMaterialAsset: function(asset) {
            var assets = this.system.app.assets;

            if (asset.resource) {
                this.material = asset.resource;
            } else {
                assets.load(asset);
            }
        },

        setMaterialAsset: function (value) {
            // if the type of the value is not a number assume it is an pc.Asset
            var id = typeof value === 'number' || !value ? value : value.id;

            // var material;
            var assets = this.system.app.assets;
            var self = this;

            // unsubscribe
            if (this.data.materialAsset !== id) {
                if (this.data.materialAsset)
                    this._onMaterialAssetRemove(this.data.materialAsset);

                if (id) {
                    assets.on('load:' + id, this._onMaterialAsset, this);
                    assets.once('remove:' + id, this._onMaterialAssetRemove, this);
                }
            }

            // try to load the material asset
            if (id !== undefined && id !== null) {
                var asset = assets.get(id);
                if (asset)
                    this._onMaterialAsset(asset);

                // subscribe for adds
                assets.once('add:' + id, this._onMaterialAsset, this);
            } else if (id === null) {
                self.material = pc.ModelHandler.DEFAULT_MATERIAL;
            }

            var valueOld = this.data.materialAsset;
            this.data.materialAsset = id;
            this.fire('set', 'materialAsset', valueOld, id);
        },

        getMaterialAsset: function () {
            return this.system.app.assets.get(this.data.materialAsset);
        },

        onSetMaterial: function (name, oldValue, newValue) {
            if (newValue !== oldValue) {
                this.data.material = newValue;
                if (this.data.model && this.data.type !== 'asset') {
                    var meshInstances = this.data.model.meshInstances;
                    for (var i=0; i<meshInstances.length; i++) {
                        meshInstances[i].material = newValue;
                    }
                }
            }
        },

        onSetMapping: function (name, oldValue, newValue) {
            if (this.data.type !== 'asset' || !this.data.model) {
                return;
            }

            // unsubscribe from old events
            if (oldValue) {
                this._unsetMaterialEvents();
            }

            if (! newValue) {
                newValue = {};
            }

            var meshInstances = this.data.model.meshInstances;
            var modelAsset = this.asset ? this.system.app.assets.get(this.asset) : null;
            var assetMapping = modelAsset ? modelAsset.data.mapping : null;

            for (var i = 0, len = meshInstances.length; i < len; i++) {
                if (newValue[i] !== undefined) {
                    if (newValue[i]) {
                        this._loadAndSetMeshInstanceMaterial(newValue[i], meshInstances[i], i);
                    } else {
                        meshInstances[i].material = pc.ModelHandler.DEFAULT_MATERIAL;
                    }
                } else if (assetMapping) {
                    if (assetMapping[i] && (assetMapping[i].material || assetMapping[i].path)) {
                        var idOrPath = assetMapping[i].material || assetMapping[i].path;
                        this._loadAndSetMeshInstanceMaterial(idOrPath, meshInstances[i], i);
                    } else {
                        meshInstances[i].material = pc.ModelHandler.DEFAULT_MATERIAL;
                    }
                }
            }
        },

        _setMaterialEvent: function (index, event, id, handler) {
            var evt = event + ':' + id;
            this.system.app.assets.on(evt, handler, this);

            if (!this._materialEvents) this._materialEvents = [];

            if (!this._materialEvents[index]) this._materialEvents[index] = {};

            this._materialEvents[index][evt] = {
                id: id,
                handler: handler
            };
        },

        _unsetMaterialEvents: function () {
            var assets = this.system.app.assets;
            var events = this._materialEvents;
            if (! events)
                return;

            for (var i = 0, len = events.length; i < len; i++) {
                if (! events[i]) continue;
                var evt = events[i];
                for (var key in evt) {
                    assets.off(key, evt[key].handler, this);
                }
            }

            this._materialEvents = null;
        },

        _loadAndSetMeshInstanceMaterial: function (idOrPath, meshInstance, index) {
            var self = this;
            var asset;
            var assets = this.system.app.assets;

            var isPath = isNaN(parseInt(idOrPath, 10));

            // get asset by id or url
            if (!isPath) {
                asset = assets.get(idOrPath);
            } else if (self.asset) {
                var url = self._getMaterialAssetUrl(idOrPath);
                if (!url) return;

                asset = assets.getByUrl(url);
            }

            var handleMaterial = function (asset) {
                if (asset.resource) {
                    meshInstance.material = asset.resource;

                    self._setMaterialEvent(index, 'remove', asset.id, function () {
                        meshInstance.material = pc.ModelHandler.DEFAULT_MATERIAL;
                    });
                } else {
                    self._setMaterialEvent(index, 'load', asset.id, function (asset) {
                        meshInstance.material = asset.resource;

                        self._setMaterialEvent(index, 'remove', asset.id, function () {
                            meshInstance.material = pc.ModelHandler.DEFAULT_MATERIAL;
                        });
                    });

                    assets.load(asset);
                }
            };

            if (asset) {
                handleMaterial(asset);
            } else {
                meshInstance.material = pc.ModelHandler.DEFAULT_MATERIAL;
                self._setMaterialEvent(index, isPath ? 'add:url' : 'add', idOrPath, handleMaterial);
            }
        },

        _getMaterialAssetUrl: function (path) {
            if (!this.asset) return null;

            var modelAsset = this.system.app.assets.get(this.asset);
            if (!modelAsset) return null;

            var fileUrl = modelAsset.getFileUrl();
            var dirUrl = pc.path.getDirectory(fileUrl);
            return pc.path.join(dirUrl, path);
        },

        onSetReceiveShadows: function (name, oldValue, newValue) {
            if (newValue !== undefined) {
                var componentData = this.data;
                if (componentData.model) {
                    var meshInstances = componentData.model.meshInstances;
                    for (var i = 0; i < meshInstances.length; i++) {
                        meshInstances[i].receiveShadow = newValue;
                    }
                }
            }
        },

        onEnable: function () {
            ModelComponent._super.onEnable.call(this);

            var model = this.data.model;
            if (model) {
                var inScene = this.system.app.scene.containsModel(model);
                if (!inScene) {
                    this.system.app.scene.addModel(model);
                }
            }
        },

        onDisable: function () {
            ModelComponent._super.onDisable.call(this);

            var model = this.data.model;
            if (model) {
                var inScene = this.system.app.scene.containsModel(model);
                if (inScene) {
                    this.system.app.scene.removeModel(model);
                }
            }
        }
    });

    return {
        ModelComponent: ModelComponent
    };
}());
<|endoftext|>