<commit_before>'use babel';
'use strict';
import fs from 'fs';
import path from 'path';
import os from 'os';
import glob from 'glob';
import {spawn} from 'child_process';

/**
 */
export default class CMake {
  /**
   *@param {path} executable
   */
  constructor(executable) {
    this.executable = executable.trim();
    this.generators = null;
  }

  /**
   * @param {string} command
   * @param {Array} commandArguments
   * @param {regex} outputRegex
   * @param {function} callback
   * @param {string} outputMessage
   * @return {Promise}
   */
  grepStdout(command, commandArguments, outputRegex, callback, outputMessage) {
    return new Promise((resolve, reject) => {
      let child = spawn(command, commandArguments, {shell: false});
      let stdout = '';
      let stderr = '';
      child.stdout.on('data', (data) => {
        stdout = stdout + data.toString('utf8');
      });
      child.stderr.on('data', (data) => {
        stderr = stderr + data.toString('utf8');
      });
      child.on('close', (code) => {
        if (code !== 0) {
          reject(stderr.replace('/bin/sh: ', '').trim() + '!');
        } else {
          let matches = [];
          let match = outputRegex.exec(stdout);
          while (match != null) {
            matches = matches.concat(callback(match));
            match = outputRegex.exec(stdout);
          }
          if (outputMessage != null && matches.length <= 0)
            reject(outputMessage);
          else
            resolve(matches);
        }
      });
    });
  }

