<commit_before>var
	/**
	 * Namespace for all classes, static methods and static properties.
	 * @class OO
	 * @singleton
	 */
	oo = {},
	hasOwn = Object.prototype.hasOwnProperty;

/* Class Methods */

/**
 * Utility for common usage of Object#create for inheriting from one
 * prototype to another.
 *
 * Beware: This redefines the prototype, call before setting your prototypes.
 * Beware: This redefines the prototype, can only be called once on a function.
 *  If called multiple times on the same function, the previous prototype is lost.
 *  This is how prototypal inheritance works, it can only be one straight chain
 *  (just like classical inheritance in PHP for example). If you need to work with
 *  multiple constructors consider storing an instance of the other constructor in a
 *  property instead, or perhaps use a mixin (see oo.mixinClass).
 *
 *     function Foo() {}
 *     Foo.prototype.jump = function () {};
 *
 *     function FooBar() {}
 *     oo.inheritClass( FooBar, Foo );
 *     FooBar.prop.feet = 2;
 *     FooBar.prototype.walk = function () {};
 *
 *     function FooBarQuux() {}
 *     OO.inheritClass( FooBarQuux, FooBar );
 *     FooBarQuux.prototype.jump = function () {};
 *
 *     FooBarQuux.prop.feet === 2;
 *     var fb = new FooBar();
 *     fb.jump();
 *     fb.walk();
 *     fb instanceof Foo && fb instanceof FooBar && fb instanceof FooBarQuux;
 *
 * @method
 * @param {Function} targetFn
 * @param {Function} originFn
 * @throws {Error} If target already inherits from origin
 */
oo.inheritClass = function ( targetFn, originFn ) {
	if ( targetFn.prototype instanceof originFn ) {
		throw new Error( 'Target already inherits from origin' );
	}

	var targetConstructor = targetFn.prototype.constructor;

	targetFn.prototype = Object.create( originFn.prototype );

	// Restore constructor property of targetFn
	targetFn.prototype.constructor = targetConstructor;

	// Extend static properties - always initialize both sides
	originFn.static = originFn.static || {};
	targetFn.static = Object.create( originFn.static );

	// Copy mixin tracking
	targetFn.mixins = originFn.mixins ? originFn.mixins.slice( 0 ) : [];
};

/**
 * Utility to copy over *own* prototype properties of a mixin.
 * The 'constructor' (whether implicit or explicit) is not copied over.
 *
 * This does not create inheritance to the origin. If inheritance is needed
 * use oo.inheritClass instead.
 *
 * Beware: This can redefine a prototype property, call before setting your prototypes.
 * Beware: Don't call before oo.inheritClass.
 *
 *     function Foo() {}
 *     function Context() {}
 *
 *     // Avoid repeating this code
 *     function ContextLazyLoad() {}
 *     ContextLazyLoad.prototype.getContext = function () {
 *         if ( !this.context ) {
 *             this.context = new Context();
 *         }
 *         return this.context;
 *     };
 *
 *     function FooBar() {}
 *     OO.inheritClass( FooBar, Foo );
 *     OO.mixinClass( FooBar, ContextLazyLoad );
 *
 * @method
 * @param {Function} targetFn
 * @param {Function} originFn
 */
oo.mixinClass = function ( targetFn, originFn ) {
	var key;

	// Copy prototype properties
	for ( key in originFn.prototype ) {
		if ( key !== 'constructor' && hasOwn.call( originFn.prototype, key ) ) {
			targetFn.prototype[key] = originFn.prototype[key];
		}
	}

	// Copy static properties - always initialize both sides
	targetFn.static = targetFn.static || {};
	if ( originFn.static ) {
		for ( key in originFn.static ) {
			if ( hasOwn.call( originFn.static, key ) ) {
				targetFn.static[key] = originFn.static[key];
			}
		}
	} else {
		originFn.static = {};
	}
};

/* Object Methods */

/**
 * Create a new object that is an instance of the same
 * constructor as the input, inherits from the same object
 * and contains the same own properties.
 *
 * This makes a shallow non-recursive copy of own properties.
 * To create a recursive copy of plain objects, use #copy.
 *
 *     var foo = new Person( mom, dad );
 *     foo.setAge( 21 );
 *     var foo2 = OO.cloneObject( foo );
 *     foo.setAge( 22 );
 *
 *     // Then
 *     foo2 !== foo; // true
 *     foo2 instanceof Person; // true
 *     foo2.getAge(); // 21
 *     foo.getAge(); // 22
 *
 * @method
 * @param {Object} origin
 * @return {Object} Clone of origin
 */
oo.cloneObject = function ( origin ) {
	var key, r;

	r = Object.create( origin.constructor.prototype );

	for ( key in origin ) {
		if ( hasOwn.call( origin, key ) ) {
			r[key] = origin[key];
		}
	}

	return r;
};

/**
 * Gets an array of all property values in an object.
 *
 * @method
 * @param {Object} Object to get values from
 * @returns {Array} List of object values
 */
oo.getObjectValues = function ( obj ) {
	var key, values;

	if ( obj !== Object( obj ) ) {
		throw new TypeError( 'Called on non-object' );
	}

	values = [];
	for ( key in obj ) {
		if ( hasOwn.call( obj, key ) ) {
			values[values.length] = obj[key];
		}
	}

	return values;
};

/**
 * Recursively compares string and number property between two objects.
 *
 * A false result may be caused by property inequality or by properties in one object missing from
 * the other. An asymmetrical test may also be performed, which checks only that properties in the
 * first object are present in the second object, but not the inverse.
 *
 * @method
 * @param {Object} a First object to compare
 * @param {Object} b Second object to compare
 * @param {boolean} [asymmetrical] Whether to check only that b contains values from a
 * @returns {boolean} If the objects contain the same values as each other
 */
oo.compareObjects = function ( a, b, asymmetrical ) {
	var aValue, bValue, aType, bType, k;
	for ( k in a ) {
		aValue = a[k];
		bValue = b[k];
		aType = typeof aValue;
		bType = typeof bValue;
		if ( aType !== bType ||
			( ( aType === 'string' || aType === 'number' ) && aValue !== bValue ) ||
			( aValue === Object( aValue ) && !oo.compareObjects( aValue, bValue ) ) ) {
			return false;
		}
	}
	// If the check is not asymmetrical, recursing with the arguments swapped will verify our result
	return asymmetrical ? true : oo.compareObjects( b, a, true );
};

/**
 * Create a plain deep copy of any kind of object.
 *
 * Copies are deep, and will either be an object or an array depending on `source`.
 *
 * @method
 * @param {Object} source Object to copy
 * @param {Function} [callback] Applied to leaf values before they added to the clone
 * @returns {Object} Copy of source object
 */
oo.copy = function ( source, callback ) {
	var key, sourceValue, sourceType, destination;

	if ( typeof source.clone === 'function' ) {
		return source.clone();
	}

	destination = Array.isArray( source ) ? new Array( source.length ) : {};

	for ( key in source ) {
		sourceValue = source[key];
		sourceType = typeof sourceValue;
		if ( Array.isArray( sourceValue ) ) {
			// Array
			destination[key] = oo.copy( sourceValue, callback );
		} else if ( sourceValue && typeof sourceValue.clone === 'function' ) {
			// Duck type object with custom clone method
			destination[key] = callback ?
				callback( sourceValue.clone() ) : sourceValue.clone();
		} else if ( sourceValue && typeof sourceValue.cloneNode === 'function' ) {
			// DOM Node
			destination[key] = callback ?
				callback( sourceValue.cloneNode( true ) ) : sourceValue.cloneNode( true );
		} else if ( sourceValue === Object( sourceValue ) && typeof sourceValue !== 'function' ) {
			// Other objects, except for functions that we cannot clone
			destination[key] = oo.copy( sourceValue, callback );
		} else {
			// Primitive values and functions
			destination[key] = callback ? callback( sourceValue ) : sourceValue;
		}
	}

	return destination;
};
<commit_msg>core: Rename oo.compareObjects to oo.compare<commit_after>var
	/**
	 * Namespace for all classes, static methods and static properties.
	 * @class OO
	 * @singleton
	 */
	oo = {},
	hasOwn = Object.prototype.hasOwnProperty;

/* Class Methods */

/**
 * Utility for common usage of Object#create for inheriting from one
 * prototype to another.
 *
 * Beware: This redefines the prototype, call before setting your prototypes.
 * Beware: This redefines the prototype, can only be called once on a function.
 *  If called multiple times on the same function, the previous prototype is lost.
 *  This is how prototypal inheritance works, it can only be one straight chain
 *  (just like classical inheritance in PHP for example). If you need to work with
 *  multiple constructors consider storing an instance of the other constructor in a
 *  property instead, or perhaps use a mixin (see oo.mixinClass).
 *
 *     function Foo() {}
 *     Foo.prototype.jump = function () {};
 *
 *     function FooBar() {}
 *     oo.inheritClass( FooBar, Foo );
 *     FooBar.prop.feet = 2;
 *     FooBar.prototype.walk = function () {};
 *
 *     function FooBarQuux() {}
 *     OO.inheritClass( FooBarQuux, FooBar );
 *     FooBarQuux.prototype.jump = function () {};
 *
 *     FooBarQuux.prop.feet === 2;
 *     var fb = new FooBar();
 *     fb.jump();
 *     fb.walk();
 *     fb instanceof Foo && fb instanceof FooBar && fb instanceof FooBarQuux;
 *
 * @method
 * @param {Function} targetFn
 * @param {Function} originFn
 * @throws {Error} If target already inherits from origin
 */
oo.inheritClass = function ( targetFn, originFn ) {
	if ( targetFn.prototype instanceof originFn ) {
		throw new Error( 'Target already inherits from origin' );
	}

	var targetConstructor = targetFn.prototype.constructor;

	targetFn.prototype = Object.create( originFn.prototype );

	// Restore constructor property of targetFn
	targetFn.prototype.constructor = targetConstructor;

	// Extend static properties - always initialize both sides
	originFn.static = originFn.static || {};
	targetFn.static = Object.create( originFn.static );

	// Copy mixin tracking
	targetFn.mixins = originFn.mixins ? originFn.mixins.slice( 0 ) : [];
};

/**
 * Utility to copy over *own* prototype properties of a mixin.
 * The 'constructor' (whether implicit or explicit) is not copied over.
 *
 * This does not create inheritance to the origin. If inheritance is needed
 * use oo.inheritClass instead.
 *
 * Beware: This can redefine a prototype property, call before setting your prototypes.
 * Beware: Don't call before oo.inheritClass.
 *
 *     function Foo() {}
 *     function Context() {}
 *
 *     // Avoid repeating this code
 *     function ContextLazyLoad() {}
 *     ContextLazyLoad.prototype.getContext = function () {
 *         if ( !this.context ) {
 *             this.context = new Context();
 *         }
 *         return this.context;
 *     };
 *
 *     function FooBar() {}
 *     OO.inheritClass( FooBar, Foo );
 *     OO.mixinClass( FooBar, ContextLazyLoad );
 *
 * @method
 * @param {Function} targetFn
 * @param {Function} originFn
 */
oo.mixinClass = function ( targetFn, originFn ) {
	var key;

	// Copy prototype properties
	for ( key in originFn.prototype ) {
		if ( key !== 'constructor' && hasOwn.call( originFn.prototype, key ) ) {
			targetFn.prototype[key] = originFn.prototype[key];
		}
	}

	// Copy static properties - always initialize both sides
	targetFn.static = targetFn.static || {};
	if ( originFn.static ) {
		for ( key in originFn.static ) {
			if ( hasOwn.call( originFn.static, key ) ) {
				targetFn.static[key] = originFn.static[key];
			}
		}
	} else {
		originFn.static = {};
	}
};

