<commit_before>import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
import _ from 'lodash';
import async from 'async';
import moment from 'moment';
import spahql from 'spahql';
import { connect } from 'react-redux';
import { updatePath } from 'redux-simple-router';
import { ActionCreators } from 'redux-undo';
import Card from 'material-ui/lib/card/card';
import CardHeader from 'material-ui/lib/card/card-header';
import CardTitle from 'material-ui/lib/card/card-title';
import CardMedia from 'material-ui/lib/card/card-media';
import CardText from 'material-ui/lib/card/card-text';
import CardActions from 'material-ui/lib/card/card-actions';
import Styles from 'material-ui/lib/styles';
import List from 'material-ui/lib/lists/list';
import ListDivider from 'material-ui/lib/lists/list-divider';
import ListItem from 'material-ui/lib/lists/list-item';
import CheckCircle from 'material-ui/lib/svg-icons/action/check-circle';
import TextField from 'material-ui/lib/text-field';
import Avatar from 'material-ui/lib/avatar';
import DropDownMenu from 'material-ui/lib/drop-down-menu';
import RaisedButton from 'material-ui/lib/raised-button';
import FontIcon from 'material-ui/lib/font-icon';
import DropDownIcon from 'material-ui/lib/drop-down-icon';
import IconMenu from 'material-ui/lib/menus/icon-menu';
import IconButton from 'material-ui/lib/icon-button';
import MoreVertIcon from 'material-ui/lib/svg-icons/navigation/more-vert';
import Menu from 'material-ui/lib/menu/menu';
import MenuItem from 'material-ui/lib/menus/menu-item';
import FlatButton from 'material-ui/lib/flat-button';
import Snackbar from 'material-ui/lib/snackbar';
import CircularProgress from 'material-ui/lib/circular-progress';
import { Grid, Row, Col } from 'react-bootstrap';
import { updateClassAttendance, updateNote, addNote, updateClassAttendanceLocal } from '../actions';

class EditDayAttendance extends Component {
  constructor(props, context) {
    super(props, context);
  }

  componentWillMount() {
    const { configs, params, dispatch } = this.props;

    let db = spahql.db(configs.data),
        attendances = db.select("//divisionClassAttendances/*[/attendanceDate =~ '^"+params.date+"']").values(),
        weekday = attendances[0].day;

    this.delayedAttendanceUpdate = _.debounce(function (divClass, count, event) {
      //console.log(divClass, event.target.value, attendanceDay);
      dispatch(updateClassAttendance(divClass.id, weekday, moment.utc(params.date+" 00:00:00Z").format('YYYY-MM-DD 00:00:00'), count));
    }, 500);

    this.setState({
      spahql: spahql,
      attendances: attendances,
      weekday: weekday,
      displayAttendance: this.displayAttendance(weekday)
    });
  }

  componentWillReceiveProps(nextProps) {
    console.log(nextProps);
  }

  componentDidMount() {
    const { dispatch } = this.props;
  }

  formatYears() {
    const { configs } = this.props;
    let years = configs.data[this.state.divisionConfig].divisionYears;
    return years.map(function(year, index){
      return {
        'id': year.id,
        'year': moment(year.endDate).format("YYYY")
      }
    });
  }

  formatDateRange(division) {
    return moment(division.start).format("MMM D YYYY") + " - " + moment(division.end).format("MMM D YYYY")
  }

  displayAttendance(weekday) {
    const { configs } = this.props;

    let db = spahql.db(configs.data),
        classDay = db.select("/*/classMeetingDays/*[/day == "+weekday+"]").value();

    if (!Array.isArray(classDay) && classDay !== null) {
      classDay = [classDay];
    } else if (classDay === null) {
      classDay = [];
    }
    let results = classDay.map(function(day, index){
      let config = db.select("/*[/id == "+day.divisionConfigId+"]"),
          classes = db.select("/*[/id == "+day.divisionConfigId+"]/divisionYears/0/divisions/0/divisionClasses");
      day.classes = classes.value();
      day.config = config.value();
    });
    return classDay;
  }

  getClassAttendance(divClass) {
    const { params } = this.props;
    let db = spahql.db(divClass),
        attendance = db.select("//divisionClassAttendances/*[/attendanceDate =~ '^"+params.date+"']").values();
    //console.log("getClassAttendance", divClass, attendance);
    if (attendance.length) {
      return attendance[0].count.toString();
    } else {
      return "0";
    }
  }

  attendanceUpdate(divClass, e) {
    const { dispatch, configs, params } = this.props,
          { attendances, weekday } = this.state,
          db = spahql.db(divClass),
          attendance = db.select("//divisionClassAttendances/*[/attendanceDate =~ '^"+params.date+"']"),
          exists = attendance.values.length,
          count = parseInt(e.target.value, 10);
    let attend = {},
        today = moment.utc(params.date+" 00:00:00Z").format("YYYY-MM-DD")+"T00:00:00.000Z";
    e.persist();
    if (!exists) {
      attend = {
        attendanceDate: today,
        count: count,
        createdAt: today,
        day: weekday,
        deletedAt: null,
        divisionClassId: divClass.id,
        id: null,
        revision: null,
        updatedAt: today,
        updating: true
      }
      divClass.divisionClassAttendances.unshift(attend);
    } else {
      attendance.values[0].count = count;
      attendance.values[0].updating = true;
    }
    dispatch(updateClassAttendanceLocal(divClass));
    this.delayedAttendanceUpdate(divClass, count, e);
  }

  handleTeacherTouchTap(teacher, divisionClass, e, el) {
    this.refs.snackbar.setState({
      teacher: teacher,
      divClass: divisionClass
    })
    this.refs.snackbar.show();
  }

  _handleInputChange(e) {
    const { people, dispatch } = this.props,
          filter = e.target.value;
    if (filter.length > 1) {
      dispatch(getPeople(people.key, e.target.value));
    }
  }

  _handleSelectValueChange(e) {
    const { people } = this.props;
    people.key = e.target.value;
  }

  menuItemTap(person, item, event) {
    const { dispatch, params, configs } = this.props;
    const { attendances, weekday } = this.state;
    let db = spahql.db(configs.data),
        divClass = db.select(divClassPath).value(),
        opts;
    switch (item) {
      case "confirm":
        opts = {confirmed: true};
        break;
      case "unconfirm":
        opts = {confirmed: false};
        break;
      default:
        opts = null;
        break;
    };
    dispatch(
      manageDivisionClassTeacher(
        item,
        config.id,
        year.id,
        division.id,
        divClass.id,
        classDay.day,
        person.id,
        opts
      )
    );
  }

  isUpdating(divClass) {
    const { params } = this.props;
    let db = spahql.db(divClass),
        attendance = db.select("//divisionClassAttendances/*[/attendanceDate =~ '^"+params.date+"']").values();

    if (attendance.length && "updating" in attendance) {
      console.log("isUpdating", true);
      return true;
    } else {
      console.log("isUpdating", false);
      return false;
    }
  }

  highlightText(e) {
    if (e) {
      e.target.select();
    }
  }

  render() {
    const { dispatch, params, configs, ...props } = this.props,
          { spahql, weekday, displayAttendance, attendances } = this.state;

    let db = spahql.db(configs.data);
    return (

        <Grid fluid={true}>
          <Row>
            <Col xs={12} sm={12} md={12} lg={12}>

Edit Attendance - {params.date}

Attendance

            </Col>
          </Row>
          {displayAttendance.map((attendance, index) =>
            <Row key={attendance.id}>
              <Col xs={12} sm={12} md={12} lg={12}>
                <Card>
                  <CardHeader
                    title={attendance.config.title+" Attendance"}
                    subtitle={moment.utc(params.date+" 00:00:00Z").format("dddd MM/DD/YYYY")}
                    avatar={<Avatar>{moment.utc(params.date+" 00:00:00Z").format("dd")}</Avatar>}>
                  </CardHeader>
                  <CardMedia>
                    <Grid fluid={true}>
                      {attendance.classes.map((divClass, index) =>
                        <Col style={{display: "flex", alignItems: "center", justifyContent: "center"}} key={divClass.id} xs={12} sm={6} md={4} lg={3}>

                            <TextField
                              type="number"
                              hintText="Enter attendance"
                              value={::this.getClassAttendance(divClass)}
                              min="0"
                              max="500"
                              ref={"inputAttendance"+divClass.id}
                              onFocus={((...args)=>this.highlightText(...args))}
                              onChange={((...args)=>this.attendanceUpdate(divClass, ...args))}
                              floatingLabelText={divClass.class.title} />

<CircularProgress style={{display: (this.isUpdating(divClass)) ? "block" : "none"}} size={0.35} mode="indeterminate" />

                        </Col>
                      )}
                    </Grid>
                  </CardMedia>
                </Card>
              </Col>
            </Row>
          )}
        </Grid>

    );
  }
}

EditDayAttendance.propTypes = {
  dispatch: PropTypes.func.isRequired
};

function select(state) {
  return {
    configs: state.divisionConfigs.present,
    people: state.people.present
  };
}

export default connect(select)(EditDayAttendance);
<commit_msg>update attendance day edit view<commit_after>import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
import _ from 'lodash';
import async from 'async';
import moment from 'moment';
import spahql from 'spahql';
import { connect } from 'react-redux';
import { updatePath } from 'redux-simple-router';
import { ActionCreators } from 'redux-undo';
import Card from 'material-ui/lib/card/card';
import CardHeader from 'material-ui/lib/card/card-header';
import CardTitle from 'material-ui/lib/card/card-title';
import CardMedia from 'material-ui/lib/card/card-media';
import CardText from 'material-ui/lib/card/card-text';
import CardActions from 'material-ui/lib/card/card-actions';
import Styles from 'material-ui/lib/styles';
import List from 'material-ui/lib/lists/list';
import ListDivider from 'material-ui/lib/lists/list-divider';
import ListItem from 'material-ui/lib/lists/list-item';
import CheckCircle from 'material-ui/lib/svg-icons/action/check-circle';
import TextField from 'material-ui/lib/text-field';
import Avatar from 'material-ui/lib/avatar';
import DropDownMenu from 'material-ui/lib/drop-down-menu';
import RaisedButton from 'material-ui/lib/raised-button';
import FontIcon from 'material-ui/lib/font-icon';
import DropDownIcon from 'material-ui/lib/drop-down-icon';
import IconMenu from 'material-ui/lib/menus/icon-menu';
import IconButton from 'material-ui/lib/icon-button';
import MoreVertIcon from 'material-ui/lib/svg-icons/navigation/more-vert';
import Menu from 'material-ui/lib/menu/menu';
import MenuItem from 'material-ui/lib/menus/menu-item';
import FlatButton from 'material-ui/lib/flat-button';
import Snackbar from 'material-ui/lib/snackbar';
import CircularProgress from 'material-ui/lib/circular-progress';
import { Grid, Row, Col } from 'react-bootstrap';
import { updateClassAttendance, updateNote, addNote, updateClassAttendanceLocal } from '../actions';

class EditDayAttendance extends Component {
  constructor(props, context) {
    super(props, context);
  }

  componentWillMount() {
    console.log("EditDayAttendance:componentWillMount");
    const { configs, params, dispatch } = this.props;

    let db = spahql.db(configs.data);
    let attendances = db.select("/*/divisionYears/*/divisions/*/divisionClasses/*/divisionClassAttendances/*[/attendanceDate =~ '^"+params.date+"']").values(),
        weekday = attendances[0].day;

    this.delayedAttendanceUpdate = _.debounce(function (divClass, count, event) {
      //console.log(divClass, event.target.value, attendanceDay);
      dispatch(updateClassAttendance(divClass.id, weekday, moment.utc(params.date+" 00:00:00Z").format('YYYY-MM-DD 00:00:00'), count));
    }, 500);

    this.setState({
      attendances: attendances,
      weekday: weekday,
      displayAttendance: this.displayAttendance(weekday)
    });
  }

