<commit_before>import Team from '../models/team'
import { getPasswordHash, checkPassword } from '../utils/security'
import fs from 'fs'
import path from 'path'
import gm from 'gm'
import queue from '../utils/queue'
import token from '../utils/token'
import logger from '../utils/logger'
import { InternalError, TeamNotFoundError, TeamCredentialsTakenError, InvalidTeamCredentialsError, EmailConfirmedError, EmailTakenError, InvalidTeamPasswordError, InvalidResetPasswordURLError, InvalidVerificationURLError } from '../utils/errors'
import publish from '../utils/publisher'
import BaseEvent from '../utils/events'

import teamSerializer from '../serializers/team'

class UpdateTeamProfileEvent extends BaseEvent {
  constructor(team) {
    super('updateTeamProfile')
    let publicData = teamSerializer(team)
    this.data.guests = publicData
    this.data.teams = publicData

    this.data.supervisors = teamSerializer(team, { exposeEmail: true })
  }
}

class QualifyTeamEvent extends BaseEvent {
  constructor(team) {
    super('qualifyTeam')
    let publicData = teamSerializer(team)
    this.data.guests = publicData
    this.data.teams = publicData

    this.data.supervisors = teamSerializer(team, { exposeEmail: true })
  }
}

class CreateTeamEvent extends BaseEvent {
  constructor(team) {
    super('createTeam')
    this.data.supervisors = teamSerializer(team, { exposeEmail: true })
  }
}

class ChangeTeamEmailEvent extends BaseEvent {
  constructor(team) {
    super('changeTeamEmail')
    this.data.supervisors = teamSerializer(team, { exposeEmail: true })
  }
}

class TeamController {
  static restore(email, callback) {
    Team
      .query()
      .where('email', email.toLowerCase())
      .first()
      .then((team) => {
        Team
          .query()
          .patchAndFetchById(team.id, {
            resetPasswordToken: token.generate()
          })
          .then((updatedTeam) => {
            query('sendEmailQueue').add({
              message: 'restore',
              name: updatedTeam.name,
              email: updatedTeam.email,
              token: updatedTeam.resetPasswordToken
            })
            callback(null)
          })
          .catch((err) => {
            logger.error(err)
            callback(new InternalError())
          })
      })
      .catch((err) => {
        logger.error(err)
        callback(new InternalError())
      })
  }

  static create(options, callback) {
    Team
      .query()
      .where('name', options.team)
      .orWhere('email', options.email.toLowerCase())
      .first()
      .then((team) => {
        if (team) {
          callback(new TeamCredentialsTakenError())
        } else {
          getPasswordHash(options.password, (err, hash) => {
            if (err) {
              logger.error(err)
              callback(new InternalError())
            } else {
              Team
                .query()
                .insert({
                  name: options.team,
                  email: options.email,
                  createdAt: new Date(),
                  emailConfirmed: false,
                  emailConfirmationToken: token.generate(),
                  passwordHash: hash,
                  country: options.country,
                  locality: options.locality,
                  institution: options.institution,
                  disqualified: false,
                  resetPasswordToken: null
                })
                .then((team) => {
                  if (options.logoFilename) {
                    queue('createLogoQueue').add({
                      id: team.id,
                      filename: options.logoFilename
                    })
                  }

                  queue('sendEmailQueue').add({
                    message: 'welcome',
                    name: team.name,
                    email: team.email,
                    token: team.emailConfirmationToken
                  })

                  callback(null)
                  publish('realtime', new CreateTeamEvent(team))
                })
                .catch((err) => {
                  logger.error(err)
                  callback(new InternalError())
                })
            }
          })
        }
      })
      .catch((err) => {
        logger.error(err)
        callback(new InternalError())
      })
  }

  static signin(name, password, callback) {
    Team
      .query()
      .where('name', name)
      .first()
      .then((team) => {
        if (team) {
          checkPassword(password, team.passwordHash, (err, res) => {
            if (err) {
              logger.error(err)
              callback(new InvalidTeamCredentialsError(), null)
            } else {
              if (res) {
                callback(null, team)
              } else {
                callback(new InvalidTeamCredentialsError(), null)
              }
            }
          })
        } else {
          callback(new InvalidTeamCredentialsError(), null)
        }
      })
      .catch((err) => {
        logger.error(err)
        callback(new InternalError(), null)
      })
  }

  static resendConfirmationEmail(id, callback) {
    TeamController.get(id, (err, team) => {
      if (err) {
        callback(err)
      } else {
        if (team.emailConfirmed) {
          callback(new EmailConfirmedError())
        } else {
          Team
            .query()
            .patchAndFetchById(team.id, {
              emailConfirmationToken: token.generate()
            })
            .then((updatedTeam) => {
              queue('sendEmailQueue').add({
                message: 'welcome',
                name: updatedTeam.name,
                email: updatedTeam.email,
                token: updatedTeam.emailConfirmationToken
              })

              callback(null)
            })
            .catch((err) => {
              logger.error(err)
              callback(new InternalError())
            })
        }
      }
    })
  }

  static changeEmail(id, email, callback) {
    TeamController.get(id, (err, team) => {
      if (err) {
        callback(err)
      } else {
        if (team.emailConfirmed) {
          callback(new EmailConfirmedError())
        } else {
          Team
            .query()
            .where('email', email.toLowerCase())
            .first()
            .then((duplicateTeam) => {
              if (duplicateTeam) {
                callback(new EmailTakenError())
              } else {
                Team
                  .query()
                  .patchAndFetchById(team.id, {
                    email: email,
                    emailConfirmationToken: token.generate()
                  })
                  .then((updatedTeam) => {
                    queue('sendEmailQueue').add({
                      message: 'welcome',
                      name: updatedTeam.name,
                      email: updatedTeam.email,
                      token: updatedTeam.emailConfirmationToken
                    })

                    callback(null)
                    publish('realtime', new ChangeTeamEmailEvent(updatedTeam))
                  })
                  .catch((err) => {
                    logger.error(err)
                    callback(err)
                  })
              }
            })
            .catch((err) => {
              logger.error(err)
              callback(new InternalError())
            })
        }
      }
    })
  }

  static editProfile(id, country, locality, institution, callback) {
    TeamController.get(id, (err, team) => {
      if (err) {
        callback(err)
      } else {
        Team
          .query()
          .patchAndFetchById(team.id, {
            country: country,
            locality: locality,
            institution: institution
          })
          .then((updatedTeam) => {
            callback(null)
            publish('realtime', new UpdateTeamProfileEvent(updatedTeam))
          })
          .catch((err) => {
            logger.error(err)
            callback(new InternalError())
          })
      }
    })
  }

  static changeLogo(id, logoFilename, callback) {
    TeamController.get(id, (err, team) => {
      if (err) {
        callback(err)
      } else {
        queue('createLogoQueue').add({
          id: team.id,
          filename: logoFilename
        })
        callback(null)
      }
    })
  }

  static changePassword(id, currentPassword, newPassword, callback) {
    TeamController.get(id, (err, team) => {
      if (err) {
        callback(err)
      } else {
        checkPassword(currentPassword, team.passwordHash, (err, res) => {
          if (err) {
            logger.error(err)
            callback(new InternalError())
          } else {
            if (res) {
              getPasswordHash(newPassword, (err, hash) => {
                if (err) {
                  logger.error(err)
                  callback(new InternalError())
                } else {
                  Team
                    .query()
                    .patchAndFetchById(team.id, {
                      passwordHash: hash
                    })
                    .then((updatedTeam) => {
                      callback(null)
                    })
                    .catch((err) => {
                      logger.error(err)
                      callback(new InternalError())
                    })
                }
              })
            } else {
              callback(new InvalidTeamPasswordError())
            }
          }
        })
      }
    })
  }

  static list(callback) {
    Team
      .query()
      .then((teams) => {
        callback(null, teams)
      })
      .catch((err) => {
        logger.error(err)
        callback(new InternalError(), null)
      })
  }

  static listQualified(callback) {
    Team
      .query()
      .where('emailConfirmed', true)
      .then((teams) => {
        callback(null, teams)
      })
      .catch((err) => {
        logger.error(err)
        callback(new InternalError(), null)
      })
  }

  static resetPassword(encodedEmail, encodedToken, newPassword, callback) {
    let email = null
    let code = null
    try {
      email = token.decodeString(encodedEmail)
      code = token.decode(encodedToken)
      if (!code) {
        throw 'Reset password code is null'
      }
    } catch (e) {
      logger.error(e)
      callback(new InvalidResetPasswordURLError())
      return
    }

    Team
      .query()
      .where('email', email)
      .andWhere('resetPasswordToken', code)
      .first()
      .then((team) => {
        if (team) {
          getPasswordHash(newPassword, (err, hash) => {
            if (err) {
              logger.error(err)
              callback(new InternalError())
            } else {
              Team
                .query()
                .patchAndFetchById(team.id, {
                  passwordHash: hash,
                  resetPasswordToken: null
                })
                .then((updatedTeam) => {
                  callback(null)
                })
                .catch((err) => {
                  logger.error(err)
                  callback(new InternalError())
                })
            }
          })
        } else {
          callback(new InvalidResetPasswordURLError())
        }
      })
      .catch((err) => {
        logger.error(err)
        callback(new InternalError())
      })
  }