/* Object Methods */

/**
 * Create a new object that is an instance of the same
 * constructor as the input, inherits from the same object
 * and contains the same own properties.
 *
 * This makes a shallow non-recursive copy of own properties.
 * To create a recursive copy of plain objects, use #copy.
 *
 *     var foo = new Person( mom, dad );
 *     foo.setAge( 21 );
 *     var foo2 = OO.cloneObject( foo );
 *     foo.setAge( 22 );
 *
 *     // Then
 *     foo2 !== foo; // true
 *     foo2 instanceof Person; // true
 *     foo2.getAge(); // 21
 *     foo.getAge(); // 22
 *
 * @method
 * @param {Object} origin
 * @return {Object} Clone of origin
 */
oo.cloneObject = function ( origin ) {
	var key, r;

	r = Object.create( origin.constructor.prototype );

	for ( key in origin ) {
		if ( hasOwn.call( origin, key ) ) {
			r[key] = origin[key];
		}
	}

	return r;
};

/**
 * Gets an array of all property values in an object.
 *
 * @method
 * @param {Object} Object to get values from
 * @returns {Array} List of object values
 */
oo.getObjectValues = function ( obj ) {
	var key, values;

	if ( obj !== Object( obj ) ) {
		throw new TypeError( 'Called on non-object' );
	}

	values = [];
	for ( key in obj ) {
		if ( hasOwn.call( obj, key ) ) {
			values[values.length] = obj[key];
		}
	}

	return values;
};

/**
 * Recursively compares string and number property between two objects.
 *
 * A false result may be caused by property inequality or by properties in one object missing from
 * the other. An asymmetrical test may also be performed, which checks only that properties in the
 * first object are present in the second object, but not the inverse.
 *
 * @method
 * @param {Object} a First object to compare
 * @param {Object} b Second object to compare
 * @param {boolean} [asymmetrical] Whether to check only that b contains values from a
 * @returns {boolean} If the objects contain the same values as each other
 */
oo.compare = function ( a, b, asymmetrical ) {
	var aValue, bValue, aType, bType, k;
	for ( k in a ) {
		aValue = a[k];
		bValue = b[k];
		aType = typeof aValue;
		bType = typeof bValue;
		if ( aType !== bType ||
			( ( aType === 'string' || aType === 'number' ) && aValue !== bValue ) ||
			( aValue === Object( aValue ) && !oo.compare( aValue, bValue ) ) ) {
			return false;
		}
	}
	// If the check is not asymmetrical, recursing with the arguments swapped will verify our result
	return asymmetrical ? true : oo.compare( b, a, true );
};

/**
 * Create a plain deep copy of any kind of object.
 *
 * Copies are deep, and will either be an object or an array depending on `source`.
 *
 * @method
 * @param {Object} source Object to copy
 * @param {Function} [callback] Applied to leaf values before they added to the clone
 * @returns {Object} Copy of source object
 */
oo.copy = function ( source, callback ) {
	var key, sourceValue, sourceType, destination;

	if ( typeof source.clone === 'function' ) {
		return source.clone();
	}

	destination = Array.isArray( source ) ? new Array( source.length ) : {};

	for ( key in source ) {
		sourceValue = source[key];
		sourceType = typeof sourceValue;
		if ( Array.isArray( sourceValue ) ) {
			// Array
			destination[key] = oo.copy( sourceValue, callback );
		} else if ( sourceValue && typeof sourceValue.clone === 'function' ) {
			// Duck type object with custom clone method
			destination[key] = callback ?
				callback( sourceValue.clone() ) : sourceValue.clone();
		} else if ( sourceValue && typeof sourceValue.cloneNode === 'function' ) {
			// DOM Node
			destination[key] = callback ?
				callback( sourceValue.cloneNode( true ) ) : sourceValue.cloneNode( true );
		} else if ( sourceValue === Object( sourceValue ) && typeof sourceValue !== 'function' ) {
			// Other objects, except for functions that we cannot clone
			destination[key] = oo.copy( sourceValue, callback );
		} else {
			// Primitive values and functions
			destination[key] = callback ? callback( sourceValue ) : sourceValue;
		}
	}

	return destination;
};
<|endoftext|>


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

/**
 * Markdown-plus-plus is a project of markdown syntax highlighting for Notepad++, by customized UDL file (user defined language).
 * This project is open source on:
 * [Source]{@link https://github.com/Edditoria/markdown-plus-plus}.
 * Code released under the MIT license:
 * [License]{@link https://github.com/Edditoria/validid/blob/master/LICENSE.txt}.
 *
 * @file Build UML XML file(s) in current working directory.
 * @auther Edditoria
 * @license MIT
 */

var fs = require('fs');
var path = require('path');

/*
Expected usage:
```cmd
npx markdown-plus-plus [options]    # In UDL folder of Notepad++
mpp [options]                       # After install this package globally
npm run mpp -- [options]            # Develop in package directory
````
*/
/* @type {Array<string>} - Usage messages to print */
var usageMsg = [
	'Build UML XML file(s) for Notepad++ in current working directory.',
	'',
	'Usage: npx markdown-plus-plus [theme-name...] [--all] [-f | --force]',
	'',
	'Options:',
	'theme-name       Build UDL file(s) for specified theme(s)',
	'                 You can give multiple theme names',
	'                 e.g. npx markdown-plus-plus solarized zenburn deep-black',
	'-f, --force      Force to overwrite any file that already exists in directory',
	'-l, --list       List bundled themes in this package',
	'-v, --version    Print current version of this package',
	'-h, --help       Print usage',
	'',
	'Examples:',
	'npx markdown-plus-plus               Build all UDL files without overwrite',
	'npx markdown-plus-plus zenburn       Build Zenburn UDL file without overwrite',
	'npx markdown-plus-plus zenburn -f    Build Zenburn UDL file and overwrite',
	''
];

/* @type {string} - Version of this npm package. */
var version = process.env.npm_package_version;
/* @type {Array<string>} - Arguments from node. */
var args = process.argv.slice(2);
/* @type {string} - Current working directory. */
var cwd = process.cwd();
/* @type {string} - Root directory of this NPM package. */
var packagePath = path.resolve(__dirname, '../');
/* @type {string} - Path for <udl/> in this package. */
var udlPath = packagePath + '/udl';

/*
 * Parse arguments passed from user command input.
 * "-f" can only be passed at first or last argument.
 * "-l/-h/-v" can only be passed at first argument.
 * @param {Array<string>} args - Arguments from Node's `process.argv.slice(2)`.
 * @param {buildinUdls} buildinUdls - Basically the theme names and filenames in <udl/>.
 * @return {Object} - The parsed object passing to build steps.
 */
var parseArgs = function(args, buildinUdls) {
	var options = {
		themeList: [],
		fileList: [],
		shouldContinue: false,
		isForce: false
	};
	// Parse the first argument. Quit asap if user put a proper flag.
	var firstArg = args[0];
	switch (firstArg) {
		case '-l': case '--list':
			console.log(buildinUdls.themeList.join('\n'));
			return options;
		case '-v': case '--version':
			console.log(version);
			return options;
		case '-h': case '--help':
			console.log(usageMsg.join('\n'));
			return options;
	}
	// Loop all arguments. Match the theme names, otherwise the program should quits.
	var arg, indexOfTheme;
	for (var i = 0; i < args.length; i++) {
		arg = args[i];
		if (arg === '-f' || arg === '--force') {
			if (i === 0 || i === args.length - 1) {
				console.log('Warning! In force mode, any existing file(s) will be overwritten.\n');
				options.isForce = true;
			} else {
				console.log('arg[' + i + ']: You pass the \"' + arg + '\" flag in wrong position. Quit for safety.');
				return options;
			}
		} else if (buildinUdls.themeList.indexOf(arg) >= 0) {
			options.themeList.push(arg);
			indexOfTheme = buildinUdls.themeList.indexOf(arg);
			options.fileList.push(buildinUdls.fileList[indexOfTheme]);
		} else {
			console.log('arg[' + i + ']: ' + 'Unsupported option \"' + arg + '\" or combination of options.');
			return options;
		}
	}
	options.shouldContinue = true;
	return options;
};

/**
 * Get file list in a directory.
 * Similar to `dir /w` in command prompt and `ls` in bash.
 * Expect program quit with error if it does not find the directory.
 * @param {string} dir - Path of a directory.
 * @return {string[]} - An array as a list of filenames.
 */
var getFileList = function(dir) {
	return fs.readdirSync(dir);
};

/**
 * Get theme name from a UDL XML filename, assuming the filename is in proper format.
 * @param {string} filename - Expected format: 'markdown.hyphen-lowercase-theme-name.udl.xml'.
 * @return {string}
 */
var getThemeName = function(filename) {
	var reHead = /^(markdown\.)/;
	var reTail = /(\.udl\.xml)$/;
	return filename.replace(reHead, '').replace(reTail, '');
};

/**
 * Get a list of theme names in a directory.
 * Theme names are stripped from filename of the built-in UDL files in <udl/>.
 * @param {Array<string>} - A list of theme names, e.g. ['default', 'zenburn'].
 */
var getThemeList = function(dir) {
	var files = getFileList(dir);
	var themeList = [];
	for (var i = 0; i < files.length; i++) {
		themeList.push(getThemeName(files[i]));
	}
	return themeList;
};

/**
 * @typedef {Object} BuildinUdls
 * @description An object that contains a list of theme names and a list of their filenames.
 * @property {Array<string>} themeList - A list of theme names corresponding to the file list.
 * @property {Array<string>} fileList - A list of filenames in <udl/> of this package.
 */
/**
 * Create a special object that contains information of files in <udl/>.
 * This approach could avoid some wired theme names, e.g. '__proto__'
 * @param {udlPath} - The path of <udl/>.
 * @return {BuildinUdls}
 */
var createBuildinUdls = function(udlPath) {
	var fileList = getFileList(udlPath);
	var themeList = [];
	var filename, themeName;
	for (var i = 0; i < fileList.length; i++) {
		filename = fileList[i];
		themeName = getThemeName(filename);
		themeList.push(themeName);
	}
	return { themeList: themeList, fileList: fileList };
};

/**
 * Copy file from source to destination using `fs.createReadSteam()`.
 * This function will overwritten any files that already exists.
 * This function is created because `fs.copyFile()` requires Node v8.5+.
 * This NPM package targets to serve under Node v4+.
 * @param {string} src - Path of source directory.
 * @param {string} dest - Path of destination directory.
 */
var copyFile = function(src, dest) {
	var readSteam = fs.createReadStream(src);
	readSteam.once('error', function(error) {
		console.log(error);
	});
	// readSteam.once('end', function() {
	// 	console.log('copyFile() done');
	// });
	readSteam.pipe(fs.createWriteStream(dest));
};

/**
 * Build UDL XML file(s) in current working directory.
 * @param {Array} args - This array is produced by Node's `process.argv`.
 */