  componentWillReceiveProps(nextProps) {
    console.log(nextProps);
  }

  componentDidMount() {
    const { dispatch } = this.props;
  }

  formatYears() {
    const { configs } = this.props;
    let years = configs.data[this.state.divisionConfig].divisionYears;
    return years.map(function(year, index){
      return {
        'id': year.id,
        'year': moment(year.endDate).format("YYYY")
      }
    });
  }

  formatDateRange(division) {
    return moment(division.start).format("MMM D YYYY") + " - " + moment(division.end).format("MMM D YYYY")
  }

  displayAttendance(weekday) {
    console.log("displayAttendance");
    const { configs } = this.props;

    let db = spahql.db(configs.data),
        classDay = db.select("/*/classMeetingDays/*[/day == "+weekday+"]").value();

    if (!Array.isArray(classDay) && classDay !== null) {
      classDay = [classDay];
    } else if (classDay === null) {
      classDay = [];
    }
    let results = classDay.map(function(day, index){
      let config = db.select("/*[/id == "+day.divisionConfigId+"]"),
          classes = db.select("/*[/id == "+day.divisionConfigId+"]/divisionYears/0/divisions/0/divisionClasses");
      day.classes = classes.value();
      day.config = config.value();
    });
    return classDay;
  }

  getClassAttendance(divClass) {
    const { params } = this.props;
    let db = spahql.db(divClass),
        attendance = db.select("//divisionClassAttendances/*[/attendanceDate =~ '^"+params.date+"']").values();
    //console.log("getClassAttendance", divClass, attendance);
    if (attendance.length) {
      return attendance[0].count.toString();
    } else {
      return "0";
    }
  }

  attendanceUpdate(divClass, e) {
    const { dispatch, configs, params } = this.props,
          { attendances, weekday } = this.state,
          db = spahql.db(divClass),
          attendance = db.select("//divisionClassAttendances/*[/attendanceDate =~ '^"+params.date+"']"),
          exists = attendance.values.length,
          count = parseInt(e.target.value, 10);
    let attend = {},
        today = moment.utc(params.date+" 00:00:00Z").format("YYYY-MM-DD")+"T00:00:00.000Z";
    e.persist();
    if (!exists) {
      attend = {
        attendanceDate: today,
        count: count,
        createdAt: today,
        day: weekday,
        deletedAt: null,
        divisionClassId: divClass.id,
        id: null,
        revision: null,
        updatedAt: today,
        updating: true
      }
      divClass.divisionClassAttendances.unshift(attend);
    } else {
      attendance.values[0].count = count;
      attendance.values[0].updating = true;
    }
    dispatch(updateClassAttendanceLocal(divClass));
    this.delayedAttendanceUpdate(divClass, count, e);
  }

  handleTeacherTouchTap(teacher, divisionClass, e, el) {
    this.refs.snackbar.setState({
      teacher: teacher,
      divClass: divisionClass
    })
    this.refs.snackbar.show();
  }

  _handleInputChange(e) {
    const { people, dispatch } = this.props,
          filter = e.target.value;
    if (filter.length > 1) {
      dispatch(getPeople(people.key, e.target.value));
    }
  }

  _handleSelectValueChange(e) {
    const { people } = this.props;
    people.key = e.target.value;
  }

  menuItemTap(person, item, event) {
    const { dispatch, params, configs } = this.props;
    const { attendances, weekday } = this.state;
    let db = spahql.db(configs.data),
        divClass = db.select(divClassPath).value(),
        opts;
    switch (item) {
      case "confirm":
        opts = {confirmed: true};
        break;
      case "unconfirm":
        opts = {confirmed: false};
        break;
      default:
        opts = null;
        break;
    };
    dispatch(
      manageDivisionClassTeacher(
        item,
        config.id,
        year.id,
        division.id,
        divClass.id,
        classDay.day,
        person.id,
        opts
      )
    );
  }

  isUpdating(divClass) {
    const { params } = this.props;
    let db = spahql.db(divClass),
        attendance = db.select("//divisionClassAttendances/*[/attendanceDate =~ '^"+params.date+"']").values();

    if (attendance.length && "updating" in attendance) {
      console.log("isUpdating", true);
      return true;
    } else {
      console.log("isUpdating", false);
      return false;
    }
  }

  highlightText(e) {
    if (e) {
      e.target.select();
    }
  }

  render() {
    const { dispatch, params, configs, ...props } = this.props,
          { weekday, displayAttendance, attendances } = this.state;

    let db = spahql.db(configs.data);
    return (

        <Grid fluid={true}>
          <Row>
            <Col xs={12} sm={12} md={12} lg={12}>

                    Dashboard
                    Attendance
                    Edit

            </Col>
          </Row>
          {displayAttendance.map((attendance, index) =>
            <Row key={attendance.id}>
              <Col xs={12} sm={12} md={12} lg={12}>
                <Card>
                  <CardHeader
                    title={attendance.config.title+" Attendance"}
                    subtitle={moment.utc(params.date+" 00:00:00Z").format("dddd MM/DD/YYYY")}
                    avatar={<Avatar>{moment.utc(params.date+" 00:00:00Z").format("dd")}</Avatar>}>
                  </CardHeader>
                  <CardMedia>
                    <Grid fluid={true}>
                      {attendance.classes.map((divClass, index) =>
                        <Col style={{display: "flex", alignItems: "center", justifyContent: "center"}} key={divClass.id} xs={12} sm={6} md={4} lg={3}>

                            <TextField
                              type="number"
                              hintText="Enter attendance"
                              value={::this.getClassAttendance(divClass)}
                              min="0"
                              max="500"
                              ref={"inputAttendance"+divClass.id}
                              onFocus={((...args)=>this.highlightText(...args))}
                              onChange={((...args)=>this.attendanceUpdate(divClass, ...args))}
                              floatingLabelText={divClass.class.title} />

<CircularProgress style={{display: (this.isUpdating(divClass)) ? "block" : "none"}} size={0.35} mode="indeterminate" />

                        </Col>
                      )}
                    </Grid>
                  </CardMedia>
                </Card>
              </Col>
            </Row>
          )}
        </Grid>

    );
  }
}

EditDayAttendance.propTypes = {
  dispatch: PropTypes.func.isRequired
};

function select(state) {
  return {
    configs: state.divisionConfigs.present,
    people: state.people.present
  };
}

export default connect(select)(EditDayAttendance);
<|endoftext|>


<commit_before>//scene.js
//Scene Object management and rendering