  static verifyEmail(encodedEmail, encodedToken, callback) {
    let email = null
    let code = null
    try {
      email = token.decodeString(encodedEmail)
      code = token.decode(encodedToken)
    } catch (e) {
      logger.error(e)
      callback(new InvalidVerificationURLError())
      return
    }

    Team
      .query()
      .where('email', email)
      .andWhere('emailConfirmed', false)
      .andWhere('emailConfirmationToken', code)
      .first()
      .then((team) => {
        if (team) {
          Team
            .query()
            .patchAndFetchById(team.id, {
              emailConfirmed: true,
              emailConfirmationToken: null
            })
            .then((updatedTeam) => {
              callback(null)
              publish('realtime', new QualifyTeamEvent(updatedTeam))
            })
            .catch((err) => {
              logger.error(err)
              callback(new InternalError())
            })
        } else {
          callback(new InvalidVerificationURLError())
        }
      })
      .catch((err) => {
        logger.error(err)
        callback(new InternalError())
      })
  }

  static get(id, callback) {
    Team
      .query()
      .where('id', id)
      .first()
      .then((team) => {
        if (team) {
          callback(null, team)
        } else {
          callback(new TeamNotFoundError(), null)
        }
      })
      .catch((err) => {
        logger.error(err)
        callback(new InternalError(), null)
      })
  }
}

export default TeamController
<commit_msg>Fix typo<commit_after>import Team from '../models/team'
import { getPasswordHash, checkPassword } from '../utils/security'
import fs from 'fs'
import path from 'path'
import gm from 'gm'
import queue from '../utils/queue'
import token from '../utils/token'
import logger from '../utils/logger'
import { InternalError, TeamNotFoundError, TeamCredentialsTakenError, InvalidTeamCredentialsError, EmailConfirmedError, EmailTakenError, InvalidTeamPasswordError, InvalidResetPasswordURLError, InvalidVerificationURLError } from '../utils/errors'
import publish from '../utils/publisher'
import BaseEvent from '../utils/events'

import teamSerializer from '../serializers/team'

class UpdateTeamProfileEvent extends BaseEvent {
  constructor(team) {
    super('updateTeamProfile')
    let publicData = teamSerializer(team)
    this.data.guests = publicData
    this.data.teams = publicData

    this.data.supervisors = teamSerializer(team, { exposeEmail: true })
  }
}

class QualifyTeamEvent extends BaseEvent {
  constructor(team) {
    super('qualifyTeam')
    let publicData = teamSerializer(team)
    this.data.guests = publicData
    this.data.teams = publicData

    this.data.supervisors = teamSerializer(team, { exposeEmail: true })
  }
}

class CreateTeamEvent extends BaseEvent {
  constructor(team) {
    super('createTeam')
    this.data.supervisors = teamSerializer(team, { exposeEmail: true })
  }
}

class ChangeTeamEmailEvent extends BaseEvent {
  constructor(team) {
    super('changeTeamEmail')
    this.data.supervisors = teamSerializer(team, { exposeEmail: true })
  }
}

class TeamController {
  static restore(email, callback) {
    Team
      .query()
      .where('email', email.toLowerCase())
      .first()
      .then((team) => {
        Team
          .query()
          .patchAndFetchById(team.id, {
            resetPasswordToken: token.generate()
          })
          .then((updatedTeam) => {
            queue('sendEmailQueue').add({
              message: 'restore',
              name: updatedTeam.name,
              email: updatedTeam.email,
              token: updatedTeam.resetPasswordToken
            })
            callback(null)
          })
          .catch((err) => {
            logger.error(err)
            callback(new InternalError())
          })
      })
      .catch((err) => {
        logger.error(err)
        callback(new InternalError())
      })
  }

  static create(options, callback) {
    Team
      .query()
      .where('name', options.team)
      .orWhere('email', options.email.toLowerCase())
      .first()
      .then((team) => {
        if (team) {
          callback(new TeamCredentialsTakenError())
        } else {
          getPasswordHash(options.password, (err, hash) => {
            if (err) {
              logger.error(err)
              callback(new InternalError())
            } else {
              Team
                .query()
                .insert({
                  name: options.team,
                  email: options.email,
                  createdAt: new Date(),
                  emailConfirmed: false,
                  emailConfirmationToken: token.generate(),
                  passwordHash: hash,
                  country: options.country,
                  locality: options.locality,
                  institution: options.institution,
                  disqualified: false,
                  resetPasswordToken: null
                })
                .then((team) => {
                  if (options.logoFilename) {
                    queue('createLogoQueue').add({
                      id: team.id,
                      filename: options.logoFilename
                    })
                  }

                  queue('sendEmailQueue').add({
                    message: 'welcome',
                    name: team.name,
                    email: team.email,
                    token: team.emailConfirmationToken
                  })

                  callback(null)
                  publish('realtime', new CreateTeamEvent(team))
                })
                .catch((err) => {
                  logger.error(err)
                  callback(new InternalError())
                })
            }
          })
        }
      })
      .catch((err) => {
        logger.error(err)
        callback(new InternalError())
      })
  }

  static signin(name, password, callback) {
    Team
      .query()
      .where('name', name)
      .first()
      .then((team) => {
        if (team) {
          checkPassword(password, team.passwordHash, (err, res) => {
            if (err) {
              logger.error(err)
              callback(new InvalidTeamCredentialsError(), null)
            } else {
              if (res) {
                callback(null, team)
              } else {
                callback(new InvalidTeamCredentialsError(), null)
              }
            }
          })
        } else {
          callback(new InvalidTeamCredentialsError(), null)
        }
      })
      .catch((err) => {
        logger.error(err)
        callback(new InternalError(), null)
      })
  }

  static resendConfirmationEmail(id, callback) {
    TeamController.get(id, (err, team) => {
      if (err) {
        callback(err)
      } else {
        if (team.emailConfirmed) {
          callback(new EmailConfirmedError())
        } else {
          Team
            .query()
            .patchAndFetchById(team.id, {
              emailConfirmationToken: token.generate()
            })
            .then((updatedTeam) => {
              queue('sendEmailQueue').add({
                message: 'welcome',
                name: updatedTeam.name,
                email: updatedTeam.email,
                token: updatedTeam.emailConfirmationToken
              })

              callback(null)
            })
            .catch((err) => {
              logger.error(err)
              callback(new InternalError())
            })
        }
      }
    })
  }

  static changeEmail(id, email, callback) {
    TeamController.get(id, (err, team) => {
      if (err) {
        callback(err)
      } else {
        if (team.emailConfirmed) {
          callback(new EmailConfirmedError())
        } else {
          Team
            .query()
            .where('email', email.toLowerCase())
            .first()
            .then((duplicateTeam) => {
              if (duplicateTeam) {
                callback(new EmailTakenError())
              } else {
                Team
                  .query()
                  .patchAndFetchById(team.id, {
                    email: email,
                    emailConfirmationToken: token.generate()
                  })
                  .then((updatedTeam) => {
                    queue('sendEmailQueue').add({
                      message: 'welcome',
                      name: updatedTeam.name,
                      email: updatedTeam.email,
                      token: updatedTeam.emailConfirmationToken
                    })

                    callback(null)
                    publish('realtime', new ChangeTeamEmailEvent(updatedTeam))
                  })
                  .catch((err) => {
                    logger.error(err)
                    callback(err)
                  })
              }
            })
            .catch((err) => {
              logger.error(err)
              callback(new InternalError())
            })
        }
      }
    })
  }

  static editProfile(id, country, locality, institution, callback) {
    TeamController.get(id, (err, team) => {
      if (err) {
        callback(err)
      } else {
        Team
          .query()
          .patchAndFetchById(team.id, {
            country: country,
            locality: locality,
            institution: institution
          })
          .then((updatedTeam) => {
            callback(null)
            publish('realtime', new UpdateTeamProfileEvent(updatedTeam))
          })
          .catch((err) => {
            logger.error(err)
            callback(new InternalError())
          })
      }
    })
  }

  static changeLogo(id, logoFilename, callback) {
    TeamController.get(id, (err, team) => {
      if (err) {
        callback(err)
      } else {
        queue('createLogoQueue').add({
          id: team.id,
          filename: logoFilename
        })
        callback(null)
      }
    })
  }

  static changePassword(id, currentPassword, newPassword, callback) {
    TeamController.get(id, (err, team) => {
      if (err) {
        callback(err)
      } else {
        checkPassword(currentPassword, team.passwordHash, (err, res) => {
          if (err) {
            logger.error(err)
            callback(new InternalError())
          } else {
            if (res) {
              getPasswordHash(newPassword, (err, hash) => {
                if (err) {
                  logger.error(err)
                  callback(new InternalError())
                } else {
                  Team
                    .query()
                    .patchAndFetchById(team.id, {
                      passwordHash: hash
                    })
                    .then((updatedTeam) => {
                      callback(null)
                    })
                    .catch((err) => {
                      logger.error(err)
                      callback(new InternalError())
                    })
                }
              })
            } else {
              callback(new InvalidTeamPasswordError())
            }
          }
        })
      }
    })
  }

  static list(callback) {
    Team
      .query()
      .then((teams) => {
        callback(null, teams)
      })
      .catch((err) => {
        logger.error(err)
        callback(new InternalError(), null)
      })
  }

  static listQualified(callback) {
    Team
      .query()
      .where('emailConfirmed', true)
      .then((teams) => {
        callback(null, teams)
      })
      .catch((err) => {
        logger.error(err)
        callback(new InternalError(), null)
      })
  }

  static resetPassword(encodedEmail, encodedToken, newPassword, callback) {
    let email = null
    let code = null
    try {
      email = token.decodeString(encodedEmail)
      code = token.decode(encodedToken)
      if (!code) {
        throw 'Reset password code is null'
      }
    } catch (e) {
      logger.error(e)
      callback(new InvalidResetPasswordURLError())
      return
    }

    Team
      .query()
      .where('email', email)
      .andWhere('resetPasswordToken', code)
      .first()
      .then((team) => {
        if (team) {
          getPasswordHash(newPassword, (err, hash) => {
            if (err) {
              logger.error(err)
              callback(new InternalError())
            } else {
              Team
                .query()
                .patchAndFetchById(team.id, {
                  passwordHash: hash,
                  resetPasswordToken: null
                })
                .then((updatedTeam) => {
                  callback(null)
                })
                .catch((err) => {
                  logger.error(err)
                  callback(new InternalError())
                })
            }
          })
        } else {
          callback(new InvalidResetPasswordURLError())
        }
      })
      .catch((err) => {
        logger.error(err)
        callback(new InternalError())
      })
  }

