<commit_before>import { blank, keys } from './utils/object';
import { sequence } from './utils/promise';
import getLocation from './utils/getLocation';
import walk from './ast/walk';
import Scope from './ast/Scope';

function isIife ( node, parent ) {
	return parent && parent.type === 'CallExpression' && node === parent.callee;
}

export default class Statement {
	constructor ( node, magicString, module, index ) {
		this.node = node;
		this.module = module;
		this.magicString = magicString;
		this.index = index;
		this.id = module.id + '#' + index;

		this.scope = new Scope();
		this.defines = blank();
		this.modifies = blank();
		this.dependsOn = blank();
		this.stronglyDependsOn = blank();

		this.isIncluded = false;

		this.leadingComments = [];
		this.trailingComment = null;
		this.margin = [ 0, 0 ];

		// some facts about this statement...
		this.isImportDeclaration = node.type === 'ImportDeclaration';
		this.isExportDeclaration = /^Export/.test( node.type );
		this.isExportAllDeclaration = /^ExportAll/.test( node.type );
	}

	analyse () {
		if ( this.isImportDeclaration ) return; // nothing to analyse

		const statement = this; // TODO use arrow functions instead
		const magicString = this.magicString;

		let scope = this.scope;

		walk( this.node, {
			enter ( node, parent ) {
				let newScope;

				magicString.addSourcemapLocation( node.start );

				switch ( node.type ) {
					case 'FunctionExpression':
					case 'FunctionDeclaration':
					case 'ArrowFunctionExpression':
						if ( node.type === 'FunctionDeclaration' ) {
							scope.addDeclaration( node.id.name, node );
						}

						newScope = new Scope({
							parent: scope,
							params: node.params, // TODO rest params?
							block: false
						});

						// named function expressions - the name is considered
						// part of the function's scope
						if ( node.type === 'FunctionExpression' && node.id ) {
							newScope.addDeclaration( node.id.name, node );
						}

						break;

					case 'BlockStatement':
						if ( !/Function/.test( parent.type ) ) {
							newScope = new Scope({
								parent: scope,
								block: true
							});
						}

						break;

					case 'CatchClause':
						newScope = new Scope({
							parent: scope,
							params: [ node.param ],
							block: true
						});

						break;

					case 'VariableDeclaration':
						node.declarations.forEach( declarator => {
							scope.addDeclaration( declarator.id.name, node );
						});
						break;

					case 'ClassDeclaration':
						scope.addDeclaration( node.id.name, node );
						break;
				}

				if ( newScope ) {
					Object.defineProperty( node, '_scope', { value: newScope });
					scope = newScope;
				}
			},
			leave ( node ) {
				if ( node._scope ) {
					scope = scope.parent;
				}
			}
		});

		// This allows us to track whether we're looking at code that will
		// be executed immediately (either outside a function, or immediately
		// inside an IIFE), for the purposes of determining whether dependencies
		// are strong or weak. It's not bulletproof, since it wouldn't catch...
		//
		//    var calledImmediately = function () {
		//      doSomethingWith( strongDependency );
		//    }
		//    calledImmediately();
		//
		// ...but it's better than nothing
		let depth = 0;

		if ( !this.isImportDeclaration ) {
			walk( this.node, {
				enter: ( node, parent ) => {
					if ( node._scope ) {
						if ( !scope.isBlockScope && !isIife( node, parent ) ) depth += 1;
						scope = node._scope;
					}

					this.checkForReads( scope, node, parent, !depth );
					this.checkForWrites( scope, node, !depth );
				},
				leave: ( node, parent ) => {
					if ( node._scope ) {
						if ( !scope.isBlockScope && !isIife( node, parent ) ) depth -= 1;
						scope = scope.parent;
					}
				}
			});
		}

		keys( scope.declarations ).forEach( name => {
			statement.defines[ name ] = true;
		});
	}

	checkForReads ( scope, node, parent, strong ) {
		if ( node.type === 'Identifier' ) {
			// disregard the `bar` in `foo.bar` - these appear as Identifier nodes
			if ( parent.type === 'MemberExpression' && node !== parent.object ) {
				return;
			}

			// disregard the `bar` in { bar: foo }
			if ( parent.type === 'Property' && node !== parent.value ) {
				return;
			}

			// disregard the `bar` in `class Foo { bar () {...} }`
			if ( parent.type === 'MethodDefinition' ) return;

			const definingScope = scope.findDefiningScope( node.name );

			if ( ( !definingScope || definingScope.depth === 0 ) && !this.defines[ node.name ] ) {
				this.dependsOn[ node.name ] = true;
				if ( strong ) this.stronglyDependsOn[ node.name ] = true;
			}
		}
	}

	checkForWrites ( scope, node, strong ) {
		const parent = node;

		const addNode = ( node, isAssignment ) => {
			let depth = 0; // determine whether we're illegally modifying a binding or namespace

			while ( node.type === 'MemberExpression' ) {

				// In a situation like that below, make sure the assignments
				// to arr depend on `a` and `b`.
				//
				//    var a = 1, b = 0;
				//
				//    arr[a] = arr[b] = true;
				//
				if ( node.computed ) {
					this.checkForReads( scope, node.property, parent, strong );
				}

				node = node.object;
				depth += 1;
			}

			// disallow assignments/updates to imported bindings and namespaces
			if ( isAssignment ) {
				const importSpecifier = this.module.imports[ node.name ];

				if ( importSpecifier && !scope.contains( node.name ) ) {
					const minDepth = importSpecifier.name === '*' ?
						2 : // cannot do e.g. `namespace.foo = bar`
						1;  // cannot do e.g. `foo = bar`, but `foo.bar = bar` is fine

					if ( depth < minDepth ) {
						const err = new Error( `Illegal reassignment to import '${node.name}'` );
						err.file = this.module.id;
						err.loc = getLocation( this.module.magicString.toString(), node.start );
						throw err;
					}
				}

				// special case = `export default foo; foo += 1;` - we'll
				// need to assign a new variable so that the exported
				// value is not updated by the second statement
				if ( this.module.exports.default && depth === 0 && this.module.exports.default.identifier === node.name ) {
					// but only if this is a) inside a function body or
					// b) after the export declaration
					if ( !!scope.parent || node.start > this.module.exports.default.statement.node.start ) {
						this.module.exports.default.isModified = true;
					}
				}
			}

			if ( node.type === 'Identifier' ) {
				this.modifies[ node.name ] = true;
			}
		};

		if ( node.type === 'AssignmentExpression' ) {
			addNode( node.left, true );
		}

		else if ( node.type === 'UpdateExpression' ) {
			addNode( node.argument, true );
		}

		else if ( node.type === 'CallExpression' ) {
			node.arguments.forEach( arg => addNode( arg, false ) );

			// `foo.bar()` is assumed to mutate foo
			if ( node.callee.type === 'MemberExpression' ) {
				addNode( node.callee );
			}
		}
	}

	mark () {
		if ( this.isIncluded ) return; // prevent infinite loops
		this.isIncluded = true;

		const dependencies = Object.keys( this.dependsOn );

		return sequence( dependencies, name => {
			if ( this.defines[ name ] ) return; // TODO maybe exclude from `this.dependsOn` in the first place?
			return this.module.mark( name );
		});
	}

