<commit_before>import { SimpleSchema } from 'meteor/aldeed:simple-schema';
import { Slugs } from '/imports/api/slug/SlugCollection';
import { Semesters } from '/imports/api/semester/SemesterCollection';
import { Opportunities } from '/imports/api/opportunity/OpportunityCollection';
import { Users } from '/imports/api/user/UserCollection';
import { Courses } from '/imports/api/course/CourseCollection';
import BaseInstanceCollection from '/imports/api/base/BaseInstanceCollection';
import { radgradCollections } from '/imports/api/integrity/RadGradCollections';
import { _ } from 'meteor/erasaur:meteor-lodash';
import { Meteor } from 'meteor/meteor';

/** @module Review */

/**
 * Represents an Opportunity, such as "LiveWire Internship".
 * To represent an Opportunity taken by a specific student in a specific semester, use OpportunityInstance.
 * @extends module:BaseInstance~BaseInstanceCollection
 */
class ReviewCollection extends BaseInstanceCollection {

  /**
   * Creates the Review collection.
   */
  constructor() {
    super('Review', new SimpleSchema({
      slugID: { type: SimpleSchema.RegEx.Id },
      studentID: { type: SimpleSchema.RegEx.Id },
      reviewType: { type: String },
      revieweeID: { type: SimpleSchema.RegEx.Id },
      semesterID: { type: SimpleSchema.RegEx.Id },
      rating: { type: Number },
      comments: { type: String },
      moderated: { type: Boolean },
      visible: { type: Boolean },
      moderatorComments: { type: String, optional: true},
    }));
  }

  /**
   * Defines a new Opportunity.
   * @example
   * Opportunities.define({ name: 'ATT Hackathon',
   *                        slug: 'att-hackathon',
   *                        description: 'Programming challenge at Sacred Hearts Academy, $10,000 prize',
   *                        opportunityType: 'event',
   *                        sponsor: 'philipjohnson',
   *                        ice: { i: 10, c: 0, e: 10},
   *                        interests: ['software-engineering'],
   *                        semesters: ['Fall-2016', 'Spring-2016', 'Summer-2106'],
    *                       moreInformation: 'http://atthackathon.com',
     *                      independentStudy: true});
   * @param { Object } description Object with keys student, reviewee, semester, rating, comments, moderated,
   * public, and moderatorComments.
   * Slug must not be previously defined.
   * OpportunityType and sponsor must be defined slugs.
   * Interests must be a (possibly empty) array of interest slugs or IDs.
   * Semesters must be a (possibly empty) array of semester slugs or IDs.
   * Sponsor must be a User with role 'FACULTY', 'ADVISOR', or 'ADMIN'.
   * ICE must be a valid ICE object.
   * MoreInformation is optional, but if supplied should be a URL.
   * IndependentStudy is optional and defaults to false.
   * @throws {Meteor.Error} If the definition includes a defined slug or undefined interest, sponsor, opportunityType,
   * or startActive or endActive are not valid.
   * @returns The newly created docID.
   */
  define({ slug, student, reviewType, reviewee, semester, rating, comments,
      moderated = false, visible = true, moderatorComments }) {
    // Get instances, or throw error
    const studentID = Users.getID(student);
    // Get instances, or throw error if not found or not a valid reviewType
    let revieweeID;
    if (reviewType === 'course') {
      revieweeID = Courses.getID(reviewee);
    } else if (reviewType === 'opportunity') {
      revieweeID = Opportunities.getID(reviewee);
    } else {
      throw new Meteor.Error(`reviewType ${reviewType} is not a valid reviewType.`);
    }
    console.log(semester);
    const semesterID = Semesters.getID(semester);
    // Make sure rating is a number between 1 and 5.
    if (!(typeof rating) === 'number' || (rating < 1) || (rating > 5)) {
      throw new Meteor.Error(`Rating ${rating} is not a number between 1 and 5.`);
    }
    // Guarantee that moderated and public are booleans.
    /* eslint no-param-reassign: "off" */
    moderated = !!moderated;
    visible = !!visible;
    // Get SlugID, throw error if found.
    const slugID = Slugs.define({ name: slug, entityName: this.getType() });
    // Define the new Review and its Slug.
    const reviewID = this._collection.insert({ slugID, studentID, reviewType, revieweeID, semesterID,
      rating, comments, moderated, visible, moderatorComments });
    Slugs.updateEntityID(slugID, reviewID);

    // Return the id to the newly created Opportunity.
    return reviewID;
  }

  /**
   * Removes the passed Opportunity and its associated Slug.
   * @param opportunity The document or _id associated with this Opportunity.
   * @throws {Meteor.Error} If opportunity is not defined or there are any OpportunityInstances associated with it.
   */
  removeIt(review) {
    // TODO: check for defined OpportunityInstances before deletion.
    super.removeIt(review);
  }

  /**
   * Returns the slug for the given opportunity ID.
   * @param opportunityID the opportunity ID.
   */
  getSlug(reviewID) {
    this.assertDefined(reviewID);
    const reviewDoc = this.findDoc(reviewID);
    return Slugs.findDoc(reviewDoc.slugID).name;
  }

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

  /**
   * Returns an object representing the Opportunity docID in a format acceptable to define().
   * @param docID The docID of an Opportunity.
   * @returns { Object } An object representing the definition of docID.
   */
  dumpOne(docID) {
    const doc = this.findDoc(docID);
    const slug = Slugs.getNameFromID(doc.slugID);
    const student = Users.findSlugByID(doc.studentID);
    const reviewType = doc.reviewType;
    let reviewee;
    if (reviewType === 'course') {
      reviewee = Courses.findSlugByID(doc.revieweeID);
    } else if (reviewType === 'opportunity') {
      reviewee = Opportunities.findSlugByID(doc.revieweeID);
    }
    const semester = Semesters.findSlugByID(doc.semesterID);
    const rating = doc.rating;
    const comments = doc.comments;
    const moderated = doc.moderated;
    const visible = doc.visible;
    const moderatorComments = doc.moderatorComments;

    return { slug, student, reviewType, reviewee, semester, rating, comments, moderated, visible, moderatorComments  };
  }
}

/**
 * Provides the singleton instance of this class to all other entities.
 */
export const Reviews = new ReviewCollection();
radgradCollections.push(Reviews);
<commit_msg>Fixed review collection doc<commit_after>import { SimpleSchema } from 'meteor/aldeed:simple-schema';
import { Slugs } from '/imports/api/slug/SlugCollection';
import { Semesters } from '/imports/api/semester/SemesterCollection';
import { Opportunities } from '/imports/api/opportunity/OpportunityCollection';
import { Users } from '/imports/api/user/UserCollection';
import { Courses } from '/imports/api/course/CourseCollection';
import BaseInstanceCollection from '/imports/api/base/BaseInstanceCollection';
import { radgradCollections } from '/imports/api/integrity/RadGradCollections';
import { _ } from 'meteor/erasaur:meteor-lodash';
import { Meteor } from 'meteor/meteor';

/** @module Review */

/**
 * Represents a course or opportunity student Review
 * @extends module:BaseInstance~BaseInstanceCollection
 */
class ReviewCollection extends BaseInstanceCollection {

  /**
   * Creates the Review collection.
   */
  constructor() {
    super('Review', new SimpleSchema({
      slugID: { type: SimpleSchema.RegEx.Id },
      studentID: { type: SimpleSchema.RegEx.Id },
      reviewType: { type: String },
      revieweeID: { type: SimpleSchema.RegEx.Id },
      semesterID: { type: SimpleSchema.RegEx.Id },
      rating: { type: Number },
      comments: { type: String },
      moderated: { type: Boolean },
      visible: { type: Boolean },
      moderatorComments: { type: String, optional: true},
    }));
  }

  /**
   * Defines a new Review.
   * @example
   * Review.define({ slug: 'review-course-ics111-abi',
   *                 student: 'abi',
   *                 reviewType: 'course',
   *                 reviewee: 'ics111,
   *                 semester: 'Fall-2016',
   *                 rating: 3,
   *                 comments: 'sample comments here',
   *                 moderated: false,
   *                 visible: true,
   *                 moderatedComments: 'sample comments here'});
   * @param { Object } description Object with keys slug, student, reviewee,
   * reviewType,semester, rating, comments, moderated, public, and moderatorComments.
   * Slug must not be previously defined.
   * Reviewee must be a defined course or opportunity slug.
   * Semester must be a defined slug.
   * Student must be a user with role 'STUDENT.'
   * Moderated is optional and defaults to false.
   * Visible is optional and defaults to true.
   * ModeratorComments is optional.
   * @throws {Meteor.Error} If the definition includes a defined slug, undefined student or undefined reviewee
   * or startActive or endActive are not valid.
   * @returns The newly created docID.
   */
  define({ slug, student, reviewType, reviewee, semester, rating, comments,
      moderated = false, visible = true, moderatorComments }) {
    // Get instances, or throw error
    const studentID = Users.getID(student);
    // Get instances, or throw error if not found or not a valid reviewType
    let revieweeID;
    if (reviewType === 'course') {
      revieweeID = Courses.getID(reviewee);
    } else if (reviewType === 'opportunity') {
      revieweeID = Opportunities.getID(reviewee);
    } else {
      throw new Meteor.Error(`reviewType ${reviewType} is not a valid reviewType.`);
    }
    const semesterID = Semesters.getID(semester);
    // Make sure rating is a number between 1 and 5.
    if (!(typeof rating) === 'number' || (rating < 1) || (rating > 5)) {
      throw new Meteor.Error(`Rating ${rating} is not a number between 1 and 5.`);
    }
    // Guarantee that moderated and public are booleans.
    /* eslint no-param-reassign: "off" */
    moderated = !!moderated;
    visible = !!visible;
    // Get SlugID, throw error if found.
    const slugID = Slugs.define({ name: slug, entityName: this.getType() });
    // Define the new Review and its Slug.
    const reviewID = this._collection.insert({ slugID, studentID, reviewType, revieweeID, semesterID,
      rating, comments, moderated, visible, moderatorComments });
    Slugs.updateEntityID(slugID, reviewID);

    // Return the id to the newly created Review.
    return reviewID;
  }

  /**
   * Removes the passed Review and its associated Slug.
   * @param opportunity The document or _id associated with this Reivew.
   * @throws {Meteor.Error} If review is not defined.
   */
  removeIt(review) {
    super.removeIt(review);
  }

  /**
   * Returns the slug for the given opportunity ID.
   * @param opportunityID the opportunity ID.
   */
  getSlug(reviewID) {
    this.assertDefined(reviewID);
    const reviewDoc = this.findDoc(reviewID);
    return Slugs.findDoc(reviewDoc.slugID).name;
  }

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

