<commit_before>/** @license MIT License (c) copyright B Cavalier & J Hann */

/**
 * meld
 * Aspect Oriented Programming for Javascript
 *
 * meld is part of the cujo.js family of libraries (http://cujojs.com/)
 *
 * Licensed under the MIT License at:
 * http://www.opensource.org/licenses/mit-license.php
 *
 * @version 0.8.0
 */
(function (define) {
define(function () {

	var ap, prepend, append, iterators, slice, isArray, defineProperty, freeze;
	freeze = Object.freeze || function (o) { return o; };

	ap      = Array.prototype;
	prepend = ap.unshift;
	append  = ap.push;
	slice   = ap.slice;

	isArray = Array.isArray || function(it) {
		return Object.prototype.toString.call(it) == '[object Array]';
	};

	defineProperty = Object.defineProperty || function(obj, prop, descriptor) {
		obj[prop] = descriptor.value;
	};

	iterators = {
		// Before uses reverse iteration
		before: forEachReverse,
		around: false
	};

	// All other advice types use forward iteration
	// Around is a special case that uses recursion rather than
	// iteration.  See Advisor._callAroundAdvice
	iterators.on
		= iterators.afterReturning
		= iterators.afterThrowing
		= iterators.after
		= forEach;

	function Advisor(target, func) {

		var orig, advisor, advised;

		this.target = target;
		this.func = func;
		this.aspects = {};

		orig = this.orig = target[func];
		advisor = this;

		advised = this.advised = function() {
			var context, args, callOrig, result, afterType, exception;

			// If called as a constructor (i.e. using "new"), create a context
			// of the correct type, so that all advice types (including before!)
			// are called with the correct context.
			// NOTE: Requires ES5 Object.create()
			if(this instanceof advised) {
				// shamelessly derived from https://github.com/cujojs/wire/blob/c7c55fe50238ecb4afbb35f902058ab6b32beb8f/lib/component.js#L25
				if (!Object.create) {
					throw new Error('An ES5 environment is required for advice on constructors');
				}

				context = Object.create(orig.prototype);
				callOrig = function (args) {
					return applyConstructor(orig, context, args);
				};

			} else {
				context = this;
				callOrig = function(args) {
					return orig.apply(context, args);
				};

			}

			args = slice.call(arguments);
			afterType = 'afterReturning';

			advisor._callSimpleAdvice('before', context, args);

			try {
				result = advisor._callAroundAdvice(context, func, args, callOrigAndOn);
			} catch(e) {
				result = exception = e;
				// Switch to afterThrowing
				afterType = 'afterThrowing';
			}

			args = [result];

			callAfter(afterType, args);
			callAfter('after', args);

			if(exception) {
				throw exception;
			}

			return result;

			function callOrigAndOn(args) {
				var result = callOrig(args);
				advisor._callSimpleAdvice('on', context, args);

				return result;
			}

			function callAfter(afterType, args) {
				advisor._callSimpleAdvice(afterType, context, args);
			}
		};

		defineProperty(advised, '_advisor', { value: advisor });
	}

	Advisor.prototype = {

		   /**
			* Invoke all advice functions in the supplied context, with the supplied args
			*
			* @param adviceType
			* @param context
			* @param args
			*/
		_callSimpleAdvice: function(adviceType, context, args) {

			// before advice runs LIFO, from most-recently added to least-recently added.
			// All other advice is FIFO
			var iterator, advices;

			advices = this.aspects[adviceType];
			if(!advices) {
				return;
			}

			iterator = iterators[adviceType];

			iterator(this.aspects[adviceType], function(aspect) {
				var advice = aspect.advice;
				advice && advice.apply(context, args);
			});
		},

		/**
		 * Invoke all around advice and then the original method
		 *
		 * @param context
		 * @param method
		 * @param args
		 * @param applyOriginal
		 */
		_callAroundAdvice: function (context, method, args, applyOriginal) {
			var len, aspects;

			aspects = this.aspects.around;
			len = aspects ? aspects.length : 0;

			/**
			 * Call the next function in the around chain, which will either be another around
			 * advice, or the orig method.
			 * @param i {Number} index of the around advice
			 * @param args {Array} arguments with with to call the next around advice
			 */
			function callNext(i, args) {
				// If we exhausted all aspects, finally call the original
				// Otherwise, if we found another around, call it
				return i < 0
					? applyOriginal(args)
					: callAround(aspects[i].advice, i, args);
			}

			function callAround(around, i, args) {
				var proceedCalled, joinpoint;

				proceedCalled = 0;

				// Joinpoint is immutable
				joinpoint = freeze({
					target: context,
					method: method,
					args: args,
					proceed: proceedCall,
					proceedApply: proceedApply,
					proceedCount: proceedCount
				});

				// Call supplied around advice function
				return around.call(context, joinpoint);

				/**
				 * The number of times proceed() has been called
				 * @return {Number}
				 */
				function proceedCount() {
					return proceedCalled;
				}

				/**
				 * Proceed to the original method/function or the next around
				 * advice using original arguments or new argument list if
				 * arguments.length > 0
				 * @return {*} result of original method/function or next around advice
				 */
				function proceedCall(/* newArg1, newArg2... */) {
					return proceed(arguments.length > 0 ? slice.call(arguments) : args);
				}

				/**
				 * Proceed to the original method/function or the next around
				 * advice using original arguments or new argument list if
				 * newArgs is supplied
				 * @param [newArgs] {Array} new arguments with which to proceed
				 * @return {*} result of original method/function or next around advice
				 */
				function proceedApply(newArgs) {
					return proceed(newArgs || args);
				}

				/**
				 * Create proceed function that calls the next around advice, or
				 * the original.  May be called multiple times, for example, in retry
				 * scenarios
				 * @param [args] {Array} optional arguments to use instead of the
				 * original arguments
				 */
				function proceed(args) {
					proceedCalled++;
					return callNext(i - 1, args);
				}

			}

			return callNext(len - 1, args);
		},

		/**
		 * Adds the supplied aspect to the advised target method
		 *
		 * @param aspect
		 */
		add: function(aspect) {

			var advisor, aspects;

			advisor = this;
			aspects = advisor.aspects;

			insertAspect(aspects, aspect);

			return {
				remove: function () {
					var remaining = removeAspect(aspects, aspect);

					// If there are no aspects left, restore the original method
					if (!remaining) {
						advisor.remove();
					}
				}
			};
		},

		/**
		 * Removes the Advisor and thus, all aspects from the advised target method, and
		 * restores the original target method, copying back all properties that may have
		 * been added or updated on the advised function.
		 */
		remove: function () {
			delete this.advised._advisor;
			this.target[this.func] = this.orig;
		}
	};

	/**
	 * Returns the advisor for the target object-function pair.  A new advisor
	 * will be created if one does not already exist.
	 * @param target {*} target containing a method with tthe supplied methodName
	 * @param methodName {String} name of method on target for which to get an advisor
	 * @return {Object} existing or newly created advisor for the supplied method
	 */
	Advisor.get = function(target, methodName) {
		if(!(methodName in target)) {
			return;
		}

		var advisor, advised;

		advised = target[methodName];

		if(typeof advised !== 'function') {
			throw new Error('Advice can only be applied to functions: ' + methodName);
		}

		advisor = advised._advisor;
		if(!advisor) {
			advisor = new Advisor(target, methodName);
			target[methodName] = advisor.advised;
		}

		return advisor;
	};

	//
	// Public API
	//

	return {
		// General add aspect
		// Returns a function that will remove the newly-added aspect
		add:            addAspect,

		// Add a single, specific type of advice
		// returns a function that will remove the newly-added advice
		before:         adviceApi('before'),
		around:         adviceApi('around'),
		on:             adviceApi('on'),
		afterReturning: adviceApi('afterReturning'),
		afterThrowing:  adviceApi('afterThrowing'),
		after:          adviceApi('after')
	};

	function addAspect(target, pointcut, aspect) {
		// pointcut can be: string, Array of strings, RegExp, Function(targetObject): Array of strings
		// advice can be: object, Function(targetObject, targetMethodName)

		var pointcutType, remove;

		if(arguments.length < 3) {
			return addAspectToFunction(target, pointcut);
		} else {
			target = getPointcutTarget(target);

			if (isArray(pointcut)) {
				remove = addAspectToAll(target, pointcut, aspect);

			} else {
				pointcutType = typeof pointcut;

				if (pointcutType === 'string') {
					if (typeof target[pointcut] === 'function') {
						remove = addAspectToMethod(target, pointcut, aspect);
					}

				} else if (pointcutType === 'function') {
					remove = addAspectToAll(target, pointcut(target), aspect);

				} else {
					remove = addAspectToMatches(target, pointcut, aspect);
				}
			}

			return remove;
		}

	}

	/**
	 * Add an aspect to a pure function, returning an advised version of it.
	 * NOTE: *only the returned function* is advised.  The original (input) function
	 * is not modified in any way.
	 * @param func {Function} function to advise
	 * @param aspect {Object} aspect to add
	 * @return {Function} advised function
	 */
	function addAspectToFunction(func, aspect) {
		var name, placeholderTarget;

		name = func.name || '_';

		placeholderTarget = {};
		placeholderTarget[name] = func;

		addAspectToMethod(placeholderTarget, name, aspect);

		return placeholderTarget[name];

	}

	function addAspectToMethod(target, method, aspect) {
		var advisor = Advisor.get(target, method);

		return advisor && advisor.add(aspect);
	}

	function addAspectToAll(target, methodArray, aspect) {
		var removers, added, f, i;

		removers = [];
		i = 0;

		while((f = methodArray[i++])) {
			added = addAspectToMethod(target, f, aspect);
			added && removers.push(added);
		}

		return createRemover(removers);
	}

	function addAspectToMatches(target, pointcut, aspect) {
		var removers = [];
		// Assume the pointcut is a an object with a .test() method
		for (var p in target) {
			// TODO: Decide whether hasOwnProperty is correct here
			// Only apply to own properties that are functions, and match the pointcut regexp
			if (typeof target[p] == 'function' && pointcut.test(p)) {
				// if(object.hasOwnProperty(p) && typeof object[p] === 'function' && pointcut.test(p)) {
				removers.push(addAspectToMethod(target, p, aspect));
			}
		}

		return createRemover(removers);
	}

	function createRemover(removers) {
		return {
			remove: function() {
				for (var i = removers.length - 1; i >= 0; --i) {
					removers[i].remove();
				}
			}
		};
	}

	function getPointcutTarget(target) {
		return typeof target == 'function' ? target.prototype||target : target;
	}

	// Create an API function for the specified advice type
	function adviceApi(type) {
		return function(target, method, adviceFunc) {
			var aspect = {};

			if(arguments.length === 2) {
				aspect[type] = method;
				return addAspect(target, aspect);
			} else {
				aspect[type] = adviceFunc;
				return addAspect(target, method, aspect);
			}
		};
	}

	/**
	 * Insert the supplied aspect into aspectList
	 * @param aspectList {Object} list of aspects, categorized by advice type
	 * @param aspect {Object} aspect containing one or more supported advice types
	 */
	function insertAspect(aspectList, aspect) {
		var adviceType, advice, advices;

		for(adviceType in iterators) {
			advice = aspect[adviceType];

			if(advice) {
				advices = aspectList[adviceType];
				if(!advices) {
					aspectList[adviceType] = advices = [];
				}

				advices.push({
					aspect: aspect,
					advice: advice
				});
			}
		}
	}

	/**
	 * Remove the supplied aspect from aspectList
	 * @param aspectList {Object} list of aspects, categorized by advice type
	 * @param aspect {Object} aspect containing one or more supported advice types
	 * @return {Number} Number of *advices* left on the advised function.  If
	 *  this returns zero, then it is safe to remove the advisor completely.
	 */
	function removeAspect(aspectList, aspect) {
		var adviceType, advices, remaining;

		remaining = 0;

		for(adviceType in iterators) {
			advices = aspectList[adviceType];
			if(advices) {
				remaining += advices.length;

				for (var i = advices.length - 1; i >= 0; --i) {
					if (advices[i].aspect === aspect) {
						advices.splice(i, 1);
						--remaining;
						break;
					}
				}
			}
		}

		return remaining;
	}

	function applyConstructor(C, instance, args) {
		try {
			Object.defineProperty(instance, 'constructor', {
				value: C,
				enumerable: false
			});
		} catch(e) {
			// ignore
		}

		C.apply(instance, args);

		return instance;
	}

	function forEach(array, func) {
		for (var i = 0, len = array.length; i < len; i++) {
			func(array[i]);
		}
	}

	function forEachReverse(array, func) {
		for (var i = array.length - 1; i >= 0; --i) {
			func(array[i]);
		}
	}

});
})(typeof define == 'function'
	? define
	: function (factory) { typeof module != 'undefined'
		? (module.exports = factory())
		: (this.meld = factory());
	}
	// Boilerplate for AMD, Node, and browser global
);
<commit_msg>Tweak UMD boilerplate<commit_after>/** @license MIT License (c) copyright B Cavalier & J Hann */

