<commit_before>var glMatrix = require('../vendor/gl-matrix');

/**
 * A general 3d transform. This class represents a transformation from one
 * frame to another as a pair of a quaternion orientation and a vec3
 * position.
 *
 * @constructor
 * @param orientation the initial orientation.
 * @param position the initial position.
 */
function transform(orientation, position) {
    /** a quat representing the transforms orientation. */
    this.orientation = orientation;

    /** a vec3 representing the transforms position. */
    this.position = position;
}

transform.create = function() {
    return new transform(glMatrix.quat.create(), glMatrix.vec3.create());
}

transform.clone = function(a) {
    return a.clone();
}

/**
 * Clone the transform.
 */
transform.prototype.clone = function() {
    return new transform(glMatrix.quat.clone(this.orientation),
                         glMatrix.vec3.clone(this.position));
}

transform.copy = function(out, a) {
    glMatrix.quat.copy(out.orientation, a.orientation);
    glMatrix.vec3.copy(out.position, a.position);

    return out;
}

transform.multiply = function(out, a, b) {
    var apos = a.position;
    var aori = a.orientation;

    var bpos = b.position;
    var bori = b.orientation;

    glMatrix.vec3.add(out.position, apos, glMatrix.vec3.transformQuat(out.position, bpos, aori));
    glMatrix.quat.mul(out.orientation, aori, bori);

    return out;
}

transform.mul = transform.multiply;

/**
 * Multiply the transform by another transform. Note that this
 * modifies the receiving transform.
 *
 * @param transform a transform to multiply with.
 */
transform.prototype.mul = function(transform) {
    return transform.mul(this, this, transform);
}

transform.rotateX = function(out, a, rad) {
    glMatrix.vec3.copy(out.position, a.position);
    glMatrix.quat.rotateX(out.orientation, a.orientation, rad);

    return out;
}

/**
 * Rotate the transform around its local X axis. Note that this
 * modifies the receiving transform.
 *
 * @param rad the angle by which to rotate in radians.
 */
transform.prototype.rotateX = function(rad) {
    return transform.rotateX(this, this, rad);
}

transform.rotateY = function(out, a, rad) {
    glMatrix.vec3.copy(out.position, a.position);
    glMatrix.quat.rotateY(out.orientation, a.orientation, rad);

    return out;
}

/**
 * Rotate the transform around its local Y axis. Note that this
 * modifies the receiving transform.
 *
 * @param rad the angle by which to rotate in radians.
 */
transform.prototype.rotateY = function(rad) {
    return transform.rotateY(this, this, rad);
}

transform.rotateZ = function(out, a, rad) {
    glMatrix.vec3.copy(out.position, a.position);
    glMatrix.quat.rotateZ(out.orientation, a.orientation, rad);

    return out;
}

/**
 * Rotate the transform around its local Z axis. Note that this
 * modifies the receiving transform.
 *
 * @param rad the angle by which to rotate in radians.
 */
transform.prototype.rotateZ = function(rad) {
    return transform.rotateZ(this, this, rad);
}

transform.rotate = function(out, a, q) {
    glMatrix.vec3.copy(out.position, a.position);
    glMatrix.quat.mul(out.orientation, a.orientation, q);

    return out;
}

/**
 * Rotate the transform by the given quaternion. Note that this
 * modifies the receiving transform.
 *
 * @param q the quaternion by which to rotate.
 */
transform.prototype.rotate = function(q) {
    return transform.rotate(this, this, q);
}

transform.translateX = function(out, a, v) {
    glMatrix.quat.copy(out.orientation, a.orientation);
    glMatrix.vec3.copy(out.position, a.position);

    out.position[0] += v;
    return out;
}

/**
 * Translate on X in the parent frame. If you want to translate
 * the transform in the local frame, use {@link module:math~transform#translateSide translateSide}.
 * Note that this modifies the receiving transform.
 *
 * @param v a scalar to translate by.
 * @see {@link module:math~transform#translateSide translateSide}.
 */
transform.prototype.translateX = function(v) {
    return transform.translateX(this, this, v);
}

transform.translateY = function(out, a, v) {
    glMatrix.quat.copy(out.orientation, a.orientation);
    glMatrix.vec3.copy(out.position, a.position);

    out.position[1] += v;
    return out;
}

/**
 * Translate on Y in the parent frame. If you want to translate
 * the transform in the local frame, use {@link translateUp}.
 * Note that this modifies the receiving transform.
 *
 * @param v a scalar to translate by.
 * @see {@link translateUp}.
 */
transform.prototype.translateY = function(v) {
    return transform.translateY(this, this, v);
}

transform.translateZ = function(out, a, v) {
    glMatrix.quat.copy(out.orientation, a.orientation);
    glMatrix.vec3.copy(out.position, a.position);

    out.position[2] += v;
    return out;
}

/**
 * Translate on Z in the parent frame. If you want to translate
 * the transform in the local frame, use {@link translateForward}.
 * Note that this modifies the receiving transform.
 *
 * @param v a scalar to translate by.
 * @see {@link translateForward}.
 */
transform.prototype.translateZ = function(v) {
    return transform.translateZ(this, this, v);
}

transform.translate = function(out, a, v) {
    glMatrix.quat.copy(out.orientation, a.orientation);
    glMatrix.vec3.add(out.position, a.position, v);

    return out;
}

/**
 * Translate by the given vector. Note that this modifies the
 * receiving transform.
 *
 * @param v a vec3 to translate by.
 */
transform.prototype.translate = function(v) {
    return transform.translate(this, this, v);
}

transform.sideAxis = function(out, a) {
    var o = a.orientation,
        x = o[0], y = o[1], z = o[2], w = o[3],
        x2 = x + x,
        y2 = y + y,
        z2 = z + z,
        yy = y * y2,
        zz = z * z2,
        yx = y * x2,
        wz = w * z2,
        zx = z * x2,
        wy = w * y2;

    out[0] = 1 - yy - zz;
    out[1] = yx + wz;
    out[2] = zx - wy;

    return out;
}

/**
 * Obtain the local orientation's side axis. This is the same
 * as the X column of the transform's orientation matrix.
 */
transform.prototype.sideAxis = function() {
    return transform.sideAxis(glMatrix.vec3.create(), this);
}

transform.upAxis = function(out, a) {
    var o = a.orientation,
        x = o[0], y = o[1], z = o[2], w = o[3],
        x2 = x + x,
        y2 = y + y,
        z2 = z + z,
        yx = y * x2,
        wz = w * z2,
        xx = x * x2,
        zz = z * z2,
        zy = z * y2,
        wx = w * x2;

    out[0] = yx - wz;
    out[1] = 1 - xx - zz;
    out[2] = zy + wx;

    return out;
}

/**
 * Obtain the local orientation's up axis. This is the same
 * as the Y column of the transform's orientation matrix.
 */
transform.prototype.upAxis = function() {
    return transform.upAxis(glMatrix.vec3.create(), this);
}

transform.forwardAxis = function(out, a) {
    var o = a.orientation,
        x = o[0], y = o[1], z = o[2], w = o[3],
        x2 = x + x,
        y2 = y + y,
        z2 = z + z,
        zx = z * x2,
        wy = w * y2,
        zy = z * y2,
        wx = w * x2,
        xx = x * x2,
        yy = y * y2;

    out[0] = zx + wy;
    out[1] = zy - wx;
    out[2] = 1 - xx - yy;

    return out;
}

/**
 * Obtain the local orientation's forward axis. This is the same
 * as the Z column of the transform's orientation matrix.
 */
transform.prototype.forwardAxis = function() {
    return transform.forwardAxis(glMatrix.vec3.create(), this);
}

transform.translateSide = function(out, a, v) {
    var axis = transform.sideAxis(glMatrix.vec3.create(), a);
    glMatrix.vec3.scale(axis, axis, v);

    return transform.translate(out, a, axis);
}

/**
 * Translate the transform along its local orientation side axis.
 * Note that this modifies the receiving transform.
 *
 * @param v a scalar to translate sideways by.
 */
transform.prototype.translateSide = function(v) {
    return transform.translateSide(this, this, v);
}

transform.translateUp = function(out, a, v) {
    var axis = transform.upAxis(glMatrix.vec3.create(), a);
    glMatrix.vec3.scale(axis, axis, v);

    return transform.translate(out, a, axis);
}

/**
 * Translate the transform along its local orientation up axis.
 * Note that this modifies the receiving transform.
 *
 * @param v a scalar to translate upwards by.
 */
transform.prototype.translateUp = function(v) {
    return transform.translateUp(this, this, v);
}

transform.translateForward = function(out, a, v) {
    var axis = transform.forwardAxis(glMatrix.vec3.create(), a);
    glMatrix.vec3.scale(axis, axis, v);

    return transform.translate(out, a, axis);
}

/**
 * Translate the transform along its local orientation forward axis.
 * Note that this modifies the receiving transform.
 *
 * @param v a scalar amount to translate forwards by
 */
transform.prototype.translateForward = function(v) {
    return transform.translateForward(this, this, v);
}

transform.invert = function(out, a) {
    glMatrix.quat.invert(out.orientation, a.orientation);
    glMatrix.vec3.negate(out.position, a.position);

    glMatrix.vec3.transformQuat(out.position, out.position, out.orientation);

    return out;
}

/**
 * Invert the transformation. Note that this modifies the receiving
 * transform.
 */
transform.prototype.invert = function() {
    return transform.invert(this, this);
}

transform.str = function(a) {
    return '{' + glMatrix.quat.str(a.orientation) + ', ' + glMatrix.vec3.str(a.position) + '}';
}

glMatrix.mat4.fromTransform = function(out, t) {
    return glMatrix.mat4.fromRotationTranslation(glMatrix.mat4.create(), t.orientation, t.position);
}

glMatrix.vec4.transformTransform = function(out, a, t) {
    glMatrix.vec3.transformTransform(out, a, t);

    var tr = t.position;

    out[0] += a[3] * tr[0];
    out[1] += a[3] * tr[1];
    out[2] += a[3] * tr[2];
    out[3] = a[4];

    return out;
}

glMatrix.vec3.transformTransform = function(out, a, t) {
    return glMatrix.vec3.add(out, glMatrix.vec3.transformQuat(out, t.orientation), t.position);
}

function wrapIsMat(orig) {
    return function() {
        var ret = orig.apply(this, arguments);
        ret.isMat = true;

        return ret;
    }
}