	replaceIdentifiers ( names, bundleExports ) {
		const magicString = this.magicString.clone();
		const replacementStack = [ names ];
		const nameList = keys( names );

		let deshadowList = [];
		nameList.forEach( name => {
			const replacement = names[ name ];
			deshadowList.push( replacement.split( '.' )[0] );
		});

		let topLevel = true;
		let depth = 0;

		walk( this.node, {
			enter ( node, parent ) {
				if ( node._skip ) return this.skip();

				if ( /^Function/.test( node.type ) ) depth += 1;

				// `this` is undefined at the top level of ES6 modules
				if ( node.type === 'ThisExpression' && depth === 0 ) {
					magicString.overwrite( node.start, node.end, 'undefined' );
				}

				// special case - variable declarations that need to be rewritten
				// as bundle exports
				if ( topLevel ) {
					if ( node.type === 'VariableDeclaration' ) {
						// if this contains a single declarator, and it's one that
						// needs to be rewritten, we replace the whole lot
						const name = node.declarations[0].id.name;
						if ( node.declarations.length === 1 && bundleExports[ name ] ) {
							magicString.overwrite( node.start, node.declarations[0].id.end, bundleExports[ name ] );
							node.declarations[0].id._skip = true;
						}

						// otherwise, we insert the `exports.foo = foo` after the declaration
						else {
							const exportInitialisers = node.declarations
								.map( declarator => declarator.id.name )
								.filter( name => !!bundleExports[ name ] )
								.map( name => `\n${bundleExports[name]} = ${name};` )
								.join( '' );

							// TODO clean this up
							try {
								magicString.insert( node.end, exportInitialisers );
							} catch ( err ) {
								magicString.append( exportInitialisers );
							}
						}
					}
				}

				const scope = node._scope;

				if ( scope ) {
					topLevel = false;

					let newNames = blank();
					let hasReplacements;

					// special case = function foo ( foo ) {...}
					if ( node.id && names[ node.id.name ] && scope.declarations[ node.id.name ] ) {
						magicString.overwrite( node.id.start, node.id.end, names[ node.id.name ] );
					}

					keys( names ).forEach( name => {
						if ( !scope.declarations[ name ] ) {
							newNames[ name ] = names[ name ];
							hasReplacements = true;
						}
					});

					deshadowList.forEach( name => {
						if ( ~scope.declarations[ name ] ) { // TODO is this right? no indexOf?
							newNames[ name ] = name + '$$'; // TODO better mechanism
							hasReplacements = true;
						}
					});

					if ( !hasReplacements && depth > 0 ) {
						return this.skip();
					}

					names = newNames;
					replacementStack.push( newNames );
				}

				// We want to rewrite identifiers (that aren't property names etc)
				if ( node.type !== 'Identifier' ) return;
				if ( parent.type === 'MemberExpression' && !parent.computed && node !== parent.object ) return;
				if ( parent.type === 'Property' && node !== parent.value ) return;
				// TODO others...?

				const name = names[ node.name ];

				if ( name && name !== node.name ) {
					magicString.overwrite( node.start, node.end, name );
				}
			},

			leave ( node ) {
				if ( /^Function/.test( node.type ) ) depth -= 1;

				if ( node._scope ) {
					replacementStack.pop();
					names = replacementStack[ replacementStack.length - 1 ];
				}
			}
		});

		return magicString;
	}
}
<commit_msg>check for computed property before abandoning checkForReads (#47)<commit_after>import { blank, keys } from './utils/object';
import { sequence } from './utils/promise';
import getLocation from './utils/getLocation';
import walk from './ast/walk';
import Scope from './ast/Scope';

function isIife ( node, parent ) {
	return parent && parent.type === 'CallExpression' && node === parent.callee;
}

export default class Statement {
	constructor ( node, magicString, module, index ) {
		this.node = node;
		this.module = module;
		this.magicString = magicString;
		this.index = index;
		this.id = module.id + '#' + index;

		this.scope = new Scope();
		this.defines = blank();
		this.modifies = blank();
		this.dependsOn = blank();
		this.stronglyDependsOn = blank();

		this.isIncluded = false;

		this.leadingComments = [];
		this.trailingComment = null;
		this.margin = [ 0, 0 ];

		// some facts about this statement...
		this.isImportDeclaration = node.type === 'ImportDeclaration';
		this.isExportDeclaration = /^Export/.test( node.type );
		this.isExportAllDeclaration = /^ExportAll/.test( node.type );
	}

	analyse () {
		if ( this.isImportDeclaration ) return; // nothing to analyse

		const statement = this; // TODO use arrow functions instead
		const magicString = this.magicString;

		let scope = this.scope;

		walk( this.node, {
			enter ( node, parent ) {
				let newScope;

				magicString.addSourcemapLocation( node.start );

				switch ( node.type ) {
					case 'FunctionExpression':
					case 'FunctionDeclaration':
					case 'ArrowFunctionExpression':
						if ( node.type === 'FunctionDeclaration' ) {
							scope.addDeclaration( node.id.name, node );
						}

						newScope = new Scope({
							parent: scope,
							params: node.params, // TODO rest params?
							block: false
						});

						// named function expressions - the name is considered
						// part of the function's scope
						if ( node.type === 'FunctionExpression' && node.id ) {
							newScope.addDeclaration( node.id.name, node );
						}

						break;

					case 'BlockStatement':
						if ( !/Function/.test( parent.type ) ) {
							newScope = new Scope({
								parent: scope,
								block: true
							});
						}

						break;

					case 'CatchClause':
						newScope = new Scope({
							parent: scope,
							params: [ node.param ],
							block: true
						});

						break;

					case 'VariableDeclaration':
						node.declarations.forEach( declarator => {
							scope.addDeclaration( declarator.id.name, node );
						});
						break;

					case 'ClassDeclaration':
						scope.addDeclaration( node.id.name, node );
						break;
				}

				if ( newScope ) {
					Object.defineProperty( node, '_scope', { value: newScope });
					scope = newScope;
				}
			},
			leave ( node ) {
				if ( node._scope ) {
					scope = scope.parent;
				}
			}
		});

		// This allows us to track whether we're looking at code that will
		// be executed immediately (either outside a function, or immediately
		// inside an IIFE), for the purposes of determining whether dependencies
		// are strong or weak. It's not bulletproof, since it wouldn't catch...
		//
		//    var calledImmediately = function () {
		//      doSomethingWith( strongDependency );
		//    }
		//    calledImmediately();
		//
		// ...but it's better than nothing
		let depth = 0;

		if ( !this.isImportDeclaration ) {
			walk( this.node, {
				enter: ( node, parent ) => {
					if ( node._scope ) {
						if ( !scope.isBlockScope && !isIife( node, parent ) ) depth += 1;
						scope = node._scope;
					}

					this.checkForReads( scope, node, parent, !depth );
					this.checkForWrites( scope, node );
				},
				leave: ( node, parent ) => {
					if ( node._scope ) {
						if ( !scope.isBlockScope && !isIife( node, parent ) ) depth -= 1;
						scope = scope.parent;
					}
				}
			});
		}

		keys( scope.declarations ).forEach( name => {
			statement.defines[ name ] = true;
		});
	}

	checkForReads ( scope, node, parent, strong ) {
		if ( node.type === 'Identifier' ) {
			// disregard the `bar` in `foo.bar` - these appear as Identifier nodes
			if ( parent.type === 'MemberExpression' && !parent.computed && node !== parent.object ) {
				return;
			}

			// disregard the `bar` in { bar: foo }
			if ( parent.type === 'Property' && node !== parent.value ) {
				return;
			}

			// disregard the `bar` in `class Foo { bar () {...} }`
			if ( parent.type === 'MethodDefinition' ) return;

			const definingScope = scope.findDefiningScope( node.name );

			if ( ( !definingScope || definingScope.depth === 0 ) && !this.defines[ node.name ] ) {
				this.dependsOn[ node.name ] = true;
				if ( strong ) this.stronglyDependsOn[ node.name ] = true;
			}
		}
	}

	checkForWrites ( scope, node ) {
		const addNode = ( node, isAssignment ) => {
			let depth = 0; // determine whether we're illegally modifying a binding or namespace

			while ( node.type === 'MemberExpression' ) {
				node = node.object;
				depth += 1;
			}

			// disallow assignments/updates to imported bindings and namespaces
			if ( isAssignment ) {
				const importSpecifier = this.module.imports[ node.name ];

				if ( importSpecifier && !scope.contains( node.name ) ) {
					const minDepth = importSpecifier.name === '*' ?
						2 : // cannot do e.g. `namespace.foo = bar`
						1;  // cannot do e.g. `foo = bar`, but `foo.bar = bar` is fine

					if ( depth < minDepth ) {
						const err = new Error( `Illegal reassignment to import '${node.name}'` );
						err.file = this.module.id;
						err.loc = getLocation( this.module.magicString.toString(), node.start );
						throw err;
					}
				}

				// special case = `export default foo; foo += 1;` - we'll
				// need to assign a new variable so that the exported
				// value is not updated by the second statement
				if ( this.module.exports.default && depth === 0 && this.module.exports.default.identifier === node.name ) {
					// but only if this is a) inside a function body or
					// b) after the export declaration
					if ( !!scope.parent || node.start > this.module.exports.default.statement.node.start ) {
						this.module.exports.default.isModified = true;
					}
				}
			}

			if ( node.type === 'Identifier' ) {
				this.modifies[ node.name ] = true;
			}
		};

		if ( node.type === 'AssignmentExpression' ) {
			addNode( node.left, true );
		}

		else if ( node.type === 'UpdateExpression' ) {
			addNode( node.argument, true );
		}

		else if ( node.type === 'CallExpression' ) {
			node.arguments.forEach( arg => addNode( arg, false ) );

			// `foo.bar()` is assumed to mutate foo
			if ( node.callee.type === 'MemberExpression' ) {
				addNode( node.callee );
			}
		}
	}

	mark () {
		if ( this.included ) return; // prevent infinite loops
		this.isIncluded = true;

		const dependencies = Object.keys( this.dependsOn );

		return sequence( dependencies, name => {
			if ( this.defines[ name ] ) return; // TODO maybe exclude from `this.dependsOn` in the first place?
			return this.module.mark( name );
		});
	}

	replaceIdentifiers ( names, bundleExports ) {
		const magicString = this.magicString.clone();
		const replacementStack = [ names ];
		const nameList = keys( names );

		let deshadowList = [];
		nameList.forEach( name => {
			const replacement = names[ name ];
			deshadowList.push( replacement.split( '.' )[0] );
		});

		let topLevel = true;
		let depth = 0;

		walk( this.node, {
			enter ( node, parent ) {
				if ( node._skip ) return this.skip();

				if ( /^Function/.test( node.type ) ) depth += 1;

				// `this` is undefined at the top level of ES6 modules
				if ( node.type === 'ThisExpression' && depth === 0 ) {
					magicString.overwrite( node.start, node.end, 'undefined' );
				}

				// special case - variable declarations that need to be rewritten
				// as bundle exports
				if ( topLevel ) {
					if ( node.type === 'VariableDeclaration' ) {
						// if this contains a single declarator, and it's one that
						// needs to be rewritten, we replace the whole lot
						const name = node.declarations[0].id.name;
						if ( node.declarations.length === 1 && bundleExports[ name ] ) {
							magicString.overwrite( node.start, node.declarations[0].id.end, bundleExports[ name ] );
							node.declarations[0].id._skip = true;
						}

						// otherwise, we insert the `exports.foo = foo` after the declaration
						else {
							const exportInitialisers = node.declarations
								.map( declarator => declarator.id.name )
								.filter( name => !!bundleExports[ name ] )
								.map( name => `\n${bundleExports[name]} = ${name};` )
								.join( '' );

							// TODO clean this up
							try {
								magicString.insert( node.end, exportInitialisers );
							} catch ( err ) {
								magicString.append( exportInitialisers );
							}
						}
					}
				}

				const scope = node._scope;

				if ( scope ) {
					topLevel = false;

					let newNames = blank();
					let hasReplacements;

					// special case = function foo ( foo ) {...}
					if ( node.id && names[ node.id.name ] && scope.declarations[ node.id.name ] ) {
						magicString.overwrite( node.id.start, node.id.end, names[ node.id.name ] );
					}

					keys( names ).forEach( name => {
						if ( !scope.declarations[ name ] ) {
							newNames[ name ] = names[ name ];
							hasReplacements = true;
						}
					});

					deshadowList.forEach( name => {
						if ( ~scope.declarations[ name ] ) { // TODO is this right? no indexOf?
							newNames[ name ] = name + '$$'; // TODO better mechanism
							hasReplacements = true;
						}
					});

					if ( !hasReplacements && depth > 0 ) {
						return this.skip();
					}

					names = newNames;
					replacementStack.push( newNames );
				}

				// We want to rewrite identifiers (that aren't property names etc)
				if ( node.type !== 'Identifier' ) return;
				if ( parent.type === 'MemberExpression' && !parent.computed && node !== parent.object ) return;
				if ( parent.type === 'Property' && node !== parent.value ) return;
				// TODO others...?

				const name = names[ node.name ];

				if ( name && name !== node.name ) {
					magicString.overwrite( node.start, node.end, name );
				}
			},

			leave ( node ) {
				if ( /^Function/.test( node.type ) ) depth -= 1;

				if ( node._scope ) {
					replacementStack.pop();
					names = replacementStack[ replacementStack.length - 1 ];
				}
			}
		});

		return magicString;
	}
}
<|endoftext|>


<commit_before>"use strict";

var fs = require("fs"),
    path = require("path");

// Expose to the world
module.exports = PickupDirectoryTransport;

/**
 *
Generates a Transport object which stores mails in a pickup directory

 *
 *
Possible options can be the following:

 *
 *

 *
pickupDirectoryLocation - The directory where applications save e-mail for later processing by the SMTP server.

 *

 *
 * @constructor
 * @param {Object} [options] Pickup directory options
 */
function PickupDirectoryTransport(options) {
    this.options = options || {};

    if (typeof this.options.pickupDirectoryLocation === "undefined") {
        throw new Error();
    }
}

/**
 *
Generates a raw e-mail source and stores it in the pickup directory

 *
 * @param {Object} emailMessage MailComposer object
 * @param {Function} callback Callback function to run when the e-mail is composed
 */
PickupDirectoryTransport.prototype.sendMail = function(emailMessage, callback) {

    var output = "";
    var pickupDirectoryLocation = this.options.pickupDirectoryLocation;

    // sendmail strips this header line by itself
    emailMessage.options.keepBcc = true;

    emailMessage.on("data", function(data) {
        output += (data || "").toString("utf-8");
    });

    emailMessage.on("error", function(err) {
        callback(err);
    });

    emailMessage.on("end", function() {

        var target =
            path.join(
                pickupDirectoryLocation,
                emailMessage._messageId + ".eml");

        fs.writeFile(target, output, function (err) {

            if (err) {
                callback(err);
            }

            callback(null, {
                message: output,
                envelope: emailMessage.getEnvelope(),
                messageId: emailMessage._messageId
            });
        });
    });

    emailMessage.streamMessage();
};<commit_msg>checked if the specified directory exists.<commit_after>"use strict";

var fs = require("fs"),
    path = require("path");

// Expose to the world
module.exports = PickupDirectoryTransport;

/**
 *
Generates a Transport object which stores mails in a pickup directory

 *
 *
Possible options can be the following:

 *
 *

 *
pickupDirectoryLocation - The directory where applications save e-mail for later processing by the SMTP server.

 *

 *
 * @constructor
 * @param {Object} [options] Pickup directory options
 */
function PickupDirectoryTransport(options) {
    this.options = options || {};

    // Check if target directory has been set.
    if (typeof this.options.pickupDirectoryLocation === "undefined") {
        throw new Error('pickupDirectoryLocation is not set.');
    }

    // Check if the target directory exists.
    fs.exists(this.options.pickupDirectoryLocation, function(exists) {
        if (!exists) {
            throw new Error('pickupDirectoryLocation does not exists.');
        }
    });
}

/**
 *
Generates a raw e-mail source and stores it in the pickup directory

 *
 * @param {Object} emailMessage MailComposer object
 * @param {Function} callback Callback function to run when the e-mail is composed
 */
PickupDirectoryTransport.prototype.sendMail = function(emailMessage, callback) {

    var output = "";
    var pickupDirectoryLocation = this.options.pickupDirectoryLocation;

    // sendmail strips this header line by itself
    emailMessage.options.keepBcc = true;

    emailMessage.on("data", function(data) {
        output += (data || "").toString("utf-8");
    });

    emailMessage.on("error", function(err) {
        callback(err);
    });

    emailMessage.on("end", function() {

        var target =
            path.join(
                pickupDirectoryLocation,
                emailMessage._messageId + ".eml");

        fs.writeFile(target, output, function (err) {

            if (err) {
                callback(err);
            }

            callback(null, {
                message: output,
                envelope: emailMessage.getEnvelope(),
                messageId: emailMessage._messageId
            });
        });
    });

    emailMessage.streamMessage();
};<|endoftext|>


<commit_before>'use strict';

var Query = require('../query')
var Conditions = require('./utils/conditions')

class FindAllQuery extends Query {
	constructor(entity, id, params, opts) {
		super(entity, id, params)
		if (!opts) {
			opts = {}
		}

		this.opts = opts
		this.type = 'findAll'
		this.conditions = new Conditions(opts.conditions)
		this.include = opts.include || []

		this.limit = opts.limit || 30

		if (!opts.offset && opts.page) {
			this.page = opts.page
			this.offset = null
		}
		else {
			this.offset = opts.offset || 0
			this.page = null
		}

		this.orderBy = opts.orderBy || []

		this.refresh()
	}

	refresh() {
		this.select = this.opts.select
		if (!this.select || this.select == []) {
			this.select = []
			this.entity.fields.forEach((field) => {
				if (field.read) {
					this.select.push(field.name)
				}
			})
			if (this.entity.createdAt !== false) {
				this.select.push('createdAt')
			}
			if (this.entity.updatedAt !== false) {
				this.select.push('updatedAt')
			}
		}
	}

	run(params) {

		let include = []
		let includeNames = this.include

		//construct recursively include array for Sequelize Query
		let constructInclude = (includeArray, includedName) => {
			for (let entity of includedName) {
				let includeEntity = this.entity.app.entities.get(entity.model)
				let includePart = { model: includeEntity.model, attributes: entity.through.attributes }
				if (entity.include) {
					includePart.include = []
					let includeNames = entity.include
					constructInclude(includePart.include, includeNames)
				}
				includeArray.push(includePart)
			}
		}
		constructInclude(include, includeNames)

		let pagination = this.getPagination(params)
		let principalConditions = []

		let opts = {
			attributes: this.select,
			where: this.conditions.toSequelize(params, this.entity.name),
			include: include,
			raw: true
		}

		//Add conditions to opts recursively for included obj
		let addConditions= (entities) => {
			for (let x in entities) {
				let entity = entities[x]
				for (let i in this.conditions.conditions) {
					let condition = this.conditions.conditions[i]
					if (this.conditions.conditions[i] && this.conditions.conditions[i].entity == entity.model.name) {
						if (entity.model.name != this.entity.name){
							entity.where = this.conditions.toSequelize(params, entity.model.name, this.entity.name)
						} else {
							entity.where = this.conditions.toSequelize(params, entity.model.name, this.entity.name)
						}
					}
					if (entity.include) {
						addConditions(entity.include)
					}
				}
			}
		}
		addConditions(opts.include)

		if (pagination) {
			if (pagination.offset) {
				opts.offset = pagination.offset
			}
			if (pagination.limit) {
				opts.limit = pagination.limit
			}
		}
		opts.order = []
		this.orderBy.forEach((order) => {
			let ascTxt = 'ASC'
			if (order.desc) {
				ascTxt = 'DESC'
			}
			opts.order.push([order.field, ascTxt]);
		})

		//TODO: opts.order
		//console.log(opts)
		return this.entity.model.findAndCountAll(opts)/*.then((data) => {
			for (let item in data.rows)
				data.rows[item] = data.rows[item]
			return data
		})*/
	}

	_paramResolver(name, value, params, defaultValue) {
		let tmp;
		try {
			tmp = Query.resolveParam({ name: name, value: value }, params)
			if (!tmp) {
				throw 'error'
			}
		}
		catch (e) {
			tmp = defaultValue
		}
		return tmp
	}

	getPagination(params) {
		let limit, offset;

		limit = this._paramResolver('limit', this.limit, params, null)

		if (this.page) {
			let page = this._paramResolver('page', this.page, params, 1)
			offset = (page - 1) * limit
		}
		else {
			offset = this._paramResolver('offset', this.offset, params, 0)
		}

		return {
			limit: limit,
			offset: offset
		}
	}

	toJson() {
		let res = {
			id: this.id,
			type: 'findAll',
			params: this.params,
			opts: {}
		}
		if (this.opts.select) {
			res.opts.select = this.opts.select
		}
		if (this.conditions.toJson() != []) {
			res.opts.conditions = this.conditions.toJson()
		}
		if (this.opts.include) {
			res.opts.include = this.opts.include
		}
		if (this.opts.offset) {
			res.opts.offset = this.opts.offset
		}
		if (this.opts.limit) {
			res.opts.limit = this.opts.limit
		}
		if (this.opts.page) {
			res.opts.page = this.opts.page
		}

		if (this.opts.orderBy) {
			res.opts.orderBy = this.opts.orderBy
		}

		return res
	}
}

module.exports = FindAllQuery
<commit_msg>change to support new queries format in entity.json<commit_after>'use strict';

var Query = require('../query')
var Conditions = require('./utils/conditions')

class FindAllQuery extends Query {
	constructor(entity, id, params, opts) {
		super(entity, id, params)
		if (!opts) {
			opts = {}
		}

		this.opts = opts
		this.type = 'findAll'
		this.conditions = new Conditions(opts.conditions)
		this.include = opts.include || []

		this.limit = opts.limit || 30

		if (!opts.offset && opts.page) {
			this.page = opts.page
			this.offset = null
		}
		else {
			this.offset = opts.offset || 0
			this.page = null
		}

		this.orderBy = opts.orderBy || []

		this.refresh()
	}

	refresh() {
		this.select = this.opts.select
		if (!this.select || this.select == []) {
			this.select = []
			this.entity.fields.forEach((field) => {
				if (field.read) {
					this.select.push(field.name)
				}
			})
			if (this.entity.createdAt !== false) {
				this.select.push('createdAt')
			}
			if (this.entity.updatedAt !== false) {
				this.select.push('updatedAt')
			}
		}
	}

	run(params) {

		let include = []
		let includeNames = this.include

		//construct recursively include array for Sequelize Query
		let constructInclude = (includeArray, includedName) => {
			for (let entity of includedName) {
				let includeEntity = this.entity.app.entities.get(entity.entity)
				let includePart = { model: includeEntity.model, attributes: entity.fields }
				if (entity.include) {
					includePart.include = []
					let includeNames = entity.include
					constructInclude(includePart.include, includeNames)
				}
				includeArray.push(includePart)
			}
		}
		constructInclude(include, includeNames)

		let pagination = this.getPagination(params)
		let principalConditions = []

		let opts = {
			attributes: this.select,
			where: this.conditions.toSequelize(params, this.entity.name),
			include: include,
			raw: true
		}

		//Add conditions to opts recursively for included obj
		let addConditions= (entities) => {
			for (let x in entities) {
				let entity = entities[x]
				for (let i in this.conditions.conditions) {
					let condition = this.conditions.conditions[i]
					if (this.conditions.conditions[i] && this.conditions.conditions[i].entity == entity.model.name) {
						if (entity.model.name != this.entity.name){
							entity.where = this.conditions.toSequelize(params, entity.model.name, this.entity.name)
						} else {
							entity.where = this.conditions.toSequelize(params, entity.model.name, this.entity.name)
						}
					}
					if (entity.include) {
						addConditions(entity.include)
					}
				}
			}
		}
		addConditions(opts.include)

		if (pagination) {
			if (pagination.offset) {
				opts.offset = pagination.offset
			}
			if (pagination.limit) {
				opts.limit = pagination.limit
			}
		}
		opts.order = []
		this.orderBy.forEach((order) => {
			let ascTxt = 'ASC'
			if (order.desc) {
				ascTxt = 'DESC'
			}
			opts.order.push([order.field, ascTxt]);
		})

		//TODO: opts.order
		//console.log(opts)
		return this.entity.model.findAndCountAll(opts)/*.then((data) => {
			for (let item in data.rows)
				data.rows[item] = data.rows[item]
			return data
		})*/
	}

	_paramResolver(name, value, params, defaultValue) {
		let tmp;
		try {
			tmp = Query.resolveParam({ name: name, value: value }, params)
			if (!tmp) {
				throw 'error'
			}
		}
		catch (e) {
			tmp = defaultValue
		}
		return tmp
	}

	getPagination(params) {
		let limit, offset;

		limit = this._paramResolver('limit', this.limit, params, null)

		if (this.page) {
			let page = this._paramResolver('page', this.page, params, 1)
			offset = (page - 1) * limit
		}
		else {
			offset = this._paramResolver('offset', this.offset, params, 0)
		}

		return {
			limit: limit,
			offset: offset
		}
	}

	toJson() {
		let res = {
			id: this.id,
			type: 'findAll',
			params: this.params,
			opts: {}
		}
		if (this.opts.select) {
			res.opts.select = this.opts.select
		}
		if (this.conditions.toJson() != []) {
			res.opts.conditions = this.conditions.toJson()
		}
		if (this.opts.include) {
			res.opts.include = this.opts.include
		}
		if (this.opts.offset) {
			res.opts.offset = this.opts.offset
		}
		if (this.opts.limit) {
			res.opts.limit = this.opts.limit
		}
		if (this.opts.page) {
			res.opts.page = this.opts.page
		}

		if (this.opts.orderBy) {
			res.opts.orderBy = this.opts.orderBy
		}

		return res
	}
}

module.exports = FindAllQuery
<|endoftext|>


<commit_before>/* eslint-env node */
/*eslint no-console: 0*/

var URL = require('url');
var http = require('./http-wrapper');

var protocols = {
  http: require('http'),
  https: require('https'),
};

var RE_contentType = /([^/]+)\/([^+]+\+)?([^;]*)/;
function parseContentType(contentType) {
  var matches = contentType && contentType.match(RE_contentType);
  return matches ? matches[3] : 'text';
}

var parseData = {
  json: function (data) {
    return JSON.parse(data);
  }
};

http.useRequest(function (config, resolve, reject) {

  var url = URL.parse(config.url);

  var options = {
    method: config.method,
    hostname: url.hostname,
    port: url.port,
    path: url.path,
    encoding: null,
    headers: config.headers
  };

  var data = null;
  // var data = [];

  var req = protocols[url.protocol.replace(/:$/, '')].request(options, function (res) {
    // console.log(`STATUS: ${res.statusCode}`);
    // console.log(`HEADERS: ${JSON.stringify(res.headers)}`);
    // res.setEncoding('utf8');
    res.setEncoding(config.headers['Content-Type'] ? 'utf8' : 'binary');

    req.on('socket', function (socket) {
      if( 'timeout' in config )
      socket.setTimeout(config.timeout);
      socket.on('timeout', function () {
        req.abort();
        reject('timeout');
      });
    });

    res.on('data', function (chunk) {

      data = data ? data.concat(chunk) : chunk;
      // data.push(chunk);

    });

    res.on('end', function () {
      console.log('fetched', url.format() );
      var format = parseContentType(res.headers['Content-Type']);
      resolve({ config: config, headers: res.headers, data: parseData[format] ? parseData[format](data) : data });
      // resolve({ config: options, data: Buffer.concat(data) });
    });

    res.on('error', function(err) {
      console.log('HTTP ERROR', err);
      reject({ ok: false, status: res.statusCode, statusText: res.statusMessage });
    });
  });

  req.on('error', function (e) {
    reject(e);
    // console.log(`problem with request: ${e.message}`);
  });

  req.end();

  return {
    abort: function () {
      req.abort();
    },
  };

});

module.exports = http;
<commit_msg>Added timeout and abort<commit_after>/* eslint-env node */
/*eslint no-console: 0*/

var URL = require('url');
var http = require('./http-wrapper');

var protocols = {
  http: require('http'),
  https: require('https'),
};

var RE_contentType = /([^/]+)\/([^+]+\+)?([^;]*)/;
function parseContentType(contentType) {
  var matches = contentType && contentType.match(RE_contentType);
  return matches ? matches[3] : 'text';
}

var parseData = {
  json: function (data) {
    return JSON.parse(data);
  }
};

http.useRequest(function (config, resolve, reject) {

  var url = URL.parse(config.url);

  var options = {
    method: config.method,
    hostname: url.hostname,
    port: url.port,
    path: url.path,
    encoding: null,
    headers: config.headers
  };

  var data = null;
  // var data = [];

  var req = protocols[url.protocol.replace(/:$/, '')].request(options, function (res) {
    // console.log(`STATUS: ${res.statusCode}`);
    // console.log(`HEADERS: ${JSON.stringify(res.headers)}`);
    // res.setEncoding('utf8');
    res.setEncoding(config.headers['Content-Type'] ? 'utf8' : 'binary');

    req.on('socket', function (socket) {
      if( 'timeout' in config )
      socket.setTimeout(config.timeout);
      socket.on('timeout', function () {
        // req.abort();
        reject('timeout');
      });
    });

    res.on('data', function (chunk) {

      data = data ? data.concat(chunk) : chunk;
      // data.push(chunk);

    });

    res.on('end', function () {
      console.log('fetched', url.format() );
      var format = parseContentType(res.headers['Content-Type']);
      resolve({ config: config, headers: res.headers, data: parseData[format] ? parseData[format](data) : data });
      // resolve({ config: options, data: Buffer.concat(data) });
    });

    res.on('error', function(err) {
      console.log('HTTP ERROR', err);
      reject({ ok: false, status: res.statusCode, statusText: res.statusMessage });
    });
  });

  req.on('error', function (e) {
    reject(e);
    // console.log(`problem with request: ${e.message}`);
  });

  req.end();

  return {
    abort: function () {
      req.abort();
    },
  };

});

module.exports = http;
<|endoftext|>


<commit_before>'use strict';

define(['frontend/providers/sounds'], function(sounds) {
});
<commit_msg>Added test for sounds service.<commit_after>'use strict';

define(['angular-mocks', 'frontend/providers/sounds'], function(mocks) {
  describe('Sounds provider', function() {

    var sounds, $window;

    beforeEach(mocks.module("DarkWallet.providers"));
    beforeEach(mocks.inject(['sounds', '$window', function(_sounds_, _$window_) {
      sounds = _sounds_;
      $window = _$window_;
      var el = $window.document.createElement('div');
      el.setAttribute('id', 'fixed');
      $window.document.body.appendChild(el);
    }]));

    it('plays', function() {
      sounds.play('beep');
      var html = '<audio autoplay="autoplay"><source src="../sound/beep.opus" type="audio/ogg"></audio>';
      expect($window.document.getElementById('fixed').innerHTML).toBe(html);
    });
  });
});<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import { FormattedMessage, injectIntl, intlShape } from 'react-intl';
import Pusher from 'pusher-js';
import DocumentTitle from 'react-document-title';
import MediaDetail from './MediaDetail';
import MediaUtil from './MediaUtil';
import MediaChecklist from './MediaChecklist';
import { Tags } from '../source';
import Annotations from './Annotations';
import config from 'config';
import { pageTitle } from '../../helpers';
import CheckContext from '../../CheckContext';
import Tasks from '../task/Tasks';
import CreateTask from '../task/CreateTask';
import { bemClass, bemClassFromMediaStatus, safelyParseJSON, getStatus, getStatusStyle } from '../../helpers';
import ContentColumn from '../layout/ContentColumn';
import MediaStatus from './MediaStatus';
import MdInfoOutline from 'react-icons/lib/md/info-outline';
import Tooltip from 'rc-tooltip';

class MediaComponent extends Component {
  getContext() {
    const context = new CheckContext(this).getContextStore();
    return context;
  }

  setCurrentContext() {
    const context = new CheckContext(this);
    if (!context.getContextStore().project) {
      const contextStore = {
        project: this.props.media.project,
      }
      context.setContextStore(contextStore);
    }
    this.props.relay.setVariables({ contextId: this.getContext().project.dbid });
  }

  componentDidMount() {
    this.setCurrentContext();
    this.scrollToAnnotation();
    this.subscribe();
  }

  componentDidUpdate() {
    this.setCurrentContext();
    this.scrollToAnnotation();
  }

  scrollToAnnotation() {
    if (window.location.hash != '') {
      let id = window.location.hash.replace(/^#/, ''),
        element = document.getElementById(id);
      if (element.scrollIntoView != undefined) {
        element.scrollIntoView();
      }
    }
  }

  subscribe() {
    const pusher = this.getContext().pusher;
    if (pusher) {
      const that = this;
      pusher.subscribe(this.props.media.pusher_channel).bind('media_updated', (data) => {
        const annotation = JSON.parse(data.message);
        if (annotation.annotated_id === that.props.media.dbid) {
          that.props.relay.forceFetch();
        }
      });
    }
  }

  unsubscribe() {
    const pusher = this.getContext().pusher;
    if (pusher) {
      pusher.unsubscribe(this.props.media.pusher_channel);
    }
  }

  componentWillUnmount() {
    this.unsubscribe();
  }

  render() {
    if (this.props.relay.variables.contextId === null) {
      return null;
    }

    const media = this.props.media;
    const data = JSON.parse(media.embed);
    media.url = media.media.url;
    media.quote = media.media.quote;
    media.embed_path = media.media.embed_path;
    const userOverrides = safelyParseJSON(media.overridden);
    const primaryHeading = (userOverrides && userOverrides.title) ?
        MediaUtil.title(media, data, this.props.intl) : MediaUtil.attributedType(media, data, this.props.intl);
    const status = getStatus(this.props.media.verification_statuses, media.last_status);

    return (
      <DocumentTitle title={pageTitle(MediaUtil.title(media, data, this.props.intl), false, this.getContext().team)}>

{primaryHeading}

                <MediaStatus media={media} readonly={this.props.readonly} />

            <ContentColumn wide className='media__expanded-column-wrapper'>
              <ContentColumn className='media__media-column'>
                <MediaDetail media={media} />
                <CreateTask media={media} />
              </ContentColumn>
              <ContentColumn className='media__tasks-column'>

<FormattedMessage id="mediaComponent.verificationTasks" defaultMessage="Verification tasks" />

                  {media.tasks.edges.filter((t) => { return !!t.node.first_response; }).length}/{media.tasks.edges.length} <FormattedMessage id="mediaComponent.resolved" defaultMessage="resolved" />
                  <CreateTask media={media} plusIcon/>

                <Tasks tasks={media.tasks.edges} media={media} />
              </ContentColumn>
            </ContentColumn>

          <ContentColumn>
            <Tooltip placement="bottom" trigger={['click']} overlay={<MediaChecklist/>} overlayClassName="">

                <FormattedMessage id="mediaComponent.verificationTimeline" defaultMessage="Verification Timeline" />
                <MdInfoOutline/>

            </Tooltip>
          </ContentColumn>
          <Annotations annotations={media.log.edges} annotated={media} annotatedType="ProjectMedia" />

      </DocumentTitle>
    );
  }
}

MediaComponent.propTypes = {
  intl: intlShape.isRequired,
};

MediaComponent.contextTypes = {
  store: React.PropTypes.object,
};

export default injectIntl(MediaComponent);
<commit_msg>Last cosmetic change, I promise #5889<commit_after>import React, { Component, PropTypes } from 'react';
import { FormattedMessage, injectIntl, intlShape } from 'react-intl';
import Pusher from 'pusher-js';
import DocumentTitle from 'react-document-title';
import MediaDetail from './MediaDetail';
import MediaUtil from './MediaUtil';
import MediaChecklist from './MediaChecklist';
import { Tags } from '../source';
import Annotations from './Annotations';
import config from 'config';
import { pageTitle } from '../../helpers';
import CheckContext from '../../CheckContext';
import Tasks from '../task/Tasks';
import CreateTask from '../task/CreateTask';
import { bemClass, bemClassFromMediaStatus, safelyParseJSON, getStatus, getStatusStyle } from '../../helpers';
import ContentColumn from '../layout/ContentColumn';
import MediaStatus from './MediaStatus';
import MdInfoOutline from 'react-icons/lib/md/info-outline';
import Tooltip from 'rc-tooltip';

class MediaComponent extends Component {
  getContext() {
    const context = new CheckContext(this).getContextStore();
    return context;
  }

  setCurrentContext() {
    const context = new CheckContext(this);
    if (!context.getContextStore().project) {
      context.setContextStore({
        project: this.props.media.project,
      });
    }
    this.props.relay.setVariables({ contextId: this.getContext().project.dbid });
  }

  componentDidMount() {
    this.setCurrentContext();
    this.scrollToAnnotation();
    this.subscribe();
  }

  componentDidUpdate() {
    this.setCurrentContext();
    this.scrollToAnnotation();
  }

  scrollToAnnotation() {
    if (window.location.hash != '') {
      let id = window.location.hash.replace(/^#/, ''),
        element = document.getElementById(id);
      if (element.scrollIntoView != undefined) {
        element.scrollIntoView();
      }
    }
  }

  subscribe() {
    const pusher = this.getContext().pusher;
    if (pusher) {
      const that = this;
      pusher.subscribe(this.props.media.pusher_channel).bind('media_updated', (data) => {
        const annotation = JSON.parse(data.message);
        if (annotation.annotated_id === that.props.media.dbid) {
          that.props.relay.forceFetch();
        }
      });
    }
  }

  unsubscribe() {
    const pusher = this.getContext().pusher;
    if (pusher) {
      pusher.unsubscribe(this.props.media.pusher_channel);
    }
  }

  componentWillUnmount() {
    this.unsubscribe();
  }

  render() {
    if (this.props.relay.variables.contextId === null) {
      return null;
    }

    const media = this.props.media;
    const data = JSON.parse(media.embed);
    media.url = media.media.url;
    media.quote = media.media.quote;
    media.embed_path = media.media.embed_path;
    const userOverrides = safelyParseJSON(media.overridden);
    const primaryHeading = (userOverrides && userOverrides.title) ?
        MediaUtil.title(media, data, this.props.intl) : MediaUtil.attributedType(media, data, this.props.intl);
    const status = getStatus(this.props.media.verification_statuses, media.last_status);

    return (
      <DocumentTitle title={pageTitle(MediaUtil.title(media, data, this.props.intl), false, this.getContext().team)}>

{primaryHeading}

                <MediaStatus media={media} readonly={this.props.readonly} />

            <ContentColumn wide className='media__expanded-column-wrapper'>
              <ContentColumn className='media__media-column'>
                <MediaDetail media={media} />
                <CreateTask media={media} />
              </ContentColumn>
              <ContentColumn className='media__tasks-column'>

<FormattedMessage id="mediaComponent.verificationTasks" defaultMessage="Verification tasks" />

                  {media.tasks.edges.filter((t) => { return !!t.node.first_response; }).length}/{media.tasks.edges.length} <FormattedMessage id="mediaComponent.resolved" defaultMessage="resolved" />
                  <CreateTask media={media} plusIcon/>

                <Tasks tasks={media.tasks.edges} media={media} />
              </ContentColumn>
            </ContentColumn>

          <ContentColumn>
            <Tooltip placement="bottom" trigger={['click']} overlay={<MediaChecklist/>} overlayClassName="">

                <FormattedMessage id="mediaComponent.verificationTimeline" defaultMessage="Verification Timeline" />
                <MdInfoOutline/>

            </Tooltip>
          </ContentColumn>
          <Annotations annotations={media.log.edges} annotated={media} annotatedType="ProjectMedia" />

      </DocumentTitle>
    );
  }
}

MediaComponent.propTypes = {
  intl: intlShape.isRequired,
};

MediaComponent.contextTypes = {
  store: React.PropTypes.object,
};

export default injectIntl(MediaComponent);
<|endoftext|>


<commit_before>import React from 'react';
import { FormattedMessage, defineMessages, injectIntl, intlShape } from 'react-intl';
import rtlDetect from 'rtl-detect';
import { Card, CardActions, CardHeader, CardText } from 'material-ui/Card';
import UserInfo from './UserInfo';
import UserInfoEdit from './UserInfoEdit';
import { can } from '../Can';
import HeaderCard from '../HeaderCard';
import PageTitle from '../PageTitle';
import ContentColumn from '../layout/ContentColumn';
import SwitchTeams from '../team/SwitchTeams';

class UserComponent extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      isEditing: false,
    };
  }

  handleEnterEditMode = () => {
    this.setState({ isEditing: true });
  };

  handleLeaveEditMode = () => {
    this.setState({ isEditing: false });
  };

  render() {

    const team =  this.props.user.current_team;
    const { user } = this.props;

    const isRtl = rtlDetect.isRtlLang(this.props.intl.locale);

    const direction = {
      from: isRtl ? 'right' : 'left',
      to: isRtl ? 'left' : 'right',
    };

    return (
      <PageTitle prefix={user.source.name} skipTeam={true}>

          <HeaderCard
            canEdit={can(user.permissions, 'update User')}
            direction={direction}
            handleEnterEditMode={this.handleEnterEditMode.bind(this)}
            isEditing={this.state.isEditing}
          >
            <ContentColumn>
              { this.state.isEditing ? <UserInfoEdit user={user} onCancelEdit={this.handleLeaveEditMode} /> : <UserInfo user={user} />}
            </ContentColumn>
          </HeaderCard>
          <ContentColumn>
            <SwitchTeams />
          </ContentColumn>

      </PageTitle>
    );
  }
}