  static verifyEmail(encodedEmail, encodedToken, callback) {
    let email = null
    let code = null
    try {
      email = token.decodeString(encodedEmail)
      code = token.decode(encodedToken)
    } catch (e) {
      logger.error(e)
      callback(new InvalidVerificationURLError())
      return
    }

    Team
      .query()
      .where('email', email)
      .andWhere('emailConfirmed', false)
      .andWhere('emailConfirmationToken', code)
      .first()
      .then((team) => {
        if (team) {
          Team
            .query()
            .patchAndFetchById(team.id, {
              emailConfirmed: true,
              emailConfirmationToken: null
            })
            .then((updatedTeam) => {
              callback(null)
              publish('realtime', new QualifyTeamEvent(updatedTeam))
            })
            .catch((err) => {
              logger.error(err)
              callback(new InternalError())
            })
        } else {
          callback(new InvalidVerificationURLError())
        }
      })
      .catch((err) => {
        logger.error(err)
        callback(new InternalError())
      })
  }

  static get(id, callback) {
    Team
      .query()
      .where('id', id)
      .first()
      .then((team) => {
        if (team) {
          callback(null, team)
        } else {
          callback(new TeamNotFoundError(), null)
        }
      })
      .catch((err) => {
        logger.error(err)
        callback(new InternalError(), null)
      })
  }
}

export default TeamController
<|endoftext|>


<commit_before>"use strict";

/**
 * author Michal Zimmermann <zimmicz@gmail.com>
 * Displays coordinates of mouseclick.
 * @param object options:
 *        position: bottomleft, bottomright etc. (just as you are used to it with Leaflet)
 *        latitudeText: description of latitude value (defaults to lat.)
 *        longitudeText: description of latitude value (defaults to lon.)
 *        promptText: text displayed when user clicks the control
 *        precision: number of decimals to be displayed
 */
L.Control.Coordinates = L.Control.extend({
	options: {
		position: 'topright',
		latitudeText: 'lat',
		longitudeText: 'lng',
		promptText: 'Press Ctrl+C to copy coordinates',
		precision: 4
	},

	initialize: function(options)
	{
		L.Control.prototype.initialize.call(this, options);
	},

	onAdd: function(map)
	{
		var className = 'leaflet-control-coordinates',
			that = this,
			container = this._container = L.DomUtil.create('div', className);
		this.visible = false;

			L.DomUtil.addClass(container, 'hidden');

		L.DomEvent.disableClickPropagation(container);

		this._addText(container, map);

		L.DomEvent.addListener(container, 'click', function() {
			var lat = L.DomUtil.get(that._lat),
				lng = L.DomUtil.get(that._lng),
				latTextLen = this.options.latitudeText.length + 1,
				lngTextLen = this.options.longitudeText.length + 1,
				latTextIndex = lat.textContent.indexOf(this.options.latitudeText) + latTextLen,
				lngTextIndex = lng.textContent.indexOf(this.options.longitudeText) + lngTextLen,
				latCoordinate = lat.textContent.substr(latTextIndex),
				lngCoordinate = lng.textContent.substr(lngTextIndex);

			window.prompt(this.options.promptText, latCoordinate + ' ' + lngCoordinate);
    	}, this);

		return container;
	},

	_addText: function(container, context)
	{
		this._lat = L.DomUtil.create('span', 'leaflet-control-coordinates-lat' , container),
		this._lng = L.DomUtil.create('span', 'leaflet-control-coordinates-lng' , container);

		return container;
	},

	/**
	 * This method should be called when user clicks the map.
	 * @param event object
	 */
	setCoordinates: function(obj)
	{
		if (!this.visible) {
			L.DomUtil.removeClass(this._container, 'hidden');
		}

		if (obj.latlng) {
			L.DomUtil.get(this._lat).innerHTML = '' + this.options.latitudeText + ': ' + obj.latlng.lat.toFixed(this.options.precision).toString();
			L.DomUtil.get(this._lng).innerHTML = '' + this.options.longitudeText + ': ' + obj.latlng.lng.toFixed(this.options.precision).toString();
		}
	}
});
<commit_msg>Update Control.Coordinates.js<commit_after>"use strict";

/**
 * author Michal Zimmermann <zimmicz@gmail.com>
 * Displays coordinates of mouseclick.
 * @param object options:
 *        position: bottomleft, bottomright etc. (just as you are used to it with Leaflet)
 *        latitudeText: description of latitude value (defaults to lat.)
 *        longitudeText: description of latitude value (defaults to lon.)
 *        promptText: text displayed when user clicks the control
 *        precision: number of decimals to be displayed
 */
L.Control.Coordinates = L.Control.extend({
	options: {
		position: 'bottomleft',
		latitudeText: 'lat',
		longitudeText: 'lng',
		promptText: 'Press Ctrl+C to copy coordinates',
		precision: 4
	},

	initialize: function(options)
	{
		L.Control.prototype.initialize.call(this, options);
	},

	onAdd: function(map)
	{
		var className = 'leaflet-control-coordinates',
			that = this,
			container = this._container = L.DomUtil.create('div', className);
		this.visible = false;

			L.DomUtil.addClass(container, 'hidden');

		L.DomEvent.disableClickPropagation(container);

		this._addText(container, map);

		L.DomEvent.addListener(container, 'click', function() {
			var lat = L.DomUtil.get(that._lat),
				lng = L.DomUtil.get(that._lng),
				latTextLen = this.options.latitudeText.length + 1,
				lngTextLen = this.options.longitudeText.length + 1,
				latTextIndex = lat.textContent.indexOf(this.options.latitudeText) + latTextLen,
				lngTextIndex = lng.textContent.indexOf(this.options.longitudeText) + lngTextLen,
				latCoordinate = lat.textContent.substr(latTextIndex),
				lngCoordinate = lng.textContent.substr(lngTextIndex);

			window.prompt(this.options.promptText, latCoordinate + ' ' + lngCoordinate);
    	}, this);

		return container;
	},

	_addText: function(container, context)
	{
		this._lat = L.DomUtil.create('span', 'leaflet-control-coordinates-lat' , container),
		this._lng = L.DomUtil.create('span', 'leaflet-control-coordinates-lng' , container);

		return container;
	},

	/**
	 * This method should be called when user clicks the map.
	 * @param event object
	 */
	setCoordinates: function(obj)
	{
		if (!this.visible) {
			L.DomUtil.removeClass(this._container, 'hidden');
		}

		if (obj.latlng) {
			L.DomUtil.get(this._lat).innerHTML = '' + this.options.latitudeText + ': ' + obj.latlng.lat.toFixed(this.options.precision).toString();
			L.DomUtil.get(this._lng).innerHTML = '' + this.options.longitudeText + ': ' + obj.latlng.lng.toFixed(this.options.precision).toString();
		}
	}
});
<|endoftext|>