var mats = [glMatrix.mat2, glMatrix.mat3, glMatrix.mat4];

for (var i = 0; i < mats.length; i++) {
    mats[i].create = wrapIsMat(mats[i].create);
    mats[i].clone = wrapIsMat(mats[i].clone);
}

for (var k in glMatrix) {
    if (glMatrix.hasOwnProperty(k)) {
        var v = glMatrix[k];

        var f = function() {
            return glMatrix[k].fromValues.apply(this, arguments);
        };

        for (var j in v) {
            if (v.hasOwnProperty(j)) {
                f[j] = v[j];
            }
        }

        exports[k] = f;
    }
}

exports.transform = transform;

// vi:ts=4:et
<commit_msg>Fix transform multiply<commit_after>var glMatrix = require('../vendor/gl-matrix');

/**
 * A general 3d transform. This class represents a transformation from one
 * frame to another as a pair of a quaternion orientation and a vec3
 * position.
 *
 * @constructor
 * @param orientation the initial orientation.
 * @param position the initial position.
 */
function transform(orientation, position) {
    /** a quat representing the transforms orientation. */
    this.orientation = orientation;

    /** a vec3 representing the transforms position. */
    this.position = position;
}

transform.create = function() {
    return new transform(glMatrix.quat.create(), glMatrix.vec3.create());
}

transform.clone = function(a) {
    return a.clone();
}

/**
 * Clone the transform.
 */
transform.prototype.clone = function() {
    return new transform(glMatrix.quat.clone(this.orientation),
                         glMatrix.vec3.clone(this.position));
}

transform.copy = function(out, a) {
    glMatrix.quat.copy(out.orientation, a.orientation);
    glMatrix.vec3.copy(out.position, a.position);

    return out;
}

transform.multiply = function(out, a, b) {
    var apos = a.position;
    var aori = a.orientation;

    var bpos = b.position;
    var bori = b.orientation;

    glMatrix.vec3.add(out.position, apos, glMatrix.vec3.transformQuat(out.position, bpos, aori));
    glMatrix.quat.mul(out.orientation, aori, bori);

    return out;
}

transform.mul = transform.multiply;

/**
 * Multiply the transform by another transform. Note that this
 * modifies the receiving transform.
 *
 * @param other a transform to multiply with.
 */
transform.prototype.mul = function(other) {
    return transform.mul(this, this, other);
}

transform.rotateX = function(out, a, rad) {
    glMatrix.vec3.copy(out.position, a.position);
    glMatrix.quat.rotateX(out.orientation, a.orientation, rad);

    return out;
}

/**
 * Rotate the transform around its local X axis. Note that this
 * modifies the receiving transform.
 *
 * @param rad the angle by which to rotate in radians.
 */
transform.prototype.rotateX = function(rad) {
    return transform.rotateX(this, this, rad);
}

transform.rotateY = function(out, a, rad) {
    glMatrix.vec3.copy(out.position, a.position);
    glMatrix.quat.rotateY(out.orientation, a.orientation, rad);

    return out;
}

/**
 * Rotate the transform around its local Y axis. Note that this
 * modifies the receiving transform.
 *
 * @param rad the angle by which to rotate in radians.
 */
transform.prototype.rotateY = function(rad) {
    return transform.rotateY(this, this, rad);
}

transform.rotateZ = function(out, a, rad) {
    glMatrix.vec3.copy(out.position, a.position);
    glMatrix.quat.rotateZ(out.orientation, a.orientation, rad);

    return out;
}

/**
 * Rotate the transform around its local Z axis. Note that this
 * modifies the receiving transform.
 *
 * @param rad the angle by which to rotate in radians.
 */
transform.prototype.rotateZ = function(rad) {
    return transform.rotateZ(this, this, rad);
}

transform.rotate = function(out, a, q) {
    glMatrix.vec3.copy(out.position, a.position);
    glMatrix.quat.mul(out.orientation, a.orientation, q);

    return out;
}

/**
 * Rotate the transform by the given quaternion. Note that this
 * modifies the receiving transform.
 *
 * @param q the quaternion by which to rotate.
 */
transform.prototype.rotate = function(q) {
    return transform.rotate(this, this, q);
}

transform.translateX = function(out, a, v) {
    glMatrix.quat.copy(out.orientation, a.orientation);
    glMatrix.vec3.copy(out.position, a.position);

    out.position[0] += v;
    return out;
}

/**
 * Translate on X in the parent frame. If you want to translate
 * the transform in the local frame, use {@link module:math~transform#translateSide translateSide}.
 * Note that this modifies the receiving transform.
 *
 * @param v a scalar to translate by.
 * @see {@link module:math~transform#translateSide translateSide}.
 */
transform.prototype.translateX = function(v) {
    return transform.translateX(this, this, v);
}

transform.translateY = function(out, a, v) {
    glMatrix.quat.copy(out.orientation, a.orientation);
    glMatrix.vec3.copy(out.position, a.position);

    out.position[1] += v;
    return out;
}

/**
 * Translate on Y in the parent frame. If you want to translate
 * the transform in the local frame, use {@link translateUp}.
 * Note that this modifies the receiving transform.
 *
 * @param v a scalar to translate by.
 * @see {@link translateUp}.
 */
transform.prototype.translateY = function(v) {
    return transform.translateY(this, this, v);
}

transform.translateZ = function(out, a, v) {
    glMatrix.quat.copy(out.orientation, a.orientation);
    glMatrix.vec3.copy(out.position, a.position);

    out.position[2] += v;
    return out;
}

/**
 * Translate on Z in the parent frame. If you want to translate
 * the transform in the local frame, use {@link translateForward}.
 * Note that this modifies the receiving transform.
 *
 * @param v a scalar to translate by.
 * @see {@link translateForward}.
 */
transform.prototype.translateZ = function(v) {
    return transform.translateZ(this, this, v);
}

transform.translate = function(out, a, v) {
    glMatrix.quat.copy(out.orientation, a.orientation);
    glMatrix.vec3.add(out.position, a.position, v);

    return out;
}

/**
 * Translate by the given vector. Note that this modifies the
 * receiving transform.
 *
 * @param v a vec3 to translate by.
 */
transform.prototype.translate = function(v) {
    return transform.translate(this, this, v);
}

transform.sideAxis = function(out, a) {
    var o = a.orientation,
        x = o[0], y = o[1], z = o[2], w = o[3],
        x2 = x + x,
        y2 = y + y,
        z2 = z + z,
        yy = y * y2,
        zz = z * z2,
        yx = y * x2,
        wz = w * z2,
        zx = z * x2,
        wy = w * y2;

    out[0] = 1 - yy - zz;
    out[1] = yx + wz;
    out[2] = zx - wy;

    return out;
}

/**
 * Obtain the local orientation's side axis. This is the same
 * as the X column of the transform's orientation matrix.
 */
transform.prototype.sideAxis = function() {
    return transform.sideAxis(glMatrix.vec3.create(), this);
}

transform.upAxis = function(out, a) {
    var o = a.orientation,
        x = o[0], y = o[1], z = o[2], w = o[3],
        x2 = x + x,
        y2 = y + y,
        z2 = z + z,
        yx = y * x2,
        wz = w * z2,
        xx = x * x2,
        zz = z * z2,
        zy = z * y2,
        wx = w * x2;

    out[0] = yx - wz;
    out[1] = 1 - xx - zz;
    out[2] = zy + wx;

    return out;
}

/**
 * Obtain the local orientation's up axis. This is the same
 * as the Y column of the transform's orientation matrix.
 */
transform.prototype.upAxis = function() {
    return transform.upAxis(glMatrix.vec3.create(), this);
}

transform.forwardAxis = function(out, a) {
    var o = a.orientation,
        x = o[0], y = o[1], z = o[2], w = o[3],
        x2 = x + x,
        y2 = y + y,
        z2 = z + z,
        zx = z * x2,
        wy = w * y2,
        zy = z * y2,
        wx = w * x2,
        xx = x * x2,
        yy = y * y2;

    out[0] = zx + wy;
    out[1] = zy - wx;
    out[2] = 1 - xx - yy;

    return out;
}

/**
 * Obtain the local orientation's forward axis. This is the same
 * as the Z column of the transform's orientation matrix.
 */
transform.prototype.forwardAxis = function() {
    return transform.forwardAxis(glMatrix.vec3.create(), this);
}

transform.translateSide = function(out, a, v) {
    var axis = transform.sideAxis(glMatrix.vec3.create(), a);
    glMatrix.vec3.scale(axis, axis, v);

    return transform.translate(out, a, axis);
}

/**
 * Translate the transform along its local orientation side axis.
 * Note that this modifies the receiving transform.
 *
 * @param v a scalar to translate sideways by.
 */
transform.prototype.translateSide = function(v) {
    return transform.translateSide(this, this, v);
}

transform.translateUp = function(out, a, v) {
    var axis = transform.upAxis(glMatrix.vec3.create(), a);
    glMatrix.vec3.scale(axis, axis, v);

    return transform.translate(out, a, axis);
}

/**
 * Translate the transform along its local orientation up axis.
 * Note that this modifies the receiving transform.
 *
 * @param v a scalar to translate upwards by.
 */
transform.prototype.translateUp = function(v) {
    return transform.translateUp(this, this, v);
}

transform.translateForward = function(out, a, v) {
    var axis = transform.forwardAxis(glMatrix.vec3.create(), a);
    glMatrix.vec3.scale(axis, axis, v);

    return transform.translate(out, a, axis);
}

/**
 * Translate the transform along its local orientation forward axis.
 * Note that this modifies the receiving transform.
 *
 * @param v a scalar amount to translate forwards by
 */
transform.prototype.translateForward = function(v) {
    return transform.translateForward(this, this, v);
}

transform.invert = function(out, a) {
    glMatrix.quat.invert(out.orientation, a.orientation);
    glMatrix.vec3.negate(out.position, a.position);

    glMatrix.vec3.transformQuat(out.position, out.position, out.orientation);

    return out;
}

/**
 * Invert the transformation. Note that this modifies the receiving
 * transform.
 */
transform.prototype.invert = function() {
    return transform.invert(this, this);
}

transform.str = function(a) {
    return '{' + glMatrix.quat.str(a.orientation) + ', ' + glMatrix.vec3.str(a.position) + '}';
}

glMatrix.mat4.fromTransform = function(out, t) {
    return glMatrix.mat4.fromRotationTranslation(glMatrix.mat4.create(), t.orientation, t.position);
}