(function() {
  //Define some locals
  var Vec3 = PhiloGL.Vec3,
      Mat4 = PhiloGL.Mat4;

  //Scene class
  var Scene = function(program, camera, opt) {
    opt = $.merge({
      lights: {
        enable: false,
        //ambient light
        ambient: {
          r: 0.2,
          g: 0.2,
          b: 0.2
        },
        //directional light
        directional: {
          direction: {
            x: 1,
            y: 1,
            z: 1
          },
          color: {
            r: 0,
            g: 0,
            b: 0
          }
        }
        //point light
        //points: []
      },
      effects: {
        fog: false
        // { near, far, color }
      }
    }, opt || {});

    this.program = opt.program ? program[opt.program] : program;
    this.camera = camera;
    this.models = [];
    this.config = opt;
  };

  Scene.prototype = {

    add: function() {
      for (var i = 0, models = this.models, l = arguments.length; i < l; i++) {
        var model = arguments[i];
        //Generate unique id for model
        model.id = model.id || $.uid();
        models.push(model);
        //Create and load Buffers
        this.defineBuffers(model);
      }
    },

    remove: function(model) {
      var models = this.models,
          indexOf = models.indexOf(model);

      if (indexOf > -1) {
        models.splice(indexOf, 1);
      }
    },

    getProgram: function(obj) {
      var program = this.program;
      if (program.$$family != 'program' && obj && obj.program) {
        program = program[obj.program];
        program.use();
        return program;
      }
      return program;
    },

    defineBuffers: function(obj) {
      var program = this.getProgram(obj),
          prevDynamic = obj.dynamic;

      obj.dynamic = true;
      obj.setState(program);
      obj.dynamic = prevDynamic;
      obj.unsetState(program);
    },

    beforeRender: function(program) {
      //Setup lighting and scene effects like fog, etc.
      this.setupLighting(program);
      this.setupEffects(program);
      if (this.camera) {
        this.camera.setStatus(program);
      }
    },

    //Setup the lighting system: ambient, directional, point lights.
    setupLighting: function(program) {
      //Setup Lighting
      var abs = Math.abs,
          camera = this.camera,
          cpos = camera.position,
          light = this.config.lights,
          ambient = light.ambient,
          directional = light.directional,
          dcolor = directional.color,
          dir = directional.direction,
          enable = light.enable,
          points = light.points && $.splat(light.points) || [],
          numberPoints = points.length,
          pointLocations = [],
          pointColors = [],
          enableSpecular = [],
          pointSpecularColors = [];

      //Normalize lighting direction vector
      dir = new Vec3(dir.x, dir.y, dir.z).$unit().$scale(-1);

      //Set light uniforms. Ambient and directional lights.
      program.setUniform('enableLights', enable);

      if (!enable) return;

      program.setUniform('ambientColor', [ambient.r, ambient.g, ambient.b]);
      program.setUniform('directionalColor', [dcolor.r, dcolor.g, dcolor.b]);
      program.setUniform('lightingDirection', [dir.x, dir.y, dir.z]);

      //Set point lights
      program.setUniform('numberPoints', numberPoints);
      for (var i = 0, l = numberPoints; i < l; i++) {
        var point = points[i],
            position = point.position,
            color = point.color || point.diffuse,
            spec = point.specular;

        pointLocations.push(position.x, position.y, position.z);
        pointColors.push(color.r, color.g, color.b);

        //Add specular color
        enableSpecular.push(+!!spec);
        if (spec) {
          pointSpecularColors.push(spec.r, spec.g, spec.b);
        } else {
          pointSpecularColors.push(0, 0, 0);
        }
      }

      if (pointLocations.length) {
        program.setUniforms({
          'pointLocation': pointLocations,
          'pointColor': pointColors
        });
        program.setUniforms({
          'enableSpecular': enableSpecular,
          'pointSpecularColor': pointSpecularColors
        });
      }

    },

    //Setup effects like fog, etc.
    setupEffects: function(program) {
      var config = this.config.effects,
          fog = config.fog,
          color = fog.color || { r: 0.5, g: 0.5, b: 0.5 };

      if (fog) {
        program.setUniforms({
          'hasFog': true,
          'fogNear': fog.near,
          'fogFar': fog.far,
          'fogColor': [color.r, color.g, color.b]
        });
      } else {
        program.setUniform('hasFog', false);
      }
    },

    //Renders all objects in the scene.
    render: function(opt) {
      opt = opt || {};
      var camera = this.camera,
          program = this.program,
          renderProgram = opt.renderProgram,
          pType = $.type(program),
          multiplePrograms = !renderProgram && pType == 'object',
          options = $.extend({
            onBeforeRender: $.empty,
            onAfterRender: $.empty
          }, opt || {});

      //If we're just using one program then
      //execute the beforeRender method once.
      !multiplePrograms && this.beforeRender(renderProgram || program);

      //Go through each model and render it.
      for (var i = 0, models = this.models, l = models.length; i < l; ++i) {
        var elem = models[i];
        if (elem.display) {
          var program = renderProgram || this.getProgram(elem);
          //Setup the beforeRender method for each object
          //when there are multiple programs to be used.
          multiplePrograms && this.beforeRender(program);
          elem.onBeforeRender(program, camera);
          options.onBeforeRender(elem, i);
          this.renderObject(elem, program);
          options.onAfterRender(elem, i);
          elem.onAfterRender(program, camera);
        }
      }
    },

    renderToTexture: function(name, opt) {
      opt = opt || {};
      var texture = app.textures[name + '-texture'],
          texMemo = app.textureMemo[name + '-texture'];

      this.render(opt);

      gl.bindTexture(texMemo.textureType, texture);
      //gl.generateMipmap(texMemo.textureType);
      //gl.bindTexture(texMemo.textureType, null);
    },

    renderObject: function(obj, program) {
      var camera = this.camera,
          view = camera.view,
          projection = camera.projection,
          object = obj.matrix,
          world = view.mulMat4(object),
          worldInverse = world.invert(),
          worldInverseTranspose = worldInverse.transpose();

      obj.setState(program);

      //Now set view and normal matrices
      program.setUniforms({
        objectMatrix: object,
        worldMatrix: world,
        worldInverseMatrix: worldInverse,
        worldInverseTransposeMatrix: worldInverseTranspose
//        worldViewProjection:  view.mulMat4(object).$mulMat4(view.mulMat4(projection))
      });

      //Draw
      //TODO(nico): move this into O3D, but, somehow, abstract the gl.draw* methods inside that object.
      if (obj.render) {
        obj.render(gl, program, camera);
      } else {
        if (obj.$indicesLength) {
          gl.drawElements((obj.drawType !== undefined) ? gl.get(obj.drawType) : gl.TRIANGLES, obj.$indicesLength, gl.UNSIGNED_SHORT, 0);
        } else {
          gl.drawArrays((obj.drawType !== undefined) ? gl.get(obj.drawType) : gl.TRIANGLES, 0, obj.$verticesLength / 3);
        }
      }

      obj.unsetState(program);
    },

    //setup picking framebuffer
    setupPicking: function() {
      //create picking program
      var program = PhiloGL.Program.fromDefaultShaders(),
          floor = Math.floor;
      //create framebuffer
      app.setFrameBuffer('$picking', {
        width: 5,
        height: 1,
        bindToTexture: {
          parameters: [{
            name: 'TEXTURE_MAG_FILTER',
            value: 'LINEAR'
          }, {
            name: 'TEXTURE_MIN_FILTER',
            value: 'LINEAR'
          },{
            name: 'TEXTURE_WRAP_S',
            value: 'CLAMP_TO_EDGE'
          }, {
            name: 'TEXTURE_WRAP_T',
            value: 'CLAMP_TO_EDGE'
          }]
        },
        bindToRenderBuffer: true
      });
      app.setFrameBuffer('$picking', false);
      this.pickingProgram = program;
    },

    //returns an element at the given position
    //returns an element at the given position
    pick: function(x, y, opt) {
      opt = opt || {};
      //setup the picking program if this is
      //the first time we enter the method.
      if (!this.pickingProgram) {
        this.setupPicking();
      }

      var o3dHash = {},
          o3dList = [],
          program = app.usedProgram,
          pickingProgram = this.pickingProgram,
          camera = this.camera,
          oldtarget = camera.target,
          oldaspect = camera.aspect,
          config = this.config,
          memoLightEnable = config.lights.enable,
          memoFog = config.effects.fog,
          canvas = gl.canvas,
          viewport = opt.viewport || {},
          width = viewport.width || canvas.offsetWidth || canvas.width,
          height = viewport.height || canvas.offsetHeight || canvas.height,
          floor = Math.floor,
          resWidth = 5,
          resHeight = 1,
          xp = x - (viewport.x || 0),
          yp = y - (viewport.y || 0),
          ndcx = xp * 2 / width - 1,
          ndcy = 1 - yp * 2 / height,
          target = this.unproject([ndcx, ndcy,  1.0], camera),
          hash = [],
          pixel = new Uint8Array(1 * 1 * 4),
          index = 0,
          backgroundColor, capture, pindex;

      this.camera.target = target;
      this.camera.update ();
      //setup the scene for picking
      config.lights.enable = false;
      config.effects.fog = false;

      //enable picking and render to texture
      app.setFrameBuffer('$picking', true);
      pickingProgram.use();
      pickingProgram.setUniform('enablePicking', true);

      //render the scene to a texture
      gl.disable(gl.BLEND);
      gl.viewport(0, 0, resWidth, resHeight);
      gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
      //read the background color so we don't step on it
      gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
      backgroundColor = pixel[0] + pixel[1] * 256 + pixel[2] * 256 * 256;

      //render picking scene
      this.renderPickingScene({
        background: backgroundColor,
        o3dHash: o3dHash,
        o3dList: o3dList,
        hash: hash
      });

      // the target point is in the center of the screen,
      // so it should be the center point.
      gl.readPixels(2, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);

      var stringColor = [pixel[0], pixel[1], pixel[2]].join(),
          elem = o3dHash[stringColor],
          pick;

     console.log('o3dHash', stringColor, x, y, width, height);

      if (!elem) {
        for (var i = 0, l = o3dList.length; i < l; i++) {
          elem = o3dList[i];
          pick = elem.pick(pixel);
          if (pick !== false) {
            elem.$pickingIndex = pick;
          } else {
            elem = false;
          }
        }
      }

      //restore all values and unbind buffers
      app.setFrameBuffer('$picking', false);
      app.setTexture('$picking-texture', false);
      pickingProgram.setUniform('enablePicking', false);
      config.lights.enable = memoLightEnable;
      config.effects.fog = memoFog;

      //restore previous program
      if (program) program.use();
      //restore the viewport size to original size
      gl.viewport(viewport.x || 0,
    		          viewport.y || 0,
    		          width,
    		          height);
      //restore camera properties
      camera.target = oldtarget;
      camera.aspect = oldaspect;
      camera.update();

      //store model hash and pixel array
      this.o3dHash = o3dHash;
      this.o3dList = o3dList;
      this.pixel = pixel;
      this.capture = capture;

      return elem && elem.pickable && elem;
    },

    unproject: function(pt, camera) {
      return camera.view.invert().mulMat4(camera.projection.invert()).mulVec3(pt);
    },

    renderPickingScene: function(opt) {
      //if set through the config, render a custom scene.
      if (this.config.renderPickingScene) {
        this.config.renderPickingScene.call(this, opt);
        return;
      }

      var pickingProgram = this.pickingProgram,
          o3dHash = opt.o3dHash,
          o3dList = opt.o3dList,
          background = opt.background,
          hash = opt.hash,
          index = 0;

      //render to texture
      this.renderToTexture('$picking', {
        renderProgram: pickingProgram,
        onBeforeRender: function(elem, i) {
          if (i == background) {
            index = 1;
          }
          var suc = i + index,
              hasPickingColors = !!elem.pickingColors;

          pickingProgram.setUniform('hasPickingColors', hasPickingColors);

          if (!hasPickingColors) {
            hash[0] = suc % 256;
            hash[1] = ((suc / 256) >> 0) % 256;
            hash[2] = ((suc / (256 * 256)) >> 0) % 256;
            pickingProgram.setUniform('pickColor', [hash[0] / 255, hash[1] / 255, hash[2] / 255]);
            o3dHash[hash.join()] = elem;
          } else {
            o3dList.push(elem);
          }
        }
      });
    },

    resetPicking: $.empty
  };

  Scene.MAX_TEXTURES = 10;
  Scene.MAX_POINT_LIGHTS = 4;
  Scene.PICKING_RES = 4;

  PhiloGL.Scene = Scene;

})();
<commit_msg>Add pixelRatio option to scene.pick<commit_after>//scene.js
//Scene Object management and rendering