export default injectIntl(UserComponent);
<commit_msg>#5961 Fix ContentColumn path<commit_after>import React from 'react';
import { FormattedMessage, defineMessages, injectIntl, intlShape } from 'react-intl';
import rtlDetect from 'rtl-detect';
import { Card, CardActions, CardHeader, CardText } from 'material-ui/Card';
import UserInfo from './UserInfo';
import UserInfoEdit from './UserInfoEdit';
import { can } from '../Can';
import HeaderCard from '../HeaderCard';
import PageTitle from '../PageTitle';
import { ContentColumn } from '../../styles/js/shared';

import SwitchTeams from '../team/SwitchTeams';

class UserComponent extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      isEditing: false,
    };
  }

  handleEnterEditMode = () => {
    this.setState({ isEditing: true });
  };

  handleLeaveEditMode = () => {
    this.setState({ isEditing: false });
  };

  render() {

    const team =  this.props.user.current_team;
    const { user } = this.props;

    const isRtl = rtlDetect.isRtlLang(this.props.intl.locale);

    const direction = {
      from: isRtl ? 'right' : 'left',
      to: isRtl ? 'left' : 'right',
    };

    return (
      <PageTitle prefix={user.source.name} skipTeam={true}>

          <HeaderCard
            canEdit={can(user.permissions, 'update User')}
            direction={direction}
            handleEnterEditMode={this.handleEnterEditMode.bind(this)}
            isEditing={this.state.isEditing}
          >
            <ContentColumn>
              { this.state.isEditing ? <UserInfoEdit user={user} onCancelEdit={this.handleLeaveEditMode} /> : <UserInfo user={user} />}
            </ContentColumn>
          </HeaderCard>
          <ContentColumn>
            <SwitchTeams />
          </ContentColumn>

      </PageTitle>
    );
  }
}