glMatrix.vec4.transformTransform = function(out, a, t) {
    glMatrix.vec3.transformTransform(out, a, t);

    var tr = t.position;

    out[0] += a[3] * tr[0];
    out[1] += a[3] * tr[1];
    out[2] += a[3] * tr[2];
    out[3] = a[4];

    return out;
}

glMatrix.vec3.transformTransform = function(out, a, t) {
    return glMatrix.vec3.add(out, glMatrix.vec3.transformQuat(out, t.orientation), t.position);
}

function wrapIsMat(orig) {
    return function() {
        var ret = orig.apply(this, arguments);
        ret.isMat = true;

        return ret;
    }
}

var mats = [glMatrix.mat2, glMatrix.mat3, glMatrix.mat4];

for (var i = 0; i < mats.length; i++) {
    mats[i].create = wrapIsMat(mats[i].create);
    mats[i].clone = wrapIsMat(mats[i].clone);
}

for (var k in glMatrix) {
    if (glMatrix.hasOwnProperty(k)) {
        var v = glMatrix[k];

        var f = function() {
            return glMatrix[k].fromValues.apply(this, arguments);
        };

        for (var j in v) {
            if (v.hasOwnProperty(j)) {
                f[j] = v[j];
            }
        }

        exports[k] = f;
    }
}

exports.transform = transform;

// vi:ts=4:et
<|endoftext|>


<commit_before>angular.module('mwFileUpload', [])

  .provider('mwFileUpload', function () {
    var _defaultConfig = {};

    this.setGlobalConfig = function (conf) {
      _.extend(_defaultConfig, conf);
    };

    this.$get = function () {
      return {
        getGlobalConfig: function () {
          return _defaultConfig;
        }
      };
    };

  })

  .directive('mwFileUpload', function ($q, $timeout, $filter, mwFileUpload, ResponseHandler, mwMimetype, i18n) {
    return {
      restrict: 'A',
      scope: {
        url: '@',
        name: '@',
        model: '=?',
        attribute: '@',
        labelAttribute: '@',
        showFileName: '=',
        mwRequired: '=',
        validator: '@',
        text: '@',
        formData: '=',
        successCallback: '&',
        errorCallback: '&',
        stateChangeCallback: '&',
        fullScreen: '=',
        hiddenBtn: '=',
        showUploadBtnAlways: '=?',
        hasDropZone: '=?',
        hideCancelBtn: '=?',
        hideRemoveBtn: '=?',
        abortFlag: '=?',
        maxFileSizeByte: '=?'
      },
      require: '?^form',
      templateUrl: 'uikit/templates/mwFileUpload/mwFileUpload.html',
      link: function (scope, elm, attrs, formController) {
        scope.viewModel = {
          uploaderOptions: {},
          state: null,
          uploadProgress: 0,
          fileName: null,
          uploadError: null,
          isInvalid: false,
          dropZoneId: _.uniqueId('dropzone'),
          dropZoneElmement: null,
          documentDragOver: false,
          dropzoneDragOver: false,
          hasDropzone: false
        };

        var handleResponse = function (response, isError) {
          var ngResponse = {
            config: {
              method: response.type,
              url: response.url
            },
            data: response.result,
            headers: response.headers,
            status: response.xhr().status,
            statusText: response.xhr().statusText
          };
          var handler = ResponseHandler.handle(ngResponse, isError);
          if (handler) {
            return handler;
          } else if (isError) {
            return $q.reject(ngResponse);
          } else {
            return $q.when(ngResponse);
          }
        };

        var updateFileName = function () {
          var labelAttr = scope.labelAttribute || 'name';
          if (scope.model instanceof window.Backbone.Model) {
            scope.viewModel.fileName = scope.model.get(labelAttr);
          } else if (scope.model) {
            scope.viewModel.fileName = scope.model[labelAttr];
          } else {
            scope.viewModel.fileName = null;
          }
        };

        var updateModel = function (file) {
          if (scope.model instanceof Backbone.Model) {
            scope.model.set(scope.model.parse(file));
          } else if (scope.attribute) {
            scope.model = file[scope.attribute];
          } else {
            scope.model = file;
          }
          updateFileName();
        };

        var updateFile = function (xhr, response, file) {
          if (attrs.validator && !mwMimetype.checkMimeType(file.contentType, attrs.validator)) {
            if (response && response.message) {
              scope.uploadError = i18n.get('rlnUikit.mwFileUpload.invalidMimeType', {mimeType: attrs.validator});
              scope.viewModel.isInvalid = true;
              scope.viewModel.fileName = file.name;
              return;
            }
          }

          updateModel(file);

          handleResponse(xhr, false).then(function () {
            $timeout(scope.successCallback.bind(this, {result: file}));
          });
        };

        var setFormDirty = function () {
          if (formController) {
            formController.$setDirty();
          }
        };

        var triggerFileToBigError = function (files) {
          if (!files || !angular.isArray(files) || files.length === 0) {
            return;
          }
          var file = files[0];
          var actualFileSizeReadable = $filter('mwReadableFileSize')(file.size);
          var maxFileSizeReadable = $filter('mwReadableFileSize')(scope.maxFileSizeByte);
          var errorMsg = i18n.get('rlnUikit.mwFileUpload.fileTooLarge', {
            fileName: file.name,
            max: maxFileSizeReadable,
            actual: '(' + actualFileSizeReadable + ')'
          });
          $timeout(scope.errorCallback.bind(this, {
            result: {
              msg: errorMsg,
              code: 413,
              fileSize: file.size,
              fileName: file.name,
              fileType: file.type,
              maxFileSizeByte: scope.maxFileSizeByte
            }
          }));
          scope.viewModel.fileName = file.name;
          scope.viewModel.uploadError = errorMsg;
        };

        var updateUploaderOptions = function (options) {
          options = options || {};
          _.extend(scope.viewModel.uploaderOptions, options);
        };

        var setDropZone = function () {
          $timeout(function () {
            scope.viewModel.dropZoneElmement = elm.find('#' + scope.viewModel.dropZoneId);
          });
        };

        scope.abort = function () {
          scope.abortFlag = true;
        };

        scope.dragoverDocumentStateChange = function (newState) {
          if (newState === 'DOCUMENT_DRAG_OVER') {
            scope.viewModel.documentDragOver = true;
          } else {
            scope.viewModel.documentDragOver = false;
            scope.viewModel.dropzoneDragOver = false;
          }
        };

        scope.dragoverDropzoneStateChange = function (newState) {
          if (newState === 'DROPZONE_DRAG_OVER') {
            scope.viewModel.dropzoneDragOver = true;
          } else {
            scope.viewModel.dropzoneDragOver = false;
          }
        };

        scope.canShowUploadBtn = function () {
          return scope.viewModel.state !== 'UPLOADING' &&
            (!scope.viewModel.fileName || scope.showUploadBtnAlways || scope.hideRemoveBtn);
        };

        scope.canShowRemoveBtn = function () {
          return !scope.hideRemoveBtn && scope.viewModel.fileName;
        };

        scope.onUploadStart = function() {
          scope.viewModel.state = 'UPLOADING';
        }
        /* progressData = {
            data:blueImpXhr,
            progress: progress,
            total: dataTotal,
            loaded: dataLoaded
         } */
        scope.onUploadProgress = function (progressData) {
          var fileName = '';
          if (!angular.isObject(progressData)) {
            return;
          }
          if (progressData.data && angular.isArray(progressData.data.files) && progressData.data.files[0]) {
            fileName = progressData.data.files[0].name;
          }
          scope.abortFlag = false;
          scope.viewModel.isInvalid = true;
          scope.viewModel.uploadProgress = progressData.progress;
          scope.viewModel.uploadMessage = i18n.get('rlnUikit.mwFileUpload.uploading', {
            fileName: fileName
          });
          $timeout(scope.stateChangeCallback.bind(this, {
            data: progressData.data,
            progress: progressData.progress
          }));
          scope.dragoverDocumentStateChange();
          scope.dragoverDropzoneStateChange();
        };

        /* data = {
           xhr: blueImpXhr,
           response: {}
           file: {}
         } */
        scope.onUploadSuccess = function (data) {
          scope.viewModel.state = 'DONE';
          scope.viewModel.uploadProgress = 0;
          updateFile(data.xhr, data.response, data.file);
          scope.viewModel.isInvalid = false;
          setFormDirty();
          $timeout(scope.successCallback.bind(this, {result: data.file}));
        };

        /* data = {
           type: FILE_TOO_BIG | SERVER | CUSTOM_VALIDATION_FAILED,
           xhr: blueImpXhr,
           result: {
             files: [] | null,
             response: {} | null
           }
         } */
        scope.onUploadError = function (data) {
          scope.viewModel.state = 'DONE';
          switch (data.type) {
            case 'FILE_TOO_BIG':
              triggerFileToBigError(data.result.files);
              break;
            case 'SERVER':
              break;
            case 'CUSTOM_VALIDATION_FAILED':
              break;
          }
          scope.viewModel.isInvalid = true;
          setFormDirty();
          $timeout(scope.errorCallback.bind(this, {result: data.response || {}}));
        };

        scope.onUploadAbort = function () {
          scope.viewModel.state = 'DONE';
          scope.viewModel.isInvalid = false;
        };

        scope.removeFile = function () {
          updateModel(null);
          scope.viewModel.isInvalid = false;
        };

        scope.$watch('url', function (val) {
          if (val) {
            updateUploaderOptions({
              url: val
            });
          }
        });

        scope.$watch('formData', function (val) {
          if (val) {
            updateUploaderOptions({
              formData: val
            });
          }
        }, true);

        scope.$watch('hasDropZone', function (val) {
          if (val) {
            scope.viewModel.hasDropZone = true;
            setDropZone();
          } else {
            scope.viewModel.hasDropZone = false;
          }
        });

        if (angular.isUndefined(scope.hasDropZone)) {
          scope.hasDropZone = true;
        }

        updateUploaderOptions(mwFileUpload.getGlobalConfig());

        scope.$watch('model', updateFileName);
      }
    };
  });<commit_msg>Add missing semicolon<commit_after>angular.module('mwFileUpload', [])

  .provider('mwFileUpload', function () {
    var _defaultConfig = {};

    this.setGlobalConfig = function (conf) {
      _.extend(_defaultConfig, conf);
    };

    this.$get = function () {
      return {
        getGlobalConfig: function () {
          return _defaultConfig;
        }
      };
    };

  })

  .directive('mwFileUpload', function ($q, $timeout, $filter, mwFileUpload, ResponseHandler, mwMimetype, i18n) {
    return {
      restrict: 'A',
      scope: {
        url: '@',
        name: '@',
        model: '=?',
        attribute: '@',
        labelAttribute: '@',
        showFileName: '=',
        mwRequired: '=',
        validator: '@',
        text: '@',
        formData: '=',
        successCallback: '&',
        errorCallback: '&',
        stateChangeCallback: '&',
        fullScreen: '=',
        hiddenBtn: '=',
        showUploadBtnAlways: '=?',
        hasDropZone: '=?',
        hideCancelBtn: '=?',
        hideRemoveBtn: '=?',
        abortFlag: '=?',
        maxFileSizeByte: '=?'
      },
      require: '?^form',
      templateUrl: 'uikit/templates/mwFileUpload/mwFileUpload.html',
      link: function (scope, elm, attrs, formController) {
        scope.viewModel = {
          uploaderOptions: {},
          state: null,
          uploadProgress: 0,
          fileName: null,
          uploadError: null,
          isInvalid: false,
          dropZoneId: _.uniqueId('dropzone'),
          dropZoneElmement: null,
          documentDragOver: false,
          dropzoneDragOver: false,
          hasDropzone: false
        };

        var handleResponse = function (response, isError) {
          var ngResponse = {
            config: {
              method: response.type,
              url: response.url
            },
            data: response.result,
            headers: response.headers,
            status: response.xhr().status,
            statusText: response.xhr().statusText
          };
          var handler = ResponseHandler.handle(ngResponse, isError);
          if (handler) {
            return handler;
          } else if (isError) {
            return $q.reject(ngResponse);
          } else {
            return $q.when(ngResponse);
          }
        };

        var updateFileName = function () {
          var labelAttr = scope.labelAttribute || 'name';
          if (scope.model instanceof window.Backbone.Model) {
            scope.viewModel.fileName = scope.model.get(labelAttr);
          } else if (scope.model) {
            scope.viewModel.fileName = scope.model[labelAttr];
          } else {
            scope.viewModel.fileName = null;
          }
        };

        var updateModel = function (file) {
          if (scope.model instanceof Backbone.Model) {
            scope.model.set(scope.model.parse(file));
          } else if (scope.attribute) {
            scope.model = file[scope.attribute];
          } else {
            scope.model = file;
          }
          updateFileName();
        };

        var updateFile = function (xhr, response, file) {
          if (attrs.validator && !mwMimetype.checkMimeType(file.contentType, attrs.validator)) {
            if (response && response.message) {
              scope.uploadError = i18n.get('rlnUikit.mwFileUpload.invalidMimeType', {mimeType: attrs.validator});
              scope.viewModel.isInvalid = true;
              scope.viewModel.fileName = file.name;
              return;
            }
          }

          updateModel(file);

          handleResponse(xhr, false).then(function () {
            $timeout(scope.successCallback.bind(this, {result: file}));
          });
        };

        var setFormDirty = function () {
          if (formController) {
            formController.$setDirty();
          }
        };

        var triggerFileToBigError = function (files) {
          if (!files || !angular.isArray(files) || files.length === 0) {
            return;
          }
          var file = files[0];
          var actualFileSizeReadable = $filter('mwReadableFileSize')(file.size);
          var maxFileSizeReadable = $filter('mwReadableFileSize')(scope.maxFileSizeByte);
          var errorMsg = i18n.get('rlnUikit.mwFileUpload.fileTooLarge', {
            fileName: file.name,
            max: maxFileSizeReadable,
            actual: '(' + actualFileSizeReadable + ')'
          });
          $timeout(scope.errorCallback.bind(this, {
            result: {
              msg: errorMsg,
              code: 413,
              fileSize: file.size,
              fileName: file.name,
              fileType: file.type,
              maxFileSizeByte: scope.maxFileSizeByte
            }
          }));
          scope.viewModel.fileName = file.name;
          scope.viewModel.uploadError = errorMsg;
        };

        var updateUploaderOptions = function (options) {
          options = options || {};
          _.extend(scope.viewModel.uploaderOptions, options);
        };

        var setDropZone = function () {
          $timeout(function () {
            scope.viewModel.dropZoneElmement = elm.find('#' + scope.viewModel.dropZoneId);
          });
        };

        scope.abort = function () {
          scope.abortFlag = true;
        };

        scope.dragoverDocumentStateChange = function (newState) {
          if (newState === 'DOCUMENT_DRAG_OVER') {
            scope.viewModel.documentDragOver = true;
          } else {
            scope.viewModel.documentDragOver = false;
            scope.viewModel.dropzoneDragOver = false;
          }
        };

        scope.dragoverDropzoneStateChange = function (newState) {
          if (newState === 'DROPZONE_DRAG_OVER') {
            scope.viewModel.dropzoneDragOver = true;
          } else {
            scope.viewModel.dropzoneDragOver = false;
          }
        };

        scope.canShowUploadBtn = function () {
          return scope.viewModel.state !== 'UPLOADING' &&
            (!scope.viewModel.fileName || scope.showUploadBtnAlways || scope.hideRemoveBtn);
        };

        scope.canShowRemoveBtn = function () {
          return !scope.hideRemoveBtn && scope.viewModel.fileName;
        };

        scope.onUploadStart = function() {
          scope.viewModel.state = 'UPLOADING';
        };
        /* progressData = {
            data:blueImpXhr,
            progress: progress,
            total: dataTotal,
            loaded: dataLoaded
         } */
        scope.onUploadProgress = function (progressData) {
          var fileName = '';
          if (!angular.isObject(progressData)) {
            return;
          }
          if (progressData.data && angular.isArray(progressData.data.files) && progressData.data.files[0]) {
            fileName = progressData.data.files[0].name;
          }
          scope.abortFlag = false;
          scope.viewModel.isInvalid = true;
          scope.viewModel.uploadProgress = progressData.progress;
          scope.viewModel.uploadMessage = i18n.get('rlnUikit.mwFileUpload.uploading', {
            fileName: fileName
          });
          $timeout(scope.stateChangeCallback.bind(this, {
            data: progressData.data,
            progress: progressData.progress
          }));
          scope.dragoverDocumentStateChange();
          scope.dragoverDropzoneStateChange();
        };

        /* data = {
           xhr: blueImpXhr,
           response: {}
           file: {}
         } */
        scope.onUploadSuccess = function (data) {
          scope.viewModel.state = 'DONE';
          scope.viewModel.uploadProgress = 0;
          updateFile(data.xhr, data.response, data.file);
          scope.viewModel.isInvalid = false;
          setFormDirty();
          $timeout(scope.successCallback.bind(this, {result: data.file}));
        };

        /* data = {
           type: FILE_TOO_BIG | SERVER | CUSTOM_VALIDATION_FAILED,
           xhr: blueImpXhr,
           result: {
             files: [] | null,
             response: {} | null
           }
         } */
        scope.onUploadError = function (data) {
          scope.viewModel.state = 'DONE';
          switch (data.type) {
            case 'FILE_TOO_BIG':
              triggerFileToBigError(data.result.files);
              break;
            case 'SERVER':
              break;
            case 'CUSTOM_VALIDATION_FAILED':
              break;
          }
          scope.viewModel.isInvalid = true;
          setFormDirty();
          $timeout(scope.errorCallback.bind(this, {result: data.response || {}}));
        };

        scope.onUploadAbort = function () {
          scope.viewModel.state = 'DONE';
          scope.viewModel.isInvalid = false;
        };

        scope.removeFile = function () {
          updateModel(null);
          scope.viewModel.isInvalid = false;
        };

        scope.$watch('url', function (val) {
          if (val) {
            updateUploaderOptions({
              url: val
            });
          }
        });

        scope.$watch('formData', function (val) {
          if (val) {
            updateUploaderOptions({
              formData: val
            });
          }
        }, true);

        scope.$watch('hasDropZone', function (val) {
          if (val) {
            scope.viewModel.hasDropZone = true;
            setDropZone();
          } else {
            scope.viewModel.hasDropZone = false;
          }
        });

        if (angular.isUndefined(scope.hasDropZone)) {
          scope.hasDropZone = true;
        }

        updateUploaderOptions(mwFileUpload.getGlobalConfig());

        scope.$watch('model', updateFileName);
      }
    };
  });<|endoftext|>