  /**
   * Returns an object representing the Review docID in a format acceptable to define().
   * @param docID The docID of an Review.
   * @returns { Object } An object representing the definition of docID.
   */
  dumpOne(docID) {
    const doc = this.findDoc(docID);
    const slug = Slugs.getNameFromID(doc.slugID);
    const student = Users.findSlugByID(doc.studentID);
    const reviewType = doc.reviewType;
    let reviewee;
    if (reviewType === 'course') {
      reviewee = Courses.findSlugByID(doc.revieweeID);
    } else if (reviewType === 'opportunity') {
      reviewee = Opportunities.findSlugByID(doc.revieweeID);
    }
    const semester = Semesters.findSlugByID(doc.semesterID);
    const rating = doc.rating;
    const comments = doc.comments;
    const moderated = doc.moderated;
    const visible = doc.visible;
    const moderatorComments = doc.moderatorComments;

    return { slug, student, reviewType, reviewee, semester, rating, comments, moderated, visible, moderatorComments  };
  }
}

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


<commit_before>//ConditionalLines.js
'use strict';
define(["svg", "jquery", "app/BaseChart"],
	function(svg, $, BaseChart) {
		var ConditionalLines = function() {
			BaseChart.call(this);
			this.rect = null; //svg rectangle
			this.selectionUIs = null;
			this.draw = null; //svg.js drawing object
			this.rectX1 = 0;
			this.rectY1 = 0;
			this.rectX2 = 100;
			this.rectY2 = 100;

		};

		ConditionalLines.prototype = new BaseChart();

		ConditionalLines.prototype.constructor = ConditionalLines;

		ConditionalLines.prototype.render = function() {
			console.log("target id", this.target.attr("id"));
			var self = this;
			if (!this.rect) {
				console.log("no rect on render");
				this.draw = svg(this.target.attr("id")).size(this.width, this.height);
				this.rect = this.draw.rect(1, 1).attr({
					fill: '#f06',
					opacity: 0.5
				});
				this.selectionUI = this.draw.group();
				var c0 = this.draw.rect(5, 5).attr({
					fill: 'white',
					stroke: 'blue'
				}).id("c1").mousedown(function() {
					this.attr("class", "selected");
				});
				var c1 = c0.clone().id("c2").mousedown(function() {
					this.attr("class", "selected");
				});
				var c2 = c0.clone().id("c3").mousedown(function() {
					this.attr("class", "selected");
				});
				var c3 = c0.clone().id("c4").mousedown(function() {
					this.attr("class", "selected");
				});
				this.selectionUI.add(c0).add(c1).add(c2).add(c3);
				$(document).mousemove(function(data) {
					var x = self.parent.inverseXScale()(data.offsetX);
					var y = self.parent.inverseYScale()(data.offsetY);
					if (c0.attr("class") == "selected"){
						self.rectX1 = x;
						self.rectY1 = y;
						self.render();
					}
					else if (c1.attr("class") == "selected"){
						console.log(data);
						self.setRectDimensions({
							x: data.offsetX,
							y: self.rectY1,
							width: self.rectWidth+(self.rectX1-data.offsetX),
							height: data.offsetY-self.rectY1
						});
						self.render();
					}
					else if (c2.attr("class") == "selected"){
						console.log(data);
						self.setRectDimensions({
							x: self.rectX1,
							y: self.rectY1,
							width: data.offsetX-self.rectX1,
							height: data.offsetY-self.rectY1
						});
						self.render();
					}
					else if (c3.attr("class") == "selected"){
						console.log(data);
						self.setRectDimensions({
							x: self.rectX1,
							y: data.offsetY1,
							width: data.offsetX-self.rectX1,
							height:self.rectHeight+(self.rectY1-data.offsetY)
						});
						self.render();
					}
				});

				$(document).mouseup(function() {
					self.selectionUI.each(function(i, children) {
						this.attr("class", "");
					});
				});
			}

			var x = this.parent.xScale()(this.rectX1);
			var y = this.parent.yScale()(this.rectY1);
			var x2 =this.parent.xScale()(this.rectX2);
			var y2 =this.parent.yScale()(this.rectY2);
			var width =  x2-x;
			var height = y2-y;
			console.log("rect dimensions: x1=",this.rectX1,"x2=",this.rectX2,"y1=",this.rectY1,"y2=",this.rectY2);

			console.log("screen dimensions: x1=",x,"x2=",x2,"y1=",y,"y2=",y2,"width=",width,"height=",height,"\n============================\n\n");

			this.rect.size(width, height)
				.x(x)
				.y(y);
			this.selectionUI.children()[0].cx(x).cy(y);
			this.selectionUI.children()[1].cx(x).cy(y2);
			this.selectionUI.children()[2].cx(x2).cy(y2);
			this.selectionUI.children()[3].cx(x2).cy(y);

		};

		ConditionalLines.prototype.setRectDimensions = function(value) {
			if (!arguments.length) {
				return this;
			}
			this.rectX1 = this.parent.inverseXScale()(value.x);
			this.rectY1 = this.parent.inverseYScale()(value.y);

			this.rectX2 = this.parent.inverseXScale()(value.x+value.width);

			this.rectY2 = this.parent.inverseYScale()(value.y+value.height);

			return this;
		};

		return ConditionalLines;

	});<commit_msg>fixed bounding box adjustment<commit_after>//ConditionalLines.js
'use strict';
define(["svg", "jquery", "app/BaseChart"],
	function(svg, $, BaseChart) {
		var ConditionalLines = function() {
			BaseChart.call(this);
			this.rect = null; //svg rectangle
			this.selectionUIs = null;
			this.draw = null; //svg.js drawing object
			this.rectX1 = 0;
			this.rectY1 = 0;
			this.rectX2 = 100;
			this.rectY2 = 100;

		};

		ConditionalLines.prototype = new BaseChart();

		ConditionalLines.prototype.constructor = ConditionalLines;

		ConditionalLines.prototype.render = function() {
			console.log("target id", this.target.attr("id"));
			var self = this;
			if (!this.rect) {
				console.log("no rect on render");
				this.draw = svg(this.target.attr("id")).size(this.width, this.height);
				this.rect = this.draw.rect(1, 1).attr({
					fill: '#f06',
					opacity: 0.5
				});
				this.selectionUI = this.draw.group();
				var c0 = this.draw.rect(5, 5).attr({
					fill: 'white',
					stroke: 'blue'
				}).id("c1").mousedown(function() {
					this.attr("class", "selected");
				});
				var c1 = c0.clone().id("c2").mousedown(function() {
					this.attr("class", "selected");
				});
				var c2 = c0.clone().id("c3").mousedown(function() {
					this.attr("class", "selected");
				});
				var c3 = c0.clone().id("c4").mousedown(function() {
					this.attr("class", "selected");
				});
				this.selectionUI.add(c0).add(c1).add(c2).add(c3);
				$(document).mousemove(function(data) {
					var x = self.parent.inverseXScale()(data.offsetX);
					var y = self.parent.inverseYScale()(data.offsetY);

					if (c0.attr("class") == "selected"){
											console.log("mouse dimensions: x=",x,"y=",y);

						self.rectX1 = x;
						self.rectY1 = y;
						self.render();
					}
					else if (c1.attr("class") == "selected"){
											console.log("mouse dimensions: x=",x,"y=",y);

						self.rectX1=x;
						self.rectY2=y;
						self.render();
					}
					else if (c2.attr("class") == "selected"){
											console.log("mouse dimensions: x=",x,"y=",y);

						self.rectX2=x;
						self.rectY2=y;
						self.render();
					}
					else if (c3.attr("class") == "selected"){
											console.log("mouse dimensions: x=",x,"y=",y);

						self.rectX2=x;
						self.rectY1=y;
						self.render();
					}
				});

				$(document).mouseup(function() {
					self.selectionUI.each(function(i, children) {
						this.attr("class", "");
					});
				});
			}

			var x = this.parent.xScale()(this.rectX1);
			var y = this.parent.yScale()(this.rectY1);
			var x2 =this.parent.xScale()(this.rectX2);
			var y2 =this.parent.yScale()(this.rectY2);
			var width =  x2-x;
			var height = y2-y;
			console.log("rect dimensions: x1=",this.rectX1,"x2=",this.rectX2,"y1=",this.rectY1,"y2=",this.rectY2);

			console.log("screen dimensions: x1=",x,"x2=",x2,"y1=",y,"y2=",y2,"width=",width,"height=",height,"\n============================\n\n");

			this.rect.size(width, height)
				.x(x)
				.y(y);
			this.selectionUI.children()[0].cx(x).cy(y);
			this.selectionUI.children()[1].cx(x).cy(y2);
			this.selectionUI.children()[2].cx(x2).cy(y2);
			this.selectionUI.children()[3].cx(x2).cy(y);

		};

		ConditionalLines.prototype.setRectDimensions = function(value) {
			if (!arguments.length) {
				return this;
			}
			this.rectX1 = this.parent.inverseXScale()(value.x);
			this.rectY1 = this.parent.inverseYScale()(value.y);

			this.rectX2 = this.parent.inverseXScale()(value.x+value.width);

			this.rectY2 = this.parent.inverseYScale()(value.y+value.height);

			return this;
		};

		return ConditionalLines;

	});<|endoftext|>


<commit_before>/**
 * Backbone localStorage Adapter
 * Version 1.1.6
 *
 * https://github.com/jeromegn/Backbone.localStorage
 */