  /**
   * @return {Promise}
   */
  validateExecutable() {
    const that = this;
    if (that.generators == null) {
      return that
          .grepStdout(
              that.executable, ['--help'],
              /\s{3}(.*)\s*=\sGenerates(.*\.|.*\s*.*\.)(\s*Optional\s\[arch\]\scan\sbe\s(\".*\").)?/g,
              (match) => {
                if (match[4]) {
                  const arches = match[4].split('or').map(
                      (arch) => arch.replace(/\"/g, ''));
                  return arches.map(
                      (arch) => match[1].replace(/\[arch\]/g, arch).trim());
                } else {
                  return [match[1].trim()];
                }
              },
              'The executable "' + that.executable +
                  '" failed to produce generators!')
          .then((generators) => {
            that.generators = generators;
          });
    }
  }

  /**
   * @param {path} cachePath
   * @return {Promise}
   */
  extractGeneratorFromCache(cachePath) {
    return new Promise(function(resolve, reject) {
      fs.readFile(cachePath, 'utf8', function(err, cache) {
        if (err) {
          reject(err);
        } else {
          const matches = cache.match(/CMAKE_GENERATOR:INTERNAL=(.*)/);
          if (matches.length != 2)
            reject('Could not find generator in cmake cache!');
          else
            resolve(matches[1]);
        }
      });
    });
  }

  /**
   * @param {string} targetName
   * @param {path} sourceDirectory
   * @param {path} buildDirectory
   * @param {boolean} parallelBuild
   * @param {Array} extraBuildArguments
   * @return {target}
   */
  createMakeFileTarget(
      targetName, sourceDirectory, buildDirectory, parallelBuild,
      extraBuildArguments) {
    argumentList = ['--build', buildDirectory, '--target', targetName, '--'];
    if (parallelBuild) argumentList.push('-j' + os.cpus().length);
    return {
      name: targetName,
      exec: this.executable,
      cwd: sourceDirectory,
      args: argumentList.concat(extraBuildArguments),
    };
  }

  /**
   * @param {path} sourceDirectory
   * @param {path} buildDirectory
   * @param {boolean} parallelBuild
   * @param {Array} extraBuildArguments
   * @return {Promise}
   */
  makeFileTargets(
      sourceDirectory, buildDirectory, parallelBuild, extraBuildArguments) {
    const that = this;
    return that.grepStdout(
        that.executable, ['--build', buildDirectory, '--target', 'help'],
        /\.{3}\s(.[^\s]+)/g, (match) => {
          return [that.createMakeFileTarget(
              match[1].trim(), sourceDirectory, buildDirectory, parallelBuild,
              extraBuildArguments)];
        });
  }

  /**
   * @param {string} targetName
   * @param {path} sourceDirectory
   * @param {path} buildDirectory
   * @param {boolean} parallelBuild
   * @param {Array} extraBuildArguments
   * @return {target}
   */
  ninjaTarget(
      targetName, sourceDirectory, buildDirectory, parallelBuild,
      extraBuildArguments) {
    argumentList = ['--build', buildDirectory, '--target', targetName, '--'];
    if (parallelBuild) argumentList.push('-j' + os.cpus().length);
    return {
      name: targetName,
      exec: this.executable,
      cwd: sourceDirectory,
      args: argumentList.concat(extraBuildArguments),
    };
  }

  /**
   * @param {path} sourceDirectory
   * @param {path} buildDirectory
   * @param {boolean} parallelBuild
   * @param {Array} extraBuildArguments
   * @return {Promise}
   */
  ninjaTargets(
      sourceDirectory, buildDirectory, parallelBuild, extraBuildArguments) {
    const that = this;
    return that.grepStdout(
        that.executable, ['--build', buildDirectory, '--', '-t', 'targets'],
        /(.+):/g, (match) => {
          return [that.ninjaTarget(
              match[1].trim(), sourceDirectory, buildDirectory, parallelBuild,
              extraBuildArguments)];
        });
  }

  /**
   * @param {string} targetName
   * @param {path} sourceDirectory
   * @param {path} buildDirectory
   * @param {boolean} parallelBuild
   * @param {Array} extraBuildArguments
   * @return {target}
   */
  createVisualStudioTarget(
      targetName, sourceDirectory, buildDirectory, parallelBuild,
      extraBuildArguments) {
    argumentList = ['--build', buildDirectory, '--target', targetName, '--'];
    if (parallelBuild) argumentList.push('/maxcpucount');
    return {
      name: targetName,
      exec: this.executable,
      cwd: sourceDirectory,
      args: argumentList.concat(extraBuildArguments),
    };
  }

  /**
   * @param {path} sourceDirectory
   * @param {path} buildDirectory
   * @param {boolean} parallelBuild
   * @param {Array} extraBuildArguments
   * @return {Promise}
   */
  visualStudioTargets(
      sourceDirectory, buildDirectory, parallelBuild, extraBuildArguments) {
    const that = this;
    return new Promise(function(resolve, reject) {
      glob(
          '**/*.vcxproj', {
            cwd: sourceDirectory,
            nodir: true,
            ignore: 'CMakeFiles/**',
          },
          (error, files) => {
            if (error)
              reject(error);
            else
              resolve(files.map((file) => path.basename(file, '.vcxproj'))
                          .concat(['clean'])
                          .map(
                              (targetName) => that.createVisualStudioTarget(
                                  targetName, buildDirectory, parallelBuild,
                                  extraBuildArguments)));
          });
    });
  }

  /**
   * @param {path} sourceDirectory
   * @param {path} buildDirectory
   * @param {boolean} parallelBuild
   * @param {Array} extraBuildArguments
   * @return {Promise}
   */
  extractTargets(
      sourceDirectory, buildDirectory, parallelBuild, extraBuildArguments) {
    const that = this;
    return that
        .extractGeneratorFromCache(path.join(buildDirectory, 'CMakeCache.txt'))
        .then((generator) => {
          if (generator.match('Visual Studio'))
            return that
                .visualStudioTargets(
                    sourceDirectory, buildDirectory, parallelBuild,
                    extraBuildArguments)
                .then((targets) => {
                  return {generator: generator, targets: targets};
                });
          else if (generator.match('Ninja'))
            return that
                .ninjaTargets(
                    sourceDirectory, buildDirectory, parallelBuild,
                    extraBuildArguments)
                .then((targets) => {
                  return {generator: generator, targets: targets};
                });
          else if (generator.match('Unix Makefiles'))
            return that
                .makeFileTargets(
                    sourceDirectory, buildDirectory, parallelBuild,
                    extraBuildArguments)
                .then((targets) => {
                  return {generator: generator, targets: targets};
                });
        });
  }
};
<commit_msg>Fixed Visual Studio targets.<commit_after>'use babel';
'use strict';
import fs from 'fs';
import path from 'path';
import os from 'os';
import glob from 'glob';
import {spawn} from 'child_process';

/**
 */
export default class CMake {
  /**
   *@param {path} executable
   */
  constructor(executable) {
    this.executable = executable.trim();
    this.generators = null;
  }

  /**
   * @param {string} command
   * @param {Array} commandArguments
   * @param {regex} outputRegex
   * @param {function} callback
   * @param {string} outputMessage
   * @return {Promise}
   */
  grepStdout(command, commandArguments, outputRegex, callback, outputMessage) {
    return new Promise((resolve, reject) => {
      let child = spawn(command, commandArguments, {shell: false});
      let stdout = '';
      let stderr = '';
      child.stdout.on('data', (data) => {
        stdout = stdout + data.toString('utf8');
      });
      child.stderr.on('data', (data) => {
        stderr = stderr + data.toString('utf8');
      });
      child.on('close', (code) => {
        if (code !== 0) {
          reject(stderr.replace('/bin/sh: ', '').trim() + '!');
        } else {
          let matches = [];
          let match = outputRegex.exec(stdout);
          while (match != null) {
            matches = matches.concat(callback(match));
            match = outputRegex.exec(stdout);
          }
          if (outputMessage != null && matches.length <= 0)
            reject(outputMessage);
          else
            resolve(matches);
        }
      });
    });
  }

  /**
   * @return {Promise}
   */
  validateExecutable() {
    const that = this;
    if (that.generators == null) {
      return that
          .grepStdout(
              that.executable, ['--help'],
              /\s{3}(.*)\s*=\sGenerates(.*\.|.*\s*.*\.)(\s*Optional\s\[arch\]\scan\sbe\s(\".*\").)?/g,
              (match) => {
                if (match[4]) {
                  const arches = match[4].split('or').map(
                      (arch) => arch.replace(/\"/g, ''));
                  return arches.map(
                      (arch) => match[1].replace(/\[arch\]/g, arch).trim());
                } else {
                  return [match[1].trim()];
                }
              },
              'The executable "' + that.executable +
                  '" failed to produce generators!')
          .then((generators) => {
            that.generators = generators;
          });
    }
  }

  /**
   * @param {path} cachePath
   * @return {Promise}
   */
  extractGeneratorFromCache(cachePath) {
    return new Promise(function(resolve, reject) {
      fs.readFile(cachePath, 'utf8', function(err, cache) {
        if (err) {
          reject(err);
        } else {
          const matches = cache.match(/CMAKE_GENERATOR:INTERNAL=(.*)/);
          if (matches.length != 2)
            reject('Could not find generator in cmake cache!');
          else
            resolve(matches[1]);
        }
      });
    });
  }

  /**
   * @param {string} targetName
   * @param {path} sourceDirectory
   * @param {path} buildDirectory
   * @param {boolean} parallelBuild
   * @param {Array} extraBuildArguments
   * @return {target}
   */
  createMakeFileTarget(
      targetName, sourceDirectory, buildDirectory, parallelBuild,
      extraBuildArguments) {
    argumentList = ['--build', buildDirectory, '--target', targetName, '--'];
    if (parallelBuild) argumentList.push('-j' + os.cpus().length);
    return {
      name: targetName,
      exec: this.executable,
      cwd: sourceDirectory,
      args: argumentList.concat(extraBuildArguments),
    };
  }

  /**
   * @param {path} sourceDirectory
   * @param {path} buildDirectory
   * @param {boolean} parallelBuild
   * @param {Array} extraBuildArguments
   * @return {Promise}
   */
  makeFileTargets(
      sourceDirectory, buildDirectory, parallelBuild, extraBuildArguments) {
    const that = this;
    return that.grepStdout(
        that.executable, ['--build', buildDirectory, '--target', 'help'],
        /\.{3}\s(.[^\s]+)/g, (match) => {
          return [that.createMakeFileTarget(
              match[1].trim(), sourceDirectory, buildDirectory, parallelBuild,
              extraBuildArguments)];
        });
  }

  /**
   * @param {string} targetName
   * @param {path} sourceDirectory
   * @param {path} buildDirectory
   * @param {boolean} parallelBuild
   * @param {Array} extraBuildArguments
   * @return {target}
   */
  ninjaTarget(
      targetName, sourceDirectory, buildDirectory, parallelBuild,
      extraBuildArguments) {
    argumentList = ['--build', buildDirectory, '--target', targetName, '--'];
    if (parallelBuild) argumentList.push('-j' + os.cpus().length);
    return {
      name: targetName,
      exec: this.executable,
      cwd: sourceDirectory,
      args: argumentList.concat(extraBuildArguments),
    };
  }

  /**
   * @param {path} sourceDirectory
   * @param {path} buildDirectory
   * @param {boolean} parallelBuild
   * @param {Array} extraBuildArguments
   * @return {Promise}
   */
  ninjaTargets(
      sourceDirectory, buildDirectory, parallelBuild, extraBuildArguments) {
    const that = this;
    return that.grepStdout(
        that.executable, ['--build', buildDirectory, '--', '-t', 'targets'],
        /(.+):/g, (match) => {
          return [that.ninjaTarget(
              match[1].trim(), sourceDirectory, buildDirectory, parallelBuild,
              extraBuildArguments)];
        });
  }

  /**
   * @param {string} targetName
   * @param {path} sourceDirectory
   * @param {path} buildDirectory
   * @param {boolean} parallelBuild
   * @param {Array} extraBuildArguments
   * @return {target}
   */
  createVisualStudioTarget(
      targetName, sourceDirectory, buildDirectory, parallelBuild,
      extraBuildArguments) {
    argumentList = ['--build', buildDirectory, '--target', targetName, '--'];
    if (parallelBuild) argumentList.push('/maxcpucount');
    return {
      name: targetName,
      exec: this.executable,
      cwd: sourceDirectory,
      args: argumentList.concat(extraBuildArguments),
    };
  }

  /**
   * @param {path} sourceDirectory
   * @param {path} buildDirectory
   * @param {boolean} parallelBuild
   * @param {Array} extraBuildArguments
   * @return {Promise}
   */
  visualStudioTargets(
      sourceDirectory, buildDirectory, parallelBuild, extraBuildArguments) {
    const that = this;
    return new Promise(function(resolve, reject) {
      glob(
          '**/*.vcxproj', {
            cwd: sourceDirectory,
            nodir: true,
            ignore: 'CMakeFiles/**',
          },
          (error, files) => {
            if (error)
              reject(error);
            else
              resolve(files.map((file) => path.basename(file, '.vcxproj'))
                          .concat(['clean'])
                          .map(
                              (targetName) => that.createVisualStudioTarget(
                                  targetName, sourceDirectory, buildDirectory,
                                  parallelBuild, extraBuildArguments)));
          });
    });
  }

  /**
   * @param {path} sourceDirectory
   * @param {path} buildDirectory
   * @param {boolean} parallelBuild
   * @param {Array} extraBuildArguments
   * @return {Promise}
   */
  extractTargets(
      sourceDirectory, buildDirectory, parallelBuild, extraBuildArguments) {
    const that = this;
    return that
        .extractGeneratorFromCache(path.join(buildDirectory, 'CMakeCache.txt'))
        .then((generator) => {
          if (generator.match('Visual Studio'))
            return that
                .visualStudioTargets(
                    sourceDirectory, buildDirectory, parallelBuild,
                    extraBuildArguments)
                .then((targets) => {
                  return {generator: generator, targets: targets};
                });
          else if (generator.match('Ninja'))
            return that
                .ninjaTargets(
                    sourceDirectory, buildDirectory, parallelBuild,
                    extraBuildArguments)
                .then((targets) => {
                  return {generator: generator, targets: targets};
                });
          else if (generator.match('Unix Makefiles'))
            return that
                .makeFileTargets(
                    sourceDirectory, buildDirectory, parallelBuild,
                    extraBuildArguments)
                .then((targets) => {
                  return {generator: generator, targets: targets};
                });
        });
  }
};
<|endoftext|>


<commit_before>var Backbone = require('backbone');

var Image = require('../../models/image');

module.exports = Backbone.View.extend({

    tagName: 'div',

    className: 'image-chooser',

    events: {
        'click': 'choose'
    },

    initialize: function (options) {
        _.extend(this, options);

        if (typeof this.model === 'undefined') {
            this.model = new Image(this.$el.data('model'));
        }

        this.size = this.$el.data('size');

        this.update();

        this.listenTo(this.model, 'change', this.update);
    },

    update: function () {
        // Update image
        if (this.model.get('id')) {
            this.$el.css('background-image', 'url(' + this.model.fileUrl(this.size) + ')');
        } else {
            this.$el.css('background-image', 'none');
        }

        // Update input
        this.$('input').val(this.model.get('id'));
    },

    choose: function () {
        var that = this;
        this.manager.call({
            multiple: false,
            done: function (images) {
                that.model.set(images[0].attributes);
            }
        });
    }
});
<commit_msg>Image chooser check existence when deleting images from manager #72<commit_after>var Backbone = require('backbone');

var Image = require('../../models/image');

module.exports = Backbone.View.extend({

    tagName: 'div',

    className: 'image-chooser',

    events: {
        'click': 'choose'
    },

    initialize: function (options) {
        _.extend(this, options);

        if (typeof this.model === 'undefined') {
            this.model = new Image(this.$el.data('model'));
        }

        this.size = this.$el.data('size');

        this.update();

        // Bind events
        this.listenTo(this.model, 'change', this.update);
        this.listenTo(this.manager.collection, 'remove', this.checkExistence);
    },

    update: function () {
        // Update image
        if (this.model.get('id')) {
            this.$el.css('background-image', 'url(' + this.model.fileUrl(this.size) + ')');
        } else {
            this.$el.css('background-image', 'none');
        }

        // Update input
        this.$('input').val(this.model.get('id'));
    },

    choose: function () {
        var that = this;
        this.manager.call({
            multiple: false,
            done: function (images) {
                that.model.set(images[0].attributes);
            }
        });
    },

    /**
     * Fire when an image model is removed from image manager. If the chosen image
     * was deleted from image manager, set the image model to null.
     */
    checkExistence: function () {
        if (!this.manager.collection.get(this.model)) {
            this.model.clear();
        }
    }
});
<|endoftext|>


<commit_before>import { Meteor } from 'meteor/meteor';
import { _ } from 'meteor/erasaur:meteor-lodash';
import { Roles } from 'meteor/alanning:roles';
import SimpleSchema from 'simpl-schema';
import { Courses } from '../course/CourseCollection';
import { AcademicYearInstances } from '../degree-plan/AcademicYearInstanceCollection';
import { ROLE } from '../role/Role';
import { Semesters } from '../semester/SemesterCollection';
import { Users } from '../user/UserCollection';
import { Slugs } from '../slug/SlugCollection';
import BaseCollection from '../base/BaseCollection';
import { makeCourseICE } from '../ice/IceProcessor';

/** @module api/course/CourseInstanceCollection */

/**
 * Represents the taking of a course by a specific student in a specific semester.
 * @extends module:api/base/BaseCollection~BaseCollection
 */
class CourseInstanceCollection extends BaseCollection {
  /**
   * Creates the CourseInstance collection.
   */
  constructor() {
    super('CourseInstance', new SimpleSchema({
      semesterID: SimpleSchema.RegEx.Id,
      courseID: { type: SimpleSchema.RegEx.Id, optional: true },
      verified: Boolean,
      fromSTAR: { type: Boolean, optional: true },
      grade: { type: String, optional: true },
      creditHrs: Number,
      note: { type: String, optional: true },
      studentID: SimpleSchema.RegEx.Id,
      ice: { type: Object, optional: true, blackbox: true },
    }));
    this.validGrades = ['', 'A', 'A+', 'A-',
      'B', 'B+', 'B-', 'C', 'C+', 'C-', 'D', 'D+', 'D-', 'F', 'CR', 'NC', '***', 'W'];
    this.publicationNames = {
      student: this._collectionName,
      publicPublish: `${this._collectionName}.Public`,
      perStudentAndSemester: `${this._collectionName}.PerStudentAndSemester`,
      publicStudent: `${this._collectionName}.PublicStudent`,
      publicSlugStudent: `${this._collectionName}.PublicSlugStudent`,
      studentID: `${this._collectionName}.studentID`,
    };
    if (Meteor.server) {
      this._collection._ensureIndex({ _id: 1, studentID: 1, courseID: 1 });
    }
  }

  /**
   * Defines a new CourseInstance.
   * @example
   * // To define an instance of a CS course:
   * CourseInstances.define({ semester: 'Spring-2016',
   *                          course: 'ics311',
   *                          verified: false,
   *                          fromSTAR: false,
   *                          grade: 'B',
   *                          note: '',
   *                          student: 'joesmith',
   *                          creditHrs: 3 });
   * @param { Object } description Object with keys semester, course, verified, fromSTAR, grade,
   * note, student, creditHrs.
   * Required fields: semester, student, course, which must all be valid slugs or instance IDs.
   * If the course slug is 'other', then the note field will be used as the course number.
   * Optional fields: note (defaults to ''), valid (defaults to false), grade (defaults to '').
   * CreditHrs defaults to the creditHrs assigned to course, or can be provided explicitly.
   * @throws {Meteor.Error} If the definition includes an undefined course or student.
   * @returns The newly created docID.
   */
  define({ semester, course, verified = false, fromSTAR = false, grade = '', note = '', student, creditHrs }) {
    // Check arguments
    const semesterID = Semesters.getID(semester);
    const semesterDoc = Semesters.findDoc(semesterID);
    const courseID = Courses.getID(course);
    const studentID = Users.getID(student);
    const user = Users.findDoc(studentID);
    // ensure the AcademicYearInstance is defined.
    if (semesterDoc.term === Semesters.SPRING || semesterDoc.term === Semesters.SUMMER) {
      AcademicYearInstances.define({ year: semesterDoc.year - 1, student: user.username });
    } else {
      AcademicYearInstances.define({ year: semesterDoc.year, student: user.username });
    }
    const ice = makeCourseICE(course, grade);
    if ((typeof verified) !== 'boolean') {
      throw new Meteor.Error(`${verified} is not a boolean.`);
    }
    if (!_.includes(this.validGrades, grade)) {
      if (grade.startsWith('I')) {
        grade = grade.substring(1);
      }
      if (!_.includes(this.validGrades, grade)) {
        throw new Meteor.Error(`${grade} is not a valid grade.`);
      }
    }
    /* eslint no-param-reassign: "off" */
    if (!creditHrs) {
      creditHrs = Courses.findDoc(courseID).creditHrs;
    }
    // Define and return the CourseInstance
    // eslint-disable-next-line max-len
    return this._collection.insert({ semesterID, courseID, verified, fromSTAR, grade, studentID, creditHrs, note, ice });
  }

  /**
   * Update the course instance. Only a subset of fields can be updated.
   * @param docID The course instance docID (required).
   * @param verified optional.
   * @param fromSTAR optional.
   * @param grade optional.
   * @param note optional.
   */
  update(docID, { verified, fromSTAR, grade, note }) {
    this.assertDefined(docID);
    const updateData = {};
    if (verified) {
      updateData.verified = verified;
    }
    if (fromSTAR) {
      updateData.fromSTAR = fromSTAR;
    }
    if (grade) {
      updateData.grade = grade;
    }
    if (note) {
      updateData.note = note;
    }
    this._collection.update(docID, { $set: updateData });
  }

  /**
   * Implementation of assertValidRoleForMethod. Asserts that userId is logged in as an Admin, Advisor or
   * Student.
   * This is used in the define, update, and removeIt Meteor methods associated with each class.
   * @param userId The userId of the logged in user. Can be null or undefined
   * @throws { Meteor.Error } If there is no logged in user, or the user is not an Admin or Advisor.
   */
  assertValidRoleForMethod(userId) {
    this._assertRole(userId, [ROLE.ADMIN, ROLE.ADVISOR, ROLE.STUDENT]);
  }

  /**
   * Returns the Course associated with the CourseInstance with the given instanceID.
   * @param instanceID The id of the CourseInstance.
   * @returns {Object} The associated Course.
   * @throws {Meteor.Error} If instanceID is not a valid ID.
   */
  getCourseDoc(instanceID) {
    this.assertDefined(instanceID);
    const instance = this._collection.findOne({ _id: instanceID });
    return Courses.findDoc(instance.courseID);
  }

  /**
   * Returns the Course slug for the instance's corresponding Course.
   * @param instanceID The CourseInstanceID.
   * @return {string} The course slug.
   */
  getCourseSlug(instanceID) {
    this.assertDefined(instanceID);
    const instance = this._collection.findOne({ _id: instanceID });
    return Courses.getSlug(instance.courseID);
  }

  /**
   * Returns the Semester associated with the CourseInstance with the given instanceID.
   * @param instanceID The id of the CourseInstance.
   * @returns {Object} The associated Semester.
   * @throws {Meteor.Error} If instanceID is not a valid ID.
   */
  getSemesterDoc(instanceID) {
    this.assertDefined(instanceID);
    const instance = this._collection.findOne({ _id: instanceID });
    return Semesters.findDoc(instance.semesterID);
  }

  /**
   * Returns the Student associated with the CourseInstance with the given instanceID.
   * @param instanceID The id of the CourseInstance.
   * @returns {Object} The associated Student.
   * @throws {Meteor.Error} If instanceID is not a valid ID.
   */
  getStudentDoc(instanceID) {
    this.assertDefined(instanceID);
    const instance = this._collection.findOne({ _id: instanceID });
    return Users.findDoc(instance.studentID);
  }

  /**
   * @returns { String } The course name associated with courseInstanceID.
   * @param courseInstanceID The course instance ID.
   * @throws {Meteor.Error} If courseInstanceID is not a valid ID.
   */
  findCourseName(courseInstanceID) {
    this.assertDefined(courseInstanceID);
    const courseID = this.findDoc(courseInstanceID).courseID;
    return Courses.findDoc(courseID).name;
  }

  /**
   * Returns the courseInstance document associated with semester, course, and student.
   * @param semester The semester (slug or ID).
   * @param course The course (slug or ID).
   * @param student The student (slug or ID)
   * @returns { Object } Returns the document or null if not found.
   * @throws { Meteor.Error } If semester, course, or student does not exist.
   */
  findCourseInstanceDoc(semester, course, student) {
    const semesterID = Semesters.getID(semester);
    const studentID = Users.getID(student);
    const courseID = Courses.getID(course);
    return this._collection.findOne({ semesterID, studentID, courseID });
  }

  /**
   * Returns true if there exists a CourseInstance for the given semester, course, and student.
   * @param semester The semester (slug or ID).
   * @param course The course (slug or ID).
   * @param student The student (slug or ID).
   * @returns True if the course instance exists.
   * @throws { Meteor.Error } If semester, course, or student does not exist.
   */
  isCourseInstance(semester, course, student) {
    return !!this.findCourseInstanceDoc(semester, course, student);
  }

  /**
   * @returns { boolean } If the course is an interesting course associated with courseInstanceID.
   * @param courseInstanceID The course instance ID.
   * @throws {Meteor.Error} If courseInstanceID is not a valid ID.
   */
  isInteresting(courseInstanceID) {
    this.assertDefined(courseInstanceID);
    const instance = this.findDoc(courseInstanceID);
    return Courses.findDoc(instance.courseID).number !== Courses.unInterestingSlug;
  }

  /**
   * Depending on the logged in user publish only their CourseInstances. If
   * the user is in the Role.ADMIN then publish all CourseInstances. If the
   * system is in mockup mode publish all CourseInstances.
   */
  publish() {
    if (Meteor.isServer) {
      const instance = this;
      Meteor.publish(this.publicationNames.student, function publish() {
        if (Roles.userIsInRole(this.userId, [ROLE.ADMIN, ROLE.ADVISOR])) {
          return instance._collection.find();
        }
        return instance._collection.find({ studentID: this.userId });
      });
      Meteor.publish(this.publicationNames.publicPublish, function publicPublish(courseID) {  // eslint-disable-line
        // check the courseID.
        new SimpleSchema({
          courseID: { type: String },
        }).validate({ courseID });

        return instance._collection.find({ courseID }, { fields: { studentID: 1, semesterID: 1, courseID: 1 } });
      });
      Meteor.publish(this.publicationNames.perStudentAndSemester,
          function perStudentAndSemester(studentID, semesterID) {  // eslint-disable-line
            new SimpleSchema({
              studentID: { type: String },
              semesterID: { type: String },
            }).validate({ studentID, semesterID });
            return instance._collection.find({ studentID, semesterID });
          });
      Meteor.publish(this.publicationNames.publicStudent, function publicStudentPublish() {  // eslint-disable-line
        return instance._collection.find({}, { fields: { studentID: 1, semesterID: 1, courseID: 1 } });
      });
      Meteor.publish(this.publicationNames.publicSlugStudent, function publicSlugPublish(courseSlug) {  // eslint-disable-line
        // check the courseID.
        const slug = Slugs.find({ name: courseSlug }).fetch();
        const course = Courses.find({ slugID: slug[0]._id }).fetch();
        const courseID = course[0]._id;
        new SimpleSchema({
          courseID: { type: String },
        }).validate({ courseID });

        return instance._collection.find({ courseID }, { fields: { studentID: 1, semesterID: 1, courseID: 1 } });
      });
      Meteor.publish(this.publicationNames.studentID, function filterStudentID(studentID) { // eslint-disable-line
        new SimpleSchema({
          studentID: { type: String },
        }).validate({ studentID });
        return instance._collection.find({ studentID });
      });
    }
  }

  /**
   * @returns {String} A formatted string representing the course instance.
   * @param courseInstanceID The course instance ID.
   * @throws {Meteor.Error} If not a valid ID.
   */
  toString(courseInstanceID) {
    this.assertDefined(courseInstanceID);
    const courseInstanceDoc = this.findDoc(courseInstanceID);
    const courseName = this.findCourseName(courseInstanceID);
    const semester = Semesters.toString(courseInstanceDoc.semesterID);
    const grade = courseInstanceDoc.grade;
    return `[CI ${semester} ${courseName} ${grade}]`;
  }

  /* eslint-disable class-methods-use-this */
  /**
   * Updates the CourseInstance's grade. This should be used for planning purposes on the client side.
   * @param courseInstanceID The course instance ID.
   * @param grade The new grade.
   */
  clientUpdateGrade(courseInstanceID, grade) {
    Meteor.call('CourseInstance.updateGrade', { courseInstanceID, grade });
  }

  /**
   * Updates the CourseInstance's grade. This should be used for planning purposes.
   * @param courseInstanceID The course instance ID.
   * @param grade The new grade.
   * @throws {Meteor.Error} If courseInstanceID is not a valid ID.
   */
  updateGrade(courseInstanceID, grade) {
    this.assertDefined(courseInstanceID);
    const ice = makeCourseICE(courseInstanceID, grade);
    this._collection.update({ _id: courseInstanceID }, { $set: { grade, ice, verified: false } });
  }

  /**
   * Updates the CourseInstance's Semester.
   * @param courseInstanceID The course instance ID.
   * @param semesterID The semester id.
   * @throws {Meteor.Error} If courseInstanceID is not a valid ID.
   */
  updateSemester(courseInstanceID, semesterID) {
    this.assertDefined(courseInstanceID);
    Semesters.assertSemester(semesterID);
    this._collection.update({ _id: courseInstanceID }, { $set: { semesterID } });
  }

  /**
   * Returns an array of strings, each one representing an integrity problem with this collection.
   * Returns an empty array if no problems were found.
   * Checks semesterID, courseID, and studentID.
   * @returns {Array} A (possibly empty) array of strings indicating integrity issues.
   */
  checkIntegrity() {
    const problems = [];
    this.find().forEach(doc => {
      if (!Semesters.isDefined(doc.semesterID)) {
        problems.push(`Bad semesterID: ${doc.semesterID}`);
      }
      if (!Courses.isDefined(doc.courseID)) {
        problems.push(`Bad courseID: ${doc.courseID}`);
      }
      if (!Users.isDefined(doc.studentID)) {
        problems.push(`Bad studentID: ${doc.studentID}`);
      }
    });
    return problems;
  }

  /**
   * Returns an object representing the CourseInstance docID in a format acceptable to define().
   * @param docID The docID of a CourseInstance.
   * @returns { Object } An object representing the definition of docID.
   */
  dumpOne(docID) {
    const doc = this.findDoc(docID);
    const semester = Semesters.findSlugByID(doc.semesterID);
    const course = Courses.findSlugByID(doc.courseID);
    const note = doc.note;
    const verified = doc.verified;
    const creditHrs = doc.creditHrs;
    const grade = doc.grade;
    const student = Users.findSlugByID(doc.studentID);
    return { semester, course, note, verified, creditHrs, grade, student };
  }
}

/**
 * Provides the singleton instance of this class to all other entities.
 */
export const CourseInstances = new CourseInstanceCollection();
<commit_msg>Checkpoint<commit_after>import { Meteor } from 'meteor/meteor';
import { _ } from 'meteor/erasaur:meteor-lodash';
import { Roles } from 'meteor/alanning:roles';
import SimpleSchema from 'simpl-schema';
import { Courses } from '../course/CourseCollection';
import { AcademicYearInstances } from '../degree-plan/AcademicYearInstanceCollection';
import { ROLE } from '../role/Role';
import { Semesters } from '../semester/SemesterCollection';
import { Users } from '../user/UserCollection';
import { Slugs } from '../slug/SlugCollection';
import BaseCollection from '../base/BaseCollection';
import { makeCourseICE } from '../ice/IceProcessor';

/** @module api/course/CourseInstanceCollection */

/**
 * Represents the taking of a course by a specific student in a specific semester.
 * @extends module:api/base/BaseCollection~BaseCollection
 */
class CourseInstanceCollection extends BaseCollection {
  /**
   * Creates the CourseInstance collection.
   */
  constructor() {
    super('CourseInstance', new SimpleSchema({
      semesterID: SimpleSchema.RegEx.Id,
      courseID: { type: SimpleSchema.RegEx.Id, optional: true },
      verified: Boolean,
      fromSTAR: { type: Boolean, optional: true },
      grade: { type: String, optional: true },
      creditHrs: Number,
      note: { type: String, optional: true },
      studentID: SimpleSchema.RegEx.Id,
      ice: { type: Object, optional: true, blackbox: true },
    }));
    this.validGrades = ['', 'A', 'A+', 'A-',
      'B', 'B+', 'B-', 'C', 'C+', 'C-', 'D', 'D+', 'D-', 'F', 'CR', 'NC', '***', 'W'];
    this.publicationNames = {
      student: this._collectionName,
      publicPublish: `${this._collectionName}.Public`,
      perStudentAndSemester: `${this._collectionName}.PerStudentAndSemester`,
      publicStudent: `${this._collectionName}.PublicStudent`,
      publicSlugStudent: `${this._collectionName}.PublicSlugStudent`,
      studentID: `${this._collectionName}.studentID`,
    };
    if (Meteor.server) {
      this._collection._ensureIndex({ _id: 1, studentID: 1, courseID: 1 });
    }
  }

  /**
   * Defines a new CourseInstance.
   * @example
   * // To define an instance of a CS course:
   * CourseInstances.define({ semester: 'Spring-2016',
   *                          course: 'ics311',
   *                          verified: false,
   *                          fromSTAR: false,
   *                          grade: 'B',
   *                          note: '',
   *                          student: 'joesmith',
   *                          creditHrs: 3 });
   * @param { Object } description Object with keys semester, course, verified, fromSTAR, grade,
   * note, student, creditHrs.
   * Required fields: semester, student, course, which must all be valid slugs or instance IDs.
   * If the course slug is 'other', then the note field will be used as the course number.
   * Optional fields: note (defaults to ''), valid (defaults to false), grade (defaults to '').
   * CreditHrs defaults to the creditHrs assigned to course, or can be provided explicitly.
   * @throws {Meteor.Error} If the definition includes an undefined course or student.
   * @returns The newly created docID.
   */
  define({ semester, course, verified = false, fromSTAR = false, grade = '', note = '', student, creditHrs }) {
    // Check arguments
    const semesterID = Semesters.getID(semester);
    const semesterDoc = Semesters.findDoc(semesterID);
    const courseID = Courses.getID(course);
    const studentID = Users.getID(student);
    const user = Users.findDoc(studentID);
    // ensure the AcademicYearInstance is defined.
    if (semesterDoc.term === Semesters.SPRING || semesterDoc.term === Semesters.SUMMER) {
      AcademicYearInstances.define({ year: semesterDoc.year - 1, student: user.username });
    } else {
      AcademicYearInstances.define({ year: semesterDoc.year, student: user.username });
    }
    const ice = makeCourseICE(course, grade);
    if ((typeof verified) !== 'boolean') {
      throw new Meteor.Error(`${verified} is not a boolean.`);
    }
    if (!_.includes(this.validGrades, grade)) {
      if (grade.startsWith('I')) {
        grade = grade.substring(1);
      }
      if (!_.includes(this.validGrades, grade)) {
        throw new Meteor.Error(`${grade} is not a valid grade.`);
      }
    }
    /* eslint no-param-reassign: "off" */
    if (!creditHrs) {
      creditHrs = Courses.findDoc(courseID).creditHrs;
    }
    // Define and return the CourseInstance
    // eslint-disable-next-line max-len
    return this._collection.insert({ semesterID, courseID, verified, fromSTAR, grade, studentID, creditHrs, note, ice });
  }

  /**
   * Update the course instance. Only a subset of fields can be updated.
   * @param docID The course instance docID (required).
   * @param verified optional.
   * @param fromSTAR optional.
   * @param grade optional.
   * @param note optional.
   */
  update(docID, { verified, fromSTAR, grade, note }) {
    this.assertDefined(docID);
    const updateData = {};
    if (verified) {
      updateData.verified = verified;
    }
    if (fromSTAR) {
      updateData.fromSTAR = fromSTAR;
    }
    if (grade) {
      updateData.grade = grade;
    }
    if (note) {
      updateData.note = note;
    }
    this._collection.update(docID, { $set: updateData });
  }

  /**
   * Remove the course instance.
   * @param instance The docID of the course instance.
   */
  removeIt(instance) {
    const docID = this.getID(instance);
    // OK, clear to delete.
    this._collection.remove(docID);
  }

  /**
   * Implementation of assertValidRoleForMethod. Asserts that userId is logged in as an Admin, Advisor or
   * Student.
   * This is used in the define, update, and removeIt Meteor methods associated with each class.
   * @param userId The userId of the logged in user. Can be null or undefined
   * @throws { Meteor.Error } If there is no logged in user, or the user is not an Admin or Advisor.
   */
  assertValidRoleForMethod(userId) {
    this._assertRole(userId, [ROLE.ADMIN, ROLE.ADVISOR, ROLE.STUDENT]);
  }

  /**
   * Returns the Course associated with the CourseInstance with the given instanceID.
   * @param instanceID The id of the CourseInstance.
   * @returns {Object} The associated Course.
   * @throws {Meteor.Error} If instanceID is not a valid ID.
   */
  getCourseDoc(instanceID) {
    this.assertDefined(instanceID);
    const instance = this._collection.findOne({ _id: instanceID });
    return Courses.findDoc(instance.courseID);
  }

  /**
   * Returns the Course slug for the instance's corresponding Course.
   * @param instanceID The CourseInstanceID.
   * @return {string} The course slug.
   */
  getCourseSlug(instanceID) {
    this.assertDefined(instanceID);
    const instance = this._collection.findOne({ _id: instanceID });
    return Courses.getSlug(instance.courseID);
  }

  /**
   * Returns the Semester associated with the CourseInstance with the given instanceID.
   * @param instanceID The id of the CourseInstance.
   * @returns {Object} The associated Semester.
   * @throws {Meteor.Error} If instanceID is not a valid ID.
   */
  getSemesterDoc(instanceID) {
    this.assertDefined(instanceID);
    const instance = this._collection.findOne({ _id: instanceID });
    return Semesters.findDoc(instance.semesterID);
  }

  /**
   * Returns the Student associated with the CourseInstance with the given instanceID.
   * @param instanceID The id of the CourseInstance.
   * @returns {Object} The associated Student.
   * @throws {Meteor.Error} If instanceID is not a valid ID.
   */
  getStudentDoc(instanceID) {
    this.assertDefined(instanceID);
    const instance = this._collection.findOne({ _id: instanceID });
    return Users.findDoc(instance.studentID);
  }

  /**
   * @returns { String } The course name associated with courseInstanceID.
   * @param courseInstanceID The course instance ID.
   * @throws {Meteor.Error} If courseInstanceID is not a valid ID.
   */
  findCourseName(courseInstanceID) {
    this.assertDefined(courseInstanceID);
    const courseID = this.findDoc(courseInstanceID).courseID;
    return Courses.findDoc(courseID).name;
  }

  /**
   * Returns the courseInstance document associated with semester, course, and student.
   * @param semester The semester (slug or ID).
   * @param course The course (slug or ID).
   * @param student The student (slug or ID)
   * @returns { Object } Returns the document or null if not found.
   * @throws { Meteor.Error } If semester, course, or student does not exist.
   */
  findCourseInstanceDoc(semester, course, student) {
    const semesterID = Semesters.getID(semester);
    const studentID = Users.getID(student);
    const courseID = Courses.getID(course);
    return this._collection.findOne({ semesterID, studentID, courseID });
  }

  /**
   * Returns true if there exists a CourseInstance for the given semester, course, and student.
   * @param semester The semester (slug or ID).
   * @param course The course (slug or ID).
   * @param student The student (slug or ID).
   * @returns True if the course instance exists.
   * @throws { Meteor.Error } If semester, course, or student does not exist.
   */
  isCourseInstance(semester, course, student) {
    return !!this.findCourseInstanceDoc(semester, course, student);
  }

  /**
   * @returns { boolean } If the course is an interesting course associated with courseInstanceID.
   * @param courseInstanceID The course instance ID.
   * @throws {Meteor.Error} If courseInstanceID is not a valid ID.
   */
  isInteresting(courseInstanceID) {
    this.assertDefined(courseInstanceID);
    const instance = this.findDoc(courseInstanceID);
    return Courses.findDoc(instance.courseID).number !== Courses.unInterestingSlug;
  }

  /**
   * Depending on the logged in user publish only their CourseInstances. If
   * the user is in the Role.ADMIN then publish all CourseInstances. If the
   * system is in mockup mode publish all CourseInstances.
   */
  publish() {
    if (Meteor.isServer) {
      const instance = this;
      Meteor.publish(this.publicationNames.student, function publish() {
        if (Roles.userIsInRole(this.userId, [ROLE.ADMIN, ROLE.ADVISOR])) {
          return instance._collection.find();
        }
        return instance._collection.find({ studentID: this.userId });
      });
      Meteor.publish(this.publicationNames.publicPublish, function publicPublish(courseID) {  // eslint-disable-line
        // check the courseID.
        new SimpleSchema({
          courseID: { type: String },
        }).validate({ courseID });

        return instance._collection.find({ courseID }, { fields: { studentID: 1, semesterID: 1, courseID: 1 } });
      });
      Meteor.publish(this.publicationNames.perStudentAndSemester,
          function perStudentAndSemester(studentID, semesterID) {  // eslint-disable-line
            new SimpleSchema({
              studentID: { type: String },
              semesterID: { type: String },
            }).validate({ studentID, semesterID });
            return instance._collection.find({ studentID, semesterID });
          });
      Meteor.publish(this.publicationNames.publicStudent, function publicStudentPublish() {  // eslint-disable-line
        return instance._collection.find({}, { fields: { studentID: 1, semesterID: 1, courseID: 1 } });
      });
      Meteor.publish(this.publicationNames.publicSlugStudent, function publicSlugPublish(courseSlug) {  // eslint-disable-line
        // check the courseID.
        const slug = Slugs.find({ name: courseSlug }).fetch();
        const course = Courses.find({ slugID: slug[0]._id }).fetch();
        const courseID = course[0]._id;
        new SimpleSchema({
          courseID: { type: String },
        }).validate({ courseID });

        return instance._collection.find({ courseID }, { fields: { studentID: 1, semesterID: 1, courseID: 1 } });
      });
      Meteor.publish(this.publicationNames.studentID, function filterStudentID(studentID) { // eslint-disable-line
        new SimpleSchema({
          studentID: { type: String },
        }).validate({ studentID });
        return instance._collection.find({ studentID });
      });
    }
  }

  /**
   * @returns {String} A formatted string representing the course instance.
   * @param courseInstanceID The course instance ID.
   * @throws {Meteor.Error} If not a valid ID.
   */
  toString(courseInstanceID) {
    this.assertDefined(courseInstanceID);
    const courseInstanceDoc = this.findDoc(courseInstanceID);
    const courseName = this.findCourseName(courseInstanceID);
    const semester = Semesters.toString(courseInstanceDoc.semesterID);
    const grade = courseInstanceDoc.grade;
    return `[CI ${semester} ${courseName} ${grade}]`;
  }

  /* eslint-disable class-methods-use-this */
  /**
   * Updates the CourseInstance's grade. This should be used for planning purposes on the client side.
   * @param courseInstanceID The course instance ID.
   * @param grade The new grade.
   */
  clientUpdateGrade(courseInstanceID, grade) {
    Meteor.call('CourseInstance.updateGrade', { courseInstanceID, grade });
  }

  /**
   * Updates the CourseInstance's grade. This should be used for planning purposes.
   * @param courseInstanceID The course instance ID.
   * @param grade The new grade.
   * @throws {Meteor.Error} If courseInstanceID is not a valid ID.
   */
  updateGrade(courseInstanceID, grade) {
    this.assertDefined(courseInstanceID);
    const ice = makeCourseICE(courseInstanceID, grade);
    this._collection.update({ _id: courseInstanceID }, { $set: { grade, ice, verified: false } });
  }

  /**
   * Updates the CourseInstance's Semester.
   * @param courseInstanceID The course instance ID.
   * @param semesterID The semester id.
   * @throws {Meteor.Error} If courseInstanceID is not a valid ID.
   */
  updateSemester(courseInstanceID, semesterID) {
    this.assertDefined(courseInstanceID);
    Semesters.assertSemester(semesterID);
    this._collection.update({ _id: courseInstanceID }, { $set: { semesterID } });
  }

  /**
   * Returns an array of strings, each one representing an integrity problem with this collection.
   * Returns an empty array if no problems were found.
   * Checks semesterID, courseID, and studentID.
   * @returns {Array} A (possibly empty) array of strings indicating integrity issues.
   */
  checkIntegrity() {
    const problems = [];
    this.find().forEach(doc => {
      if (!Semesters.isDefined(doc.semesterID)) {
        problems.push(`Bad semesterID: ${doc.semesterID}`);
      }
      if (!Courses.isDefined(doc.courseID)) {
        problems.push(`Bad courseID: ${doc.courseID}`);
      }
      if (!Users.isDefined(doc.studentID)) {
        problems.push(`Bad studentID: ${doc.studentID}`);
      }
    });
    return problems;
  }

  /**
   * Returns an object representing the CourseInstance docID in a format acceptable to define().
   * @param docID The docID of a CourseInstance.
   * @returns { Object } An object representing the definition of docID.
   */
  dumpOne(docID) {
    const doc = this.findDoc(docID);
    const semester = Semesters.findSlugByID(doc.semesterID);
    const course = Courses.findSlugByID(doc.courseID);
    const note = doc.note;
    const verified = doc.verified;
    const creditHrs = doc.creditHrs;
    const grade = doc.grade;
    const student = Users.findSlugByID(doc.studentID);
    return { semester, course, note, verified, creditHrs, grade, student };
  }
}

/**
 * Provides the singleton instance of this class to all other entities.
 */
export const CourseInstances = new CourseInstanceCollection();
<|endoftext|>


<commit_before>/* Implements functions that allow duplicating sections and questions.
 * It works by copying the DOM of that element and adjusting the IDs
 * for that question/section to an unused one. */

/* @public
 * Shows/hides the duplication buttons on sections/questions.
 * @param enable  if the buttons should be shown/hidden */
FormEditor.prototype.toggleDuplicating = function(enable) {
  enable = enable === undefined || enable === null ? $("#duplicate").is(":visible") : enable;
  if(enable) {
    $("#duplicate").hide();
    $(".duplicate, #cancel-duplicate").show();
  } else {
    $("#duplicate").show();
    $(".duplicate, #cancel-duplicate").hide();
  }
};

/* @public
 * Duplicates Question.
 * @param link   DOM reference to an element inside the question that
 *        should be duplicated */
FormEditor.prototype.duplicateQuestion = function(link) {
  var q = $(link).parents(".question");
  this.addUndoStep("duplicating question " + q.children("h6").data("db-column"));
  this.duplicate(q, "Question", "questions");
};

/* @public
 * Duplicates Section.
 * @param link   DOM reference to an element inside the section that
 *        should be duplicated */
FormEditor.prototype.duplicateSection = function(link) {
  var s = $(link).parents(".section");
  this.addUndoStep("duplicating section " + s.children("h5").data("title"));
  this.duplicate(s, "Section", "sections");
};

/* @private
 * Handles copying the DOM element as well as replacing the old IDs with
 * unused ones.
 * @param  DOM element which should be copied
 * @param  Type/AbstractForm Class of equivalent of DOM element being
 *         copied. Required to determine correct path from the hidden
 *         element (…/rubyobject).
 * @param  Name of the part in the path that preceeds the n-th question
 *         or section. Usually multiple and lower-case of type. E.g.
 *         /pages/0/sections/1/questions/2/ → "sections" if a section
 *         is to be duplicated */
FormEditor.prototype.duplicate = function(elm, type, pathGroup) {
  var r = new RegExp("/" + pathGroup + "/([0-9]+)/");

  // find new, not yet used id
  var lastPath = elm.parent().find("[type=hidden][value="+type+"][id^='/']").last().attr("id").match(r),
      oldPath = "/" + pathGroup + "/" + lastPath[1] + "/",
      pos = parseInt(lastPath[1])+1;
  while(true) {
    newPath = "/" + pathGroup + "/" + pos + "/";
    var check = document.getElementById(lastPath[0].replace(oldPath, newPath));
    if(check === null) break;
    pos++;
  }

  // clone and update id/for attributes
  var newElm = elm.clone();
  newElm.find("[id^='/']").each(function(pos, elm) {
    $(elm).attr("id", $(elm).attr("id").replace(r, newPath));
  });
  newElm.find("[for^='/']").each(function(pos, elm) {
    $(elm).attr("for", $(elm).attr("for").replace(r, newPath));
  });

  newElm.insertAfter(elm);
  this.checkDuplicateIds();
};
<commit_msg>fix sorting after duplication<commit_after>/* Implements functions that allow duplicating sections and questions.
 * It works by copying the DOM of that element and adjusting the IDs
 * for that question/section to an unused one. */

/* @public
 * Shows/hides the duplication buttons on sections/questions.
 * @param enable  if the buttons should be shown/hidden */
FormEditor.prototype.toggleDuplicating = function(enable) {
  enable = enable === undefined || enable === null ? $("#duplicate").is(":visible") : enable;
  if(enable) {
    $("#duplicate").hide();
    $(".duplicate, #cancel-duplicate").show();
  } else {
    $("#duplicate").show();
    $(".duplicate, #cancel-duplicate").hide();
  }
};

/* @public
 * Duplicates Question.
 * @param link   DOM reference to an element inside the question that
 *        should be duplicated */
FormEditor.prototype.duplicateQuestion = function(link) {
  var q = $(link).parents(".question");
  this.addUndoStep("duplicating question " + q.children("h6").data("db-column"));
  this.duplicate(q, "Question", "questions");
};

/* @public
 * Duplicates Section.
 * @param link   DOM reference to an element inside the section that
 *        should be duplicated */
FormEditor.prototype.duplicateSection = function(link) {
  var s = $(link).parents(".section");
  this.addUndoStep("duplicating section " + s.children("h5").data("title"));
  this.duplicate(s, "Section", "sections");
};

/* @private
 * Handles copying the DOM element as well as replacing the old IDs with
 * unused ones.
 * @param  DOM element which should be copied
 * @param  Type/AbstractForm Class of equivalent of DOM element being
 *         copied. Required to determine correct path from the hidden
 *         element (…/rubyobject).
 * @param  Name of the part in the path that preceeds the n-th question
 *         or section. Usually multiple and lower-case of type. E.g.
 *         /pages/0/sections/1/questions/2/ → "sections" if a section
 *         is to be duplicated */
FormEditor.prototype.duplicate = function(elm, type, pathGroup) {
  var r = new RegExp("/" + pathGroup + "/([0-9]+)/");

  // find new, not yet used id
  var lastPath = elm.parent().find("[type=hidden][value="+type+"][id^='/']").last().attr("id").match(r),
      oldPath = "/" + pathGroup + "/" + lastPath[1] + "/",
      pos = parseInt(lastPath[1])+1;
  while(true) {
    newPath = "/" + pathGroup + "/" + pos + "/";
    var check = document.getElementById(lastPath[0].replace(oldPath, newPath));
    if(check === null) break;
    pos++;
  }

  // clone and update id/for attributes
  var newElm = elm.clone();
  newElm.find("[id^='/']").each(function(pos, elm) {
    $(elm).attr("id", $(elm).attr("id").replace(r, newPath));
  });
  newElm.find("[for^='/']").each(function(pos, elm) {
    $(elm).attr("for", $(elm).attr("for").replace(r, newPath));
  });

  newElm.insertAfter(elm);
  $(".sortable-question").sortable("refresh");
  this.checkDuplicateIds();
};
<|endoftext|>


<commit_before>'use strict';

app.factory('Task', function(FURL, $firebase, Auth) {

	var ref = new Firebase(FURL);
	var tasks = $firebase(ref.child('task')).$asArray();
	var user = Auth.user;

}<commit_msg>add getTask<commit_after>'use strict';

app.factory('Task', function(FURL, $firebase, Auth) {

	var ref = new Firebase(FURL);
	var tasks = $firebase(ref.child('task')).$asArray();
	var user = Auth.user;

	var Task = {
		all: tasks,

		getTask: function(taskId) {
			return $firebase(ref.child('tasks').child(taskId));
		},

	};

});<|endoftext|>


<commit_before>$(function() {
	function Color(r, g, b, a) {
		this.r = r;
		this.g = g;
		this.b = b;
		this.a = (a === undefined) ? 255 : a;

		this.toStyleString = function() {
			return 'rgba(' + this.r + ', ' + this.g + ', ' + this.b + ', ' + this.a + ')';
		};
		this.clone = function() {
			return new Color(this.r, this.g, this.b, this.a);
		};
	}
	Colors = { // static utilities for Color
		BLACK: new Color(0, 0, 0),
		BLUE: new Color(0, 0, 255),
		ORANGE: new Color(255, 132, 0),
		RED: new Color(255, 0, 0),
		WHITE: new Color(255, 255, 255),
		CLEAR: new Color(255, 255, 255, 0),

		interpolateBetween: function(color1, color2, interpolationFunction, interpolation) {
			var lerp = interpolationFunction(interpolation);
			return new Color(
					lerpNumbers(color1.r, color2.r, lerp),
					lerpNumbers(color1.g, color2.g, lerp),
					lerpNumbers(color1.b, color2.b, lerp),
					lerpNumbers(color1.a, color2.a, lerp)
				);
		},
		fromStyleString: function(styleString) {
			var noParens = styleString.split('(')[1].split(')')[0];
			var stringVals = noParens.split(',');
			var numberVals = stringVals.map(function(val) { return parseFloat(val); });
			return new Color(numberVals[0], numberVals[1], numberVals[2], numberVals[3]);
		},
	}

	function Pixel(color, deathTime, interpolationFunction) {
		this.color = color.clone();
		this.deathTime = deathTime;
		this.interpolationFunction = interpolationFunction || EasingFunctions.linear;

		this.lifeTime = 0;
		this.alive = true;
		this.deathColor = color;
		this.position = {x: 0, y: 0};

		this.update = function(dt) {
			this.lifeTime += dt;
			if (this.lifeTime >= this.deathTime) {
				this.alive = false;
				return;
			}

			var interp = this.lifeTime / this.deathTime;
			this.color.a = 255 * (1 - interp);
		};

		this.draw = function(canvas, ctx, size) {
			if (!this.alive)
				return;

			ctx.globalAlpha = this.color.a / 255;
			ctx.fillStyle = this.color.toStyleString();
			ctx.fillRect(this.position.x, this.position.y, size, size);
		};
	}
	Pixels = { // static utilities for Pixel
	};

	ScreenPositionToColorsMap = {
		BORDERING_COLORS_INTERPOLATION_RANGE: 60, // px
		colorIntervals: [], // contains list of: { screenInterval = [start, end], pixelsColor = color object }

		colorsAtVerticalPosition: function(verticalPosition) {
			var color = DEFAULT_COLOR;
			for (var i = 0; i < this.colorIntervals.length; i++) {
				var interval = this.colorIntervals[i].screenInterval;
				if (verticalPosition >= interval[0] && verticalPosition <= interval[1]) {
					color = this.colorIntervals[i].pixelsColor;
					//interp between bordering colors
					var interpRange = this.BORDERING_COLORS_INTERPOLATION_RANGE;
					if (i > 0 && interval[0] + interpRange >= verticalPosition) { // near previous color
						var interp = (((interval[0] + interpRange) - verticalPosition) / interpRange) * 0.5;
						var previousColor = this.colorIntervals[i - 1].pixelsColor;
						color = Colors.interpolateBetween(color, previousColor, EasingFunctions.linear, interp);
					} else if (i < (this.colorIntervals.length - 1) && interval[1] - interpRange <= verticalPosition) { // near next color
						var interp = ((verticalPosition - (interval[1] - interpRange)) / interpRange) * 0.5;
						var nextColor = this.colorIntervals[i + 1].pixelsColor;
						color = Colors.interpolateBetween(color, nextColor, EasingFunctions.linear, interp);
					}
					break;
				}
			}
			return color;
		},
	};

	PixelManager = {
		allPixels: {},
		pixelCount: 0,

		spawnPixels: function(options) {
			options = options || {};
			var count = options.count || 1;

			var maxIndex = pixelsAcross * pixelsDown;
			for (var i = 0; i < count; i++) {
				if (this.pixelCount >= maxIndex)
					return;

				var index = Math.floor(Math.random() * maxIndex);
				while (index in this.allPixels) { // linear probing ftw
					index = (index + 1) % maxIndex;
				}
				this.spawnPixelAtPositionIndex(index, options);
			}
		},

		spawnPixelAtPositionIndex: function(index, options) {
			if (index in this.allPixels)
				return;

			options = options || {};
			var randomInitialLifetime = options.randomInitialLifetime || false;

			this.allPixels[index] = new Pixel(DEFAULT_COLOR, PIXEL_DEATH_TIME);
			if (randomInitialLifetime)
				this.allPixels[index].lifeTime = Math.random() * PIXEL_DEATH_TIME / 2;
			this.pixelCount++;
		},

		removePixel: function(positionIndex) {
			if (positionIndex in this.allPixels) {
				delete this.allPixels[positionIndex];
				this.pixelCount--;
			}
		},

		updatePixels: function(dt) {
			var canvasTop = pixelsCanvas.offset().top - $(window).scrollTop();
			for (i in this.allPixels) {
				var pixel = this.allPixels[i];
				pixel.update(dt);
				if (!pixel.alive) {
					this.removePixel(i);
					continue;
				}

				var tileX = (i % pixelsAcross);
				var tileY = Math.floor(i / pixelsAcross);
				pixel.position.x = tileX * pixelSize;
				pixel.position.y = tileY * pixelSize;

				var color = ScreenPositionToColorsMap.colorsAtVerticalPosition(canvasTop + pixel.position.y);
				pixel.color.r = Math.round(color.r);
				pixel.color.g = Math.round(color.g);
				pixel.color.b = Math.round(color.b);
			}
		},

		drawPixels: function(canvas, ctx) {
			for (i in this.allPixels) {
				this.allPixels[i].draw(canvas, ctx, pixelSize);
			}
		},
	};

	var NUM_PIXELS_ACROSS_VERT = 10;
	var NUM_PIXELS_ACROSS_HORIZ = 60;
	var PIXEL_DEATH_TIME = 4 * 1000; // ms
	var PIXEL_SPAWN_INTERVAL = 6; // ms
	var DEFAULT_COLOR = Colors.BLACK;

	var pixelSize;
	var pixelsAcross;
	var pixelsDown;

	var lastUpdateTime = new Date().getTime();
	var pixelSpawnTimer = 0;
	var pixelsCanvas = $('#pixels');
	var pixelsCanvasDOM = $('#pixels')[0];
	var pixelsContext = pixelsCanvasDOM.getContext('2d');

	//Start everything off
	if (isCanvasSupported()) {
		pixelsCanvas.show();
		initPixels();
	} else {
		pixelsCanvas.hide();
		return;
	}

	function initPixels() {
		recalculateCanvasData();

		pixelsCanvas.mousemove(canvasMouseMoveHandler);
		pixelsCanvas.on('touchstart', canvasMouseMoveHandler);
		pixelsCanvas.on('touchmove', canvasMouseMoveHandler);

		requestAnimFrame(function() { updatePixels(); });
	}

	function updatePixels() {
		var currentUpdateTime = new Date().getTime();
		var dt = currentUpdateTime - lastUpdateTime;
		lastUpdateTime = currentUpdateTime;

		recalculateCanvasData();

		pixelSpawnTimer += dt;
		if (pixelSpawnTimer >= PIXEL_SPAWN_INTERVAL) {
			var count = Math.floor(pixelSpawnTimer / PIXEL_SPAWN_INTERVAL);
			PixelManager.spawnPixels({count: count, randomInitialLifetime: true});
			pixelSpawnTimer -= PIXEL_SPAWN_INTERVAL * count;
		}

		PixelManager.updatePixels(dt);

		drawPixels();
	}

	function recalculateCanvasData() {
		var contentDivs = $('[data-pixel-colors]');
		var height = pixelsCanvas.height(); // get CSS dimensions of box containing canvas
		var width = pixelsCanvas.width();
		pixelsCanvasDOM.height = height; // actually set canvas dimensions so it isn't stretched into its box
		pixelsCanvasDOM.width = width;

		var canvasVertical = height > width;
		if (canvasVertical) {
			pixelSize = width / NUM_PIXELS_ACROSS_VERT;
			pixelsAcross = NUM_PIXELS_ACROSS_VERT;
			pixelsDown = Math.ceil(height / pixelSize);
		} else {
			pixelSize = width / NUM_PIXELS_ACROSS_HORIZ;
			pixelsAcross = NUM_PIXELS_ACROSS_HORIZ;
			pixelsDown = Math.ceil(height / pixelSize);
		}

		ScreenPositionToColorsMap.colorIntervals = [];
		for	(var i = 0; i < contentDivs.length; i++) {
			var content = $(contentDivs[i]);
			var pixelsColor = content.attr('data-pixel-colors') || DEFAULT_COLOR;
			//content.css('border', "5px solid " + pixelsColor);

			var scrollY = $(window).scrollTop();
			var screenheight = $(window).height();
			var contentY = content.offset().top;
			var contentHeight = content.outerHeight();
			var screenInterval = [contentY - scrollY, (contentY - scrollY) + contentHeight];

			if (screenInterval[1] >= 0 && screenInterval[0] <= screenheight) { // filter out off-screen intervals
				ScreenPositionToColorsMap.colorIntervals.push({screenInterval: screenInterval, pixelsColor: Colors.fromStyleString(pixelsColor)});
			}
		}
		ScreenPositionToColorsMap.colorIntervals.sort(function(a, b) { return a.screenInterval[0] - b.screenInterval[0]; }); // sort by beginning of interval, ascending
	}

	function canvasMouseMoveHandler(evt) {
		var tileX, tileY;
		var indices = [];

		if (evt.originalEvent instanceof MouseEvent) {
			tileX = Math.floor(evt.offsetX / pixelSize);
			tileY = Math.floor(evt.offsetY / pixelSize);
			indices.push(tileY * pixelsAcross + tileX);
		} else if (evt.originalEvent instanceof TouchEvent) {
			evt.preventDefault(); // prevent default scrolling behavior which causes touchmove events to stop

			var canvasLeft = pixelsCanvas.position().left;
			var canvasTop = pixelsCanvas.position().top;
			for (var i = 0; i < evt.originalEvent.changedTouches.length; i++) {
				var touch = evt.originalEvent.changedTouches[i];
				tileX = Math.floor((touch.clientX - canvasLeft) / pixelSize);
				tileY = Math.floor((touch.clientY - canvasTop) / pixelSize);
				if (tileX < 0 || tileY < 0)
					continue;
				indices.push(tileY * pixelsAcross + tileX);
			}
		}

		for (var i = 0; i < indices.length; i++) {
			PixelManager.spawnPixelAtPositionIndex(indices[i]);
			PixelManager.allPixels[indices[i]].lifeTime = 0;
		}
	}

	function drawPixels() {
		var canvas = pixelsCanvas;
		var ctx = pixelsContext;
		ctx.clearRect(0, 0, canvas.width, canvas.height);

		PixelManager.drawPixels(canvas, ctx);

		requestAnimFrame(function() { updatePixels(); });
	}
});

// Utilities
window.lerpNumbers = function(a, b, interpolation) {
	if (a === b)
		return a;
	return a * (1 - interpolation) + b * interpolation;
};

window.requestAnimFrame = (function(){
	return  window.requestAnimationFrame ||
		window.webkitRequestAnimationFrame ||
		window.mozRequestAnimationFrame ||
		function(callback) {
			window.setTimeout(callback, 1000 / 60);
		};
})();

window.isCanvasSupported = function() {
	var elem = document.createElement('canvas');
	return !!(elem.getContext && elem.getContext('2d'));
};

EasingFunctions = {
	// no easing, no acceleration
	linear: function (t) { return t },
	// accelerating from zero velocity
	easeInQuad: function (t) { return t*t },
	// decelerating to zero velocity
	easeOutQuad: function (t) { return t*(2-t) },
	// acceleration until halfway, then deceleration
	easeInOutQuad: function (t) { return t<.5 ? 2*t*t : -1+(4-2*t)*t },
	// accelerating from zero velocity
	easeInCubic: function (t) { return t*t*t },
	// decelerating to zero velocity
	easeOutCubic: function (t) { return (--t)*t*t+1 },
	// acceleration until halfway, then deceleration
	easeInOutCubic: function (t) { return t<.5 ? 4*t*t*t : (t-1)*(2*t-2)*(2*t-2)+1 },
	// accelerating from zero velocity
	easeInQuart: function (t) { return t*t*t*t },
	// decelerating to zero velocity
	easeOutQuart: function (t) { return 1-(--t)*t*t*t },
	// acceleration until halfway, then deceleration
	easeInOutQuart: function (t) { return t<.5 ? 8*t*t*t*t : 1-8*(--t)*t*t*t },
	// accelerating from zero velocity
	easeInQuint: function (t) { return t*t*t*t*t },
	// decelerating to zero velocity
	easeOutQuint: function (t) { return 1+(--t)*t*t*t*t },
	// acceleration until halfway, then deceleration
	easeInOutQuint: function (t) { return t<.5 ? 16*t*t*t*t*t : 1+16*(--t)*t*t*t*t }
}<commit_msg>Don't need debug color anymore<commit_after>$(function() {
	function Color(r, g, b, a) {
		this.r = r;
		this.g = g;
		this.b = b;
		this.a = (a === undefined) ? 255 : a;

		this.toStyleString = function() {
			return 'rgba(' + this.r + ', ' + this.g + ', ' + this.b + ', ' + this.a + ')';
		};
		this.clone = function() {
			return new Color(this.r, this.g, this.b, this.a);
		};
	}
	Colors = { // static utilities for Color
		BLACK: new Color(0, 0, 0),
		BLUE: new Color(0, 0, 255),
		ORANGE: new Color(255, 132, 0),
		RED: new Color(255, 0, 0),
		WHITE: new Color(255, 255, 255),
		CLEAR: new Color(255, 255, 255, 0),

		interpolateBetween: function(color1, color2, interpolationFunction, interpolation) {
			var lerp = interpolationFunction(interpolation);
			return new Color(
					lerpNumbers(color1.r, color2.r, lerp),
					lerpNumbers(color1.g, color2.g, lerp),
					lerpNumbers(color1.b, color2.b, lerp),
					lerpNumbers(color1.a, color2.a, lerp)
				);
		},
		fromStyleString: function(styleString) {
			var noParens = styleString.split('(')[1].split(')')[0];
			var stringVals = noParens.split(',');
			var numberVals = stringVals.map(function(val) { return parseFloat(val); });
			return new Color(numberVals[0], numberVals[1], numberVals[2], numberVals[3]);
		},
	}

	function Pixel(color, deathTime, interpolationFunction) {
		this.color = color.clone();
		this.deathTime = deathTime;
		this.interpolationFunction = interpolationFunction || EasingFunctions.linear;

		this.lifeTime = 0;
		this.alive = true;
		this.deathColor = color;
		this.position = {x: 0, y: 0};

		this.update = function(dt) {
			this.lifeTime += dt;
			if (this.lifeTime >= this.deathTime) {
				this.alive = false;
				return;
			}

			var interp = this.lifeTime / this.deathTime;
			this.color.a = 255 * (1 - interp);
		};

		this.draw = function(canvas, ctx, size) {
			if (!this.alive)
				return;

			ctx.globalAlpha = this.color.a / 255;
			ctx.fillStyle = this.color.toStyleString();
			ctx.fillRect(this.position.x, this.position.y, size, size);
		};
	}
	Pixels = { // static utilities for Pixel
	};

	ScreenPositionToColorsMap = {
		BORDERING_COLORS_INTERPOLATION_RANGE: 60, // px
		colorIntervals: [], // contains list of: { screenInterval = [start, end], pixelsColor = color object }

		colorsAtVerticalPosition: function(verticalPosition) {
			var color = DEFAULT_COLOR;
			for (var i = 0; i < this.colorIntervals.length; i++) {
				var interval = this.colorIntervals[i].screenInterval;
				if (verticalPosition >= interval[0] && verticalPosition <= interval[1]) {
					color = this.colorIntervals[i].pixelsColor;
					//interp between bordering colors
					var interpRange = this.BORDERING_COLORS_INTERPOLATION_RANGE;
					if (i > 0 && interval[0] + interpRange >= verticalPosition) { // near previous color
						var interp = (((interval[0] + interpRange) - verticalPosition) / interpRange) * 0.5;
						var previousColor = this.colorIntervals[i - 1].pixelsColor;
						color = Colors.interpolateBetween(color, previousColor, EasingFunctions.linear, interp);
					} else if (i < (this.colorIntervals.length - 1) && interval[1] - interpRange <= verticalPosition) { // near next color
						var interp = ((verticalPosition - (interval[1] - interpRange)) / interpRange) * 0.5;
						var nextColor = this.colorIntervals[i + 1].pixelsColor;
						color = Colors.interpolateBetween(color, nextColor, EasingFunctions.linear, interp);
					}
					break;
				}
			}
			return color;
		},
	};

	PixelManager = {
		allPixels: {},
		pixelCount: 0,

		spawnPixels: function(options) {
			options = options || {};
			var count = options.count || 1;

			var maxIndex = pixelsAcross * pixelsDown;
			for (var i = 0; i < count; i++) {
				if (this.pixelCount >= maxIndex)
					return;

				var index = Math.floor(Math.random() * maxIndex);
				while (index in this.allPixels) { // linear probing ftw
					index = (index + 1) % maxIndex;
				}
				this.spawnPixelAtPositionIndex(index, options);
			}
		},

		spawnPixelAtPositionIndex: function(index, options) {
			if (index in this.allPixels)
				return;

			options = options || {};
			var randomInitialLifetime = options.randomInitialLifetime || false;

			this.allPixels[index] = new Pixel(DEFAULT_COLOR, PIXEL_DEATH_TIME);
			if (randomInitialLifetime)
				this.allPixels[index].lifeTime = Math.random() * PIXEL_DEATH_TIME / 2;
			this.pixelCount++;
		},

		removePixel: function(positionIndex) {
			if (positionIndex in this.allPixels) {
				delete this.allPixels[positionIndex];
				this.pixelCount--;
			}
		},

		updatePixels: function(dt) {
			var canvasTop = pixelsCanvas.offset().top - $(window).scrollTop();
			for (i in this.allPixels) {
				var pixel = this.allPixels[i];
				pixel.update(dt);
				if (!pixel.alive) {
					this.removePixel(i);
					continue;
				}

				var tileX = (i % pixelsAcross);
				var tileY = Math.floor(i / pixelsAcross);
				pixel.position.x = tileX * pixelSize;
				pixel.position.y = tileY * pixelSize;

				var color = ScreenPositionToColorsMap.colorsAtVerticalPosition(canvasTop + pixel.position.y);
				pixel.color.r = Math.round(color.r);
				pixel.color.g = Math.round(color.g);
				pixel.color.b = Math.round(color.b);
			}
		},

		drawPixels: function(canvas, ctx) {
			for (i in this.allPixels) {
				this.allPixels[i].draw(canvas, ctx, pixelSize);
			}
		},
	};

	var NUM_PIXELS_ACROSS_VERT = 10;
	var NUM_PIXELS_ACROSS_HORIZ = 60;
	var PIXEL_DEATH_TIME = 4 * 1000; // ms
	var PIXEL_SPAWN_INTERVAL = 6; // ms
	var DEFAULT_COLOR = Colors.WHITE;

	var pixelSize;
	var pixelsAcross;
	var pixelsDown;

	var lastUpdateTime = new Date().getTime();
	var pixelSpawnTimer = 0;
	var pixelsCanvas = $('#pixels');
	var pixelsCanvasDOM = $('#pixels')[0];
	var pixelsContext = pixelsCanvasDOM.getContext('2d');

	//Start everything off
	if (isCanvasSupported()) {
		pixelsCanvas.show();
		initPixels();
	} else {
		pixelsCanvas.hide();
		return;
	}

	function initPixels() {
		recalculateCanvasData();

		pixelsCanvas.mousemove(canvasMouseMoveHandler);
		pixelsCanvas.on('touchstart', canvasMouseMoveHandler);
		pixelsCanvas.on('touchmove', canvasMouseMoveHandler);

		requestAnimFrame(function() { updatePixels(); });
	}

	function updatePixels() {
		var currentUpdateTime = new Date().getTime();
		var dt = currentUpdateTime - lastUpdateTime;
		lastUpdateTime = currentUpdateTime;

		recalculateCanvasData();

		pixelSpawnTimer += dt;
		if (pixelSpawnTimer >= PIXEL_SPAWN_INTERVAL) {
			var count = Math.floor(pixelSpawnTimer / PIXEL_SPAWN_INTERVAL);
			PixelManager.spawnPixels({count: count, randomInitialLifetime: true});
			pixelSpawnTimer -= PIXEL_SPAWN_INTERVAL * count;
		}

		PixelManager.updatePixels(dt);

		drawPixels();
	}

	function recalculateCanvasData() {
		var contentDivs = $('[data-pixel-colors]');
		var height = pixelsCanvas.height(); // get CSS dimensions of box containing canvas
		var width = pixelsCanvas.width();
		pixelsCanvasDOM.height = height; // actually set canvas dimensions so it isn't stretched into its box
		pixelsCanvasDOM.width = width;

		var canvasVertical = height > width;
		if (canvasVertical) {
			pixelSize = width / NUM_PIXELS_ACROSS_VERT;
			pixelsAcross = NUM_PIXELS_ACROSS_VERT;
			pixelsDown = Math.ceil(height / pixelSize);
		} else {
			pixelSize = width / NUM_PIXELS_ACROSS_HORIZ;
			pixelsAcross = NUM_PIXELS_ACROSS_HORIZ;
			pixelsDown = Math.ceil(height / pixelSize);
		}

		ScreenPositionToColorsMap.colorIntervals = [];
		for	(var i = 0; i < contentDivs.length; i++) {
			var content = $(contentDivs[i]);
			var pixelsColor = content.attr('data-pixel-colors') || DEFAULT_COLOR;
			//content.css('border', "5px solid " + pixelsColor);

			var scrollY = $(window).scrollTop();
			var screenheight = $(window).height();
			var contentY = content.offset().top;
			var contentHeight = content.outerHeight();
			var screenInterval = [contentY - scrollY, (contentY - scrollY) + contentHeight];

			if (screenInterval[1] >= 0 && screenInterval[0] <= screenheight) { // filter out off-screen intervals
				ScreenPositionToColorsMap.colorIntervals.push({screenInterval: screenInterval, pixelsColor: Colors.fromStyleString(pixelsColor)});
			}
		}
		ScreenPositionToColorsMap.colorIntervals.sort(function(a, b) { return a.screenInterval[0] - b.screenInterval[0]; }); // sort by beginning of interval, ascending
	}

	function canvasMouseMoveHandler(evt) {
		var tileX, tileY;
		var indices = [];

		if (evt.originalEvent instanceof MouseEvent) {
			tileX = Math.floor(evt.offsetX / pixelSize);
			tileY = Math.floor(evt.offsetY / pixelSize);
			indices.push(tileY * pixelsAcross + tileX);
		} else if (evt.originalEvent instanceof TouchEvent) {
			evt.preventDefault(); // prevent default scrolling behavior which causes touchmove events to stop

			var canvasLeft = pixelsCanvas.position().left;
			var canvasTop = pixelsCanvas.position().top;
			for (var i = 0; i < evt.originalEvent.changedTouches.length; i++) {
				var touch = evt.originalEvent.changedTouches[i];
				tileX = Math.floor((touch.clientX - canvasLeft) / pixelSize);
				tileY = Math.floor((touch.clientY - canvasTop) / pixelSize);
				if (tileX < 0 || tileY < 0)
					continue;
				indices.push(tileY * pixelsAcross + tileX);
			}
		}

		for (var i = 0; i < indices.length; i++) {
			PixelManager.spawnPixelAtPositionIndex(indices[i]);
			PixelManager.allPixels[indices[i]].lifeTime = 0;
		}
	}

	function drawPixels() {
		var canvas = pixelsCanvas;
		var ctx = pixelsContext;
		ctx.clearRect(0, 0, canvas.width, canvas.height);

		PixelManager.drawPixels(canvas, ctx);

		requestAnimFrame(function() { updatePixels(); });
	}
});

// Utilities
window.lerpNumbers = function(a, b, interpolation) {
	if (a === b)
		return a;
	return a * (1 - interpolation) + b * interpolation;
};

window.requestAnimFrame = (function(){
	return  window.requestAnimationFrame ||
		window.webkitRequestAnimationFrame ||
		window.mozRequestAnimationFrame ||
		function(callback) {
			window.setTimeout(callback, 1000 / 60);
		};
})();

window.isCanvasSupported = function() {
	var elem = document.createElement('canvas');
	return !!(elem.getContext && elem.getContext('2d'));
};

EasingFunctions = {
	// no easing, no acceleration
	linear: function (t) { return t },
	// accelerating from zero velocity
	easeInQuad: function (t) { return t*t },
	// decelerating to zero velocity
	easeOutQuad: function (t) { return t*(2-t) },
	// acceleration until halfway, then deceleration
	easeInOutQuad: function (t) { return t<.5 ? 2*t*t : -1+(4-2*t)*t },
	// accelerating from zero velocity
	easeInCubic: function (t) { return t*t*t },
	// decelerating to zero velocity
	easeOutCubic: function (t) { return (--t)*t*t+1 },
	// acceleration until halfway, then deceleration
	easeInOutCubic: function (t) { return t<.5 ? 4*t*t*t : (t-1)*(2*t-2)*(2*t-2)+1 },
	// accelerating from zero velocity
	easeInQuart: function (t) { return t*t*t*t },
	// decelerating to zero velocity
	easeOutQuart: function (t) { return 1-(--t)*t*t*t },
	// acceleration until halfway, then deceleration
	easeInOutQuart: function (t) { return t<.5 ? 8*t*t*t*t : 1-8*(--t)*t*t*t },
	// accelerating from zero velocity
	easeInQuint: function (t) { return t*t*t*t*t },
	// decelerating to zero velocity
	easeOutQuint: function (t) { return 1+(--t)*t*t*t*t },
	// acceleration until halfway, then deceleration
	easeInOutQuint: function (t) { return t<.5 ? 16*t*t*t*t*t : 1+16*(--t)*t*t*t*t }
}<|endoftext|>


<commit_before>var gulp = require('gulp');
var rimraf = require('gulp-rimraf');
var watch = require('gulp-watch')
var browserSync = require('browser-sync');
var reload = browserSync.reload;
var concat = require("gulp-concat");
var babel = require("gulp-babel");
var sourcemaps = require("gulp-sourcemaps");
var uglify = require('gulp-uglify');
var cssmin = require('gulp-uglify');
//var debug = require('gulp-debug');

var config = {
  entryFiles: [
    './src/components/map/core/init.js',
    './src/components/map/core/log.js',
    './src/components/utilities/polyfills.js',
    './src/components/utilities/general.js',
    './src/components/utilities/environment.js',
    './src/components/preloading/Preload.js',
    './src/components/map/core/utils/dataManipulation.js',
    './src/components/map/core/utils/effects.js',
    './src/components/map/core/utils/utils.js',
    './src/components/map/core/mapApi.js',
    './src/components/map/core/mapEvents.js',
    './src/components/map/core/eventlisteners.js',
    './src/components/map/core/MapLayers.js',
    './src/components/map/core/MapDataManipulator.js',
    './src/components/map/core/UI.js',
    './src/components/map/core/ObjectManager.js',
    './src/components/map/core/Objects.js',
    './src/components/map/core/Sound.js',
    './src/components/map/core/mapStates.js',
    './src/components/map/core/UI.js',
    './src/components/map/core/UITemplateBase.js',
    './src/components/map/core/baseEventlisteners/*.js',
    './src/components/map/core/move/*.js',
    './src/components/map/core/zoom/*.js',
    './src/components/map/extensions/basicActions/*.js',
    './src/components/map/extensions/hexagons/init.js',
    './src/components/map/extensions/hexagons/utils/hexagonMath.js',
    './src/components/map/extensions/hexagons/utils/createHexagon.js',
    './src/components/map/extensions/hexagons/eventListeners/*.js',
    './src/components/map/extensions/hexagons/*.js',
    './src/components/map/extensions/mapMovement/mapMovement.js',
    './src/components/map/UIs/default/init.js',
    './src/components/map/UIs/default/utils/*.js',
    './src/components/map/UIs/default/layout/*.js',
    './src/components/map/UIs/default/default.js',
    './src/components/map/core/Flatworld.js',
    './src/factories/*.js'],
  cssFiles: ['./src/assets/lib/pace/css/loadingBar.css'],
  outputDir: './dist/',
  outputDirDev: './src/dist/',
  outputFile: 'flatworld.js'
};

gulp.task('deploy', ['clean', 'bundleLib', 'bundleCss', 'bundleProd']);

gulp.task('build', ['cleanDev', 'bundleLibDev', 'bundleCssDev', 'bundleDev']);

gulp.task('bundleProd', function() {
  return gulp.src(config.entryFiles)
    .pipe(sourcemaps.init())
    .pipe(babel())
    .pipe(uglify())
    .pipe(concat('flatworld.min.js'))
    .pipe(sourcemaps.write("."))
    .pipe(gulp.dest(config.outputDir));
});

gulp.task('bundleLib', function() {
  return gulp.src('./src/assets/lib/**/*.js', { base: './src/assets/lib/' })
    .pipe(concat('flatworld_libraries.js'))
    .pipe(gulp.dest(config.outputDir));
});

gulp.task('bundleLibDev', function() {
  return gulp.src('./src/assets/lib/**/*.js', { base: './src/assets/lib/' })
    .pipe(concat('flatworld_libraries.js'))
    .pipe(gulp.dest(config.outputDirDev));
});

gulp.task('bundleDev', function() {
  return gulp.src(config.entryFiles)
    .pipe(sourcemaps.init())
    .pipe(babel())
    .pipe(uglify())
    .pipe(concat('flatworld.js'))
    .pipe(sourcemaps.write("."))
    .pipe(gulp.dest(config.outputDirDev));
});

gulp.task('watch', function() {
  return gulp.watch(config.entryFiles, ['bundleDev']);
});

gulp.task('cleanDev', function(){
  return rimraf(config.outputDirDev);
});

gulp.task('clean', function(){
  return rimraf(config.outputDir);
});

gulp.task('bundleCss', function () {
  return gulp.src(config.cssFiles)
    .pipe(cssmin())
    .pipe(concat('flatworld.css'))
    .pipe(gulp.dest(config.outputDir + "css/"));
});

gulp.task('bundleCssDev', function () {
  return gulp.src(config.cssFiles)
    .pipe(concat('flatworld.css'))
    .pipe(gulp.dest(config.outputDirDev + "css/"));
});

gulp.task('develope', ['build', 'watch'], function() {
  console.log("HEP")
  return browserSync({
    server: {
      baseDir: './src/'
    },
    startPath: "tests/manualStressTest.html"
  });
});

gulp.task('default', ['deploy', 'build']);<commit_msg>fixed capitalization regarding mapApi in gulpfile. This worked in windows, only noticed in linux<commit_after>var gulp = require('gulp');
var rimraf = require('gulp-rimraf');
var watch = require('gulp-watch')
var browserSync = require('browser-sync');
var reload = browserSync.reload;
var concat = require("gulp-concat");
var babel = require("gulp-babel");
var sourcemaps = require("gulp-sourcemaps");
var uglify = require('gulp-uglify');
var cssmin = require('gulp-uglify');
//var debug = require('gulp-debug');

var config = {
  entryFiles: [
    './src/components/map/core/init.js',
    './src/components/map/core/log.js',
    './src/components/utilities/polyfills.js',
    './src/components/utilities/general.js',
    './src/components/utilities/environment.js',
    './src/components/preloading/Preload.js',
    './src/components/map/core/utils/dataManipulation.js',
    './src/components/map/core/utils/effects.js',
    './src/components/map/core/utils/utils.js',
    './src/components/map/core/mapAPI.js',
    './src/components/map/core/mapEvents.js',
    './src/components/map/core/eventlisteners.js',
    './src/components/map/core/MapLayers.js',
    './src/components/map/core/MapDataManipulator.js',
    './src/components/map/core/UI.js',
    './src/components/map/core/ObjectManager.js',
    './src/components/map/core/Objects.js',
    './src/components/map/core/Sound.js',
    './src/components/map/core/mapStates.js',
    './src/components/map/core/UI.js',
    './src/components/map/core/UITemplateBase.js',
    './src/components/map/core/baseEventlisteners/*.js',
    './src/components/map/core/move/*.js',
    './src/components/map/core/zoom/*.js',
    './src/components/map/extensions/basicActions/*.js',
    './src/components/map/extensions/hexagons/init.js',
    './src/components/map/extensions/hexagons/utils/hexagonMath.js',
    './src/components/map/extensions/hexagons/utils/createHexagon.js',
    './src/components/map/extensions/hexagons/eventListeners/*.js',
    './src/components/map/extensions/hexagons/*.js',
    './src/components/map/extensions/mapMovement/mapMovement.js',
    './src/components/map/UIs/default/init.js',
    './src/components/map/UIs/default/utils/*.js',
    './src/components/map/UIs/default/layout/*.js',
    './src/components/map/UIs/default/default.js',
    './src/components/map/core/Flatworld.js',
    './src/factories/*.js'],
  cssFiles: ['./src/assets/lib/pace/css/loadingBar.css'],
  outputDir: './dist/',
  outputDirDev: './src/dist/',
  outputFile: 'flatworld.js'
};

gulp.task('deploy', ['clean', 'bundleLib', 'bundleCss', 'bundleProd']);

gulp.task('build', ['cleanDev', 'bundleLibDev', 'bundleCssDev', 'bundleDev']);

gulp.task('bundleProd', function() {
  return gulp.src(config.entryFiles)
    .pipe(sourcemaps.init())
    .pipe(babel())
    .pipe(uglify())
    .pipe(concat('flatworld.min.js'))
    .pipe(sourcemaps.write("."))
    .pipe(gulp.dest(config.outputDir));
});

gulp.task('bundleLib', function() {
  return gulp.src('./src/assets/lib/**/*.js', { base: './src/assets/lib/' })
    .pipe(concat('flatworld_libraries.js'))
    .pipe(gulp.dest(config.outputDir));
});

gulp.task('bundleLibDev', function() {
  return gulp.src('./src/assets/lib/**/*.js', { base: './src/assets/lib/' })
    .pipe(concat('flatworld_libraries.js'))
    .pipe(gulp.dest(config.outputDirDev));
});

gulp.task('bundleDev', function() {
  return gulp.src(config.entryFiles)
    .pipe(sourcemaps.init())
    .pipe(babel())
    .pipe(uglify())
    .pipe(concat('flatworld.js'))
    .pipe(sourcemaps.write("."))
    .pipe(gulp.dest(config.outputDirDev));
});

gulp.task('watch', function() {
  return gulp.watch(config.entryFiles, ['bundleDev']);
});

gulp.task('cleanDev', function(){
  return rimraf(config.outputDirDev);
});

gulp.task('clean', function(){
  return rimraf(config.outputDir);
});

gulp.task('bundleCss', function () {
  return gulp.src(config.cssFiles)
    .pipe(cssmin())
    .pipe(concat('flatworld.css'))
    .pipe(gulp.dest(config.outputDir + "css/"));
});

gulp.task('bundleCssDev', function () {
  return gulp.src(config.cssFiles)
    .pipe(concat('flatworld.css'))
    .pipe(gulp.dest(config.outputDirDev + "css/"));
});

gulp.task('develope', ['build', 'watch'], function() {
  console.log("HEP")
  return browserSync({
    server: {
      baseDir: './src/'
    },
    startPath: "tests/manualStressTest.html"
  });
});

gulp.task('default', ['deploy', 'build']);<|endoftext|>


<commit_before>import Vue from 'vue'

import { getScrollPosition, getScrollTarget, getHorizontalScrollPosition } from '../../utils/scroll.js'
import { listenOpts, noop } from '../../utils/event.js'

const { passive } = listenOpts

export default Vue.extend({
  name: 'QScrollObserver',

  props: {
    debounce: [ String, Number ],
    horizontal: Boolean,

    scrollTarget: {
      default: void 0
    }
  },

  render: noop, // eslint-disable-line

  data () {
    return {
      pos: 0,
      dir: this.horizontal === true ? 'right' : 'down',
      dirChanged: false,
      dirChangePos: 0
    }
  },

  watch: {
    scrollTarget () {
      this.__unconfigureScrollTarget()
      this.__configureScrollTarget()
    },

    '$q.lang.rtl' () {
      this.trigger(true)
    }
  },

  methods: {
    getPosition () {
      return {
        position: this.pos,
        direction: this.dir,
        directionChanged: this.dirChanged,
        inflexionPosition: this.dirChangePos
      }
    },

    trigger (immediately) {
      if (immediately === true || this.debounce === 0 || this.debounce === '0') {
        this.__emit()
      }
      else if (this.clearTimer === void 0) {
        const [ timer, fn ] = this.debounce
          ? [ setTimeout(this.__emit, this.debounce), clearTimeout ]
          : [ requestAnimationFrame(this.__emit), cancelAnimationFrame ]

        this.clearTimer = () => {
          fn(timer)
          this.clearTimer = void 0
        }
      }
    },

    __emit () {
      this.clearTimer !== void 0 && this.clearTimer()

      const fn = this.horizontal === true
        ? getHorizontalScrollPosition
        : getScrollPosition

      const
        pos = Math.max(0, fn(this.__scrollTarget)),
        delta = pos - this.pos,
        dir = this.horizontal === true
          ? delta < 0 ? 'left' : 'right'
          : delta < 0 ? 'up' : 'down'

      this.dirChanged = this.dir !== dir

      if (this.dirChanged) {
        this.dir = dir
        this.dirChangePos = this.pos
      }

      this.pos = pos
      this.$emit('scroll', this.getPosition())
    },

    __configureScrollTarget () {
      this.__scrollTarget = getScrollTarget(this.$el.parentNode, this.scrollTarget)
      this.__scrollTarget.addEventListener('scroll', this.trigger, passive)
      this.trigger(true)
    },

    __unconfigureScrollTarget () {
      if (this.__scrollTarget !== void 0) {
        this.__scrollTarget.removeEventListener('scroll', this.trigger, passive)
        this.__scrollTarget = void 0
      }
    }
  },

  mounted () {
    this.__configureScrollTarget()
  },

  beforeDestroy () {
    this.clearTimer !== void 0 && this.clearTimer()
    this.__unconfigureScrollTarget()
  }
})
<commit_msg>chore(QScrollObserver): small tweak to $q.rtl watcher<commit_after>import Vue from 'vue'

import { getScrollPosition, getScrollTarget, getHorizontalScrollPosition } from '../../utils/scroll.js'
import { listenOpts, noop } from '../../utils/event.js'

const { passive } = listenOpts

export default Vue.extend({
  name: 'QScrollObserver',

  props: {
    debounce: [ String, Number ],
    horizontal: Boolean,

    scrollTarget: {
      default: void 0
    }
  },

  render: noop, // eslint-disable-line

  data () {
    return {
      pos: 0,
      dir: this.horizontal === true ? 'right' : 'down',
      dirChanged: false,
      dirChangePos: 0
    }
  },

  watch: {
    scrollTarget () {
      this.__unconfigureScrollTarget()
      this.__configureScrollTarget()
    },

    '$q.lang.rtl' () {
      this.__emit()
    }
  },

  methods: {
    getPosition () {
      return {
        position: this.pos,
        direction: this.dir,
        directionChanged: this.dirChanged,
        inflexionPosition: this.dirChangePos
      }
    },

    trigger (immediately) {
      if (immediately === true || this.debounce === 0 || this.debounce === '0') {
        this.__emit()
      }
      else if (this.clearTimer === void 0) {
        const [ timer, fn ] = this.debounce
          ? [ setTimeout(this.__emit, this.debounce), clearTimeout ]
          : [ requestAnimationFrame(this.__emit), cancelAnimationFrame ]

        this.clearTimer = () => {
          fn(timer)
          this.clearTimer = void 0
        }
      }
    },

    __emit () {
      this.clearTimer !== void 0 && this.clearTimer()

      const fn = this.horizontal === true
        ? getHorizontalScrollPosition
        : getScrollPosition

      const
        pos = Math.max(0, fn(this.__scrollTarget)),
        delta = pos - this.pos,
        dir = this.horizontal === true
          ? delta < 0 ? 'left' : 'right'
          : delta < 0 ? 'up' : 'down'

      this.dirChanged = this.dir !== dir

      if (this.dirChanged) {
        this.dir = dir
        this.dirChangePos = this.pos
      }

      this.pos = pos
      this.$emit('scroll', this.getPosition())
    },

    __configureScrollTarget () {
      this.__scrollTarget = getScrollTarget(this.$el.parentNode, this.scrollTarget)
      this.__scrollTarget.addEventListener('scroll', this.trigger, passive)
      this.trigger(true)
    },

    __unconfigureScrollTarget () {
      if (this.__scrollTarget !== void 0) {
        this.__scrollTarget.removeEventListener('scroll', this.trigger, passive)
        this.__scrollTarget = void 0
      }
    }
  },

  mounted () {
    this.__configureScrollTarget()
  },

  beforeDestroy () {
    this.clearTimer !== void 0 && this.clearTimer()
    this.__unconfigureScrollTarget()
  }
})
<|endoftext|>


<commit_before>'use strict';

const Command = require('../../Command.js');
const EnhancementEmbed = require('../../embeds/EnhancementEmbed.js');
const arcanes = require('../../resources/arcanes.json');

/**
 * Displays the response time for the bot and checks Warframe's servers to see if they are up
 */
class Arcane extends Command {
  /**
   * Constructs a callable command
   * @param {Genesis} bot The bot object
   */
  constructor(bot) {
    super(bot, 'warframe.misc.arcane', 'arcane', 'Get information about an Arcane Enhancement');
    this.regex = new RegExp('^arcane(.+)?', 'i');
    this.usages = [
      {
        description: 'Get information about an Arcane Enhancement',
        parameters: ['enhancement name'],
      },
    ];
  }

  /**
   * Run the command
   * @param {Message} message Message with a command to handle, reply to,
   *                          or perform an action based on parameters.
   */
  run(message) {
    let arcane = message.strippedContent.match(this.regex)[1];
    if (arcane) {
      arcane = arcane.trim().toLowerCase();
      arcanes.forEach((enhancement) => {
        if (new RegExp(enhancement.regex, 'ig').test(arcane)) {
          this.messageManager.reply(message, new EnhancementEmbed(enhancement), true, false);
        }
      });
    } else {
      this.messageManager.embed(message, new EnhancementEmbed(undefined), true, false);
    }
  }
}

module.exports = Arcane;
<commit_msg>fix reply instead of embed<commit_after>'use strict';

const Command = require('../../Command.js');
const EnhancementEmbed = require('../../embeds/EnhancementEmbed.js');
const arcanes = require('../../resources/arcanes.json');

/**
 * Displays the response time for the bot and checks Warframe's servers to see if they are up
 */
class Arcane extends Command {
  /**
   * Constructs a callable command
   * @param {Genesis} bot The bot object
   */
  constructor(bot) {
    super(bot, 'warframe.misc.arcane', 'arcane', 'Get information about an Arcane Enhancement');
    this.regex = new RegExp('^arcane(.+)?', 'i');
    this.usages = [
      {
        description: 'Get information about an Arcane Enhancement',
        parameters: ['enhancement name'],
      },
    ];
  }

  /**
   * Run the command
   * @param {Message} message Message with a command to handle, reply to,
   *                          or perform an action based on parameters.
   */
  run(message) {
    let arcane = message.strippedContent.match(this.regex)[1];
    if (arcane) {
      arcane = arcane.trim().toLowerCase();
      arcanes.forEach((enhancement) => {
        if (new RegExp(enhancement.regex, 'ig').test(arcane)) {
          this.messageManager.embed(message, new EnhancementEmbed(enhancement), true, false);
        }
      });
    } else {
      this.messageManager.embed(message, new EnhancementEmbed(undefined), true, false);
    }
  }
}

module.exports = Arcane;
<|endoftext|>