export default injectIntl(UserComponent);
<|endoftext|>


<commit_before>import { computed, action, observable } from 'mobx';
import _ from 'lodash';

import { $try } from './utils';

import VJF from './validators/VJF'; // Vanilla JavaScript Functions
import SVK from './validators/SVK'; // Json Schema Validation Keywords
import DVR from './validators/DVR'; // Declarative Validation Rules

export default class Validator {

  promises = [];

  form = {};

  options = {};

  schema = {};

  plugins = {
    vjf: true,
    dvr: false,
    svk: false,
  };

  drivers = {
    vjf: null,
    dvr: null,
    svk: null,
  };

  @observable $genericErrorMessage = null;

  constructor(obj = {}) {
    this.assignInitData(obj);
    this.initializePlugins();
    this.checkSVKValidationPlugin();
  }

  assignInitData({ form, options = {}, plugins = {}, schema = {} }) {
    _.merge(this.plugins, plugins);
    this.form = form;
    this.options = options;
    this.schema = schema;
  }

  initializePlugins() {
    _.map({
      vjf: VJF,
      dvr: DVR,
      svk: SVK,
    }, (Class, key) => this.plugins[key] &&
      (this.drivers[key] = new Class(this.plugins[key], {
        schema: (key === 'svk') ? this.schema : null,
        promises: this.promises,
        options: this.options,
      })));
  }