<commit_before>/*global window:false*/
import _ from 'lodash';
import d3 from 'd3';

import {TAU, velocityI, velocityJ, perpendicularI, perpendicularJ} from './const';
import {tileSize, oscillations, polarizationScaleH, polarizationScaleV, resizeThrottle, absorptionDuration, absorptionTextDuration} from './config';

class Particle {

  constructor(i, j, dir, hRe, hIm, vRe, vIm) {
    this.i = i;
    this.j = j;
    this.dir = dir;
    this.hRe = hRe;
    this.hIm = hIm;
    this.vRe = vRe;
    this.vIm = vIm;
  }

  get startX() {
    return tileSize * this.i + tileSize / 2;
  }

  get endX() {
    return tileSize * (this.i + velocityI[this.dir]) + tileSize / 2;
  }

  get startY() {
    return tileSize * this.j + tileSize / 2;
  }

  get endY() {
    return tileSize * (this.j + velocityJ[this.dir]) + tileSize / 2;
  }

  get prob() {
    return this.hRe * this.hRe + this.hIm * this.hIm
         + this.vRe * this.vRe + this.vIm * this.vIm;
  }

}

class ParticleAnimation {
  constructor(board, history, measurementHistory, absorptionProbabilities, callback) {

    this.history = history.map((state) => {
      return _.chain(state)
        .groupBy((val) => `${val.i},${val.j},${val.to[0]}`)
        .mapValues((ray) => {
          const rayind = _.indexBy(ray, (val) => val.to[1]);

          const hRe = rayind['-'] ? rayind['-'].re : 0;
          const hIm = rayind['-'] ? rayind['-'].im : 0;
          const vRe = rayind['|'] ? rayind['|'].re : 0;
          const vIm = rayind['|'] ? rayind['|'].im : 0;

          return new Particle(ray[0].i, ray[0].j, ray[0].to[0], hRe, hIm, vRe, vIm);
        })
        .values()
        .value();
    });

    this.measurementHistory = measurementHistory;
    this.absorptionProbabilities = absorptionProbabilities;
    this.animationStepDuration = board.animationStepDuration;
    this.callback = callback;
    this.board = board;
    this.stepNo = 0;
    this.playing = false;
    this.initialized = false;
  }

