<commit_before>define([
	'config/types',
	'utils/removeFromArray',
	'shared/resolveRef',
	'shared/Unresolved',
	'shared/registerDependant',
	'shared/unregisterDependant',
	'render/shared/Resolvers/ExpressionResolver',
	'render/shared/utils/getNewKeypath'
], function (
	types,
	removeFromArray,
	resolveRef,
	Unresolved,
	registerDependant,
	unregisterDependant,
	ExpressionResolver,
	getNewKeypath
) {

	'use strict';

	var KeypathExpressionResolver = function ( mustache, descriptor, callback ) {
		var resolver = this, ractive, parentFragment, keypath, dynamic, members;

		ractive = mustache.root;
		this.parentFragment = parentFragment = mustache.parentFragment;

		this.ref = descriptor.r;
		this.root = mustache.root;
		this.mustache = mustache;

		this.callback = callback;

		this.pending = 0;
		this.unresolved = [];
		members = this.members = [];
		this.indexRefMembers = [];
		this.keypathObservers = [];
		this.expressionResolvers = [];

		descriptor.m.forEach( function ( member, i ) {
			var ref, indexRefs, index, createKeypathObserver, unresolved, expressionResolver;

			if ( typeof member === 'string' ) {
				resolver.members[i] = member;
				return;
			}

			// simple reference?
			if ( member.t === types.REFERENCE ) {
				ref = member.n;

				indexRefs = parentFragment.indexRefs;
				if ( indexRefs && ( index = indexRefs[ ref ] ) !== undefined ) {
					members[i] = index;

					// make a note of it, in case of reassignments
					resolver.indexRefMembers.push({
						ref: ref,
						index: i
					});

					return;
				}

				dynamic = true;

				createKeypathObserver = function ( keypath ) {
					var keypathObserver = new KeypathObserver( ractive, keypath, mustache.priority, resolver, i );
					resolver.keypathObservers.push( keypathObserver );
				};

				// Can we resolve the reference immediately?
				if ( keypath = resolveRef( ractive, ref, parentFragment ) ) {
					createKeypathObserver( keypath );
					return;
				}

				// Couldn't resolve yet
				members[i] = undefined;
				resolver.pending += 1;

				unresolved = new Unresolved( ractive, ref, parentFragment, function ( keypath ) {
					resolver.resolve( i, keypath );
					removeFromArray( resolver.unresolved, unresolved );
				});

				resolver.unresolved.push( unresolved );

				return null;
			}

			// Otherwise we have an expression in its own right
			dynamic = true;

			resolver.pending += 1;
			expressionResolver = new ExpressionResolver( resolver, parentFragment, member, function ( keypath ) {
				resolver.resolve( i, keypath );
				removeFromArray( resolver.unresolved, expressionResolver );
			});
			resolver.unresolved.push( expressionResolver );
		});

		// Some keypath expressions (e.g. foo["bar"], or foo[i] where `i` is an
		// index reference) won't change. So we don't need to register any watchers
		if ( !dynamic ) {
			keypath = this.getKeypath();
			callback( keypath );

			return;
		}

		this.ready = true;
		this.bubble(); // trigger initial resolution if possible
	};

	KeypathExpressionResolver.prototype = {
		getKeypath: function () {
			return this.ref + '.' + this.members.join( '.' );
		},

		bubble: function () {
			if ( !this.ready || this.pending ) {
				return;
			}

			var self = this, keypath = this.getKeypath();

			if( !this.currentKeypath || this.currentKeypath !== keypath ) {
				this.currentKeypath = keypath;

				if ( keypath = resolveRef( this.root, keypath, this.parentFragment ) ) {
					this.keypath = keypath;
				} else {

					new Unresolved( this.root, this.currentKeypath, this.parentFragment, function ( keypath ) {
						self.keypath = keypath;
						self.callback( keypath );
					});

					return;
				}

			}

			this.callback( this.keypath );
		},

		resolve: function ( index, value ) {
			var keypathObserver = new KeypathObserver( this.root, value, this.mustache.priority, this, index );
			keypathObserver.update();

			this.keypathObservers.push( keypathObserver );

			// when all references have been resolved, we can flag the entire expression
			// as having been resolved
			this.resolved = !( --this.pending );
			this.bubble();
		},

		teardown: function () {
			var unresolved;

			while ( unresolved = this.unresolved.pop() ) {
				unresolved.teardown();
			}
		},

		reassign: function ( indexRef, newIndex, oldKeypath, newKeypath ) {
			var changed, changedKeypath, i, member;

			i = this.indexRefMembers.length;
			while ( i-- ) {
				member = this.indexRefMembers[i];
				if ( member.ref === indexRef ) {
					changed = true;
					this.members[ member.index ] = newIndex;
				}
			}
			if( changed ) {
				this.currentKeypath = null;
			}

			//Already resolved the full keypath? Just fix it up...
			if( this.keypath ){
				if ( changedKeypath = getNewKeypath( this.keypath, oldKeypath, newKeypath ) ) {
					this.keypath = changedKeypath;
					changed = true;
				}
			}

			if ( changed ) {
				this.bubble();
			}
		}
	};

	var KeypathObserver = function ( ractive, keypath, priority, resolver, index ) {
		this.root = ractive;
		this.keypath = keypath;
		this.priority = priority;

		this.resolver = resolver;
		this.index = index;

		registerDependant( this );

		this.update();
	};

	KeypathObserver.prototype = {
		update: function () {
			var resolver = this.resolver;

			resolver.members[ this.index ] = this.root.get( this.keypath );
			resolver.bubble();
		},

		teardown: function () {
			unregisterDependant( this );
		}
	};

	return KeypathExpressionResolver;

});
<commit_msg>almost all tests. last case is "reassign" of input in new section<commit_after>define([
	'config/types',
	'utils/removeFromArray',
	'shared/resolveRef',
	'shared/Unresolved',
	'shared/registerDependant',
	'shared/unregisterDependant',
	'render/shared/Resolvers/ExpressionResolver',
	'render/shared/utils/getNewKeypath'
], function (
	types,
	removeFromArray,
	resolveRef,
	Unresolved,
	registerDependant,
	unregisterDependant,
	ExpressionResolver,
	getNewKeypath
) {

	'use strict';

	var KeypathExpressionResolver = function ( mustache, descriptor, callback ) {
		var resolver = this, ractive, parentFragment, keypath, dynamic, members;

		ractive = mustache.root;
		this.parentFragment = parentFragment = mustache.parentFragment;

		this.ref = descriptor.r;
		this.root = mustache.root;
		this.mustache = mustache;

		this.callback = callback;

		this.pending = 0;
		this.unresolved = [];
		members = this.members = [];
		this.indexRefMembers = [];
		this.keypathObservers = [];
		this.expressionResolvers = [];

		descriptor.m.forEach( function ( member, i ) {
			var ref, indexRefs, index, createKeypathObserver, unresolved, expressionResolver;

			if ( typeof member === 'string' ) {
				resolver.members[i] = member;
				return;
			}

			// simple reference?
			if ( member.t === types.REFERENCE ) {
				ref = member.n;

				indexRefs = parentFragment.indexRefs;
				if ( indexRefs && ( index = indexRefs[ ref ] ) !== undefined ) {
					members[i] = index;

					// make a note of it, in case of reassignments
					resolver.indexRefMembers.push({
						ref: ref,
						index: i
					});

					return;
				}

				dynamic = true;

				createKeypathObserver = function ( keypath ) {
					var keypathObserver = new KeypathObserver( ractive, keypath, mustache.priority, resolver, i );
					resolver.keypathObservers.push( keypathObserver );
				};

				// Can we resolve the reference immediately?
				if ( keypath = resolveRef( ractive, ref, parentFragment ) ) {
					createKeypathObserver( keypath );
					return;
				}

				// Couldn't resolve yet
				members[i] = undefined;
				resolver.pending += 1;

				unresolved = new Unresolved( ractive, ref, parentFragment, function ( keypath ) {
					resolver.resolve( i, keypath );
					removeFromArray( resolver.unresolved, unresolved );
				});

				resolver.unresolved.push( unresolved );

				return null;
			}

			// Otherwise we have an expression in its own right
			dynamic = true;

			resolver.pending += 1;
			expressionResolver = new ExpressionResolver( resolver, parentFragment, member, function ( keypath ) {
				resolver.resolve( i, keypath );
				removeFromArray( resolver.unresolved, expressionResolver );
			});
			resolver.unresolved.push( expressionResolver );
		});

		// Some keypath expressions (e.g. foo["bar"], or foo[i] where `i` is an
		// index reference) won't change. So we don't need to register any watchers
		if ( !dynamic ) {
			keypath = this.getKeypath();
			callback( keypath );

			return;
		}

		this.ready = true;
		this.bubble(); // trigger initial resolution if possible
	};

	KeypathExpressionResolver.prototype = {
		getKeypath: function () {
			return this.ref + '.' + this.members.join( '.' );
		},

		bubble: function () {
			if ( !this.ready || this.pending ) {
				return;
			}

			var self = this, keypath = this.getKeypath();

			if( !this.mustache.ref || this.mustache.ref !== keypath ) {
				this.mustache.ref = keypath;

				if ( keypath = resolveRef( this.root, keypath, this.parentFragment ) ) {
					this.keypath = keypath;
				} else {

					this.rootUnresolved = new Unresolved( this.root, this.mustache.ref, this.parentFragment, function ( keypath ) {
						self.keypath = keypath;
						self.rootUnresolved = null;
						self.callback( keypath );
					});
				}

			}

			this.callback( this.rootUnresolved ? this.mustache.ref : this.keypath );
		},

		resolve: function ( index, value ) {
			var keypathObserver = new KeypathObserver( this.root, value, this.mustache.priority, this, index );
			keypathObserver.update();

			this.keypathObservers.push( keypathObserver );

			// when all references have been resolved, we can flag the entire expression
			// as having been resolved
			this.resolved = !( --this.pending );
			this.bubble();
		},

		teardown: function () {
			var unresolved;

			while ( unresolved = this.unresolved.pop() ) {
				unresolved.teardown();
			}
		},

		reassign: function ( indexRef, newIndex, oldKeypath, newKeypath ) {
			var changed, changedKeypath, i, member;

			i = this.indexRefMembers.length;
			while ( i-- ) {
				member = this.indexRefMembers[i];
				if ( member.ref === indexRef ) {
					changed = true;
					this.members[ member.index ] = newIndex;
				}
			}
			if( changed ) {
				this.mustache.ref = null;
			}

			//Already resolved the full keypath? Just fix it up...
			if( this.keypath ){
				if ( changedKeypath = getNewKeypath( this.keypath, oldKeypath, newKeypath ) ) {
					this.keypath = changedKeypath;
					changed = true;
				}
			}

			if ( changed ) {
				this.bubble();
			}
		}
	};

	var KeypathObserver = function ( ractive, keypath, priority, resolver, index ) {
		this.root = ractive;
		this.keypath = keypath;
		this.priority = priority;

		this.resolver = resolver;
		this.index = index;

		registerDependant( this );

		this.update();
	};

	KeypathObserver.prototype = {
		update: function () {
			var resolver = this.resolver;

			resolver.members[ this.index ] = this.root.get( this.keypath );
			resolver.bubble();
		},

		teardown: function () {
			unregisterDependant( this );
		}
	};

	return KeypathExpressionResolver;

});
<|endoftext|>


<commit_before>// Load modules

var Util = require('util');
var Http = require('http');
var Stream = require('stream');

// Declare internals

var internals = {};