(function(root, factory) {
	if (typeof exports === 'object' && root.require) {
		module.exports = factory(require('underscore'), require('backbone'));
	} else if (typeof define === 'function' && define.amd) {
		// AMD. Register as an anonymous module.
		define(['underscore', 'backbone'], function(_, Backbone) {
			// Use global variables if the locals are undefined.
			return factory(_ || root._, Backbone || root.Backbone);
		});
	} else {
		// RequireJS isn't being used. Assume underscore and backbone are loaded in <script> tags
		factory(_, Backbone);
	}
}(this, function(_, Backbone) {
	// A simple module to replace `Backbone.sync` with *localStorage*-based
	// persistence. Models are given GUIDS, and saved into a JSON object. Simple
	// as that.

	// Get rid off browser prefixes
	window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
	window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction;
	window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;

	// Hold reference to Underscore.js and Backbone.js in the closure in order
	// to make things work even if they are removed from the global namespace

	// Generate four random hex digits.
	function S4() {
		return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
	};

	// Generate a pseudo-GUID by concatenating random hexadecimal.

	function guid() {
		return (S4() + S4() + '-' + S4() + '-' + S4() + '-' + S4() + '-' + S4() + S4() + S4());
	};

	// Our Store is represented by a single JS object in *localStorage*. Create it
	// with a meaningful name, like the name you'd give a table.
	// window.Store is deprectated, use Backbone.LocalStorage instead
	Backbone.LocalStorage = function(name) {
		if (!window.indexedDB) {
			throw 'Backbone.indexedDB: Environment does not support IndexedDB.';
		}
		this.name = name;
		this.db = null;
		var request = window.indexedDB.open('backbone-indexeddb', Backbone.LocalStorage.version);
		this.dbRequest = request;
		var that = this;

		request.addEventListener('error', function(e) {
			// user probably disallowed idb
			throw 'Error code: ' + this.errorCode;
			// what are the possible codes???
		});

		request.addEventListener('success', function(e) {
			that.db = this.result;
		});

		request.addEventListener('upgradeneeded', function(e) {
			var db = this.result;
			Backbone.LocalStorage.prepare(db);
		});

		//var store = this.localStorage().getItem(this.name);
		//this.records = (store && store.split(',')) || [];
	};

	_.extend(Backbone.LocalStorage.prototype, {

		// Save the current state of the **Store** to *localStorage*.
		save: function() {
			//this.localStorage().setItem(this.name, this.records.join(','));
		},

		// Add a model, giving it a (hopefully)-unique GUID, if it doesn't already
		// have an id of it's own.
		create: function(model, cb) {
			if (!model.id) {
				model.id = guid();
				model.set(model.idAttribute, model.id);
			}
			this.localStorage().add(model.toJSON());
			//this.localStorage().setItem(this.name + '-' + model.id, JSON.stringify(model));
			//this.records.push(model.id.toString());
			//this.save();
			this.find(model, cb);
		},

		// Update a model by replacing its copy in `this.data`.
		update: function(model, cb) {
			this.localStorage().put(model.toJSON());
			/*this.localStorage().setItem(this.name + '-' + model.id, JSON.stringify(model));
			if (!_.include(this.records, model.id.toString()))
				this.records.push(model.id.toString());
			this.save();*/
			this.find(model, cb);
		},

		// Retrieve a model from `this.data` by id.
		find: function(model, cb) {
			var request = this.localStorage().get(model.id);
			request.onsuccess = function() {
				cb(this.result);
			}
			request.onerror = function() {
				throw 'IndexDB Error: Can\'t read from or write to database';
			}
			//return this.jsonData(this.localStorage().getItem(this.name + '-' + model.id));
		},

		// Return the array of all models currently in storage.
		findAll: function(cb) {
			// Lodash removed _#chain in v1.0.0-rc.1
			/*return (_.chain || _)(this.records)
				.map(function(id) {
					return this.jsonData(this.localStorage().getItem(this.name + '-' + id));
				}, this)
				.compact()
				.value();*/
			var items = [];

			this.localStorage().openCursor().onsuccess = function(event) {
				var cursor = this.result;
				if (cursor) {
					items.push(cursor.value);
					cursor.continue();
				} else {
					cb(items);
				}
			};
		},

		// Delete a model from `this.data`, returning it.
		destroy: function(model, cb) {
			if (model.isNew())
				return false
			this.localStorage().delete(model.id);
			/*this.records = _.reject(this.records, function(id) {
				return id === model.id.toString();
			});
			this.save();*/
			cb(model);
			return model;
		},

		localStorage: function(cb) {
			var tx = this.db.transaction([this.name], 'readwrite');
			return tx.objectStore(this.name);
			//return localStorage;
		},

		// Clear localStorage for specific collection.
		_clear: function(cb) {
			var req = this.localStorage().clear();
			req.onsuccess = cb;
			req.onerror = cb;
			/*var local = this.localStorage(),
				itemRe = new RegExp('^' + this.name + '-');

			// Remove id-tracking item (e.g., 'foo').
			local.removeItem(this.name);

			// Lodash removed _#chain in v1.0.0-rc.1
			// Match all data items (e.g., 'foo-ID') and remove.
			(_.chain || _)(local).keys()
				.filter(function(k) {
					return itemRe.test(k);
				})
				.each(function(k) {
					local.removeItem(k);
				});

			this.records.length = 0;*/
		},

		// Size of localStorage.
		_storageSize: function(cb) {
			this.localStorage().count().onsuccess = cb;
		}

	});

	// localSync delegate to the model or collection's
	// *localStorage* property, which should be an instance of `Store`.
	// window.Store.sync and Backbone.localSync is deprecated, use Backbone.LocalStorage.sync instead
	Backbone.LocalStorage.sync = Backbone.localSync = function(method, model, options) {
		var store = model.localStorage || model.collection.localStorage;
		options = options || {};

		var that = this;

		var errorMessage, syncDfd = options.syncDfd || (Backbone.$.Deferred && Backbone.$.Deferred()); //If $ is having Deferred - use it.
		options.syncDfd = syncDfd;

		if (!store.db) {
			store.dbRequest.addEventListener('success', function(e) {
				store.db = this.result;
				Backbone.LocalStorage.sync.call(that, method, model, options);
			});
		} else {

			var cbh = callbackHandler.bind(this, options);

			try {

				switch (method) {
					case 'read':
						model.id != undefined ? store.find(model, cbh) : store.findAll(cbh);
						break;
					case 'create':
						store.create(model, nothing);
						cbh(model.toJSON());
						break;
					case 'update':
						store.update(model, nothing);
						cbh(model.toJSON());
						break;
					case 'delete':
						store.destroy(model, nothing);
						cbh(model.toJSON());
						break;
				}

			} catch (error) {
				if (error.code === 22) { // && store._storageSize() === 0, what is code 22?
					errorMessage = 'Private browsing is unsupported';
				} else {
					errorMessage = error.message;
				}
				console.log('IDB ERR: ' + errorMessage);
				cbh(null, errorMessage);
			}
		}
		return syncDfd && syncDfd.promise();
	};

	function nothing() {

	}

	function callbackHandler(options, resp, errorMessage) {

		var syncDfd = options.syncDfd;
		if (resp) {
			if (options && options.success) {
				if (Backbone.VERSION === '0.9.10') {
					options.success(model, resp, options);
				} else {
					options.success(resp);
				}
			}
			if (syncDfd) {
				syncDfd.resolve(resp);
			}

		} else {
			errorMessage = errorMessage ? errorMessage : 'Record Not Found';

			if (options && options.error) {
				if (Backbone.VERSION === '0.9.10') {
					options.error(model, errorMessage, options);
				} else {
					options.error(errorMessage);
				}
			}

			if (syncDfd) {
				syncDfd.reject(errorMessage);
			}
		}

		if (options && options.complete) {
			options.complete(resp);
		}

	}

	Backbone.ajaxSync = Backbone.sync;

	Backbone.getSyncMethod = function(model) {
		if (model.localStorage || (model.collection && model.collection.localStorage)) {
			return Backbone.localSync;
		}

		return Backbone.ajaxSync;
	};

	// Override 'Backbone.sync' to default to localSync,
	// the original 'Backbone.sync' is still available in 'Backbone.ajaxSync'
	Backbone.sync = function(method, model, options) {
		return Backbone.getSyncMethod(model).apply(this, [method, model, options]);
	};

	return Backbone.LocalStorage;
}));<commit_msg>init idboptim<commit_after>/**
 * Backbone localStorage Adapter
 * Version 1.1.6
 *
 * https://github.com/jeromegn/Backbone.localStorage
 */