/**
 * meld
 * Aspect Oriented Programming for Javascript
 *
 * meld is part of the cujo.js family of libraries (http://cujojs.com/)
 *
 * Licensed under the MIT License at:
 * http://www.opensource.org/licenses/mit-license.php
 *
 * @version 0.8.0
 */
(function (define) {
define(function () {

	var ap, prepend, append, iterators, slice, isArray, defineProperty, freeze;
	freeze = Object.freeze || function (o) { return o; };

	ap      = Array.prototype;
	prepend = ap.unshift;
	append  = ap.push;
	slice   = ap.slice;

	isArray = Array.isArray || function(it) {
		return Object.prototype.toString.call(it) == '[object Array]';
	};

	defineProperty = Object.defineProperty || function(obj, prop, descriptor) {
		obj[prop] = descriptor.value;
	};

	iterators = {
		// Before uses reverse iteration
		before: forEachReverse,
		around: false
	};

	// All other advice types use forward iteration
	// Around is a special case that uses recursion rather than
	// iteration.  See Advisor._callAroundAdvice
	iterators.on
		= iterators.afterReturning
		= iterators.afterThrowing
		= iterators.after
		= forEach;

	function Advisor(target, func) {

		var orig, advisor, advised;

		this.target = target;
		this.func = func;
		this.aspects = {};

		orig = this.orig = target[func];
		advisor = this;

		advised = this.advised = function() {
			var context, args, callOrig, result, afterType, exception;

			// If called as a constructor (i.e. using "new"), create a context
			// of the correct type, so that all advice types (including before!)
			// are called with the correct context.
			// NOTE: Requires ES5 Object.create()
			if(this instanceof advised) {
				// shamelessly derived from https://github.com/cujojs/wire/blob/c7c55fe50238ecb4afbb35f902058ab6b32beb8f/lib/component.js#L25
				if (!Object.create) {
					throw new Error('An ES5 environment is required for advice on constructors');
				}

				context = Object.create(orig.prototype);
				callOrig = function (args) {
					return applyConstructor(orig, context, args);
				};

			} else {
				context = this;
				callOrig = function(args) {
					return orig.apply(context, args);
				};

			}

			args = slice.call(arguments);
			afterType = 'afterReturning';

			advisor._callSimpleAdvice('before', context, args);

			try {
				result = advisor._callAroundAdvice(context, func, args, callOrigAndOn);
			} catch(e) {
				result = exception = e;
				// Switch to afterThrowing
				afterType = 'afterThrowing';
			}

			args = [result];

			callAfter(afterType, args);
			callAfter('after', args);

			if(exception) {
				throw exception;
			}

			return result;

			function callOrigAndOn(args) {
				var result = callOrig(args);
				advisor._callSimpleAdvice('on', context, args);

				return result;
			}

			function callAfter(afterType, args) {
				advisor._callSimpleAdvice(afterType, context, args);
			}
		};

		defineProperty(advised, '_advisor', { value: advisor });
	}

	Advisor.prototype = {

		   /**
			* Invoke all advice functions in the supplied context, with the supplied args
			*
			* @param adviceType
			* @param context
			* @param args
			*/
		_callSimpleAdvice: function(adviceType, context, args) {

			// before advice runs LIFO, from most-recently added to least-recently added.
			// All other advice is FIFO
			var iterator, advices;

			advices = this.aspects[adviceType];
			if(!advices) {
				return;
			}

			iterator = iterators[adviceType];

			iterator(this.aspects[adviceType], function(aspect) {
				var advice = aspect.advice;
				advice && advice.apply(context, args);
			});
		},

		/**
		 * Invoke all around advice and then the original method
		 *
		 * @param context
		 * @param method
		 * @param args
		 * @param applyOriginal
		 */
		_callAroundAdvice: function (context, method, args, applyOriginal) {
			var len, aspects;

			aspects = this.aspects.around;
			len = aspects ? aspects.length : 0;

			/**
			 * Call the next function in the around chain, which will either be another around
			 * advice, or the orig method.
			 * @param i {Number} index of the around advice
			 * @param args {Array} arguments with with to call the next around advice
			 */
			function callNext(i, args) {
				// If we exhausted all aspects, finally call the original
				// Otherwise, if we found another around, call it
				return i < 0
					? applyOriginal(args)
					: callAround(aspects[i].advice, i, args);
			}

			function callAround(around, i, args) {
				var proceedCalled, joinpoint;

				proceedCalled = 0;

				// Joinpoint is immutable
				joinpoint = freeze({
					target: context,
					method: method,
					args: args,
					proceed: proceedCall,
					proceedApply: proceedApply,
					proceedCount: proceedCount
				});

				// Call supplied around advice function
				return around.call(context, joinpoint);

				/**
				 * The number of times proceed() has been called
				 * @return {Number}
				 */
				function proceedCount() {
					return proceedCalled;
				}

				/**
				 * Proceed to the original method/function or the next around
				 * advice using original arguments or new argument list if
				 * arguments.length > 0
				 * @return {*} result of original method/function or next around advice
				 */
				function proceedCall(/* newArg1, newArg2... */) {
					return proceed(arguments.length > 0 ? slice.call(arguments) : args);
				}

				/**
				 * Proceed to the original method/function or the next around
				 * advice using original arguments or new argument list if
				 * newArgs is supplied
				 * @param [newArgs] {Array} new arguments with which to proceed
				 * @return {*} result of original method/function or next around advice
				 */
				function proceedApply(newArgs) {
					return proceed(newArgs || args);
				}

				/**
				 * Create proceed function that calls the next around advice, or
				 * the original.  May be called multiple times, for example, in retry
				 * scenarios
				 * @param [args] {Array} optional arguments to use instead of the
				 * original arguments
				 */
				function proceed(args) {
					proceedCalled++;
					return callNext(i - 1, args);
				}

			}

			return callNext(len - 1, args);
		},

		/**
		 * Adds the supplied aspect to the advised target method
		 *
		 * @param aspect
		 */
		add: function(aspect) {

			var advisor, aspects;

			advisor = this;
			aspects = advisor.aspects;

			insertAspect(aspects, aspect);

			return {
				remove: function () {
					var remaining = removeAspect(aspects, aspect);

					// If there are no aspects left, restore the original method
					if (!remaining) {
						advisor.remove();
					}
				}
			};
		},

		/**
		 * Removes the Advisor and thus, all aspects from the advised target method, and
		 * restores the original target method, copying back all properties that may have
		 * been added or updated on the advised function.
		 */
		remove: function () {
			delete this.advised._advisor;
			this.target[this.func] = this.orig;
		}
	};

	/**
	 * Returns the advisor for the target object-function pair.  A new advisor
	 * will be created if one does not already exist.
	 * @param target {*} target containing a method with tthe supplied methodName
	 * @param methodName {String} name of method on target for which to get an advisor
	 * @return {Object} existing or newly created advisor for the supplied method
	 */
	Advisor.get = function(target, methodName) {
		if(!(methodName in target)) {
			return;
		}

		var advisor, advised;

		advised = target[methodName];

		if(typeof advised !== 'function') {
			throw new Error('Advice can only be applied to functions: ' + methodName);
		}

		advisor = advised._advisor;
		if(!advisor) {
			advisor = new Advisor(target, methodName);
			target[methodName] = advisor.advised;
		}

		return advisor;
	};

	//
	// Public API
	//

	return {
		// General add aspect
		// Returns a function that will remove the newly-added aspect
		add:            addAspect,

		// Add a single, specific type of advice
		// returns a function that will remove the newly-added advice
		before:         adviceApi('before'),
		around:         adviceApi('around'),
		on:             adviceApi('on'),
		afterReturning: adviceApi('afterReturning'),
		afterThrowing:  adviceApi('afterThrowing'),
		after:          adviceApi('after')
	};

	function addAspect(target, pointcut, aspect) {
		// pointcut can be: string, Array of strings, RegExp, Function(targetObject): Array of strings
		// advice can be: object, Function(targetObject, targetMethodName)

		var pointcutType, remove;

		if(arguments.length < 3) {
			return addAspectToFunction(target, pointcut);
		} else {
			target = getPointcutTarget(target);

			if (isArray(pointcut)) {
				remove = addAspectToAll(target, pointcut, aspect);

			} else {
				pointcutType = typeof pointcut;

				if (pointcutType === 'string') {
					if (typeof target[pointcut] === 'function') {
						remove = addAspectToMethod(target, pointcut, aspect);
					}

				} else if (pointcutType === 'function') {
					remove = addAspectToAll(target, pointcut(target), aspect);

				} else {
					remove = addAspectToMatches(target, pointcut, aspect);
				}
			}

			return remove;
		}

	}

	/**
	 * Add an aspect to a pure function, returning an advised version of it.
	 * NOTE: *only the returned function* is advised.  The original (input) function
	 * is not modified in any way.
	 * @param func {Function} function to advise
	 * @param aspect {Object} aspect to add
	 * @return {Function} advised function
	 */
	function addAspectToFunction(func, aspect) {
		var name, placeholderTarget;

		name = func.name || '_';

		placeholderTarget = {};
		placeholderTarget[name] = func;

		addAspectToMethod(placeholderTarget, name, aspect);

		return placeholderTarget[name];

	}

	function addAspectToMethod(target, method, aspect) {
		var advisor = Advisor.get(target, method);

		return advisor && advisor.add(aspect);
	}

	function addAspectToAll(target, methodArray, aspect) {
		var removers, added, f, i;

		removers = [];
		i = 0;

		while((f = methodArray[i++])) {
			added = addAspectToMethod(target, f, aspect);
			added && removers.push(added);
		}

		return createRemover(removers);
	}

	function addAspectToMatches(target, pointcut, aspect) {
		var removers = [];
		// Assume the pointcut is a an object with a .test() method
		for (var p in target) {
			// TODO: Decide whether hasOwnProperty is correct here
			// Only apply to own properties that are functions, and match the pointcut regexp
			if (typeof target[p] == 'function' && pointcut.test(p)) {
				// if(object.hasOwnProperty(p) && typeof object[p] === 'function' && pointcut.test(p)) {
				removers.push(addAspectToMethod(target, p, aspect));
			}
		}

		return createRemover(removers);
	}

	function createRemover(removers) {
		return {
			remove: function() {
				for (var i = removers.length - 1; i >= 0; --i) {
					removers[i].remove();
				}
			}
		};
	}

	function getPointcutTarget(target) {
		return typeof target == 'function' ? target.prototype||target : target;
	}

	// Create an API function for the specified advice type
	function adviceApi(type) {
		return function(target, method, adviceFunc) {
			var aspect = {};

			if(arguments.length === 2) {
				aspect[type] = method;
				return addAspect(target, aspect);
			} else {
				aspect[type] = adviceFunc;
				return addAspect(target, method, aspect);
			}
		};
	}

	/**
	 * Insert the supplied aspect into aspectList
	 * @param aspectList {Object} list of aspects, categorized by advice type
	 * @param aspect {Object} aspect containing one or more supported advice types
	 */
	function insertAspect(aspectList, aspect) {
		var adviceType, advice, advices;

		for(adviceType in iterators) {
			advice = aspect[adviceType];

			if(advice) {
				advices = aspectList[adviceType];
				if(!advices) {
					aspectList[adviceType] = advices = [];
				}

				advices.push({
					aspect: aspect,
					advice: advice
				});
			}
		}
	}

	/**
	 * Remove the supplied aspect from aspectList
	 * @param aspectList {Object} list of aspects, categorized by advice type
	 * @param aspect {Object} aspect containing one or more supported advice types
	 * @return {Number} Number of *advices* left on the advised function.  If
	 *  this returns zero, then it is safe to remove the advisor completely.
	 */
	function removeAspect(aspectList, aspect) {
		var adviceType, advices, remaining;

		remaining = 0;

		for(adviceType in iterators) {
			advices = aspectList[adviceType];
			if(advices) {
				remaining += advices.length;

				for (var i = advices.length - 1; i >= 0; --i) {
					if (advices[i].aspect === aspect) {
						advices.splice(i, 1);
						--remaining;
						break;
					}
				}
			}
		}

		return remaining;
	}

	function applyConstructor(C, instance, args) {
		try {
			Object.defineProperty(instance, 'constructor', {
				value: C,
				enumerable: false
			});
		} catch(e) {
			// ignore
		}

		C.apply(instance, args);

		return instance;
	}

	function forEach(array, func) {
		for (var i = 0, len = array.length; i < len; i++) {
			func(array[i]);
		}
	}

	function forEachReverse(array, func) {
		for (var i = array.length - 1; i >= 0; --i) {
			func(array[i]);
		}
	}

});
})(typeof define == 'function' && define.amd
	? define
	: function (factory) { typeof exports == 'object'
		? (module.exports = factory())
		: (this.meld = factory());
	}
	// Boilerplate for AMD, CommonJS, and browser global
);
<|endoftext|>