  initialize() {
    this.measurementTextGroup = this.board.svg
      .append('g')
      .attr('class', 'measurement-texts');
    this.absorptionTextGroup = this.board.svg
      .append('g')
      .attr('class', 'absorption-texts');
    this.initialized = true;
  }

  play() {
    if (!this.initialized) {
      this.initialize();
    }
    if (!this.playing) {
      this.playing = true;
      this.nextFrame();
    }
  }

  stop() {
    this.pause();
    this.measurementTextGroup.remove();
    this.absorptionTextGroup.remove();
    this.initialized = false;
  }

  pause() {
    this.playing = false;
  }

  forward() {
    this.nextFrame();
  }

  backward() {
    throw new Error('backward() unimplemented');
  }

  nextFrame() {
    throw new Error('nextFrame() unimplemented');
  }

  finish() {
    window.setTimeout(
      this.displayAbsorptionTexts.bind(this),
      absorptionDuration
    );
    const lastStep = this.measurementHistory.length - 1;
    window.setTimeout(
      this.displayMeasurementTexts.bind(this, lastStep),
      this.animationStepDuration
    );
    window.setTimeout(
      this.callback.bind(this),
      this.absorptionDuration
    );
    // Make text groups disappear
    window.setTimeout(
      this.stop.bind(this),
      absorptionDuration + absorptionTextDuration
    );
  }

  displayMeasurementTexts(stepNo) {
    _.forEach(this.measurementHistory[stepNo], (measurement) => {
      this.measurementTextGroup.datum(measurement)
        .append('text')
        .attr('class', 'measurement-text')
        .attr('x', (d) => tileSize * d.i + tileSize / 2)
        .attr('y', (d) => tileSize * d.j + tileSize / 2)
        .style('font-size', '20px')
        .text((d) => d.measured ? 'click!' : 'not here...')
        .transition().duration(2 * this.animationStepDuration)
        .style('font-size', '60px')
        .style('opacity', 0)
        .remove();

      this.measurementTextGroup.datum(measurement)
        .each((d) => {
          if (d.measured && d.tile != null) {
            d.tile.absorbSound();
            d.tile.absorbAnimation();
          }
        });
    });

  }

  displayAbsorptionTexts() {
    // TODO(pmigdal): instead of texts - a heatmap of colorful tiles?
    this.absorptionTextGroup
      .selectAll('.absorption-text')
      .data(this.absorptionProbabilities)
      .enter()
      .append('text')
      .attr('class', 'absorption-text')
      .attr('x', (d) => tileSize * d.i + tileSize / 2)
      .attr('y', (d) => tileSize * d.j + tileSize / 2)
      .text((d) => (100 * d.probability).toFixed(0) + '%')
      .transition().duration(absorptionTextDuration)
      .style('opacity', 0)
      .remove();

  }
}

export class SVGParticleAnimation extends ParticleAnimation {
  constructor(board, history, measurementHistory, absorptionProbabilities, callback) {
    super(board, history, measurementHistory, absorptionProbabilities, callback);
    this.particleGroup = null;
    this.currentTimeout = 0;
  }

  pause() {
    super.pause();
    window.clearTimeout(this.currentTimeout);
  }

  stop() {
    super.stop();
    this.exitParticles();
  }

  initialize() {
    super.initialize();
    this.particleGroup = this.board.svg
      .append('g')
      .attr('class', 'particles');
  }

  finish() {
    super.finish();
    this.exitParticles();
  }

  /**
   * Make next frame of animation, possibly setting the timeout for the
   * next frame of animation.
   */
  nextFrame() {
    this.updateParticles();
    this.displayMeasurementTexts(this.stepNo);
    this.stepNo++;

    if (this.stepNo < this.history.length - 1) {
      // Set timeout only if playing
      if (this.playing) {
        this.currentTimeout = window.setTimeout(
          this.nextFrame.bind(this),
          this.animationStepDuration
        );
      }
    } else {
      this.finish();
    }
  }

  updateParticles() {
    const particles = this.particleGroup
      .selectAll('.particle')
      .data(this.history[this.stepNo]);

    particles
      .exit()
      .remove();

    particles
      .enter()
      .append('use')
      .attr({
        'xlink:href': '#particle',
        'class': 'particle',
      });

    particles
      .attr('transform', (d) => `translate(${d.startX},${d.startY})`)
      .style('opacity', (d) => Math.sqrt(d.prob));

    particles
      .interrupt()
      .transition()
      .ease([0, 1])
      .duration(this.animationStepDuration)
      .attrTween('transform', (d) => (t) => {
        const h = polarizationScaleH * (d.hRe * Math.cos(oscillations * TAU * t) + d.hIm * Math.sin(oscillations * TAU * t)) / Math.sqrt(d.prob);
        const x = (1 - t) * d.startX + t * d.endX + perpendicularI[d.dir] * h;
        const y = (1 - t) * d.startY + t * d.endY + perpendicularJ[d.dir] * h;
        const s = 1 + polarizationScaleV * (d.vRe * Math.cos(oscillations * TAU * t) + d.vIm * Math.sin(oscillations * TAU * t)) / Math.sqrt(d.prob);
        return `translate(${x}, ${y}) scale(${s})`;
      });
  };

  exitParticles() {
    this.particleGroup.selectAll('.particle')
      .transition().duration(this.animationStepDuration)
        .style('opacity', 0)
        .delay(this.animationStepDuration)
        .remove();
  }
}

export class CanvasParticleAnimation extends ParticleAnimation {
  constructor(board, history, measurementHistory, absorptionProbabilities, callback) {
    super(board, history, measurementHistory, absorptionProbabilities, callback);
    this.canvas = null;
    this.helperCanvas = null;
    this.ctx = null;
    this.startTime = 0;
    this.pauseTime = 0;
    // Prepare throttled version of resizeCanvas
    this.throttledResizeCanvas =
      _.throttle(this.resizeCanvas, resizeThrottle)
      .bind(this);
  }

  updateStartTime() {
    // If we paused, we have to change startTime for animation to work.
    if (!this.playing && this.startTime <= this.pauseTime) {
      const time = new Date().getTime();
      this.startTime += time - this.pauseTime;
    }
  }

  stop() {
    super.stop();
    window.removeEventListener('resize', this.throttledResizeCanvas);
    if (this.canvas) {
      this.canvas.remove();
    }
    if (this.helperCanvas) {
      this.helperCanvas.remove();
    }
  }

  play() {
    this.updateStartTime();
    super.play();
  }

  forward() {
    this.updateStartTime();
    super.forward();
  }

  initialize() {
    super.initialize();
    // Create canvas, get context
    this.canvas = d3.select('#game')
      .append('canvas');
    this.ctx = this.canvas[0][0].getContext('2d');
    // Similar for helper canvas
    this.helperCanvas = d3.select('#game')
      .append('canvas');
    this.helperCanvas.classed('helper-canvas', true);
    this.helperCtx = this.helperCanvas[0][0].getContext('2d');
    // Stop animation when clicked on canvas
    this.canvas[0][0].addEventListener('click', this.stop.bind(this));
    // Initial canvas resize
    this.resizeCanvas();
    // Set resize event handler
    window.addEventListener('resize', this.throttledResizeCanvas);
    this.startTime = new Date().getTime();
  }

  resizeCanvas() {
    // Get the size of #game > svg > .background element
    const box = this.board.svg.select('.background').node().getBoundingClientRect();
    const resizer = (canvas) => {
      canvas
        .style({
          width:  `${Math.round(box.width)}px`,
          height: `${Math.round(box.height)}px`,
          top:    `${Math.round(box.top)}px`,
          left:   `${Math.round(box.left)}px`,
        })
        .attr({
          width:  this.board.level.width * tileSize,
          height: this.board.level.height * tileSize,
        });
    }
    resizer(this.canvas);
    resizer(this.helperCanvas);
  }

  nextFrame() {
    const time = new Date().getTime();
    const stepFloat = (time - this.startTime) / this.animationStepDuration;
    const oldStepNo = this.stepNo;
    this.stepNo = Math.floor(stepFloat);
    const stepIncreased = this.stepNo > oldStepNo;

    if (this.stepNo < this.history.length - 1) {
      this.updateParticles(stepFloat - this.stepNo);
      if (stepIncreased) {
        this.displayMeasurementTexts(this.stepNo);
      }
      // Request next frame if playing or if the animation didn't manage
      // to get to the keyframe.
      if (this.playing || !stepIncreased) {
        window.requestAnimationFrame(this.nextFrame.bind(this));
      } else {
        this.pauseTime = time;
      }
    } else {
      this.finish();
    }
  }

  /**
   * Draw particles basing on current step (this.stepNo)
   * and t, which represents how far we are in progression
   * from step stepNo to step (stepNo + 1).
   * t is in range [0, 1).
   */
  updateParticles(t) {
    // Copy image to helper context
    this.helperCtx.clearRect(
      0, 0,
      this.board.level.width * tileSize,
      this.board.level.height * tileSize
    );
    this.helperCtx.globalAlpha = 0.97;
    this.helperCtx.drawImage(this.canvas[0][0], 0, 0);
    // Draw image from helper context, a bit faded-out
    this.ctx.clearRect(
      0, 0,
      this.board.level.width * tileSize,
      this.board.level.height * tileSize
    );
    this.ctx.drawImage(this.helperCanvas[0][0], 0, 0);
    // Actual drawing
    this.ctx.fillStyle = 'red';
    _.each(this.history[this.stepNo], (d) => {
      this.ctx.beginPath();
      this.ctx.globalAlpha = d.prob;
      const h = polarizationScaleH * (d.hRe * Math.cos(oscillations * TAU * t) + d.hIm * Math.sin(oscillations * TAU * t)) / Math.sqrt(d.prob);
      const x = (1 - t) * d.startX + t * d.endX + perpendicularI[d.dir] * h;
      const y = (1 - t) * d.startY + t * d.endY + perpendicularJ[d.dir] * h;
      const s = 10 * (
          1 + polarizationScaleV * (
              d.vRe * Math.cos(oscillations * TAU * t)
            + d.vIm * Math.sin(oscillations * TAU * t)
          ) / Math.sqrt(d.prob)
        );
      this.ctx.arc(x, y, s, 0, 360, false);
      this.ctx.fill();
    });
    // Reset alpha
    this.ctx.globalAlpha = 1;
  }
}
<commit_msg>feat(animation): particle fade-out on absorption for CanvasParticleAnimation<commit_after>/*global window:false*/
import _ from 'lodash';
import d3 from 'd3';