(function(root, factory) {
	if (typeof exports === 'object' && root.require) {
		module.exports = factory(require('underscore'), require('backbone'));
	} else if (typeof define === 'function' && define.amd) {
		// AMD. Register as an anonymous module.
		define(['underscore', 'backbone'], function(_, Backbone) {
			// Use global variables if the locals are undefined.
			return factory(_ || root._, Backbone || root.Backbone);
		});
	} else {
		// RequireJS isn't being used. Assume underscore and backbone are loaded in <script> tags
		factory(_, Backbone);
	}
}(this, function(_, Backbone) {
	// A simple module to replace `Backbone.sync` with *localStorage*-based
	// persistence. Models are given GUIDS, and saved into a JSON object. Simple
	// as that.

	// Get rid off browser prefixes
	window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
	window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction;
	window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;

	// Hold reference to Underscore.js and Backbone.js in the closure in order
	// to make things work even if they are removed from the global namespace

	// Generate four random hex digits.
	function S4() {
		return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
	};

	// Generate a pseudo-GUID by concatenating random hexadecimal.

	function guid() {
		return (S4() + S4() + '-' + S4() + '-' + S4() + '-' + S4() + '-' + S4() + S4() + S4());
	};

	// Our Store is represented by a single JS object in *localStorage*. Create it
	// with a meaningful name, like the name you'd give a table.
	// window.Store is deprectated, use Backbone.LocalStorage instead
	Backbone.LocalStorage = function(name) {
		if (!window.indexedDB) {
			throw 'Backbone.indexedDB: Environment does not support IndexedDB.';
		}
		this.name = name;
		this.db = null;
		var request = window.indexedDB.open('backbone-indexeddb', Backbone.LocalStorage.version);
		this.dbRequest = request;
		var that = this;

		request.addEventListener('error', function(e) {
			// user probably disallowed idb
			throw 'Error code: ' + this.errorCode;
			// what are the possible codes???
		});

		request.addEventListener('success', function(e) {
			that.db = this.result;
		});

		request.addEventListener('upgradeneeded', function(e) {
			var db = this.result;
			Backbone.LocalStorage.prepare(db);
		});

		//var store = this.localStorage().getItem(this.name);
		//this.records = (store && store.split(',')) || [];
	};

	_.extend(Backbone.LocalStorage.prototype, {

		// Save the current state of the **Store** to *localStorage*.
		save: function() {
			//this.localStorage().setItem(this.name, this.records.join(','));
		},

		// Add a model, giving it a (hopefully)-unique GUID, if it doesn't already
		// have an id of it's own.
		create: function(model, cb) {
			if (!model.id) {
				model.id = guid();
				model.set(model.idAttribute, model.id);
			}
			this.localStorage().add(model.toJSON());
			//this.localStorage().setItem(this.name + '-' + model.id, JSON.stringify(model));
			//this.records.push(model.id.toString());
			//this.save();
			this.find(model, cb);
		},

		// Update a model by replacing its copy in `this.data`.
		update: function(model, cb) {
			var req = this.localStorage().put(model.toJSON());
			this.find(model, cb);
		},

		// Retrieve a model from `this.data` by id.
		find: function(model, cb) {
			var request = this.localStorage('readonly').get(model.id);
			request.onsuccess = function() {
				cb(this.result);
			}
			request.onerror = function() {
				throw 'IndexDB Error: Can\'t read from or write to database';
			}
			//return this.jsonData(this.localStorage().getItem(this.name + '-' + model.id));
		},

		// Return the array of all models currently in storage.
		findAll: function(cb) {
			var items = [];

			this.localStorage('readonly').openCursor().onsuccess = function(event) {
				var cursor = this.result;
				if (cursor) {
					items.push(cursor.value);
					cursor.continue();
				} else {
					cb(items);
				}
			};
		},

		// Delete a model from `this.data`, returning it.
		destroy: function(model, cb) {
			if (model.isNew())
				return false
			this.localStorage().delete(model.id);
			/*this.records = _.reject(this.records, function(id) {
				return id === model.id.toString();
			});
			this.save();*/
			cb(model);
			return model;
		},

		localStorage: (function() {
			var stores = [];
			return function(type) {
				if (stores[this.name] && !type) {
					return stores[this.name];
				}

				var tx = this.db.transaction([this.name], type || 'readwrite');

				var tmpStore = tx.objectStore(this.name);
				if (!type && !stores[this.name]) {
					stores[this.name] = tmpStore;
					setTimeout(function() {
						stores = [];
					}, 0);
				}

				return tmpStore;
			}
		})(),

		// Clear localStorage for specific collection.
		_clear: function(cb) {
			var req = this.localStorage().clear();
			req.onsuccess = cb;
			req.onerror = cb;
			/*var local = this.localStorage(),
				itemRe = new RegExp('^' + this.name + '-');

			// Remove id-tracking item (e.g., 'foo').
			local.removeItem(this.name);

			// Lodash removed _#chain in v1.0.0-rc.1
			// Match all data items (e.g., 'foo-ID') and remove.
			(_.chain || _)(local).keys()
				.filter(function(k) {
					return itemRe.test(k);
				})
				.each(function(k) {
					local.removeItem(k);
				});

			this.records.length = 0;*/
		},

		// Size of localStorage.
		_storageSize: function(cb) {
			this.localStorage().count().onsuccess = cb;
		}

	});

	// localSync delegate to the model or collection's
	// *localStorage* property, which should be an instance of `Store`.
	// window.Store.sync and Backbone.localSync is deprecated, use Backbone.LocalStorage.sync instead
	Backbone.LocalStorage.sync = Backbone.localSync = function(method, model, options) {
		var store = model.localStorage || model.collection.localStorage;
		options = options || {};

		var that = this;

		var errorMessage, syncDfd = options.syncDfd || (Backbone.$.Deferred && Backbone.$.Deferred()); //If $ is having Deferred - use it.
		options.syncDfd = syncDfd;

		if (!store.db) {
			store.dbRequest.addEventListener('success', function(e) {
				store.db = this.result;
				Backbone.LocalStorage.sync.call(that, method, model, options);
			});
		} else {

			var cbh = callbackHandler.bind(this, options);

			try {

				switch (method) {
					case 'read':
						model.id != undefined ? store.find(model, cbh) : store.findAll(cbh);
						break;
					case 'create':
						store.create(model, nothing);
						cbh(model.toJSON());
						break;
					case 'update':
						store.update(model, nothing);
						cbh(model.toJSON());
						break;
					case 'delete':
						store.destroy(model, nothing);
						cbh(model.toJSON());
						break;
				}

			} catch (error) {
				if (error.code === 22) { // && store._storageSize() === 0, what is code 22?
					errorMessage = 'Private browsing is unsupported';
				} else {
					errorMessage = error.message;
				}
				console.log('IDB ERR: ' + errorMessage);
				cbh(null, errorMessage);
			}
		}
		return syncDfd && syncDfd.promise();
	};

	function nothing() {

	}

	function callbackHandler(options, resp, errorMessage) {

		var syncDfd = options.syncDfd;
		if (resp) {
			if (options && options.success) {
				if (Backbone.VERSION === '0.9.10') {
					options.success(model, resp, options);
				} else {
					options.success(resp);
				}
			}
			if (syncDfd) {
				syncDfd.resolve(resp);
			}

		} else {
			errorMessage = errorMessage ? errorMessage : 'Record Not Found';

			if (options && options.error) {
				if (Backbone.VERSION === '0.9.10') {
					options.error(model, errorMessage, options);
				} else {
					options.error(errorMessage);
				}
			}

			if (syncDfd) {
				syncDfd.reject(errorMessage);
			}
		}

		if (options && options.complete) {
			options.complete(resp);
		}

	}

	Backbone.ajaxSync = Backbone.sync;

	Backbone.getSyncMethod = function(model) {
		if (model.localStorage || (model.collection && model.collection.localStorage)) {
			return Backbone.localSync;
		}

		return Backbone.ajaxSync;
	};

	// Override 'Backbone.sync' to default to localSync,
	// the original 'Backbone.sync' is still available in 'Backbone.ajaxSync'
	Backbone.sync = function(method, model, options) {
		return Backbone.getSyncMethod(model).apply(this, [method, model, options]);
	};

	return Backbone.LocalStorage;
}));<|endoftext|>


<commit_before>const MongoClient = require('mongodb').MongoClient
const mongoUri = require('./secrets.json').mongoUri

// Helper for DB functions
class DBController {
  /* Wrapper for all db functions which connects,
   * runs the given dbFunction, and closes connection
   */
  connectAndPerformFunction(dbFunction) {
    MongoClient.connect(mongoUri, (err, db) => {
      // Call db function on this db
      dbFunction(db)
      if (err) {
        throw err
      }
      db.close()
    })
  }

  // Add document to collection
  addDocument(collection, document) {
    this.connectAndPerformFunction((db) => {
      db.collection(collection).insertOne(document)
    })
  }

  // Update given document in collection
  updateDocument(collection, document) {
    this.connectAndPerformFunction((db) => {
      const query = { _id: document._id }
      db.collection(collection).updateOne(query, document)
    })
  }

  /* Return promise which resolves to array containing
   * documents in given collection which match query
   */
  getMatchingDocumentsInCollection(collection, query) {
    let docs = new Promise((resolve, reject) => {
      this.connectAndPerformFunction((db) => {
        docs = db.collection(collection).find(query).toArray((err, result) => {
          if (err) {
            reject(err)
            throw err
          }
          resolve(result)
        })
      })
    })
    return docs
  }

  /* Helper function to return promise which resolves to
   * array containing all documents in given collection
   */
  getAllDocumentsInCollection(collection) {
    return this.getMatchingDocumentsInCollection(collection, {})
  }
}

export default DBController
<commit_msg>Add functionality to add an element to an array in db<commit_after>const MongoClient = require('mongodb').MongoClient
const mongoUri = require('./secrets.json').mongoUri

// Helper for DB functions
class DBController {
  /* Wrapper for all db functions which connects,
   * runs the given dbFunction, and closes connection
   */
  connectAndPerformFunction(dbFunction) {
    MongoClient.connect(mongoUri, (err, db) => {
      // Call db function on this db
      dbFunction(db)
      if (err) {
        throw err
      }
      db.close()
    })
  }

  // Add document to collection
  addDocument(collection, document) {
    this.connectAndPerformFunction((db) => {
      db.collection(collection).insertOne(document)
    })
  }

  // Add an element to an array within a document
  addElementToArray(collection, query, array, element) {
    let arraySearch = {}
    arraySearch[array] = element
    this.connectAndPerformFunction((db) => {
      db.collection(collection)
        .updateOne(query, { $push: arraySearch })
    })
  }

  // Update given document in collection
  updateDocument(collection, document) {
    this.connectAndPerformFunction((db) => {
      const query = { _id: document._id }
      db.collection(collection).updateOne(query, document)
    })
  }

  /* Return promise which resolves to array containing
   * documents in given collection which match query
   */
  getMatchingDocumentsInCollection(collection, query) {
    let docs = new Promise((resolve, reject) => {
      this.connectAndPerformFunction((db) => {
        docs = db.collection(collection).find(query).toArray((err, result) => {
          if (err) {
            reject(err)
            throw err
          }
          resolve(result)
        })
      })
    })
    return docs
  }

  /* Helper function to return promise which resolves to
   * array containing all documents in given collection
   */
  getAllDocumentsInCollection(collection) {
    return this.getMatchingDocumentsInCollection(collection, {})
  }
}

export default DBController
<|endoftext|>


<commit_before>#!/usr/bin/env node

var es = require('event-stream');
var lingon = require('../../../lib/boot');
var spawn  = require('child_process').spawn;

lingon.preProcessors.unshift('ejs', function(global, context) {
  return es.map(function(file, cb) {
      var ls = spawn('ls', [
        context.file
      ]);

      ls.stdout.on('data', function (data) {
        context.metadata = data.toString().trim();
      });

      ls.on('close', function (data) {
        cb(null, file);
      });
    });
});
<commit_msg>Updated system tests<commit_after>#!/usr/bin/env node

var es = require('event-stream');
var lingon = require('../../../lib/boot');
var spawn  = require('child_process').spawn;

lingon.preProcessors.unshift('ejs', function(params) {
  return es.map(function(file, cb) {
      var ls = spawn('ls', [
        params.context.file
      ]);

      ls.stdout.on('data', function (data) {
        params.context.metadata = data.toString().trim();
      });

      ls.on('close', function (data) {
        cb(null, file);
      });
    });
});
<|endoftext|>