(function() {
  //Define some locals
  var Vec3 = PhiloGL.Vec3,
      Mat4 = PhiloGL.Mat4;

  //Scene class
  var Scene = function(program, camera, opt) {
    opt = $.merge({
      lights: {
        enable: false,
        //ambient light
        ambient: {
          r: 0.2,
          g: 0.2,
          b: 0.2
        },
        //directional light
        directional: {
          direction: {
            x: 1,
            y: 1,
            z: 1
          },
          color: {
            r: 0,
            g: 0,
            b: 0
          }
        }
        //point light
        //points: []
      },
      effects: {
        fog: false
        // { near, far, color }
      }
    }, opt || {});

    this.program = opt.program ? program[opt.program] : program;
    this.camera = camera;
    this.models = [];
    this.config = opt;
  };

  Scene.prototype = {

    add: function() {
      for (var i = 0, models = this.models, l = arguments.length; i < l; i++) {
        var model = arguments[i];
        //Generate unique id for model
        model.id = model.id || $.uid();
        models.push(model);
        //Create and load Buffers
        this.defineBuffers(model);
      }
    },

    remove: function(model) {
      var models = this.models,
          indexOf = models.indexOf(model);

      if (indexOf > -1) {
        models.splice(indexOf, 1);
      }
    },

    getProgram: function(obj) {
      var program = this.program;
      if (program.$$family != 'program' && obj && obj.program) {
        program = program[obj.program];
        program.use();
        return program;
      }
      return program;
    },

    defineBuffers: function(obj) {
      var program = this.getProgram(obj),
          prevDynamic = obj.dynamic;

      obj.dynamic = true;
      obj.setState(program);
      obj.dynamic = prevDynamic;
      obj.unsetState(program);
    },

    beforeRender: function(program) {
      //Setup lighting and scene effects like fog, etc.
      this.setupLighting(program);
      this.setupEffects(program);
      if (this.camera) {
        this.camera.setStatus(program);
      }
    },

    //Setup the lighting system: ambient, directional, point lights.
    setupLighting: function(program) {
      //Setup Lighting
      var abs = Math.abs,
          camera = this.camera,
          cpos = camera.position,
          light = this.config.lights,
          ambient = light.ambient,
          directional = light.directional,
          dcolor = directional.color,
          dir = directional.direction,
          enable = light.enable,
          points = light.points && $.splat(light.points) || [],
          numberPoints = points.length,
          pointLocations = [],
          pointColors = [],
          enableSpecular = [],
          pointSpecularColors = [];

      //Normalize lighting direction vector
      dir = new Vec3(dir.x, dir.y, dir.z).$unit().$scale(-1);

      //Set light uniforms. Ambient and directional lights.
      program.setUniform('enableLights', enable);

      if (!enable) return;

      program.setUniform('ambientColor', [ambient.r, ambient.g, ambient.b]);
      program.setUniform('directionalColor', [dcolor.r, dcolor.g, dcolor.b]);
      program.setUniform('lightingDirection', [dir.x, dir.y, dir.z]);

      //Set point lights
      program.setUniform('numberPoints', numberPoints);
      for (var i = 0, l = numberPoints; i < l; i++) {
        var point = points[i],
            position = point.position,
            color = point.color || point.diffuse,
            spec = point.specular;

        pointLocations.push(position.x, position.y, position.z);
        pointColors.push(color.r, color.g, color.b);

        //Add specular color
        enableSpecular.push(+!!spec);
        if (spec) {
          pointSpecularColors.push(spec.r, spec.g, spec.b);
        } else {
          pointSpecularColors.push(0, 0, 0);
        }
      }

      if (pointLocations.length) {
        program.setUniforms({
          'pointLocation': pointLocations,
          'pointColor': pointColors
        });
        program.setUniforms({
          'enableSpecular': enableSpecular,
          'pointSpecularColor': pointSpecularColors
        });
      }

    },

    //Setup effects like fog, etc.
    setupEffects: function(program) {
      var config = this.config.effects,
          fog = config.fog,
          color = fog.color || { r: 0.5, g: 0.5, b: 0.5 };

      if (fog) {
        program.setUniforms({
          'hasFog': true,
          'fogNear': fog.near,
          'fogFar': fog.far,
          'fogColor': [color.r, color.g, color.b]
        });
      } else {
        program.setUniform('hasFog', false);
      }
    },

    //Renders all objects in the scene.
    render: function(opt) {
      opt = opt || {};
      var camera = this.camera,
          program = this.program,
          renderProgram = opt.renderProgram,
          pType = $.type(program),
          multiplePrograms = !renderProgram && pType == 'object',
          options = $.extend({
            onBeforeRender: $.empty,
            onAfterRender: $.empty
          }, opt || {});

      //If we're just using one program then
      //execute the beforeRender method once.
      !multiplePrograms && this.beforeRender(renderProgram || program);

      //Go through each model and render it.
      for (var i = 0, models = this.models, l = models.length; i < l; ++i) {
        var elem = models[i];
        if (elem.display) {
          var program = renderProgram || this.getProgram(elem);
          //Setup the beforeRender method for each object
          //when there are multiple programs to be used.
          multiplePrograms && this.beforeRender(program);
          elem.onBeforeRender(program, camera);
          options.onBeforeRender(elem, i);
          this.renderObject(elem, program);
          options.onAfterRender(elem, i);
          elem.onAfterRender(program, camera);
        }
      }
    },

    renderToTexture: function(name, opt) {
      opt = opt || {};
      var texture = app.textures[name + '-texture'],
          texMemo = app.textureMemo[name + '-texture'];

      this.render(opt);

      gl.bindTexture(texMemo.textureType, texture);
      //gl.generateMipmap(texMemo.textureType);
      //gl.bindTexture(texMemo.textureType, null);
    },

    renderObject: function(obj, program) {
      var camera = this.camera,
          view = camera.view,
          projection = camera.projection,
          object = obj.matrix,
          world = view.mulMat4(object),
          worldInverse = world.invert(),
          worldInverseTranspose = worldInverse.transpose();

      obj.setState(program);

      //Now set view and normal matrices
      program.setUniforms({
        objectMatrix: object,
        worldMatrix: world,
        worldInverseMatrix: worldInverse,
        worldInverseTransposeMatrix: worldInverseTranspose
//        worldViewProjection:  view.mulMat4(object).$mulMat4(view.mulMat4(projection))
      });

      //Draw
      //TODO(nico): move this into O3D, but, somehow, abstract the gl.draw* methods inside that object.
      if (obj.render) {
        obj.render(gl, program, camera);
      } else {
        if (obj.$indicesLength) {
          gl.drawElements((obj.drawType !== undefined) ? gl.get(obj.drawType) : gl.TRIANGLES, obj.$indicesLength, gl.UNSIGNED_SHORT, 0);
        } else {
          gl.drawArrays((obj.drawType !== undefined) ? gl.get(obj.drawType) : gl.TRIANGLES, 0, obj.$verticesLength / 3);
        }
      }

      obj.unsetState(program);
    },

    //setup picking framebuffer
    setupPicking: function() {
      //create picking program
      var program = PhiloGL.Program.fromDefaultShaders(),
          floor = Math.floor;
      //create framebuffer
      app.setFrameBuffer('$picking', {
        width: 5,
        height: 1,
        bindToTexture: {
          parameters: [{
            name: 'TEXTURE_MAG_FILTER',
            value: 'LINEAR'
          }, {
            name: 'TEXTURE_MIN_FILTER',
            value: 'LINEAR'
          },{
            name: 'TEXTURE_WRAP_S',
            value: 'CLAMP_TO_EDGE'
          }, {
            name: 'TEXTURE_WRAP_T',
            value: 'CLAMP_TO_EDGE'
          }]
        },
        bindToRenderBuffer: true
      });
      app.setFrameBuffer('$picking', false);
      this.pickingProgram = program;
    },

    pick: function(x, y, opt) {
      opt = opt || {};
      //setup the picking program if this is
      //the first time we enter the method.
      if (!this.pickingProgram) {
        this.setupPicking();
      }

      var o3dHash = {},
          o3dList = [],
          program = app.usedProgram,
          pickingProgram = this.pickingProgram,
          camera = this.camera,
          oldtarget = camera.target,
          oldaspect = camera.aspect,
          config = this.config,
          memoLightEnable = config.lights.enable,
          memoFog = config.effects.fog,
          canvas = gl.canvas,
          viewport = opt.viewport || {},
          pixelRatio = opt.pixelRatio || 1,
          width = (viewport.width || canvas.offsetWidth || canvas.width),
          height = (viewport.height || canvas.offsetHeight || canvas.height),
          floor = Math.floor,
          resWidth = 5,
          resHeight = 1,
          xp = (x * pixelRatio - (viewport.x || 0)),
          yp = (y * pixelRatio - (viewport.y || 0)),
          ndcx = xp * 2 / width - 1,
          ndcy = 1 - yp * 2 / height,
          target = this.unproject([ndcx, ndcy,  1.0], camera),
          hash = [],
          pixel = new Uint8Array(1 * 1 * 4),
          index = 0,
          backgroundColor, capture, pindex;

      this.camera.target = target;
      this.camera.update ();
      //setup the scene for picking
      config.lights.enable = false;
      config.effects.fog = false;

      //enable picking and render to texture
      app.setFrameBuffer('$picking', true);
      pickingProgram.use();
      pickingProgram.setUniform('enablePicking', true);

      //render the scene to a texture
      gl.disable(gl.BLEND);
      gl.viewport(0, 0, resWidth, resHeight);
      gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
      //read the background color so we don't step on it
      gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
      backgroundColor = pixel[0] + pixel[1] * 256 + pixel[2] * 256 * 256;

      //render picking scene
      this.renderPickingScene({
        background: backgroundColor,
        o3dHash: o3dHash,
        o3dList: o3dList,
        hash: hash
      });

      // the target point is in the center of the screen,
      // so it should be the center point.
      gl.readPixels(2, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);

      var stringColor = [pixel[0], pixel[1], pixel[2]].join(),
          elem = o3dHash[stringColor],
          pick;

     console.log('o3dHash', stringColor, x, y, width, height);

      if (!elem) {
        for (var i = 0, l = o3dList.length; i < l; i++) {
          elem = o3dList[i];
          pick = elem.pick(pixel);
          if (pick !== false) {
            elem.$pickingIndex = pick;
          } else {
            elem = false;
          }
        }
      }

      //restore all values and unbind buffers
      app.setFrameBuffer('$picking', false);
      app.setTexture('$picking-texture', false);
      pickingProgram.setUniform('enablePicking', false);
      config.lights.enable = memoLightEnable;
      config.effects.fog = memoFog;

      //restore previous program
      if (program) program.use();
      //restore the viewport size to original size
      gl.viewport(viewport.x || 0,
    		          viewport.y || 0,
    		          width,
    		          height);
      //restore camera properties
      camera.target = oldtarget;
      camera.aspect = oldaspect;
      camera.update();

      //store model hash and pixel array
      this.o3dHash = o3dHash;
      this.o3dList = o3dList;
      this.pixel = pixel;
      this.capture = capture;

      return elem && elem.pickable && elem;
    },

    unproject: function(pt, camera) {
      return camera.view.invert().mulMat4(camera.projection.invert()).mulVec3(pt);
    },

    renderPickingScene: function(opt) {
      //if set through the config, render a custom scene.
      if (this.config.renderPickingScene) {
        this.config.renderPickingScene.call(this, opt);
        return;
      }

      var pickingProgram = this.pickingProgram,
          o3dHash = opt.o3dHash,
          o3dList = opt.o3dList,
          background = opt.background,
          hash = opt.hash,
          index = 0;

      //render to texture
      this.renderToTexture('$picking', {
        renderProgram: pickingProgram,
        onBeforeRender: function(elem, i) {
          if (i == background) {
            index = 1;
          }
          var suc = i + index,
              hasPickingColors = !!elem.pickingColors;

          pickingProgram.setUniform('hasPickingColors', hasPickingColors);

          if (!hasPickingColors) {
            hash[0] = suc % 256;
            hash[1] = ((suc / 256) >> 0) % 256;
            hash[2] = ((suc / (256 * 256)) >> 0) % 256;
            pickingProgram.setUniform('pickColor', [hash[0] / 255, hash[1] / 255, hash[2] / 255]);
            o3dHash[hash.join()] = elem;
          } else {
            o3dList.push(elem);
          }
        }
      });
    },

    resetPicking: $.empty
  };

  Scene.MAX_TEXTURES = 10;
  Scene.MAX_POINT_LIGHTS = 4;
  Scene.PICKING_RES = 4;

  PhiloGL.Scene = Scene;

})();
<|endoftext|>


<commit_before>import cloneDeep from 'lodash/cloneDeep';
import React from 'react';
import { FormattedMessage } from 'react-intl';
import PropTypes from 'prop-types';

import { AppIcon } from '@folio/stripes/core';
import {
  Paneset,
  Pane,
  PaneMenu,
  PaneHeaderIconButton,
  PaneFooter,
  Button,
  ExpandAllButton,
  expandAllFunction,
  Row,
  Col,
  Headline,
  HasCommand,
  AccordionSet,
} from '@folio/stripes/components';
import stripesForm from '@folio/stripes/form';

import {
  EditUserInfo,
  EditExtendedInfo,
  EditContactInfo,
  EditProxy,
  EditServicePoints,
} from '../../components/EditSections';
import { getFullName } from '../../components/util';
import PermissionsAccordion from '../../components/PermissionsAccordion';
import {
  statusFilterConfig,
  permissionTypeFilterConfig,
} from '../../components/PermissionsAccordion/helpers/filtersConfig';

import css from './UserForm.css';

function validate(values, props) {
  const errors = {};
  errors.personal = {};

  if (!values.personal || !values.personal.lastName) {
    errors.personal.lastName = <FormattedMessage id="ui-users.errors.missingRequiredField" />;
  }

  if (!values.username && values.creds && values.creds.password) {
    errors.username = <FormattedMessage id="ui-users.errors.missingRequiredUsername" />;
  }

  if (!props.initialValues.id && (!values.creds || !values.creds.password) && values.username) {
    errors.creds = { password: <FormattedMessage id="ui-users.errors.missingRequiredPassword" /> };
  }

  if (!values.patronGroup) {
    errors.patronGroup = <FormattedMessage id="ui-users.errors.missingRequiredPatronGroup" />;
  }

  if (!values.personal || !values.personal.preferredContactTypeId) {
    if (errors.personal) errors.personal.preferredContactTypeId = <FormattedMessage id="ui-users.errors.missingRequiredContactType" />;
    else errors.personal = { preferredContactTypeId: <FormattedMessage id="ui-users.errors.missingRequiredContactType" /> };
  }

  if (values.personal && values.personal.addresses) {
    errors.personal.addresses = [];
    values.personal.addresses.forEach((addr) => {
      const err = (!addr.addressType) ? { addressType: <FormattedMessage id="ui-users.errors.missingRequiredAddressType" /> } : {};
      errors.personal.addresses.push(err);
    });
  }

  if (values.servicePoints && values.preferredServicePoint === undefined) {
    errors.preferredServicePoint = <FormattedMessage id="ui-users.errors.missingRequiredPreferredServicePoint" />;
  }

  return errors;
}

function asyncValidateField(field, value, validator) {
  return new Promise((resolve, reject) => {
    const query = `(${field}=="${value}")`;
    validator.reset();

    return validator.GET({ params: { query } }).then((users) => {
      if (users.length > 0) {
        const error = { [field]: <FormattedMessage id={`ui-users.errors.${field}Unavailable`} /> };
        return reject(error);
      } else {
        return resolve();
      }
    });
  });
}