import {TAU, velocityI, velocityJ, perpendicularI, perpendicularJ} from './const';
import {tileSize, oscillations, polarizationScaleH, polarizationScaleV, resizeThrottle, absorptionDuration, absorptionTextDuration} from './config';

class Particle {

  constructor(i, j, dir, hRe, hIm, vRe, vIm) {
    this.i = i;
    this.j = j;
    this.dir = dir;
    this.hRe = hRe;
    this.hIm = hIm;
    this.vRe = vRe;
    this.vIm = vIm;
  }

  get startX() {
    return tileSize * this.i + tileSize / 2;
  }

  get endX() {
    return tileSize * (this.i + velocityI[this.dir]) + tileSize / 2;
  }

  get startY() {
    return tileSize * this.j + tileSize / 2;
  }

  get endY() {
    return tileSize * (this.j + velocityJ[this.dir]) + tileSize / 2;
  }

  get prob() {
    return this.hRe * this.hRe + this.hIm * this.hIm
         + this.vRe * this.vRe + this.vIm * this.vIm;
  }

}

class ParticleAnimation {
  constructor(board, history, measurementHistory, absorptionProbabilities, callback) {

    this.history = history.map((state) => {
      return _.chain(state)
        .groupBy((val) => `${val.i},${val.j},${val.to[0]}`)
        .mapValues((ray) => {
          const rayind = _.indexBy(ray, (val) => val.to[1]);

          const hRe = rayind['-'] ? rayind['-'].re : 0;
          const hIm = rayind['-'] ? rayind['-'].im : 0;
          const vRe = rayind['|'] ? rayind['|'].re : 0;
          const vIm = rayind['|'] ? rayind['|'].im : 0;

          return new Particle(ray[0].i, ray[0].j, ray[0].to[0], hRe, hIm, vRe, vIm);
        })
        .values()
        .value();
    });

    this.measurementHistory = measurementHistory;
    this.absorptionProbabilities = absorptionProbabilities;
    this.animationStepDuration = board.animationStepDuration;
    this.callback = callback;
    this.board = board;
    this.stepNo = 0;
    this.playing = false;
    this.initialized = false;
  }

  initialize() {
    this.measurementTextGroup = this.board.svg
      .append('g')
      .attr('class', 'measurement-texts');
    this.absorptionTextGroup = this.board.svg
      .append('g')
      .attr('class', 'absorption-texts');
    this.initialized = true;
  }

  play() {
    if (!this.initialized) {
      this.initialize();
    }
    if (!this.playing) {
      this.playing = true;
      this.nextFrame();
    }
  }

  stop() {
    this.pause();
    this.measurementTextGroup.remove();
    this.absorptionTextGroup.remove();
    this.initialized = false;
  }

  pause() {
    this.playing = false;
  }

  forward() {
    this.nextFrame();
  }

  backward() {
    throw new Error('backward() unimplemented');
  }

  nextFrame() {
    throw new Error('nextFrame() unimplemented');
  }

  finish() {
    window.setTimeout(
      this.displayAbsorptionTexts.bind(this),
      absorptionDuration
    );
    const lastStep = this.measurementHistory.length - 1;
    window.setTimeout(
      this.displayMeasurementTexts.bind(this, lastStep),
      this.animationStepDuration
    );
    window.setTimeout(
      this.callback.bind(this),
      this.absorptionDuration
    );
    // Make text groups disappear
    window.setTimeout(
      this.stop.bind(this),
      absorptionDuration + absorptionTextDuration
    );
  }

  displayMeasurementTexts(stepNo) {
    _.forEach(this.measurementHistory[stepNo], (measurement) => {
      this.measurementTextGroup.datum(measurement)
        .append('text')
        .attr('class', 'measurement-text')
        .attr('x', (d) => tileSize * d.i + tileSize / 2)
        .attr('y', (d) => tileSize * d.j + tileSize / 2)
        .style('font-size', '20px')
        .text((d) => d.measured ? 'click!' : 'not here...')
        .transition().duration(2 * this.animationStepDuration)
        .style('font-size', '60px')
        .style('opacity', 0)
        .remove();

      this.measurementTextGroup.datum(measurement)
        .each((d) => {
          if (d.measured && d.tile != null) {
            d.tile.absorbSound();
            d.tile.absorbAnimation();
          }
        });
    });

  }

  displayAbsorptionTexts() {
    // TODO(pmigdal): instead of texts - a heatmap of colorful tiles?
    this.absorptionTextGroup
      .selectAll('.absorption-text')
      .data(this.absorptionProbabilities)
      .enter()
      .append('text')
      .attr('class', 'absorption-text')
      .attr('x', (d) => tileSize * d.i + tileSize / 2)
      .attr('y', (d) => tileSize * d.j + tileSize / 2)
      .text((d) => (100 * d.probability).toFixed(0) + '%')
      .transition().duration(absorptionTextDuration)
      .style('opacity', 0)
      .remove();

  }
}

export class SVGParticleAnimation extends ParticleAnimation {
  constructor(board, history, measurementHistory, absorptionProbabilities, callback) {
    super(board, history, measurementHistory, absorptionProbabilities, callback);
    this.particleGroup = null;
    this.currentTimeout = 0;
  }

  pause() {
    super.pause();
    window.clearTimeout(this.currentTimeout);
  }

  stop() {
    super.stop();
    this.exitParticles();
  }

  initialize() {
    super.initialize();
    this.particleGroup = this.board.svg
      .append('g')
      .attr('class', 'particles');
  }

  finish() {
    super.finish();
    this.exitParticles();
  }

  /**
   * Make next frame of animation, possibly setting the timeout for the
   * next frame of animation.
   */
  nextFrame() {
    this.updateParticles();
    this.displayMeasurementTexts(this.stepNo);
    this.stepNo++;

    if (this.stepNo < this.history.length - 1) {
      // Set timeout only if playing
      if (this.playing) {
        this.currentTimeout = window.setTimeout(
          this.nextFrame.bind(this),
          this.animationStepDuration
        );
      }
    } else {
      this.finish();
    }
  }

  updateParticles() {
    const particles = this.particleGroup
      .selectAll('.particle')
      .data(this.history[this.stepNo]);

    particles
      .exit()
      .remove();

    particles
      .enter()
      .append('use')
      .attr({
        'xlink:href': '#particle',
        'class': 'particle',
      });

    particles
      .attr('transform', (d) => `translate(${d.startX},${d.startY})`)
      .style('opacity', (d) => Math.sqrt(d.prob));

    particles
      .interrupt()
      .transition()
      .ease([0, 1])
      .duration(this.animationStepDuration)
      .attrTween('transform', (d) => (t) => {
        const h = polarizationScaleH * (d.hRe * Math.cos(oscillations * TAU * t) + d.hIm * Math.sin(oscillations * TAU * t)) / Math.sqrt(d.prob);
        const x = (1 - t) * d.startX + t * d.endX + perpendicularI[d.dir] * h;
        const y = (1 - t) * d.startY + t * d.endY + perpendicularJ[d.dir] * h;
        const s = 1 + polarizationScaleV * (d.vRe * Math.cos(oscillations * TAU * t) + d.vIm * Math.sin(oscillations * TAU * t)) / Math.sqrt(d.prob);
        return `translate(${x}, ${y}) scale(${s})`;
      });
  };

  exitParticles() {
    this.particleGroup.selectAll('.particle')
      .transition().duration(this.animationStepDuration)
        .style('opacity', 0)
        .delay(this.animationStepDuration)
        .remove();
  }
}

export class CanvasParticleAnimation extends ParticleAnimation {
  constructor(board, history, measurementHistory, absorptionProbabilities, callback) {
    super(board, history, measurementHistory, absorptionProbabilities, callback);
    this.canvas = null;
    this.helperCanvas = null;
    this.ctx = null;
    this.startTime = 0;
    this.pauseTime = 0;
    // Prepare throttled version of resizeCanvas
    this.throttledResizeCanvas =
      _.throttle(this.resizeCanvas, resizeThrottle)
      .bind(this);
  }

  updateStartTime() {
    // If we paused, we have to change startTime for animation to work.
    if (!this.playing && this.startTime <= this.pauseTime) {
      const time = new Date().getTime();
      this.startTime += time - this.pauseTime;
    }
  }

  stop() {
    super.stop();
    window.removeEventListener('resize', this.throttledResizeCanvas);
    if (this.canvas) {
      this.canvas.remove();
    }
    if (this.helperCanvas) {
      this.helperCanvas.remove();
    }
  }

  play() {
    this.updateStartTime();
    super.play();
  }

  forward() {
    this.updateStartTime();
    super.forward();
  }

  initialize() {
    super.initialize();
    // Create canvas, get context
    this.canvas = d3.select('#game')
      .append('canvas');
    this.ctx = this.canvas[0][0].getContext('2d');
    // Similar for helper canvas
    this.helperCanvas = d3.select('#game')
      .append('canvas');
    this.helperCanvas.classed('helper-canvas', true);
    this.helperCtx = this.helperCanvas[0][0].getContext('2d');
    // Stop animation when clicked on canvas
    this.canvas[0][0].addEventListener('click', this.stop.bind(this));
    // Initial canvas resize
    this.resizeCanvas();
    // Set resize event handler
    window.addEventListener('resize', this.throttledResizeCanvas);
    this.startTime = new Date().getTime();
  }

  resizeCanvas() {
    // Get the size of #game > svg > .background element
    const box = this.board.svg.select('.background').node().getBoundingClientRect();
    const resizer = (canvas) => {
      canvas
        .style({
          width:  `${Math.round(box.width)}px`,
          height: `${Math.round(box.height)}px`,
          top:    `${Math.round(box.top)}px`,
          left:   `${Math.round(box.left)}px`,
        })
        .attr({
          width:  this.board.level.width * tileSize,
          height: this.board.level.height * tileSize,
        });
    }
    resizer(this.canvas);
    resizer(this.helperCanvas);
  }