<commit_before>define(['modules/core/src/SongModel', 'modules/MidiCSL/src/converters/SongConverterMidi_MidiCSL', 'modules/MidiCSL/src/model/SongModel_MidiCSL', 'modules/MidiCSL/src/model/NoteModel_MidiCSL', 'tests/test-songs', 'modules/converters/MusicCSLJson/src/SongModel_CSLJson'],
	function(SongModel, SongConverterMidi_MidiCSL, SongModel_MidiCSL, NoteModel_MidiCSL, testSongs, SongModel_CSLJson) {
	return {
		run: function() {
			test("SongConverterMidi_MidiCSL", function(assert) {
				function testExportNotesToMidiCSL(){
					var song = SongModel_CSLJson.importFromMusicCSLJSON(testSongs.keySigChanges);
					var midiSong = SongConverterMidi_MidiCSL.exportNotesToMidiCSL(song);
					var pitch = [];
					var durations = [];
					var currentTime = [];
					for (var i = 0; i < midiSong.length; i++) {
						pitch.push(midiSong[i].midiNote[0]);
						durations.push(midiSong[i].duration);
						currentTime.push(midiSong[i].currentTime);
					}

					assert.deepEqual(durations, [4, 0.5, 0.5, 0.5, 0.5, 0.5, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 1, 1, 0.5, 0.5, 0.5, 0.5, 4, 6.5, 2], 'ExportNotesToMidiCSL: durations');
					assert.deepEqual(pitch, [65, 65, 65, 78, 61, 59, 59, false, 69, 70, 65, 70, 66, 60, 63, 62, 62, undefined, undefined, 70, 71], 'ExportNotesToMidiCSL: pitches');
					assert.deepEqual(currentTime,  [0, 4, 4.5, 5, 5.5, 6, 6.5, 6.75, 7, 7.25, 7.5, 7.75, 8, 9, 10, 10.5, 11, 11.5, 12, 11.5, 18], 'ExportNotesToMidiCSL: currentTimes')

				}

				testExportNotesToMidiCSL();

				var msm = new SongModel_MidiCSL();
				assert.deepEqual(msm.getSong(), []);

				// Create a song from testSong
				var songModel  = new SongModel();
				var song = SongModel_CSLJson.importFromMusicCSLJSON(testSongs.simpleLeadSheet, songModel);
				var done = assert.async();
				// Convert songmodel to a readable model that we can insert in SongModel_MidiCSL
				SongConverterMidi_MidiCSL.exportToMidiCSL(songModel, false, function(midiSong) {
					var midiSongModel = new SongModel_MidiCSL({'song': midiSong});

					var fakeSong = [];
					fakeSong.push(new NoteModel_MidiCSL({currentTime:0, duration:4, midiNote:[69,73,76,80], type:'chord'}));
					fakeSong.push(new NoteModel_MidiCSL({currentTime:4, duration:4, midiNote:[69,73,76,80], type:'chord'}));
					fakeSong.push(new NoteModel_MidiCSL({currentTime:8, duration:4, midiNote:[71,75,78,81], type:'chord'}));
					fakeSong.push(new NoteModel_MidiCSL({currentTime:12, duration:4, midiNote:[71,75,78,81], type:'chord'}));
					fakeSong.push(new NoteModel_MidiCSL({currentTime:16, duration:4, midiNote:[64,67,71], type:'chord'}));
					fakeSong.push(new NoteModel_MidiCSL({currentTime:20, duration:4, midiNote:[64,67,71], type:'chord'}));
					fakeSong.push(new NoteModel_MidiCSL({currentTime:24, duration:4, midiNote:[65,69,72,75], type:'chord'}));
					fakeSong.push(new NoteModel_MidiCSL({currentTime:28, duration:4, midiNote:[65,69,72,75], type:'chord'}));
					assert.deepEqual(midiSongModel.getFromType('chord'), fakeSong);

					var fakeNote = new NoteModel_MidiCSL({currentTime:0, duration:1, midiNote:[69], type:'melody', 'noteIndex':0});
					assert.deepEqual(midiSongModel.getFromType('melody')[0], fakeNote);

					fakeNote = new NoteModel_MidiCSL({currentTime:7, duration:1, midiNote:[64], type:'melody', 'noteIndex':8});
					assert.deepEqual(midiSongModel.getFromType('melody')[8], fakeNote);

					fakeNote = new NoteModel_MidiCSL({currentTime:2, duration:1, midiNote:[65], type:'melody', 'noteIndex':3});
					assert.deepEqual(midiSongModel.getMelodySoundModelFromIndex(3), fakeNote, 'getmidiSongModel melody from index');
					done();
				});

				//

			});
		}
	};
});
<commit_msg>fixed tests Song converter to Midi<commit_after>define(['modules/core/src/SongModel', 'modules/MidiCSL/src/converters/SongConverterMidi_MidiCSL', 'modules/MidiCSL/src/model/SongModel_MidiCSL', 'modules/MidiCSL/src/model/NoteModel_MidiCSL', 'tests/test-songs', 'modules/converters/MusicCSLJson/src/SongModel_CSLJson'],
	function(SongModel, SongConverterMidi_MidiCSL, SongModel_MidiCSL, NoteModel_MidiCSL, testSongs, SongModel_CSLJson) {
		return {
			run: function() {
				test("SongConverterMidi_MidiCSL", function(assert) {
					function testExportNotesToMidiCSL() {
						var song = SongModel_CSLJson.importFromMusicCSLJSON(testSongs.keySigChanges);
						var midiSong = SongConverterMidi_MidiCSL.exportNotesToMidiCSL(song);
						var pitch = [];
						var durations = [];
						var currentTime = [];
						for (var i = 0; i < midiSong.length; i++) {
							pitch.push(midiSong[i].midiNote[0]);
							durations.push(midiSong[i].duration);
							currentTime.push(midiSong[i].currentTime);
						}

						assert.deepEqual(durations, [4, 0.5, 0.5, 0.5, 0.5, 0.5, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 1, 1, 0.5, 0.5, 0.5, 6.5, 2], 'ExportNotesToMidiCSL: durations');
						assert.deepEqual(currentTime, [0, 4, 4.5, 5, 5.5, 6, 6.5, 6.75, 7, 7.25, 7.5, 7.75, 8, 9, 10, 10.5, 11, 11.5, 18], 'ExportNotesToMidiCSL: currentTimes');
						assert.deepEqual(pitch, [65, 65, 65, 78, 61, 59, 59, false, 69, 70, 65, 70, 66, 60, 63, 62, 62, 70, 71], 'ExportNotesToMidiCSL: pitches');
					}

					testExportNotesToMidiCSL();

					var msm = new SongModel_MidiCSL();
					assert.deepEqual(msm.getSong(), []);

					// Create a song from testSong
					var songModel = new SongModel();
					var song = SongModel_CSLJson.importFromMusicCSLJSON(testSongs.simpleLeadSheet, songModel);
					var done = assert.async();
					// Convert songmodel to a readable model that we can insert in SongModel_MidiCSL
					SongConverterMidi_MidiCSL.exportToMidiCSL(songModel, false, function(midiSong) {
						var midiSongModel = new SongModel_MidiCSL({
							'song': midiSong
						});

						var fakeSong = [];
						fakeSong.push(new NoteModel_MidiCSL({
							currentTime: 0,
							duration: 4,
							midiNote: [69, 73, 76, 80],
							type: 'chord'
						}));
						fakeSong.push(new NoteModel_MidiCSL({
							currentTime: 4,
							duration: 4,
							midiNote: [69, 73, 76, 80],
							type: 'chord'
						}));
						fakeSong.push(new NoteModel_MidiCSL({
							currentTime: 8,
							duration: 4,
							midiNote: [71, 75, 78, 81],
							type: 'chord'
						}));
						fakeSong.push(new NoteModel_MidiCSL({
							currentTime: 12,
							duration: 4,
							midiNote: [71, 75, 78, 81],
							type: 'chord'
						}));
						fakeSong.push(new NoteModel_MidiCSL({
							currentTime: 16,
							duration: 4,
							midiNote: [64, 67, 71],
							type: 'chord'
						}));
						fakeSong.push(new NoteModel_MidiCSL({
							currentTime: 20,
							duration: 4,
							midiNote: [64, 67, 71],
							type: 'chord'
						}));
						fakeSong.push(new NoteModel_MidiCSL({
							currentTime: 24,
							duration: 4,
							midiNote: [65, 69, 72, 75],
							type: 'chord'
						}));
						fakeSong.push(new NoteModel_MidiCSL({
							currentTime: 28,
							duration: 4,
							midiNote: [65, 69, 72, 75],
							type: 'chord'
						}));
						assert.deepEqual(midiSongModel.getFromType('chord'), fakeSong);

						var fakeNote = new NoteModel_MidiCSL({
							currentTime: 0,
							duration: 1,
							midiNote: [69],
							type: 'melody',
							'noteIndex': 0
						});
						assert.deepEqual(midiSongModel.getFromType('melody')[0], fakeNote);

						fakeNote = new NoteModel_MidiCSL({
							currentTime: 7,
							duration: 1,
							midiNote: [64],
							type: 'melody',
							'noteIndex': 8
						});
						assert.deepEqual(midiSongModel.getFromType('melody')[8], fakeNote);

						fakeNote = new NoteModel_MidiCSL({
							currentTime: 2,
							duration: 1,
							midiNote: [65],
							type: 'melody',
							'noteIndex': 3
						});
						assert.deepEqual(midiSongModel.getMelodySoundModelFromIndex(3), fakeNote, 'getmidiSongModel melody from index');
						done();
					});

					//

				});
			}
		};
	});<|endoftext|>


<commit_before>'use strict';

var app = angular.module('fireoflondonApp');

app.factory('Posts', ['$resource', function ($resource) {
    var Posts = $resource('http://wordpress.fireoflondon.org/wp-json/posts',
        {
            query: {
                isArray: true,
                transformResponse: function (data) {
                    data = angular.fromJson(data);
                    return data.content;
                }
            }
        });
    return Posts;
}]);

app.factory('Post', ['$resource', function($resource) {
  return $resource('http://wordpress.fireoflondon.org/wp-json/posts/:articleId', { articleId: '@articleId'})
}]);

<commit_msg>More wordpress config attempts<commit_after>'use strict';

var app = angular.module('fireoflondonApp');

app.factory('Posts', ['$resource', function ($resource) {
    var Posts = $resource('http://localhost/wp-json/posts',
        {
            query: {
                isArray: true,
                transformResponse: function (data) {
                    data = angular.fromJson(data);
                    return data.content;
                }
            }
        });
    return Posts;
}]);

app.factory('Post', ['$resource', function($resource) {
  return $resource('http://localhost/wp-json/posts/:articleId', { articleId: '@articleId'})
}]);

<|endoftext|>