function asyncValidate(values, dispatch, props, blurredField) {
  const { uniquenessValidator, initialValues } = props;
  const { username, barcode } = values;
  const curValue = values[blurredField];
  const prevValue = initialValues[blurredField];

  // validate on blur
  if (blurredField && curValue && curValue !== prevValue) {
    return asyncValidateField(blurredField, curValue, uniquenessValidator);
  }

  const promises = [];

  // validate on submit
  if (username !== initialValues.username) {
    promises.push(asyncValidateField('username', username, uniquenessValidator));
  }

  if (barcode && barcode !== initialValues.barcode) {
    promises.push(asyncValidateField('barcode', barcode, uniquenessValidator));
  }

  return Promise.all(promises);
}

class UserForm extends React.Component {
  static propTypes = {
    change: PropTypes.func,
    stripes: PropTypes.shape({
      store: PropTypes.object,
    }).isRequired,
    formData: PropTypes.object,
    handleSubmit: PropTypes.func.isRequired,
    location: PropTypes.object,
    history: PropTypes.object,
    uniquenessValidator: PropTypes.shape({
      reset: PropTypes.func.isRequired,
      GET: PropTypes.func.isRequired,
    }).isRequired,
    pristine: PropTypes.bool,
    submitting: PropTypes.bool,
    invalid: PropTypes.bool,
    onSubmit: PropTypes.func.isRequired,
    initialValues: PropTypes.object.isRequired,
  };

  static defaultProps = {
    pristine: false,
    submitting: false,
    invalid: false,
  };

  constructor() {
    super();

    this.state = {
      sections: {
        editUserInfo: true,
        extendedInfo: true,
        contactInfo: true,
        proxy: false,
        permissions: false,
        servicePoints: false,
      },
    };

    this.handleExpandAll = this.handleExpandAll.bind(this);
    this.handleSectionToggle = this.handleSectionToggle.bind(this);
    this.toggleAllSections = this.toggleAllSections.bind(this);
    this.collapseAllSections = this.collapseAllSections.bind(this);
    this.expandAllSections = this.expandAllSections.bind(this);

    this.ignoreEnterKey = this.ignoreEnterKey.bind(this);
    this.closeButton = React.createRef();
    this.saveButton = React.createRef();

    this.keyboardCommands = [
      {
        name: 'save',
        handler: this.handleSaveKeyCommand
      },
      {
        name: 'ignoreEnter',
        handler: this.ignoreEnterKey,
        shortcut: 'enter'
      },
      {
        name: 'cancel',
        handler: this.handleCancel,
        shortcut: 'esc'
      },
      {
        name: 'expandAllSections',
        handler: this.expandAllSections,
      },
      {
        name: 'collapseAllSections',
        handler: this.collapseAllSections,
      }
    ];
  }

  handleCancel = () => {
    this.props.history.goBack();
  }

  getAddFirstMenu() {
    return (
      <PaneMenu>
        <FormattedMessage id="ui-users.crud.closeNewUserDialog">
          {ariaLabel => (
            <PaneHeaderIconButton
              id="clickable-closenewuserdialog"
              onClick={this.handleCancel}
              ref={this.closeButton}
              ariaLabel={ariaLabel}
              icon="times"
            />
          )}
        </FormattedMessage>
      </PaneMenu>
    );
  }

  handleExpandAll(sections) {
    this.setState({ sections });
  }

  // eslint-disable-next-line class-methods-use-this
  ignoreEnterKey = (e) => {
    if (e.target !== this.saveButton.current) {
      e.preventDefault();
    }
  };

  handleSectionToggle({ id }) {
    this.setState((curState) => {
      const newState = cloneDeep(curState);
      newState.sections[id] = !newState.sections[id];

      return newState;
    });
  }

  toggleAllSections(expand) {
    this.setState((curState) => {
      const newSections = expandAllFunction(curState.sections, expand);

      return {
        sections: newSections
      };
    });
  }

  expandAllSections(e) {
    e.preventDefault();
    this.toggleAllSections(true);
  }

  collapseAllSections(e) {
    e.preventDefault();
    this.toggleAllSections(false);
  }

  handleSaveKeyCommand = (e) => {
    e.preventDefault();
    this.executeSave();
  };

  executeSave() {
    const {
      handleSubmit,
      onSubmit,
    } = this.props;

    const submitter = handleSubmit(onSubmit);

    submitter();
  }

  getPaneFooter() {
    const {
      pristine,
      submitting,
      invalid,
      onCancel,
    } = this.props;

    const disabled = pristine || submitting || invalid;

    const startButton = (
      <Button
        data-test-user-form-cancel-button
        marginBottom0
        id="clickable-cancel"
        buttonStyle="default mega"
        onClick={onCancel}
      >
        <FormattedMessage id="ui-users.cancel" />
      </Button>
    );

    const endButton = (
      <Button
        data-test-user-form-submit-button
        marginBottom0
        id="clickable-save"
        buttonStyle="primary mega"
        type="submit"
        disabled={disabled}
        buttonRef={this.saveButton}
      >
        <FormattedMessage id="ui-users.saveAndClose" />
      </Button>
    );

    return (
      <PaneFooter
        renderStart={startButton}
        renderEnd={endButton}
        className={css.paneFooterClass}
        innerClassName={css.paneFooterContentClass}
      />
    );
  }

  render() {
    const {
      initialValues,
      handleSubmit,
      formData,
      stripes,
      change, // from redux-form...
    } = this.props;

    const { sections } = this.state;
    const firstMenu = this.getAddFirstMenu();
    const footer = this.getPaneFooter();
    const fullName = getFullName(initialValues);
    const paneTitle = initialValues.id
      ? fullName
      : <FormattedMessage id="ui-users.crud.createUser" />;

    return (
      <HasCommand commands={this.keyboardCommands}>
        <form
          data-test-form-page
          className={css.UserFormRoot}
          id="form-user"
          onSubmit={handleSubmit}
        >
          <Paneset>
            <Pane
              defaultWidth="100%"
              firstMenu={firstMenu}
              footer={footer}
              appIcon={<AppIcon app="users" appIconKey="users" />}
              paneTitle={

                  {paneTitle}

              }
            >

                <Headline
                  size="xx-large"
                  tag="h2"
                  data-test-header-title
                >
                  {fullName}
                </Headline>
                <Row end="xs">
                  <Col xs>
                    <ExpandAllButton
                      accordionStatus={sections}
                      onToggle={this.handleExpandAll}
                    />
                  </Col>
                </Row>
                <AccordionSet>
                  <EditUserInfo
                    accordionId="editUserInfo"
                    expanded={sections.editUserInfo}
                    onToggle={this.handleSectionToggle}
                    initialValues={initialValues}
                    patronGroups={formData.patronGroups}
                  />
                  <EditExtendedInfo
                    accordionId="extendedInfo"
                    expanded={sections.extendedInfo}
                    onToggle={this.handleSectionToggle}
                    userId={initialValues.id}
                    userFirstName={initialValues.personal.firstName}
                    userEmail={initialValues.personal.email}
                  />
                  <EditContactInfo
                    accordionId="contactInfo"
                    expanded={sections.contactInfo}
                    onToggle={this.handleSectionToggle}
                    addressTypes={formData.addressTypes}
                    preferredContactTypeId={initialValues.preferredContactTypeId}
                  />
                  {initialValues.id &&

                      <EditProxy
                        accordionId="proxy"
                        expanded={sections.proxy}
                        onToggle={this.handleSectionToggle}
                        sponsors={initialValues.sponsors}
                        proxies={initialValues.proxies}
                        fullName={fullName}
                        stripes={stripes}
                        change={change}
                      />
                      <PermissionsAccordion
                        filtersConfig={[
                          permissionTypeFilterConfig,
                          statusFilterConfig,
                        ]}
                        visibleColumns={[
                          'selected',
                          'permissionName',
                          'type',
                          'status',
                        ]}
                        accordionId="permissions"
                        expanded={sections.permissions}
                        onToggle={this.handleSectionToggle}
                        headlineContent={<FormattedMessage id="ui-users.permissions.userPermissions" />}
                        permToRead="perms.permissions.get"
                        permToDelete="perms.permissions.item.delete"
                        permToModify="perms.permissions.item.put"
                        formName="userForm"
                        permissionsField="permissions"
                      />
                      <EditServicePoints
                        accordionId="servicePoints"
                        expanded={sections.servicePoints}
                        onToggle={this.handleSectionToggle}
                        {...this.props}
                      />

                  }
                </AccordionSet>

            </Pane>
          </Paneset>
        </form>
      </HasCommand>
    );
  }
}

export default stripesForm({
  form: 'userForm',
  validate,
  asyncValidate,
  asyncBlurFields: ['username', 'barcode'],
  navigationCheck: true,
  enableReinitialize: true,
})(UserForm);
<commit_msg>UIU-1289: Add trim method to username value<commit_after>import cloneDeep from 'lodash/cloneDeep';
import React from 'react';
import { FormattedMessage } from 'react-intl';
import PropTypes from 'prop-types';

import { AppIcon } from '@folio/stripes/core';
import {
  Paneset,
  Pane,
  PaneMenu,
  PaneHeaderIconButton,
  PaneFooter,
  Button,
  ExpandAllButton,
  expandAllFunction,
  Row,
  Col,
  Headline,
  HasCommand,
  AccordionSet,
} from '@folio/stripes/components';
import stripesForm from '@folio/stripes/form';

import {
  EditUserInfo,
  EditExtendedInfo,
  EditContactInfo,
  EditProxy,
  EditServicePoints,
} from '../../components/EditSections';
import { getFullName } from '../../components/util';
import PermissionsAccordion from '../../components/PermissionsAccordion';
import {
  statusFilterConfig,
  permissionTypeFilterConfig,
} from '../../components/PermissionsAccordion/helpers/filtersConfig';

import css from './UserForm.css';

function validate(values, props) {
  const errors = {};
  errors.personal = {};

  if (!values.personal || !values.personal.lastName) {
    errors.personal.lastName = <FormattedMessage id="ui-users.errors.missingRequiredField" />;
  }

  if (!values.username && values.creds && values.creds.password) {
    errors.username = <FormattedMessage id="ui-users.errors.missingRequiredUsername" />;
  }

  if (!props.initialValues.id && (!values.creds || !values.creds.password) && values.username) {
    errors.creds = { password: <FormattedMessage id="ui-users.errors.missingRequiredPassword" /> };
  }

  if (!values.patronGroup) {
    errors.patronGroup = <FormattedMessage id="ui-users.errors.missingRequiredPatronGroup" />;
  }

  if (!values.personal || !values.personal.preferredContactTypeId) {
    if (errors.personal) errors.personal.preferredContactTypeId = <FormattedMessage id="ui-users.errors.missingRequiredContactType" />;
    else errors.personal = { preferredContactTypeId: <FormattedMessage id="ui-users.errors.missingRequiredContactType" /> };
  }

  if (values.personal && values.personal.addresses) {
    errors.personal.addresses = [];
    values.personal.addresses.forEach((addr) => {
      const err = (!addr.addressType) ? { addressType: <FormattedMessage id="ui-users.errors.missingRequiredAddressType" /> } : {};
      errors.personal.addresses.push(err);
    });
  }

  if (values.servicePoints && values.preferredServicePoint === undefined) {
    errors.preferredServicePoint = <FormattedMessage id="ui-users.errors.missingRequiredPreferredServicePoint" />;
  }

  return errors;
}

function asyncValidateField(field, value, validator) {
  return new Promise((resolve, reject) => {
    const query = `(${field}=="${value}")`;
    validator.reset();

    return validator.GET({ params: { query } }).then((users) => {
      if (users.length > 0) {
        const error = { [field]: <FormattedMessage id={`ui-users.errors.${field}Unavailable`} /> };
        return reject(error);
      } else {
        return resolve();
      }
    });
  });
}