  nextFrame() {
    const time = new Date().getTime();
    const stepFloat = (time - this.startTime) / this.animationStepDuration;
    const oldStepNo = this.stepNo;
    this.stepNo = Math.floor(stepFloat);
    const stepIncreased = this.stepNo > oldStepNo;

    if (this.stepNo < this.history.length - 1) {
      this.updateParticles(stepFloat - this.stepNo);
      if (stepIncreased) {
        this.displayMeasurementTexts(this.stepNo);
      }
      // Request next frame if playing or if the animation didn't manage
      // to get to the keyframe.
      if (this.playing || !stepIncreased) {
        window.requestAnimationFrame(this.nextFrame.bind(this));
      } else {
        this.pauseTime = time;
      }
    } else {
      this.finish();
    }
  }

  /**
   * Draw particles basing on current step (this.stepNo)
   * and t, which represents how far we are in progression
   * from step stepNo to step (stepNo + 1).
   * t is in range [0, 1).
   */
  updateParticles(t) {
    this.clearAlpha(0.95);
    // Actual drawing
    this.ctx.fillStyle = 'red';
    _.each(this.history[this.stepNo], (d) => {
      this.ctx.beginPath();
      this.ctx.globalAlpha = d.prob;
      const h = polarizationScaleH * (d.hRe * Math.cos(oscillations * TAU * t) + d.hIm * Math.sin(oscillations * TAU * t)) / Math.sqrt(d.prob);
      const x = (1 - t) * d.startX + t * d.endX + perpendicularI[d.dir] * h;
      const y = (1 - t) * d.startY + t * d.endY + perpendicularJ[d.dir] * h;
      const s = 10 * (
          1 + polarizationScaleV * (
              d.vRe * Math.cos(oscillations * TAU * t)
            + d.vIm * Math.sin(oscillations * TAU * t)
          ) / Math.sqrt(d.prob)
        );
      this.ctx.arc(x, y, s, 0, 360, false);
      this.ctx.fill();
    });
  }

  finish() {
    super.finish();
    window.setInterval(() => { this.clearAlpha(0.8); }, 50, 20);
    window.setTimeout(() => { this.clearAlpha(0); }, 1100);
  }

  /**
   * clearRect with alpha support.
   * alpha - how much (in terms of transparency) of previous frame stays.
   * clearAlpha(0) should work like clearRect().
   */
   clearAlpha(alpha) {
     // Reset alpha
     this.ctx.globalAlpha = 1;
     // Copy image to helper context
     if (alpha > 0) {
       this.helperCtx.clearRect(
         0, 0,
         this.board.level.width * tileSize,
         this.board.level.height * tileSize
       );
       this.helperCtx.globalAlpha = alpha;
       this.helperCtx.drawImage(this.canvas[0][0], 0, 0);
     }
     // Draw image from helper context, a bit faded-out
     this.ctx.clearRect(
       0, 0,
       this.board.level.width * tileSize,
       this.board.level.height * tileSize
     );
     if (alpha > 0) {
       this.ctx.drawImage(this.helperCanvas[0][0], 0, 0);
     }
   }
}
<|endoftext|>


<commit_before>(function () {
    /** Pixelator class. **/

    function Pixelator(imageData) {
        if (!validateProps(imageData, Object, 'width', 'number', 'height', 'number', 'data', Uint8ClampedArray)) {
            throw new TypeError('The first argument passed into the Pixelator constructor was not a valid ImageData object.');
        }

        this.width = imageData.width;
        this.height = imageData.height;
        this.data = imageData.data;

        this.canvas_ = document.createElement('canvas');
        this.canvas_.width = this.width;
        this.canvas_.height = this.height;

        this.ctx_ = this.canvas_.getContext('2d');
        this.ctx_.putImageData(imageData, 0, 0);
    }

    Pixelator.prototype.pixelate = function (sectionWidth, sectionHeight) {
        var sections = this.getAllSections(sectionWidth, sectionHeight),
            canvas = document.createElement('canvas'),
            ctx = canvas.getContext('2d'),
            i = sections.length;

        canvas.width = this.width;
        canvas.height = this.height;

        while (i--) {
            var section = sections[i],
                average = this.getAverageColor(section);

            ctx.fillStyle = 'rgba(' + average.r + ',' + average.g + ',' + average.b + ',' + average.a / 255 + ')';
            ctx.fillRect(section.x, section.y, section.width, section.height);
        }

        return ctx;
    };

    Pixelator.prototype.getAllSections = function (sectionWidth, sectionHeight) {
        var sections = [],

            rightEdgeSectionWidth = this.width % sectionWidth,
            bottomEdgeSectionHeight = this.height % sectionHeight,

            rightEdgeSectionX = this.width - rightEdgeSectionWidth,
            bottomEdgeSectionY = this.height - bottomEdgeSectionHeight;

        function addAllSectionsForRow(y, height) {
            sections.push(new Section(rightEdgeSectionX, y, rightEdgeSectionWidth, height));

            var x = rightEdgeSectionX - sectionWidth;

            while (x >= 0) {
                sections.push(new Section(x, y, sectionWidth, height));

                x -= sectionWidth;
            }
        }

        addAllSectionsForRow(bottomEdgeSectionY, bottomEdgeSectionHeight);

        var y = bottomEdgeSectionY - sectionHeight;

        while (y >= 0) {
            addAllSectionsForRow(y, sectionHeight);

            y -= sectionHeight;
        }

        return sections;
    };

    Pixelator.prototype.getAverageColor = function (section) {
        var data = this.ctx_.getImageData(section.x, section.y, section.width, section.height).data,

            sumColor = {r: 0, g: 0, b: 0, a: 0},
            avgColor = {},

            len = data.length,
            numOfPixels = len / 4,
            i = 0;//needs serious fixing

        while (i < len) {
            sumColor.r += data[i++];
            sumColor.g += data[i++];
            sumColor.b += data[i++];
            sumColor.a += data[i++];
        }

        avgColor.r = sumColor.r / numOfPixels;
        avgColor.g = sumColor.g / numOfPixels;
        avgColor.b = sumColor.b / numOfPixels;
        avgColor.a = sumColor.a / numOfPixels;

        return avgColor;
    };

    Pixelator.prototype.filters_ = [];

    /** Section class. **/

    function Section(x, y, w, h) {
        this.x = x;
        this.y = y;
        this.width = w;
        this.height = h;
    }

    /** Miscellaneous utilities. **/

    function validateProps(obj, constructor) {
        if (!(obj instanceof Object) || typeof constructor !== 'function' || !(obj instanceof constructor)) {
            return false;
        }

        var numOfArgs = arguments.length;

        for (var i = 2; i < numOfArgs; i += 2) {
            var prop = obj[arguments[i]],
                propType = arguments[i + 1],
                propTypeType = typeof propType;

            if (
                (propTypeType === 'function' && !(prop instanceof propType)) ||
                (propTypeType === 'string' && typeof prop !== propType) ||
                (propTypeType !== 'function' && propTypeType !== 'string')
            ) {
                return false;
            }
        }

        return true;
    }

    function multiValueTypeCheck(type) {
        var typeType = typeof type,
            i = arguments.length;

        if (typeType === 'function') {
            while (--i) { // Skip first argument (which is the type argument).
                if (!(arguments[i] instanceof type)) {
                    return false;
                }
            }
        } else if (typeType === 'string') {
            while (--i) {
                if (typeof arguments[i] !== type) {
                    return false;
                }
            }
        } else return true;
    }

    window['Pixelator'] = Pixelator;
})();
<commit_msg>Update pixelator.js<commit_after>(function () {
    /** Pixelator class. **/

    function Pixelator(imageData) {
        if (!validateProps(imageData, Object, 'width', 'number', 'height', 'number', 'data', Uint8ClampedArray)) {
            throw new TypeError('The first argument passed into the Pixelator constructor was not a valid ImageData object.');
        }

        this.width = imageData.width;
        this.height = imageData.height;
        this.data = imageData.data;

        this.canvas_ = document.createElement('canvas');
        this.canvas_.width = this.width;
        this.canvas_.height = this.height;

        this.ctx_ = this.canvas_.getContext('2d');
        this.ctx_.putImageData(imageData, 0, 0);
    }

    Pixelator.prototype.pixelate = function (sectionWidth, sectionHeight) {
        var sections = this.getAllSections(sectionWidth, sectionHeight),
            canvas = document.createElement('canvas'),
            ctx = canvas.getContext('2d'),
            i = sections.length;

        canvas.width = this.width;
        canvas.height = this.height;

        while (i--) {
            var section = sections[i],
                average = this.getAverageColor(section);

            ctx.fillStyle = 'rgba(' + average.r + ',' + average.g + ',' + average.b + ',' + average.a / 255 + ')';
            ctx.fillRect(section.x, section.y, section.width, section.height);
        }

        return ctx;
    };

    Pixelator.prototype.getAllSections = function (sectionWidth, sectionHeight) {
        var sections = [],

            rightEdgeSectionWidth = (this.width % sectionWidth) || sectionWidth,
            bottomEdgeSectionHeight = (this.height % sectionHeight) || sectionHeight,

            rightEdgeSectionX = this.width - rightEdgeSectionWidth,
            bottomEdgeSectionY = this.height - bottomEdgeSectionHeight;

        function addAllSectionsForRow(y, height) {
            sections.push(new Section(rightEdgeSectionX, y, rightEdgeSectionWidth, height));

            var x = rightEdgeSectionX - sectionWidth;

            while (x >= 0) {
                sections.push(new Section(x, y, sectionWidth, height));

                x -= sectionWidth;
            }
        }

        addAllSectionsForRow(bottomEdgeSectionY, bottomEdgeSectionHeight);

        var y = bottomEdgeSectionY - sectionHeight;

        while (y >= 0) {
            addAllSectionsForRow(y, sectionHeight);

            y -= sectionHeight;
        }

        return sections;
    };

    Pixelator.prototype.getAverageColor = function (section) {
        var data = this.ctx_.getImageData(section.x, section.y, section.width, section.height).data,

            sumColor = {r: 0, g: 0, b: 0, a: 0},
            avgColor = {},

            len = data.length,
            numOfPixels = len / 4,
            i = 0;//needs serious fixing

        while (i < len) {
            sumColor.r += data[i++];
            sumColor.g += data[i++];
            sumColor.b += data[i++];
            sumColor.a += data[i++];
        }

        avgColor.r = sumColor.r / numOfPixels;
        avgColor.g = sumColor.g / numOfPixels;
        avgColor.b = sumColor.b / numOfPixels;
        avgColor.a = sumColor.a / numOfPixels;

        return avgColor;
    };

    Pixelator.prototype.filters_ = [];

    /** Section class. **/

    function Section(x, y, w, h) {
        this.x = x;
        this.y = y;
        this.width = w;
        this.height = h;
    }

    /** Miscellaneous utilities. **/

    function validateProps(obj, constructor) {
        if (!(obj instanceof Object) || typeof constructor !== 'function' || !(obj instanceof constructor)) {
            return false;
        }

        var numOfArgs = arguments.length;

        for (var i = 2; i < numOfArgs; i += 2) {
            var prop = obj[arguments[i]],
                propType = arguments[i + 1],
                propTypeType = typeof propType;

            if (
                (propTypeType === 'function' && !(prop instanceof propType)) ||
                (propTypeType === 'string' && typeof prop !== propType) ||
                (propTypeType !== 'function' && propTypeType !== 'string')
            ) {
                return false;
            }
        }

        return true;
    }

    window['Pixelator'] = Pixelator;
})();
<|endoftext|>