var main = function(args) {
	/* @type {string[]} - A list of filenames in <udl/> in this package. */
	var fileList = getFileList(packagePath + '/udl');
	// Loop the file list.
	var filename, src, dest, themeName, isExist;
	for (var i = 0; i < fileList.length; i++) {
		filename = fileList[i];
		src = packagePath + '/udl/' + filename;
		dest = cwd + '/' + filename;
		themeName = getThemeName(filename);
		// Synchronously check whether file exists.
		isExist = fs.existsSync(dest);
		// Jump to next iteration if file already exists.
		if (isExist) {
			console.log('[' + themeName + '] Warning: File already exists. Have skipped this one.');
			continue; // Jump to next iteration.
		}
		copyFile(src, dest);
		console.log('[' + themeName + '] Done: UDL file is created');
	}
};

/* @type {BuildinUdls} - Create a special object that contains theme list and file list. */
var buildinUdls = createBuildinUdls(udlPath);
/* @type {Object} - Parse args into options object. */
var options = parseArgs(args, buildinUdls);
console.log(options);
// main(options);
<commit_msg>Improve `main()` that looks for user inputs<commit_after>#!/usr/bin/env node

/**
 * Markdown-plus-plus is a project of markdown syntax highlighting for Notepad++, by customized UDL file (user defined language).
 * This project is open source on:
 * [Source]{@link https://github.com/Edditoria/markdown-plus-plus}.
 * Code released under the MIT license:
 * [License]{@link https://github.com/Edditoria/validid/blob/master/LICENSE.txt}.
 *
 * @file Build UML XML file(s) in current working directory.
 * @auther Edditoria
 * @license MIT
 */

var fs = require('fs');
var path = require('path');

/*
Expected usage:
```cmd
npx markdown-plus-plus [options]    # In UDL folder of Notepad++
mpp [options]                       # After install this package globally
npm run mpp -- [options]            # Develop in package directory
````
*/
/* @type {Array<string>} - Usage messages to print */
var usageMsg = [
	'Build UML XML file(s) for Notepad++ in current working directory.',
	'',
	'Usage: npx markdown-plus-plus [theme-name...] [--all] [-f | --force]',
	'',
	'Options:',
	'theme-name       Build UDL file(s) for specified theme(s)',
	'                 You can give multiple theme names',
	'                 e.g. npx markdown-plus-plus solarized zenburn deep-black',
	'-f, --force      Force to overwrite any file that already exists in directory',
	'-l, --list       List bundled themes in this package',
	'-v, --version    Print current version of this package',
	'-h, --help       Print usage',
	'',
	'Examples:',
	'npx markdown-plus-plus               Build all UDL files without overwrite',
	'npx markdown-plus-plus zenburn       Build Zenburn UDL file without overwrite',
	'npx markdown-plus-plus zenburn -f    Build Zenburn UDL file and overwrite',
	''
];

/* @type {string} - Version of this npm package. */
var version = process.env.npm_package_version;
/* @type {Array<string>} - Arguments from node. */
var args = process.argv.slice(2);
/* @type {string} - Current working directory. */
var cwd = process.cwd();
/* @type {string} - Root directory of this NPM package. */
var packagePath = path.resolve(__dirname, '../');
/* @type {string} - Path for <udl/> in this package. */
var udlPath = packagePath + '/udl';

/*
 * Parse arguments passed from user command input.
 * "-f" can only be passed at first or last argument.
 * "-l/-h/-v" can only be passed at first argument.
 * @param {Array<string>} args - Arguments from Node's `process.argv.slice(2)`.
 * @param {buildinUdls} buildinUdls - Basically the theme names and filenames in <udl/>.
 * @return {Object} - The parsed object passing to build steps.
 */
var parseArgs = function(args, buildinUdls) {
	var options = {
		themeList: [],
		fileList: [],
		shouldContinue: false,
		isForce: false
	};
	// Parse the first argument. Quit asap if user put a proper flag.
	var firstArg = args[0];
	switch (firstArg) {
		case '-l': case '--list':
			console.log(buildinUdls.themeList.join('\n'));
			return options;
		case '-v': case '--version':
			console.log(version);
			return options;
		case '-h': case '--help':
			console.log(usageMsg.join('\n'));
			return options;
	}
	// Loop all arguments. Match the theme names, otherwise the program should quits.
	var arg, indexOfTheme;
	for (var i = 0; i < args.length; i++) {
		arg = args[i];
		if (arg === '-f' || arg === '--force') {
			if (i === 0 || i === args.length - 1) {
				console.log('Warning! In force mode, any existing file(s) will be overwritten.\n');
				options.isForce = true;
			} else {
				console.log('arg[' + i + ']: You pass the \"' + arg + '\" flag in wrong position. Quit for safety.');
				return options;
			}
		} else if (buildinUdls.themeList.indexOf(arg) >= 0) {
			options.themeList.push(arg);
			indexOfTheme = buildinUdls.themeList.indexOf(arg);
			options.fileList.push(buildinUdls.fileList[indexOfTheme]);
		} else {
			console.log('arg[' + i + ']: ' + 'Unsupported option \"' + arg + '\" or combination of options.');
			return options;
		}
	}
	options.shouldContinue = true;
	return options;
};

/**
 * Get file list in a directory.
 * Similar to `dir /w` in command prompt and `ls` in bash.
 * Expect program quit with error if it does not find the directory.
 * @param {string} dir - Path of a directory.
 * @return {string[]} - An array as a list of filenames.
 */
var getFileList = function(dir) {
	return fs.readdirSync(dir);
};

/**
 * Get theme name from a UDL XML filename, assuming the filename is in proper format.
 * @param {string} filename - Expected format: 'markdown.hyphen-lowercase-theme-name.udl.xml'.
 * @return {string}
 */
var getThemeName = function(filename) {
	var reHead = /^(markdown\.)/;
	var reTail = /(\.udl\.xml)$/;
	return filename.replace(reHead, '').replace(reTail, '');
};

/**
 * Get a list of theme names in a directory.
 * Theme names are stripped from filename of the built-in UDL files in <udl/>.
 * @param {Array<string>} - A list of theme names, e.g. ['default', 'zenburn'].
 */
var getThemeList = function(dir) {
	var files = getFileList(dir);
	var themeList = [];
	for (var i = 0; i < files.length; i++) {
		themeList.push(getThemeName(files[i]));
	}
	return themeList;
};

/**
 * @typedef {Object} BuildinUdls
 * @description An object that contains a list of theme names and a list of their filenames.
 * @property {Array<string>} themeList - A list of theme names corresponding to the file list.
 * @property {Array<string>} fileList - A list of filenames in <udl/> of this package.
 */
/**
 * Create a special object that contains information of files in <udl/>.
 * This approach could avoid some wired theme names, e.g. '__proto__'
 * @param {udlPath} - The path of <udl/>.
 * @return {BuildinUdls}
 */
var createBuildinUdls = function(udlPath) {
	var fileList = getFileList(udlPath);
	var themeList = [];
	var filename, themeName;
	for (var i = 0; i < fileList.length; i++) {
		filename = fileList[i];
		themeName = getThemeName(filename);
		themeList.push(themeName);
	}
	return { themeList: themeList, fileList: fileList };
};

/**
 * Copy file from source to destination using `fs.createReadSteam()`.
 * This function will overwritten any files that already exists.
 * This function is created because `fs.copyFile()` requires Node v8.5+.
 * This NPM package targets to serve under Node v4+.
 * @param {string} src - Path of source directory.
 * @param {string} dest - Path of destination directory.
 */
var copyFile = function(src, dest) {
	var readSteam = fs.createReadStream(src);
	readSteam.once('error', function(error) {
		console.log(error);
	});
	// readSteam.once('end', function() {
	// 	console.log('copyFile() done');
	// });
	readSteam.pipe(fs.createWriteStream(dest));
};

/**
 * Build UDL XML file(s) in current working directory.
 * Normally the file list should be checked and they are available in <udl/>.
 * @param {Object} options - User options originally from command line.
 * @param {BuildinUdls} - Will use it when need to build all built-in files.
 */
var main = function(options, buildinUdls) {
	var isForce = options.isForce;
	var isBuildAll = options.themeList.length === 0;
	var themeList = isBuildAll ? buildinUdls.themeList : options.themeList;
	var fileList = isBuildAll ? buildinUdls.fileList : options.fileList;
	// Loop the file list.
	var filename, src, dest, themeName, isExist;
	for (var i = 0; i < fileList.length; i++) {
		filename = fileList[i];
		src = packagePath + '/udl/' + filename;
		dest = cwd + '/' + filename;
		themeName = themeList[i];
		// Synchronously check whether file exists.
		isExist = fs.existsSync(dest);
		// Jump to next iteration if file already exists and not in force mode.
		if (isExist && !isForce) {
			console.log('[' + themeName + '] Warning: File already exists. Have skipped this one.');
			continue; // Jump to next iteration.
		}
		copyFile(src, dest);
		console.log('[' + themeName + '] Done: UDL file is created');
	}
};

/* @type {BuildinUdls} - Create a special object that contains theme list and file list. */
var buildinUdls = createBuildinUdls(udlPath);
/* @type {Object} - Parse args into options object. */
var options = parseArgs(args, buildinUdls);
if (options.shouldContinue) {
	main(options, buildinUdls);
}
<|endoftext|>


<commit_before>// @flow

import React, { Component, type Node } from 'react';
import Icon from 'app/components/Icon';
import LoadingIndicator from 'app/components/LoadingIndicator';
import { TextInput, CheckBox } from 'app/components/Form';
import { Overlay } from 'react-overlays';
import cx from 'classnames';
import { isEmpty } from 'lodash';
import InfiniteScroll from 'react-infinite-scroller';
import styles from './Table.css';
import { get } from 'lodash';

type sortProps = {
  direction?: 'asc' | 'desc',
  sort?: string
};

type checkFilter = {
  label: string,
  value: any
};

type columnProps = {
  dataIndex: string,
  title?: string,
  sorter?: (any, any) => number,
  filter?: Array<checkFilter>,
  /*
   * Map the value to to "another" value to use
   * for filtering. Eg. the result from the backend
   * is in english, and the search should be in norwegian
   *
   */
  filterMapping?: any => any,
  search?: boolean,
  width?: number,
  render?: (any, Object) => Node,
  // Should column be rendered. Will render when not set
  visible?: boolean
};

type Props = {
  rowKey?: string,
  columns: Array<columnProps>,
  data: Array<Object>,
  hasMore: boolean,
  loading: boolean,
  onChange?: (filters: Object, sort: sortProps) => void,
  onLoad?: (filters: Object, sort: sortProps) => void
};

type State = {
  filters: Object,
  isShown: Object,
  sort: sortProps
};

export default class Table extends Component<Props, State> {
  props: Props;
  components: { [string]: ?HTMLDivElement } = {};

  state: State = {
    sort: {},
    filters: {},
    isShown: {}
  };

  static defaultProps = {
    rowKey: 'id'
  };

  toggleSearch = (dataIndex: string) => {
    this.setState({
      isShown: {
        [dataIndex]: !this.state.isShown[dataIndex]
      }
    });
  };

  toggleFilter = (dataIndex: string) => {
    this.setState({
      isShown: {
        [dataIndex]: !this.state.isShown[dataIndex]
      }
    });
  };

  onSearchInput = ({ target }: SyntheticInputEvent<*>, dataIndex: string) => {
    this.setState(
      { filters: { ...this.state.filters, [dataIndex]: target.value } },
      () => this.onChange()
    );
  };

  onFilterInput = (value: any, dataIndex: string) => {
    this.setState(
      { filters: { ...this.state.filters, [dataIndex]: value } },
      () => this.onChange()
    );
  };

  checkifActive = (dataIndex: string) => {
    return (
      this.state.filters[dataIndex].length &&
      typeof this.state.filters[dataIndex].find(e => e.value) !== 'undefined'
    );
  };