  @action
  validate(opt = {}, obj = {}) {
    const path = $try(opt.path, opt);
    const field = $try(opt.field, this.form.select(path, null, null));
    const related = $try(opt.related, obj.related, true);
    const showErrors = $try(opt.showErrors, obj.showErrors, false);
    const instance = field || this.form;
    instance.$validating = true;
    this.$genericErrorMessage = null;

    return new Promise((resolve) => {
      // validate instance (form or filed)
      if (instance.path || _.isString(path)) {
        this.validateField({
          field: instance,
          showErrors,
          related,
          path,
        });
      }

      // validate nested fields
      instance.each($field =>
        this.validateField({
          path: $field.path,
          $field,
          showErrors,
          related,
        }));

      // wait all promises then resolve
      return Promise.all(this.promises)
        .then(action(() => (instance.$validating = false))) // eslint-disable-line
        .catch(action((err) => {
          // eslint-disable-next-line
          instance.$validating = false;
          throw err;
        }))
        .then(() => resolve(instance));
    });
  }

  @action
  validateField({ field = null, path, showErrors = false, related = false }) {
    const $field = field || this.form.select(path);
    // reset field validation
    $field.resetValidation();
    // get all validators
    const { svk, dvr, vjf } = this.drivers;
    // validate with vanilla js functions (vjf)
    if (vjf) vjf.validateField($field, this.form);
    // validate with json schema validation keywords (dvr)
    if (dvr) dvr.validateField($field, this.form);
    // validate with json schema validation keywords (svk)
    if (svk) svk.validateField($field);
    // send error to the view
    $field.showErrors(showErrors);
    // related validation
    if (related) this.relatedFieldValidation($field, showErrors);
  }

  /**
    Validate 'related' fields if specified
    and related validation allowed (recursive)
  */
  relatedFieldValidation(field, showErrors) {
    if (!field.related || !field.related.length) return;

    _.each(field.related, path =>
      this.validateField({ path, showErrors, related: false }));
  }

  @computed get genericErrorMessage() {
    return this.options.get('alwaysShowDefaultError')
      ? (this.$genericErrorMessage || this.options.get('defaultGenericError'))
      : this.$genericErrorMessage;
  }