<commit_before>// ajax from http://stackoverflow.com/questions/18251632/another-force-chrome-to-fully-buffer-mp4-video

console.log("Downloading videos...Please wait...")

var myVids=[
   "videos/01-16s_h264.mov",
   "videos/02-18s_h264.mov",
   "videos/03-13s_h264.mov",
   "videos/04-13s_h264.mov",
   "videos/05-22s_h264.mov",
]

var myVidsWebm=[
   "videos/01-16s_h264.webm",
   "videos/02-18s_h264.webm",
   "videos/03-13s_h264.webm",
   "videos/04-13s_h264.webm",
   "videos/05-22s_h264.webm",
]

var myVidsOgg=[
   "videos/01-16s_h264.ogv",
   "videos/02-18s_h264.ogv",
   "videos/03-13s_h264.ogv",
   "videos/04-13s_h264.ogv",
   "videos/05-22s_h264.ogv",
]

var vidElements = [];

console.log("modernizr video h264 support? " + Modernizr.video.h264);
console.log("modernizr video ogg support? " + Modernizr.video.ogg);
console.log("modernizr video webm support? " + Modernizr.video.webm);

function checkLoad(){
  var percentLoaded = 0;
  var timeoutID = setInterval(function(){
    console.log("videlements.length: "+vidElements.length);
    console.log("myVids.length: "+myVids.length);
    if(percentLoaded < 100){
      percentLoaded = (vidElements.length+1) / (myVids.length+1) * 100;
      $('progress').val(percentLoaded);
    }
    else{
      console.log("percentLoaded: "+percentLoaded);
      clearTimeout(timeoutID);
      $('progress').val(percentLoaded);
      document.getElementById("loading").style.display="none";
      player();
    }
  }, 400);}

var player =
  function(){
    var videoPlayer = document.getElementById("video_player");
    var vindex = 0;
    var theVideo = document.getElementById("video"+vindex);

    videoPlayer.style.display="table";
    theVideo.style.display="block";

    var prevBtn = document.getElementById("btn_prev");
    var nextBtn = document.getElementById("btn_next");

    // iterate through video playlist backwards
    prevBtn.addEventListener("click", function(){
      if(vindex == 0){
        prevBtn.disabled = true;
      }
      else if (vindex == 1) {
        theVideo.style.display="none";
        vindex--;
        theVideo = document.getElementById("video"+vindex);
        theVideo.currentTime=0;
        theVideo.style.display="block";
        theVideo.pause();
        prevBtn.disabled = true;
      }
      else{
        theVideo.style.display="none";
        vindex--;
        theVideo = document.getElementById("video"+vindex);
        theVideo.currentTime=0;
        theVideo.style.display="block";
        theVideo.pause();
        nextBtn.disabled = false;
      }
    })
    // iterate through video playlist forwards
    nextBtn.addEventListener("click", function(){
      if(vindex == vidElements.length-1){
        nextBtn.disabled = true;
      }
      else if(vindex == vidElements.length-2){
        theVideo.style.display="none";
        vindex++;
        theVideo = document.getElementById("video"+vindex);
        theVideo.currentTime=0;
        theVideo.style.display="block";
        theVideo.pause();
        prevBtn.disabled = false;
        nextBtn.disabled = true;
      }
      else{
        theVideo.style.display="none";
        vindex++;
        theVideo = document.getElementById("video"+vindex);
        theVideo.currentTime=0;
        theVideo.style.display="block";
        theVideo.pause();
        prevBtn.disabled = false;
      }
    })
  }

var preLoader = function(){
  for (var i = 0; i < myVids.length; i++) {
    if(Modernizr.video.h264){
    	console.log("video " +i+ " filename: " + (myVids[i]));
    	loadVid(myVids, "video/h264", i);

    }
    else if(Modernizr.video.webm){
    	console.log("video " +i+ " filename: " + (myVidsWebm[i]));
    	loadVid(myVidsWebm, "video/webm", i);
    }
    else if(Modernizr.video.ogg){
    	console.log("video " +i+ " filename: " + (myVidsOgg[i]));
    	loadVid(myVidsOgg, "video/ogg", i);
    }
    else{
    	console.log("no html5 video support :( consider upgrading to a modern browser");
    }
  }
  checkLoad();
}

preLoader();

function loadVid(vidArray, vidType, i){
	var elementID = "video"+i;
    console.log("element id: "+elementID);
    var xhr = new XMLHttpRequest();
    xhr.open('GET', vidArray[i], true);
    xhr.responseType = 'blob';
    xhr.onload = function(e) {
    if (this.status == 200) {
    	console.log("got it");
    	var myBlob = this.response;
    	var vid = (window.webkitURL ? webkitURL : URL).createObjectURL(myBlob);
        // myBlob is now the blob that the object URL pointed to.
        var video = document.getElementById(elementID);
        video.style.display="none";
        console.log("Loading video "+i+" into element");
        video.src = vid;
        video.type=vidType;
        video.controls=true;
        vidElements.push(video);
   }
  }
  xhr.send();

}

<commit_msg>removed ogg/theora support<commit_after>// ajax from http://stackoverflow.com/questions/18251632/another-force-chrome-to-fully-buffer-mp4-video

console.log("Downloading videos...Please wait...")

var myVids=[
   "videos/01-16s_h264.mov",
   "videos/02-18s_h264.mov",
   "videos/03-13s_h264.mov",
   "videos/04-13s_h264.mov",
   "videos/05-22s_h264.mov",
]

var myVidsWebm=[
   "videos/01-16s_h264.webm",
   "videos/02-18s_h264.webm",
   "videos/03-13s_h264.webm",
   "videos/04-13s_h264.webm",
   "videos/05-22s_h264.webm",
]

var vidElements = [];

console.log("modernizr video h264 support? " + Modernizr.video.h264);
console.log("modernizr video ogg support? " + Modernizr.video.ogg);
console.log("modernizr video webm support? " + Modernizr.video.webm);

function checkLoad(){
  var percentLoaded = 0;
  var timeoutID = setInterval(function(){
    console.log("videlements.length: "+vidElements.length);
    console.log("myVids.length: "+myVids.length);
    if(percentLoaded < 100){
      percentLoaded = (vidElements.length+1) / (myVids.length+1) * 100;
      $('progress').val(percentLoaded);
    }
    else{
      console.log("percentLoaded: "+percentLoaded);
      clearTimeout(timeoutID);
      $('progress').val(percentLoaded);
      document.getElementById("loading").style.display="none";
      player();
    }
  }, 400);}

var player =
  function(){
    var videoPlayer = document.getElementById("video_player");
    var vindex = 0;
    var theVideo = document.getElementById("video"+vindex);

    videoPlayer.style.display="table";
    theVideo.style.display="block";

    var prevBtn = document.getElementById("btn_prev");
    var nextBtn = document.getElementById("btn_next");

    // iterate through video playlist backwards
    prevBtn.addEventListener("click", function(){
      if(vindex == 0){
        prevBtn.disabled = true;
      }
      else if (vindex == 1) {
        theVideo.style.display="none";
        vindex--;
        theVideo = document.getElementById("video"+vindex);
        theVideo.currentTime=0;
        theVideo.style.display="block";
        theVideo.pause();
        prevBtn.disabled = true;
      }
      else{
        theVideo.style.display="none";
        vindex--;
        theVideo = document.getElementById("video"+vindex);
        theVideo.currentTime=0;
        theVideo.style.display="block";
        theVideo.pause();
        nextBtn.disabled = false;
      }
    })
    // iterate through video playlist forwards
    nextBtn.addEventListener("click", function(){
      if(vindex == vidElements.length-1){
        nextBtn.disabled = true;
      }
      else if(vindex == vidElements.length-2){
        theVideo.style.display="none";
        vindex++;
        theVideo = document.getElementById("video"+vindex);
        theVideo.currentTime=0;
        theVideo.style.display="block";
        theVideo.pause();
        prevBtn.disabled = false;
        nextBtn.disabled = true;
      }
      else{
        theVideo.style.display="none";
        vindex++;
        theVideo = document.getElementById("video"+vindex);
        theVideo.currentTime=0;
        theVideo.style.display="block";
        theVideo.pause();
        prevBtn.disabled = false;
      }
    })
  }

var preLoader = function(){
  for (var i = 0; i < myVids.length; i++) {
    if(Modernizr.video.h264){
    	console.log("video " +i+ " filename: " + (myVids[i]));
    	loadVid(myVids, "video/h264", i);

    }
    else if(Modernizr.video.webm){
    	console.log("video " +i+ " filename: " + (myVidsWebm[i]));
    	loadVid(myVidsWebm, "video/webm", i);
    }
    else{
    	console.log("no html5 video support :( consider upgrading to a modern browser");
    }
  }
  checkLoad();
}

preLoader();

function loadVid(vidArray, vidType, i){
	var elementID = "video"+i;
    console.log("element id: "+elementID);
    var xhr = new XMLHttpRequest();
    xhr.open('GET', vidArray[i], true);
    xhr.responseType = 'blob';
    xhr.onload = function(e) {
    if (this.status == 200) {
    	console.log("got it");
    	var myBlob = this.response;
    	var vid = (window.webkitURL ? webkitURL : URL).createObjectURL(myBlob);
        // myBlob is now the blob that the object URL pointed to.
        var video = document.getElementById(elementID);
        video.style.display="none";
        console.log("Loading video "+i+" into element");
        video.src = vid;
        video.type=vidType;
        video.controls=true;
        vidElements.push(video);
   }
  }
  xhr.send();

}

<|endoftext|>