  renderCell = (column: columnProps, data: Object, index: number) => {
    const cellData = get(data, column.dataIndex);
    return (
      <td key={`${column.dataIndex}-${index}-${data.id}`}>
        {column.render ? column.render(cellData, data) : cellData}

    );
  };

  renderHeadCell = (
    { dataIndex, search, title, sorter, filter }: columnProps,
    index: number
  ) => {
    const { filters, isShown } = this.state;
    return (
      <th key={`${dataIndex}-${index}`}>
        {title}
        {sorter && (

            <Icon name="arrow-up" />
            <Icon name="arrow-down" />

        )}
        {search && (

 (this.components[dataIndex] = c)}
              className={styles.searchIcon}
            >
              <Icon
                onClick={() => this.toggleSearch(dataIndex)}
                name="search"
                className={cx(
                  styles.icon,
                  ((filters[dataIndex] && filters[dataIndex].length) ||
                    isShown[dataIndex]) &&
                    styles.iconActive
                )}
              />

            <Overlay
              show={isShown[dataIndex]}
              onHide={() => this.toggleSearch(dataIndex)}
              placement="bottom"
              container={this.components[dataIndex]}
              target={() => this.components[dataIndex]}
              rootClose
            >

                <TextInput
                  autoFocus
                  placeholder="Filtrer"
                  value={filters[dataIndex]}
                  onChange={e => this.onSearchInput(e, dataIndex)}
                  onKeyDown={({ keyCode }) => {
                    if (keyCode === 13) {
                      this.toggleSearch(dataIndex);
                    }
                  }}
                />

            </Overlay>

        )}
        {filter && (

 (this.components[dataIndex] = c)}
              className={styles.filterIcon}
            >
              <Icon
                onClick={() => this.toggleFilter(dataIndex)}
                name="funnel"
                className={cx(
                  styles.icon,
                  (filters[dataIndex] !== undefined || isShown[dataIndex]) &&
                    styles.iconActive
                )}
              />

            <Overlay
              show={isShown[dataIndex]}
              onHide={() => this.toggleFilter(dataIndex)}
              placement="bottom"
              container={this.components[dataIndex]}
              target={() => this.components[dataIndex]}
              rootClose
            >

                {filter.map(({ label, value }) => (
                  <CheckBox
                    key={label}
                    label={label}
                    value={value === this.state.filters[dataIndex]}
                    onChange={() => this.onFilterInput(value, dataIndex)}
                  />
                ))}

                    this.setState({ filters: { [dataIndex]: undefined } })
                  }
                >
                  Nullstill

            </Overlay>

        )}

    );
  };

  filter = (item: Object) => {
    if (isEmpty(this.state.filters)) {
      return true;
    }

    const match = Object.keys(this.state.filters).filter(key => {
      if (this.state.filters[key] === undefined) {
        return true;
      }
      if (typeof this.state.filters[key] === 'boolean') {
        return this.state.filters[key] === get(item, key);
      }

      const filter = this.state.filters[key].toLowerCase();

      if (!filter.length) {
        return true;
      }

      const { filterMapping = val => val } =
        this.props.columns.find(col => col.dataIndex == key) || {};

      return filterMapping(get(item, key))
        .toLowerCase()
        .includes(filter);
    }).length;

    return match > 0;
  };

  loadMore = () => {
    if (this.props.onLoad && !this.props.loading) {
      this.props.onLoad(this.state.filters, this.state.sort);
    }
  };

  onChange = () => {
    if (this.props.onChange) {
      this.props.onChange(this.state.filters, this.state.sort);
    }
  };

  render() {
    const { columns, data, rowKey, hasMore, loading } = this.props;
    return (

            {columns
              .filter(column => column.visible || column.visible === undefined)
              .map((column, index) => this.renderHeadCell(column, index))}

        <InfiniteScroll
          element="tbody"
          hasMore={hasMore}
          loadMore={this.loadMore}
          threshold={50}
          loader={

                <LoadingIndicator loading={loading} />

          }
        >
          {data.filter(this.filter).map((item, index) => (

              {columns
                .filter(
                  column => column.visible || column.visible === undefined
                )

                .map((column, index) => this.renderCell(column, item, index))}

          ))}
        </InfiniteScroll>

    );
  }
}
<commit_msg>Move isVisible logic to separate func<commit_after>// @flow

import React, { Component, type Node } from 'react';
import Icon from 'app/components/Icon';
import LoadingIndicator from 'app/components/LoadingIndicator';
import { TextInput, CheckBox } from 'app/components/Form';
import { Overlay } from 'react-overlays';
import cx from 'classnames';
import { isEmpty } from 'lodash';
import InfiniteScroll from 'react-infinite-scroller';
import styles from './Table.css';
import { get } from 'lodash';

type sortProps = {
  direction?: 'asc' | 'desc',
  sort?: string
};

type checkFilter = {
  label: string,
  value: any
};

type columnProps = {
  dataIndex: string,
  title?: string,
  sorter?: (any, any) => number,
  filter?: Array<checkFilter>,
  /*
   * Map the value to to "another" value to use
   * for filtering. Eg. the result from the backend
   * is in english, and the search should be in norwegian
   *
   */
  filterMapping?: any => any,
  search?: boolean,
  width?: number,
  render?: (any, Object) => Node,
  // Should column be rendered. Will render when not set
  visible?: boolean
};

type Props = {
  rowKey?: string,
  columns: Array<columnProps>,
  data: Array<Object>,
  hasMore: boolean,
  loading: boolean,
  onChange?: (filters: Object, sort: sortProps) => void,
  onLoad?: (filters: Object, sort: sortProps) => void
};

type State = {
  filters: Object,
  isShown: Object,
  sort: sortProps
};

const isVisible = ({ visible = true }: columnProps) => visible;

export default class Table extends Component<Props, State> {
  props: Props;
  components: { [string]: ?HTMLDivElement } = {};

  state: State = {
    sort: {},
    filters: {},
    isShown: {}
  };

  static defaultProps = {
    rowKey: 'id'
  };

  toggleSearch = (dataIndex: string) => {
    this.setState({
      isShown: {
        [dataIndex]: !this.state.isShown[dataIndex]
      }
    });
  };

  toggleFilter = (dataIndex: string) => {
    this.setState({
      isShown: {
        [dataIndex]: !this.state.isShown[dataIndex]
      }
    });
  };

  onSearchInput = ({ target }: SyntheticInputEvent<*>, dataIndex: string) => {
    this.setState(
      { filters: { ...this.state.filters, [dataIndex]: target.value } },
      () => this.onChange()
    );
  };

  onFilterInput = (value: any, dataIndex: string) => {
    this.setState(
      { filters: { ...this.state.filters, [dataIndex]: value } },
      () => this.onChange()
    );
  };

  checkifActive = (dataIndex: string) => {
    return (
      this.state.filters[dataIndex].length &&
      typeof this.state.filters[dataIndex].find(e => e.value) !== 'undefined'
    );
  };

  renderCell = (column: columnProps, data: Object, index: number) => {
    const cellData = get(data, column.dataIndex);
    return (
      <td key={`${column.dataIndex}-${index}-${data.id}`}>
        {column.render ? column.render(cellData, data) : cellData}

    );
  };

  renderHeadCell = (
    { dataIndex, search, title, sorter, filter }: columnProps,
    index: number
  ) => {
    const { filters, isShown } = this.state;
    return (
      <th key={`${dataIndex}-${index}`}>
        {title}
        {sorter && (

            <Icon name="arrow-up" />
            <Icon name="arrow-down" />

        )}
        {search && (

 (this.components[dataIndex] = c)}
              className={styles.searchIcon}
            >
              <Icon
                onClick={() => this.toggleSearch(dataIndex)}
                name="search"
                className={cx(
                  styles.icon,
                  ((filters[dataIndex] && filters[dataIndex].length) ||
                    isShown[dataIndex]) &&
                    styles.iconActive
                )}
              />

            <Overlay
              show={isShown[dataIndex]}
              onHide={() => this.toggleSearch(dataIndex)}
              placement="bottom"
              container={this.components[dataIndex]}
              target={() => this.components[dataIndex]}
              rootClose
            >

                <TextInput
                  autoFocus
                  placeholder="Filtrer"
                  value={filters[dataIndex]}
                  onChange={e => this.onSearchInput(e, dataIndex)}
                  onKeyDown={({ keyCode }) => {
                    if (keyCode === 13) {
                      this.toggleSearch(dataIndex);
                    }
                  }}
                />

            </Overlay>

        )}
        {filter && (

 (this.components[dataIndex] = c)}
              className={styles.filterIcon}
            >
              <Icon
                onClick={() => this.toggleFilter(dataIndex)}
                name="funnel"
                className={cx(
                  styles.icon,
                  (filters[dataIndex] !== undefined || isShown[dataIndex]) &&
                    styles.iconActive
                )}
              />

            <Overlay
              show={isShown[dataIndex]}
              onHide={() => this.toggleFilter(dataIndex)}
              placement="bottom"
              container={this.components[dataIndex]}
              target={() => this.components[dataIndex]}
              rootClose
            >

                {filter.map(({ label, value }) => (
                  <CheckBox
                    key={label}
                    label={label}
                    value={value === this.state.filters[dataIndex]}
                    onChange={() => this.onFilterInput(value, dataIndex)}
                  />
                ))}

                    this.setState({ filters: { [dataIndex]: undefined } })
                  }
                >
                  Nullstill

            </Overlay>

        )}

    );
  };

  filter = (item: Object) => {
    if (isEmpty(this.state.filters)) {
      return true;
    }

    const match = Object.keys(this.state.filters).filter(key => {
      if (this.state.filters[key] === undefined) {
        return true;
      }
      if (typeof this.state.filters[key] === 'boolean') {
        return this.state.filters[key] === get(item, key);
      }

      const filter = this.state.filters[key].toLowerCase();

      if (!filter.length) {
        return true;
      }

      const { filterMapping = val => val } =
        this.props.columns.find(col => col.dataIndex == key) || {};

      return filterMapping(get(item, key))
        .toLowerCase()
        .includes(filter);
    }).length;

    return match > 0;
  };

  loadMore = () => {
    if (this.props.onLoad && !this.props.loading) {
      this.props.onLoad(this.state.filters, this.state.sort);
    }
  };

  onChange = () => {
    if (this.props.onChange) {
      this.props.onChange(this.state.filters, this.state.sort);
    }
  };

  render() {
    const { columns, data, rowKey, hasMore, loading } = this.props;
    return (

            {columns
              .filter(isVisible)
              .map((column, index) => this.renderHeadCell(column, index))}

        <InfiniteScroll
          element="tbody"
          hasMore={hasMore}
          loadMore={this.loadMore}
          threshold={50}
          loader={

                <LoadingIndicator loading={loading} />

          }
        >
          {data.filter(this.filter).map((item, index) => (

              {columns
                .filter(isVisible)
                .map((column, index) => this.renderCell(column, item, index))}

          ))}
        </InfiniteScroll>

    );
  }
}
<|endoftext|>


<commit_before>var _ = require("lodash");

var wrapInOr = function(states){
  if(_.size(states) === 1){
    return _.head(states);
  }
  return ["or", _.head(states), wrapInOr(_.tail(states))];
};

var uid = _.uniqueId;