internals.Request = function (options) {

    var self = this;

    Stream.Readable.call(this);

    // options: method, url, payload, headers

    this.url = options.url;
    this.method = options.method.toUpperCase();
    this.headers = options.headers || {};
    this.headers['user-agent'] = this.headers['user-agent'] || 'shot'
    this.httpVersion = '1.1';

    // Use _shot namespace to avoid collision with Node

    this._shot = {
        payload: options.payload || null,
        listeners: {},
        isDone: false,
        simulate: options.simulate || {}
    };

    return this;
};

Util.inherits(internals.Request, Stream.Readable);

internals.Request.prototype._read = function (size) {

    var self = this;

    if (this._shot.isDone) {
        this.push(null);
        return;
    }

    this._shot.isDone = true;

    if (this._shot.payload) {
        this.push(this._shot.payload);
    }

    setImmediate(function () {

        if (self._shot.simulate.error) {
            self.emit('error', new Error('Simulated'));
        }

        if (self._shot.simulate.close) {
            self.emit('close');
        }

        if (self._shot.simulate.end !== false) {                                    // 'end' defaults to true
            self.push(null);
        }
    });
};

internals.Response = function (req, onEnd) {

    Http.ServerResponse.call(this, { method: req.method, httpVersionMajor: 1, httpVersionMinor: 1 });

    this._shot = {
        req: req,
        onEnd: onEnd
    };

    return this;
};

Util.inherits(internals.Response, Http.ServerResponse);

internals.Response.prototype.writeHead = function () {

    var headers = ((arguments.length === 2 && typeof arguments[1] === 'object') ? arguments[1] : (arguments.length === 3 ? arguments[2] : {}));
    var result = Http.ServerResponse.prototype.writeHead.apply(this, arguments);

    this._headers = this._headers || {};
    var keys = Object.keys(headers);
    for (var i = 0, il = keys.length; i < il; ++i) {
        this._headers[keys[i]] = headers[keys[i]];
    }

    return result;
};

internals.Response.prototype.write = function (chunk, encoding) {

    Http.ServerResponse.prototype.write.call(this, chunk, encoding);
    return true;
};

internals.Response.prototype.end = function (data, encoding) {

    var self = this;

    if (!this._shot) {
        return;
    }

    Http.ServerResponse.prototype.end.call(this, data, encoding);
    this.emit('finish');                                            // Will not be emitted internally on an inactive socket

    // Prepare response object

    var res = {
        raw: {
            req: this._shot.req,
            res: this
        },
        headers: this._headers,
        statusCode: this.statusCode
    };

    // Parse payload

    var output = '';

    if (self.output &&
        self.output instanceof Array &&
        self.output.length) {

        for (var i = 0, il = self.output.length; i < il; ++i) {
            if (self.output[i] instanceof Buffer) {
                output += self.output[i].toString();
            }
            else {
                output += new Buffer(self.output[i], self.outputEncodings[i]).toString();
            }
        }
    }

    var sep = output.indexOf('\r\n\r\n');
    var payloadBlock = output.slice(sep + 4);
    var headerBlock = output.slice(0, sep);

    if (headerBlock.indexOf('Transfer-Encoding: chunked') !== -1) {
        var rest = payloadBlock;
        res.payload = '';

        while (rest) {
            var next = rest.indexOf('\r\n');
            var size = parseInt(rest.slice(0, next), 16);
            if (size === 0) {
                break;
            }

            res.payload += rest.substr(next + 2, size);
            rest = rest.slice(next + 2 + size + 2);
        }
    }
    else {
        res.payload = payloadBlock;
    }

    // Callback response

    var onEnd = this._shot.onEnd;
    delete this._shot;
    onEnd(res);
};

internals.Response.prototype.destroy = function () {

};

exports.inject = function (dispatchFunc, options, callback) {

    var req = new internals.Request(options);
    var res = new internals.Response(req, callback);
    dispatchFunc(req, res);
};

exports.isInjection = function (obj) {

    return (obj instanceof internals.Request || obj instanceof internals.Response);
};

<commit_msg>restore req.destroy<commit_after>// Load modules

var Util = require('util');
var Http = require('http');
var Stream = require('stream');

// Declare internals

var internals = {};

internals.Request = function (options) {

    var self = this;

    Stream.Readable.call(this);

    // options: method, url, payload, headers

    this.url = options.url;
    this.method = options.method.toUpperCase();
    this.headers = options.headers || {};
    this.headers['user-agent'] = this.headers['user-agent'] || 'shot'
    this.httpVersion = '1.1';

    // Use _shot namespace to avoid collision with Node

    this._shot = {
        payload: options.payload || null,
        listeners: {},
        isDone: false,
        simulate: options.simulate || {}
    };

    return this;
};

Util.inherits(internals.Request, Stream.Readable);

internals.Request.prototype._read = function (size) {

    var self = this;

    if (this._shot.isDone) {
        this.push(null);
        return;
    }

    this._shot.isDone = true;

    if (this._shot.payload) {
        this.push(this._shot.payload);
    }

    setImmediate(function () {

        if (self._shot.simulate.error) {
            self.emit('error', new Error('Simulated'));
        }

        if (self._shot.simulate.close) {
            self.emit('close');
        }

        if (self._shot.simulate.end !== false) {                                    // 'end' defaults to true
            self.push(null);
        }
    });
};

internals.Request.prototype.destroy = function () {

};

internals.Response = function (req, onEnd) {

    Http.ServerResponse.call(this, { method: req.method, httpVersionMajor: 1, httpVersionMinor: 1 });

    this._shot = {
        req: req,
        onEnd: onEnd
    };

    return this;
};

Util.inherits(internals.Response, Http.ServerResponse);

internals.Response.prototype.writeHead = function () {

    var headers = ((arguments.length === 2 && typeof arguments[1] === 'object') ? arguments[1] : (arguments.length === 3 ? arguments[2] : {}));
    var result = Http.ServerResponse.prototype.writeHead.apply(this, arguments);

    this._headers = this._headers || {};
    var keys = Object.keys(headers);
    for (var i = 0, il = keys.length; i < il; ++i) {
        this._headers[keys[i]] = headers[keys[i]];
    }

    return result;
};

internals.Response.prototype.write = function (chunk, encoding) {

    Http.ServerResponse.prototype.write.call(this, chunk, encoding);
    return true;
};

internals.Response.prototype.end = function (data, encoding) {

    var self = this;

    if (!this._shot) {
        return;
    }

    Http.ServerResponse.prototype.end.call(this, data, encoding);
    this.emit('finish');                                            // Will not be emitted internally on an inactive socket

    // Prepare response object

    var res = {
        raw: {
            req: this._shot.req,
            res: this
        },
        headers: this._headers,
        statusCode: this.statusCode
    };

    // Parse payload

    var output = '';

    if (self.output &&
        self.output instanceof Array &&
        self.output.length) {

        for (var i = 0, il = self.output.length; i < il; ++i) {
            if (self.output[i] instanceof Buffer) {
                output += self.output[i].toString();
            }
            else {
                output += new Buffer(self.output[i], self.outputEncodings[i]).toString();
            }
        }
    }

    var sep = output.indexOf('\r\n\r\n');
    var payloadBlock = output.slice(sep + 4);
    var headerBlock = output.slice(0, sep);

    if (headerBlock.indexOf('Transfer-Encoding: chunked') !== -1) {
        var rest = payloadBlock;
        res.payload = '';

        while (rest) {
            var next = rest.indexOf('\r\n');
            var size = parseInt(rest.slice(0, next), 16);
            if (size === 0) {
                break;
            }

            res.payload += rest.substr(next + 2, size);
            rest = rest.slice(next + 2 + size + 2);
        }
    }
    else {
        res.payload = payloadBlock;
    }

    // Callback response

    var onEnd = this._shot.onEnd;
    delete this._shot;
    onEnd(res);
};

internals.Response.prototype.destroy = function () {

};

exports.inject = function (dispatchFunc, options, callback) {

    var req = new internals.Request(options);
    var res = new internals.Response(req, callback);
    dispatchFunc(req, res);
};

exports.isInjection = function (obj) {

    return (obj instanceof internals.Request || obj instanceof internals.Response);
};

<|endoftext|>


<commit_before>const Signatures = require('./content')
const request = require('./request')
const constants = require('./constants')
const dateFormat = require('dateformat')
const moment = require('moment')
const ptLocale = require('moment/locale/pt-br')
const enLocale = require('moment/locale/en-gb')
const i18n = require('i18n')

const setMomentLocale = (lang) => {
  if (lang === 'br') {
    moment.locale('pt-br', ptLocale)
  }
  else {
    moment.locale('en-gb', enLocale)
  }
}

const AssignMail = {
  owner: (to, task, name) => {},
  interested: (to, task, name) => {},
  error: (msg) => {}
}