<commit_before>Taskar.Sections.Actions = {
  click: {
    'input.cancel': function(e, element){
      e.findElement('li').slideUp();
    },
    '.more': function(e, element){
      var p = element.up('.task').down('p');
      if (p.hasClassName('less')){
        p.morph('height:18px', function(){
          p.removeClassName('less');
          element.update(element.getAttribute('data-more'));
        });
      } else {
        p.addClassName('less');
        p.morph('height:' + p.scrollHeight + 'px', function(){
          element.update(element.getAttribute('data-less'));
        });
      }
    },
    '.add': function(e, element){
      e.stop();

      var newTask = $('new_task');

      var before = element.getAttribute('data-after'),
          scroll = false;
      if (before){
        element.up('.task').insert({after: newTask});
      } else {
        $('tasks').insert({bottom: newTask});
        scroll = true;
      }

      Taskar.Sections.resetTaskForm(newTask.down('form'), before);

      if (newTask.visible()){
        newTask.down('textarea').focus();
        return scroll && new Taskar.FX.ScrollTo(newTask);
      }

      newTask.slideDown(function(e){
        e.element.down('textarea').focus();
        scroll && new Taskar.FX.ScrollTo(e.element);
      });
    },
    '.toggle_archived': function(e, element){
      var archive = $('archived_tasks');
      if (archive){
        element.toggleClassName('selected');
        return archive.toggleWithEffect('slide', function(e){
          if (archive.visible()){
            new Taskar.FX.ScrollTo(element);
          }
        });
      }

      element.addClassName('loading');

      new Ajax.Request(element.getAttribute('data-url'), {
        method:     'get',
        onComplete: function(t){
          element.removeClassName('loading');
          element.addClassName('selected');
          e.findElement('ul').insert({ after: t.responseText });
          $('archived_tasks').hide().slideDown(function(e){ new Taskar.FX.ScrollTo(element); })
        }
      });
    }
  },
  'drag:start': {
    '.tasks_list': function(e, element){
      element.addClassName('dragging');
    }
  },
  'drag:finish': {
    '.tasks_list': function(e, element){
      element.removeClassName('dragging');
    }
  },
  'order:updated': function(e){
    new Ajax.Request(e.findElement('#tasks').getAttribute('data-sortable'), {
      method:     'put',
      parameters: e.memo.sortable.serialize('items[]')
    });
  },
  'ajax:post': {
    '#new_task': function(e){
      Taskar.Sections.resetTaskForm(e.findElement('form'));
    }
  },
  'ajax:put': {
    '.archive': function(e){
      var task = e.findElement('.task');
      task.slideUp(function(e){
        var parent, insert, disabled;
        if (task.parentNode.id == 'tasks'){
          parent    = $('archived_tasks');
          insert    = {top: task};
          disabled  = "true";
        } else {
          parent    = $('tasks');
          insert    = {bottom: task};
          disabled  = null;
        }

        task.down('.checkbox').writeAttribute('data-disabled', disabled);

        if (!parent){
          task.remove();
        } else {
          parent.insert(insert);
          task.slideDown();
        }

        task.fire('task:archive_changed');
      });
    }
  },
  'ajax:delete': {
    '.task': function(e, element){
      Taskar.FX.dropOut(element, function(e){e.element.remove()});
    }
  },
  'key:esc': {
    '.task': function(e, element){
      var cancel = element.down('.cancel');
      cancel && cancel.request();
    },
    '#new_task': function(e){
      e.findElement('li').slideUp();
    }
  },
  mouseover: {
    '.task': function(e, element){
      var content = element.down('p');

      if (content && !content._marked){
        content._marked = [1];
        if (!(content.scrollHeight > content.getHeight() + 5 || content.scrollWidth > content.getWidth() + 5)){
          element.down('.more').hide();
        }
      }
    }
  },
  'keyup': {
    'textarea': function(e, element){
      if (e.keyCode == Event.KEY_RETURN && e.shiftKey && element.form){
        e.stop();
        element.fire('ajax:' + $(element.form).request().options.method)
      }
    }
  },
  'task:archive_changed': function(e){
    var archive = $('archived_tasks');
    archive && $('archived_tasks_empty')[archive.getElementsByTagName('li').length == 1 ? 'show' : 'hide']();
  }
};<commit_msg>use more generic sortable behavior<commit_after>Taskar.Sections.Actions = {
  click: {
    'input.cancel': function(e, element){
      e.findElement('li').slideUp();
    },
    '.more': function(e, element){
      var p = element.up('.task').down('p');
      if (p.hasClassName('less')){
        p.morph('height:18px', function(){
          p.removeClassName('less');
          element.update(element.getAttribute('data-more'));
        });
      } else {
        p.addClassName('less');
        p.morph('height:' + p.scrollHeight + 'px', function(){
          element.update(element.getAttribute('data-less'));
        });
      }
    },
    '.add': function(e, element){
      e.stop();

      var newTask = $('new_task');

      var before = element.getAttribute('data-after'),
          scroll = false;
      if (before){
        element.up('.task').insert({after: newTask});
      } else {
        $('tasks').insert({bottom: newTask});
        scroll = true;
      }

      Taskar.Sections.resetTaskForm(newTask.down('form'), before);

      if (newTask.visible()){
        newTask.down('textarea').focus();
        return scroll && new Taskar.FX.ScrollTo(newTask);
      }

      newTask.slideDown(function(e){
        e.element.down('textarea').focus();
        scroll && new Taskar.FX.ScrollTo(e.element);
      });
    },
    '.toggle_archived': function(e, element){
      var archive = $('archived_tasks');
      if (archive){
        element.toggleClassName('selected');
        return archive.toggleWithEffect('slide', function(e){
          if (archive.visible()){
            new Taskar.FX.ScrollTo(element);
          }
        });
      }

      element.addClassName('loading');

      new Ajax.Request(element.getAttribute('data-url'), {
        method:     'get',
        onComplete: function(t){
          element.removeClassName('loading');
          element.addClassName('selected');
          e.findElement('ul').insert({ after: t.responseText });
          $('archived_tasks').hide().slideDown(function(e){ new Taskar.FX.ScrollTo(element); })
        }
      });
    }
  },
  'drag:start': {
    '.tasks_list': function(e, element){
      element.addClassName('dragging');
    }
  },
  'drag:finish': {
    '.tasks_list': function(e, element){
      element.removeClassName('dragging');
    }
  },
  'order:updated': {
    '[data-sortable]': function(e, element){
      new Ajax.Request(element.getAttribute('data-sortable'), {
        method:     'put',
        parameters: e.memo.sortable.serialize('items[]')
      });
    }
  },
  'ajax:post': {
    '#new_task': function(e){
      Taskar.Sections.resetTaskForm(e.findElement('form'));
    }
  },
  'ajax:put': {
    '.archive': function(e){
      var task = e.findElement('.task');
      task.slideUp(function(e){
        var parent, insert, disabled;
        if (task.parentNode.id == 'tasks'){
          parent    = $('archived_tasks');
          insert    = {top: task};
          disabled  = "true";
        } else {
          parent    = $('tasks');
          insert    = {bottom: task};
          disabled  = null;
        }

        task.down('.checkbox').writeAttribute('data-disabled', disabled);

        if (!parent){
          task.remove();
        } else {
          parent.insert(insert);
          task.slideDown();
        }

        task.fire('task:archive_changed');
      });
    }
  },
  'ajax:delete': {
    '.task': function(e, element){
      Taskar.FX.dropOut(element, function(e){e.element.remove()});
    }
  },
  'key:esc': {
    '.task': function(e, element){
      var cancel = element.down('.cancel');
      cancel && cancel.request();
    },
    '#new_task': function(e){
      e.findElement('li').slideUp();
    }
  },
  mouseover: {
    '.task': function(e, element){
      var content = element.down('p');

      if (content && !content._marked){
        content._marked = [1];
        if (!(content.scrollHeight > content.getHeight() + 5 || content.scrollWidth > content.getWidth() + 5)){
          element.down('.more').hide();
        }
      }
    }
  },
  'keyup': {
    'textarea': function(e, element){
      if (e.keyCode == Event.KEY_RETURN && e.shiftKey && element.form){
        e.stop();
        element.fire('ajax:' + $(element.form).request().options.method)
      }
    }
  },
  'task:archive_changed': function(e){
    var archive = $('archived_tasks');
    archive && $('archived_tasks_empty')[archive.getElementsByTagName('li').length == 1 ? 'show' : 'hide']();
  }
};<|endoftext|>


<commit_before>var inc = require('./increment').increment;
var a = 1;
inc(a); // 2<commit_msg>Update example.js<commit_after>const inc = require('./increment').increment;
const a = 1;
inc(a); // 2
<|endoftext|>


<commit_before>/*
 @author Zakai Hamilton
 @component MediaFile
 */