var StateMachine = function(){
  var start = uid();
  var end = uid();
  var transitions = [];
  var normalizeStartEnd = function(state){
    if(state === start){
      return "start";
    }else if(state === end){
      return "end";
    }
    return state;
  };
  return {
    start: start,
    end: end,
    add: function(from_state, on_event, to_state){
      transitions.push([from_state, on_event, to_state]);
    },
    getTransitions: function(){
      return transitions;
    },
    concat: function(other){
      _.each(other.getTransitions(), function(t){
        transitions.push(_.cloneDeep(t));
      });
    },
    join: function(state_1, state_2){
      _.each(transitions, function(t){
        if(t[0] === state_1){
          t[0] = state_2;
        }
        if(t[2] === state_1){
          t[2] = state_2;
        }
      });
    },
    toJSON: function(){
      var stm = {};
      _.each(transitions, function(t){
        var from_state = normalizeStartEnd(t[0]);
        var on_event = t[1];
        var to_state = normalizeStartEnd(t[2]);
        if(!_.has(stm, from_state)){
          stm[from_state] = [];
        }
        stm[from_state].push([on_event, to_state]);
      });
      return stm;
    },
    clone: function(){
    }
  };
};

var toLispArgs = function(ast, traverse){
  return _.map(ast.args, traverse);
};

var event_ops = {
  "or": {
    toLispArgs: toLispArgs,
    mkStateMachine: function(args, evalEELisp){
      var s = StateMachine();

      var a = evalEELisp(args[0]);
      var b = evalEELisp(args[1]);

      s.concat(a);
      s.concat(b);

      s.join(a.start, s.start);
      s.join(b.start, s.start);

      s.join(a.end, s.end);
      s.join(b.end, s.end);

      return s;
    }
  },
  "and": {
    toLispArgs: toLispArgs,
    mkStateMachine: function(start, end, args, newState, evalEELisp){
      var a = evalEELisp(args[0], start, end);
      var b = evalEELisp(args[1], start, end);

      var s1 = newState();
      var s2 = newState();

      var stm = {};
      var stmPush = function(state, transition){
        if(!_.has(stm, state)){
          stm[state] = [];
        }
        stm[state].push(transition);
      };

      stm[start] = [];
      stm[s1] = [];
      stm[s2] = [];

      _.each(a[start], function(transition){
        var condition = transition[0];
        var next_state = transition[1];
        if(next_state === end){
          stmPush(start, [condition, s1]);
          stmPush(s2, [condition, end]);
        }
      });

      _.each(b[start], function(transition){
        var condition = transition[0];
        var next_state = transition[1];
        if(next_state === end){
          stmPush(start, [condition, s2]);
          stmPush(s1, [condition, end]);
        }
      });

      return stm;
    }
  }
};

module.exports = function(ast, comp, e){
  if(ast.kind !== "when"){
    throw new Error("RuleSelect.kind not supported: " + ast.kind);
  }
  var ee_id = 0;
  var graph = {};
  var eventexprs = {};

  var onEE = function(ast){
    var domain = ast.event_domain.value;
    var type = ast.event_type.value;
    var id = "expr_" + (ee_id++);

    _.set(graph, [domain, type, id], true);

    eventexprs[id] = comp(ast);
    return id;
  };

  var traverse = function(ast){
    if(ast.type === "EventExpression"){
      return onEE(ast);
    }else if(ast.type === "EventOperator"){
      if(_.has(event_ops, ast.op)){
        return [ast.op].concat(event_ops[ast.op].toLispArgs(ast, traverse));
      }
      throw new Error("EventOperator.op not supported: " + ast.op);
    }
    throw new Error("invalid event ast node: " + ast.type);
  };

  var evalEELisp = function(lisp){
    if(_.isString(lisp)){
      var s = StateMachine();
      s.add(s.start, lisp, s.end);
      return s;
    }
    if(_.has(event_ops, lisp[0])){
      return event_ops[lisp[0]].mkStateMachine(lisp.slice(1), evalEELisp);
    }else{
      throw new Error("EventOperator.op not supported: " + ast.op);
    }
  };

  var lisp = traverse(ast.event);
  var state_machine = evalEELisp(lisp);

  return e("obj", {
    graph: e("json", graph),
    eventexprs: e("obj", eventexprs),
    state_machine: e("json", state_machine.toJSON())
  });
};
<commit_msg>select when a a and b b<commit_after>var _ = require("lodash");

var wrapInOr = function(states){
  if(_.size(states) === 1){
    return _.head(states);
  }
  return ["or", _.head(states), wrapInOr(_.tail(states))];
};

var uid = _.uniqueId;

var StateMachine = function(){
  var start = uid();
  var end = uid();
  var transitions = [];
  var normalizeStartEnd = function(state){
    if(state === start){
      return "start";
    }else if(state === end){
      return "end";
    }
    return state;
  };
  return {
    start: start,
    end: end,
    add: function(from_state, on_event, to_state){
      transitions.push([from_state, on_event, to_state]);
    },
    getTransitions: function(){
      return transitions;
    },
    concat: function(other){
      _.each(other.getTransitions(), function(t){
        transitions.push(_.cloneDeep(t));
      });
    },
    join: function(state_1, state_2){
      _.each(transitions, function(t){
        if(t[0] === state_1){
          t[0] = state_2;
        }
        if(t[2] === state_1){
          t[2] = state_2;
        }
      });
    },
    toJSON: function(){
      var stm = {};
      _.each(transitions, function(t){
        var from_state = normalizeStartEnd(t[0]);
        var on_event = t[1];
        var to_state = normalizeStartEnd(t[2]);
        if(!_.has(stm, from_state)){
          stm[from_state] = [];
        }
        stm[from_state].push([on_event, to_state]);
      });
      return stm;
    },
    clone: function(){
    }
  };
};

var toLispArgs = function(ast, traverse){
  return _.map(ast.args, traverse);
};

var event_ops = {
  "or": {
    toLispArgs: toLispArgs,
    mkStateMachine: function(args, evalEELisp){
      var s = StateMachine();

      var a = evalEELisp(args[0]);
      var b = evalEELisp(args[1]);

      s.concat(a);
      s.concat(b);

      s.join(a.start, s.start);
      s.join(b.start, s.start);

      s.join(a.end, s.end);
      s.join(b.end, s.end);

      return s;
    }
  },
  "and": {
    toLispArgs: toLispArgs,
    mkStateMachine: function(args, evalEELisp){
      var s = StateMachine();

      var a0 = evalEELisp(args[0]);
      var b0 = evalEELisp(args[1]);
      var a1 = evalEELisp(args[0]);
      var b1 = evalEELisp(args[1]);

      s.concat(a0);
      s.concat(b0);
      s.concat(a1);
      s.concat(b1);

      s.join(a0.start, s.start);
      s.join(b0.start, s.start);

      s.join(a0.end, b1.start);
      s.join(b0.end, a1.start);

      s.join(a1.end, s.end);
      s.join(b1.end, s.end);

      return s;
    }
  }
};

module.exports = function(ast, comp, e){
  if(ast.kind !== "when"){
    throw new Error("RuleSelect.kind not supported: " + ast.kind);
  }
  var ee_id = 0;
  var graph = {};
  var eventexprs = {};

  var onEE = function(ast){
    var domain = ast.event_domain.value;
    var type = ast.event_type.value;
    var id = "expr_" + (ee_id++);

    _.set(graph, [domain, type, id], true);

    eventexprs[id] = comp(ast);
    return id;
  };

  var traverse = function(ast){
    if(ast.type === "EventExpression"){
      return onEE(ast);
    }else if(ast.type === "EventOperator"){
      if(_.has(event_ops, ast.op)){
        return [ast.op].concat(event_ops[ast.op].toLispArgs(ast, traverse));
      }
      throw new Error("EventOperator.op not supported: " + ast.op);
    }
    throw new Error("invalid event ast node: " + ast.type);
  };

  var evalEELisp = function(lisp){
    if(_.isString(lisp)){
      var s = StateMachine();
      s.add(s.start, lisp, s.end);
      return s;
    }
    if(_.has(event_ops, lisp[0])){
      return event_ops[lisp[0]].mkStateMachine(lisp.slice(1), evalEELisp);
    }else{
      throw new Error("EventOperator.op not supported: " + ast.op);
    }
  };

  var lisp = traverse(ast.event);
  var state_machine = evalEELisp(lisp);

  return e("obj", {
    graph: e("json", graph),
    eventexprs: e("obj", eventexprs),
    state_machine: e("json", state_machine.toJSON())
  });
};
<|endoftext|>


<commit_before>const React = require('react');
const PropTypes = require('prop-types');
const Radium = require('radium');

const ButtonGroup = require('./ButtonGroup');
const StyleConstants = require('../constants/Style');

class StandardTabs extends React.Component {
  static propTypes = {
    activeTabStyles: PropTypes.object,
    brandColor: PropTypes.string,
    onTabSelect: PropTypes.func.isRequired,
    selectedTab: PropTypes.number,
    tabs: PropTypes.array.isRequired
  };

  static defaultProps = {
    brandColor: StyleConstants.Colors.PRIMARY
  };

  constructor (props) {
    super(props);

    this.state = {
      selectedTab: this.props.selectedTab || 0
    };
  }

  componentWillReceiveProps (nextProps) {
    if (nextProps.selectedTab !== this.state.selectedTab) {
      this.setState({
        selectedTab: nextProps.selectedTab
      });
    }
  }

  handleTabSelect (selectedTab) {
    this.props.onTabSelect(selectedTab);

    this.setState({
      selectedTab
    });
  }

  render () {
    return (

        {this.props.tabs.map((tab, index) =>
          <Tab
            isActive={this.state.selectedTab === index}
            key={tab}
            onClick={this.handleTabSelect.bind(this, index)}
            styles={{ activeTab: this.props.activeTabStyles }}
          >
            {tab}
          </Tab>
        )}

    );
  }
}

class TabWithoutRadium extends React.Component {
  static propTypes = {
    activeTabStyles: PropTypes.object,
    brandColor: PropTypes.string,
    isActive: PropTypes.bool,
    onClick: PropTypes.func,
    styles: PropTypes.object
  }

  static defaultProps = {
    brandColor: StyleConstants.Colors.PRIMARY,
    isActive: false,
    onClick: () => {},
    styles: {}
  }

  render () {
    const styles = this.styles();
    let style = Object.assign({}, styles.tab, this.props.styles.tab);

    if (this.props.isActive)
      style = Object.assign({}, style, styles.activeTab, this.props.styles.activeTab);

    return (

        {this.props.children}

    );
  }

  styles = () => {
    return {
      tab: {
        boxSizing: 'border-box',
        color: StyleConstants.Colors.ASH,
        cursor: 'pointer',
        display: 'inline-block',
        fontSize: StyleConstants.FontSizes.MEDIUM,
        fontStyle: StyleConstants.Fonts.SEMIBOLD,
        padding: StyleConstants.Spacing.MEDIUM,
        whiteSpace: 'nowrap',

        ':hover': {
          color: StyleConstants.Colors.CHARCOAL
        }
      },
      activeTab: Object.assign({
        cursor: 'default',
        color: this.props.brandColor,
        borderBottom: '2px solid ' + this.props.brandColor,

        ':hover': {
          color: this.props.brandColor
        }
      }, this.props.activeTabStyles)
    };
  }
}

const Tab = Radium(TabWithoutRadium);

class PillTabs extends StandardTabs {
  render () {
    const styles = this.styles();

    return (

        <ButtonGroup
          buttons={this.props.tabs.map((tab, index) => ({
            'aria-label': tab,
            onClick: (this.state.selectedTab === index ? null : this.handleTabSelect.bind(this, index)),
            style: Object.assign({}, styles.tab, this.state.selectedTab === index && styles.selected),
            text: tab
          }))}
          style={styles.buttonGroup}
          type='neutral'
        />

    );
  }