<commit_before>'use strict';

var Promise = require('bluebird'),
	Utility = require('../utility'),
	config = require('../../config.json'),
	nodePath = require('path'),
	fse = Promise.promisify(require('fs-extra')),
	pathe = require('path-extra'),
	fs = require('fs'),
	md5 = require('MD5');

module.exports = function (name, path) {
	var templatePath = nodePath.resolve(__dirname, '../../templates/umbraco'),
		umbracoZipPath = pathe.datadir('rtk/umbraco/' + md5(config.umbraco) + '.zip'),
		websitePath = nodePath.resolve(path, name + '.Website');

	return fse.mkdirsAsync(nodePath.dirname(umbracoZipPath))
		.then(function () {
			return fse.copyAsync(templatePath, path);
		})
		.then(function () {
			/*
			 Find and replace all instances of 'RHYTHM' in the
			 directory names, file names, and file contents
			 */
			Utility.FileSystem.findAndReplace(path, 'RHYTHM', name);

			// Create the directory we'll store umbraco in if it doesn't exist
			Utility.Logging.info('Creating umbraco project directory');
			return fse.mkdirsAsync(websitePath);
		})
		.then(function () {
			if (fs.existsSync(umbracoZipPath)) {
				return Promise.fulfilled();
			} else {
				// Download umbraco
				Utility.Logging.info('Downloading umbraco from ' + config.umbraco);
				return Utility.Net.download(config.umbraco, umbracoZipPath);
			}
		})
		.then(function () {
			// Wait for 1 second before trying to extract to ensure the file is fully downloaded
			return Utility.Process.sleep(1000);
		})
		.then(function () {
			// Extracting umbraco to our newly created directory
			Utility.Logging.info('Extracting umbraco');
			return Utility.Compression.extract(umbracoZipPath, websitePath);
		})
		.then(function () {
			// Create .gitkeep files
			Utility.Logging.info('Creating .gitkeep files');
			return Utility.FileSystem.createGitKeepFiles(websitePath);
		});
};<commit_msg>Windows is buggy, adjusting timing.<commit_after>'use strict';

var Promise = require('bluebird'),
	Utility = require('../utility'),
	config = require('../../config.json'),
	nodePath = require('path'),
	fse = Promise.promisify(require('fs-extra')),
	pathe = require('path-extra'),
	fs = require('fs'),
	md5 = require('MD5');

module.exports = function (name, path) {
	var templatePath = nodePath.resolve(__dirname, '../../templates/umbraco'),
		umbracoZipPath = pathe.datadir('rtk/umbraco/' + md5(config.umbraco) + '.zip'),
		websitePath = nodePath.resolve(path, name + '.Website');

	return fse.mkdirsAsync(nodePath.dirname(umbracoZipPath))
		.then(function () {
			return fse.copyAsync(templatePath, path);
		})
		.then(function () {
			/*
			 Find and replace all instances of 'RHYTHM' in the
			 directory names, file names, and file contents
			 */
			Utility.FileSystem.findAndReplace(path, 'RHYTHM', name);

			// Create the directory we'll store umbraco in if it doesn't exist
			Utility.Logging.info('Creating umbraco project directory');
			return fse.mkdirsAsync(websitePath);
		})
		.then(function () {
			if (fs.existsSync(umbracoZipPath)) {
				return Promise.fulfilled();
			} else {
				// Download umbraco
				Utility.Logging.info('Downloading umbraco from ' + config.umbraco);
				return Utility.Net.download(config.umbraco, umbracoZipPath);
			}
		})
		.then(function () {
			// Wait for 1 second before trying to extract to ensure the file is fully downloaded
			return Utility.Process.sleep(3000);
		})
		.then(function () {
			// Extracting umbraco to our newly created directory
			Utility.Logging.info('Extracting umbraco');
			return Utility.Compression.extract(umbracoZipPath, websitePath);
		})
		.then(function () {
			// Create .gitkeep files
			Utility.Logging.info('Creating .gitkeep files');
			return Utility.FileSystem.createGitKeepFiles(websitePath);
		});
};<|endoftext|>


<commit_before>'use babel'

const {Point, Disposable} = require('atom')
const _ = require('underscore-plus')
const ProviderBase = require('./provider-base')
const Path = require('path')
const fs = require('fs-plus')
const {limitNumber} = require('../utils')

function getModifiedFilePathsForRepository (repository) {
  const dir = repository.getWorkingDirectory()
  return Object.keys(repository.statuses)
    .map(filePath => Path.join(dir, filePath))
    .filter(fs.isFileSync)
}

function itemForGitDiff (diff, {buffer, filePath}) {
  const row = limitNumber(diff.newStart - 1, {min: 0})
  const lineText = buffer.lineForRow(row)
  return {
    point: new Point(row, lineText.match(/\s*/)[0].length),
    text: lineText,
    filePath: filePath
  }
}

async function getItemsForFilePath (repo, filePath) {
  const existingBuffer = atom.project.findBufferForPath(filePath)
  const buffer = existingBuffer || (await atom.project.buildBuffer(filePath))
  const diffs = repo.getLineDiffs(filePath, buffer.getText())
  // When file was completely new file, getLineDiffs return null, so need guard.
  const result = diffs ? diffs.map(diff => itemForGitDiff(diff, {buffer, filePath})) : []
  if (!existingBuffer) buffer.destroy()

  return result
}

// Borrowed from git-diff core pacakge.
function repositoryForPath (filePath) {
  const directories = atom.project.getDirectories()
  for (let i = 0; i < directories.length; i++) {
    const directory = directories[i]
    if (filePath === directory.getPath() || directory.contains(filePath)) {
      return atom.project.getRepositories()[i]
    }
  }
}

const providerConfig = {
  showProjectHeader: true,
  showFileHeader: true,
  supportCacheItems: true,
  supportFilePathOnlyItemsUpdate: true,
  refreshOnDidStopChanging: true
}

module.exports = class GitDiffAll extends ProviderBase {
  constructor (...args) {
    super(...args)
    Object.assign(this, providerConfig)
    this.enabledInlineGitDiff = new Set()
    this.subscriptions.add(new Disposable(() => this.destroyEnabledInlineGitDiff()))
    this.toggleInlineGitDiff = this.toggleInlineGitDiff.bind(this)
    this.lastOpenedEditor = this.editor
  }

  onItemOpened (editor) {
    if (this.getConfig('inlineGitDiffIntegration')) {
      if (this.lastOpenedEditor !== editor) {
        this.lastOpenedEditor = editor
        this.enabledInlineGitDiffForEditor(editor)
      }
    }
  }

  enabledInlineGitDiffForEditor (editor) {
    if (this.service.inlineGitDiff) {
      const inlineGitDiff = this.service.inlineGitDiff.getInlineGitDiff(editor)
      // If inlineGitDiff was NOT already enabled we dispose it on narrow:close
      if (inlineGitDiff.enable()) {
        this.enabledInlineGitDiff.add(inlineGitDiff)
      }
    }
  }

  destroyEnabledInlineGitDiff () {
    this.enabledInlineGitDiff.forEach(diff => diff.destroy())
    this.enabledInlineGitDiff.clear()
  }

  toggleInlineGitDiff () {
    const enabled = !this.getConfig('inlineGitDiffIntegration')
    this.setConfig('inlineGitDiffIntegration', enabled)
    if (enabled) {
      this.enabledInlineGitDiffForEditor(this.lastOpenedEditor)
    } else {
      this.destroyEnabledInlineGitDiff()
    }
    this.updateControlBar(enabled)
  }

  updateControlBar (value = this.getConfig('inlineGitDiffIntegration')) {
    this.ui.controlBar.updateElements({inlineGitDiff: value})
  }

  getItems ({filePath}) {
    this.updateControlBar()

    if (filePath) {
      const repo = repositoryForPath(filePath)
      if (repo) {
        getItemsForFilePath(repo, filePath).then(this.finishUpdateItems)
      } else {
        this.finishUpdateItems([])
      }
    } else {
      const promises = []
      const updateItems = items => this.updateItems(_.compact(items))

      const repositories = atom.project.getRepositories().filter(repo => repo != null)
      for (const repo of repositories) {
        for (const filePath of getModifiedFilePathsForRepository(repo)) {
          const promise = getItemsForFilePath(repo, filePath).then(updateItems)
          promises.push(promise)
        }
      }
      Promise.all(promises).then(() => this.finishUpdateItems())
    }
  }
}
<commit_msg>re-fix bad fix: assign lastOpenedEditor first prevent initial enablement on open<commit_after>'use babel'

const {Point, Disposable} = require('atom')
const _ = require('underscore-plus')
const ProviderBase = require('./provider-base')
const Path = require('path')
const fs = require('fs-plus')
const {limitNumber} = require('../utils')

function getModifiedFilePathsForRepository (repository) {
  const dir = repository.getWorkingDirectory()
  return Object.keys(repository.statuses)
    .map(filePath => Path.join(dir, filePath))
    .filter(fs.isFileSync)
}

function itemForGitDiff (diff, {buffer, filePath}) {
  const row = limitNumber(diff.newStart - 1, {min: 0})
  const lineText = buffer.lineForRow(row)
  return {
    point: new Point(row, lineText.match(/\s*/)[0].length),
    text: lineText,
    filePath: filePath
  }
}