<commit_before>define([], function () {
    'use strict';

    function Duckface() {
    }

    return Duckface;
};
<commit_msg>Added stricter duckface checking of methods<commit_after>/*
 * Code taken from http://jscriptpatterns.blogspot.be/2013/01/javascript-interfaces.html
 * and minimally modified for stricter method checking via
 * arguments inspection (http://stackoverflow.com/questions/1007981/how-to-get-function-parameter-names-values-dynamically-from-javascript)
 */
define([], function () {
    'use strict';

    function Duckface(objectName, methods) {
        if (arguments.length != Duckface.length) {
            throw new Error ("Duckface constructor called with " + arguments.length + "arguments, but expected exactly " + Duckface.length);
        }

        if (!this.constructor == Duckface) {
            throw new Error ("Duckface should be called as a constructor");
        }

        this.name = objectName;
        this.methods = [];

        if (methods instanceof Array) {
            for (var i = 0, len = methods.length; i < len; i++) {
                if (typeof methods[i] !== 'string') {
                    throw new Error ("Method names should be specified as strings");
                }

                this.methods.push({
                    name: methods[i],
                    args: null
                });
            }
        }

        if (methods instanceof Object) {
            var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
            var ARGUMENT_NAMES = /([^\s,]+)/g;
            function getParamNames(func) {
                var fnStr = func.toString().replace(STRIP_COMMENTS, '');
                var result = fnStr.slice(fnStr.indexOf('(')+1, fnStr.indexOf(')')).match(ARGUMENT_NAMES);
                if(result === null) {
                    result = [];
                }
                return result;
            }

            var keys = Object.keys(methods);

            for (var i = 0, len = keys.length; i < len; i++) {
                var key = keys[i];
                var dummyMethod = methods[key];
                var args = getParamNames(dummyMethod);

                this.methods.push({
                    name: key,
                    args: args
                });
            }
        }
    }

    Duckface.ensureImplements = function(object) {
        if (arguments.length < 2) {
            throw new Error("Function Duckface.ensureImplements called with " + arguments.length + "arguments, but expected at least 2.");
        }

        for (var i = 1, len = arguments.length; i < len; i++) {
            var duckface = arguments[i];
            if (duckface.constructor !== Duckface) {
                throw new Error("Function Duckface.ensureImplements expects arguments two and above to be instances of Duckface.");
            }

            for (var j = 0, methodsLen = duckface.methods.length; j < methodsLen; j++) {
                var method = duckface.methods[j];
                if (!object[method.name] || typeof object[method.name] !== 'function') {
                    throw new Error("Function Duckface.ensureImplements: object does not implement the " + duckface.name + " duckface. Method " + method.name + " was not found.");
                }

                // stricter checking
                if (method.args !== null && method.args != getParamNames(object[method.name]) {
                    throw new Error("Method " + method.name + " does not adhere to the Duckface definition. Requires arguments '" + method.args.join("','") + "' in the method definition.");
                }
            }
        }
    };

    return Duckface;
};
<|endoftext|>


<commit_before>/**
 * SurveyQuestionSingleSelect component.
 *
 * Site Kit by Google, Copyright 2021 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * External dependencies
 */
import PropTypes from 'prop-types';

/**
 * WordPress dependencies
 */
import { useState } from '@wordpress/element';

/**
 * Internal dependencies
 */
import Button from '../Button';
import SurveyHeader from './SurveyHeader';
// import { TextField, Input } from '../../material-components';
import SurveyQuestionSingleSelectChoice from './SurveyQuestionSingleSelectChoice';

// const MAXIMUM_CHARACTER_LIMIT = 100;

/* eslint-disable camelcase */

const SurveyQuestionSingleSelect = ( {
	question,
	choices,
	answerQuestion,
	dismissSurvey,
} ) => {
	const [ value, setValue ] = useState( '' ); // eslint-disable-line
	const [ writeIn, setWriteIn ] = useState( '' ); // eslint-disable-line

	const handleSubmit = () => {
		answerQuestion();
	};
	const isSubmitButtonDisabled = value === '' || writeIn === '';

	return (

			<SurveyHeader title={ question } dismissSurvey={ dismissSurvey } />

			{ choices.map( ( _, id ) => (
				<SurveyQuestionSingleSelectChoice key={ id } />
			) ) }

				<Button
					onClick={ handleSubmit }
					disabled={ isSubmitButtonDisabled }
				>
					Next
				</Button>

	);
};

SurveyQuestionSingleSelect.propTypes = {
	question: PropTypes.string.isRequired,
	choices: PropTypes.arrayOf(
		PropTypes.shape( {
			answer_ordinal: PropTypes.oneOfType( [
				PropTypes.string,
				PropTypes.number,
			] ),
			text: PropTypes.string,
			write_in: PropTypes.bool,
		} )
	).isRequired,
	answerQuestion: PropTypes.func.isRequired,
	dismissSurvey: PropTypes.func.isRequired,
};

export default SurveyQuestionSingleSelect;
<commit_msg>Pass props into SurveyQuestionSingleSelectChoice.<commit_after>/**
 * SurveyQuestionSingleSelect component.
 *
 * Site Kit by Google, Copyright 2021 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * External dependencies
 */
import PropTypes from 'prop-types';

/**
 * WordPress dependencies
 */
import { useState } from '@wordpress/element';

/**
 * Internal dependencies
 */
import Button from '../Button';
import SurveyHeader from './SurveyHeader';
// import { TextField, Input } from '../../material-components';
import SurveyQuestionSingleSelectChoice from './SurveyQuestionSingleSelectChoice';

// const MAXIMUM_CHARACTER_LIMIT = 100;

/* eslint-disable camelcase */

const SurveyQuestionSingleSelect = ( {
	question,
	choices,
	answerQuestion,
	dismissSurvey,
} ) => {
	const [ value, setValue ] = useState( '' ); // eslint-disable-line
	const [ writeIn, setWriteIn ] = useState( '' ); // eslint-disable-line

	const handleSubmit = () => {
		answerQuestion();
	};
	const isSubmitButtonDisabled = value === '' || writeIn === '';

	return (

			<SurveyHeader title={ question } dismissSurvey={ dismissSurvey } />

			{ choices.map( ( choice, id ) => (
				<SurveyQuestionSingleSelectChoice
					key={ id }
					value={ value }
					setValue={ setValue }
					writeIn={ writeIn }
					setWriteIn={ setWriteIn }
					choice={ choice }
				/>
			) ) }

				<Button
					onClick={ handleSubmit }
					disabled={ isSubmitButtonDisabled }
				>
					Next
				</Button>

	);
};

SurveyQuestionSingleSelect.propTypes = {
	question: PropTypes.string.isRequired,
	choices: PropTypes.arrayOf(
		PropTypes.shape( {
			answer_ordinal: PropTypes.oneOfType( [
				PropTypes.string,
				PropTypes.number,
			] ),
			text: PropTypes.string,
			write_in: PropTypes.bool,
		} )
	).isRequired,
	answerQuestion: PropTypes.func.isRequired,
	dismissSurvey: PropTypes.func.isRequired,
};

export default SurveyQuestionSingleSelect;
<|endoftext|>


<commit_before>//PATH EXTRACTION

//Given a ray described by an initial point P0 and a direction V both in world coordinates,
//check to see if it intersects the polygon described by "vertices," an array of vec3
//values describing the location of the polygon vertices in its child frame.
//mvMatrix is a matrix describing how to transform "vertices" into world coordinates
//which you will have to do to get the correct intersection in world coordinates.
//Be sure to compute the plane normal only after you have transformed the points,
//and be sure to only compute intersections which are inside of the polygon
//(you can assume that all polygons are convex and use the area method)

function rayIntersectPolygon(P0, V, vertices, mvMatrix) {
    //TODO: Fill this in
    //Step 1: Make a new array of vec3s which holds "vertices" transformed to world
    //coordinates (hint: vec3 has a function "transformMat4" which is useful)
    var wc_vertices = [];
    for (vertex in vertices) {
        var wc_vertex = vec3.create();
        vec3.transformMat4(wc_vertex, vertex, mvMatrix);
        wc_vertices.push(wc_vertex);
    }
    //Step 2: Compute the plane normal of the plane spanned by the transformed vertices
    var v1 = vec3.create();
    vec3.subtract(v1, wc_vertices[0], wc_vertices[1]);
    var v2 = vec3.create();
    vec3.subtract(v2, wc_vertices[1], wc_vertices[2]);
    var normal = vec3.create();
    vec3.cross(normal, v1, v2);
    vec3.normalize(normal, normal);
    if (vec3.dot(normal, V) == 0) return null;
    //Step 3: Perform ray intersect plane
    var top = -1 * vec3.dot(P0, normal);
    var bottom = vec3.dot(V, normal);
    var t_int = top / bottom;
    if (t_int <= 0) return null;
    //Step 4: Check to see if the intersection point is inside of the transformed
    //polygon. You can assume that the polygon is convex.  If you use the area test,
    //you can allow for some wiggle room in the two areas you're comparing (e.g.
    //absolute difference not exceeding 1e-4)
    var area1 = 0;
    for (var i = 1; i < wc_vertices.length-1; i++) {
        area1 += getTriangleArea(wc_vertices[0], wc_vertices[i], wc_vertices[i+1]);
    }
    var area2 = 0;
    var intercept = vec3.create();
    vec3.scale(intercept, V, t_int);
    vec3.add(intercept, P0, intercept);
    for (var i = 0; i < wc_vertices.length-1; i++) {
        area2 += getTriangleArea(intercept, wc_vertices[i], wc_vertices[i+1]);
    }
    if (Math.absolute(area1 - area2) > Math.pow(10, -4)) return null;

    //Step 5: Return the intersection point if it exists or null if it's outside
    //of the polygon or if the ray is perpendicular to the plane normal (no intersection)
    return {t:t_int, P:vec3.fromValues(intercept[0], intercept[1], intercept[2])}; //These are dummy values
    //you should return both an intersection point and a parameter t.  The parameter t
    //will be used to sort intersections in order of occurrence to figure out which one happened first
}

function getTriangleArea(a, b, c) {
    var v1 = vec3.create(); //allocate a vector "v1" (ab)
    var v2 = vec3.create(); //allocate a vector "v2" (ac)
    vec3.subtract(v1, b, a); //calculate the vector from point a to point b (ab = b-a)
    vec3.subtract(v2, c, a); //calculate the vector from point a to point c (ac = c-a)
    // area = 0.5 * |v1 x v2|
    var cp = vec3.create(); //allocate a vector "cp" for the cross product of v1 and v2
    vec3.cross(cp, v1, v2); //calculate cross product
    var area = 0.5*vec3.len(cp) //calculate area by halving the magnitude of the cp vector (area of parallelogram formed by v1 and v2)
    return area;
}

function sceneGraphTraversal(s, node, mvMatrix, scene){ //complete the recursive scene graph traversal
  if ('children' in node) {
    for (var c = 0; c < node.children.length; c++) {
      //for each child in node.children
      if ('mesh' in node.children[c]){ // check if node is dummy: if not dummy node, it will have a mesh object
        var mesh = node.children[c].mesh; // access the mesh object
        for (var f = 0; f < mesh.faces.length; f++){ //loop through the array of its faces
          var face = mesh.faces[f]; // pointer to face
          if (face == s.genFace) { //if face is the face that generated the source
            continue; //Don't reflect with the face (you'll get parent image)
          }
          var vertices = face.getVerticesPos(); //get all vertices for a 'face' in CCW order & NCS
          // Convert vertices from NCS to WCS:
          var nextmvMatrix = mat4.create(); //Allocate transformation matrix
          mat4.mul(nextmvMatrix, mvMatrix, node.children[c].transform); //Calculate transformation matrix based on hierarchy
          var wc_vertices = []; //Make a new array that will contains vertices (vec3s) in WCS
          for (var v=0; v<vertices.length; v++){
            var wc_vertex= vec3.create(); //allocate a vector for the transfromed vertex
            vec3.transformMat4(wc_vertex, vertices[v], nextmvMatrix);
            wc_vertices.push(wc_vertex);
          };
          //Create the mirror image across each face (plane):

 var u1 = vec3.create(); //allocate a vector "u1" (vertex 1 to vertex 2)
          var u2 = vec3.create(); //allocate a vector "u2" (vertex 1 to vertex 3)
          vec3.subtract(u1, wc_vertices[1], wc_vertices[0]); //calculate the vector
          vec3.subtract(u2, wc_vertices[2], wc_vertices[0]); //calculate the vector
          var norm = vec3.create(); //allocate a vector for the plane normal
          vec3.cross(norm, u1, u2); //calculate plane normal using cross product
          var n = vec3.create();
          vec3.normalize(n, norm); // normalize n
          console.log("normal vector: " +norm);
          console.log("normalized norm vector: " +n);

          // s' = s - (2(s-p) dot n )*n ; assuming n is normalized (|n|=1)

          var p = wc_vertices[0]; // point p on the plane; arbitrarily a vertex
          console.log("p: " +p);

          var vecPS = vec3.create();
          vec3.subtract(vecPS, s.pos, p); // (s-p)
          console.log("s-p: " + vecPS);

          var dp = 2*vec3.dot(vecPS, n); // (2(s-p) dot n )
          console.log("dot product*2: " +dp);

          var dpn = vec3.create();
          vec3.scale(dpn, n, dp); //(2(s-p) dot n )*n
          console.log("(2(s-p) dot n )*n: " +dpn);

          var src = vec3.create(); // s'
          vec3.subtract(src, s.pos, dpn);

          var imgSrc = {pos:src, order: (s.order + 1), parent:s, genFace:face, rcoeff:s.rcoeff};
          scene.imsources.push(imgSrc);
        }
      }
      else{

        var nextmvMatrix = mat4.create(); //Allocate transformation matrix
        mat4.mul(nextmvMatrix, mvMatrix, node.children[c].transform);

      }
      sceneGraphTraversal(s, node.children[c], nextmvMatrix, scene); // recursive call on child
    }
  }

}

function addImageSourcesFunctions(scene) {
 //Purpose: A recursive function provided which helps to compute intersections
    //of rays with all faces in the scene, taking into consideration the scene graph
    //structure

    //Inputs: P0 (vec3): Ray starting point, V (vec3): ray direction
    //node (object): node in scene tree to process,
    //mvMatrix (mat4): Matrix to put geometry in this node into world coordinates
    //excludeFace: Pointer to face object to be excluded (don't intersect with
    //the face that this point lies on)
    //Returns: null if no intersection,
    //{tmin:minimum t along ray, PMin(vec3): corresponding point, faceMin:Pointer to mesh face hit first}

    //NOTE: Calling this function with node = scene and an identity matrix for mvMatrix
    //will start the recursion at the top of the scene tree in world coordinates

    //PROVIDED
  scene.rayIntersectFaces = function(P0, V, node, mvMatrix, excludeFace) {

    var tmin = Infinity;
    var PMin = null;
    var faceMin = null;
    if (node === null) {

      return null;

    }

    if ('mesh' in node) {
      var mesh = node.mesh;

      for (var f = 0; f < mesh.faces.length; f++) {

        if (mesh.faces[f] == excludeFace) {

          continue;
        }

        var res = rayIntersectPolygon(P0, V, mesh.faces[f].getVerticesPos(), mvMatrix);

        if (!(res === null) && (res.t < tmin)) {

          tmin = res.t;

          PMin = res.P;

          faceMin = mesh.faces[f];

        }

      }

    }

    if ('children' in node) {

      for (var i = 0; i < node.children.length; i++) {

        var nextmvMatrix = mat4.create();

        mat4.mul(nextmvMatrix, mvMatrix, node.children[i].transform);

        var cres = scene.rayIntersectFaces(P0, V, node.children[i], nextmvMatrix, excludeFace);

        if (!(cres === null) && (cres.tmin < tmin)) {

          tmin = cres.tmin;

          PMin = cres.PMin;

          faceMin = cres.faceMin;

        }

      }

    }

    if (PMin === null) {

      return null;

    }

    return {tmin:tmin, PMin:PMin, faceMin:faceMin};

  }

//IMAGE SOURCE GENERATION
//Purpose: Fill in the array scene.imsources[] with a bunch of source objects.
//Inputs: order (int) : maximum number of bounces to take
//Notes:
//source objects need "pos", "genFace", "rcoeff", "order", & "parent" fields (at least)
//use recursion (reflecting images of images of images (etc.) across polygon faces)

  scene.computeImageSources = function(order) {

    scene.source.order = 0;
    scene.source.rcoeff = 1.0;
    scene.source.parent = null;
    scene.source.genFace = null;
    scene.imsources= [scene.source];

    for (var o = 1; o<=order; o++){
      for (var s=0; s<scene.imsources.length; s++){ //check all previous image sources in scene.imsources
        if (scene.imsources[s].order === (o-1)){ //reflect image sources with 1 order less than the current order
          //reflect image by calling recursive scene tree function
          sceneGraphTraversal (scene.imsources[s], scene, mat4.create(), scene); //Start off recursion with scene and identity matrix
        }
      }
    }

    //DEBUGGING
    console.log("Number of scene sources: " + scene.imsources.length);

    for (var a = 0; a < scene.imsources.length; a++) {

      console.log("Position of scene source " +a + " : " + scene.imsources[a].pos);

    }

  }

//Purpose: Based on the extracted image sources, trace back paths from the
//receiver to the source, checking to make sure there are no occlusions
//along the way.  Remember, you're always starting by tracing a path from
//the receiver to the image, and then from the intersection point with
//that image's corresponding face to the image's parent, and so on
//all the way until you get back to the original source.
//Fill in the array scene.paths, where each element of the array is itself
//an array of objects describing vertices along the path, starting
//with the receiver and ending with the source.  Each object in each path
//array should contain a field "pos" which describes the position, as well
//as an element "rcoeff" which stores the reflection coefficient at that

//part of the path, which will be used to compute decays in "computeInpulseResponse()"

//Don't forget the direct path from source to receiver!

  scene.extractPaths = function() {

    scene.paths = [];
 //TODO: Finish this. Extract the rest of the paths by backtracing from
    //the image sources you calculated.  Return an array of arrays in
    //scene.paths.  Recursion is highly recommended
    //Each path should start at the receiver and end at the source
    //(or vice versa), so scene.receiver should be the first element
    //and scene.source should be the last element of every array in
    //scene.paths

  }

//Inputs: Fs: Sampling rate (samples per second)
  scene.computeImpulseResponse = function(Fs) {

    var SVel = 340;//Sound travels at 340 meters/second
    //TODO: Finish this.  Be sure to scale each bounce by 1/(1+r^p),
    //where r is the length of the line segment of that bounce in meters
    //and p is some integer less than 1 (make it smaller if you want the
    //paths to attenuate less and to be more echo-y as they propagate)
    //Also be sure to scale by the reflection coefficient of each material
    //bounce (you should have stored this in extractPaths() if you followed
    //those directions).  Use some form of interpolation to spread an impulse
    //which doesn't fall directly in a bin to nearby bins
    //Save the result into the array scene.impulseResp[]

  }

}
<commit_msg>Added debugged version of RayIntersectPolygon, started path extraction<commit_after>//PATH EXTRACTION

//Given a ray described by an initial point P0 and a direction V both in world coordinates,
//check to see if it intersects the polygon described by "vertices," an array of vec3
//values describing the location of the polygon vertices in its child frame.
//mvMatrix is a matrix describing how to transform "vertices" into world coordinates
//which you will have to do to get the correct intersection in world coordinates.
//Be sure to compute the plane normal only after you have transformed the points,
//and be sure to only compute intersections which are inside of the polygon
//(you can assume that all polygons are convex and use the area method)

function rayIntersectPolygon(P0, V, vertices, mvMatrix) {
    //Step 1: Make a new array of vec3s which holds "vertices" transformed to world
    //coordinates (hint: vec3 has a function "transformMat4" which is useful)
    var wc_vertices = [];
    for (var i = 0; i < vertices.length; i++) {
        var wc_vertex = vec3.create();
        vec3.transformMat4(wc_vertex, vertices[i], mvMatrix);
        wc_vertices.push(wc_vertex);
    }
    //Step 2: Compute the plane normal of the plane spanned by the transformed vertices
    var v1 = vec3.create();
    vec3.subtract(v1, wc_vertices[0], wc_vertices[1]);
    var v2 = vec3.create();
    vec3.subtract(v2, wc_vertices[1], wc_vertices[2]);
    var normal = vec3.create();
    vec3.cross(normal, v1, v2);
    vec3.normalize(normal, normal);
    if (vec3.dot(normal, V) == 0) return null;
    //Step 3: Perform ray intersect plane
    var sub = vec3.create();
    var top = vec3.dot(vec3.sub(sub, wc_vertices[0], P0), normal);
    var bottom = vec3.dot(V, normal);
    var t_int = top / bottom;
    if (t_int <= 0) return null;
    //Step 4: Check to see if the intersection point is inside of the transformed
    //polygon. You can assume that the polygon is convex.  If you use the area test,
    //you can allow for some wiggle room in the two areas you're comparing (e.g.
    //absolute difference not exceeding 1e-4)
    var area1 = 0;
    for (var i = 1; i < wc_vertices.length-1; i++) {
        area1 += getTriangleArea(wc_vertices[0], wc_vertices[i], wc_vertices[i+1]);
    }
    var area2 = 0;
    var intercept = vec3.create();
    vec3.scale(intercept, V, t_int);
    vec3.add(intercept, P0, intercept);
    for (var i = 0; i < wc_vertices.length-1; i++) {
        area2 += getTriangleArea(intercept, wc_vertices[i], wc_vertices[i+1]);
    }
    area2 += getTriangleArea(intercept, wc_vertices[wc_vertices.length-1], wc_vertices[0]);
    if (Math.abs(area1 - area2) > Math.pow(10, -4)) return null;

    //Step 5: Return the intersection point if it exists or null if it's outside
    //of the polygon or if the ray is perpendicular to the plane normal (no intersection)
    return {t:t_int, P:vec3.fromValues(intercept[0], intercept[1], intercept[2])}; //These are dummy values
    //you should return both an intersection point and a parameter t.  The parameter t
    //will be used to sort intersections in order of occurrence to figure out which one happened first
}

function getTriangleArea(a, b, c) {
    var v1 = vec3.create(); //allocate a vector "v1" (ab)
    var v2 = vec3.create(); //allocate a vector "v2" (ac)
    vec3.subtract(v1, b, a); //calculate the vector from point a to point b (ab = b-a)
    vec3.subtract(v2, c, a); //calculate the vector from point a to point c (ac = c-a)
    // area = 0.5 * |v1 x v2|
    var cp = vec3.create(); //allocate a vector "cp" for the cross product of v1 and v2
    vec3.cross(cp, v1, v2); //calculate cross product
    var area = 0.5*vec3.len(cp) //calculate area by halving the magnitude of the cp vector (area of parallelogram formed by v1 and v2)
    return area;
}

function sceneGraphTraversal(s, node, mvMatrix, scene){ //complete the recursive scene graph traversal
  if ('children' in node) {
    for (var c = 0; c < node.children.length; c++) {
      //for each child in node.children
      if ('mesh' in node.children[c]){ // check if node is dummy: if not dummy node, it will have a mesh object
        var mesh = node.children[c].mesh; // access the mesh object
        for (var f = 0; f < mesh.faces.length; f++){ //loop through the array of its faces
          var face = mesh.faces[f]; // pointer to face
          if (face == s.genFace) { //if face is the face that generated the source
            continue; //Don't reflect with the face (you'll get parent image)
          }
          var vertices = face.getVerticesPos(); //get all vertices for a 'face' in CCW order & NCS
          // Convert vertices from NCS to WCS:
          var nextmvMatrix = mat4.create(); //Allocate transformation matrix
          mat4.mul(nextmvMatrix, mvMatrix, node.children[c].transform); //Calculate transformation matrix based on hierarchy
          var wc_vertices = []; //Make a new array that will contains vertices (vec3s) in WCS
          for (var v=0; v<vertices.length; v++){
            var wc_vertex= vec3.create(); //allocate a vector for the transfromed vertex
            vec3.transformMat4(wc_vertex, vertices[v], nextmvMatrix);
            wc_vertices.push(wc_vertex);
          };
          //Create the mirror image across each face (plane):

 var u1 = vec3.create(); //allocate a vector "u1" (vertex 1 to vertex 2)
          var u2 = vec3.create(); //allocate a vector "u2" (vertex 1 to vertex 3)
          vec3.subtract(u1, wc_vertices[1], wc_vertices[0]); //calculate the vector
          vec3.subtract(u2, wc_vertices[2], wc_vertices[0]); //calculate the vector
          var norm = vec3.create(); //allocate a vector for the plane normal
          vec3.cross(norm, u1, u2); //calculate plane normal using cross product
          var n = vec3.create();
          vec3.normalize(n, norm); // normalize n
          console.log("normal vector: " +norm);
          console.log("normalized norm vector: " +n);

          // s' = s - (2(s-p) dot n )*n ; assuming n is normalized (|n|=1)

          var p = wc_vertices[0]; // point p on the plane; arbitrarily a vertex
          console.log("p: " +p);

          var vecPS = vec3.create();
          vec3.subtract(vecPS, s.pos, p); // (s-p)
          console.log("s-p: " + vecPS);

          var dp = 2*vec3.dot(vecPS, n); // (2(s-p) dot n )
          console.log("dot product*2: " +dp);

          var dpn = vec3.create();
          vec3.scale(dpn, n, dp); //(2(s-p) dot n )*n
          console.log("(2(s-p) dot n )*n: " +dpn);

          var src = vec3.create(); // s'
          vec3.subtract(src, s.pos, dpn);

          var imgSrc = {pos:src, order: (s.order + 1), parent:s, genFace:face, rcoeff:s.rcoeff};
          scene.imsources.push(imgSrc);
        }
      }
      else{

        var nextmvMatrix = mat4.create(); //Allocate transformation matrix
        mat4.mul(nextmvMatrix, mvMatrix, node.children[c].transform);

      }
      sceneGraphTraversal(s, node.children[c], nextmvMatrix, scene); // recursive call on child
    }
  }

}

function addImageSourcesFunctions(scene) {
 //Purpose: A recursive function provided which helps to compute intersections
    //of rays with all faces in the scene, taking into consideration the scene graph
    //structure

    //Inputs: P0 (vec3): Ray starting point, V (vec3): ray direction
    //node (object): node in scene tree to process,
    //mvMatrix (mat4): Matrix to put geometry in this node into world coordinates
    //excludeFace: Pointer to face object to be excluded (don't intersect with
    //the face that this point lies on)
    //Returns: null if no intersection,
    //{tmin:minimum t along ray, PMin(vec3): corresponding point, faceMin:Pointer to mesh face hit first}

    //NOTE: Calling this function with node = scene and an identity matrix for mvMatrix
    //will start the recursion at the top of the scene tree in world coordinates

    //PROVIDED
  scene.rayIntersectFaces = function(P0, V, node, mvMatrix, excludeFace) {

    var tmin = Infinity;
    var PMin = null;
    var faceMin = null;
    if (node === null) {

      return null;

    }

    if ('mesh' in node) {
      var mesh = node.mesh;

      for (var f = 0; f < mesh.faces.length; f++) {

        if (mesh.faces[f] == excludeFace) {

          continue;
        }

        var res = rayIntersectPolygon(P0, V, mesh.faces[f].getVerticesPos(), mvMatrix);

        if (!(res === null) && (res.t < tmin)) {

          tmin = res.t;

          PMin = res.P;

          faceMin = mesh.faces[f];

        }

      }

    }

    if ('children' in node) {

      for (var i = 0; i < node.children.length; i++) {

        var nextmvMatrix = mat4.create();

        mat4.mul(nextmvMatrix, mvMatrix, node.children[i].transform);

        var cres = scene.rayIntersectFaces(P0, V, node.children[i], nextmvMatrix, excludeFace);

        if (!(cres === null) && (cres.tmin < tmin)) {

          tmin = cres.tmin;

          PMin = cres.PMin;

          faceMin = cres.faceMin;

        }

      }

    }

    if (PMin === null) {

      return null;

    }

    return {tmin:tmin, PMin:PMin, faceMin:faceMin};

  }

//IMAGE SOURCE GENERATION
//Purpose: Fill in the array scene.imsources[] with a bunch of source objects.
//Inputs: order (int) : maximum number of bounces to take
//Notes:
//source objects need "pos", "genFace", "rcoeff", "order", & "parent" fields (at least)
//use recursion (reflecting images of images of images (etc.) across polygon faces)

  scene.computeImageSources = function(order) {

    scene.source.order = 0;
    scene.source.rcoeff = 1.0;
    scene.source.parent = null;
    scene.source.genFace = null;
    scene.imsources= [scene.source];

    for (var o = 1; o<=order; o++){
      for (var s=0; s<scene.imsources.length; s++){ //check all previous image sources in scene.imsources
        if (scene.imsources[s].order === (o-1)){ //reflect image sources with 1 order less than the current order
          //reflect image by calling recursive scene tree function
          sceneGraphTraversal (scene.imsources[s], scene, mat4.create(), scene); //Start off recursion with scene and identity matrix
        }
      }
    }

    //DEBUGGING
    console.log("Number of scene sources: " + scene.imsources.length);

    for (var a = 0; a < scene.imsources.length; a++) {

      console.log("Position of scene source " +a + " : " + scene.imsources[a].pos);

    }

  }

//Purpose: Based on the extracted image sources, trace back paths from the
//receiver to the source, checking to make sure there are no occlusions
//along the way.  Remember, you're always starting by tracing a path from
//the receiver to the image, and then from the intersection point with
//that image's corresponding face to the image's parent, and so on
//all the way until you get back to the original source.
//Fill in the array scene.paths, where each element of the array is itself
//an array of objects describing vertices along the path, starting
//with the receiver and ending with the source.  Each object in each path
//array should contain a field "pos" which describes the position, as well
//as an element "rcoeff" which stores the reflection coefficient at that

//part of the path, which will be used to compute decays in "computeInpulseResponse()"

//Don't forget the direct path from source to receiver!

  scene.extractPaths = function() {
    scene.paths = [];

    //TODO: Finish this. Extract the rest of the paths by backtracing from
    //the image sources you calculated.  Return an array of arrays in
    //scene.paths.  Recursion is highly recommended
    //Each path should start at the receiver and end at the source
    //(or vice versa), so scene.receiver should be the first element
    //and scene.source should be the last element of every array in
    //scene.paths
    var imsources_sorted = [];
    var done = false;
    var curr_order = 0;
    while (done == false) {
        var sources_of_order = [];
        for (var i = 0; i < scene.imsources.length; i++) {
            if (scene.imsources[i].order == curr_order) {
                sources_of_order.push(scene.imsources[i]);
            }
        }
        if (sources_of_order.length == 0) done = true;
        else {
            imsources_sorted.push(sources_of_order);
            curr_order++
        }
    }
}

//Inputs: Fs: Sampling rate (samples per second)
  scene.computeImpulseResponse = function(Fs) {

    var SVel = 340;//Sound travels at 340 meters/second
    //TODO: Finish this.  Be sure to scale each bounce by 1/(1+r^p),
    //where r is the length of the line segment of that bounce in meters
    //and p is some integer less than 1 (make it smaller if you want the
    //paths to attenuate less and to be more echo-y as they propagate)
    //Also be sure to scale by the reflection coefficient of each material
    //bounce (you should have stored this in extractPaths() if you followed
    //those directions).  Use some form of interpolation to spread an impulse
    //which doesn't fall directly in a bin to nearby bins
    //Save the result into the array scene.impulseResp[]

  }

}
<|endoftext|>


<commit_before>var methodMap = {
  'create': 'POST',
  'update': 'PUT',
  'patch':  'PATCH',
  'delete': 'DELETE',
  'read':   'GET'
};

// Replacements for _.result
var isFunction = function (func) {
  var getType = {};
  return func && getType.toString.call(func) === '[object Function]';
};
var result = function (thing, name) {
  return isFunction(thing[name]) ? thing[name]() : thing[name];
};

var throwUrlError = function () {
  var err = new Error('Must provide a url for sync');
  throw err;
};

function backboneFetchSync(method, model, options) {
  options = options || {};
  var type = methodMap[method];

  var params = {
    method: type
  };

  if (!options.url) {
    options.url = result(model, 'url') || throwUrlError();
  }

  if (type !== 'GET') {
    if (!options.headers) {
      options.headers = new Headers();
      options.headers.append('Content-Type', 'application/json');
    }

    params.headers = options.headers;
    params.body = JSON.stringify(options.body || model.toJSON(options));
  }

  return new Promise(function (resolve, reject) {
    var origResponse;
    var xhr = options.xhr = fetch(options.url, params)
      .then(function (response) {
        origResponse = response;
        return response.json();
      })
      .then(function (responseData) {
        if (options.success) {
          options.success.call(this, responseData, origResponse, options);
        }

        resolve({ responseData, origResponse, options });
      }, function (err) {
        if (options.error) {
          options.error.call(this, err, origResponse, options);
        }

        reject({ err, origResponse, options });
      });

    model.trigger('request', model, xhr, options);
  });
 }

 module.exports = backboneFetchSync;
<commit_msg>Set Headers also when using GET<commit_after>var methodMap = {
  'create': 'POST',
  'update': 'PUT',
  'patch':  'PATCH',
  'delete': 'DELETE',
  'read':   'GET'
};

// Replacements for _.result
var isFunction = function (func) {
  var getType = {};
  return func && getType.toString.call(func) === '[object Function]';
};
var result = function (thing, name) {
  return isFunction(thing[name]) ? thing[name]() : thing[name];
};

var throwUrlError = function () {
  var err = new Error('Must provide a url for sync');
  throw err;
};

function backboneFetchSync(method, model, options) {
  options = options || {};
  var type = methodMap[method];

  var params = {
    method: type
  };

  if (!options.url) {
    options.url = result(model, 'url') || throwUrlError();
  }

  if (!options.headers) {
    options.headers = new Headers();
    options.headers.append('Content-Type', 'application/json');
  }
  params.headers = options.headers;

  if (type !== 'GET') {
    params.body = JSON.stringify(options.body || model.toJSON(options));
  }

  return new Promise(function (resolve, reject) {
    var origResponse;
    var xhr = options.xhr = fetch(options.url, params)
      .then(function (response) {
        origResponse = response;
        return response.json();
      })
      .then(function (responseData) {
        if (options.success) {
          options.success.call(this, responseData, origResponse, options);
        }

        resolve({ responseData, origResponse, options });
      }, function (err) {
        if (options.error) {
          options.error.call(this, err, origResponse, options);
        }

        reject({ err, origResponse, options });
      });

    model.trigger('request', model, xhr, options);
  });
 }

 module.exports = backboneFetchSync;
<|endoftext|>


<commit_before>// Backbone.Validation v0.1.1
//
// Copyright (C)2011 Thomas Pedersen
// Distributed under MIT License
//
// Documentation and full license availabe at:
// http://github.com/thedersen/backbone.validation

// ----------------------------
// Backbone.Validation
// ----------------------------

Backbone.Validation = (function(Backbone, _) {
    var getValidators = function(view, attr) {
        var validation = view.model.validation[attr];

        if (_.isFunction(validation)) {
            return validation;
        } else {
            var valdations = [];
            for(attr in validation) {
                if(attr !== 'msg' && validation.hasOwnProperty(attr)) {
                    valdations.push({
                        fn: Backbone.Validation.validators[attr],
                        val: validation[attr],
                        msg: validation['msg']
                    });
                }
            }
            return valdations;
        }
    };

    var validateAttr = function(view, attr, value){
        var validators = getValidators(view, attr);

        if(_.isFunction(validators)){
            return validators(value);
        } else {
            var result = '';
            for (var i=0; i < validators.length; i++) {
               var validator = validators[i];
               var res = validator.fn(value, attr, validator.msg, validator.val);
               if(res){
                   result += res;
               }
            };
            return result;
        }
    };

    return {
        version: '0.1.1',

        bind: function(view, options) {
            options = options || {};
            var validFn = options.valid || Backbone.Validation.callbacks.valid,
                invalidFn = options.invalid || Backbone.Validation.callbacks.invalid,
                invalidAttrs = view.model.invalidAttrs = view.model.invalidAttrs || [];

            view.model.validate = function(attrs) {
                var invalid = false;

                for (changedAttr in attrs) {
                    if(changedAttr === 'isValid'){
                        return false;
                    }

                    var i = _.indexOf(invalidAttrs, changedAttr);
                    if(i !== -1){
                        delete invalidAttrs[i];
                    }

                    var result = validateAttr(view, changedAttr, attrs[changedAttr]);
                    if (result) {
                        invalid = true;
                        invalidAttrs.push(changedAttr);
                        invalidFn(view, changedAttr, result);
                    } else {
                        validFn(view, changedAttr);
                    }
                }

                view.model.set({isValid: _.compact(invalidAttrs).length === 0});

                return invalid;
            };
        },

        unbind: function(view){
            view.model.validate = undefined;
        }
    };
} (Backbone, _));

Backbone.Validation.callbacks = {
    valid: function(view, attr) {
        view.$('#' + attr).removeClass('invalid');
        view.$('#' + attr).removeAttr('data-error');
    },

    invalid: function(view, attr, error) {
        view.$('#' + attr).addClass('invalid');
        view.$('#' + attr).attr('data-error', error);
    }
};

Backbone.Validation.patterns = {
    number: /^\d$/,
    email: /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/,
    url: /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i
};

Backbone.Validation.validators = (function(_, $){
    return {
       required: function(value, attr, msg) {
            var isEmptyString = _.isString(value) && $.trim(value) === '';
            var isFalseBoolean = _.isBoolean(value) && value === false;

            if (_.isNull(value) || _.isUndefined(value) || isEmptyString || isFalseBoolean) {
                return msg || attr + ' is required';
            }
        },
        min: function(value, attr, msg, minValue) {
            value = parseInt(value, 10);
            if(!_.isNumber(value) || value < minValue) {
                return msg || attr + ' must be larger than or equal to ' + minValue;
            }
        },
        max: function(value, attr, msg, maxValue) {
            value = parseInt(value, 10);
            if(!_.isNumber(value) || value > maxValue) {
                return msg || attr + ' must be less than or equal to ' + maxValue;
            }
        },
        minLength: function(value, attr, msg, minLength){
            value = $.trim(value);
            if(_.isString(value) && value.length < minLength){
                return msg || attr + ' must be longer than or equal to ' + minLength + ' characters';
            }
        },
        maxLength: function(value, attr, msg, maxLength){
            value = $.trim(value);
            if(_.isString(value) && value.length > maxLength){
                return msg || attr + ' must be shorter than or equal to' + maxLength + ' characters';
            }
        },
        pattern: function(value, attr, msg, pattern){
            pattern = Backbone.Validation.patterns[pattern] || pattern;
            if (_.isString(value) && !value.match(pattern)) {
                return msg || attr + ' is not a valid ' + pattern;
            }
        }
    };
}(_, jQuery));
<commit_msg>Fixed typo<commit_after>// Backbone.Validation v0.1.1
//
// Copyright (C)2011 Thomas Pedersen
// Distributed under MIT License
//
// Documentation and full license availabe at:
// http://github.com/thedersen/backbone.validation

// ----------------------------
// Backbone.Validation
// ----------------------------

Backbone.Validation = (function(Backbone, _) {
    var getValidators = function(view, attr) {
        var validation = view.model.validation[attr];

        if (_.isFunction(validation)) {
            return validation;
        } else {
            var validations = [];
            for(attr in validation) {
                if(attr !== 'msg' && validation.hasOwnProperty(attr)) {
                    validations.push({
                        fn: Backbone.Validation.validators[attr],
                        val: validation[attr],
                        msg: validation['msg']
                    });
                }
            }
            return validations;
        }
    };

    var validateAttr = function(view, attr, value){
        var validators = getValidators(view, attr);

        if(_.isFunction(validators)){
            return validators(value);
        } else {
            var result = '';
            for (var i=0; i < validators.length; i++) {
               var validator = validators[i];
               var res = validator.fn(value, attr, validator.msg, validator.val);
               if(res){
                   result += res;
               }
            };
            return result;
        }
    };

    return {
        version: '0.1.1',

        bind: function(view, options) {
            options = options || {};
            var validFn = options.valid || Backbone.Validation.callbacks.valid,
                invalidFn = options.invalid || Backbone.Validation.callbacks.invalid,
                invalidAttrs = view.model.invalidAttrs = view.model.invalidAttrs || [];

            view.model.validate = function(attrs) {
                var invalid = false;

                for (changedAttr in attrs) {
                    if(changedAttr === 'isValid'){
                        return false;
                    }

                    var i = _.indexOf(invalidAttrs, changedAttr);
                    if(i !== -1){
                        delete invalidAttrs[i];
                    }

                    var result = validateAttr(view, changedAttr, attrs[changedAttr]);
                    if (result) {
                        invalid = true;
                        invalidAttrs.push(changedAttr);
                        invalidFn(view, changedAttr, result);
                    } else {
                        validFn(view, changedAttr);
                    }
                }

                view.model.set({isValid: _.compact(invalidAttrs).length === 0});

                return invalid;
            };
        },

        unbind: function(view){
            view.model.validate = undefined;
        }
    };
} (Backbone, _));

Backbone.Validation.callbacks = {
    valid: function(view, attr) {
        view.$('#' + attr).removeClass('invalid');
        view.$('#' + attr).removeAttr('data-error');
    },

    invalid: function(view, attr, error) {
        view.$('#' + attr).addClass('invalid');
        view.$('#' + attr).attr('data-error', error);
    }
};

Backbone.Validation.patterns = {
    number: /^\d$/,
    email: /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/,
    url: /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i
};

Backbone.Validation.validators = (function(_, $){
    return {
       required: function(value, attr, msg) {
            var isEmptyString = _.isString(value) && $.trim(value) === '';
            var isFalseBoolean = _.isBoolean(value) && value === false;

            if (_.isNull(value) || _.isUndefined(value) || isEmptyString || isFalseBoolean) {
                return msg || attr + ' is required';
            }
        },
        min: function(value, attr, msg, minValue) {
            value = parseInt(value, 10);
            if(!_.isNumber(value) || value < minValue) {
                return msg || attr + ' must be larger than or equal to ' + minValue;
            }
        },
        max: function(value, attr, msg, maxValue) {
            value = parseInt(value, 10);
            if(!_.isNumber(value) || value > maxValue) {
                return msg || attr + ' must be less than or equal to ' + maxValue;
            }
        },
        minLength: function(value, attr, msg, minLength){
            value = $.trim(value);
            if(_.isString(value) && value.length < minLength){
                return msg || attr + ' must be longer than or equal to ' + minLength + ' characters';
            }
        },
        maxLength: function(value, attr, msg, maxLength){
            value = $.trim(value);
            if(_.isString(value) && value.length > maxLength){
                return msg || attr + ' must be shorter than or equal to' + maxLength + ' characters';
            }
        },
        pattern: function(value, attr, msg, pattern){
            pattern = Backbone.Validation.patterns[pattern] || pattern;
            if (_.isString(value) && !value.match(pattern)) {
                return msg || attr + ' is not a valid ' + pattern;
            }
        }
    };
}(_, jQuery));
<|endoftext|>


<commit_before>'use strict';

define([
  'jquery',
  'underscore',
  'marionette',
  'dust',
  'dust.marionette',
  'controllers/event-bus',
  'views/comment-view',
  'views/reply-form-view',
  'views/replyable-view',
  'content/content-template',
  'content/post-template'
], function ($, _, Marionette, dust, dustMarionette, EventBus, CommentView, ReplyFormView, ReplyableView) {
  var view = _.extend(ReplyableView, {
    template:  'content/content-template.dust',
    childView: CommentView,
    tagName: 'div id="post"',
    events: {
      'click .b3-reply-post':   'renderReplyBox', // from ReplyableView
      'click .pagination-next': 'renderNextPage',
      'click .pagination-prev': 'renderPrevPage'
    },

    initialize: function (options) {
      this.model.fetchComments({
        done: function (data) { this.collection.add(data.models); }.bind(this),
        fail: function () { this.displayError(); }.bind(this)
      });
      this.page    = 0;
      this.content = this.model.get('content').split(/<!--nextpage-->/);
      this.post    = this.model;
      this.user    = options.user;

      _.bindAll(this, 'addComment');
      EventBus.bind('comment:created', this.addComment);
    },

    addComment: function (comment) {
      this.collection.add(comment);
      this.collection.sort();
      this.render();
    },

    parentId: function () {
      return 0;
    },

    serializeData: function () {
      return _.extend(this.parseModel(), this.getDustTemplate());
    },

    onDestroy: function () {
      if (this.replyBoxRendered) {
        this.replyBox.destroy();
      }

      EventBus.unbind('comment:created', this.addComment);
    },

    attachHtml: function (collectionView, itemView, index) {
      itemView.post = this.post;
      itemView.user = this.user;

      if (itemView.model.get('parent') > 0) {
        collectionView.$('#comment-' + itemView.model.get('parent') + ' > ul.b3-comments').append(itemView.el);
      } else {
        var commentSection = collectionView.$('.b3-comments');
        $(commentSection[0]).append(itemView.el);
      }
    },

    renderNextPage: function () {
      this.page++;
      this.render();
    },

    renderPrevPage: function () {
      this.page--;
      this.render();
    },

    displayError: function () {
      this.$('.b3-comments').text('Could not retrieve comments');
    },

    parseModel: function () {
      var model = this.model.toJSON();
      model.content = this.content[this.page];
      return _.extend(model, this.getPagination());
    },

    getPagination: function () {
      var total = this.content.length,
          next = (total > 1 && this.page < total - 1),
          prev = (this.page > 0);

      return {has_next: next, has_previous: prev};
    },

    getDustTemplate: function () {
      return {b3type: 'post', b3folder: 'content'};
    }
  });

  var ContentSingleView = Backbone.Marionette.CompositeView.extend(view);
  return ContentSingleView;
});<commit_msg>Fixes issue with clickable disabled buttons<commit_after>'use strict';

define([
  'jquery',
  'underscore',
  'marionette',
  'dust',
  'dust.marionette',
  'controllers/event-bus',
  'views/comment-view',
  'views/reply-form-view',
  'views/replyable-view',
  'content/content-template',
  'content/post-template'
], function ($, _, Marionette, dust, dustMarionette, EventBus, CommentView, ReplyFormView, ReplyableView) {
  var view = _.extend(ReplyableView, {
    template:  'content/content-template.dust',
    childView: CommentView,
    tagName: 'div id="post"',
    events: {
      'click .b3-reply-post':   'renderReplyBox', // from ReplyableView
      'click .pagination-next': 'renderNextPage',
      'click .pagination-prev': 'renderPrevPage'
    },

    initialize: function (options) {
      this.model.fetchComments({
        done: function (data) { this.collection.add(data.models); }.bind(this),
        fail: function () { this.displayError(); }.bind(this)
      });
      this.page    = 0;
      this.content = this.model.get('content').split(/<!--nextpage-->/);
      this.post    = this.model;
      this.user    = options.user;

      _.bindAll(this, 'addComment');
      EventBus.bind('comment:created', this.addComment);
    },

    addComment: function (comment) {
      this.collection.add(comment);
      this.collection.sort();
      this.render();
    },

    parentId: function () {
      return 0;
    },

    serializeData: function () {
      return _.extend(this.parseModel(), this.getDustTemplate());
    },

    onDestroy: function () {
      if (this.replyBoxRendered) {
        this.replyBox.destroy();
      }

      EventBus.unbind('comment:created', this.addComment);
    },

    attachHtml: function (collectionView, itemView, index) {
      itemView.post = this.post;
      itemView.user = this.user;

      if (itemView.model.get('parent') > 0) {
        collectionView.$('#comment-' + itemView.model.get('parent') + ' > ul.b3-comments').append(itemView.el);
      } else {
        var commentSection = collectionView.$('.b3-comments');
        $(commentSection[0]).append(itemView.el);
      }
    },

    renderNextPage: function () {
      if (this.hasNext()) {
        this.page++;
        this.render();
      }
    },

    renderPrevPage: function () {
      if (this.hasPrevious()) {
        this.page--;
        this.render();
      }
    },

    displayError: function () {
      this.$('.b3-comments').text('Could not retrieve comments');
    },

    parseModel: function () {
      var model = this.model.toJSON();
      model.content = this.content[this.page];
      return _.extend(model, this.getPagination());
    },

    getPagination: function () {
      var next = this.hasNext(),
          prev = this.hasPrevious();

      return {has_next: next, has_previous: prev};
    },

    getDustTemplate: function () {
      return {b3type: 'post', b3folder: 'content'};
    },

    hasNext: function () {
      var total = this.content.length;
      return (total > 1 && this.page < total - 1);
    },

    hasPrevious: function () {
      return this.page > 0;
    }
  });

  var ContentSingleView = Backbone.Marionette.CompositeView.extend(view);
  return ContentSingleView;
});<|endoftext|>


<commit_before>'use strict';

angular.module('app.dashboard', [])

  .controller('dashboardCtrl', ['$scope', 'elencoCitta' , function ($scope, elencoCitta) {
    var vm = this;
    vm.componentName = "Dashboard";
    /*Imposto la variabile elenco alla risposta del servizio*/
    vm.elenco = elencoCitta.elenco

    vm.aggiungiCitta = function(){
      elencoCitta.aggiungi({
        nome: vm.citta.nome,
        regione: vm.citta.regione
      })
    }
  }]);
<commit_msg>ripush<commit_after>'use strict';

angular.module('app.dashboard', [])

  .controller('dashboardCtrl', ['$scope', 'elencoCitta' , function ($scope, elencoCitta) {
    var vm = this;
    vm.componentName = "Dashboard";
    /*Imposto la variabile elenco alla risposta del servizio*/
    vm.elenco = elencoCitta.elenco;
    vm.citta = {};
    vm.aggiungiCitta = function(){
      console.log(vm.citta)
      elencoCitta.aggiungi({
        nome: vm.citta.nome,
        regione: vm.citta.regione
      })
    }
  }]);
<|endoftext|>