  getDefaultErrorMessage() {
    // set defaultGenericError message from options
    const $default = this.options.get('defaultGenericError');
    if (_.isString($default)) return $default;
    return 'The form is invalid';
  }

  @action
  invalidate(message = null) {
    // set custom genericErrorMessage if provided
    if (_.isString(message)) {
      this.$genericErrorMessage = message;
      return;
    }
    // if no string provided, show default error.
    this.$genericErrorMessage = this.getDefaultErrorMessage();
  }

  checkSVKValidationPlugin() {
    if (_.isNil(this.drivers.svk) && !_.isEmpty(this.schema)) {
      // eslint-disable-next-line
      console.warn(
        'The SVK validation schema is defined',
        'but no plugin provided (SVK).',
      );
    }
  }
}
<commit_msg>fix: https://github.com/foxhound87/mobx-react-form-demo/issues/5<commit_after>import { computed, action, observable } from 'mobx';
import _ from 'lodash';

import { $try } from './utils';

import VJF from './validators/VJF'; // Vanilla JavaScript Functions
import SVK from './validators/SVK'; // Json Schema Validation Keywords
import DVR from './validators/DVR'; // Declarative Validation Rules

export default class Validator {

  promises = [];

  form = {};

  options = {};

  schema = {};

  plugins = {
    vjf: true,
    dvr: false,
    svk: false,
  };

  drivers = {
    vjf: null,
    dvr: null,
    svk: null,
  };

  @observable $genericErrorMessage = null;

  constructor(obj = {}) {
    this.assignInitData(obj);
    this.initializePlugins();
    this.checkSVKValidationPlugin();
  }

  assignInitData({ form, options = {}, plugins = {}, schema = {} }) {
    _.merge(this.plugins, plugins);
    this.form = form;
    this.options = options;
    this.schema = schema;
  }

  initializePlugins() {
    _.map({
      vjf: VJF,
      dvr: DVR,
      svk: SVK,
    }, (Class, key) => this.plugins[key] &&
      (this.drivers[key] = new Class(this.plugins[key], {
        schema: (key === 'svk') ? this.schema : null,
        promises: this.promises,
        options: this.options,
      })));
  }

  @action
  validate(opt = {}, obj = {}) {
    const path = $try(opt.path, opt);
    const field = $try(opt.field, this.form.select(path, null, null));
    const related = $try(opt.related, obj.related, true);
    const showErrors = $try(opt.showErrors, obj.showErrors, false);
    const instance = field || this.form;
    instance.$validating = true;
    this.$genericErrorMessage = null;

    return new Promise((resolve) => {
      // validate instance (form or filed)
      if (instance.path || _.isString(path)) {
        this.validateField({
          field: instance,
          showErrors,
          related,
          path,
        });
      }

      // validate nested fields
      instance.each($field =>
        this.validateField({
          path: $field.path,
          field: $field,
          showErrors,
          related,
        }));

      // wait all promises then resolve
      return Promise.all(this.promises)
        .then(action(() => (instance.$validating = false))) // eslint-disable-line
        .catch(action((err) => {
          // eslint-disable-next-line
          instance.$validating = false;
          throw err;
        }))
        .then(() => resolve(instance));
    });
  }

  @action
  validateField({ field = null, path, showErrors = false, related = false }) {
    const instance = field || this.form.select(path);
    // reset field validation
    if (instance.path) instance.resetValidation();
    // get all validators
    const { svk, dvr, vjf } = this.drivers;
    // validate with vanilla js functions (vjf)
    if (vjf) vjf.validateField(instance, this.form);
    // validate with json schema validation keywords (dvr)
    if (dvr) dvr.validateField(instance, this.form);
    // validate with json schema validation keywords (svk)
    if (svk) svk.validateField(instance);
    // send error to the view
    instance.showErrors(showErrors);
    // related validation
    if (related) this.relatedFieldValidation(instance, showErrors);
  }

  /**
    Validate 'related' fields if specified
    and related validation allowed (recursive)
  */
  relatedFieldValidation(field, showErrors) {
    if (!field.related || !field.related.length) return;

    _.each(field.related, path =>
      this.validateField({ path, showErrors, related: false }));
  }

  @computed get genericErrorMessage() {
    return this.options.get('alwaysShowDefaultError')
      ? (this.$genericErrorMessage || this.options.get('defaultGenericError'))
      : this.$genericErrorMessage;
  }

  getDefaultErrorMessage() {
    // set defaultGenericError message from options
    const $default = this.options.get('defaultGenericError');
    if (_.isString($default)) return $default;
    return 'The form is invalid';
  }

  @action
  invalidate(message = null) {
    // set custom genericErrorMessage if provided
    if (_.isString(message)) {
      this.$genericErrorMessage = message;
      return;
    }
    // if no string provided, show default error.
    this.$genericErrorMessage = this.getDefaultErrorMessage();
  }

  checkSVKValidationPlugin() {
    if (_.isNil(this.drivers.svk) && !_.isEmpty(this.schema)) {
      // eslint-disable-next-line
      console.warn(
        'The SVK validation schema is defined',
        'but no plugin provided (SVK).',
      );
    }
  }
}
<|endoftext|>


<commit_before>/**
 * Heir v0.0.0 - http://git.io/F87mKg
 * Oliver Caldwell
 * MIT license
 */

;(function(exports) {
    // JSHint config - http://www.jshint.com/
    /*jshint laxcomma:true*/
    /*global define:true*/

    // Place the script into strict mode
    'use strict';

    /**
     * Works out if a variable is a true object (created with {} etc) and not an array or anything else that usually shows up as an object.
     *
     * @param {Mixed} chk The variable to check to see if it is an object. It must be a pure object, not even a prototype.
     * @return {Boolean} True if it is a true object, false if it is anything else.
     */
    function isObject(chk) {
        return (chk && typeof chk === 'object' && Object.prototype.toString.call(chk) !== '[object Array]') === true;
    }

    /**
     * Recursively merges two objects. Object `a` will be overridden by the values in object `b`.
     * Please run the values through a cloning function first, this function does not try to clone them for you.
     * The base object will be edited directly, please be careful!
     *
     * @param {Object} a The base object to merge into.
     * @param {Object} b The object to merge down into object `a`.
     * @return {Object} This is object `a` but merged with `b`.
     */
    function merge(a, b) {
        // Loop over all values in b. If they are not found in a then set them
        // If both values are objects then recursively merge them
        for(var key in b) {
            // Make sure the value is not in __proto__ or something like that
            if(b.hasOwnProperty(key)) {
                // If they are both objects then merge recursively
                if(isObject(a[key]) && isObject(b[key])) {
                    merge(a[key], b[key]);
                }

                // Otherwise just replace the base value
                else {
                    a[key] = b[key];
                }
            }
        }

        // Return the merged object
        return a;
    }

    /**
     * Returns a recursive clone of the passed object.
     * So when you edit the original the clone will not change.
     * Used in prototypical inheritance.
     * It will not clone arrays.
     *
     * @param {Object} orig The original object to clone.
     * @return {Object} The cloned version of orig that can be edited without changing the original.
     */
    function clone(orig) {
        // Initialise variables
        var cl = {}
          , key;

        // Loop over all values in the object
        // If the value is an object then clone recursively
        // Otherwise just copy the value
        for(key in orig) {
            if(orig.hasOwnProperty(key)) {
                cl[key] = isObject(orig[key]) ? clone(orig[key]) : orig[key];
            }
        }

        // Return the clone
        return cl;
    }

    /**
     * Inherits other functions prototype objects into the current function.
     *
     * @param {Function|Function[]} parent A function which should have it's prototype cloned and placed into the current functions prototype. If you pass an array of functions they will all be inherited from.
     * @param {Function} [fn] Optional function to use as the current function which is inheriting the other prototypes. It will default to `this`.
     * @return {Function} The current function to allow chaining.
     */
    function inherit(parent, fn) {
        // Set fn to this if it was not passed
        if(!fn) {
            fn = this;
        }

        // Return the current function to allow chaining
        return fn;
    }

    // Expose the inherit function by placing it in the Function prototype
    Function.prototype.inherit = inherit;

    // Create a nice little namespace to expose
    var ns = {
        isObject: isObject,
        merge: merge,
        clone: clone,
        inherit: inherit
    };

    // And expose everything else either via AMD or a global object
    if(typeof define === 'function' && define.amd) {
        define(function() {
            return ns;
        });
    }
    else {
        exports.ns = ns;
    }
}(this));<commit_msg>Wrote what I think is all I need for the inherit method. Wow, those helper functions really help.<commit_after>/**
 * Heir v0.0.0 - http://git.io/F87mKg
 * Oliver Caldwell
 * MIT license
 */

;(function(exports) {
    // JSHint config - http://www.jshint.com/
    /*jshint laxcomma:true*/
    /*global define:true*/

    // Place the script into strict mode
    'use strict';

    /**
     * Works out if a variable is a true object (created with {} etc) and not an array or anything else that usually shows up as an object.
     *
     * @param {Mixed} chk The variable to check to see if it is an object. It must be a pure object, not even a prototype.
     * @return {Boolean} True if it is a true object, false if it is anything else.
     */
    function isObject(chk) {
        return (chk && typeof chk === 'object' && Object.prototype.toString.call(chk) !== '[object Array]') === true;
    }

    /**
     * Recursively merges two objects. Object `a` will be overridden by the values in object `b`.
     * Please run the values through a cloning function first, this function does not try to clone them for you.
     * The base object will be edited directly, please be careful!
     *
     * @param {Object} a The base object to merge into.
     * @param {Object} b The object to merge down into object `a`.
     * @return {Object} This is object `a` but merged with `b`.
     */
    function merge(a, b) {
        // Loop over all values in b. If they are not found in a then set them
        // If both values are objects then recursively merge them
        for(var key in b) {
            // Make sure the value is not in __proto__ or something like that
            if(b.hasOwnProperty(key)) {
                // If they are both objects then merge recursively
                if(isObject(a[key]) && isObject(b[key])) {
                    merge(a[key], b[key]);
                }

                // Otherwise just replace the base value
                else {
                    a[key] = b[key];
                }
            }
        }

        // Return the merged object
        return a;
    }

    /**
     * Returns a recursive clone of the passed object.
     * So when you edit the original the clone will not change.
     * Used in prototypical inheritance.
     * It will not clone arrays.
     *
     * @param {Object} orig The original object to clone.
     * @return {Object} The cloned version of orig that can be edited without changing the original.
     */
    function clone(orig) {
        // Initialise variables
        var cl = {}
          , key;

        // Loop over all values in the object
        // If the value is an object then clone recursively
        // Otherwise just copy the value
        for(key in orig) {
            if(orig.hasOwnProperty(key)) {
                cl[key] = isObject(orig[key]) ? clone(orig[key]) : orig[key];
            }
        }

        // Return the clone
        return cl;
    }

    /**
     * Inherits other functions prototype objects into the current function.
     *
     * @param {Function|Function[]} parent A function which should have it's prototype cloned and placed into the current functions prototype. If you pass an array of functions they will all be inherited from.
     * @param {Function} [forceFn] Optional function to use as the current function which is inheriting the other prototypes. It will default to `this`.
     * @return {Function} The current function to allow chaining.
     */
    function inherit(parent, forceFn) {
        // Initialise variables
        var fn = forceFn || this
          , i;

        // If the parent variable is not a function then it must be an array
        // So we have to loop over it and inherit each of them
        // Remember to pass the current function instance!
        if(typeof parent !== 'function') {
            i = parent.length;
            while(i--) {
                inherit(parent[i], fn);
            }
        }
        else {
            // It is not an array, it is a plain function
            // Merge it's prototype into this one
            merge(fn.prototype, clone(parent.prototype));
        }

        // Return the current function to allow chaining
        return fn;
    }

    // Expose the inherit function by placing it in the Function prototype
    Function.prototype.inherit = inherit;

    // Create a nice little namespace to expose
    var ns = {
        isObject: isObject,
        merge: merge,
        clone: clone,
        inherit: inherit
    };

    // And expose everything else either via AMD or a global object
    if(typeof define === 'function' && define.amd) {
        define(function() {
            return ns;
        });
    }
    else {
        exports.ns = ns;
    }
}(this));<|endoftext|>