async function getItemsForFilePath (repo, filePath) {
  const existingBuffer = atom.project.findBufferForPath(filePath)
  const buffer = existingBuffer || (await atom.project.buildBuffer(filePath))
  const diffs = repo.getLineDiffs(filePath, buffer.getText())
  // When file was completely new file, getLineDiffs return null, so need guard.
  const result = diffs ? diffs.map(diff => itemForGitDiff(diff, {buffer, filePath})) : []
  if (!existingBuffer) buffer.destroy()

  return result
}

// Borrowed from git-diff core pacakge.
function repositoryForPath (filePath) {
  const directories = atom.project.getDirectories()
  for (let i = 0; i < directories.length; i++) {
    const directory = directories[i]
    if (filePath === directory.getPath() || directory.contains(filePath)) {
      return atom.project.getRepositories()[i]
    }
  }
}

const providerConfig = {
  showProjectHeader: true,
  showFileHeader: true,
  supportCacheItems: true,
  supportFilePathOnlyItemsUpdate: true,
  refreshOnDidStopChanging: true
}

module.exports = class GitDiffAll extends ProviderBase {
  constructor (...args) {
    super(...args)
    Object.assign(this, providerConfig)
    this.enabledInlineGitDiff = new Set()
    this.subscriptions.add(new Disposable(() => this.destroyEnabledInlineGitDiff()))
    this.toggleInlineGitDiff = this.toggleInlineGitDiff.bind(this)
  }

  onItemOpened (editor) {
    if (this.getConfig('inlineGitDiffIntegration')) {
      if (this.lastOpenedEditor !== editor) {
        this.lastOpenedEditor = editor
        this.enabledInlineGitDiffForEditor(editor)
      }
    }
  }

  enabledInlineGitDiffForEditor (editor) {
    if (this.service.inlineGitDiff) {
      const inlineGitDiff = this.service.inlineGitDiff.getInlineGitDiff(editor)
      // If inlineGitDiff was NOT already enabled we dispose it on narrow:close
      if (inlineGitDiff.enable()) {
        this.enabledInlineGitDiff.add(inlineGitDiff)
      }
    }
  }

  destroyEnabledInlineGitDiff () {
    this.enabledInlineGitDiff.forEach(diff => diff.destroy())
    this.enabledInlineGitDiff.clear()
  }

  toggleInlineGitDiff () {
    const enabled = !this.getConfig('inlineGitDiffIntegration')
    this.setConfig('inlineGitDiffIntegration', enabled)
    if (enabled) {
      this.enabledInlineGitDiffForEditor(this.lastOpenedEditor || this.editor)
    } else {
      this.destroyEnabledInlineGitDiff()
    }
    this.updateControlBar(enabled)
  }

  updateControlBar (value = this.getConfig('inlineGitDiffIntegration')) {
    this.ui.controlBar.updateElements({inlineGitDiff: value})
  }

  getItems ({filePath}) {
    this.updateControlBar()

    if (filePath) {
      const repo = repositoryForPath(filePath)
      if (repo) {
        getItemsForFilePath(repo, filePath).then(this.finishUpdateItems)
      } else {
        this.finishUpdateItems([])
      }
    } else {
      const promises = []
      const updateItems = items => this.updateItems(_.compact(items))

      const repositories = atom.project.getRepositories().filter(repo => repo != null)
      for (const repo of repositories) {
        for (const filePath of getModifiedFilePathsForRepository(repo)) {
          const promise = getItemsForFilePath(repo, filePath).then(updateItems)
          promises.push(promise)
        }
      }
      Promise.all(promises).then(() => this.finishUpdateItems())
    }
  }
}
<|endoftext|>


<commit_before>//STYLE
$('.pagetitlediv').remove();

var table = document.createElement('table');
table.setAttribute('class', 'menu-table');
var table_container = document.createElement('div');
document.body.appendChild(table_container);
table_container.appendChild(table);
table.appendChild(document.createElement('tbody'));

var fst = false;
$('.menuplaintable').eq(0).find('tr').each(function() {
    tr = $(this);
    var a = tr.find('a').last();
    var href = a.attr('href');
    var title = a.text();
    var desc = tr.find('span.menulinkdesctext').first().text().replace(/$\s*/, '');

    var row;
    if(fst){
        fst = false;
        row = table.rows[table.rows.length - 1];
    }else{
        fst = true;
        row = table.insertRow(-1);
    }
    var div = document.createElement('div');
    div.innerHTML = `<a class="list" href="${href}"> <div class="list-container"> <div class="list-title">${title}</div> <div class="list-description">${desc}</div> </div> </a>`;
    var cell = row.insertCell(0);
    cell.setAttribute('class', 'menu-cell');
    cell.appendChild(div);
});