screens.media.file = function MediaFile(me, { core, storage, media, db, manager }) {
    me.resolutions = ["800x600", "1024x768"];
    me.rootPath = "/Kab/concepts/private";
    me.cachePath = "cache";
    me.awsBucket = "screens";
    me.init = function () {
        const ffprobePath = require("@ffprobe-installer/ffprobe").path;
        me.ffmpeg = require("fluent-ffmpeg");
        me.ffmpeg.setFfprobePath(ffprobePath);
        me.os = require("os");
        me.tempDir = me.os.tmpdir();
        core.file.makeDir(me.cachePath);
        core.mutex.enable(me.id, true);
        core.broadcast.register(me, {
            startup: "media.file.groups"
        });
    };
    me.info = function (path) {
        return new Promise(resolve => {
            me.ffmpeg.ffprobe(path, function (err, info) {
                if (err) {
                    me.log_error("Cannot parse metadata for file: " + path + " error: " + err);
                    info = null;
                }
                resolve(info);
            });
        });
    };
    me.paths = function (groupName, path) {
        var paths = {
            local: me.cachePath + "/" + path,
            remote: me.rootPath + "/" + groupName + "/" + path
        };
        return paths;
    };
    me.download = async function (groupName, path) {
        var target = await manager.file.download(me.rootPath + "/" + groupName + "/" + path,
            me.cachePath + "/" + path);
        return target;
    };
    me.groups = async function (update = false) {
        var files = await db.cache.file.listing(me.rootPath, update);
        for (let file of files) {
            file.path = me.rootPath + "/" + file.name;
            var sessions = await me.listing(file, update);
            sessions = sessions.sort((a, b) => a.label.localeCompare(b.label));
            sessions.map(item => {
                ["is_downloadable",
                    "content_hash",
                    ".tag",
                    "path_lower",
                    "path_display",
                    "_id",
                    "client_modified",
                    "server_modified",
                    "rev"].map(key => delete item[key]);
            });
            sessions.reverse();
            file.sessions = sessions;
        }
        files = files.sort((a, b) => a.name.localeCompare(b.name));
        files.sort();
        return files;
    };
    me.exists = async function (name) {
        var groups = await me.groups();
        for (var group of groups) {
            for (var session of group.sessions) {
                if (session.name.includes(name)) {
                    return group.name;
                }
            }
        }
        return null;
    };
    me.listing = async function (parent, update = false) {
        let argList = [];
        var files = await db.cache.file.listing(parent.path, update, async (file) => {
            let result = false;
            file.group = parent.name;
            file.session = core.path.fileName(file.name);
            file.extension = core.path.extension(file.name);
            file.label = core.string.title(core.path.fileName(file.name));
            file.remote = parent.path + "/" + file.name;
            file.local = me.cachePath + "/" + file.name;
            let awsPath = me.awsBucket + "/" + parent.name + "/" + file.name;
            let deleteFile = false;

            if (!await storage.aws.exists(awsPath)) {
                me.log("Downloading file: " + file.local + ", size: " + file.size);
                await manager.file.download(file.remote, file.local);
                deleteFile = true;
                me.log("Uploading file: " + file.local + ", size: " + file.size);
                await storage.aws.uploadFile(file.local, awsPath);
                me.log("Finished uploading file: " + file.local);
            }
            if (file.local.endsWith(".m4a")) {
                if (!file.duration || !file.durationText) {
                    me.log("Downloading file: " + file.local + ", size: " + file.size);
                    await storage.aws.downloadFile(awsPath, file.local);
                    deleteFile = true;
                    me.log("Retrieving metadata for file: " + file.local);
                    var metadata = await me.info(file.local);
                    if (metadata) {
                        if (metadata.format) {
                            file.duration = metadata.format.duration;
                            file.durationText = core.string.formatDuration(file.duration);
                            result = true;
                            me.log("Found metadata for file: " + file.local + " duration: " + file.durationText);
                        }
                    }
                }
            }
            if (deleteFile) {
                me.log("Deleting file: " + file.local);
                await core.file.delete(file.local);
                me.log("Deleted file: " + file.local);
            }
            if (file.local.endsWith(".mp4")) {
                let resolutions = [];
                for (let resolution of me.resolutions) {
                    let path = me.awsBucket + "/" + file.group + "/" + file.session + "_" + resolution + ".mp4";
                    if (await storage.aws.exists(path)) {
                        resolutions.push(resolution);
                    }
                    else {
                        argList.push(["media.file.convertItem", resolution, file.group, file.session]);
                        resolutions.push(resolution);
                    }
                }
                argList.push(["media.file.screenshot", file.group, file.session]);
                if ((!file.resolutions && resolutions.length) || file.resolutions.length !== resolutions.length) {
                    file.resolutions = resolutions;
                    result = true;
                }
            }
            return result;
        });
        if (argList.length) {
            await db.events.msg.sendParallel(argList);
        }
        files.reverse();
        return files;
    };
    me.size = async function () {
        var info = { groups: {}, total: 0, count: 0 };
        var groups = await me.groups(true, true);
        for (let group of groups) {
            var files = group.sessions;
            ["m4a", "mp4"].map(extension => {
                let list = files.filter(file => file.extension === extension);
                var diskSize = list.reduce((total, item) => total + item.size, 0);
                let infoGroup = info.groups[group.name];
                if (!infoGroup) {
                    infoGroup = info.groups[group.name] = { total: 0, count: 0 };
                }
                infoGroup.total += diskSize;
                infoGroup.count += list.length;
                info.total += diskSize;
                info.total += list.length;
            });
        }
        for (let infoName in info.groups) {
            let group = info.groups[infoName];
            for (let key in group) {
                if (key === "total") {
                    continue;
                }
                group[key].total = core.string.formatBytes(group[key].total);
            }
            group.total = core.string.formatBytes(group.total);
        }
        info.total = core.string.formatBytes(info.total);
        return info;
    };
    me.updateListing = async function () {
        me.log("updateListing");
        var groups = await me.groups(true, true);
        for (let group of groups) {
            var files = group.sessions;
            var diskSize = files.reduce((total, item) => total + item.size, 0);
            me.log("Requires " + core.string.formatBytes(diskSize) + " Disk space for files");
            for (var file of files) {
                if (file.extension === "m4a") {
                    try {
                        if (!media.speech.exists(file.local)) {
                            me.log("transcribing: " + file.local);
                            await media.speech.transcribe(file.local);
                            var info = await manager.download.clean(me.tempDir, "flac");
                            me.log("cleaned cache, deleted: " + info.deleted + " failed: " + info.failed + " skipped: " + info.skipped);
                        }
                    }
                    catch (err) {
                        me.log_error("Failed to transcribe: " + file.local);
                    }
                }
            }
        }
        me.log("finished update");
    };
    me.streamingPath = async function (group, name, extension, resolution) {
        let path = me.awsBucket + "/" + group + "/" + name + (resolution ? "_" + resolution : "") + "." + extension;
        await db.shared.stream.use({ user: this.userName, group, session: name }, { userId: this.userId, date: new Date().toString() });
        return storage.aws.url(path);
    };
    me.streamingList = async function (group, name) {
        var records = await db.shared.stream.list({ group, session: name });
        var users = records.map(record => record.user);
        return users;
    };
    me.screenshot = async function (group, session) {
        let result = false;
        try {
            const from_ext = ".mp4", to_ext = ".png";
            var remote = me.awsBucket + "/" + group + "/" + session + from_ext;
            var local = me.cachePath + "/" + session + from_ext;
            var local_convert = me.cachePath + "/" + session + to_ext;
            var remote_convert = me.awsBucket + "/" + group + "/" + session + to_ext;
            if (await storage.aws.exists(remote_convert)) {
                me.log("already converted: " + remote_convert);
                return false;
            }
            if (!await core.file.exists(local_convert)) {
                if (!await core.file.exists(local)) {
                    await db.events.state.set(me.id, session, "download", {
                        from: remote,
                        to: local
                    });
                    me.log("downloading: " + remote + " to: " + local);
                    await storage.aws.downloadFile(remote, local);
                }
                me.log("converting: " + local + " to: " + local_convert);
                await db.events.state.set(me.id, session, "screenshot", {
                    from: remote,
                    to: local_convert
                });
                try {
                    await media.ffmpeg.convert(local, local_convert, {
                        seek: "0:05"
                    }, async (percent) => {
                        await db.events.state.set(me.id, session, "screenshot", {
                            from: local,
                            to: local_convert,
                            percent
                        });
                    });
                }
                catch (err) {
                    if (!await core.file.exists(local_convert)) {
                        me.log_error("Cannot convert session: " + session + " in group: " + group + " error: " + err);
                        await db.events.state.set(me.id, session, "error", {
                            error: err
                        });
                        try {
                            await core.file.delete(local);
                            await core.file.delete(local_convert);
                        }
                        // eslint-disable-next-line no-empty
                        catch (err) {

                        }
                    }
                }
            }
            me.log("uploading: " + local_convert + " to: " + remote_convert);
            await db.events.state.set(me.id, session, "upload", {
                from: local_convert,
                to: remote_convert
            });
            await storage.aws.uploadFile(local_convert, remote_convert);
            me.log("deleting: " + local_convert);
            await core.file.delete(local_convert);
            me.log("deleting: " + local);
            await core.file.delete(local);
            me.log("finished screenshot: " + session);
            await db.events.state.set(me.id, session);
            result = true;
        }
        catch (err) {
            me.log_error("Cannot convert session: " + session + " in group: " + group + " error: " + err);
            await db.events.state.set(me.id, session, "error", {
                error: err
            });
            try {
                await core.file.delete(local);
                await core.file.delete(local_convert);
            }
            // eslint-disable-next-line no-empty
            catch (err) {

            }
        }
        return result;
    };
    me.convertItem = async function (resolution, group, session) {
        let result = false;
        try {
            var extension = ".mp4";
            var remote = me.awsBucket + "/" + group + "/" + session + extension;
            var local = me.cachePath + "/" + session + extension;
            var local_convert = me.cachePath + "/" + session + "_" + resolution + extension;
            var remote_convert = me.awsBucket + "/" + group + "/" + session + "_" + resolution + extension;
            if (await storage.aws.exists(remote_convert)) {
                me.log("already converted: " + remote_convert);
                return false;
            }
            if (!await core.file.exists(local_convert)) {
                if (!await core.file.exists(local)) {
                    await db.events.state.set(me.id, session, "download", {
                        from: remote,
                        to: local
                    });
                    me.log("downloading: " + remote + " to: " + local);
                    await storage.aws.downloadFile(remote, local);
                }
                me.log("converting: " + local + " to: " + local_convert);
                await db.events.state.set(me.id, session, "convert", {
                    from: remote,
                    to: local_convert,
                    resolution
                });
                await media.ffmpeg.convert(local, local_convert, {
                    size: resolution,
                }, async (percent) => {
                    await db.events.state.set(me.id, session, "convert", {
                        from: local,
                        to: local_convert,
                        percent
                    });
                });
            }
            me.log("uploading: " + local_convert + " to: " + remote_convert);
            await db.events.state.set(me.id, session, "upload", {
                from: local_convert,
                to: remote_convert
            });
            await storage.aws.uploadFile(local_convert, remote_convert);
            me.log("deleting: " + local_convert);
            await core.file.delete(local_convert);
            me.log("deleting: " + local);
            await core.file.delete(local);
            me.log("finished: " + session);
            await db.events.state.set(me.id, session);
            result = true;
        }
        catch (err) {
            me.log_error("Cannot convert session: " + session + " in group: " + group + " error: " + err);
            await db.events.state.set(me.id, session, "error", {
                error: err
            });
            try {
                await core.file.delete(local);
                await core.file.delete(local_convert);
            }
            // eslint-disable-next-line no-empty
            catch (err) {

            }
        }
        return result;
    };
    me.convertListing = async function (resolution) {
        if (!resolution) {
            for (resolution of me.resolutions) {
                await me.convertListing(resolution);
            }
            await me.convertListing("screenshot");
            return;
        }
        var groups = await me.groups();
        var argList = [];
        for (let group of groups) {
            var list = group.sessions.filter(session => session.extension === "mp4");
            me.log("Checking listing for group: " + group.name + " to convert to resolution: " + resolution + " out of " + list.length + " items");
            await Promise.all(list.map(async item => {
                if (resolution === "screenshot") {
                    const remote_convert = me.awsBucket + "/" + group.name + "/" + item.session + ".png";
                    if (await storage.aws.exists(remote_convert)) {
                        return;
                    }
                    argList.push(["media.file.screenshot", group.name, item.session]);
                }
                else {
                    const remote_convert = me.awsBucket + "/" + group.name + "/" + item.session + "_" + resolution + ".mp4";
                    if (await storage.aws.exists(remote_convert)) {
                        return;
                    }
                    argList.push(["media.file.convertItem", resolution, group.name, item.session]);
                }
            }));
        }
        await db.events.msg.sendParallel(argList);
    };
    return "server";
};
<commit_msg>extra resolution<commit_after>/*
 @author Zakai Hamilton
 @component MediaFile
 */