  styles = () => {
    return {
      component: {
        margin: StyleConstants.Spacing.SMALL
      },
      tab: {
        backgroundColor: StyleConstants.Colors.PORCELAIN,
        borderColor: StyleConstants.Colors.FOG,
        outline: 'none',

        ':hover': {
          backgroundColor: this.props.brandColor,
          color: StyleConstants.Colors.WHITE,
          fill: StyleConstants.Colors.WHITE
        },
        ':focus': {
          backgroundColor: this.props.brandColor,
          color: StyleConstants.Colors.WHITE,
          fill: StyleConstants.Colors.WHITE
        },
        ':active': {
          backgroundColor: StyleConstants.adjustColor(this.props.brandColor, -15)
        }
      },
      selected: {
        backgroundColor: StyleConstants.Colors.WHITE,
        color: this.props.brandColor,
        cursor: 'default',

        ':hover': {
          backgroundColor: 'transparent'
        },
        ':focus': {
          backgroundColor: StyleConstants.Colors.WHITE,
          color: this.props.brandColor
        },
        ':active': {
          backgroundColor: 'transparent'
        }
      }
    };
  }
}

const TabsTypes = {
  standard: StandardTabs,
  pill: PillTabs
};

const Tabs = ({
  alignment = 'left',
  showBottomBorder = true,
  style,
  type = 'standard',
  useTabsInMobile,
  ...props
}) => {
  if (typeof useTabsInMobile !== 'undefined') {
    console.warn('The useTabsInMobile prop is deprecated and will be removed in a future release.');
  }

  const componentStyle = Object.assign({
    borderBottom: showBottomBorder ? '1px solid ' + StyleConstants.Colors.FOG : 'none',
    boxSizing: 'border-box',
    display: 'flex',
    justifyContent: alignment === 'left' ? 'flex-start' : 'center',
    overflowX: 'scroll',
    width: '100%'
  }, style);
  const TabsComponent = TabsTypes[type];

  if (!TabsComponent) throw new Error(`Unknown Tabs type: ${type}`);

  return (

      <TabsComponent {...props} />

  );
};

Tabs.propTypes = {
  alignment: PropTypes.oneOf(['left', 'center']),
  showBottomBorder: PropTypes.bool,
  type: PropTypes.oneOf(Object.keys(TabsTypes)),
  useTabsInMobile: PropTypes.bool
};

module.exports = Tabs;
<commit_msg>Convert from inheritance to function/component composition<commit_after>const React = require('react');
const PropTypes = require('prop-types');
const Radium = require('radium');
const _pick = require('lodash/pick');

const ButtonGroup = require('./ButtonGroup');
const StyleConstants = require('../constants/Style');

const TabsPropTypes = {
  activeTabStyles: PropTypes.object,
  brandColor: PropTypes.string,
  onTabSelect: PropTypes.func.isRequired,
  selectedTab: PropTypes.number,
  tabs: PropTypes.array.isRequired
};

class TabWithoutRadium extends React.Component {
  static propTypes = {
    activeTabStyles: PropTypes.object,
    brandColor: PropTypes.string,
    isActive: PropTypes.bool,
    onClick: PropTypes.func,
    styles: PropTypes.object
  }

  static defaultProps = {
    brandColor: StyleConstants.Colors.PRIMARY,
    isActive: false,
    onClick: () => {},
    styles: {}
  }

  render () {
    const styles = this.styles();
    let style = Object.assign({}, styles.tab, this.props.styles.tab);

    if (this.props.isActive)
      style = Object.assign({}, style, styles.activeTab, this.props.styles.activeTab);

    return (

        {this.props.children}

    );
  }

  styles = () => {
    return {
      tab: {
        boxSizing: 'border-box',
        color: StyleConstants.Colors.ASH,
        cursor: 'pointer',
        display: 'inline-block',
        fontSize: StyleConstants.FontSizes.MEDIUM,
        fontStyle: StyleConstants.Fonts.SEMIBOLD,
        padding: StyleConstants.Spacing.MEDIUM,
        whiteSpace: 'nowrap',

        ':hover': {
          color: StyleConstants.Colors.CHARCOAL
        }
      },
      activeTab: Object.assign({
        cursor: 'default',
        color: this.props.brandColor,
        borderBottom: '2px solid ' + this.props.brandColor,

        ':hover': {
          color: this.props.brandColor
        }
      }, this.props.activeTabStyles)
    };
  }
}

const Tab = Radium(TabWithoutRadium);

const StandardTabs = (props) => (

    {props.tabs.map((tab, index) =>
      <Tab
        isActive={props.selectedTab === index}
        key={tab}
        onClick={props.onTabSelect.bind(this, index)}
        styles={{ activeTab: props.activeTabStyles }}
      >
        {tab}
      </Tab>
     )}

);

StandardTabs.propTypes = TabsPropTypes;

const PillTabs = (props) => {
  const styles = {
    component: {
      margin: StyleConstants.Spacing.SMALL
    },
    tab: {
      backgroundColor: StyleConstants.Colors.PORCELAIN,
      borderColor: StyleConstants.Colors.FOG,
      outline: 'none',

      ':hover': {
        backgroundColor: props.brandColor,
        color: StyleConstants.Colors.WHITE,
        fill: StyleConstants.Colors.WHITE
      },
      ':focus': {
        backgroundColor: props.brandColor,
        color: StyleConstants.Colors.WHITE,
        fill: StyleConstants.Colors.WHITE
      },
      ':active': {
        backgroundColor: StyleConstants.adjustColor(props.brandColor, -15)
      }
    },
    selected: {
      backgroundColor: StyleConstants.Colors.WHITE,
      color: props.brandColor,
      cursor: 'default',

      ':hover': {
        backgroundColor: 'transparent'
      },
      ':focus': {
        backgroundColor: StyleConstants.Colors.WHITE,
        color: props.brandColor
      },
      ':active': {
        backgroundColor: 'transparent'
      }
    }
  };

  return (

      <ButtonGroup
        buttons={props.tabs.map((tab, index) => ({
          'aria-label': tab,
          onClick: (props.selectedTab === index ? null : props.onTabSelect.bind(this, index)),
          style: Object.assign({}, styles.tab, props.selectedTab === index && styles.selected),
          text: tab
        }))}
        style={styles.buttonGroup}
        type='neutral'
      />

  );
};

PillTabs.propTypes = TabsPropTypes;

const TabsTypes = {
  standard: StandardTabs,
  pill: PillTabs
};

class TabsWrapper extends React.Component {
  static propTypes = Object.assign({
    alignment: PropTypes.oneOf(['left', 'center']),
    showBottomBorder: PropTypes.bool,
    type: PropTypes.oneOf(Object.keys(TabsTypes)),
    useTabsInMobile: PropTypes.bool
  }, TabsPropTypes);

  static defaultProps = {
    alignment: 'left',
    brandColor: StyleConstants.Colors.PRIMARY,
    showBottomBorder: true,
    type: 'standard'
  };

  constructor (props) {
    super(props);

    this.state = {
      selectedTab: this.props.selectedTab || 0
    };
  }

  componentWillMount () {
    if (this.props.hasOwnProperty('useTabsInMobile')) {
      console.warn('The useTabsInMobile prop is deprecated and will be removed in a future release.');
    }
  }

  componentWillReceiveProps (nextProps) {
    if (nextProps.selectedTab !== this.state.selectedTab) {
      this.setState({
        selectedTab: nextProps.selectedTab
      });
    }
  }

  handleTabSelect (selectedTab) {
    this.props.onTabSelect(selectedTab);

    this.setState({
      selectedTab
    });
  }

  render () {
    const componentStyle = Object.assign({
      borderBottom: this.props.showBottomBorder ? '1px solid ' + StyleConstants.Colors.FOG : 'none',
      boxSizing: 'border-box',
      display: 'flex',
      justifyContent: this.props.alignment === 'left' ? 'flex-start' : 'center',
      overflowX: 'scroll',
      width: '100%'
    }, this.props.style);
    const TabsComponent = TabsTypes[this.props.type];

    if (!TabsComponent) throw new Error(`Unknown Tabs type: ${this.props.type}`);

    const tabsProps = Object.assign(
      _pick(this.props, Object.keys(TabsPropTypes)),
      {
        onTabSelect: this.handleTabSelect.bind(this), // delegate for keeping state
        selectedTab: this.props.selectedTab
      }
    );

    return (

        <TabsComponent {...tabsProps} />

    );
  }
}

module.exports = TabsWrapper;
<|endoftext|>