function asyncValidate(values, dispatch, props, blurredField) {
  const { uniquenessValidator, initialValues } = props;
  const { username, barcode } = values;
  const curValue = values[blurredField];
  const prevValue = initialValues[blurredField];

  // validate on blur
  if (blurredField && curValue && curValue !== prevValue) {
    return asyncValidateField(blurredField, curValue, uniquenessValidator);
  }

  const promises = [];

  // validate on submit
  if (username !== initialValues.username) {
    const usernameTrimmed = username.trim();
    promises.push(asyncValidateField('username', usernameTrimmed, uniquenessValidator));
  }

  if (barcode && barcode !== initialValues.barcode) {
    promises.push(asyncValidateField('barcode', barcode, uniquenessValidator));
  }

  return Promise.all(promises);
}

class UserForm extends React.Component {
  static propTypes = {
    change: PropTypes.func,
    stripes: PropTypes.shape({
      store: PropTypes.object,
    }).isRequired,
    formData: PropTypes.object,
    handleSubmit: PropTypes.func.isRequired,
    location: PropTypes.object,
    history: PropTypes.object,
    uniquenessValidator: PropTypes.shape({
      reset: PropTypes.func.isRequired,
      GET: PropTypes.func.isRequired,
    }).isRequired,
    pristine: PropTypes.bool,
    submitting: PropTypes.bool,
    invalid: PropTypes.bool,
    onSubmit: PropTypes.func.isRequired,
    initialValues: PropTypes.object.isRequired,
  };

  static defaultProps = {
    pristine: false,
    submitting: false,
    invalid: false,
  };

  constructor() {
    super();

    this.state = {
      sections: {
        editUserInfo: true,
        extendedInfo: true,
        contactInfo: true,
        proxy: false,
        permissions: false,
        servicePoints: false,
      },
    };

    this.handleExpandAll = this.handleExpandAll.bind(this);
    this.handleSectionToggle = this.handleSectionToggle.bind(this);
    this.toggleAllSections = this.toggleAllSections.bind(this);
    this.collapseAllSections = this.collapseAllSections.bind(this);
    this.expandAllSections = this.expandAllSections.bind(this);

    this.ignoreEnterKey = this.ignoreEnterKey.bind(this);
    this.closeButton = React.createRef();
    this.saveButton = React.createRef();

    this.keyboardCommands = [
      {
        name: 'save',
        handler: this.handleSaveKeyCommand
      },
      {
        name: 'ignoreEnter',
        handler: this.ignoreEnterKey,
        shortcut: 'enter'
      },
      {
        name: 'cancel',
        handler: this.handleCancel,
        shortcut: 'esc'
      },
      {
        name: 'expandAllSections',
        handler: this.expandAllSections,
      },
      {
        name: 'collapseAllSections',
        handler: this.collapseAllSections,
      }
    ];
  }

  handleCancel = () => {
    this.props.history.goBack();
  }

  getAddFirstMenu() {
    return (
      <PaneMenu>
        <FormattedMessage id="ui-users.crud.closeNewUserDialog">
          {ariaLabel => (
            <PaneHeaderIconButton
              id="clickable-closenewuserdialog"
              onClick={this.handleCancel}
              ref={this.closeButton}
              ariaLabel={ariaLabel}
              icon="times"
            />
          )}
        </FormattedMessage>
      </PaneMenu>
    );
  }

  handleExpandAll(sections) {
    this.setState({ sections });
  }

  // eslint-disable-next-line class-methods-use-this
  ignoreEnterKey = (e) => {
    if (e.target !== this.saveButton.current) {
      e.preventDefault();
    }
  };

  handleSectionToggle({ id }) {
    this.setState((curState) => {
      const newState = cloneDeep(curState);
      newState.sections[id] = !newState.sections[id];

      return newState;
    });
  }

  toggleAllSections(expand) {
    this.setState((curState) => {
      const newSections = expandAllFunction(curState.sections, expand);

      return {
        sections: newSections
      };
    });
  }

  expandAllSections(e) {
    e.preventDefault();
    this.toggleAllSections(true);
  }

  collapseAllSections(e) {
    e.preventDefault();
    this.toggleAllSections(false);
  }

  handleSaveKeyCommand = (e) => {
    e.preventDefault();
    this.executeSave();
  };

  executeSave() {
    const {
      handleSubmit,
      onSubmit,
    } = this.props;

    const submitter = handleSubmit(onSubmit);

    submitter();
  }

  getPaneFooter() {
    const {
      pristine,
      submitting,
      invalid,
      onCancel,
    } = this.props;

    const disabled = pristine || submitting || invalid;

    const startButton = (
      <Button
        data-test-user-form-cancel-button
        marginBottom0
        id="clickable-cancel"
        buttonStyle="default mega"
        onClick={onCancel}
      >
        <FormattedMessage id="ui-users.cancel" />
      </Button>
    );

    const endButton = (
      <Button
        data-test-user-form-submit-button
        marginBottom0
        id="clickable-save"
        buttonStyle="primary mega"
        type="submit"
        disabled={disabled}
        buttonRef={this.saveButton}
      >
        <FormattedMessage id="ui-users.saveAndClose" />
      </Button>
    );

    return (
      <PaneFooter
        renderStart={startButton}
        renderEnd={endButton}
        className={css.paneFooterClass}
        innerClassName={css.paneFooterContentClass}
      />
    );
  }

  render() {
    const {
      initialValues,
      handleSubmit,
      formData,
      stripes,
      change, // from redux-form...
    } = this.props;

    const { sections } = this.state;
    const firstMenu = this.getAddFirstMenu();
    const footer = this.getPaneFooter();
    const fullName = getFullName(initialValues);
    const paneTitle = initialValues.id
      ? fullName
      : <FormattedMessage id="ui-users.crud.createUser" />;

    return (
      <HasCommand commands={this.keyboardCommands}>
        <form
          data-test-form-page
          className={css.UserFormRoot}
          id="form-user"
          onSubmit={handleSubmit}
        >
          <Paneset>
            <Pane
              defaultWidth="100%"
              firstMenu={firstMenu}
              footer={footer}
              appIcon={<AppIcon app="users" appIconKey="users" />}
              paneTitle={

                  {paneTitle}

              }
            >

                <Headline
                  size="xx-large"
                  tag="h2"
                  data-test-header-title
                >
                  {fullName}
                </Headline>
                <Row end="xs">
                  <Col xs>
                    <ExpandAllButton
                      accordionStatus={sections}
                      onToggle={this.handleExpandAll}
                    />
                  </Col>
                </Row>
                <AccordionSet>
                  <EditUserInfo
                    accordionId="editUserInfo"
                    expanded={sections.editUserInfo}
                    onToggle={this.handleSectionToggle}
                    initialValues={initialValues}
                    patronGroups={formData.patronGroups}
                  />
                  <EditExtendedInfo
                    accordionId="extendedInfo"
                    expanded={sections.extendedInfo}
                    onToggle={this.handleSectionToggle}
                    userId={initialValues.id}
                    userFirstName={initialValues.personal.firstName}
                    userEmail={initialValues.personal.email}
                  />
                  <EditContactInfo
                    accordionId="contactInfo"
                    expanded={sections.contactInfo}
                    onToggle={this.handleSectionToggle}
                    addressTypes={formData.addressTypes}
                    preferredContactTypeId={initialValues.preferredContactTypeId}
                  />
                  {initialValues.id &&

                      <EditProxy
                        accordionId="proxy"
                        expanded={sections.proxy}
                        onToggle={this.handleSectionToggle}
                        sponsors={initialValues.sponsors}
                        proxies={initialValues.proxies}
                        fullName={fullName}
                        stripes={stripes}
                        change={change}
                      />
                      <PermissionsAccordion
                        filtersConfig={[
                          permissionTypeFilterConfig,
                          statusFilterConfig,
                        ]}
                        visibleColumns={[
                          'selected',
                          'permissionName',
                          'type',
                          'status',
                        ]}
                        accordionId="permissions"
                        expanded={sections.permissions}
                        onToggle={this.handleSectionToggle}
                        headlineContent={<FormattedMessage id="ui-users.permissions.userPermissions" />}
                        permToRead="perms.permissions.get"
                        permToDelete="perms.permissions.item.delete"
                        permToModify="perms.permissions.item.put"
                        formName="userForm"
                        permissionsField="permissions"
                      />
                      <EditServicePoints
                        accordionId="servicePoints"
                        expanded={sections.servicePoints}
                        onToggle={this.handleSectionToggle}
                        {...this.props}
                      />

                  }
                </AccordionSet>

            </Pane>
          </Paneset>
        </form>
      </HasCommand>
    );
  }
}

export default stripesForm({
  form: 'userForm',
  validate,
  asyncValidate,
  asyncBlurFields: ['username', 'barcode'],
  navigationCheck: true,
  enableReinitialize: true,
})(UserForm);
<|endoftext|>


<commit_before>/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/
"use strict";

const path = require("path");
const crypto = require("crypto");
const RequestShortener = require("./RequestShortener");
const ConcatSource = require("webpack-sources").ConcatSource;
const RawSource = require("webpack-sources").RawSource;
const ModuleFilenameHelpers = require("./ModuleFilenameHelpers");
const SourceMapDevToolModuleOptionsPlugin = require("./SourceMapDevToolModuleOptionsPlugin");

const basename = (name) => {
	if(name.indexOf("/") < 0) return name;
	return name.substr(name.lastIndexOf("/") + 1);
};

class SourceMapDevToolPlugin {
	constructor(options) {
		if(arguments.length > 1)
			throw new Error("SourceMapDevToolPlugin only takes one argument (pass an options object)");
		// TODO: remove in webpack 3
		if(typeof options === "string") {
			options = {
				sourceMapFilename: options
			};
		}
		if(!options) options = {};
		this.sourceMapFilename = options.filename;
		this.sourceMappingURLComment = options.append === false ? false : options.append || "\n//# sourceMappingURL=[url]";
		this.moduleFilenameTemplate = options.moduleFilenameTemplate || "webpack:///[resourcePath]";
		this.fallbackModuleFilenameTemplate = options.fallbackModuleFilenameTemplate || "webpack:///[resourcePath]?[hash]";
		this.options = options;
	}