//easier cc course tool
function load_tool(){
    var container = document.createElement('div');
    var cell = table.insertRow(0).insertCell(0);
    $(cell).attr('colspan', 2).html('

');
    $('#cceasier-tool').toggle().load(chrome.runtime.getURL('html/tool_div.html'), on_tool_loaded);
}

load_tool();

function show_modal_window(schedule) {
    var modal_window = $('[data-remodal-id=result_modal]').remodal();
    var courses = $('#course_pool').val().split(',');
    var hash = {};
    $.each(courses, function () { hash[this] = true; });
    courses = [];
    $.each(hash, function (k, v) { courses.push(k); });
    var departs = {};
    $.each(courses, function () {
        var course_code = this;
        if (schedule[course_code] == null) return;
        var depart_code = course_code.slice(0, 2);
        if (departs[depart_code] == null) departs[depart_code] = [[], [], [], [], [], [], [], []];
        $.each(schedule[course_code], function () {
            var block = parseInt(this['block']);
            if (isNaN(block)) return;
            departs[depart_code][block - 1].push(this);
        });
    });
    var modal_table = $('#result_modal_table')[0];
    modal_table.children[0].innerHTML = '';
    for (i = 0; i < 9; i++) modal_table.insertRow(-1);
    for (i = 1; i < 9; i++) {
        var cell = modal_table.rows[i].insertCell(-1);
        cell.innerText = i;
    }
    modal_table.rows[0].insertCell(-1);
    var depart2name = { "AN": "Anthropology", "AR": "Arabic", "AH": "Art History", "AS": "Art Studio", "PA": "Asian Studies", "BY": "Biology", "CH": "Chemistry", "CN": "Chinese Language", "CL": "Classics", "CO": "Comparative Literature", "CP": "Computer Science", "DS": "Dance Studio", "DA": "Dance Theory", "EC": "Economics", "ED": "Education", "EN": "English", "EV": "Environmental Program", "FG": "Feminist & Gender Studies", "FM": "Film and Media Studies", "FS": "Film Studies", "FR": "French", "GS": "General Studies", "GY": "Geology", "GR": "German", "HE": "Hebrew", "HY": "History", "HK": "Human Biology and Kinesiology", "IT": "Italian", "JA": "Japanese", "MA": "Mathematics", "MB": "Molecular Biology", "MU": "Music", "BE": "Organismal Biology and Ecology", "PH": "Philosophy", "PC": "Physics", "PS": "Political Science", "PG": "Portuguese", "PY": "Psychology", "RM": "Race, Ethnicity, and Migration", "RE": "Religion", "RU": "Russian", "RS": "Russian & Eurasian Studies", "SO": "Sociology", "SW": "Southwest Studies", "SP": "Spanish", "HS": "Studies in Humanities", "NS": "Studies in Natural Science", "TH": "Theatre" };
    $.each(departs, function (depart, blocks) {
        var cell = modal_table.rows[0].insertCell(-1);
        cell.innerText = depart2name[depart];
        for (j = 0; j < 8; j++) {
            var cell = modal_table.rows[j + 1].insertCell(-1);
            cell.innerHTML = '

';
            var container_row = cell.children[0].children[0].children[0];
            $.each(blocks[j], function () {
                var card_container = container_row.insertCell(-1);
                card_container.innerHTML = `<h3>${this['course_no']}<h3><br>${this['available']}/${this['limit']} (<small>${this['waitlist']}</small>, <small>${this['reserved']}</small>)`
            });
        }
    });
    modal_window.open();
}

function on_tool_loaded(){
    var container = document.createElement('div');
    var cell = table.insertRow(0).insertCell(0);
    $(cell).attr('colspan', 2).attr('class', 'menu-cell');
    cell.appendChild(container);
    container.innerHTML = `

Easier CC Banner Course Scheduler

Scheduling courses has never been easier.

 `;
    $(container.children[0]).click(function(){
        $('#cceasier-tool').slideToggle();
    });

    //var bar = new ldBar('#progress');
    $('#course_pool').css('background-color','#b5b5b5 !important');
    var start_time = new Date().getTime();
    var timer = setInterval(function(){
        var t = (new Date().getTime() - start_time) / 1000;
        var p = 100*(1-Math.pow(0.85, t));
        //bar.set(p);
        $('#loading_bar').css('width',p+"%").css('width', '+=5px');
    }, 20);
    $('#cceasier-tool *').prop('disabled', true);
    get_schedule(schedule => {
        clearInterval(timer);
        setTimeout(function(){
            //bar.set(100);
            $('#loading_bar').css('width','100%').css('width', '+=5px');
            $('#loading_bar').css('transition','0.3s');
            $('#loading_bar').css('background-color','transparent');
            $('#course_pool').css('transition','1s');
            $('#course_pool').css('background-color','transparent');
        }, 1000);
        $('#cceasier-tool *').prop('disabled', false);

        var course_list = [];
        var instructor_sum = {};
        $.each(schedule, function() {
            var prof = '';
            var hash = {};
            $.each(schedule[this[0]['course_no']], function(){
                if(hash[this['instructor']] == null){
                    prof += this['instructor'] + ', ';
                    hash[this['instructor']] = true;
                }
            });
            prof = prof.slice(0, -2);
            course_list.push({
                "course_code": this[0]["course_no"],
                "course_title": this[0]["course_title"],
                "instructor": prof
            });
            instructor_sum[this[0]['course_no']] = prof;
        });
        var fuse = new Fuse(course_list, {
            shouldSort: true,
            threshold: 0.4,
            location: 0,
            distance: 100,
            maxPatternLength: 32,
            minMatchCharLength: 2,
            keys: [
                "course_code",
                "instructor",
                "course_title"]
        });

        new Textcomplete(new Textcomplete.editors.Textarea(document.getElementById('course_pool'))).register([
            {
                match: /(^|,)([^,]+)$/,
                search: function (term, callback) {
                    var result = fuse.search(term);
                    result = result.slice(0, 5);
                    var match = [];
                    $.each(result, function(){
                        match.push(this['course_code']);
                    });
                    callback(match);
                },
                template: function (course_code) {
                    return `<p><b>${course_code}</b> <small>(${instructor_sum[course_code]})</small></p>${schedule[course_code][0]['course_title']}<p></p>`
                },
                replace: function (course_code) {
                    return '$1' + course_code + ',';
                }
            }
        ]);

        //modal window
        $('#course_pool_confirm').click(function(){show_modal_window(schedule) });
        key('ctrl+enter', function(){
            console.log('hihi')
            if($('#course_pool').is(':focus')) show_modal_window(schedule);
        });
        key.filter = ev => true;
    });

    $(document.head).append(`<link rel="stylesheet" href="${chrome.runtime.getURL('style/main.css')}">`);

    //course scheduler style

    $("#course_pool").focus(function(){
$(".dropdown-menu").css('display','block');
        $(".dropdown-menu").addClass("hover-over-boxshadow");
        $("#course_pool").addClass("hover-over-boxshadow");
        //console.log('clicked');
    });
    $("#course_pool").blur(function(){
        $(".dropdown-menu").css('display','none');
        $(".dropdown-menu").removeClass("hover-over-boxshadow");
        $("#course_pool").removeClass("hover-over-boxshadow");
    });

    var arrowImgUrl = chrome.runtime.getURL('img/arrow.png');
    $("#course_pool_confirm").append('');
}

//remove the orignal menu
$(document.body.children[1]).remove();

<commit_msg>add relogin detection<commit_after>//check if need relogin
if(document.getElementById('UserID')){
    window.location.href = 'https://cas.coloradocollege.edu/cas/login?service=https%3A%2F%2Fbanssop.coloradocollege.edu%3A443%2Fssomanager%2Fc%2FSSB';
}

//STYLE
$('.pagetitlediv').remove();

var table = document.createElement('table');
table.setAttribute('class', 'menu-table');
var table_container = document.createElement('div');
document.body.appendChild(table_container);
table_container.appendChild(table);
table.appendChild(document.createElement('tbody'));

var fst = false;
$('.menuplaintable').eq(0).find('tr').each(function() {
    tr = $(this);
    var a = tr.find('a').last();
    var href = a.attr('href');
    var title = a.text();
    var desc = tr.find('span.menulinkdesctext').first().text().replace(/$\s*/, '');

    var row;
    if(fst){
        fst = false;
        row = table.rows[table.rows.length - 1];
    }else{
        fst = true;
        row = table.insertRow(-1);
    }
    var div = document.createElement('div');
    div.innerHTML = `<a class="list" href="${href}"> <div class="list-container"> <div class="list-title">${title}</div> <div class="list-description">${desc}</div> </div> </a>`;
    var cell = row.insertCell(0);
    cell.setAttribute('class', 'menu-cell');
    cell.appendChild(div);
});

//easier cc course tool
function load_tool(){
    var container = document.createElement('div');
    var cell = table.insertRow(0).insertCell(0);
    $(cell).attr('colspan', 2).html('

');
    $('#cceasier-tool').toggle().load(chrome.runtime.getURL('html/tool_div.html'), on_tool_loaded);
}

load_tool();

function show_modal_window(schedule) {
    var modal_window = $('[data-remodal-id=result_modal]').remodal();
    var courses = $('#course_pool').val().split(',');
    var hash = {};
    $.each(courses, function () { hash[this] = true; });
    courses = [];
    $.each(hash, function (k, v) { courses.push(k); });
    var departs = {};
    $.each(courses, function () {
        var course_code = this;
        if (schedule[course_code] == null) return;
        var depart_code = course_code.slice(0, 2);
        if (departs[depart_code] == null) departs[depart_code] = [[], [], [], [], [], [], [], []];
        $.each(schedule[course_code], function () {
            var block = parseInt(this['block']);
            if (isNaN(block)) return;
            departs[depart_code][block - 1].push(this);
        });
    });
    var modal_table = $('#result_modal_table')[0];
    modal_table.children[0].innerHTML = '';
    for (i = 0; i < 9; i++) modal_table.insertRow(-1);
    for (i = 1; i < 9; i++) {
        var cell = modal_table.rows[i].insertCell(-1);
        cell.innerText = i;
    }
    modal_table.rows[0].insertCell(-1);
    var depart2name = { "AN": "Anthropology", "AR": "Arabic", "AH": "Art History", "AS": "Art Studio", "PA": "Asian Studies", "BY": "Biology", "CH": "Chemistry", "CN": "Chinese Language", "CL": "Classics", "CO": "Comparative Literature", "CP": "Computer Science", "DS": "Dance Studio", "DA": "Dance Theory", "EC": "Economics", "ED": "Education", "EN": "English", "EV": "Environmental Program", "FG": "Feminist & Gender Studies", "FM": "Film and Media Studies", "FS": "Film Studies", "FR": "French", "GS": "General Studies", "GY": "Geology", "GR": "German", "HE": "Hebrew", "HY": "History", "HK": "Human Biology and Kinesiology", "IT": "Italian", "JA": "Japanese", "MA": "Mathematics", "MB": "Molecular Biology", "MU": "Music", "BE": "Organismal Biology and Ecology", "PH": "Philosophy", "PC": "Physics", "PS": "Political Science", "PG": "Portuguese", "PY": "Psychology", "RM": "Race, Ethnicity, and Migration", "RE": "Religion", "RU": "Russian", "RS": "Russian & Eurasian Studies", "SO": "Sociology", "SW": "Southwest Studies", "SP": "Spanish", "HS": "Studies in Humanities", "NS": "Studies in Natural Science", "TH": "Theatre" };
    $.each(departs, function (depart, blocks) {
        var cell = modal_table.rows[0].insertCell(-1);
        cell.innerText = depart2name[depart];
        for (j = 0; j < 8; j++) {
            var cell = modal_table.rows[j + 1].insertCell(-1);
            cell.innerHTML = '

';
            var container_row = cell.children[0].children[0].children[0];
            $.each(blocks[j], function () {
                var card_container = container_row.insertCell(-1);
                card_container.innerHTML = `<h3>${this['course_no']}<h3><br>${this['available']}/${this['limit']} (<small>${this['waitlist']}</small>, <small>${this['reserved']}</small>)`
            });
        }
    });
    modal_window.open();
}

function on_tool_loaded(){
    var container = document.createElement('div');
    var cell = table.insertRow(0).insertCell(0);
    $(cell).attr('colspan', 2).attr('class', 'menu-cell');
    cell.appendChild(container);
    container.innerHTML = `

Easier CC Banner Course Scheduler

Scheduling courses has never been easier.

 `;
    $(container.children[0]).click(function(){
        $('#cceasier-tool').slideToggle();
    });

    //var bar = new ldBar('#progress');
    $('#course_pool').css('background-color','#b5b5b5 !important');
    var start_time = new Date().getTime();
    var timer = setInterval(function(){
        var t = (new Date().getTime() - start_time) / 1000;
        var p = 100*(1-Math.pow(0.85, t));
        //bar.set(p);
        $('#loading_bar').css('width',p+"%").css('width', '+=5px');
    }, 20);
    $('#cceasier-tool *').prop('disabled', true);
    get_schedule(schedule => {
        clearInterval(timer);
        setTimeout(function(){
            //bar.set(100);
            $('#loading_bar').css('width','100%').css('width', '+=5px');
            $('#loading_bar').css('transition','0.3s');
            $('#loading_bar').css('background-color','transparent');
            $('#course_pool').css('transition','1s');
            $('#course_pool').css('background-color','transparent');
        }, 1000);
        $('#cceasier-tool *').prop('disabled', false);

        var course_list = [];
        var instructor_sum = {};
        $.each(schedule, function() {
            var prof = '';
            var hash = {};
            $.each(schedule[this[0]['course_no']], function(){
                if(hash[this['instructor']] == null){
                    prof += this['instructor'] + ', ';
                    hash[this['instructor']] = true;
                }
            });
            prof = prof.slice(0, -2);
            course_list.push({
                "course_code": this[0]["course_no"],
                "course_title": this[0]["course_title"],
                "instructor": prof
            });
            instructor_sum[this[0]['course_no']] = prof;
        });
        var fuse = new Fuse(course_list, {
            shouldSort: true,
            threshold: 0.4,
            location: 0,
            distance: 100,
            maxPatternLength: 32,
            minMatchCharLength: 2,
            keys: [
                "course_code",
                "instructor",
                "course_title"]
        });

        new Textcomplete(new Textcomplete.editors.Textarea(document.getElementById('course_pool'))).register([
            {
                match: /(^|,)([^,]+)$/,
                search: function (term, callback) {
                    var result = fuse.search(term);
                    result = result.slice(0, 5);
                    var match = [];
                    $.each(result, function(){
                        match.push(this['course_code']);
                    });
                    callback(match);
                },
                template: function (course_code) {
                    return `<p><b>${course_code}</b> <small>(${instructor_sum[course_code]})</small></p>${schedule[course_code][0]['course_title']}<p></p>`
                },
                replace: function (course_code) {
                    return '$1' + course_code + ',';
                }
            }
        ]);

        //modal window
        $('#course_pool_confirm').click(function(){show_modal_window(schedule) });
        key('ctrl+enter', function(){
            console.log('hihi')
            if($('#course_pool').is(':focus')) show_modal_window(schedule);
        });
        key.filter = ev => true;
    });

    $(document.head).append(`<link rel="stylesheet" href="${chrome.runtime.getURL('style/main.css')}">`);

    //course scheduler style

    $("#course_pool").focus(function(){
$(".dropdown-menu").css('display','block');
        $(".dropdown-menu").addClass("hover-over-boxshadow");
        $("#course_pool").addClass("hover-over-boxshadow");
        //console.log('clicked');
    });
    $("#course_pool").blur(function(){
        $(".dropdown-menu").css('display','none');
        $(".dropdown-menu").removeClass("hover-over-boxshadow");
        $("#course_pool").removeClass("hover-over-boxshadow");
    });

    var arrowImgUrl = chrome.runtime.getURL('img/arrow.png');
    $("#course_pool_confirm").append('');
}

//remove the orignal menu
$(document.body.children[1]).remove();

<|endoftext|>


<commit_before>module.exports = {
  prompts: {
    name: {
      type: 'string',
      required: true,
      message: 'Plugin name'
    },
    description: {
      type: 'string',
      required: false,
      message: 'Plugin description',
      default: 'A Vue.js Plugin'
    },
    version: {
      type: 'string',
      required: false,
      message: 'Initial version',
      default: '0.0.0'
    },
    author: {
      type: 'string',
      message: 'Author'
    },
    branch: {
      type: 'string',
      required: true,
      message: 'Development main branch name',
      default: 'dev'
    },
    githubAccount: {
      type: 'string',
      required: false,
      message: 'GitHub Account',
      default: ''
    },
    flow: {
      type: 'confirm',
      message: 'Setup flowtype?'
    },
    e2e: {
      type: 'confirm',
      message: 'Setup e2e tests?'
    },
    coverage: {
      type: 'confirm',
      message: 'Setup coverage services?'
    },
    coverageConfig: {
      when: 'coverage',
      type: 'list',
      message: 'Choice a coverage service',
      choices: [
        'coveralls',
        'codecov'
      ],
      default: ['codecov']
    },
    sauce: {
      type: 'confirm',
      message: 'Setup sauce tests?'
    },
    ci: {
      type: 'confirm',
      message: 'Setup CI services?'
    },
    ciConfig: {
      when: 'ci',
      type: 'list',
      message: 'Choice a CI service',
      choices: [
        'travis',
        'circlci'
      ],
      default: ['circlci']
    },
    conventional: {
      type: 'confirm',
      message: 'Setup conventional-changelog tasks?'
    },
    conventionalConfig: {
      when: 'conventional',
      type: 'list',
      message: 'Choice a conventional-changelog preset',
      choices: [
        { name: 'none (configure it youreself)', value: 'none' },
        { name: 'angular', value: 'angular' },
        { name: 'atom', value: 'atom' },
        { name: 'codemirror', value: 'codemirror' },
        { name: 'ember', value: 'ember' },
        { name: 'eslint', value: 'eslint' },
        { name: 'express', value: 'express' },
        { name: 'jquery', value: 'jquery' },
        { name: 'jshint', value: 'jshint' }
      ]
    },
    gitbook: {
      type: 'confirm',
      message: 'Setup gitbook documentaion?'
    },
    issue: {
      type: 'confirm',
      message: 'Setup issue guide?'
    },
    contribution: {
      type: 'confirm',
      message: 'Setup contribution guide?'
    }
  },
  filters: {
    "examples/**/*": 'e2e',
    "test/e2e/**/*": 'e2e',
    "test/e2e/**/*": 'e2e',
    ".travis.yml": "ciConfig === 'travis'",
    "circl.yml": "ciConfig === 'circlci'",
    "config/karma.coveralls.conf.js": "coverageConfig === 'coveralls'",
    ".flowconfig": 'flow',
    "decls/*": 'flow',
    "gitbook/**/**": 'gitbook',
    "docs/**/**": 'gitbook',
    ".github/ISSUE_TEMPLATE.md": 'issue',
    "CONTRIBUTING.md": "contribution"
  },
  helpers: {
    nowYear: function () {
      return new Date().getFullYear()
    },
    authorFullNameFrom: function (author) {
      const startPosition = author.indexOf('<')
      return author.slice(0, startPosition - 1)
    },
    authorEmailFrom: function (author) {
      const startPosition = author.indexOf('<')
      const endPosition = author.indexOf('>')
      return author.slice(startPosition + 1, endPosition)
    },
    classify: function (str) {
      return str.replace(/(?:^|[-_\/])(\w)/g, function (_, c) {
        return c ? c.toUpperCase() : ''
      })
    }
  }
}
<commit_msg>:zap: improvement(meta): github account default username<commit_after>const os = require('os')
const githubAccountDefault = os.userInfo().username

module.exports = {
  prompts: {
    name: {
      type: 'string',
      required: true,
      message: 'Plugin name'
    },
    description: {
      type: 'string',
      required: false,
      message: 'Plugin description',
      default: 'A Vue.js Plugin'
    },
    version: {
      type: 'string',
      required: false,
      message: 'Initial version',
      default: '0.0.0'
    },
    author: {
      type: 'string',
      message: 'Author'
    },
    branch: {
      type: 'string',
      required: true,
      message: 'Development main branch name',
      default: 'dev'
    },
    githubAccount: {
      type: 'string',
      required: false,
      message: 'GitHub Account',
      default: githubAccountDefault
    },
    flow: {
      type: 'confirm',
      message: 'Setup flowtype?'
    },
    e2e: {
      type: 'confirm',
      message: 'Setup e2e tests?'
    },
    coverage: {
      type: 'confirm',
      message: 'Setup coverage services?'
    },
    coverageConfig: {
      when: 'coverage',
      type: 'list',
      message: 'Choice a coverage service',
      choices: [
        'coveralls',
        'codecov'
      ],
      default: ['codecov']
    },
    sauce: {
      type: 'confirm',
      message: 'Setup sauce tests?'
    },
    ci: {
      type: 'confirm',
      message: 'Setup CI services?'
    },
    ciConfig: {
      when: 'ci',
      type: 'list',
      message: 'Choice a CI service',
      choices: [
        'travis',
        'circlci'
      ],
      default: ['circlci']
    },
    conventional: {
      type: 'confirm',
      message: 'Setup conventional-changelog tasks?'
    },
    conventionalConfig: {
      when: 'conventional',
      type: 'list',
      message: 'Choice a conventional-changelog preset',
      choices: [
        { name: 'none (configure it youreself)', value: 'none' },
        { name: 'angular', value: 'angular' },
        { name: 'atom', value: 'atom' },
        { name: 'codemirror', value: 'codemirror' },
        { name: 'ember', value: 'ember' },
        { name: 'eslint', value: 'eslint' },
        { name: 'express', value: 'express' },
        { name: 'jquery', value: 'jquery' },
        { name: 'jshint', value: 'jshint' }
      ]
    },
    gitbook: {
      type: 'confirm',
      message: 'Setup gitbook documentaion?'
    },
    issue: {
      type: 'confirm',
      message: 'Setup issue guide?'
    },
    contribution: {
      type: 'confirm',
      message: 'Setup contribution guide?'
    }
  },
  filters: {
    "examples/**/*": 'e2e',
    "test/e2e/**/*": 'e2e',
    "test/e2e/**/*": 'e2e',
    ".travis.yml": "ciConfig === 'travis'",
    "circl.yml": "ciConfig === 'circlci'",
    "config/karma.coveralls.conf.js": "coverageConfig === 'coveralls'",
    ".flowconfig": 'flow',
    "decls/*": 'flow',
    "gitbook/**/**": 'gitbook',
    "docs/**/**": 'gitbook',
    ".github/ISSUE_TEMPLATE.md": 'issue',
    "CONTRIBUTING.md": "contribution"
  },
  helpers: {
    nowYear: function () {
      return new Date().getFullYear()
    },
    authorFullNameFrom: function (author) {
      const startPosition = author.indexOf('<')
      return author.slice(0, startPosition - 1)
    },
    authorEmailFrom: function (author) {
      const startPosition = author.indexOf('<')
      const endPosition = author.indexOf('>')
      return author.slice(startPosition + 1, endPosition)
    },
    classify: function (str) {
      return str.replace(/(?:^|[-_\/])(\w)/g, function (_, c) {
        return c ? c.toUpperCase() : ''
      })
    }
  }
}
<|endoftext|>


<commit_before>/**
 * Created by vedi on 09/02/15.
 */
'use strict';

var
  _ = require('lodash'),
  HTTP_STATUSES   = require('http-statuses');

function SequelizeDataSource(ModelClass) {
  this.ModelClass = ModelClass;
}

SequelizeDataSource.prototype.initialize = function initialize(options) {
  this.idField = options.idField;
  this.fieldMap = options.fieldMap || {};
};

SequelizeDataSource.prototype.find = function find(options, callback) {
  var filter = options.filter;
  var fields = options.fields;
  var q = options.q;
  var qFields = options.qFields;
  var sort = options.sort;
  var limit = options.limit;
  var skip = options.skip;
  var queryPipe = options.queryPipe;

  this._normalizeFilter(filter, this.ModelClass);

  var qExpr = q ? this._buildQ(qFields, q) : undefined;
  if (!_.isUndefined(qExpr) && qExpr.length > 0) {
    filter.$or = qExpr;
  }
  var query = {};

  query.attributes = this._normalizeFields(fields, filter, query);

  query.where = filter;

  if (sort) {
    query.order = [];
    _.forOwn(sort, function (value, key) {
      query.order.push([key, +value > 0 ? 'ASC' : 'DESC']);
    });
    //query.order = sort;
  }
  query.limit = limit;
  if (skip > 0) {
    query.offset = skip;
  }
  if (queryPipe) {
    queryPipe(query);
  }

  var _this = this;

  this.ModelClass.findAll(query).
    then(function(docs) {
      return _.map(docs, function (doc) {
        return _this.toObject(doc);
      });
    }).
    nodeify(callback);
};

SequelizeDataSource.prototype.findOneAsync = function findOneAsync(options) {
  var filter = options.filter;
  var fields = options.fields;
  var queryPipe = options.queryPipe;

  this._normalizeFilter(filter, this.ModelClass);

  var query = {};

  query.attributes = this._normalizeFields(fields, filter, query);

  query.where = filter;

  if (queryPipe) {
    queryPipe(query);
  }
  return this.ModelClass.findOne(query);
};

SequelizeDataSource.prototype.create = function create(data, callback) {
  return callback(null, this.ModelClass.build(data));
};

SequelizeDataSource.prototype.saveAsync = function saveAsync(doc) {
  var _this = this;
  return doc.save().
    then(function (newDoc) {
      return _this.findOneAsync({filter: newDoc[_this.idField], fields: _this.fieldMap});
    }).
    then(function (result) {
      return result;
    });
};

SequelizeDataSource.prototype.removeAsync = function removeAsync(doc) {
  return doc.destroy().then(function () {
    return doc;
  });
};

SequelizeDataSource.prototype.countAsync = function countAsync(filter) {
  this._normalizeFilter(filter, this.ModelClass);
  return this.ModelClass.count({where: filter});
};

SequelizeDataSource.prototype.toObject = function toObject(model) {
  return model.get({
    plain: true
  });
};

SequelizeDataSource.prototype.getFieldValue = function getFieldValue(model, fieldName) {
  return this._resolveProp(model, fieldName);
};

SequelizeDataSource.prototype.setFieldValue = function setFieldValue(model, fieldName, value) {
  this._setProp(model, fieldName, value);
};

SequelizeDataSource.prototype.proceedArrayMethod = function proceedArrayMethod(dest, source, methodName, fieldName, req) {
  // TODO: Implement
  //// get sure we have an array
  //if (dest[fieldName] === undefined) {
  //  dest[fieldName] = [];
  //}
  //
  //if (methodName === '$addToSet') {
  //  dest[fieldName].addToSet(source);
  //} else if (methodName === '$pop') {
  //  if (source === 1) {
  //    dest[fieldName].pop();
  //  } else if (source === -1) {
  //    dest[fieldName].shift();
  //  } else {
  //    throw new Error('Illegal param value for $pop method');
  //  }
  //} else if (methodName === '$push') {
  //  dest[fieldName].push(source);
  //} else if (methodName === '$pull') {
  //  dest[fieldName].pull(source);
  //}
};

SequelizeDataSource.prototype.assignField = function assignField(dest, source, fieldName, req, callback) {
  if (_.isFunction(dest.set)) {
    dest.set(fieldName, source[fieldName]);
  } else {
    // on insert we come here
    this._setProp(dest, fieldName, source[fieldName]);
  }
  callback();
};

SequelizeDataSource.prototype.getModelFieldNames = function getModelFieldNames() {
  return _.keys(this.ModelClass.rawAttributes);
};

SequelizeDataSource.prototype.parseError = function parseError(err) {
  // TODO: Implement
  var result = {};
  if (err.name == 'SequelizeValidationError') {
    result.status = HTTP_STATUSES.BAD_REQUEST;
    result.details = err.message;
  }
  //else if (err.name == 'CastError') {
  //  result.status = HTTP_STATUSES.BAD_REQUEST;
  //  result.details = {};
  //  result.details[err.path] = {
  //    message: err.message,
  //    name: err.name,
  //    path: err.path,
  //    type: err.type,
  //    value: err.value
  //  };
  //  result.message = 'CastError';
  //}
  //else if (err.name == 'MongoError' && (err.code == 11000 || err.code == 11001)) { // E11000(1) duplicate key error index
  //  result.status = HTTP_STATUSES.BAD_REQUEST;
  //  result.details = err.err;
  //}
  //else if (err.name == 'VersionError') {
  //  result.status = HTTP_STATUSES.CONFLICT;
  //  result.details = err.message;
  //} else {
  //  return;
  //}
  else {
    // it's just for testing
    result.status = HTTP_STATUSES.BAD_REQUEST;
    result.details = err.message;
  }

  return result;
};

SequelizeDataSource.prototype._buildQ = function _buildQ(qFields, q) {
  var qExpr = [];
  _.forEach(qFields, function (qField) {
    var obj = {};
    obj[qField] = {$like: '%' + q + '%'};
    qExpr.push(obj);
  });
  return qExpr;
};

SequelizeDataSource.prototype._normalizeFields = function _normalizeFields(fields, filter, query) {
  query.include = query.include || [];
  fields = this._resolveAssociations(this.ModelClass, this.fieldMap, fields, filter, query.include);

  if (!fields.indexOf(this.ModelClass.primaryKeyField) < 0) {
    fields.push(this.ModelClass.primaryKeyField);
  }

  return fields;
};

SequelizeDataSource.prototype._resolveAssociations = function (ModelClass, fieldMap, fields, filter, include) {
  include = include || [];
  var fieldsToRemove = [];
  var filterToRemove = [];
  var result = _.map(fields, function (field) {
    var isObject = typeof(field) === 'object';
    var fieldName = isObject ? field.name : field;
    var fieldMeta = fieldMap[fieldName];
    var association = ModelClass.associations[fieldName];
    if (fieldMeta && association) {
      fieldsToRemove.push(fieldName);

      var associationFields;
      if (isObject && field.fields) {
        associationFields = field.fields;
      } else if (fieldMeta.fields) {
        associationFields = fieldMeta.fields;
      } else {
        associationFields = [association.targetKey];
      }

      var filterValue = filter ? filter[fieldName] : undefined;

      var nestedInclude = [];

      associationFields = this._resolveAssociations(association.target, fieldMeta.fields, associationFields, filterValue, nestedInclude);

      var cleanFilterValue = filterValue ? _.transform(filterValue, function (result, value, key) {
        if (associationFields.indexOf(key) >= 0) {
          result[key] = value;
        }
        return result;
      }) : undefined;

      if (cleanFilterValue) {
        filterToRemove.push(fieldName);
      }

      // put to include
      include.push({
        association: association,
        attributes: associationFields,
        include: nestedInclude,
        where: cleanFilterValue
      });
    }
    return fieldName;
  }, this);

  _.each(fieldsToRemove, function (field) {
    _.pull(result, field);
  });

  _.each(filterToRemove, function (filterItem) {
    delete filter[filterItem];
  });

  return result;
};

SequelizeDataSource.prototype._normalizeFilter = function _normalizeFilter(filter, root) {
  // TODO: Implement
  //_.forEach(_.keys(filter), function (key) {
  //  var path = root.schema.paths[key];
  //  // if it's an operator
  //  if (key.substr(0, 1) === '$') {
  //    // increase the level without changing the root
  //    this._normalizeFilter(filter[key], root);
  //  } else if (path) {
  //    var typeName = path.options.type.name;
  //    // it's embedded document
  //    if (!_.isUndefined(path.schema)) {
  //      this._normalizeFilter(filter[key], root.schema.paths[key]);
  //    } else if (typeName === 'ObjectId') {
  //      if (typeof(filter[key]) === 'string') {
  //        filter[key] = ObjectID(filter[key]);
  //      }
  //    } else if (typeName === 'Date') {
  //      if (typeof(filter[key]) === 'string') {
  //        filter[key] = new Date(filter[key]);
  //      }
  //      else if (typeof(filter[key]) === 'object') {
  //        _.forOwn(filter[key], function (value, innerKey) {
  //          if (typeof(value) === 'string') {
  //            filter[key][innerKey] = new Date(value);
  //          }
  //        });
  //      }
  //    }
  //  }
  //}, this);
};

SequelizeDataSource.prototype._resolveProp = function _resolveProp(obj, stringPath) {
  stringPath = stringPath.replace(/\[(\w+)]/g, '.$1');  // convert indexes to properties
  stringPath = stringPath.replace(/^\./, '');           // strip a leading dot
  var pathArray = stringPath.split('.');
  while (pathArray.length) {
    var pathItem = pathArray.shift();
    if (pathItem in obj) {
      obj = obj[pathItem];
    } else {
      return;
    }
  }
  return obj;
};

SequelizeDataSource.prototype._setProp = function _setProp(obj, stringPath, value) {
  stringPath = stringPath.replace(/\[(\w+)]/g, '.$1');  // convert indexes to properties
  stringPath = stringPath.replace(/^\./, '');           // strip a leading dot
  var pathArray = stringPath.split('.');
  while (pathArray.length - 1) {
    var pathItem = pathArray.shift();
    if (pathItem in obj) {
      obj = obj[pathItem];
    } else {
      return;
    }
  }
  return obj[pathArray.length ? pathArray[0] : stringPath] = value;
};

SequelizeDataSource.prototype.defaultIdField = 'id';
SequelizeDataSource.prototype.defaultArrayMethods = ['$addToSet', '$pop', '$push', '$pull'];

module.exports = SequelizeDataSource;

<commit_msg>fix count for filtering by associations<commit_after>/**
 * Created by vedi on 09/02/15.
 */
'use strict';

var
  _ = require('lodash'),
  HTTP_STATUSES   = require('http-statuses');

function SequelizeDataSource(ModelClass) {
  this.ModelClass = ModelClass;
}

SequelizeDataSource.prototype.initialize = function initialize(options) {
  this.idField = options.idField;
  this.fieldMap = options.fieldMap || {};
  this.modelFieldNames = options.modelFieldNames;
};

SequelizeDataSource.prototype.find = function find(options, callback) {
  var filter = options.filter;
  var fields = options.fields;
  var q = options.q;
  var qFields = options.qFields;
  var sort = options.sort;
  var limit = options.limit;
  var skip = options.skip;
  var queryPipe = options.queryPipe;

  this._normalizeFilter(filter, this.ModelClass);

  var qExpr = q ? this._buildQ(qFields, q) : undefined;
  if (!_.isUndefined(qExpr) && qExpr.length > 0) {
    filter.$or = qExpr;
  }
  var query = {};

  query.attributes = this._normalizeFields(fields, filter, query);

  query.where = filter;

  if (sort) {
    query.order = [];
    _.forOwn(sort, function (value, key) {
      query.order.push([key, +value > 0 ? 'ASC' : 'DESC']);
    });
    //query.order = sort;
  }
  query.limit = limit;
  if (skip > 0) {
    query.offset = skip;
  }
  if (queryPipe) {
    queryPipe(query);
  }

  var _this = this;

  this.ModelClass.findAll(query).
    then(function(docs) {
      return _.map(docs, function (doc) {
        return _this.toObject(doc);
      });
    }).
    nodeify(callback);
};

SequelizeDataSource.prototype.findOneAsync = function findOneAsync(options) {
  var filter = options.filter;
  var fields = options.fields;
  var queryPipe = options.queryPipe;

  this._normalizeFilter(filter, this.ModelClass);

  var query = {};

  query.attributes = this._normalizeFields(fields, filter, query);

  query.where = filter;

  if (queryPipe) {
    queryPipe(query);
  }
  return this.ModelClass.findOne(query);
};

SequelizeDataSource.prototype.create = function create(data, callback) {
  return callback(null, this.ModelClass.build(data));
};

SequelizeDataSource.prototype.saveAsync = function saveAsync(doc) {
  var _this = this;
  return doc.save().
    then(function (newDoc) {
      return _this.findOneAsync({filter: newDoc[_this.idField], fields: _this.fieldMap});
    }).
    then(function (result) {
      return result;
    });
};

SequelizeDataSource.prototype.removeAsync = function removeAsync(doc) {
  return doc.destroy().then(function () {
    return doc;
  });
};

SequelizeDataSource.prototype.countAsync = function countAsync(filter) {
  this._normalizeFilter(filter, this.ModelClass);

  var query = {};
  this._normalizeFields(this.modelFieldNames || this.getModelFieldNames(), filter, query, true); // for filtering by associations
  query.where = filter;

  return this.ModelClass.count(query);
};

SequelizeDataSource.prototype.toObject = function toObject(model) {
  return model.get({
    plain: true
  });
};

SequelizeDataSource.prototype.getFieldValue = function getFieldValue(model, fieldName) {
  return this._resolveProp(model, fieldName);
};

SequelizeDataSource.prototype.setFieldValue = function setFieldValue(model, fieldName, value) {
  this._setProp(model, fieldName, value);
};

SequelizeDataSource.prototype.proceedArrayMethod = function proceedArrayMethod(dest, source, methodName, fieldName, req) {
  // TODO: Implement
  //// get sure we have an array
  //if (dest[fieldName] === undefined) {
  //  dest[fieldName] = [];
  //}
  //
  //if (methodName === '$addToSet') {
  //  dest[fieldName].addToSet(source);
  //} else if (methodName === '$pop') {
  //  if (source === 1) {
  //    dest[fieldName].pop();
  //  } else if (source === -1) {
  //    dest[fieldName].shift();
  //  } else {
  //    throw new Error('Illegal param value for $pop method');
  //  }
  //} else if (methodName === '$push') {
  //  dest[fieldName].push(source);
  //} else if (methodName === '$pull') {
  //  dest[fieldName].pull(source);
  //}
};

SequelizeDataSource.prototype.assignField = function assignField(dest, source, fieldName, req, callback) {
  if (_.isFunction(dest.set)) {
    dest.set(fieldName, source[fieldName]);
  } else {
    // on insert we come here
    this._setProp(dest, fieldName, source[fieldName]);
  }
  callback();
};

SequelizeDataSource.prototype.getModelFieldNames = function getModelFieldNames() {
  return _.keys(this.ModelClass.rawAttributes);
};

SequelizeDataSource.prototype.parseError = function parseError(err) {
  // TODO: Implement
  var result = {};
  if (err.name == 'SequelizeValidationError') {
    result.status = HTTP_STATUSES.BAD_REQUEST;
    result.details = err.message;
  }
  //else if (err.name == 'CastError') {
  //  result.status = HTTP_STATUSES.BAD_REQUEST;
  //  result.details = {};
  //  result.details[err.path] = {
  //    message: err.message,
  //    name: err.name,
  //    path: err.path,
  //    type: err.type,
  //    value: err.value
  //  };
  //  result.message = 'CastError';
  //}
  //else if (err.name == 'MongoError' && (err.code == 11000 || err.code == 11001)) { // E11000(1) duplicate key error index
  //  result.status = HTTP_STATUSES.BAD_REQUEST;
  //  result.details = err.err;
  //}
  //else if (err.name == 'VersionError') {
  //  result.status = HTTP_STATUSES.CONFLICT;
  //  result.details = err.message;
  //} else {
  //  return;
  //}
  else {
    // it's just for testing
    result.status = HTTP_STATUSES.BAD_REQUEST;
    result.details = err.message;
  }

  return result;
};

SequelizeDataSource.prototype._buildQ = function _buildQ(qFields, q) {
  var qExpr = [];
  _.forEach(qFields, function (qField) {
    var obj = {};
    obj[qField] = {$like: '%' + q + '%'};
    qExpr.push(obj);
  });
  return qExpr;
};

/**
 * @param fields
 * @param filter
 * @param query
 * @param filterDriven if set, `include`s will be created for filtered values only
 * @returns {*}
 * @private
 */
SequelizeDataSource.prototype._normalizeFields = function _normalizeFields(fields, filter, query, filterDriven) {
  query.include = query.include || [];
  fields = this._resolveAssociations(this.ModelClass, this.fieldMap, fields, filter, query.include, filterDriven);

  if (!filterDriven && !fields.indexOf(this.ModelClass.primaryKeyField) < 0) {
    fields.push(this.ModelClass.primaryKeyField);
  }

  return fields;
};

SequelizeDataSource.prototype._resolveAssociations = function (ModelClass, fieldMap, fields, filter, include, filterDriven) {
  include = include || [];
  var fieldsToRemove = [];
  var filterToRemove = [];
  var result = _.map(fields, function (field) {
    var isObject = typeof(field) === 'object';
    var fieldName = isObject ? field.name : field;
    var fieldMeta = fieldMap[fieldName];
    var association = ModelClass.associations[fieldName];
    if (fieldMeta && association) {
      if (!filterDriven) {
        fieldsToRemove.push(fieldName);
      }

      var associationFields;
      if (isObject && field.fields) {
        associationFields = field.fields;
      } else if (fieldMeta.fields) {
        associationFields = fieldMeta.fields;
      } else {
        associationFields = [association.targetKey];
      }

      var filterValue = filter ? filter[fieldName] : undefined;

      var nestedInclude = [];

      associationFields = this._resolveAssociations(association.target, fieldMeta.fields, associationFields, filterValue, nestedInclude, filterDriven);

      var cleanFilterValue = filterValue ? _.transform(filterValue, function (result, value, key) {
        if (associationFields.indexOf(key) >= 0) {
          result[key] = value;
        }
        return result;
      }) : undefined;

      if (cleanFilterValue) {
        filterToRemove.push(fieldName);
      }

      if (!filterDriven || cleanFilterValue) {
        // put to include
        include.push({
          association: association,
          attributes: associationFields,
          include: nestedInclude,
          where: cleanFilterValue
        });
      }
    }
    return fieldName;
  }, this);

  if (!filterDriven) {
    _.each(fieldsToRemove, function (field) {
      _.pull(result, field);
    });
  }

  _.each(filterToRemove, function (filterItem) {
    delete filter[filterItem];
  });

  return result;
};

SequelizeDataSource.prototype._normalizeFilter = function _normalizeFilter(filter, root) {
  // TODO: Implement
  //_.forEach(_.keys(filter), function (key) {
  //  var path = root.schema.paths[key];
  //  // if it's an operator
  //  if (key.substr(0, 1) === '$') {
  //    // increase the level without changing the root
  //    this._normalizeFilter(filter[key], root);
  //  } else if (path) {
  //    var typeName = path.options.type.name;
  //    // it's embedded document
  //    if (!_.isUndefined(path.schema)) {
  //      this._normalizeFilter(filter[key], root.schema.paths[key]);
  //    } else if (typeName === 'ObjectId') {
  //      if (typeof(filter[key]) === 'string') {
  //        filter[key] = ObjectID(filter[key]);
  //      }
  //    } else if (typeName === 'Date') {
  //      if (typeof(filter[key]) === 'string') {
  //        filter[key] = new Date(filter[key]);
  //      }
  //      else if (typeof(filter[key]) === 'object') {
  //        _.forOwn(filter[key], function (value, innerKey) {
  //          if (typeof(value) === 'string') {
  //            filter[key][innerKey] = new Date(value);
  //          }
  //        });
  //      }
  //    }
  //  }
  //}, this);
};

SequelizeDataSource.prototype._resolveProp = function _resolveProp(obj, stringPath) {
  stringPath = stringPath.replace(/\[(\w+)]/g, '.$1');  // convert indexes to properties
  stringPath = stringPath.replace(/^\./, '');           // strip a leading dot
  var pathArray = stringPath.split('.');
  while (pathArray.length) {
    var pathItem = pathArray.shift();
    if (pathItem in obj) {
      obj = obj[pathItem];
    } else {
      return;
    }
  }
  return obj;
};

SequelizeDataSource.prototype._setProp = function _setProp(obj, stringPath, value) {
  stringPath = stringPath.replace(/\[(\w+)]/g, '.$1');  // convert indexes to properties
  stringPath = stringPath.replace(/^\./, '');           // strip a leading dot
  var pathArray = stringPath.split('.');
  while (pathArray.length - 1) {
    var pathItem = pathArray.shift();
    if (pathItem in obj) {
      obj = obj[pathItem];
    } else {
      return;
    }
  }
  return obj[pathArray.length ? pathArray[0] : stringPath] = value;
};

SequelizeDataSource.prototype.defaultIdField = 'id';
SequelizeDataSource.prototype.defaultArrayMethods = ['$addToSet', '$pop', '$push', '$pull'];

module.exports = SequelizeDataSource;

<|endoftext|>


<commit_before>function ProjectionStore(type){
	this.insertSql = 'insert into pge_projections_* (id, data) values ($1, $2)'.replace('*', type);
	this.updateSql = 'update pge_projections_* set data = $2 where id = $1'.replace('*', type);
	this.findSql = 'select data from pge_projections_* where id = $1'.replace('*', type);
	this.cleanSql = 'truncate table pge_projections_* CASCADE'.replace('*', type);
	this.updateProjectionSql = 'select pge_upsert_*_view($1, $2)'.replace('*', type);

	this.type = type;

	return this;
};

// TODO -- come harden everything against failures
ProjectionStore.prototype.find = function(id){
	var rows = plv8.execute(this.findSql, [id]);
	if (rows.length == 0) return null;

	return rows[0].data;
};

ProjectionStore.prototype.update = function(id, data){
	var count = plv8.execute(this.updateSql, [id, data]);
	if (count == 0){
		plv8.execute(this.insertSql, [id, data]);
	}
};

ProjectionStore.prototype.cleanAll = function(){
	plv8.execute(this.cleanSql);
};

ProjectionStore.prototype.updateProjection = function(id, evt){
	plv8.execute(this.updateProjectionSql, [id, evt]);
};

var storage = {
	newId: function(){
		var raw = plv8.execute('select uuid_generate_v1mc()');
		return raw[0].uuid_generate_v1mc;
	},

	findStream: function(id){
		if (id == null) return null;

		var raw = plv8.execute('select for update version, type, snapshot_version from pge_streams where id = $1', [id]);
		if (raw.length == 0) return null;

		return {version: parseInt(raw[0].version), type: raw[0].type, id: id, snapshotVersion: raw[0].snapshot_version};
	},

	insertStream: function(id, version, streamType){
		plv8.execute("insert into pge_streams (id, version, type) values ($1, $2, $3)", [id, version, streamType]);
	},

	updateStream: function(id, version){
		plv8.execute("update pge_streams set version = $1 where id = $2", [version, id]);
	},

	updateProjection: function(name, id, evt){
		this.forViewType(name).updateProjection(id, evt);
	},

	appendEvent: function(streamId, version, data, eventType, eventId){
		if (eventId == null){
			eventId = this.newId();
			data.$id = eventId;
		}

		plv8.execute("insert into pge_events (stream_id, id, version, data, type) values ($1, $2, $3, $4, $5)",
			[streamId, eventId, version, JSON.stringify(data), eventType]);
	},

	findCurrentSnapshot: function(id){
		var rows = plv8.execute('select snapshot, snapshot_version, type from pge_streams where id = $1', [id]);
		if (rows.length == 0){
			throw 'Unable to find a stream with id ' + id;
		}

		return {version: rows[0].snapshot_version || 0, data: rows[0].snapshot, type: rows[0].type};
	},

	persistSnapshot: function(id, data, version){
		var count = plv8.execute('update pge_streams set snapshot = $1, snapshot_version = $2 where id = $3', [data, version, id]);
		if (count == 0){
			throw 'No matching stream for id = ' + id;
		}
	},

	findEventsAfter: function(id, version){
		return plv8.execute('select data, version from pge_events where stream_id = $1 and version > $2 order by version', [id, version]);
	},

	findEventsUpTo: function(id, version){
		var rows = plv8.execute('select data, version from pge_events where stream_id = $1 and version > $2 order by version', [id, version]);

		return rows.map(function(row){
			return row.data;
		});
	},

	stores: {},

	findAggregate: function(name){
		var rows = plv8.execute('select data from pge_aggregates where name = $1', [name]);

		if (rows.length == 0) return null;
		return rows[0].data;
	},

	storeAggregate: function(name, data){
		var count = plv8.execute("update pge_aggregates set data = $2 where name = $1", [name, data]);
		if (count == 0){
			plv8.execute("insert into pge_aggregates (name, data) values ($1, $2)", [name, data]);
		}
	},

	// TODO -- this probably shouldn't be available in production mode
	cleanAll: function(){
		plv8.execute('truncate table pge_aggregates CASCADE');

		for (key in this.stores){
			this.stores[key].cleanAll();
		}
	}
};

require('./store-base')(ProjectionStore, storage);

module.exports = storage;<commit_msg>checkpoint: spiked in PostgresStore.queueProjectionEvent()<commit_after>function ProjectionStore(type){
	this.insertSql = 'insert into pge_projections_* (id, data) values ($1, $2)'.replace('*', type);
	this.updateSql = 'update pge_projections_* set data = $2 where id = $1'.replace('*', type);
	this.findSql = 'select data from pge_projections_* where id = $1'.replace('*', type);
	this.cleanSql = 'truncate table pge_projections_* CASCADE'.replace('*', type);
	this.updateProjectionSql = 'select pge_upsert_*_view($1, $2)'.replace('*', type);
	this.queueSql = 'select pge_*_append_rolling_buffer($1, $2)'.replace('*', type);

	this.type = type;

	return this;
};

ProjectionStore.prototype.queue = function(eventId, streamId){
	plv8.execute(this.queueSql, [eventId, streamId]);
}

// TODO -- come harden everything against failures
ProjectionStore.prototype.find = function(id){
	var rows = plv8.execute(this.findSql, [id]);
	if (rows.length == 0) return null;

	return rows[0].data;
};

ProjectionStore.prototype.update = function(id, data){
	var count = plv8.execute(this.updateSql, [id, data]);
	if (count == 0){
		plv8.execute(this.insertSql, [id, data]);
	}
};

ProjectionStore.prototype.cleanAll = function(){
	plv8.execute(this.cleanSql);
};

ProjectionStore.prototype.updateProjection = function(id, evt){
	plv8.execute(this.updateProjectionSql, [id, evt]);
};

var storage = {
	queueProjectionEvent: function(name, id, stream){
		this.forViewType(name).queue(id, stream);
	},

	newId: function(){
		var raw = plv8.execute('select uuid_generate_v1mc()');
		return raw[0].uuid_generate_v1mc;
	},

	findStream: function(id){
		if (id == null) return null;

		var raw = plv8.execute('select for update version, type, snapshot_version from pge_streams where id = $1', [id]);
		if (raw.length == 0) return null;

		return {version: parseInt(raw[0].version), type: raw[0].type, id: id, snapshotVersion: raw[0].snapshot_version};
	},

	insertStream: function(id, version, streamType){
		plv8.execute("insert into pge_streams (id, version, type) values ($1, $2, $3)", [id, version, streamType]);
	},

	updateStream: function(id, version){
		plv8.execute("update pge_streams set version = $1 where id = $2", [version, id]);
	},

	updateProjection: function(name, id, evt){
		this.forViewType(name).updateProjection(id, evt);
	},

	appendEvent: function(streamId, version, data, eventType, eventId){
		if (eventId == null){
			eventId = this.newId();
			data.$id = eventId;
		}

		plv8.execute("insert into pge_events (stream_id, id, version, data, type) values ($1, $2, $3, $4, $5)",
			[streamId, eventId, version, JSON.stringify(data), eventType]);
	},

	findCurrentSnapshot: function(id){
		var rows = plv8.execute('select snapshot, snapshot_version, type from pge_streams where id = $1', [id]);
		if (rows.length == 0){
			throw 'Unable to find a stream with id ' + id;
		}

		return {version: rows[0].snapshot_version || 0, data: rows[0].snapshot, type: rows[0].type};
	},

	persistSnapshot: function(id, data, version){
		var count = plv8.execute('update pge_streams set snapshot = $1, snapshot_version = $2 where id = $3', [data, version, id]);
		if (count == 0){
			throw 'No matching stream for id = ' + id;
		}
	},

	findEventsAfter: function(id, version){
		return plv8.execute('select data, version from pge_events where stream_id = $1 and version > $2 order by version', [id, version]);
	},

	findEventsUpTo: function(id, version){
		var rows = plv8.execute('select data, version from pge_events where stream_id = $1 and version > $2 order by version', [id, version]);

		return rows.map(function(row){
			return row.data;
		});
	},

	stores: {},

	findAggregate: function(name){
		var rows = plv8.execute('select data from pge_aggregates where name = $1', [name]);

		if (rows.length == 0) return null;
		return rows[0].data;
	},

	storeAggregate: function(name, data){
		var count = plv8.execute("update pge_aggregates set data = $2 where name = $1", [name, data]);
		if (count == 0){
			plv8.execute("insert into pge_aggregates (name, data) values ($1, $2)", [name, data]);
		}
	},

	// TODO -- this probably shouldn't be available in production mode
	cleanAll: function(){
		plv8.execute('truncate table pge_aggregates CASCADE');

		for (key in this.stores){
			this.stores[key].cleanAll();
		}
	}
};

require('./store-base')(ProjectionStore, storage);

module.exports = storage;<|endoftext|>