<commit_before>/*global ko, jQuery*/
(function (factory) {
    if (typeof define === "function" && define.amd) {
        // AMD anonymous module with hard-coded dependency on "knockout"
        define(["knockout", "jquery"], factory);
    } else {
        // <script> tag: use the global `ko` and `jQuery`
        factory(ko, jQuery);
    }
})(function (ko, $) {
    var dropZones = {};
    var eventZones = {};

    function Zone(args) {
        this.init(args);
    }

    Zone.prototype.init = function (args) {
        this.element = args.element;
        this.$element = $(args.element);
        this.data = args.data;
        this.dragEnter = args.dragEnter;
        this.dragOver = args.dragOver;
        this.dragLeave = args.dragLeave;
        this.active = false;
        this.inside = false;
        this.dirty = false;
    };

    Zone.prototype.refreshDomInfo = function () {
        var $element = this.$element;
        var offset = $element.offset();
        this.top = offset.top;
        this.left = offset.left;
        this.width = $element.outerWidth();
        this.height = $element.outerHeight();
    };

    Zone.prototype.isInside = function (x, y) {
        if (x < this.left || y < this.top) {
            return false;
        }

        if (this.left + this.width < x) {
            return false;
        }

        if (this.top + this.height < y) {
            return false;
        }
        return true;
    };

    Zone.prototype.update = function (event, data) {
        if (this.isInside(event.pageX, event.pageY)) {
            if (!this.inside) {
                this.enter(event, data);
            }

            if (this.dragOver) {
                this.dragOver(event, data, this.data);
            }
        } else {
            this.leave(event);
        }
    };

    Zone.prototype.enter = function (event, data) {
        this.inside = true;
        if (this.dragEnter) {
            this.active = this.dragEnter(event, data, this.data) !== false;
        } else {
            this.active = true;
        }
        this.dirty = true;
    };

    Zone.prototype.leave = function (event) {
        if (event) {
            event.target = this.element;
        }

        if (this.inside && this.dragLeave) {
            this.dragLeave(event);
        }
        this.active = false;
        this.inside = false;
        this.dirty = true;
    };

    function DropZone(args) {
        this.init(args);
        this.drop = function (data) {
            args.drop(data, args.data);
        };
    }
    DropZone.prototype = Zone.prototype;

    DropZone.prototype.updateStyling = function () {
        if (this.dirty) {
            this.$element.toggleClass('drag-over', this.active);
            this.$element.toggleClass('drop-rejected', this.inside && !this.active);
        }
        this.dirty = false;
    };

    function DragElement($element) {
        this.$element = $element;
        this.$element.addClass('drag-element').css({
            position: 'fixed',
            zIndex: 1060
        });
        this.$element.on('selectstart', false);
    }

    DragElement.prototype.updatePosition = function (event) {
        this.$element.offset({
            top: event.pageY,
            left: event.pageX
        });
    };

    DragElement.prototype.remove = function () {
        this.$element.remove();
    };

    function Draggable(args) {
        this.name = args.name;
        this.dragStart = args.dragStart;
        this.dragEnd = args.dragEnd;
        this.data = args.data;
    }

    Draggable.prototype.startDrag = function () {
        if (this.dragStart && this.dragStart(this.data) === false) {
            return false;
        }
    };

    Draggable.prototype.drag = function (event) {
        var that = this;
        var name = this.name;
        var zones = dropZones[name].concat(eventZones[name]);

        zones.forEach(function (zone) {
            zone.refreshDomInfo();
        });

        zones.forEach(function (zone) {
            event.target = zone.element;
            zone.update(event, that.data);
        });

        dropZones[name].forEach(function (zone) {
            zone.updateStyling();
        });
    };

    Draggable.prototype.dropRejected = function () {
        var name = this.name;
        var insideAZone = dropZones[name].some(function (zone) {
            return zone.inside;
        });
        if (!insideAZone) {
            return false;
        }

        var noActiveZone = !dropZones[name].some(function (zone) {
            return zone.active;
        });
        return noActiveZone;
    };

    Draggable.prototype.drop = function (event) {
        var name = this.name;

        var dropZoneElement = $(event.target).closest('.drop-zone');
        var winningDropZone = dropZones[name].filter(function (zone) {
            return zone.active;
        }).filter(function (zone) {
            return zone.$element.is(dropZoneElement);
        })[0];

        dropZones[name].concat(eventZones[name]).forEach(function (zone) {
            zone.leave(event);
        });

        dropZones[name].forEach(function (zone) {
            zone.updateStyling();
        });

        if (this.dragEnd) {
            this.dragEnd(this.data);
        }

        if (winningDropZone && winningDropZone.drop) {
            winningDropZone.drop(this.data);
        }
    };

    function ScrollArea(element) {
        this.element = element;
        this.$element = $(element);
        this.scrollMargin = Math.floor(this.$element.innerHeight() / 10);
        this.offset = this.$element.offset();
        this.innerHeight = this.$element.innerHeight();
        this.scrollDeltaMin = 5;
        this.scrollDeltaMax = 30;
    }

    ScrollArea.prototype.scroll = function (x, y) {
        var topLimit = this.scrollMargin + this.offset.top;
        var speed, scrollDelta;
        if (y < topLimit) {
            speed = (topLimit - y) / this.scrollMargin;
            scrollDelta = speed * (this.scrollDeltaMax - this.scrollDeltaMin) + this.scrollDeltaMin;
            this.element.scrollTop -= scrollDelta;
        }

        var bottomLimit = this.offset.top + this.innerHeight - this.scrollMargin;
        if (y > bottomLimit) {
            speed = (y - bottomLimit) / this.scrollMargin;
            scrollDelta = speed * (this.scrollDeltaMax - this.scrollDeltaMin) + this.scrollDeltaMin;
            this.element.scrollTop += scrollDelta;
        }
    };

    ko.utils.extend(ko.bindingHandlers, {
        dropzone: {
            init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
                var options = ko.utils.unwrapObservable(valueAccessor());
                var name = options.name;
                dropZones[name] = dropZones[name] || [];

                $(element).addClass('drop-zone');

                var zone = new DropZone({
                    element: element,
                    data: bindingContext && bindingContext.$data,
                    drop: options.drop,
                    dragEnter: options.dragEnter,
                    dragOver: options.dragOver,
                    dragLeave: options.dragLeave
                });
                dropZones[name].push(zone);

                ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
                    zone.leave();
                    dropZones[name].splice(dropZones[name].indexOf(zone), 1);
                });
            }
        },

        dragEvents: {
            init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
                var options = ko.utils.unwrapObservable(valueAccessor());
                var name = options.name;
                eventZones[name] = eventZones[name] || [];

                var zone = new Zone({
                    element: element,
                    data: bindingContext && bindingContext.$data,
                    dragEnter: options.dragEnter,
                    dragOver: options.dragOver,
                    dragLeave: options.dragLeave
                });
                eventZones[name].push(zone);

                ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
                    zone.leave();
                    eventZones[name].splice(eventZones[name].indexOf(zone), 1);
                });
            }
        },

        dragzone: {
            init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
                var options = ko.utils.unwrapObservable(valueAccessor());
                var name = options.name;
                dropZones[name] = dropZones[name] || [];
                eventZones[name] = eventZones[name] || [];

                var data = bindingContext && bindingContext.$data;

                var draggable = new Draggable({
                    name: name,
                    data: data,
                    dragStart: options.dragStart,
                    dragEnd: options.dragEnd
                });

                function createCloneProxyElement() {
                    var dragProxy = $(element).clone().appendTo($(element).parent());
                    dragProxy.css({
                        height: $(element).height(),
                        width: $(element).width(),
                        opacity: 70 / 100,
                        filter: "alpha(opacity=70"
                    });
                    return dragProxy;
                }

                function createTemplateProxyElement() {
                    var dragProxy = $('<div>').appendTo('body');
                    ko.renderTemplate(options.element, bindingContext, {}, dragProxy[0]);
                    return dragProxy;
                }

                $(element).on('selectstart', function (e) {
                    if (!$(e.target).is(':input')) {
                        return false;
                    }
                });

                $(element).addClass('draggable');
                $(element).on('mousedown', function (downEvent) {
                    if (downEvent.which !== 1) {
                        return true;
                    }

                    $(document).on('selectstart.drag', false);

                    function startDragging(startEvent) {
                        $(element).off('mouseup.startdrag click.startdrag mouseleave.startdrag mousemove.startdrag');

                        var dragElement = null;
                        if (!options.element) {
                            dragElement = new DragElement(createCloneProxyElement());
                        }

                        if (draggable.startDrag() === false) {
                            return false;
                        }

                        var $overlay = $('<div class="drag-overlay" unselectable="on">');
                        $overlay.on('selectstart', false);
                        $overlay.appendTo('body');

                        var dragProxy = null;
                        if (options.element) {
                            dragElement = new DragElement(createTemplateProxyElement());
                        }

                        dragElement.updatePosition(downEvent);

                        var dragTimer = null;
                        var dropRejected = false;
                        function drag(event) {
                            draggable.drag(event);
                            if (draggable.dropRejected() !== dropRejected) {
                                $overlay.toggleClass('drop-rejected', draggable.dropRejected());
                                dropRejected = draggable.dropRejected();
                            }
                            dragTimer = setTimeout(function () {
                                drag(event);
                            }, 250);
                        }

                        $overlay.on('mousemove.drag', function (moveEvent) {
                            clearTimeout(dragTimer);
                            dragElement.updatePosition(moveEvent);
                            drag(moveEvent);
                            return false;
                        });

                        $overlay.on('mouseup', function (upEvent) {
                            clearTimeout(dragTimer);
                            dragElement.remove();
                            $overlay.remove();
                            upEvent.target = document.elementFromPoint(upEvent.pageX, upEvent.pageY);
                            draggable.drop(upEvent);

                            $(document).off('selectstart.drag');
                            return false;
                        });
                    }

                    $(element).one('mouseup.startdrag click.startdrag', function (event) {
                        $(element).off('mouseleave.startdrag mousemove.startdrag');
                        $(document).off('selectstart.drag');
                        return true;
                    });

                    $(element).on('mousemove.startdrag', function (event) {
                        if ($(event.target).is(':input')) {
                            return;
                        }

                        var distance = Math.sqrt(Math.pow(downEvent.pageX - event.pageX, 2) +
                                                 Math.pow(downEvent.pageY - event.pageY, 2));
                        if (distance > 10) {
                            startDragging(event);
                        }
                    });

                    $(element).one('mouseleave.startdrag', function (event) {
                        if ($(event.target).is(':input')) {
                            return;
                        }

                        startDragging(event);
                    });

                    return true;
                });

                ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
                    $(document).off('selectstart.drag');
                });
            }
        },

        scrollableOnDragOver: {
            // TODO make this binding scroll on the x-axis as well
            init: function (element, valueAccessor, allBindingAccessor) {
                var name = ko.utils.unwrapObservable(valueAccessor());
                var scrollArea = null;
                var x, y;
                var timer;

                function scroll() {
                    scrollArea.scroll(x, y);
                }
                function dragEnter(e) {
                    scrollArea = new ScrollArea(element);
                    timer = setInterval(scroll, 100);
                }

                function dragOver(e) {
                    x = e.pageX;
                    y = e.pageY;
                }

                function dragLeave(e) {
                    clearTimeout(timer);
                }

                ko.bindingHandlers.dragEvents.init(element, function () {
                    return {
                        name: name,
                        dragEnter: dragEnter,
                        dragOver: dragOver,
                        dragLeave: dragLeave
                    };
                });
            }
        }
    });
});<commit_msg>Removed unused variable<commit_after>/*global ko, jQuery*/
(function (factory) {
    if (typeof define === "function" && define.amd) {
        // AMD anonymous module with hard-coded dependency on "knockout"
        define(["knockout", "jquery"], factory);
    } else {
        // <script> tag: use the global `ko` and `jQuery`
        factory(ko, jQuery);
    }
})(function (ko, $) {
    var dropZones = {};
    var eventZones = {};

    function Zone(args) {
        this.init(args);
    }

    Zone.prototype.init = function (args) {
        this.element = args.element;
        this.$element = $(args.element);
        this.data = args.data;
        this.dragEnter = args.dragEnter;
        this.dragOver = args.dragOver;
        this.dragLeave = args.dragLeave;
        this.active = false;
        this.inside = false;
        this.dirty = false;
    };

    Zone.prototype.refreshDomInfo = function () {
        var $element = this.$element;
        var offset = $element.offset();
        this.top = offset.top;
        this.left = offset.left;
        this.width = $element.outerWidth();
        this.height = $element.outerHeight();
    };

    Zone.prototype.isInside = function (x, y) {
        if (x < this.left || y < this.top) {
            return false;
        }

        if (this.left + this.width < x) {
            return false;
        }

        if (this.top + this.height < y) {
            return false;
        }
        return true;
    };

    Zone.prototype.update = function (event, data) {
        if (this.isInside(event.pageX, event.pageY)) {
            if (!this.inside) {
                this.enter(event, data);
            }

            if (this.dragOver) {
                this.dragOver(event, data, this.data);
            }
        } else {
            this.leave(event);
        }
    };

    Zone.prototype.enter = function (event, data) {
        this.inside = true;
        if (this.dragEnter) {
            this.active = this.dragEnter(event, data, this.data) !== false;
        } else {
            this.active = true;
        }
        this.dirty = true;
    };

    Zone.prototype.leave = function (event) {
        if (event) {
            event.target = this.element;
        }

        if (this.inside && this.dragLeave) {
            this.dragLeave(event);
        }
        this.active = false;
        this.inside = false;
        this.dirty = true;
    };

    function DropZone(args) {
        this.init(args);
        this.drop = function (data) {
            args.drop(data, args.data);
        };
    }
    DropZone.prototype = Zone.prototype;

    DropZone.prototype.updateStyling = function () {
        if (this.dirty) {
            this.$element.toggleClass('drag-over', this.active);
            this.$element.toggleClass('drop-rejected', this.inside && !this.active);
        }
        this.dirty = false;
    };

    function DragElement($element) {
        this.$element = $element;
        this.$element.addClass('drag-element').css({
            position: 'fixed',
            zIndex: 1060
        });
        this.$element.on('selectstart', false);
    }

    DragElement.prototype.updatePosition = function (event) {
        this.$element.offset({
            top: event.pageY,
            left: event.pageX
        });
    };

    DragElement.prototype.remove = function () {
        this.$element.remove();
    };

    function Draggable(args) {
        this.name = args.name;
        this.dragStart = args.dragStart;
        this.dragEnd = args.dragEnd;
        this.data = args.data;
    }

    Draggable.prototype.startDrag = function () {
        if (this.dragStart && this.dragStart(this.data) === false) {
            return false;
        }
    };

    Draggable.prototype.drag = function (event) {
        var that = this;
        var name = this.name;
        var zones = dropZones[name].concat(eventZones[name]);

        zones.forEach(function (zone) {
            zone.refreshDomInfo();
        });

        zones.forEach(function (zone) {
            event.target = zone.element;
            zone.update(event, that.data);
        });

        dropZones[name].forEach(function (zone) {
            zone.updateStyling();
        });
    };

    Draggable.prototype.dropRejected = function () {
        var name = this.name;
        var insideAZone = dropZones[name].some(function (zone) {
            return zone.inside;
        });
        if (!insideAZone) {
            return false;
        }

        var noActiveZone = !dropZones[name].some(function (zone) {
            return zone.active;
        });
        return noActiveZone;
    };

    Draggable.prototype.drop = function (event) {
        var name = this.name;

        var dropZoneElement = $(event.target).closest('.drop-zone');
        var winningDropZone = dropZones[name].filter(function (zone) {
            return zone.active;
        }).filter(function (zone) {
            return zone.$element.is(dropZoneElement);
        })[0];

        dropZones[name].concat(eventZones[name]).forEach(function (zone) {
            zone.leave(event);
        });

        dropZones[name].forEach(function (zone) {
            zone.updateStyling();
        });

        if (this.dragEnd) {
            this.dragEnd(this.data);
        }

        if (winningDropZone && winningDropZone.drop) {
            winningDropZone.drop(this.data);
        }
    };

    function ScrollArea(element) {
        this.element = element;
        this.$element = $(element);
        this.scrollMargin = Math.floor(this.$element.innerHeight() / 10);
        this.offset = this.$element.offset();
        this.innerHeight = this.$element.innerHeight();
        this.scrollDeltaMin = 5;
        this.scrollDeltaMax = 30;
    }

    ScrollArea.prototype.scroll = function (x, y) {
        var topLimit = this.scrollMargin + this.offset.top;
        var speed, scrollDelta;
        if (y < topLimit) {
            speed = (topLimit - y) / this.scrollMargin;
            scrollDelta = speed * (this.scrollDeltaMax - this.scrollDeltaMin) + this.scrollDeltaMin;
            this.element.scrollTop -= scrollDelta;
        }

        var bottomLimit = this.offset.top + this.innerHeight - this.scrollMargin;
        if (y > bottomLimit) {
            speed = (y - bottomLimit) / this.scrollMargin;
            scrollDelta = speed * (this.scrollDeltaMax - this.scrollDeltaMin) + this.scrollDeltaMin;
            this.element.scrollTop += scrollDelta;
        }
    };

    ko.utils.extend(ko.bindingHandlers, {
        dropzone: {
            init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
                var options = ko.utils.unwrapObservable(valueAccessor());
                var name = options.name;
                dropZones[name] = dropZones[name] || [];

                $(element).addClass('drop-zone');

                var zone = new DropZone({
                    element: element,
                    data: bindingContext && bindingContext.$data,
                    drop: options.drop,
                    dragEnter: options.dragEnter,
                    dragOver: options.dragOver,
                    dragLeave: options.dragLeave
                });
                dropZones[name].push(zone);

                ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
                    zone.leave();
                    dropZones[name].splice(dropZones[name].indexOf(zone), 1);
                });
            }
        },

        dragEvents: {
            init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
                var options = ko.utils.unwrapObservable(valueAccessor());
                var name = options.name;
                eventZones[name] = eventZones[name] || [];

                var zone = new Zone({
                    element: element,
                    data: bindingContext && bindingContext.$data,
                    dragEnter: options.dragEnter,
                    dragOver: options.dragOver,
                    dragLeave: options.dragLeave
                });
                eventZones[name].push(zone);

                ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
                    zone.leave();
                    eventZones[name].splice(eventZones[name].indexOf(zone), 1);
                });
            }
        },

        dragzone: {
            init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
                var options = ko.utils.unwrapObservable(valueAccessor());
                var name = options.name;
                dropZones[name] = dropZones[name] || [];
                eventZones[name] = eventZones[name] || [];

                var data = bindingContext && bindingContext.$data;

                var draggable = new Draggable({
                    name: name,
                    data: data,
                    dragStart: options.dragStart,
                    dragEnd: options.dragEnd
                });

                function createCloneProxyElement() {
                    var dragProxy = $(element).clone().appendTo($(element).parent());
                    dragProxy.css({
                        height: $(element).height(),
                        width: $(element).width(),
                        opacity: 70 / 100,
                        filter: "alpha(opacity=70"
                    });
                    return dragProxy;
                }

                function createTemplateProxyElement() {
                    var dragProxy = $('<div>').appendTo('body');
                    ko.renderTemplate(options.element, bindingContext, {}, dragProxy[0]);
                    return dragProxy;
                }

                $(element).on('selectstart', function (e) {
                    if (!$(e.target).is(':input')) {
                        return false;
                    }
                });

                $(element).addClass('draggable');
                $(element).on('mousedown', function (downEvent) {
                    if (downEvent.which !== 1) {
                        return true;
                    }

                    $(document).on('selectstart.drag', false);

                    function startDragging(startEvent) {
                        $(element).off('mouseup.startdrag click.startdrag mouseleave.startdrag mousemove.startdrag');

                        var dragElement = null;
                        if (!options.element) {
                            dragElement = new DragElement(createCloneProxyElement());
                        }

                        if (draggable.startDrag() === false) {
                            return false;
                        }

                        var $overlay = $('<div class="drag-overlay" unselectable="on">');
                        $overlay.on('selectstart', false);
                        $overlay.appendTo('body');

                        if (options.element) {
                            dragElement = new DragElement(createTemplateProxyElement());
                        }

                        dragElement.updatePosition(downEvent);

                        var dragTimer = null;
                        var dropRejected = false;
                        function drag(event) {
                            draggable.drag(event);
                            if (draggable.dropRejected() !== dropRejected) {
                                $overlay.toggleClass('drop-rejected', draggable.dropRejected());
                                dropRejected = draggable.dropRejected();
                            }
                            dragTimer = setTimeout(function () {
                                drag(event);
                            }, 250);
                        }

                        $overlay.on('mousemove.drag', function (moveEvent) {
                            clearTimeout(dragTimer);
                            dragElement.updatePosition(moveEvent);
                            drag(moveEvent);
                            return false;
                        });

                        $overlay.on('mouseup', function (upEvent) {
                            clearTimeout(dragTimer);
                            dragElement.remove();
                            $overlay.remove();
                            upEvent.target = document.elementFromPoint(upEvent.pageX, upEvent.pageY);
                            draggable.drop(upEvent);

                            $(document).off('selectstart.drag');
                            return false;
                        });
                    }

                    $(element).one('mouseup.startdrag click.startdrag', function (event) {
                        $(element).off('mouseleave.startdrag mousemove.startdrag');
                        $(document).off('selectstart.drag');
                        return true;
                    });

                    $(element).on('mousemove.startdrag', function (event) {
                        if ($(event.target).is(':input')) {
                            return;
                        }

                        var distance = Math.sqrt(Math.pow(downEvent.pageX - event.pageX, 2) +
                                                 Math.pow(downEvent.pageY - event.pageY, 2));
                        if (distance > 10) {
                            startDragging(event);
                        }
                    });

                    $(element).one('mouseleave.startdrag', function (event) {
                        if ($(event.target).is(':input')) {
                            return;
                        }

                        startDragging(event);
                    });

                    return true;
                });

                ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
                    $(document).off('selectstart.drag');
                });
            }
        },

        scrollableOnDragOver: {
            // TODO make this binding scroll on the x-axis as well
            init: function (element, valueAccessor, allBindingAccessor) {
                var name = ko.utils.unwrapObservable(valueAccessor());
                var scrollArea = null;
                var x, y;
                var timer;

                function scroll() {
                    scrollArea.scroll(x, y);
                }
                function dragEnter(e) {
                    scrollArea = new ScrollArea(element);
                    timer = setInterval(scroll, 100);
                }

                function dragOver(e) {
                    x = e.pageX;
                    y = e.pageY;
                }

                function dragLeave(e) {
                    clearTimeout(timer);
                }

                ko.bindingHandlers.dragEvents.init(element, function () {
                    return {
                        name: name,
                        dragEnter: dragEnter,
                        dragOver: dragOver,
                        dragLeave: dragLeave
                    };
                });
            }
        }
    });
});
<|endoftext|>