screens.media.file = function MediaFile(me, { core, storage, media, db, manager }) {
    me.resolutions = ["640x480", "800x600", "1024x768"];
    me.rootPath = "/Kab/concepts/private";
    me.cachePath = "cache";
    me.awsBucket = "screens";
    me.init = function () {
        const ffprobePath = require("@ffprobe-installer/ffprobe").path;
        me.ffmpeg = require("fluent-ffmpeg");
        me.ffmpeg.setFfprobePath(ffprobePath);
        me.os = require("os");
        me.tempDir = me.os.tmpdir();
        core.file.makeDir(me.cachePath);
        core.mutex.enable(me.id, true);
        core.broadcast.register(me, {
            startup: "media.file.groups"
        });
    };
    me.info = function (path) {
        return new Promise(resolve => {
            me.ffmpeg.ffprobe(path, function (err, info) {
                if (err) {
                    me.log_error("Cannot parse metadata for file: " + path + " error: " + err);
                    info = null;
                }
                resolve(info);
            });
        });
    };
    me.paths = function (groupName, path) {
        var paths = {
            local: me.cachePath + "/" + path,
            remote: me.rootPath + "/" + groupName + "/" + path
        };
        return paths;
    };
    me.download = async function (groupName, path) {
        var target = await manager.file.download(me.rootPath + "/" + groupName + "/" + path,
            me.cachePath + "/" + path);
        return target;
    };
    me.groups = async function (update = false) {
        var files = await db.cache.file.listing(me.rootPath, update);
        for (let file of files) {
            file.path = me.rootPath + "/" + file.name;
            var sessions = await me.listing(file, update);
            sessions = sessions.sort((a, b) => a.label.localeCompare(b.label));
            sessions.map(item => {
                ["is_downloadable",
                    "content_hash",
                    ".tag",
                    "path_lower",
                    "path_display",
                    "_id",
                    "client_modified",
                    "server_modified",
                    "rev"].map(key => delete item[key]);
            });
            sessions.reverse();
            file.sessions = sessions;
        }
        files = files.sort((a, b) => a.name.localeCompare(b.name));
        files.sort();
        return files;
    };
    me.exists = async function (name) {
        var groups = await me.groups();
        for (var group of groups) {
            for (var session of group.sessions) {
                if (session.name.includes(name)) {
                    return group.name;
                }
            }
        }
        return null;
    };
    me.listing = async function (parent, update = false) {
        let argList = [];
        var files = await db.cache.file.listing(parent.path, update, async (file) => {
            let result = false;
            file.group = parent.name;
            file.session = core.path.fileName(file.name);
            file.extension = core.path.extension(file.name);
            file.label = core.string.title(core.path.fileName(file.name));
            file.remote = parent.path + "/" + file.name;
            file.local = me.cachePath + "/" + file.name;
            let awsPath = me.awsBucket + "/" + parent.name + "/" + file.name;
            let deleteFile = false;

            if (!await storage.aws.exists(awsPath)) {
                me.log("Downloading file: " + file.local + ", size: " + file.size);
                await manager.file.download(file.remote, file.local);
                deleteFile = true;
                me.log("Uploading file: " + file.local + ", size: " + file.size);
                await storage.aws.uploadFile(file.local, awsPath);
                me.log("Finished uploading file: " + file.local);
            }
            if (file.local.endsWith(".m4a")) {
                if (!file.duration || !file.durationText) {
                    me.log("Downloading file: " + file.local + ", size: " + file.size);
                    await storage.aws.downloadFile(awsPath, file.local);
                    deleteFile = true;
                    me.log("Retrieving metadata for file: " + file.local);
                    var metadata = await me.info(file.local);
                    if (metadata) {
                        if (metadata.format) {
                            file.duration = metadata.format.duration;
                            file.durationText = core.string.formatDuration(file.duration);
                            result = true;
                            me.log("Found metadata for file: " + file.local + " duration: " + file.durationText);
                        }
                    }
                }
            }
            if (deleteFile) {
                me.log("Deleting file: " + file.local);
                await core.file.delete(file.local);
                me.log("Deleted file: " + file.local);
            }
            if (file.local.endsWith(".mp4")) {
                let resolutions = [];
                for (let resolution of me.resolutions) {
                    let path = me.awsBucket + "/" + file.group + "/" + file.session + "_" + resolution + ".mp4";
                    if (await storage.aws.exists(path)) {
                        resolutions.push(resolution);
                    }
                    else {
                        argList.push(["media.file.convertItem", resolution, file.group, file.session]);
                        resolutions.push(resolution);
                    }
                }
                argList.push(["media.file.screenshot", file.group, file.session]);
                if ((!file.resolutions && resolutions.length) || file.resolutions.length !== resolutions.length) {
                    file.resolutions = resolutions;
                    result = true;
                }
            }
            return result;
        });
        if (argList.length) {
            await db.events.msg.sendParallel(argList);
        }
        files.reverse();
        return files;
    };
    me.size = async function () {
        var info = { groups: {}, total: 0, count: 0 };
        var groups = await me.groups(true, true);
        for (let group of groups) {
            var files = group.sessions;
            ["m4a", "mp4"].map(extension => {
                let list = files.filter(file => file.extension === extension);
                var diskSize = list.reduce((total, item) => total + item.size, 0);
                let infoGroup = info.groups[group.name];
                if (!infoGroup) {
                    infoGroup = info.groups[group.name] = { total: 0, count: 0 };
                }
                infoGroup.total += diskSize;
                infoGroup.count += list.length;
                info.total += diskSize;
                info.total += list.length;
            });
        }
        for (let infoName in info.groups) {
            let group = info.groups[infoName];
            for (let key in group) {
                if (key === "total") {
                    continue;
                }
                group[key].total = core.string.formatBytes(group[key].total);
            }
            group.total = core.string.formatBytes(group.total);
        }
        info.total = core.string.formatBytes(info.total);
        return info;
    };
    me.updateListing = async function () {
        me.log("updateListing");
        var groups = await me.groups(true, true);
        for (let group of groups) {
            var files = group.sessions;
            var diskSize = files.reduce((total, item) => total + item.size, 0);
            me.log("Requires " + core.string.formatBytes(diskSize) + " Disk space for files");
            for (var file of files) {
                if (file.extension === "m4a") {
                    try {
                        if (!media.speech.exists(file.local)) {
                            me.log("transcribing: " + file.local);
                            await media.speech.transcribe(file.local);
                            var info = await manager.download.clean(me.tempDir, "flac");
                            me.log("cleaned cache, deleted: " + info.deleted + " failed: " + info.failed + " skipped: " + info.skipped);
                        }
                    }
                    catch (err) {
                        me.log_error("Failed to transcribe: " + file.local);
                    }
                }
            }
        }
        me.log("finished update");
    };
    me.streamingPath = async function (group, name, extension, resolution) {
        let path = me.awsBucket + "/" + group + "/" + name + (resolution ? "_" + resolution : "") + "." + extension;
        await db.shared.stream.use({ user: this.userName, group, session: name }, { userId: this.userId, date: new Date().toString() });
        return storage.aws.url(path);
    };
    me.streamingList = async function (group, name) {
        var records = await db.shared.stream.list({ group, session: name });
        var users = records.map(record => record.user);
        return users;
    };
    me.screenshot = async function (group, session) {
        let result = false;
        try {
            const from_ext = ".mp4", to_ext = ".png";
            var remote = me.awsBucket + "/" + group + "/" + session + from_ext;
            var local = me.cachePath + "/" + session + from_ext;
            var local_convert = me.cachePath + "/" + session + to_ext;
            var remote_convert = me.awsBucket + "/" + group + "/" + session + to_ext;
            if (await storage.aws.exists(remote_convert)) {
                me.log("already converted: " + remote_convert);
                return false;
            }
            if (!await core.file.exists(local_convert)) {
                if (!await core.file.exists(local)) {
                    await db.events.state.set(me.id, session, "download", {
                        from: remote,
                        to: local
                    });
                    me.log("downloading: " + remote + " to: " + local);
                    await storage.aws.downloadFile(remote, local);
                }
                me.log("converting: " + local + " to: " + local_convert);
                await db.events.state.set(me.id, session, "screenshot", {
                    from: remote,
                    to: local_convert
                });
                try {
                    await media.ffmpeg.convert(local, local_convert, {
                        seek: "0:05"
                    }, async (percent) => {
                        await db.events.state.set(me.id, session, "screenshot", {
                            from: local,
                            to: local_convert,
                            percent
                        });
                    });
                }
                catch (err) {
                    if (!await core.file.exists(local_convert)) {
                        me.log_error("Cannot convert session: " + session + " in group: " + group + " error: " + err);
                        await db.events.state.set(me.id, session, "error", {
                            error: err
                        });
                        try {
                            await core.file.delete(local);
                            await core.file.delete(local_convert);
                        }
                        // eslint-disable-next-line no-empty
                        catch (err) {

                        }
                    }
                }
            }
            me.log("uploading: " + local_convert + " to: " + remote_convert);
            await db.events.state.set(me.id, session, "upload", {
                from: local_convert,
                to: remote_convert
            });
            await storage.aws.uploadFile(local_convert, remote_convert);
            me.log("deleting: " + local_convert);
            await core.file.delete(local_convert);
            me.log("deleting: " + local);
            await core.file.delete(local);
            me.log("finished screenshot: " + session);
            await db.events.state.set(me.id, session);
            result = true;
        }
        catch (err) {
            me.log_error("Cannot convert session: " + session + " in group: " + group + " error: " + err);
            await db.events.state.set(me.id, session, "error", {
                error: err
            });
            try {
                await core.file.delete(local);
                await core.file.delete(local_convert);
            }
            // eslint-disable-next-line no-empty
            catch (err) {

            }
        }
        return result;
    };
    me.convertItem = async function (resolution, group, session) {
        let result = false;
        try {
            var extension = ".mp4";
            var remote = me.awsBucket + "/" + group + "/" + session + extension;
            var local = me.cachePath + "/" + session + extension;
            var local_convert = me.cachePath + "/" + session + "_" + resolution + extension;
            var remote_convert = me.awsBucket + "/" + group + "/" + session + "_" + resolution + extension;
            if (await storage.aws.exists(remote_convert)) {
                me.log("already converted: " + remote_convert);
                return false;
            }
            if (!await core.file.exists(local_convert)) {
                if (!await core.file.exists(local)) {
                    await db.events.state.set(me.id, session, "download", {
                        from: remote,
                        to: local
                    });
                    me.log("downloading: " + remote + " to: " + local);
                    await storage.aws.downloadFile(remote, local);
                }
                me.log("converting: " + local + " to: " + local_convert);
                await db.events.state.set(me.id, session, "convert", {
                    from: remote,
                    to: local_convert,
                    resolution
                });
                await media.ffmpeg.convert(local, local_convert, {
                    size: resolution,
                }, async (percent) => {
                    await db.events.state.set(me.id, session, "convert", {
                        from: local,
                        to: local_convert,
                        percent
                    });
                });
            }
            me.log("uploading: " + local_convert + " to: " + remote_convert);
            await db.events.state.set(me.id, session, "upload", {
                from: local_convert,
                to: remote_convert
            });
            await storage.aws.uploadFile(local_convert, remote_convert);
            me.log("deleting: " + local_convert);
            await core.file.delete(local_convert);
            me.log("deleting: " + local);
            await core.file.delete(local);
            me.log("finished: " + session);
            await db.events.state.set(me.id, session);
            result = true;
        }
        catch (err) {
            me.log_error("Cannot convert session: " + session + " in group: " + group + " error: " + err);
            await db.events.state.set(me.id, session, "error", {
                error: err
            });
            try {
                await core.file.delete(local);
                await core.file.delete(local_convert);
            }
            // eslint-disable-next-line no-empty
            catch (err) {

            }
        }
        return result;
    };
    me.convertListing = async function (resolution) {
        if (!resolution) {
            for (resolution of me.resolutions) {
                await me.convertListing(resolution);
            }
            await me.convertListing("screenshot");
            return;
        }
        var groups = await me.groups();
        var argList = [];
        for (let group of groups) {
            var list = group.sessions.filter(session => session.extension === "mp4");
            me.log("Checking listing for group: " + group.name + " to convert to resolution: " + resolution + " out of " + list.length + " items");
            await Promise.all(list.map(async item => {
                if (resolution === "screenshot") {
                    const remote_convert = me.awsBucket + "/" + group.name + "/" + item.session + ".png";
                    if (await storage.aws.exists(remote_convert)) {
                        return;
                    }
                    argList.push(["media.file.screenshot", group.name, item.session]);
                }
                else {
                    const remote_convert = me.awsBucket + "/" + group.name + "/" + item.session + "_" + resolution + ".mp4";
                    if (await storage.aws.exists(remote_convert)) {
                        return;
                    }
                    argList.push(["media.file.convertItem", resolution, group.name, item.session]);
                }
            }));
        }
        await db.events.msg.sendParallel(argList);
    };
    return "server";
};
<|endoftext|>