<commit_before>/* global define: false */
/* jshint browser: true */
define([
    '../Stopwatch',
    '../TrackingInfo'
], function(
    Stopwatch,
    TrackingInfo
) {

    'use strict';

    var marks = [],
        measures = [],

        hasLabel = function hasLabel(label) {
            return function isMatch(mark) {
                return mark.label === label;
            };
        },

        isProvided = function isProvided(value) {
            return value !== undefined && typeof value === 'string' && value.trim().length > 0;
        },

        getCounter = function getCounter(label) {
            return function counter(count, mark) {
                return count + ((mark.label === label) ? 1: 0);
            };
        },

        navStart = (function() {

            function exists(prev, key) {
                return !!prev && !!prev[key] ? prev[key] : null;
            }

            return ['performance', 'timing', 'navigationStart']
                .reduce(exists, window) || Stopwatch.now();

        })(),

        polyMark = function mark(name, data, ignoreBuiltIn) {

            var instance = {
                data: data,
                label: name,
                type: 'mark',
                start: navStart,
                stop: Stopwatch.now(),
                count: marks.reduce(getCounter(name), 1)
            };

            if (!ignoreBuiltIn && !!window.performance) {
                window.performance.mark(name);
            }

            marks.push(instance);

            return instance;

        },

        polyMeasure = function measure(name, start, stop, data, between) {

            var i, instance, mark1, mark2, curr,
                isStopMark = hasLabel(stop),
                isStartMark = hasLabel(start),
                startProvided = isProvided(start),
                stopProvided = isProvided(stop),

                shouldSetStartMark = function() {
                    return !mark1 && isStartMark(curr);
                },

                okayToSetStartMark = function() {
                    return !!mark2 || !stopProvided;
                },

                shouldSetStopMark = function() {
                    return !mark2 && isStopMark(curr);
                },

                bothMarksSet = function() {
                    return !!mark1 && !!mark2;
                };

            for (i = marks.length - 1; i >= 0; i--) {
                curr = marks[i];
                if (shouldSetStopMark()) {
                    mark2 = curr;
                } else if (shouldSetStartMark() && okayToSetStartMark()) {
                    mark1 = curr;
                }
                if (bothMarksSet()) {
                    break;
                }
            }

            if (!startProvided) {
                mark1 = { stop: navStart };
            }

            if (!stopProvided) {
                mark2 = { stop: Stopwatch.now() };
            }

            if (!!mark1 && !!mark2) {

                instance = {
                    data: data,
                    label: name,
                    type: 'measure',
                    start: mark1.stop,
                    stop: mark2.stop,
                    duration: mark2.stop - mark1.stop,
                    count: measures.reduce(getCounter(name), 1)
                };

                if (!!between) {
                    instance.children = marks.filter(function isBetween(mark) {
                        return mark.stop >= mark1.stop && mark.stop <= mark2.stop;
                    });
                }

                if (!!window.performance) {
                    // workaround for some versions of IE
                    // see https://connect.microsoft.com/IE/feedbackdetail/view/1884529/bug-in-html5-performance-api
                    if (!start) {
                        window.performance.measure(name);
                    } else if (!stop) {
                        window.performance.measure(name, start || undefined);
                    } else {
                        window.performance.measure(name, start || undefined, stop || undefined);
                    }
                }

                measures.push(instance);

                return instance;

            }

        },

        clearEntries = function clearEntries(rx, arr, fnName) {

            if (typeof rx === 'string') {
                rx = new RegExp(rx, 'i');
            } else if (rx === undefined) {
                rx = /./;
            } else if (!(rx instanceof RegExp)) {
                throw new Error('Regular expression or string expected.');
            }

            var test = function test(value) {
                    return rx.test(value.label);
                },

                fn = !!window.performance && !!window.performance[fnName] ?
                    window.performance[fnName].bind(window.performance) :
                    Function.prototype; // nop

            arr.filter(test).forEach(function remove(item) {
                if (arr !== marks || item.label !== 'navigationStart') {
                    fn(item.label);
                    arr.splice(arr.indexOf(item), 1);
                }
            });

        };

    /**
     * Provides the ability to mark specific moments in an application's
     * behavior, and to measure the time between any of those marks.
     * @class Marks
     */
    return function Marks(collect) {

        /**
         * Identifies a specific moment of time in the application
         * and records that moment as the time between navigation start
         * and the point this method is called. This provides a single
         * common baseline for all marks to be measured against.
         * @function Marks.set
         * @alias Marks.mark
         * @param {String} name The name of the mark to set.
         * @param {Object} [data] Optional properties to associate with
         *  the mark. This data will be used to provide properties to
         *  the {@link TrackingInfo} instance persisted to collectors.
         * @throws A mark name must be specified.
         * @example
         * Tracking.marks.set('app initialized');
         * Tracking.marks.set('generating DOM');
         */
        Marks.set = Marks.mark = function set(name, data) {

            if (typeof name !== 'string' || !name.length) {
                throw new Error('A mark name must be specified.');
            }

            collect(new TrackingInfo(polyMark(name, data)));

        };

        /**
         * Creates a new mark whose name is prepended with "Start: ".
         * This method is meant to be called in conjunction with
         * {@link Marks.stop} to create a pair of marks along with a
         * measure between them.
         * @function Marks.start
         * @param {String} name The name of the mark to set.
         * @param {Object} [data] Optional data to use to set properties
         *  on the {@link TrackingInfo} instance persisted to collectors.
         * @example
         * Tracking.marks.start('loading data');
         * $.getJSON('path/to/data')
         *   .success(function(data) {
         *     Tracking.marks.stop('loading data');
         *   });
         */
        Marks.start = function start(name, data) {
            Marks.set('Start: ' + name, data);

            // Convenience function
            return function stop() {
                return Marks.stop(name, data);
            };
        };

        /**
         * Creates a new mark whose name is prepended with "Stop: ".
         * This method is meant to be called in conjunction with
         * {@link Marks.start} to create a pair of marks along with a
         * measure between them.
         * @function Marks.stop
         * @param {String} name The name of the mark to set. This should
         *  match the name you passed to {@link Marks.start}.
         * @param {Object} [data] Optional data to use to set properties
         *  on the {@link TrackingInfo} instance persisted to collectors.
         * @param {Boolean} [between=false] Whether or not to include
         *  any marks set between calls to start() and stop() as children
         *  of the TrackingInfo measure that will be sent to collectors.
         * @example
         * Tracking.marks.start('loading data');
         * $.getJSON('path/to/data')
         *   .success(function(data) {
         *     Tracking.marks.stop('loading data', data, true);
         *   });
         */
        Marks.stop = function stop(name, data, between) {
            Marks.set('Stop: ' + name, data);
            Marks.measure(name, 'Start: ' + name, 'Stop: ' + name, data, between);
        };

        /**
         * Measures the time between 2 marks.
         * @function Marks.measure
         * @param {String} name The name of the measure to create.
         * @param {String} [start] The name of the first mark. If not
         *  specified, "navigationStart" will be used.
         * @param {String} [stop] The name of the second mark. If not
         *  specified, the current epoch time will be used.
         * @param {Object} [data] Optional object whose members will be
         *  used to set properties on the {@link TrackingInfo} instance
         *  sent to any registered collectors.
         * @param {Boolean} [between=false] Whether or not to include any
         *  marks set between the start and stop marks as children of the
         *  {@link TrackingInfo} measure that will be sent to collectors.
         * @throws Parameters `name`, `start`, and `stop` are required.
         * @example
         * Tracking.marks.set('begin load');
         * setTimeout(function() {
         *   // some long-running operation
         *   Tracking.marks.set('end load');
         *   Tracking.marks.measure('load time', 'begin load', 'end load', {
         *     category: 'loading', tags: ['network']
         *   })
         * });
         * @example
         * // navigationStart used as start mark if none specified:
         * Tracking.marks.measure('between navigation and stop mark', null, 'stop mark');
         * @example
         * // the current time will be used if no stop mark is specified:
         * Tracking.marks.measure('between start mark and now', 'start mark');
         * @example
         * // leave out start and stop marks to get navigation start to now:
         * Tracking.marks.measure('between navigationStart and now');
         */
        Marks.measure = function measure(name, start, stop, data, between) {

            if (typeof name !== 'string' || !name.length) {
                throw new Error('Parameter `name` is required.');
            }

            var instance = polyMeasure(name, start, stop, data, between);
            if (!!instance) {
                collect(new TrackingInfo(instance));
            }

        };

        /**
         * @function Marks.getMarks
         * @returns {Array} An array of mark instances.
         */
        Marks.getMarks = function getMarks() {
            return marks.concat();
        };

        /**
         * @function Marks.getMeasures
         * @returns {Array} An array of measure instances.
         */
        Marks.getMeasures = function getMeasures() {
            return measures.concat();
        };

        /**
         * Removes all marks matching the specified regular expression.
         * If no expression is provided, all marks will be removed.
         * @function Marks.clearMarks
         * @param {RegExp|String} [rx] Optional RegExp instance or
         *  regular expression pattern to use to narrow the list of
         *  marks to remove.
         * @example
         * Tracking.marks.clearMarks(/load/);
         * Tracking.marks.clearMarks('my-feature-name');
         */
        Marks.clearMarks = function clearMarks(rx) {
            clearEntries(rx, marks, 'clearMarks');
        };

        /**
         * Removes all measures matching the specified regular expression.
         * If no expression is provided, all measures will be removed.
         * @function Marks.clearMeasures
         * @param {RegExp|String} [rx] Optional RegExp instance or
         *  regular expression pattern to use to narrow the list of
         *  measures to remove.
         * @example
         * Tracking.marks.clearMeasures(/load/);
         * Tracking.marks.clearMeasures('my-feature-name');
         */
        Marks.clearMeasures = function clearMarks(rx) {
            clearEntries(rx, measures, 'clearMeasures');
        };

        /**
         * @private
         */
        Marks.reset = function reset() {
            measures = [];
            marks = [{
                data: {},
                count: 1,
                type: 'mark',
                stop: navStart,
                start: navStart,
                label: 'navigationStart'
            }];
        };

        Marks.navigationStart = navStart;

        Marks.reset();

        return Marks;

    };

});
<commit_msg>Updated comments<commit_after>/* global define: false */
/* jshint browser: true */
define([
    '../Stopwatch',
    '../TrackingInfo'
], function(
    Stopwatch,
    TrackingInfo
) {

    'use strict';

    var marks = [],
        measures = [],

        hasLabel = function hasLabel(label) {
            return function isMatch(mark) {
                return mark.label === label;
            };
        },

        isProvided = function isProvided(value) {
            return value !== undefined && typeof value === 'string' && value.trim().length > 0;
        },

        getCounter = function getCounter(label) {
            return function counter(count, mark) {
                return count + ((mark.label === label) ? 1: 0);
            };
        },

        navStart = (function() {

            function exists(prev, key) {
                return !!prev && !!prev[key] ? prev[key] : null;
            }

            return ['performance', 'timing', 'navigationStart']
                .reduce(exists, window) || Stopwatch.now();

        })(),

        polyMark = function mark(name, data, ignoreBuiltIn) {

            var instance = {
                data: data,
                label: name,
                type: 'mark',
                start: navStart,
                stop: Stopwatch.now(),
                count: marks.reduce(getCounter(name), 1)
            };

            if (!ignoreBuiltIn && !!window.performance) {
                window.performance.mark(name);
            }

            marks.push(instance);

            return instance;

        },

        polyMeasure = function measure(name, start, stop, data, between) {

            var i, instance, mark1, mark2, curr,
                isStopMark = hasLabel(stop),
                isStartMark = hasLabel(start),
                startProvided = isProvided(start),
                stopProvided = isProvided(stop),

                shouldSetStartMark = function() {
                    return !mark1 && isStartMark(curr);
                },

                okayToSetStartMark = function() {
                    return !!mark2 || !stopProvided;
                },

                shouldSetStopMark = function() {
                    return !mark2 && isStopMark(curr);
                },

                bothMarksSet = function() {
                    return !!mark1 && !!mark2;
                };

            for (i = marks.length - 1; i >= 0; i--) {
                curr = marks[i];
                if (shouldSetStopMark()) {
                    mark2 = curr;
                } else if (shouldSetStartMark() && okayToSetStartMark()) {
                    mark1 = curr;
                }
                if (bothMarksSet()) {
                    break;
                }
            }

            if (!startProvided) {
                mark1 = { stop: navStart };
            }

            if (!stopProvided) {
                mark2 = { stop: Stopwatch.now() };
            }

            if (!!mark1 && !!mark2) {

                instance = {
                    data: data,
                    label: name,
                    type: 'measure',
                    start: mark1.stop,
                    stop: mark2.stop,
                    duration: mark2.stop - mark1.stop,
                    count: measures.reduce(getCounter(name), 1)
                };

                if (!!between) {
                    instance.children = marks.filter(function isBetween(mark) {
                        return mark.stop >= mark1.stop && mark.stop <= mark2.stop;
                    });
                }

                if (!!window.performance) {
                    // workaround for some versions of IE
                    // see https://connect.microsoft.com/IE/feedbackdetail/view/1884529/bug-in-html5-performance-api
                    if (!start) {
                        window.performance.measure(name);
                    } else if (!stop) {
                        window.performance.measure(name, start || undefined);
                    } else {
                        window.performance.measure(name, start || undefined, stop || undefined);
                    }
                }

                measures.push(instance);

                return instance;

            }

        },

        clearEntries = function clearEntries(rx, arr, fnName) {

            if (typeof rx === 'string') {
                rx = new RegExp(rx, 'i');
            } else if (rx === undefined) {
                rx = /./;
            } else if (!(rx instanceof RegExp)) {
                throw new Error('Regular expression or string expected.');
            }

            var test = function test(value) {
                    return rx.test(value.label);
                },

                fn = !!window.performance && !!window.performance[fnName] ?
                    window.performance[fnName].bind(window.performance) :
                    Function.prototype; // nop

            arr.filter(test).forEach(function remove(item) {
                if (arr !== marks || item.label !== 'navigationStart') {
                    fn(item.label);
                    arr.splice(arr.indexOf(item), 1);
                }
            });

        };

    /**
     * Provides the ability to mark specific moments in an application's
     * behavior, and to measure the time between any of those marks.
     * @class Marks
     */
    return function Marks(collect) {

        /**
         * Identifies a specific moment of time in the application
         * and records that moment as the time between navigation start
         * and the point this method is called. This provides a single
         * common baseline for all marks to be measured against.
         * @function Marks.set
         * @alias Marks.mark
         * @param {String} name The name of the mark to set.
         * @param {Object} [data] Optional properties to associate with
         *  the mark. This data will be used to provide properties to
         *  the {@link TrackingInfo} instance persisted to collectors.
         * @throws A mark name must be specified.
         * @example
         * Tracking.marks.set('app initialized');
         * Tracking.marks.set('generating DOM');
         */
        Marks.set = Marks.mark = function set(name, data) {

            if (typeof name !== 'string' || !name.length) {
                throw new Error('A mark name must be specified.');
            }

            collect(new TrackingInfo(polyMark(name, data)));

        };

        /**
         * Creates a new mark whose name is prepended with "Start: ".
         * This method is meant to be called in conjunction with
         * {@link Marks.stop} to create a pair of marks along with a
         * measure between them.
         * @function Marks.start
         * @param {String} name The name of the mark to set.
         * @param {Object} [data] Optional data to use to set properties
         *  on the {@link TrackingInfo} instance persisted to collectors.
         * @returns {Function} A function which is the equivalent of Marks.stop(name, data);
         * @example
         * Tracking.marks.start('loading data');
         * $.getJSON('path/to/data')
         *   .success(function(data) {
         *     Tracking.marks.stop('loading data');
         *   });
         */
        Marks.start = function start(name, data) {
            Marks.set('Start: ' + name, data);

            // Convenience function
            return function stop() {
                return Marks.stop(name, data);
            };
        };

        /**
         * Creates a new mark whose name is prepended with "Stop: ".
         * This method is meant to be called in conjunction with
         * {@link Marks.start} to create a pair of marks along with a
         * measure between them.
         * @function Marks.stop
         * @param {String} name The name of the mark to set. This should
         *  match the name you passed to {@link Marks.start}.
         * @param {Object} [data] Optional data to use to set properties
         *  on the {@link TrackingInfo} instance persisted to collectors.
         * @param {Boolean} [between=false] Whether or not to include
         *  any marks set between calls to start() and stop() as children
         *  of the TrackingInfo measure that will be sent to collectors.
         * @example
         * Tracking.marks.start('loading data');
         * $.getJSON('path/to/data')
         *   .success(function(data) {
         *     Tracking.marks.stop('loading data', data, true);
         *   });
         */
        Marks.stop = function stop(name, data, between) {
            Marks.set('Stop: ' + name, data);
            Marks.measure(name, 'Start: ' + name, 'Stop: ' + name, data, between);
        };

        /**
         * Measures the time between 2 marks.
         * @function Marks.measure
         * @param {String} name The name of the measure to create.
         * @param {String} [start] The name of the first mark. If not
         *  specified, "navigationStart" will be used.
         * @param {String} [stop] The name of the second mark. If not
         *  specified, the current epoch time will be used.
         * @param {Object} [data] Optional object whose members will be
         *  used to set properties on the {@link TrackingInfo} instance
         *  sent to any registered collectors.
         * @param {Boolean} [between=false] Whether or not to include any
         *  marks set between the start and stop marks as children of the
         *  {@link TrackingInfo} measure that will be sent to collectors.
         * @throws Parameters `name`, `start`, and `stop` are required.
         * @example
         * Tracking.marks.set('begin load');
         * setTimeout(function() {
         *   // some long-running operation
         *   Tracking.marks.set('end load');
         *   Tracking.marks.measure('load time', 'begin load', 'end load', {
         *     category: 'loading', tags: ['network']
         *   })
         * });
         * @example
         * // navigationStart used as start mark if none specified:
         * Tracking.marks.measure('between navigation and stop mark', null, 'stop mark');
         * @example
         * // the current time will be used if no stop mark is specified:
         * Tracking.marks.measure('between start mark and now', 'start mark');
         * @example
         * // leave out start and stop marks to get navigation start to now:
         * Tracking.marks.measure('between navigationStart and now');
         */
        Marks.measure = function measure(name, start, stop, data, between) {

            if (typeof name !== 'string' || !name.length) {
                throw new Error('Parameter `name` is required.');
            }

            var instance = polyMeasure(name, start, stop, data, between);
            if (!!instance) {
                collect(new TrackingInfo(instance));
            }

        };

        /**
         * @function Marks.getMarks
         * @returns {Array} An array of mark instances.
         */
        Marks.getMarks = function getMarks() {
            return marks.concat();
        };

        /**
         * @function Marks.getMeasures
         * @returns {Array} An array of measure instances.
         */
        Marks.getMeasures = function getMeasures() {
            return measures.concat();
        };

        /**
         * Removes all marks matching the specified regular expression.
         * If no expression is provided, all marks will be removed.
         * @function Marks.clearMarks
         * @param {RegExp|String} [rx] Optional RegExp instance or
         *  regular expression pattern to use to narrow the list of
         *  marks to remove.
         * @example
         * Tracking.marks.clearMarks(/load/);
         * Tracking.marks.clearMarks('my-feature-name');
         */
        Marks.clearMarks = function clearMarks(rx) {
            clearEntries(rx, marks, 'clearMarks');
        };

        /**
         * Removes all measures matching the specified regular expression.
         * If no expression is provided, all measures will be removed.
         * @function Marks.clearMeasures
         * @param {RegExp|String} [rx] Optional RegExp instance or
         *  regular expression pattern to use to narrow the list of
         *  measures to remove.
         * @example
         * Tracking.marks.clearMeasures(/load/);
         * Tracking.marks.clearMeasures('my-feature-name');
         */
        Marks.clearMeasures = function clearMarks(rx) {
            clearEntries(rx, measures, 'clearMeasures');
        };

        /**
         * @private
         */
        Marks.reset = function reset() {
            measures = [];
            marks = [{
                data: {},
                count: 1,
                type: 'mark',
                stop: navStart,
                start: navStart,
                label: 'navigationStart'
            }];
        };

        Marks.navigationStart = navStart;

        Marks.reset();

        return Marks;

    };

});
<|endoftext|>