<commit_before><commit_msg>Update home to hold correct values<commit_after><|endoftext|>


<commit_before>import $ from 'jquery';
import 'jstree';
import 'jstree/dist/themes/default-dark/style.css';

import MouseTrap from 'mousetrap';

import Signal from '../../utils/signal';
import { state } from '../../state';
import { song } from '../../utils/songmanager';

import browserTemplate from './templates/browser.marko';

import styles from './styles.css';

export default class Browser {
  constructor(target) {
    this.target = target;
  }

  render() {
    const songs = [
      { name: "Onward", },
    ];
    $(this.target).append(browserTemplate.renderToString({songs}));

    $(this.target).find('.item-list').jstree({
      "core": {
        "themes": {
          "name": "default-dark",
          "variant": "small",
        },
        "data": {
          "url": "http://localhost:8080/songs",
          "type": "GET",
          "dataFilter": function(data) {
            let songs = JSON.parse(data);
            let result = [];
            for (let s in songs) {
              result.push({ "text": songs[s].name});
            }
            return JSON.stringify({ "text": "Demo Songs", "children": result });
          },
        },
      },
    });
  }

  refresh() {
    $(this.target).empty();
    this.render();
  }
}
<commit_msg>Double click to load song.<commit_after>import $ from 'jquery';
import 'jstree';
import 'jstree/dist/themes/default-dark/style.css';

import MouseTrap from 'mousetrap';

import Signal from '../../utils/signal';
import { state } from '../../state';
import { song } from '../../utils/songmanager';

import browserTemplate from './templates/browser.marko';

import styles from './styles.css';

export default class Browser {
  constructor(target) {
    this.target = target;
  }

  render() {
    const songs = [
      { name: "Onward", },
    ];
    $(this.target).append(browserTemplate.renderToString({songs}));

    $(this.target).find('.item-list').jstree({
      "core": {
        "themes": {
          "name": "default-dark",
          "variant": "small",
        },
        "data": {
          "url": "http://localhost:8080/songs",
          "type": "GET",
          "dataFilter": function(data) {
            let songs = JSON.parse(data);
            let result = [];
            for (let s in songs) {
              result.push({ "text": songs[s].name, "_id": songs[s]._id });
            }
            return JSON.stringify({ "text": "Demo Songs", "children": result });
          },
        },
      },
    }).on('dblclick','.jstree-anchor', function (e) {
      let instance = $.jstree.reference(this);
      let node = instance.get_node(this);
      let songfileURL = `http://localhost:8080/songs/${node.original._id}/file`;
      song.downloadSong(songfileURL);
    });
  }

  refresh() {
    $(this.target).empty();
    this.render();
  }
}
<|endoftext|>