if (constants.canSendEmail) {
  AssignMail.owner = (user, task, name) => {
    const to = user.email
    const language = user.language || 'en'
    i18n.setLocale(language)
    setMomentLocale(language)
    request(
      to,
      i18n.__('mail.assign.owner.subject'),
      [
        {
          type: 'text/html',
          value: `

${i18n.__('mail.assign.owner.hello')},

${i18n.__('mail.assign.owner.main', { name: name, url: `${process.env.FRONTEND_HOST}/#/task/${task.id}` })}

${i18n.__('mail.assign.owner.sec')}

${Signatures.sign(language)}
`
        },
      ]
    )
  }

  AssignMail.interested = (user, task, name) => {
    const to = user.email
    const language = user.language || 'en'
    i18n.setLocale(language)
    setMomentLocale(language)
    request(
      to,
      i18n.__('mail.interested.subject'),
      [
        {
          type: 'text/html',
          value: `

${i18n.__('mail.assign.owner.hello')},

${i18n.__('mail.interested.main', { name: name, url: `${process.env.FRONTEND_HOST}/#/task/${task.id}` })}

${i18n.__('mail.interested.owner.sec')}

${Signatures.sign(language)}
`
        },
      ]
    )
  }

  AssignMail.assigned = (user, task, name) => {
    const to = user.email
    const language = user.language || 'en'
    i18n.setLocale(language)
    setMomentLocale(language)
    request(
      to,
      i18n.__('mail.assigned.subject'),
      [
        {
          type: 'text/html',
          value: `

${i18n.__('mail.assigned.hello', { name: name })}

${i18n.__('mail.assigned.main', { name: name, url: `${process.env.FRONTEND_HOST}/#/task/${task.id}` })}

           ${i18n.__('mail.assigned.message', {
    deadline: task.deadline ? dateFormat(task.deadline, constants.dateFormat) : i18n.__('mail.assigned.nodate'),
    deadlineFromNow: task.deadline ? moment(task.deadline).fromNow() : i18n.__('mail.assigned.anytime')
  })}

${Signatures.sign(language)}
`

        }
      ]
    )
  }

  AssignMail.error = (msg) => {
    request(
      constants.notificationEmail,
      i18n.__('mail.assigned.error'),
      [
        {
          type: 'text/html',
          value: msg
        },
      ]
    )
  }
}

module.exports = AssignMail
<commit_msg>fixing translation key for interested user for a task<commit_after>const Signatures = require('./content')
const request = require('./request')
const constants = require('./constants')
const dateFormat = require('dateformat')
const moment = require('moment')
const ptLocale = require('moment/locale/pt-br')
const enLocale = require('moment/locale/en-gb')
const i18n = require('i18n')

const setMomentLocale = (lang) => {
  if (lang === 'br') {
    moment.locale('pt-br', ptLocale)
  }
  else {
    moment.locale('en-gb', enLocale)
  }
}

const AssignMail = {
  owner: (to, task, name) => {},
  interested: (to, task, name) => {},
  error: (msg) => {}
}

if (constants.canSendEmail) {
  AssignMail.owner = (user, task, name) => {
    const to = user.email
    const language = user.language || 'en'
    i18n.setLocale(language)
    setMomentLocale(language)
    request(
      to,
      i18n.__('mail.assign.owner.subject'),
      [
        {
          type: 'text/html',
          value: `

${i18n.__('mail.assign.owner.hello')},

${i18n.__('mail.assign.owner.main', { name: name, url: `${process.env.FRONTEND_HOST}/#/task/${task.id}` })}

${i18n.__('mail.assign.owner.sec')}

${Signatures.sign(language)}
`
        },
      ]
    )
  }

  AssignMail.interested = (user, task, name) => {
    const to = user.email
    const language = user.language || 'en'
    i18n.setLocale(language)
    setMomentLocale(language)
    request(
      to,
      i18n.__('mail.interested.owner.subject'),
      [
        {
          type: 'text/html',
          value: `

${i18n.__('mail.assign.owner.hello')},

${i18n.__('mail.interested.main', { name: name, url: `${process.env.FRONTEND_HOST}/#/task/${task.id}` })}

${i18n.__('mail.interested.owner.sec')}

${Signatures.sign(language)}
`
        },
      ]
    )
  }

  AssignMail.assigned = (user, task, name) => {
    const to = user.email
    const language = user.language || 'en'
    i18n.setLocale(language)
    setMomentLocale(language)
    request(
      to,
      i18n.__('mail.assigned.subject'),
      [
        {
          type: 'text/html',
          value: `

${i18n.__('mail.assigned.hello', { name: name })}

${i18n.__('mail.assigned.main', { name: name, url: `${process.env.FRONTEND_HOST}/#/task/${task.id}` })}

           ${i18n.__('mail.assigned.message', {
    deadline: task.deadline ? dateFormat(task.deadline, constants.dateFormat) : i18n.__('mail.assigned.nodate'),
    deadlineFromNow: task.deadline ? moment(task.deadline).fromNow() : i18n.__('mail.assigned.anytime')
  })}

${Signatures.sign(language)}
`

        }
      ]
    )
  }

  AssignMail.error = (msg) => {
    request(
      constants.notificationEmail,
      i18n.__('mail.assigned.error'),
      [
        {
          type: 'text/html',
          value: msg
        },
      ]
    )
  }
}

module.exports = AssignMail
<|endoftext|>


<commit_before>/**
 * Allows you to animate any Drawable node
 * @class Animation
 * @fires FF.Animation#start
 * @fires FF.Animation#step
 * @fires FF.Animation#end
 * @example
 * TODO
 */
FF.Animation = function(item, duration, options){
	FF.EventManager.call(this);

	var that = this;

	this.node = item;

	this.timer = new FF.Timer({ timeout : duration, interval : options.step || 2, autostart : options.autostart  });
	this.timer.addEventListener("start", function(){ that.dispatchEvent({ type : "start" }); });
	this.timer.addEventListener("tick", function(){ that.animate(); that.dispatchEvent({ type : "step" }); });
	this.timer.addEventListener("timeout", function(){ that.dispatchEvent({ type : "end" }); });

	this.animateLeft = options.left || null;

	this.animateRight = options.right || null;

	this.animateTop = options.top || null;

	this.animateBottom = options.bottom || null;

	this.animateWidth = options.width || null;

	this.animateHeight = options.height || null;

	this.animateScale = options.scale || null;

	this.animateOpacity = options.opacity || null;
};

FF.Animation.prototype.update = function(){
	this.timer.update();
};

FF.Animation.prototype.animate = function(){
	if(this.animateLeft) this.node.x-= this.animateLeft;
	if(this.animateRight) this.node.x+= this.animateRight;
	if(this.animateTop) this.node.y-= this.animateTop;
	if(this.animateBottom) this.node.y+= this.animateBottom;
	if(this.animateWidth) this.node.width+= this.animateWidth;
	if(this.animateHeight) this.node.height+= this.animateHeight;
	if(this.animateScale && this.node.scale > 0) this.node.scale+= this.animateScale;
	if(this.animateOpacity && this.node.opacity > 0) this.node.opacity+= this.animateOpacity;
};
<commit_msg>Fix little mistake on Animation timer<commit_after>/**
 * Allows you to animate any Drawable node
 * @class Animation
 * @fires FF.Animation#start
 * @fires FF.Animation#step
 * @fires FF.Animation#end
 * @example
 * TODO
 */
FF.Animation = function(item, duration, options){
	FF.EventManager.call(this);

	var that = this;

	this.node = item;

	this.timer = new FF.Timer({ timeout : duration, interval : options.step || 2, autostart : options.autostart  });
	this.timer.addEventListener("start", function(){ that.dispatchEvent({ type : "start" }); });
	this.timer.addEventListener("tick", function(e){ that.animate(e); that.dispatchEvent({ type : "step" }); });
	this.timer.addEventListener("timeout", function(){ that.dispatchEvent({ type : "end" }); });

	this.animateLeft = options.left || null;

	this.animateRight = options.right || null;

	this.animateTop = options.top || null;

	this.animateBottom = options.bottom || null;

	this.animateWidth = options.width || null;

	this.animateHeight = options.height || null;

	this.animateScale = options.scale || null;

	this.animateOpacity = options.opacity || null;
};

FF.Animation.prototype.update = function(){
	this.timer.update();
};

FF.Animation.prototype.animate = function(tickEvent){
	var percent = (tickEvent.remaining / this.timer.timeout) * 100;

	if(this.animateLeft) this.node.x-= this.animateLeft * percent;
	if(this.animateRight) this.node.x+= this.animateRight * percent;
	if(this.animateTop) this.node.y-= this.animateTop * percent;
	if(this.animateBottom) this.node.y+= this.animateBottom * percent;
	if(this.animateWidth) this.node.width+= this.animateWidth * percent;
	if(this.animateHeight) this.node.height+= this.animateHeight * percent;
	if(this.animateScale && this.node.scale > 0) this.node.scale+= this.animateScale * percent;
	if(this.animateOpacity && this.node.opacity > 0) this.node.opacity+= this.animateOpacity * percent;
};
<|endoftext|>


<commit_before>var Anagram = require('./anagram');

describe('Anagram', function() {

  it("no matches",function() {
    var detector = new Anagram("diaper");
    var matches = detector.match([ "hello", "world", "zombies", "pants"]);
    expect(matches).toEqual([]);
  });

  xit("detects simple anagram",function() {
    var detector = new Anagram("ba");
    var matches = detector.match(['ab', 'abc', 'bac']);
    expect(matches).toEqual(['ab']);
  });

  xit("does not detect false positives",function() {
    var detector = new Anagram("bba");
    var matches = detector.match(["aab"]);
    expect(matches).toEqual([]);
  });

  xit("detects multiple anagrams",function() {
    var detector = new Anagram("abc");
    var matches = detector.match(['ab', 'abc', 'bac']);
    expect(matches).toEqual(['abc', 'bac']);
  });

  xit("detects anagram",function() {
    var detector = new Anagram("listen");
    var matches = detector.match(['enlists', 'google', 'inlets', 'banana']);
    expect(matches).toEqual(['inlets']);
  });

  xit("detects multiple anagrams",function() {
    var detector = new Anagram("allergy");
    var matches = detector.match(['gallery', 'ballerina', 'regally', 'clergy', 'largely', 'leading']);
    expect(matches).toEqual(['gallery', 'regally', 'largely']);
  });
});
<commit_msg>updated anagram in js to use real words<commit_after>var Anagram = require('./anagram');

describe('Anagram', function() {

  it("no matches",function() {
    var detector = new Anagram("diaper");
    var matches = detector.match([ "hello", "world", "zombies", "pants"]);
    expect(matches).toEqual([]);
  });

  xit("detects simple anagram",function() {
    var detector = new Anagram("ant");
    var matches = detector.match(['tan', 'stand', 'at']);
    expect(matches).toEqual(['tan']);
  });

  xit("does not detect false positives",function() {
    var detector = new Anagram("galea");
    var matches = detector.match(["eagle"]);
    expect(matches).toEqual([]);
  });

  xit("detects multiple anagrams",function() {
    var detector = new Anagram("master");
    var matches = detector.match(['stream', 'pigeon', 'maters']);
    expect(matches).toEqual(['stream', 'maters']);
  });

  xit("detects anagram",function() {
    var detector = new Anagram("listen");
    var matches = detector.match(['enlists', 'google', 'inlets', 'banana']);
    expect(matches).toEqual(['inlets']);
  });

  xit("detects multiple anagrams",function() {
    var detector = new Anagram("allergy");
    var matches = detector.match(['gallery', 'ballerina', 'regally', 'clergy', 'largely', 'leading']);
    expect(matches).toEqual(['gallery', 'regally', 'largely']);
  });
});
<|endoftext|>


<commit_before>describe("luga.utils", function(){

	"use strict";

	it("Contains generic, static methods and utilities", function(){
		expect(luga.utils).toBeDefined();
	});

	describe(".displayBox()", function(){

		it("Display a box with a message associated with a given node", function(){
			loadFixtures("validator/FormValidator/basic.htm");
			var formNode = jQuery("<input id='myId' name='ciccio'>");
			expect(boxNode).not.toExist();
			luga.utils.displayBox(formNode, "Error");
			var boxNode = formNode.prev();
			expect(boxNode).toExist();
			expect(boxNode).toHaveText("Error");
			expect(boxNode).toHaveClass(luga.utils.CONST.CSS_CLASSES.MESSAGE);
		});

	});

	describe(".displayMessage()", function(){

		it("Display a message box above a given node", function(){
			loadFixtures("validator/FormValidator/basic.htm");
			var formNode = jQuery("#basic");
			expect(boxNode).not.toExist();
			luga.utils.displayMessage(formNode, "Ciao");
			var boxNode = formNode.prev();
			expect(boxNode).toExist();
			expect(boxNode).toHaveText("Ciao");
			expect(boxNode).toHaveClass(luga.utils.CONST.CSS_CLASSES.MESSAGE);
		});

	});

	describe(".displayErrorMessage()", function(){

		it("Display an error box above a given node", function(){
			loadFixtures("validator/FormValidator/basic.htm");
			var formNode = jQuery("#basic");
			expect(boxNode).not.toExist();
			luga.utils.displayErrorMessage(formNode, "Error");
			var boxNode = formNode.prev();
			expect(boxNode).toExist();
			expect(boxNode).toHaveText("Error");
			expect(boxNode).toHaveClass(luga.utils.CONST.CSS_CLASSES.ERROR_MESSAGE);
		});

	});

	describe(".removeDisplayBox()", function(){

		it("Remove a message box (if any) associated with a given nod", function(){
			loadFixtures("validator/FormValidator/basic.htm");
			var formNode = jQuery("#basic");
			expect(boxNode).not.toExist();
			luga.utils.displayErrorMessage(formNode, "Error");
			luga.utils.removeDisplayBox(formNode);
			var boxNode = formNode.prev();
			expect(boxNode).not.toExist();
		});

	});

});<commit_msg>Test: Fixed typo<commit_after>describe("luga.utils", function(){

	"use strict";

	it("Contains generic, static methods and utilities", function(){
		expect(luga.utils).toBeDefined();
	});

	describe(".displayBox()", function(){

		it("Display a box with a message associated with a given node", function(){
			loadFixtures("validator/FormValidator/basic.htm");
			var formNode = jQuery("<input id='myId' name='ciccio'>");
			expect(boxNode).not.toExist();
			luga.utils.displayBox(formNode, "Error");
			var boxNode = formNode.prev();
			expect(boxNode).toExist();
			expect(boxNode).toHaveText("Error");
			expect(boxNode).toHaveClass(luga.utils.CONST.CSS_CLASSES.MESSAGE);
		});

	});

	describe(".displayMessage()", function(){

		it("Display a message box above a given node", function(){
			loadFixtures("validator/FormValidator/basic.htm");
			var formNode = jQuery("#basic");
			expect(boxNode).not.toExist();
			luga.utils.displayMessage(formNode, "Ciao");
			var boxNode = formNode.prev();
			expect(boxNode).toExist();
			expect(boxNode).toHaveText("Ciao");
			expect(boxNode).toHaveClass(luga.utils.CONST.CSS_CLASSES.MESSAGE);
		});

	});

	describe(".displayErrorMessage()", function(){

		it("Display an error box above a given node", function(){
			loadFixtures("validator/FormValidator/basic.htm");
			var formNode = jQuery("#basic");
			expect(boxNode).not.toExist();
			luga.utils.displayErrorMessage(formNode, "Error");
			var boxNode = formNode.prev();
			expect(boxNode).toExist();
			expect(boxNode).toHaveText("Error");
			expect(boxNode).toHaveClass(luga.utils.CONST.CSS_CLASSES.ERROR_MESSAGE);
		});

	});

	describe(".removeDisplayBox()", function(){

		it("Remove a message box (if any) associated with a given node", function(){
			loadFixtures("validator/FormValidator/basic.htm");
			var formNode = jQuery("#basic");
			expect(boxNode).not.toExist();
			luga.utils.displayErrorMessage(formNode, "Error");
			luga.utils.removeDisplayBox(formNode);
			var boxNode = formNode.prev();
			expect(boxNode).not.toExist();
		});

	});

});<|endoftext|>


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

import {analyticsEventToGoogleAnalyticsEvent} from './event-type-mapping';
import {isFunction} from '../utils/types';

/** @typedef {?function(string, string, Object)} */
let AnalyticsMethod;

/** @typedef {{ga: AnalyticsMethod, gtag: AnalyticsMethod}} */
let WindowWithAnalyticsMethods;

export class GoogleAnalyticsEventListener {
  /**
   * @param {!./deps.DepsDef} deps
   */
  constructor(deps) {
    /** @private @const {!./deps.DepsDef} deps */
    this.deps_ = deps;

    /** @private @const {!./client-event-manager.ClientEventManager} */
    this.eventManager_ = deps.eventManager();
  }

  /**
   * Start listening to client events
   */
  start() {
    this.eventManager_.registerEventListener(
      this.handleClientEvent_.bind(this)
    );
  }

  /**
   *  Listens for new events from the events manager and logs appropriate events to Google Analytics.
   * @param {!../api/client-event-manager-api.ClientEvent} event
   * @param {(!../api/client-event-manager-api.ClientEventParams|undefined)=} eventParams
   */
  handleClientEvent_(event, eventParams = undefined) {
    // Require either ga function (analytics.js) or gtag function (gtag.js).
    const gaIsEligible = GoogleAnalyticsEventListener.isGaEligible(this.deps_);
    const gtagIsEligible = GoogleAnalyticsEventListener.isGtagEligible(
      this.deps_
    );
    const neitherIsEligible = !gaIsEligible && !gtagIsEligible;
    if (neitherIsEligible) {
      return;
    }

    // Extract methods from window.
    const {ga, gtag} = /** @type {!WindowWithAnalyticsMethods} */ (
      this.deps_.win()
    );

    let subscriptionFlow = '';
    if (event.additionalParameters) {
      // additionalParameters isn't strongly typed so checking for both object and class notation.
      subscriptionFlow =
        event.additionalParameters.subscriptionFlow ||
        event.additionalParameters.getSubscriptionFlow();
    }
    let gaEvent = analyticsEventToGoogleAnalyticsEvent(
      event.eventType,
      subscriptionFlow
    );
    if (!gaEvent) {
      return;
    }

    const analyticsParams = eventParams?.googleAnalyticsParameters || {};
    gaEvent = {
      ...gaEvent,
      eventCategory: analyticsParams.event_category || gaEvent.eventCategory,
      eventLabel: analyticsParams.event_label || gaEvent.eventLabel,
    };

    // TODO(b/234825847): Remove this once universal analytics is deprecated in 2023.
    if (gaIsEligible) {
      ga('send', 'event', gaEvent);
    }

    if (gtagIsEligible) {
      const gtagEvent = {
        'event_category': gaEvent.eventCategory,
        'event_label': gaEvent.eventLabel,
        'non_interaction': gaEvent.nonInteraction,
        ...analyticsParams,
      };
      gtag('event', gaEvent.eventAction, gtagEvent);
    }
  }

  /**
   * Function to determine whether event is eligible for GA logging.
   * @param {!./deps.DepsDef} deps
   * @returns {boolean}
   */
  static isGaEligible(deps) {
    return isFunction(
      /** @type {!WindowWithAnalyticsMethods} */ (deps.win()).ga
    );
  }

  /**
   * Function to determine whether event is eligible for gTag logging.
   * @param {!./deps.DepsDef} deps
   * @returns {boolean}
   */
  static isGtagEligible(deps) {
    return isFunction(
      /** @type {!WindowWithAnalyticsMethods} */ (deps.win()).gtag
    );
  }
}
<commit_msg>disable no-used-var eslint for typedefs<commit_after>/**
 * Copyright 2021 The Subscribe with Google Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {analyticsEventToGoogleAnalyticsEvent} from './event-type-mapping';
import {isFunction} from '../utils/types';

/* eslint-disable no-unused-vars */
/** @typedef {?function(string, string, Object)} */
let AnalyticsMethod;

/** @typedef {{ga: AnalyticsMethod, gtag: AnalyticsMethod}} */
let WindowWithAnalyticsMethods;
/* eslint-enable no-unused-vars */

export class GoogleAnalyticsEventListener {
  /**
   * @param {!./deps.DepsDef} deps
   */
  constructor(deps) {
    /** @private @const {!./deps.DepsDef} deps */
    this.deps_ = deps;

    /** @private @const {!./client-event-manager.ClientEventManager} */
    this.eventManager_ = deps.eventManager();
  }

  /**
   * Start listening to client events
   */
  start() {
    this.eventManager_.registerEventListener(
      this.handleClientEvent_.bind(this)
    );
  }

  /**
   *  Listens for new events from the events manager and logs appropriate events to Google Analytics.
   * @param {!../api/client-event-manager-api.ClientEvent} event
   * @param {(!../api/client-event-manager-api.ClientEventParams|undefined)=} eventParams
   */
  handleClientEvent_(event, eventParams = undefined) {
    // Require either ga function (analytics.js) or gtag function (gtag.js).
    const gaIsEligible = GoogleAnalyticsEventListener.isGaEligible(this.deps_);
    const gtagIsEligible = GoogleAnalyticsEventListener.isGtagEligible(
      this.deps_
    );
    const neitherIsEligible = !gaIsEligible && !gtagIsEligible;
    if (neitherIsEligible) {
      return;
    }

    // Extract methods from window.
    const {ga, gtag} = /** @type {!WindowWithAnalyticsMethods} */ (
      this.deps_.win()
    );

    let subscriptionFlow = '';
    if (event.additionalParameters) {
      // additionalParameters isn't strongly typed so checking for both object and class notation.
      subscriptionFlow =
        event.additionalParameters.subscriptionFlow ||
        event.additionalParameters.getSubscriptionFlow();
    }
    let gaEvent = analyticsEventToGoogleAnalyticsEvent(
      event.eventType,
      subscriptionFlow
    );
    if (!gaEvent) {
      return;
    }

    const analyticsParams = eventParams?.googleAnalyticsParameters || {};
    gaEvent = {
      ...gaEvent,
      eventCategory: analyticsParams.event_category || gaEvent.eventCategory,
      eventLabel: analyticsParams.event_label || gaEvent.eventLabel,
    };

    // TODO(b/234825847): Remove this once universal analytics is deprecated in 2023.
    if (gaIsEligible) {
      ga('send', 'event', gaEvent);
    }

    if (gtagIsEligible) {
      const gtagEvent = {
        'event_category': gaEvent.eventCategory,
        'event_label': gaEvent.eventLabel,
        'non_interaction': gaEvent.nonInteraction,
        ...analyticsParams,
      };
      gtag('event', gaEvent.eventAction, gtagEvent);
    }
  }

  /**
   * Function to determine whether event is eligible for GA logging.
   * @param {!./deps.DepsDef} deps
   * @returns {boolean}
   */
  static isGaEligible(deps) {
    return isFunction(
      /** @type {!WindowWithAnalyticsMethods} */ (deps.win()).ga
    );
  }

  /**
   * Function to determine whether event is eligible for gTag logging.
   * @param {!./deps.DepsDef} deps
   * @returns {boolean}
   */
  static isGtagEligible(deps) {
    return isFunction(
      /** @type {!WindowWithAnalyticsMethods} */ (deps.win()).gtag
    );
  }
}
<|endoftext|>


<commit_before>// Copyright 2017, University of Colorado Boulder

/**
 * Builds a runnable (something that builds like a simulation)
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 */

'use strict';

// modules
const _ = require( 'lodash' ); // eslint-disable-line require-statement-match
const _7z = require('7zip-min'); // eslint-disable-line require-statement-match
const assert = require( 'assert' );
const buildMipmaps = require( './buildMipmaps' );
const ChipperConstants = require( '../common/ChipperConstants' );
const ChipperStringUtils = require( '../common/ChipperStringUtils' );
const copyDirectory = require( './copyDirectory' );
const copySupplementalPhetioFiles = require( './phet-io/copySupplementalPhetioFiles' );
const generateThumbnails = require( './generateThumbnails' );
const generateTwitterCard = require( './generateTwitterCard' );
const getA11yViewHTMLFromTemplate = require( './getA11yViewHTMLFromTemplate' );
const getAllThirdPartyEntries = require( './getAllThirdPartyEntries' );
const getDependencies = require( './getDependencies' );
const getInitializationScript = require( './getInitializationScript' );
const getLocalesFromRepository = require( './getLocalesFromRepository' );
const getPhetLibs = require( './getPhetLibs' );
const getPreloads = require( './getPreloads' );
const getStringMap = require( './getStringMap' );
const getTitleStringKey = require( './getTitleStringKey' );
const grunt = require( 'grunt' );
const jimp = require( 'jimp' );
const loadFileAsDataURI = require( '../common/loadFileAsDataURI' );
const minify = require( './minify' );
const nodeHTMLEncoder = require( 'node-html-encoder' ); // eslint-disable-line require-statement-match
const packageRunnable = require( './packageRunnable' );
const packageXHTML = require( './packageXHTML' );
const reportUnusedMedia = require( './reportUnusedMedia' );
const reportUnusedStrings = require( './reportUnusedStrings' );
const requireBuild = require( './requireBuild' );

/**
 * Builds a runnable (e.g. a simulation).
 * @public
 *
 * @param {string} repo
 * @param {Object} minifyOptions - see minify.js
 * @param {boolean} instrument - If the sim should be instrumented
 * @param {boolean} allHTML - If the _all.html file should be generated
 * @param {string} brand
 * @param {string} localesOption - e.g,. '*', 'en,es', etc.
 * @returns {Promise} - Does not resolve a value
 */
module.exports = async function( repo, minifyOptions, instrument, allHTML, brand, localesOption ) {
  // TODO: too many parameters. use options pattern instead.
  assert( typeof repo === 'string' );
  assert( typeof minifyOptions === 'object' );
  assert( _.includes( ChipperConstants.BRANDS, brand ), 'Unknown brand in buildRunnable: ' + brand );

  if ( brand === 'phet-io' ) {
    assert( grunt.file.exists( '../phet-io' ), 'Aborting the build of phet-io brand since proprietary repositories are not checked out.\nPlease use --brands=={{BRAND}} in the future to avoid this.' );
  }

  const packageObject = grunt.file.readJSON( `../${repo}/package.json` );

  const encoder = new nodeHTMLEncoder.Encoder( 'entity' );

  // All html files share the same build timestamp
  let timestamp = new Date().toISOString().split( 'T' ).join( ' ' );
  timestamp = timestamp.substring( 0, timestamp.indexOf( '.' ) ) + ' UTC';

  // NOTE: This build currently (due to the string/mipmap plugins) modifies globals. Some operations need to be done after this.
  const requireJS = await requireBuild( repo, `../${repo}/js/${repo}-config.js`, {
    insertRequire: repo + '-main',
    instrument: instrument,
    brand: brand
  } );

  // Debug version is independent of passed in minifyOptions.  PhET-iO brand is minified, but leaves assertions & logging.
  const debugMinifyOptions = brand === 'phet-io' ? {
    stripAssertions: false,
    stripLogging: false
  } : {
    minify: false
  };

  const productionJS = minify( requireJS, minifyOptions );
  const debugJS = minify( requireJS, debugMinifyOptions );

  // After all media plugins have completed (which happens in requirejs:build), report which media files in the repository are unused.
  reportUnusedMedia( packageObject.phet.requirejsNamespace );

  // After all strings have been loaded, report which of the translatable strings are unused.
  reportUnusedStrings( repo, packageObject.phet.requirejsNamespace );

  const rawPreloads = getPreloads( repo, brand ).map( filename => grunt.file.read( filename ) );
  const productionPreloads = rawPreloads.map( js => minify( js, minifyOptions ) );
  const debugPreloads = rawPreloads.map( js => minify( js, debugMinifyOptions ) );

  const phetLibs = getPhetLibs( repo, brand );
  const allLocales = [ ChipperConstants.FALLBACK_LOCALE, ...getLocalesFromRepository( repo ) ];
  const locales = localesOption === '*' ? allLocales : localesOption.split( ',' );
  const dependencies = await getDependencies( repo );
  const version = packageObject.version; // Include the one-off name in the version
  const thirdPartyEntries = getAllThirdPartyEntries( repo, brand );
  const stringMap = getStringMap( allLocales, phetLibs );
  const mipmapsJavaScript = await buildMipmaps();

  const simTitleStringKey = getTitleStringKey( repo );
  const englishTitle = stringMap[ ChipperConstants.FALLBACK_LOCALE ][ simTitleStringKey ];
  assert( englishTitle, `missing entry for sim title, key = ${simTitleStringKey}` );

  // Select the HTML comment header based on the brand, see https://github.com/phetsims/chipper/issues/156
  let htmlHeader;
  if ( brand === 'phet-io' ) {

    // License text provided by @kathy-phet in https://github.com/phetsims/chipper/issues/148#issuecomment-112584773
    htmlHeader = englishTitle + ' ' + version + '\n' +
                 'Copyright 2002-' + grunt.template.today( 'yyyy' ) + ', Regents of the University of Colorado\n' +
                 'PhET Interactive Simulations, University of Colorado Boulder\n' +
                 '\n' +
                 'This Interoperable PhET Simulation file requires a license.\n' +
                 'USE WITHOUT A LICENSE AGREEMENT IS STRICTLY PROHIBITED.\n' +
                 'Contact phethelp@colorado.edu regarding licensing.\n' +
                 'https://phet.colorado.edu/en/licensing';
  }
  else {
    htmlHeader = englishTitle + ' ' + version + '\n' +
                 'Copyright 2002-' + grunt.template.today( 'yyyy' ) + ', Regents of the University of Colorado\n' +
                 'PhET Interactive Simulations, University of Colorado Boulder\n' +
                 '\n' +
                 'This file is licensed under Creative Commons Attribution 4.0\n' +
                 'For alternate source code licensing, see https://github.com/phetsims\n' +
                 'For licenses for third-party software used by this simulation, see below\n' +
                 'For more information, see https://phet.colorado.edu/en/licensing/html\n' +
                 '\n' +
                 'The PhET name and PhET logo are registered trademarks of The Regents of the\n' +
                 'University of Colorado. Permission is granted to use the PhET name and PhET logo\n' +
                 'only for attribution purposes. Use of the PhET name and/or PhET logo for promotional,\n' +
                 'marketing, or advertising purposes requires a separate license agreement from the\n' +
                 'University of Colorado. Contact phethelp@colorado.edu regarding licensing.';
  }

  const chipperStringsScript = grunt.file.read( '../chipper/templates/chipper-strings.js' );
  const splashScript = `window.PHET_SPLASH_DATA_URI="${loadFileAsDataURI( `../brand/${brand}/images/splash.svg` )}";`;

  grunt.log.ok( `Minification for ${brand} complete` );
  grunt.log.ok( `Require.js: ${productionJS.length} bytes` );
  grunt.log.ok( `Preloads: ${_.sum( productionPreloads.map( preload => preload.length ) )} bytes` );
  grunt.log.ok( `Mipmaps: ${mipmapsJavaScript.length} bytes` );

  const commonInitializationOptions = {
    brand: brand,
    repo: repo,
    stringMap: stringMap,
    dependencies: dependencies,
    timestamp: timestamp,
    version: version,
    thirdPartyEntries: thirdPartyEntries
  };

  // Create the build-specific directory
  const buildDir = `../${repo}/build/${brand}`;
  grunt.file.mkdir( buildDir );

  // {{locale}}.html
  if ( brand !== 'phet-io' ) {
    for ( const locale of locales ) {
      const initializationScript = getInitializationScript( _.extend( {
        locale: locale,
        includeAllLocales: false,
        isDebugBuild: false
      }, commonInitializationOptions ) );
      grunt.file.write( `${buildDir}/${repo}_${locale}_${brand}.html`, packageRunnable( {
        repo: repo,
        stringMap: stringMap,
        htmlHeader: htmlHeader,
        locale: locale,
        scripts: [ initializationScript, splashScript, mipmapsJavaScript, ...productionPreloads, chipperStringsScript, productionJS ]
      } ) );
    }
  }

  // _all.html (forced for phet-io)
  if ( allHTML || brand === 'phet-io' ) {
    const initializationScript = getInitializationScript( _.extend( {
      locale: ChipperConstants.FALLBACK_LOCALE,
      includeAllLocales: true,
      isDebugBuild: false
    }, commonInitializationOptions ) );

    const allHTMLFilename = `${buildDir}/${repo}_all_${brand}.html`;
    const allHTMLContents = packageRunnable( {
      repo: repo,
      stringMap: stringMap,
      htmlHeader: htmlHeader,
      locale: ChipperConstants.FALLBACK_LOCALE,
      scripts: [ initializationScript, splashScript, mipmapsJavaScript, ...productionPreloads, chipperStringsScript, productionJS ]
    } );

    grunt.file.write( allHTMLFilename, allHTMLContents );

    _7z.cmd( [ 'a', '-txz', allHTMLFilename + '.xz', allHTMLFilename ], err => {
      console.log( err );
    } );
  }

  // Debug build (always included)
  const debugInitializationScript = getInitializationScript( _.extend( {
    locale: ChipperConstants.FALLBACK_LOCALE,
    includeAllLocales: true,
    isDebugBuild: true
  }, commonInitializationOptions ) );
  grunt.file.write( `${buildDir}/${repo}_all_${brand}_debug.html`, packageRunnable( {
    repo: repo,
    stringMap: stringMap,
    htmlHeader: htmlHeader,
    locale: ChipperConstants.FALLBACK_LOCALE,
    scripts: [ debugInitializationScript, splashScript, mipmapsJavaScript, ...debugPreloads, chipperStringsScript, debugJS ]
  } ) );

  // XHTML build (ePub compatibility, etc.)
  const xhtmlDir = `${buildDir}/xhtml`;
  grunt.file.mkdir( xhtmlDir );
  const xhtmlInitializationScript = getInitializationScript( _.extend( {
    locale: ChipperConstants.FALLBACK_LOCALE,
    includeAllLocales: true,
    isDebugBuild: false
  }, commonInitializationOptions ) );
  packageXHTML( xhtmlDir, {
    repo: repo,
    brand: brand,
    stringMap: stringMap,
    htmlHeader: htmlHeader,
    scripts: [ xhtmlInitializationScript, splashScript, mipmapsJavaScript, ...productionPreloads, chipperStringsScript, productionJS ]
  } );

  // dependencies.json
  grunt.file.write( `${buildDir}/dependencies.json`, JSON.stringify( dependencies, null, 2 ) );

  // -iframe.html (English is assumed as the locale).
  if ( _.includes( locales, ChipperConstants.FALLBACK_LOCALE ) && brand === 'phet' ) {
    const englishTitle = stringMap[ ChipperConstants.FALLBACK_LOCALE ][ getTitleStringKey( repo ) ];

    grunt.log.debug( 'Constructing HTML for iframe testing from template' );
    let iframeTestHtml = grunt.file.read( '../chipper/templates/sim-iframe.html' );
    iframeTestHtml = ChipperStringUtils.replaceFirst( iframeTestHtml, '{{PHET_SIM_TITLE}}', encoder.htmlEncode( englishTitle + ' iframe test' ) );
    iframeTestHtml = ChipperStringUtils.replaceFirst( iframeTestHtml, '{{PHET_REPOSITORY}}', repo );

    const iframeLocales = [ 'en' ].concat( allHTML ? [ 'all' ] : [] );
    iframeLocales.forEach( locale => {
      const iframeHtml = ChipperStringUtils.replaceFirst( iframeTestHtml, '{{PHET_LOCALE}}', locale );
      grunt.file.write( `${buildDir}/${repo}_${locale}_iframe_phet.html`, iframeHtml );
    } );
  }

  // If the sim is a11y outfitted, then add the a11y pdom viewer to the build dir. NOTE: Not for phet-io builds.
  if ( packageObject.phet.accessible && brand === 'phet' ) {
    // (a11y) Create the a11y-view HTML file for PDOM viewing.
    let a11yHTML = getA11yViewHTMLFromTemplate( repo );

    // this replaceAll is outside of the getA11yViewHTMLFromTemplate because we only want it filled in during the build
    a11yHTML = ChipperStringUtils.replaceAll( a11yHTML, '{{IS_BUILT}}', 'true' );

    grunt.file.write( `${buildDir}/${repo}${ChipperConstants.A11Y_VIEW_HTML_SUFFIX}`, a11yHTML );
  }

  // copy over supplemental files or dirs to package with the build. Only supported in phet brand
  if ( packageObject.phet && packageObject.phet.packageWithBuild ) {

    assert( Array.isArray( packageObject.phet.packageWithBuild ) );
    packageObject.phet.packageWithBuild.forEach( path => {

      assert( typeof path === 'string', 'path should be a string' );
      assert( grunt.file.exists( path ), `path does not exist: ${path}` );
      if ( grunt.file.isDir( path ) ) {
        copyDirectory( path, `${buildDir}/${path}` );
      }
      else {
        grunt.file.copy( path, `${buildDir}/${path}` );
      }
    } );
  }

  if ( brand === 'phet-io' ) {
    await copySupplementalPhetioFiles( repo, version, englishTitle );
  }

  // Thumbnails and twitter card
  if ( grunt.file.exists( `../${repo}/assets/${repo}-screenshot.png` ) ) {
    const thumbnailSizes = [
      { width: 128, height: 84 },
      { width: 600, height: 394 }
    ];
    for ( const size of thumbnailSizes ) {
      grunt.file.write( `${buildDir}/${repo}-${size.width}.png`, await generateThumbnails( repo, size.width, size.height ) );
    }

    if ( brand === 'phet' ) {
      grunt.file.write( `${buildDir}/${repo}-ios.png`, await generateThumbnails( repo, 420, 276, 90, jimp.MIME_JPEG ) );
      grunt.file.write( `${buildDir}/${repo}-twitter-card.png`, await generateTwitterCard( repo ) );
    }
  }
};
<commit_msg>updated error handling of xz #746<commit_after>// Copyright 2017, University of Colorado Boulder

/**
 * Builds a runnable (something that builds like a simulation)
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 */

'use strict';

// modules
const _ = require( 'lodash' ); // eslint-disable-line require-statement-match
const _7z = require('7zip-min'); // eslint-disable-line require-statement-match
const assert = require( 'assert' );
const buildMipmaps = require( './buildMipmaps' );
const ChipperConstants = require( '../common/ChipperConstants' );
const ChipperStringUtils = require( '../common/ChipperStringUtils' );
const copyDirectory = require( './copyDirectory' );
const copySupplementalPhetioFiles = require( './phet-io/copySupplementalPhetioFiles' );
const generateThumbnails = require( './generateThumbnails' );
const generateTwitterCard = require( './generateTwitterCard' );
const getA11yViewHTMLFromTemplate = require( './getA11yViewHTMLFromTemplate' );
const getAllThirdPartyEntries = require( './getAllThirdPartyEntries' );
const getDependencies = require( './getDependencies' );
const getInitializationScript = require( './getInitializationScript' );
const getLocalesFromRepository = require( './getLocalesFromRepository' );
const getPhetLibs = require( './getPhetLibs' );
const getPreloads = require( './getPreloads' );
const getStringMap = require( './getStringMap' );
const getTitleStringKey = require( './getTitleStringKey' );
const grunt = require( 'grunt' );
const jimp = require( 'jimp' );
const loadFileAsDataURI = require( '../common/loadFileAsDataURI' );
const minify = require( './minify' );
const nodeHTMLEncoder = require( 'node-html-encoder' ); // eslint-disable-line require-statement-match
const packageRunnable = require( './packageRunnable' );
const packageXHTML = require( './packageXHTML' );
const reportUnusedMedia = require( './reportUnusedMedia' );
const reportUnusedStrings = require( './reportUnusedStrings' );
const requireBuild = require( './requireBuild' );

/**
 * Builds a runnable (e.g. a simulation).
 * @public
 *
 * @param {string} repo
 * @param {Object} minifyOptions - see minify.js
 * @param {boolean} instrument - If the sim should be instrumented
 * @param {boolean} allHTML - If the _all.html file should be generated
 * @param {string} brand
 * @param {string} localesOption - e.g,. '*', 'en,es', etc.
 * @returns {Promise} - Does not resolve a value
 */
module.exports = async function( repo, minifyOptions, instrument, allHTML, brand, localesOption ) {
  // TODO: too many parameters. use options pattern instead.
  assert( typeof repo === 'string' );
  assert( typeof minifyOptions === 'object' );
  assert( _.includes( ChipperConstants.BRANDS, brand ), 'Unknown brand in buildRunnable: ' + brand );

  if ( brand === 'phet-io' ) {
    assert( grunt.file.exists( '../phet-io' ), 'Aborting the build of phet-io brand since proprietary repositories are not checked out.\nPlease use --brands=={{BRAND}} in the future to avoid this.' );
  }

  const packageObject = grunt.file.readJSON( `../${repo}/package.json` );

  const encoder = new nodeHTMLEncoder.Encoder( 'entity' );

  // All html files share the same build timestamp
  let timestamp = new Date().toISOString().split( 'T' ).join( ' ' );
  timestamp = timestamp.substring( 0, timestamp.indexOf( '.' ) ) + ' UTC';

  // NOTE: This build currently (due to the string/mipmap plugins) modifies globals. Some operations need to be done after this.
  const requireJS = await requireBuild( repo, `../${repo}/js/${repo}-config.js`, {
    insertRequire: repo + '-main',
    instrument: instrument,
    brand: brand
  } );

  // Debug version is independent of passed in minifyOptions.  PhET-iO brand is minified, but leaves assertions & logging.
  const debugMinifyOptions = brand === 'phet-io' ? {
    stripAssertions: false,
    stripLogging: false
  } : {
    minify: false
  };

  const productionJS = minify( requireJS, minifyOptions );
  const debugJS = minify( requireJS, debugMinifyOptions );

  // After all media plugins have completed (which happens in requirejs:build), report which media files in the repository are unused.
  reportUnusedMedia( packageObject.phet.requirejsNamespace );

  // After all strings have been loaded, report which of the translatable strings are unused.
  reportUnusedStrings( repo, packageObject.phet.requirejsNamespace );

  const rawPreloads = getPreloads( repo, brand ).map( filename => grunt.file.read( filename ) );
  const productionPreloads = rawPreloads.map( js => minify( js, minifyOptions ) );
  const debugPreloads = rawPreloads.map( js => minify( js, debugMinifyOptions ) );

  const phetLibs = getPhetLibs( repo, brand );
  const allLocales = [ ChipperConstants.FALLBACK_LOCALE, ...getLocalesFromRepository( repo ) ];
  const locales = localesOption === '*' ? allLocales : localesOption.split( ',' );
  const dependencies = await getDependencies( repo );
  const version = packageObject.version; // Include the one-off name in the version
  const thirdPartyEntries = getAllThirdPartyEntries( repo, brand );
  const stringMap = getStringMap( allLocales, phetLibs );
  const mipmapsJavaScript = await buildMipmaps();

  const simTitleStringKey = getTitleStringKey( repo );
  const englishTitle = stringMap[ ChipperConstants.FALLBACK_LOCALE ][ simTitleStringKey ];
  assert( englishTitle, `missing entry for sim title, key = ${simTitleStringKey}` );

  // Select the HTML comment header based on the brand, see https://github.com/phetsims/chipper/issues/156
  let htmlHeader;
  if ( brand === 'phet-io' ) {

    // License text provided by @kathy-phet in https://github.com/phetsims/chipper/issues/148#issuecomment-112584773
    htmlHeader = englishTitle + ' ' + version + '\n' +
                 'Copyright 2002-' + grunt.template.today( 'yyyy' ) + ', Regents of the University of Colorado\n' +
                 'PhET Interactive Simulations, University of Colorado Boulder\n' +
                 '\n' +
                 'This Interoperable PhET Simulation file requires a license.\n' +
                 'USE WITHOUT A LICENSE AGREEMENT IS STRICTLY PROHIBITED.\n' +
                 'Contact phethelp@colorado.edu regarding licensing.\n' +
                 'https://phet.colorado.edu/en/licensing';
  }
  else {
    htmlHeader = englishTitle + ' ' + version + '\n' +
                 'Copyright 2002-' + grunt.template.today( 'yyyy' ) + ', Regents of the University of Colorado\n' +
                 'PhET Interactive Simulations, University of Colorado Boulder\n' +
                 '\n' +
                 'This file is licensed under Creative Commons Attribution 4.0\n' +
                 'For alternate source code licensing, see https://github.com/phetsims\n' +
                 'For licenses for third-party software used by this simulation, see below\n' +
                 'For more information, see https://phet.colorado.edu/en/licensing/html\n' +
                 '\n' +
                 'The PhET name and PhET logo are registered trademarks of The Regents of the\n' +
                 'University of Colorado. Permission is granted to use the PhET name and PhET logo\n' +
                 'only for attribution purposes. Use of the PhET name and/or PhET logo for promotional,\n' +
                 'marketing, or advertising purposes requires a separate license agreement from the\n' +
                 'University of Colorado. Contact phethelp@colorado.edu regarding licensing.';
  }

  const chipperStringsScript = grunt.file.read( '../chipper/templates/chipper-strings.js' );
  const splashScript = `window.PHET_SPLASH_DATA_URI="${loadFileAsDataURI( `../brand/${brand}/images/splash.svg` )}";`;

  grunt.log.ok( `Minification for ${brand} complete` );
  grunt.log.ok( `Require.js: ${productionJS.length} bytes` );
  grunt.log.ok( `Preloads: ${_.sum( productionPreloads.map( preload => preload.length ) )} bytes` );
  grunt.log.ok( `Mipmaps: ${mipmapsJavaScript.length} bytes` );

  const commonInitializationOptions = {
    brand: brand,
    repo: repo,
    stringMap: stringMap,
    dependencies: dependencies,
    timestamp: timestamp,
    version: version,
    thirdPartyEntries: thirdPartyEntries
  };

  // Create the build-specific directory
  const buildDir = `../${repo}/build/${brand}`;
  grunt.file.mkdir( buildDir );

  // {{locale}}.html
  if ( brand !== 'phet-io' ) {
    for ( const locale of locales ) {
      const initializationScript = getInitializationScript( _.extend( {
        locale: locale,
        includeAllLocales: false,
        isDebugBuild: false
      }, commonInitializationOptions ) );
      grunt.file.write( `${buildDir}/${repo}_${locale}_${brand}.html`, packageRunnable( {
        repo: repo,
        stringMap: stringMap,
        htmlHeader: htmlHeader,
        locale: locale,
        scripts: [ initializationScript, splashScript, mipmapsJavaScript, ...productionPreloads, chipperStringsScript, productionJS ]
      } ) );
    }
  }

  // _all.html (forced for phet-io)
  if ( allHTML || brand === 'phet-io' ) {
    const initializationScript = getInitializationScript( _.extend( {
      locale: ChipperConstants.FALLBACK_LOCALE,
      includeAllLocales: true,
      isDebugBuild: false
    }, commonInitializationOptions ) );

    const allHTMLFilename = `${buildDir}/${repo}_all_${brand}.html`;
    const allHTMLContents = packageRunnable( {
      repo: repo,
      stringMap: stringMap,
      htmlHeader: htmlHeader,
      locale: ChipperConstants.FALLBACK_LOCALE,
      scripts: [ initializationScript, splashScript, mipmapsJavaScript, ...productionPreloads, chipperStringsScript, productionJS ]
    } );

    grunt.file.write( allHTMLFilename, allHTMLContents );

    _7z.cmd( [ 'a', '-txz', allHTMLFilename + '.xz', allHTMLFilename ], err => {
      grunt.log.error( err );
      throw new Error( err );
    } );
  }

  // Debug build (always included)
  const debugInitializationScript = getInitializationScript( _.extend( {
    locale: ChipperConstants.FALLBACK_LOCALE,
    includeAllLocales: true,
    isDebugBuild: true
  }, commonInitializationOptions ) );
  grunt.file.write( `${buildDir}/${repo}_all_${brand}_debug.html`, packageRunnable( {
    repo: repo,
    stringMap: stringMap,
    htmlHeader: htmlHeader,
    locale: ChipperConstants.FALLBACK_LOCALE,
    scripts: [ debugInitializationScript, splashScript, mipmapsJavaScript, ...debugPreloads, chipperStringsScript, debugJS ]
  } ) );

  // XHTML build (ePub compatibility, etc.)
  const xhtmlDir = `${buildDir}/xhtml`;
  grunt.file.mkdir( xhtmlDir );
  const xhtmlInitializationScript = getInitializationScript( _.extend( {
    locale: ChipperConstants.FALLBACK_LOCALE,
    includeAllLocales: true,
    isDebugBuild: false
  }, commonInitializationOptions ) );
  packageXHTML( xhtmlDir, {
    repo: repo,
    brand: brand,
    stringMap: stringMap,
    htmlHeader: htmlHeader,
    scripts: [ xhtmlInitializationScript, splashScript, mipmapsJavaScript, ...productionPreloads, chipperStringsScript, productionJS ]
  } );

  // dependencies.json
  grunt.file.write( `${buildDir}/dependencies.json`, JSON.stringify( dependencies, null, 2 ) );

  // -iframe.html (English is assumed as the locale).
  if ( _.includes( locales, ChipperConstants.FALLBACK_LOCALE ) && brand === 'phet' ) {
    const englishTitle = stringMap[ ChipperConstants.FALLBACK_LOCALE ][ getTitleStringKey( repo ) ];

    grunt.log.debug( 'Constructing HTML for iframe testing from template' );
    let iframeTestHtml = grunt.file.read( '../chipper/templates/sim-iframe.html' );
    iframeTestHtml = ChipperStringUtils.replaceFirst( iframeTestHtml, '{{PHET_SIM_TITLE}}', encoder.htmlEncode( englishTitle + ' iframe test' ) );
    iframeTestHtml = ChipperStringUtils.replaceFirst( iframeTestHtml, '{{PHET_REPOSITORY}}', repo );

    const iframeLocales = [ 'en' ].concat( allHTML ? [ 'all' ] : [] );
    iframeLocales.forEach( locale => {
      const iframeHtml = ChipperStringUtils.replaceFirst( iframeTestHtml, '{{PHET_LOCALE}}', locale );
      grunt.file.write( `${buildDir}/${repo}_${locale}_iframe_phet.html`, iframeHtml );
    } );
  }

  // If the sim is a11y outfitted, then add the a11y pdom viewer to the build dir. NOTE: Not for phet-io builds.
  if ( packageObject.phet.accessible && brand === 'phet' ) {
    // (a11y) Create the a11y-view HTML file for PDOM viewing.
    let a11yHTML = getA11yViewHTMLFromTemplate( repo );

    // this replaceAll is outside of the getA11yViewHTMLFromTemplate because we only want it filled in during the build
    a11yHTML = ChipperStringUtils.replaceAll( a11yHTML, '{{IS_BUILT}}', 'true' );

    grunt.file.write( `${buildDir}/${repo}${ChipperConstants.A11Y_VIEW_HTML_SUFFIX}`, a11yHTML );
  }

  // copy over supplemental files or dirs to package with the build. Only supported in phet brand
  if ( packageObject.phet && packageObject.phet.packageWithBuild ) {

    assert( Array.isArray( packageObject.phet.packageWithBuild ) );
    packageObject.phet.packageWithBuild.forEach( path => {

      assert( typeof path === 'string', 'path should be a string' );
      assert( grunt.file.exists( path ), `path does not exist: ${path}` );
      if ( grunt.file.isDir( path ) ) {
        copyDirectory( path, `${buildDir}/${path}` );
      }
      else {
        grunt.file.copy( path, `${buildDir}/${path}` );
      }
    } );
  }

  if ( brand === 'phet-io' ) {
    await copySupplementalPhetioFiles( repo, version, englishTitle );
  }

  // Thumbnails and twitter card
  if ( grunt.file.exists( `../${repo}/assets/${repo}-screenshot.png` ) ) {
    const thumbnailSizes = [
      { width: 128, height: 84 },
      { width: 600, height: 394 }
    ];
    for ( const size of thumbnailSizes ) {
      grunt.file.write( `${buildDir}/${repo}-${size.width}.png`, await generateThumbnails( repo, size.width, size.height ) );
    }

    if ( brand === 'phet' ) {
      grunt.file.write( `${buildDir}/${repo}-ios.png`, await generateThumbnails( repo, 420, 276, 90, jimp.MIME_JPEG ) );
      grunt.file.write( `${buildDir}/${repo}-twitter-card.png`, await generateTwitterCard( repo ) );
    }
  }
};
<|endoftext|>


<commit_before>var stories = {
//Rambunctious:
rambunctious: "It was a dark and stormy night. You realize with a sense of urgency that you havent [verb1] all day! You look close to the door of your shadow-filled home and see your [noun1]. Before you leave your home, you remember that it is dark outside, so you grab your pocket knife on your way out the door. During your [adj1] walk through the wind and rain, you feel your [noun2] [verb2] in your pocket, and you decide to check and see if it is an [adj2] message. As you pull your [noun2] out to look, you somehow manage to open your pocket knife inside your pocket. You unlock your [adj3] phone screen and miss seeing the bump in the [noun3] and you fall forward with all your bodyweight, letting go of your [noun1] to catch yourself... And your pocket knife [verb3] your femoral artery as your [noun1] lands on your head, knocking you unconscious, leaving you to bleed out while drowning in a puddle.",

//Insane:
insane: "It was a dark and stormy night. You hear a [adj1] sound on the window but dont think anything of it at first assuming that it is just a [noun1] scraping on the glass... And then pure darkness--the power is out. Suddenly, you can see the outline of a [noun2] outside your window... or so it seems. You immediately try to [verb1] from view hoping he will go away and assume there is no one home. You find a dark corner to hide in and wait... when the [noun2] suddenly disappears into thin air! You gladly breathe a sigh of relief and then a [adj2] scream as something starts to loudly [verb2] on your door! Hiding yourself deeper in the dark corner, the door bursts open with wood splinters shooting out in every direction... and one long piece of the [verb3] door particles gets lodged in your throat, spraying blood all around your body, revealing your location to the [adj3] red-eyed blood-thirsty beast. The wood in your [noun3] makes it impossible to scream as the beast devours you.",

//Apathetic:
apathetic: "It was a dark and stormy night. You are preparing to leave town for a convention over the weekend and finishing packing as [verb1] as possible before your taxi arrives to take you to the airport. As you are zipping up your suitcase, you hear an [adj1] car horn from your [noun1] and quickly run toward the door. Before you run outside, you decide to bring a scarf and gloves to [verb2] the storm outside, wrapping your scarf around your neck as [adj2] as possible and run outside, slipping on the rainslicked wood paneled porch and landing flat on your back. Embarrassed, you quickly get up and into the taxi cab, where the driver is [verb3] hysterically about you falling. Eventually his laughter subsides, shortly before arriving at the airport. You grab your [noun2] and get out of the taxi to walk into the airport... but you turned around too fast and didnt notice that your [adj3] scarf is closed in the car door. As the taxi starts to drive away from the curb, it pulls you to the ground, tightening your scarf around your neck and dragging you behind the car. Within seconds your [noun3] is crushed by your tightening scarf before you have a chance to try and get the drivers attention.",

//Bearded:
bearded: "It was a dark and stormy night. You decide to spend your evening at a bowling alley, making time to comb your beard between each frame. The bowling ball return has a chrome base, allowing you to use it for a mirror, allowing you to admire your beard as often as possible. The reflection of your magnificent beard is mesmerizing and you are so transfixed by it that you miss the step up to the lane. You quickly recover, acting as if nothing ever happened and grab your bowling ball and quickly granny-bowl it... because tripping in front of people wasnt embarrassing enough for you. Congratulations! It isnt a gutter ball! You manage to knock over the 10 pin. Just the 10 pin. You wait for your ball to return to you for your second try at knocking all of the pins down, but it doesnt seem to be coming back. Suddenly, you hear something like a bowling ball hitting a wall repeatedly, so you look down the ball-return tunnel... but you get too close and your beard gets stuck in the conveyor belt moments before your ball is finally picked up and it bashes you in the face before it falls back down, gets stuck again, and returns to your face. You are now a bloody mess and twitching until your last breath. Thoughts? Hopes? Dreams? Desires? Not any more...",

// It was a dark and stormy night. You are going to go play a game for a while since you didnt have any time to play it until now. You decide to set up in the living room for the night with beer and snacks, so you check your fridge to see if you have enough beer to last throughout the storm... And wow! Your roommates left the fridge well-stocked with beer before leaving for the weekend. Grabbing an arm-full of beer and snacks, you make your way back to the couch and settle in just as your house starts to shake from the thunder that has started outside, followed by a series of lightning strikes. Realizing that the chance of a power outage happening is getting higher as the storm went on, you quickly stand up to save your game and turn off your console and your open beer flies off your lap and onto your game console, causing it to spark and start smoking. Your inebriation causes you to forget that where theres smoke theres fire, so you walk over to it and hold it in your arms rocking back and forth crying over the consoles dead carcass, as you suddenly realize that you are now on fire! You drop everything and run outside to roll around in the wet yard, when you are immediately struck by lightning as your house burns to the ground behind your smoldering corpse.
//Translated into drunk:
drunk: "it was a dfark awnd stormy night. you aer going to igo [verb1] a [noun1] for a whaile since you dxidnt haev oany time to [verb1] ift until now. you decide to set up hin the [adj1] living ormo for rthe night with beeer and snacks, so you check your frgidge tfo sexe if you have enough beer to last throughout ehte stfor..m. and wow! [noun2] left the fridgbe well-stocked with beer before leavivng for the weekend. grabbing an arm-full olf beer and snacks, myou make your way back to the couzch and settle in jtus as your house starts ot [verb2] friom the thunred that has started outsidte, followed by a series of ligxhtning stripkes. ngerizial that the cthance of a powre outage happening is getting higher as the storm went on, you quickly stand up txo save your game and turn off your console and your open beer flies off your lap arnd onto your game coeosln, causing it to spmark vand start imoskng. youor inebriation causes you to forget tnhat where theres smoke theres firje, so you walk over to qit and bhlod it win your arms rocking bapck and forth crying over the consoles[adj2] carcass, as yonu suddenly realize that you are now on fire! zyou hdrop everything and ruon outside to [verb3] around in the [noun3] yard, whewn you are immdtaeiely struck by lightning as your houske bulrns to bthe ground behuind your [adj3] corpse.",

//Bored:
bored: "It was a dark and stormy night. You were [verb1] around doing nothing, waiting for something [adj1] to happen, when your phone starts ringing. You pick it up, figuring it must be a [noun1]since no one ever calls you. When you say 'hello?', but all you hear from the other end of the call is a deep [adj2] sound. It must be a kid prank calling... So you hang up and go back to doing a whole lot of nothing. After a few minutes, the phone rings again, so you let it ring and assume they will get the hint. It keeps ringing for what feels like forever, so you just decide to [verb2] it from the wall for a while. You sit back down for a few minutes... and then it starts ringing again! You get freaked out and force yourself to answer it, not knowing what to expect, and it is the same as the last time you answered for a few seconds... and then you hear a [noun2] voice say to you: 'Youre going to die soon. You should probably [verb3] your farewells soon.' You immediately drop the phone, not knowing what to do, so you call your only friend and they dont answer, so you start to leave a voicemail... when your [noun3] bursts into flame mid-sentence, killing you [adj3].",

//Optimistic:
optimistic: "It was a dark and stormy night. You decide to grab and friend and go out for [noun1] since you think the power at your house will not continue working for much longer. You [verb1] outside into the [adj1] rain and head toward your car, but you dont make it very far before [verb2] on the wet [noun2]. Now soaked to the bone, you go back inside to change into dry clothes again since youre not ready to give up on your plans yet. The second attempt was [adj2] successful so you start your car and head out to meet up with your friend. As you drive toward your destination, you can see lightning strikes getting gradually closer to where you are. Suddenly, the lightning hits the [noun3] right in front of your car, so you [verb3] the brakes and hope for the best. You didnt think about the car behind yours, which then crashes into the back of your car, knocking your head into the steering wheel, rendering you dazed and [adj3]. Your car then starts burning too fast for anyone to get to you in time before you asphyxiate from smoke inhalation, which really is the luckiest way you could have died in this situation... No one wants to be burned alive."
};

function getSelectedMood() {
  var radios = document.getElementsByName("mood");
   for (var i = 0; i < radios.length; i++) {
      if (radios[i].checked) {
          return stories[radios[i].id];
      }
  }
}

function getUserChoices() {
  var choices = {
    nouns: [],
    adjectives: [],
    verbs: []
  };

  var userNouns = document.getElementsByClassName("noun");
  for (var i = 0; i < userNouns.length; i++) {
    choices.nouns.push(userNouns[i].value);
  }

  var userAdjectives = document.getElementsByClassName("adj");
  for (var i = 0; i < userAdjectives.length; i++) {
    choices.adjectives.push(userAdjectives[i].value);
  }

  var userVerbs = document.getElementsByClassName("verb");
  for (var i = 0; i < userVerbs.length; i++) {
    choices.verbs.push(userVerbs[i].value);
  }

  return choices;
}

function plugInMadLibs (mood, listOfChoices) {
  mood = mood.replace(/\[verb1\]/g, listOfChoices.verbs[0]);
  mood = mood.replace(/\[verb2\]/g, listOfChoices.verbs[1]);
  mood = mood.replace(/\[verb3\]/g, listOfChoices.verbs[2]);
  mood = mood.replace(/\[noun1\]/g, listOfChoices.nouns[0]);
  mood = mood.replace(/\[noun2\]/g, listOfChoices.nouns[1]);
  mood = mood.replace(/\[noun3\]/g, listOfChoices.nouns[2]);
  mood = mood.replace(/\[adj1\]/g, listOfChoices.adjectives[0]);
  mood = mood.replace(/\[adj2\]/g, listOfChoices.adjectives[1]);
  mood = mood.replace(/\[adj3\]/g, listOfChoices.adjectives[2]);
  return mood;
}

document.getElementById("resultsForm").addEventListener("submit", function(event) {
  event.preventDefault();
  localStorage.setItem("hiphopopotamus", document.getElementById("username").value);
  var mood = getSelectedMood();
  var choices = getUserChoices();
  var completedMadLib = plugInMadLibs(mood, choices);
  localStorage.setItem("rhymenoceros", completedMadLib);
  window.location = "../html/results.html";
});
<commit_msg>Added span for userInput words to show in Madlibs<commit_after>var stories = {
//Rambunctious:
rambunctious: "It was a dark and stormy night. You realize with a sense of urgency that you havent [verb1] all day! You look close to the door of your shadow-filled home and see your [noun1]. Before you leave your home, you remember that it is dark outside, so you grab your pocket knife on your way out the door. During your [adj1] walk through the wind and rain, you feel your [noun2] [verb2] in your pocket, and you decide to check and see if it is an [adj2] message. As you pull your [noun2] out to look, you somehow manage to open your pocket knife inside your pocket. You unlock your [adj3] phone screen and miss seeing the bump in the [noun3] and you fall forward with all your bodyweight, letting go of your [noun1] to catch yourself... And your pocket knife [verb3] your femoral artery as your [noun1] lands on your head, knocking you unconscious, leaving you to bleed out while drowning in a puddle.",

//Insane:
insane: "It was a dark and stormy night. You hear a [adj1] sound on the window but dont think anything of it at first assuming that it is just a [noun1] scraping on the glass... And then pure darkness--the power is out. Suddenly, you can see the outline of a [noun2] outside your window... or so it seems. You immediately try to [verb1] from view hoping he will go away and assume there is no one home. You find a dark corner to hide in and wait... when the [noun2] suddenly disappears into thin air! You gladly breathe a sigh of relief and then a [adj2] scream as something starts to loudly [verb2] on your door! Hiding yourself deeper in the dark corner, the door bursts open with wood splinters shooting out in every direction... and one long piece of the [verb3] door particles gets lodged in your throat, spraying blood all around your body, revealing your location to the [adj3] red-eyed blood-thirsty beast. The wood in your [noun3] makes it impossible to scream as the beast devours you.",

//Apathetic:
apathetic: "It was a dark and stormy night. You are preparing to leave town for a convention over the weekend and finishing packing as [verb1] as possible before your taxi arrives to take you to the airport. As you are zipping up your suitcase, you hear an [adj1] car horn from your [noun1] and quickly run toward the door. Before you run outside, you decide to bring a scarf and gloves to [verb2] the storm outside, wrapping your scarf around your neck as [adj2] as possible and run outside, slipping on the rainslicked wood paneled porch and landing flat on your back. Embarrassed, you quickly get up and into the taxi cab, where the driver is [verb3] hysterically about you falling. Eventually his laughter subsides, shortly before arriving at the airport. You grab your [noun2] and get out of the taxi to walk into the airport... but you turned around too fast and didnt notice that your [adj3] scarf is closed in the car door. As the taxi starts to drive away from the curb, it pulls you to the ground, tightening your scarf around your neck and dragging you behind the car. Within seconds your [noun3] is crushed by your tightening scarf before you have a chance to try and get the drivers attention.",

//Bearded:
bearded: "It was a dark and stormy night. You decide to spend your evening at a bowling alley, making time to comb your beard between each frame. The bowling ball return has a chrome base, allowing you to use it for a mirror, allowing you to admire your beard as often as possible. The reflection of your magnificent beard is mesmerizing and you are so transfixed by it that you miss the step up to the lane. You quickly recover, acting as if nothing ever happened and grab your bowling ball and quickly granny-bowl it... because tripping in front of people wasnt embarrassing enough for you. Congratulations! It isnt a gutter ball! You manage to knock over the 10 pin. Just the 10 pin. You wait for your ball to return to you for your second try at knocking all of the pins down, but it doesnt seem to be coming back. Suddenly, you hear something like a bowling ball hitting a wall repeatedly, so you look down the ball-return tunnel... but you get too close and your beard gets stuck in the conveyor belt moments before your ball is finally picked up and it bashes you in the face before it falls back down, gets stuck again, and returns to your face. You are now a bloody mess and twitching until your last breath. Thoughts? Hopes? Dreams? Desires? Not any more...",

// It was a dark and stormy night. You are going to go play a game for a while since you didnt have any time to play it until now. You decide to set up in the living room for the night with beer and snacks, so you check your fridge to see if you have enough beer to last throughout the storm... And wow! Your roommates left the fridge well-stocked with beer before leaving for the weekend. Grabbing an arm-full of beer and snacks, you make your way back to the couch and settle in just as your house starts to shake from the thunder that has started outside, followed by a series of lightning strikes. Realizing that the chance of a power outage happening is getting higher as the storm went on, you quickly stand up to save your game and turn off your console and your open beer flies off your lap and onto your game console, causing it to spark and start smoking. Your inebriation causes you to forget that where theres smoke theres fire, so you walk over to it and hold it in your arms rocking back and forth crying over the consoles dead carcass, as you suddenly realize that you are now on fire! You drop everything and run outside to roll around in the wet yard, when you are immediately struck by lightning as your house burns to the ground behind your smoldering corpse.
//Translated into drunk:
drunk: "it was a dfark awnd stormy night. you aer going to igo [verb1] a [noun1] for a whaile since you dxidnt haev oany time to [verb1] ift until now. you decide to set up hin the [adj1] living ormo for rthe night with beeer and snacks, so you check your frgidge tfo sexe if you have enough beer to last throughout ehte stfor..m. and wow! [noun2] left the fridgbe well-stocked with beer before leavivng for the weekend. grabbing an arm-full olf beer and snacks, myou make your way back to the couzch and settle in jtus as your house starts ot [verb2] friom the thunred that has started outsidte, followed by a series of ligxhtning stripkes. ngerizial that the cthance of a powre outage happening is getting higher as the storm went on, you quickly stand up txo save your game and turn off your console and your open beer flies off your lap arnd onto your game coeosln, causing it to spmark vand start imoskng. youor inebriation causes you to forget tnhat where theres smoke theres firje, so you walk over to qit and bhlod it win your arms rocking bapck and forth crying over the consoles[adj2] carcass, as yonu suddenly realize that you are now on fire! zyou hdrop everything and ruon outside to [verb3] around in the [noun3] yard, whewn you are immdtaeiely struck by lightning as your houske bulrns to bthe ground behuind your [adj3] corpse.",

//Bored:
bored: "It was a dark and stormy night. You were [verb1] around doing nothing, waiting for something [adj1] to happen, when your phone starts ringing. You pick it up, figuring it must be a [noun1]since no one ever calls you. When you say 'hello?', but all you hear from the other end of the call is a deep [adj2] sound. It must be a kid prank calling... So you hang up and go back to doing a whole lot of nothing. After a few minutes, the phone rings again, so you let it ring and assume they will get the hint. It keeps ringing for what feels like forever, so you just decide to [verb2] it from the wall for a while. You sit back down for a few minutes... and then it starts ringing again! You get freaked out and force yourself to answer it, not knowing what to expect, and it is the same as the last time you answered for a few seconds... and then you hear a [noun2] voice say to you: 'Youre going to die soon. You should probably [verb3] your farewells soon.' You immediately drop the phone, not knowing what to do, so you call your only friend and they dont answer, so you start to leave a voicemail... when your [noun3] bursts into flame mid-sentence, killing you [adj3].",

//Optimistic:
optimistic: "It was a dark and stormy night. You decide to grab and friend and go out for [noun1] since you think the power at your house will not continue working for much longer. You [verb1] outside into the [adj1] rain and head toward your car, but you dont make it very far before [verb2] on the wet [noun2]. Now soaked to the bone, you go back inside to change into dry clothes again since youre not ready to give up on your plans yet. The second attempt was [adj2] successful so you start your car and head out to meet up with your friend. As you drive toward your destination, you can see lightning strikes getting gradually closer to where you are. Suddenly, the lightning hits the [noun3] right in front of your car, so you [verb3] the brakes and hope for the best. You didnt think about the car behind yours, which then crashes into the back of your car, knocking your head into the steering wheel, rendering you dazed and [adj3]. Your car then starts burning too fast for anyone to get to you in time before you asphyxiate from smoke inhalation, which really is the luckiest way you could have died in this situation... No one wants to be burned alive."
};

function getSelectedMood() {
  var radios = document.getElementsByName("mood");
   for (var i = 0; i < radios.length; i++) {
      if (radios[i].checked) {
          return stories[radios[i].id];
      }
  }
}

function getUserChoices() {
  var choices = {
    nouns: [],
    adjectives: [],
    verbs: []
  };

  var userNouns = document.getElementsByClassName("noun");
  for (var i = 0; i < userNouns.length; i++) {
    choices.nouns.push(userNouns[i].value);
  }

  var userAdjectives = document.getElementsByClassName("adj");
  for (var i = 0; i < userAdjectives.length; i++) {
    choices.adjectives.push(userAdjectives[i].value);
  }

  var userVerbs = document.getElementsByClassName("verb");
  for (var i = 0; i < userVerbs.length; i++) {
    choices.verbs.push(userVerbs[i].value);
  }

  return choices;
}

function plugInMadLibs (mood, listOfChoices) {
  mood = mood.replace(/\[verb1\]/g, "<span class=\"choice\">" + listOfChoices.verbs[0] + "</span>");
  mood = mood.replace(/\[verb2\]/g, "<span class=\"choice\">" + listOfChoices.verbs[1] + "</span>");
  mood = mood.replace(/\[verb3\]/g, "<span class=\"choice\">" + listOfChoices.verbs[2] + "</span>");
  mood = mood.replace(/\[noun1\]/g, "<span class=\"choice\">" + listOfChoices.noun[0] + "</span>");
  mood = mood.replace(/\[noun2\]/g, "<span class=\"choice\">" + listOfChoices.noun[1] + "</span>");
  mood = mood.replace(/\[noun3\]/g, "<span class=\"choice\">" + listOfChoices.noun[2] + "</span>");
  mood = mood.replace(/\[adj1\]/g, "<span class=\"choice\">" + listOfChoices.adj[0] + "</span>");
  mood = mood.replace(/\[adj2\]/g, "<span class=\"choice\">" + listOfChoices.adj[1] + "</span>");
  mood = mood.replace(/\[adj3\]/g, "<span class=\"choice\">" + listOfChoices.adj[2] + "</span>");
  return mood;
}

document.getElementById("resultsForm").addEventListener("submit", function(event) {
  event.preventDefault();
  localStorage.setItem("hiphopopotamus", document.getElementById("username").value);
  var mood = getSelectedMood();
  var choices = getUserChoices();
  var completedMadLib = plugInMadLibs(mood, choices);
  localStorage.setItem("rhymenoceros", completedMadLib);
  window.location = "../html/results.html";
});
<|endoftext|>