	apply(compiler) {
		const sourceMapFilename = this.sourceMapFilename;
		const sourceMappingURLComment = this.sourceMappingURLComment;
		const moduleFilenameTemplate = this.moduleFilenameTemplate;
		const fallbackModuleFilenameTemplate = this.fallbackModuleFilenameTemplate;
		const requestShortener = new RequestShortener(compiler.context);
		const options = this.options;
		options.test = options.test || /\.(js|css)($|\?)/i;
		compiler.plugin("compilation", compilation => {
			new SourceMapDevToolModuleOptionsPlugin(options).apply(compilation);
			compilation.plugin("after-optimize-chunk-assets", function(chunks) {
				let allModules = [];
				let allModuleFilenames = [];
				const tasks = [];
				chunks.forEach(function(chunk) {
					chunk.files.filter(ModuleFilenameHelpers.matchObject.bind(undefined, options)).map(function(file) {
						const asset = compilation.assets[file];
						if(asset.__SourceMapDevToolFile === file && asset.__SourceMapDevToolData) {
							const data = asset.__SourceMapDevToolData;
							for(const cachedFile in data) {
								compilation.assets[cachedFile] = data[cachedFile];
								if(cachedFile !== file)
									chunk.files.push(cachedFile);
							}
							return;
						}
						let source, sourceMap;
						if(asset.sourceAndMap) {
							const sourceAndMap = asset.sourceAndMap(options);
							sourceMap = sourceAndMap.map;
							source = sourceAndMap.source;
						} else {
							sourceMap = asset.map(options);
							source = asset.source();
						}
						if(sourceMap) {
							return {
								chunk,
								file,
								asset,
								source,
								sourceMap
							};
						}
					}).filter(Boolean).map(task => {
						const modules = task.sourceMap.sources.map(source => {
							const module = compilation.findModule(source);
							return module || source;
						});
						const moduleFilenames = modules.map(module => ModuleFilenameHelpers.createFilename(module, moduleFilenameTemplate, requestShortener));
						task.modules = modules;
						task.moduleFilenames = moduleFilenames;
						return task;
					}).forEach(task => {
						allModules = allModules.concat(task.modules);
						allModuleFilenames = allModuleFilenames.concat(task.moduleFilenames);
						tasks.push(task);
					});
				});
				allModuleFilenames = ModuleFilenameHelpers.replaceDuplicates(allModuleFilenames, (filename, i) => ModuleFilenameHelpers.createFilename(allModules[i], fallbackModuleFilenameTemplate, requestShortener), (ai, bi) => {
					let a = allModules[ai];
					let b = allModules[bi];
					a = !a ? "" : typeof a === "string" ? a : a.identifier();
					b = !b ? "" : typeof b === "string" ? b : b.identifier();
					return a.length - b.length;
				});
				allModuleFilenames = ModuleFilenameHelpers.replaceDuplicates(allModuleFilenames, (filename, i, n) => {
					for(let j = 0; j < n; j++)
						filename += "*";
					return filename;
				});
				tasks.forEach(task => {
					task.moduleFilenames = allModuleFilenames.slice(0, task.moduleFilenames.length);
					allModuleFilenames = allModuleFilenames.slice(task.moduleFilenames.length);
				});
				tasks.forEach(function(task) {
					const chunk = task.chunk;
					const file = task.file;
					const asset = task.asset;
					const sourceMap = task.sourceMap;
					const source = task.source;
					const moduleFilenames = task.moduleFilenames;
					const modules = task.modules;
					sourceMap.sources = moduleFilenames;
					if(sourceMap.sourcesContent && !options.noSources) {
						sourceMap.sourcesContent = sourceMap.sourcesContent.map((content, i) => `${content}\n\n\n${ModuleFilenameHelpers.createFooter(modules[i], requestShortener)}`);
					} else {
						sourceMap.sourcesContent = undefined;
					}
					sourceMap.sourceRoot = options.sourceRoot || "";
					sourceMap.file = file;
					asset.__SourceMapDevToolFile = file;
					asset.__SourceMapDevToolData = {};
					let currentSourceMappingURLComment = sourceMappingURLComment;
					if(currentSourceMappingURLComment !== false && /\.css($|\?)/i.test(file)) {
						currentSourceMappingURLComment = currentSourceMappingURLComment.replace(/^\n\/\/(.*)$/, "\n/*$1*/");
					}
					const sourceMapString = JSON.stringify(sourceMap);
					if(sourceMapFilename) {
						let filename = file;
						let query = "";
						const idx = filename.indexOf("?");
						if(idx >= 0) {
							query = filename.substr(idx);
							filename = filename.substr(0, idx);
						}
						let sourceMapFile = compilation.getPath(sourceMapFilename, {
							chunk,
							filename,
							query,
							basename: basename(filename)
						});
						if(sourceMapFile.indexOf("[contenthash]") !== -1) {
							sourceMapFile = sourceMapFile.replace(/\[contenthash\]/g, crypto.createHash("md5").update(sourceMapString).digest("hex"));
						}
						const sourceMapUrl = path.relative(path.dirname(file), sourceMapFile).replace(/\\/g, "/");
						if(currentSourceMappingURLComment !== false) {
							asset.__SourceMapDevToolData[file] = compilation.assets[file] = new ConcatSource(new RawSource(source), currentSourceMappingURLComment.replace(/\[url\]/g, sourceMapUrl));
						}
						asset.__SourceMapDevToolData[sourceMapFile] = compilation.assets[sourceMapFile] = new RawSource(sourceMapString);
						chunk.files.push(sourceMapFile);
					} else {
						asset.__SourceMapDevToolData[file] = compilation.assets[file] = new ConcatSource(new RawSource(source), currentSourceMappingURLComment
							.replace(/\[map\]/g, () => sourceMapString)
							.replace(/\[url\]/g, () => `data:application/json;charset=utf-8;base64,${new Buffer(sourceMapString, "utf-8").toString("base64")}`) // eslint-disable-line
						);
					}
				});
			});
		});
	}
}

module.exports = SourceMapDevToolPlugin;
<commit_msg>generate source map names globally<commit_after>/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/
"use strict";

const path = require("path");
const crypto = require("crypto");
const RequestShortener = require("./RequestShortener");
const ConcatSource = require("webpack-sources").ConcatSource;
const RawSource = require("webpack-sources").RawSource;
const ModuleFilenameHelpers = require("./ModuleFilenameHelpers");
const SourceMapDevToolModuleOptionsPlugin = require("./SourceMapDevToolModuleOptionsPlugin");

const basename = (name) => {
	if(name.indexOf("/") < 0) return name;
	return name.substr(name.lastIndexOf("/") + 1);
};

class SourceMapDevToolPlugin {
	constructor(options) {
		if(arguments.length > 1)
			throw new Error("SourceMapDevToolPlugin only takes one argument (pass an options object)");
		// TODO: remove in webpack 3
		if(typeof options === "string") {
			options = {
				sourceMapFilename: options
			};
		}
		if(!options) options = {};
		this.sourceMapFilename = options.filename;
		this.sourceMappingURLComment = options.append === false ? false : options.append || "\n//# sourceMappingURL=[url]";
		this.moduleFilenameTemplate = options.moduleFilenameTemplate || "webpack:///[resourcePath]";
		this.fallbackModuleFilenameTemplate = options.fallbackModuleFilenameTemplate || "webpack:///[resourcePath]?[hash]";
		this.options = options;
	}

	apply(compiler) {
		const sourceMapFilename = this.sourceMapFilename;
		const sourceMappingURLComment = this.sourceMappingURLComment;
		const moduleFilenameTemplate = this.moduleFilenameTemplate;
		const fallbackModuleFilenameTemplate = this.fallbackModuleFilenameTemplate;
		const requestShortener = new RequestShortener(compiler.context);
		const options = this.options;
		options.test = options.test || /\.(js|css)($|\?)/i;
		compiler.plugin("compilation", compilation => {
			new SourceMapDevToolModuleOptionsPlugin(options).apply(compilation);
			compilation.plugin("after-optimize-chunk-assets", function(chunks) {
				const moduleToSourceNameMapping = new Map();
				const tasks = [];
				chunks.forEach(function(chunk) {
					chunk.files.filter(ModuleFilenameHelpers.matchObject.bind(undefined, options)).map(function(file) {
						const asset = compilation.assets[file];
						if(asset.__SourceMapDevToolFile === file && asset.__SourceMapDevToolData) {
							const data = asset.__SourceMapDevToolData;
							for(const cachedFile in data) {
								compilation.assets[cachedFile] = data[cachedFile];
								if(cachedFile !== file)
									chunk.files.push(cachedFile);
							}
							return;
						}
						let source, sourceMap;
						if(asset.sourceAndMap) {
							const sourceAndMap = asset.sourceAndMap(options);
							sourceMap = sourceAndMap.map;
							source = sourceAndMap.source;
						} else {
							sourceMap = asset.map(options);
							source = asset.source();
						}
						if(sourceMap) {
							return {
								chunk,
								file,
								asset,
								source,
								sourceMap
							};
						}
					}).filter(Boolean).map(task => {
						const modules = task.sourceMap.sources.map(source => {
							const module = compilation.findModule(source);
							return module || source;
						});
						for(const module of modules) {
							if(!moduleToSourceNameMapping.get(module)) {
								moduleToSourceNameMapping.set(module, ModuleFilenameHelpers.createFilename(module, moduleFilenameTemplate, requestShortener));
							}
						}
						task.modules = modules;
						return task;
					}).forEach(task => {
						tasks.push(task);
					});
				});
				const usedNamesSet = new Set(moduleToSourceNameMapping.values());
				const conflictDetectionSet = new Set();

				// all modules in defined order (longest identifier first)
				const allModules = Array.from(moduleToSourceNameMapping.keys()).sort((a, b) => {
					const ai = typeof a === "string" ? a : a.identifier();
					const bi = typeof b === "string" ? b : b.identifier();
					return ai.length - bi.length;
				});

				// find modules with conflicting source names
				for(const module of allModules) {
					let sourceName = moduleToSourceNameMapping.get(module);
					let hasName = conflictDetectionSet.has(sourceName);
					if(!hasName) {
						conflictDetectionSet.add(sourceName);
						continue;
					}

					// try the fallback name first
					sourceName = ModuleFilenameHelpers.createFilename(module, fallbackModuleFilenameTemplate, requestShortener);
					hasName = usedNamesSet.has(sourceName);
					if(!hasName) {
						moduleToSourceNameMapping.set(module, sourceName);
						usedNamesSet.add(sourceName);
						continue;
					}

					// elsewise just append stars until we have a valid name
					do {
						sourceName += "*";
						hasName = usedNamesSet.has(sourceName);
					} while(hasName);
					moduleToSourceNameMapping.set(module, sourceName);
					usedNamesSet.add(sourceName);
				}
				tasks.forEach(function(task) {
					const chunk = task.chunk;
					const file = task.file;
					const asset = task.asset;
					const sourceMap = task.sourceMap;
					const source = task.source;
					const modules = task.modules;
					const moduleFilenames = modules.map(m => moduleToSourceNameMapping.get(m));
					sourceMap.sources = moduleFilenames;
					if(sourceMap.sourcesContent && !options.noSources) {
						sourceMap.sourcesContent = sourceMap.sourcesContent.map((content, i) => `${content}\n\n\n${ModuleFilenameHelpers.createFooter(modules[i], requestShortener)}`);
					} else {
						sourceMap.sourcesContent = undefined;
					}
					sourceMap.sourceRoot = options.sourceRoot || "";
					sourceMap.file = file;
					asset.__SourceMapDevToolFile = file;
					asset.__SourceMapDevToolData = {};
					let currentSourceMappingURLComment = sourceMappingURLComment;
					if(currentSourceMappingURLComment !== false && /\.css($|\?)/i.test(file)) {
						currentSourceMappingURLComment = currentSourceMappingURLComment.replace(/^\n\/\/(.*)$/, "\n/*$1*/");
					}
					const sourceMapString = JSON.stringify(sourceMap);
					if(sourceMapFilename) {
						let filename = file;
						let query = "";
						const idx = filename.indexOf("?");
						if(idx >= 0) {
							query = filename.substr(idx);
							filename = filename.substr(0, idx);
						}
						let sourceMapFile = compilation.getPath(sourceMapFilename, {
							chunk,
							filename,
							query,
							basename: basename(filename)
						});
						if(sourceMapFile.indexOf("[contenthash]") !== -1) {
							sourceMapFile = sourceMapFile.replace(/\[contenthash\]/g, crypto.createHash("md5").update(sourceMapString).digest("hex"));
						}
						const sourceMapUrl = path.relative(path.dirname(file), sourceMapFile).replace(/\\/g, "/");
						if(currentSourceMappingURLComment !== false) {
							asset.__SourceMapDevToolData[file] = compilation.assets[file] = new ConcatSource(new RawSource(source), currentSourceMappingURLComment.replace(/\[url\]/g, sourceMapUrl));
						}
						asset.__SourceMapDevToolData[sourceMapFile] = compilation.assets[sourceMapFile] = new RawSource(sourceMapString);
						chunk.files.push(sourceMapFile);
					} else {
						asset.__SourceMapDevToolData[file] = compilation.assets[file] = new ConcatSource(new RawSource(source), currentSourceMappingURLComment
							.replace(/\[map\]/g, () => sourceMapString)
							.replace(/\[url\]/g, () => `data:application/json;charset=utf-8;base64,${new Buffer(sourceMapString, "utf-8").toString("base64")}`) // eslint-disable-line
						);
					}
				});
			});
		});
	}
}

module.exports = SourceMapDevToolPlugin;
<|endoftext|>


<commit_before>define([
    'underscore', 'backbone', 'handlebars', '../mixin', '../mixins/observable', '../mixins/deferrable', '../mixins/startable'
], function (_, Backbone, Handlebars, Mixin, Observable, Deferrable, Startable) {
  "use strict";

    var ViewExtension = {
        // if `template` is a string, it will be processed through Handlebars.
        // otherwise it leaves it untouched.
        precompile: function (template) {
            if (typeof template === "string") {
                return Handlebars.compile(template);
            }
            return template;
        },
        // allow an extra helper parameter to view render
        // this is the handlebars extra data. Learn more at
        // http://handlebarsjs.com/execution.html#Options
        //
        // This is desined to be used with the engine `render()`
        // method.
        renderHtml: function (data, extra) {
            return this.template(data, extra);
        }
    };

    var Engine = Mixin.extend({
        // extends Backbone.View prototype
        _firstStart: function () {
            _.defaults(Backbone.View.prototype, ViewExtension);
            Startable._firstStart.apply(this, arguments);
        },

        // removes Backbone.View prototype engine functions.
        _doStop: function () {
            var engine = this.engine;
            _.each(ViewExtension, function unmix(method, name) {
                if (Backbone.View.prototype[name] === method) {
                    Backbone.View.prototype[name] = null;
                }
            });
        },

        // render a view and format all required extra data for template
        // engine in use.
        //
        // View has been extended to accept formatted Handlebars extra data.
        // learn more at http://handlebarsjs.com/execution.html#Options
        render: function (view, helpers, data) {
            var extra = {
                // extra helpers to use for this context
                helpers: helpers,
                // add global available data
                data: data
            };

            return view.render(extra);
        }
    });

    Engine.mix([Observable, Deferrable, Startable]);

    return Engine;
});
<commit_msg>Remove unused variable.<commit_after>define([
    'underscore', 'backbone', 'handlebars', '../mixin', '../mixins/observable', '../mixins/deferrable', '../mixins/startable'
], function (_, Backbone, Handlebars, Mixin, Observable, Deferrable, Startable) {
  "use strict";

    var ViewExtension = {
        // if `template` is a string, it will be processed through Handlebars.
        // otherwise it leaves it untouched.
        precompile: function (template) {
            if (typeof template === "string") {
                return Handlebars.compile(template);
            }
            return template;
        },
        // allow an extra helper parameter to view render
        // this is the handlebars extra data. Learn more at
        // http://handlebarsjs.com/execution.html#Options
        //
        // This is desined to be used with the engine `render()`
        // method.
        renderHtml: function (data, extra) {
            return this.template(data, extra);
        }
    };

    var Engine = Mixin.extend({
        // extends Backbone.View prototype
        _firstStart: function () {
            _.defaults(Backbone.View.prototype, ViewExtension);
            Startable._firstStart.apply(this, arguments);
        },

        // removes Backbone.View prototype engine functions.
        _doStop: function () {
            _.each(ViewExtension, function unmix(method, name) {
                if (Backbone.View.prototype[name] === method) {
                    Backbone.View.prototype[name] = null;
                }
            });
        },

        // render a view and format all required extra data for template
        // engine in use.
        //
        // View has been extended to accept formatted Handlebars extra data.
        // learn more at http://handlebarsjs.com/execution.html#Options
        render: function (view, helpers, data) {
            var extra = {
                // extra helpers to use for this context
                helpers: helpers,
                // add global available data
                data: data
            };

            return view.render(extra);
        }
    });

    Engine.mix([Observable, Deferrable, Startable]);

    return Engine;
});
<|endoftext|>


<commit_before>/**
 * StarField.js
 *
 * FooFighter.js — StarField entity
 *
 * This class represents the display object that renders the moving
 * starfield background in the game world.
 */

;(function( FooFighter ){

'use strict';

function StarField ( gameState, group ) {
    var d = new Date();

    this.gameState = gameState;
    this.game = gameState.game;
    this.group = group;
    // Background
    this.gameState.game.load.image(
        'background',
        'assets/img/background.png'
    );
    this.gameState.game.load.image(
        'speedLine',
        'assets/img/speedLine.png'
    );
    // Star tile
    this.gameState.game.load.image(
        'starBackground',
        'assets/img/starBackground.png'
    );
    this.config = {
        field: {
            lineSpeed: 800,
            starSpeed: 500,
            lineOpacity: 0.3,
            starOpacity: 0.4,
            lineThrottle: 0,
            starThrottle: 0,
            numLines: 6,
            numStars: 100,
            velocityVariance: 0.5
        }
    };
    this.shouldAddStar = true;
    this.shouldAddSpeedLine = true;
    this.lastCreated = {
        line: d.getTime(),
        star: d.getTime()
    };
    this.bindEvents();
}

var proto = StarField.prototype;

proto.bindEvents = function(){
    var vent = this.gameState.vent;
    vent.on('create', this.create.bind(this));
    vent.on('update', this.moveField.bind(this));
    return this;
};

proto.create = function(){
    var config = this.config,
        stars,
        tile;

    // In another group, use our star tile png to create
    // the starfield background
    stars = this.group;
    for (var x = -8; x < 8; x++) {
        for (var y = -6; y < 6; y++) {
            tile = stars.create(x*254, y*256, 'starBackground');
            tile.alpha = 0.285;
        }
    }

    for (var i = config.field.numLines - 1; i >= 0; i--) {
        this.addSpeedLine.call(this);
    }
    for (i = config.field.numStars - 1; i >= 0; i--) {
        this.addStar.call(this);
    }

    return this;
};

proto.moveField = function(){
    var stars = this.group;

    // This is a bit of a hack, we're just moving the background
    // group along the Y axis and starting over when we go too far
    stars.y = stars.y > 768 ? 0 : stars.y;
    stars.y += 4.0;

    return this;
};

proto.addSpeedLine = function(){
    var field = this.config.field,
        randInRange = FooFighter.Util.randInRange,
        game = this.game,
        group = this.gameState.groups.backGroup,
        line;

    line = group.create(
        game.world.width * Math.random(),
        game.world.height * Math.random(),
        'speedLine'
    );
    line.alpha = field.lineOpacity;
    line.body.velocity.y = randInRange(
        field.lineSpeed * 0.5, field.lineSpeed
    );
    line.events.onOutOfBounds.add(function(){
        this.y = -100;
        this.x = game.world.width * Math.random();
        this.body.velocity.y = randInRange(
            field.lineSpeed * 0.5, field.lineSpeed
        );
    }, line);

    return line;
};

proto.addStar = function(){
    var field = this.config.field,
        group = this.gameState.groups.backGroup,
        randInRange = FooFighter.Util.randInRange,
        starType = 'Background/starSmall.png',
        variance = this.config.field.velocityVariance,
        game = this.game,
        star;

    if ( Math.random() >= 0.9 ) {
        starType = 'Background/starBig.png';
    }
    star = group.create(
        game.world.width * Math.random(),
        game.world.height * Math.random(),
        'sprites',
        starType
    );
    star.alpha = field.starOpacity;
    star.body.velocity.y = randInRange(
        field.starSpeed * variance, field.starSpeed
    );
    star.events.onOutOfBounds.add(function(){
        this.y = 0;
        this.x = game.world.width * Math.random();
        this.body.velocity.y = randInRange(
            field.starSpeed * variance, field.starSpeed
        );
    }, star);

    return star;
};

FooFighter.StarField = StarField;

})(FooFighter);
<commit_msg>starfield has been giving me motion sickeness lately, trying to dumb down the contrast/speed<commit_after>/**
 * StarField.js
 *
 * FooFighter.js — StarField entity
 *
 * This class represents the display object that renders the moving
 * starfield background in the game world.
 */

;(function( FooFighter ){

'use strict';

function StarField ( gameState, group ) {
    var d = new Date();

    this.gameState = gameState;
    this.game = gameState.game;
    this.group = group;
    // Background
    this.gameState.game.load.image(
        'background',
        'assets/img/background.png'
    );
    this.gameState.game.load.image(
        'speedLine',
        'assets/img/speedLine.png'
    );
    // Star tile
    this.gameState.game.load.image(
        'starBackground',
        'assets/img/starBackground.png'
    );
    this.config = {
        field: {
            lineSpeed: 800,
            starSpeed: 200,
            lineOpacity: 0.2,
            starOpacity: 0.15,
            lineThrottle: 0,
            starThrottle: 0,
            numLines: 6,
            numStars: 150,
            velocityVariance: 0.5
        }
    };
    this.shouldAddStar = true;
    this.shouldAddSpeedLine = true;
    this.lastCreated = {
        line: d.getTime(),
        star: d.getTime()
    };
    this.bindEvents();
}

var proto = StarField.prototype;

proto.bindEvents = function(){
    var vent = this.gameState.vent;
    vent.on('create', this.create.bind(this));
    vent.on('update', this.moveField.bind(this));
    return this;
};

proto.create = function(){
    var config = this.config,
        stars,
        tile;

    // In another group, use our star tile png to create
    // the starfield background
    stars = this.group;
    for (var x = -8; x < 8; x++) {
        for (var y = -6; y < 6; y++) {
            tile = stars.create(x*254, y*256, 'starBackground');
            tile.alpha = 0.19;
        }
    }

    for (var i = config.field.numLines - 1; i >= 0; i--) {
        this.addSpeedLine.call(this);
    }
    for (i = config.field.numStars - 1; i >= 0; i--) {
        this.addStar.call(this);
    }

    return this;
};

proto.moveField = function(){
    var stars = this.group;

    // This is a bit of a hack, we're just moving the background
    // group along the Y axis and starting over when we go too far
    stars.y = stars.y > 768 ? 0 : stars.y;
    // stars.y += 4.0;

    return this;
};

proto.addSpeedLine = function(){
    var field = this.config.field,
        randInRange = FooFighter.Util.randInRange,
        game = this.game,
        group = this.gameState.groups.backGroup,
        line;

    line = group.create(
        game.world.width * Math.random(),
        game.world.height * Math.random(),
        'speedLine'
    );
    line.alpha = field.lineOpacity;
    line.body.velocity.y = randInRange(
        field.lineSpeed * 0.5, field.lineSpeed
    );
    line.events.onOutOfBounds.add(function(){
        this.y = -100;
        this.x = game.world.width * Math.random();
        this.body.velocity.y = randInRange(
            field.lineSpeed * 0.5, field.lineSpeed
        );
    }, line);

    return line;
};

proto.addStar = function(){
    var field = this.config.field,
        group = this.gameState.groups.backGroup,
        randInRange = FooFighter.Util.randInRange,
        starType = 'Background/starSmall.png',
        variance = this.config.field.velocityVariance,
        game = this.game,
        star;

    if ( Math.random() >= 0.9 ) {
        starType = 'Background/starBig.png';
    }
    star = group.create(
        game.world.width * Math.random(),
        game.world.height * Math.random(),
        'sprites',
        starType
    );
    star.alpha = field.starOpacity;
    star.body.velocity.y = randInRange(
        field.starSpeed * variance, field.starSpeed
    );
    star.events.onOutOfBounds.add(function(){
        this.y = 0;
        this.x = game.world.width * Math.random();
        this.body.velocity.y = randInRange(
            field.starSpeed * variance, field.starSpeed
        );
    }, star);

    return star;
};

FooFighter.StarField = StarField;

})(FooFighter);
<|endoftext|>