<commit_before>import _ from 'lodash';
import path from 'path';
import fs from 'fs-extra';
import semver from 'semver';
import inquirer from 'inquirer';
import * as util from './util.js';
import * as git from './git.js';
import * as depTree from './depTree.js';

function areTargetsEqual( lhs, rhs ) {
	if ( lhs.branch && rhs.branch ) {
		return lhs.branch === rhs.branch;
	}
	if ( lhs.tag && rhs.tag ) {
		return lhs.tag === rhs.tag;
	}
	if ( lhs.commit && rhs.commit ) {
		return lhs.commit === rhs.commit;
	}
	return false;
}

// Returns tag name of tag that can be recycled for this repo, or empty string if nothing can be found
async function getTagPointingAtCurrentHEAD( repoDir ) {
	const target = await git.getCurrentTarget( repoDir );
	if ( target.tag ) {
		// already in detached HEAD state, pointing at a tag
		return target.tag;
	}
	// list all the tags in this repo, and look in each one to see if one of the tags was made on the commit we are currently sat on.
	const lastCommit = await git.getLastCommit( repoDir );
	const tags = await git.listTags( repoDir );
	let tagFound = '';
	for ( const tag of tags ) {
		const tagFlavioJson = JSON.parse( await git.show( repoDir, tag, 'flavio.json' ) );
		if ( _.isObject( tagFlavioJson.tag ) ) {
			// if this object exists in the flavio.json, it means the tag was created by flavio's tagging process previously
			let isEqual = false;
			if ( tagFlavioJson.tag.branch ) {
				// (backwards compatibility with old json)
				isEqual = tagFlavioJson.tag.branch === target.branch;
			} else if ( tagFlavioJson.tag.target ) {
				isEqual = areTargetsEqual( tagFlavioJson.tag.target, target );
			}
			if ( isEqual && tagFlavioJson.tag.commit === lastCommit ) {
				tagFound = tag;
				break;
			}
		}
	}
	return tagFound;
}

async function determineRecycledTagForElement( node, recycleTagMap ) {
	// if a node can recycle it's tag, and all of it's dependencies can also recycle their tag, then we can do a recycle.
	if ( !recycleTagMap.has( node.dir ) ) {
		const tagName = await getTagPointingAtCurrentHEAD( node.dir );
		recycleTagMap.set( node.dir, tagName );
	}

	if ( !recycleTagMap.get( node.dir ) ) {
		// has no valid tag to recycle - don't bother checking children
		return '';
	}

	// check all children to see if they have valid tags
	for ( const [name, module] of node.children ) { // eslint-disable-line no-unused-vars
		const childTag = await determineRecycledTagForElement( module, recycleTagMap );
		if ( !childTag ) {
			// invalid child tag - just report invalid back
			return '';
		}
	}

	return recycleTagMap.get( node.dir );
}

function getNextAvailableVersion( tagList, version, versionType ) {
	let test = version;
	do {
		test = semver.inc( test, versionType );
	} while ( tagList.indexOf( test ) >= 0 );
	return test;
}

async function determineTagName( options, node ) {
	const isInteractive = options.interactive !== false;
	const flavioJson = await util.loadFlavioJson( node.dir );
	if ( _.isEmpty( flavioJson ) ) {
		return null;
	}
	// strip prerelease tag off name
	const tagName = semver.inc( flavioJson.version, options.increment );
	// see if tag of the same name already exists
	const tagList = await git.listTags( node.dir );
	if ( tagList.indexOf( tagName ) >= 0 ) {
		// if it already exists, suggest either the next available major, minor or prerelease version for the user to pick
		const nextMajor = getNextAvailableVersion( tagList, tagName, 'major' );
		const nextMinor = getNextAvailableVersion( tagList, tagName, 'minor' );
		const nextPatch = getNextAvailableVersion( tagList, tagName, 'patch' );
		let defaultVal;
		switch ( options.increment ) {
			case 'major':
				defaultVal = nextMajor;
				break;
			default:
			case 'minor':
				defaultVal = nextMinor;
				break;
			case 'patch':
				defaultVal = nextPatch;
				break;
		}
		if ( isInteractive ) {
			const question = {
				type: 'list',
				name: 'q',
				message: `${node.name}: Tag ${tagName} already exists. Use available alternative?`,
				choices: [nextMajor, nextMinor, nextPatch],
				default: defaultVal
			};
			const answer = await inquirer.prompt( [question] );
			return answer.q;
		} else {
			return defaultVal;
		}
	}
	return tagName;
}

async function determineTagsRecursive( options, node, recycleTagMap, tagMap ) {
	if ( !tagMap.has( node.name ) ) {
		await git.fetch( node.dir );
		const target = await git.getCurrentTarget( node.dir );
		const recycledTag = await determineRecycledTagForElement( node, recycleTagMap );
		if ( recycledTag ) {
			tagMap.set( node.name, { tag: recycledTag, originalTarget: target, create: false, dir: node.dir } );
		} else {
			const tagName = await determineTagName( options, node );
			if ( tagName ) {
				const branchName = `release/${tagName}`;
				const incrementVersion = await git.isUpToDate( node.dir ); // only increment version in flavio.json if our local HEAD is up to date with the remote branch
				tagMap.set( node.name, { tag: tagName, originalTarget: target, branch: branchName, create: true, dir: node.dir, incrementMasterVersion: incrementVersion } );
			} else {
				console.log( `WARNING: ${node.name} has no valid flavio.json, so will not be tagged` );
			}
		}
	}
	for ( const [name, module] of node.children ) { // eslint-disable-line no-unused-vars
		await determineTagsRecursive( options, module, recycleTagMap, tagMap );
	}
}

async function determineTags( options, tree ) {
	// for every node in the tree, check to see if any of the dependencies are on a branch with change without a tag pointing at the current HEAD
	let recycleTagMap = new Map(); // map of module dir names and a tag they can recycle to, so we don't calculate it multiple times
	let tagMap = new Map(); // map of module dir names and tags which take into account if the children have valid tags too
	await determineTagsRecursive( options, tree, recycleTagMap, tagMap );
	return tagMap;
}

function lockFlavioJson( flavioJson, reposToTag, version, lastCommit, target ) {
	// change version
	flavioJson.version = version;
	// lock all dependency versions
	if ( flavioJson.dependencies ) {
		for ( const name of Object.keys( flavioJson.dependencies ) ) {
			if ( reposToTag.has( name ) ) {
				const repo = flavioJson.dependencies[name];
				const repoUrl = util.parseRepositoryUrl( repo );
				const newTagName = reposToTag.get( name ).tag;
				flavioJson.dependencies[name] = `${repoUrl.url}#${newTagName}`;
			}
		}
	}
	// add commit SHA and branch name
	flavioJson.tag = {
		commit: lastCommit,
		target
	};
	return flavioJson;
}

async function writeVersionToJsonFile( pkgJsonPath, version ) {
	if ( fs.existsSync( pkgJsonPath ) ) {
		const pkgJson = JSON.parse( fs.readFileSync( pkgJsonPath, 'utf8' ) );
		pkgJson.version = version;
		fs.writeFileSync( pkgJsonPath, JSON.stringify( pkgJson, null, 2 ), 'utf8' );
		return true;
	}
	return false;
}

async function prepareTags( reposToTag ) {
	for ( const [name, tagObject] of reposToTag ) { // eslint-disable-line no-unused-vars
		const dir = tagObject.dir;
		if ( tagObject.create ) {
			const lastCommit = await git.getLastCommit( dir );
			await git.createAndCheckoutBranch( dir, tagObject.branch );
			// then modify flavio.json
			let flavioJson = await util.loadFlavioJson( dir );
			flavioJson = lockFlavioJson( flavioJson, reposToTag, tagObject.tag, lastCommit, tagObject.originalTarget );
			await util.saveFlavioJson( dir, flavioJson );
			let filesArray = ['flavio.json'];
			// if there is a package.json or a bower.json, change the version number in those too
			if ( await writeVersionToJsonFile( path.join( dir, 'package.json' ), tagObject.tag ) ) {
				filesArray.push( 'package.json' );
			}
			if ( await writeVersionToJsonFile( path.join( dir, 'bower.json' ), tagObject.tag ) ) {
				filesArray.push( 'bower.json' );
			}
			// commit new flavio.json
			await git.addAndCommit( dir, filesArray, `Commiting new flavio.json for tag ${tagObject.tag}` );
			// then create tag
			await git.createTag( dir, tagObject.tag, `Tag for v${tagObject.tag}` );
			// switch back to original target
			await git.checkout( dir, tagObject.originalTarget );
		}
	}
}

function incrementMasterVersion( options, version ) {
	const prerelease = semver.prerelease( version );
	return semver.inc( version, `pre${options.increment}`, prerelease ? prerelease[0] : 'snapshot' );
}

async function incrementOriginalVersions( options, reposToTag ) {
	for ( const [name, tagObject] of reposToTag ) { // eslint-disable-line no-unused-vars
		const dir = tagObject.dir;
		if ( tagObject.incrementMasterVersion ) {
			// modify flavio.json
			let flavioJson = await util.loadFlavioJson( dir );
			flavioJson.version = incrementMasterVersion( options, flavioJson.version );
			await util.saveFlavioJson( dir, flavioJson );
			let filesArray = ['flavio.json'];
			// if there is a package.json or a bower.json, change the version number in those too
			if ( await writeVersionToJsonFile( path.join( dir, 'package.json' ), flavioJson.version ) ) {
				filesArray.push( 'package.json' );
			}
			if ( await writeVersionToJsonFile( path.join( dir, 'bower.json' ), flavioJson.version ) ) {
				filesArray.push( 'bower.json' );
			}
			// commit new flavio.json
			await git.addAndCommit( dir, filesArray, `Commiting new flavio.json for tag ${tagObject.tag}` );
			await git.push( dir, ['origin', 'HEAD'] );
		}
	}
}

async function pushTags( options, reposToTag ) {
	for ( const [name, tagObject] of reposToTag ) { // eslint-disable-line no-unused-vars
		const dir = tagObject.dir;
		if ( tagObject.create ) {
			// push release branch
			await git.push( dir, ['origin', `${tagObject.branch}`] );
			// push tag
			await git.push( dir, ['origin', `refs/tags/${tagObject.tag}`] );
		}
	}
}

async function confirmUser( options, reposToTag ) {
	const isInteractive = options.interactive !== false;
	if ( isInteractive ) {
		for ( const [name, tagObject] of reposToTag ) { // eslint-disable-line no-unused-vars
			if ( !tagObject.create ) {
				console.log( `[REUSE] ${name}: ${tagObject.tag}` );
			}
		}
		for ( const [name, tagObject] of reposToTag ) { // eslint-disable-line no-unused-vars
			if ( tagObject.create ) {
				console.log( `[NEW] ${name}: ${tagObject.tag}` );
			}
		}
		for ( const [name, tagObject] of reposToTag ) { // eslint-disable-line no-unused-vars
			if ( tagObject.create && !tagObject.incrementMasterVersion ) {
				console.log( `WARNING: ${name} is not up to date with it's upstream branch (${tagObject.originalTarget.branch || tagObject.originalTarget.commit}), and so will not have the flavio.json version automatically incremented` );
			}
		}
		const question = {
			type: 'confirm',
			name: 'q',
			message: `Commit changes?`
		};
		const answer = await inquirer.prompt( [question] );
		return answer.q;
	}
	return true;
}

/**
 *
 *
 */
async function tagOperation( options = {} ) {
	options.increment = options.increment || 'minor';
	await util.readConfigFile( options.cwd );
	const tree = await depTree.traverse( options );
	// TODO: disallow a tag operation if there are any local changes / conflicts?

	// work out which repos need to be tagged, and what those tags are going to called
	const reposToTag = await determineTags( options, tree );
	let count = 0;
	for ( const [name, tagObject] of reposToTag ) { // eslint-disable-line no-unused-vars
		if ( tagObject.create ) {
			count++;
		}
	}
	if ( count === 0 ) {
		// TODO: print out a version that they should use instead
		console.log( `No valid repositories found to tag` );
		return;
	}
	// confirm with user the values to tag
	if ( !( await confirmUser( options, reposToTag ) ) ) {
		return;
	}
	// increment version number on the original branch first
	await incrementOriginalVersions( options, reposToTag );
	// create release branches + tags, modify flavio.json
	await prepareTags( reposToTag );
	// then push everything
	await pushTags( options, reposToTag );
}

export default tagOperation;
<commit_msg>If a tag doesn't have a flavio.json, don't try to examine it<commit_after>import _ from 'lodash';
import path from 'path';
import fs from 'fs-extra';
import semver from 'semver';
import inquirer from 'inquirer';
import * as util from './util.js';
import * as git from './git.js';
import * as depTree from './depTree.js';

function areTargetsEqual( lhs, rhs ) {
	if ( lhs.branch && rhs.branch ) {
		return lhs.branch === rhs.branch;
	}
	if ( lhs.tag && rhs.tag ) {
		return lhs.tag === rhs.tag;
	}
	if ( lhs.commit && rhs.commit ) {
		return lhs.commit === rhs.commit;
	}
	return false;
}

// Returns tag name of tag that can be recycled for this repo, or empty string if nothing can be found
async function getTagPointingAtCurrentHEAD( repoDir ) {
	const target = await git.getCurrentTarget( repoDir );
	if ( target.tag ) {
		// already in detached HEAD state, pointing at a tag
		return target.tag;
	}
	// list all the tags in this repo, and look in each one to see if one of the tags was made on the commit we are currently sat on.
	const lastCommit = await git.getLastCommit( repoDir );
	const tags = await git.listTags( repoDir );
	let tagFound = '';
	for ( const tag of tags ) {
		try {
			const tagFlavioJson = JSON.parse( await git.show( repoDir, tag, 'flavio.json' ) );
			if ( _.isObject( tagFlavioJson.tag ) ) {
				// if this object exists in the flavio.json, it means the tag was created by flavio's tagging process previously
				let isEqual = false;
				if ( tagFlavioJson.tag.branch ) {
					// (backwards compatibility with old json)
					isEqual = tagFlavioJson.tag.branch === target.branch;
				} else if ( tagFlavioJson.tag.target ) {
					isEqual = areTargetsEqual( tagFlavioJson.tag.target, target );
				}
				if ( isEqual && tagFlavioJson.tag.commit === lastCommit ) {
					tagFound = tag;
					break;
				}
			}
		} catch ( err ) {}
	}
	return tagFound;
}

async function determineRecycledTagForElement( node, recycleTagMap ) {
	// if a node can recycle it's tag, and all of it's dependencies can also recycle their tag, then we can do a recycle.
	if ( !recycleTagMap.has( node.dir ) ) {
		const tagName = await getTagPointingAtCurrentHEAD( node.dir );
		recycleTagMap.set( node.dir, tagName );
	}

	if ( !recycleTagMap.get( node.dir ) ) {
		// has no valid tag to recycle - don't bother checking children
		return '';
	}

	// check all children to see if they have valid tags
	for ( const [name, module] of node.children ) { // eslint-disable-line no-unused-vars
		const childTag = await determineRecycledTagForElement( module, recycleTagMap );
		if ( !childTag ) {
			// invalid child tag - just report invalid back
			return '';
		}
	}

	return recycleTagMap.get( node.dir );
}

function getNextAvailableVersion( tagList, version, versionType ) {
	let test = version;
	do {
		test = semver.inc( test, versionType );
	} while ( tagList.indexOf( test ) >= 0 );
	return test;
}

async function determineTagName( options, node ) {
	const isInteractive = options.interactive !== false;
	const flavioJson = await util.loadFlavioJson( node.dir );
	if ( _.isEmpty( flavioJson ) ) {
		return null;
	}
	// strip prerelease tag off name
	const tagName = semver.inc( flavioJson.version, options.increment );
	// see if tag of the same name already exists
	const tagList = await git.listTags( node.dir );
	if ( tagList.indexOf( tagName ) >= 0 ) {
		// if it already exists, suggest either the next available major, minor or prerelease version for the user to pick
		const nextMajor = getNextAvailableVersion( tagList, tagName, 'major' );
		const nextMinor = getNextAvailableVersion( tagList, tagName, 'minor' );
		const nextPatch = getNextAvailableVersion( tagList, tagName, 'patch' );
		let defaultVal;
		switch ( options.increment ) {
			case 'major':
				defaultVal = nextMajor;
				break;
			default:
			case 'minor':
				defaultVal = nextMinor;
				break;
			case 'patch':
				defaultVal = nextPatch;
				break;
		}
		if ( isInteractive ) {
			const question = {
				type: 'list',
				name: 'q',
				message: `${node.name}: Tag ${tagName} already exists. Use available alternative?`,
				choices: [nextMajor, nextMinor, nextPatch],
				default: defaultVal
			};
			const answer = await inquirer.prompt( [question] );
			return answer.q;
		} else {
			return defaultVal;
		}
	}
	return tagName;
}

async function determineTagsRecursive( options, node, recycleTagMap, tagMap ) {
	if ( !tagMap.has( node.name ) ) {
		await git.fetch( node.dir );
		const target = await git.getCurrentTarget( node.dir );
		const recycledTag = await determineRecycledTagForElement( node, recycleTagMap );
		if ( recycledTag ) {
			tagMap.set( node.name, { tag: recycledTag, originalTarget: target, create: false, dir: node.dir } );
		} else {
			const tagName = await determineTagName( options, node );
			if ( tagName ) {
				const branchName = `release/${tagName}`;
				const incrementVersion = await git.isUpToDate( node.dir ); // only increment version in flavio.json if our local HEAD is up to date with the remote branch
				tagMap.set( node.name, { tag: tagName, originalTarget: target, branch: branchName, create: true, dir: node.dir, incrementMasterVersion: incrementVersion } );
			} else {
				console.log( `WARNING: ${node.name} has no valid flavio.json, so will not be tagged` );
			}
		}
	}
	for ( const [name, module] of node.children ) { // eslint-disable-line no-unused-vars
		await determineTagsRecursive( options, module, recycleTagMap, tagMap );
	}
}

async function determineTags( options, tree ) {
	// for every node in the tree, check to see if any of the dependencies are on a branch with change without a tag pointing at the current HEAD
	let recycleTagMap = new Map(); // map of module dir names and a tag they can recycle to, so we don't calculate it multiple times
	let tagMap = new Map(); // map of module dir names and tags which take into account if the children have valid tags too
	await determineTagsRecursive( options, tree, recycleTagMap, tagMap );
	return tagMap;
}

function lockFlavioJson( flavioJson, reposToTag, version, lastCommit, target ) {
	// change version
	flavioJson.version = version;
	// lock all dependency versions
	if ( flavioJson.dependencies ) {
		for ( const name of Object.keys( flavioJson.dependencies ) ) {
			if ( reposToTag.has( name ) ) {
				const repo = flavioJson.dependencies[name];
				const repoUrl = util.parseRepositoryUrl( repo );
				const newTagName = reposToTag.get( name ).tag;
				flavioJson.dependencies[name] = `${repoUrl.url}#${newTagName}`;
			}
		}
	}
	// add commit SHA and branch name
	flavioJson.tag = {
		commit: lastCommit,
		target
	};
	return flavioJson;
}

async function writeVersionToJsonFile( pkgJsonPath, version ) {
	if ( fs.existsSync( pkgJsonPath ) ) {
		const pkgJson = JSON.parse( fs.readFileSync( pkgJsonPath, 'utf8' ) );
		pkgJson.version = version;
		fs.writeFileSync( pkgJsonPath, JSON.stringify( pkgJson, null, 2 ), 'utf8' );
		return true;
	}
	return false;
}

async function prepareTags( reposToTag ) {
	for ( const [name, tagObject] of reposToTag ) { // eslint-disable-line no-unused-vars
		const dir = tagObject.dir;
		if ( tagObject.create ) {
			const lastCommit = await git.getLastCommit( dir );
			await git.createAndCheckoutBranch( dir, tagObject.branch );
			// then modify flavio.json
			let flavioJson = await util.loadFlavioJson( dir );
			flavioJson = lockFlavioJson( flavioJson, reposToTag, tagObject.tag, lastCommit, tagObject.originalTarget );
			await util.saveFlavioJson( dir, flavioJson );
			let filesArray = ['flavio.json'];
			// if there is a package.json or a bower.json, change the version number in those too
			if ( await writeVersionToJsonFile( path.join( dir, 'package.json' ), tagObject.tag ) ) {
				filesArray.push( 'package.json' );
			}
			if ( await writeVersionToJsonFile( path.join( dir, 'bower.json' ), tagObject.tag ) ) {
				filesArray.push( 'bower.json' );
			}
			// commit new flavio.json
			await git.addAndCommit( dir, filesArray, `Commiting new flavio.json for tag ${tagObject.tag}` );
			// then create tag
			await git.createTag( dir, tagObject.tag, `Tag for v${tagObject.tag}` );
			// switch back to original target
			await git.checkout( dir, tagObject.originalTarget );
		}
	}
}

function incrementMasterVersion( options, version ) {
	const prerelease = semver.prerelease( version );
	return semver.inc( version, `pre${options.increment}`, prerelease ? prerelease[0] : 'snapshot' );
}

async function incrementOriginalVersions( options, reposToTag ) {
	for ( const [name, tagObject] of reposToTag ) { // eslint-disable-line no-unused-vars
		const dir = tagObject.dir;
		if ( tagObject.incrementMasterVersion ) {
			// modify flavio.json
			let flavioJson = await util.loadFlavioJson( dir );
			flavioJson.version = incrementMasterVersion( options, flavioJson.version );
			await util.saveFlavioJson( dir, flavioJson );
			let filesArray = ['flavio.json'];
			// if there is a package.json or a bower.json, change the version number in those too
			if ( await writeVersionToJsonFile( path.join( dir, 'package.json' ), flavioJson.version ) ) {
				filesArray.push( 'package.json' );
			}
			if ( await writeVersionToJsonFile( path.join( dir, 'bower.json' ), flavioJson.version ) ) {
				filesArray.push( 'bower.json' );
			}
			// commit new flavio.json
			await git.addAndCommit( dir, filesArray, `Commiting new flavio.json for tag ${tagObject.tag}` );
			await git.push( dir, ['origin', 'HEAD'] );
		}
	}
}

async function pushTags( options, reposToTag ) {
	for ( const [name, tagObject] of reposToTag ) { // eslint-disable-line no-unused-vars
		const dir = tagObject.dir;
		if ( tagObject.create ) {
			// push release branch
			await git.push( dir, ['origin', `${tagObject.branch}`] );
			// push tag
			await git.push( dir, ['origin', `refs/tags/${tagObject.tag}`] );
		}
	}
}

async function confirmUser( options, reposToTag ) {
	const isInteractive = options.interactive !== false;
	if ( isInteractive ) {
		for ( const [name, tagObject] of reposToTag ) { // eslint-disable-line no-unused-vars
			if ( !tagObject.create ) {
				console.log( `[REUSE] ${name}: ${tagObject.tag}` );
			}
		}
		for ( const [name, tagObject] of reposToTag ) { // eslint-disable-line no-unused-vars
			if ( tagObject.create ) {
				console.log( `[NEW] ${name}: ${tagObject.tag}` );
			}
		}
		for ( const [name, tagObject] of reposToTag ) { // eslint-disable-line no-unused-vars
			if ( tagObject.create && !tagObject.incrementMasterVersion ) {
				console.log( `WARNING: ${name} is not up to date with it's upstream branch (${tagObject.originalTarget.branch || tagObject.originalTarget.commit}), and so will not have the flavio.json version automatically incremented` );
			}
		}
		const question = {
			type: 'confirm',
			name: 'q',
			message: `Commit changes?`
		};
		const answer = await inquirer.prompt( [question] );
		return answer.q;
	}
	return true;
}

/**
 *
 *
 */
async function tagOperation( options = {} ) {
	options.increment = options.increment || 'minor';
	await util.readConfigFile( options.cwd );
	const tree = await depTree.traverse( options );
	// TODO: disallow a tag operation if there are any local changes / conflicts?

	// work out which repos need to be tagged, and what those tags are going to called
	const reposToTag = await determineTags( options, tree );
	let count = 0;
	for ( const [name, tagObject] of reposToTag ) { // eslint-disable-line no-unused-vars
		if ( tagObject.create ) {
			count++;
		}
	}
	if ( count === 0 ) {
		// TODO: print out a version that they should use instead
		console.log( `No valid repositories found to tag` );
		return;
	}
	// confirm with user the values to tag
	if ( !( await confirmUser( options, reposToTag ) ) ) {
		return;
	}
	// increment version number on the original branch first
	await incrementOriginalVersions( options, reposToTag );
	// create release branches + tags, modify flavio.json
	await prepareTags( reposToTag );
	// then push everything
	await pushTags( options, reposToTag );
}

export default tagOperation;
<|endoftext|>


<commit_before>    /**
     * OAuth Consumer
     *
     * @constructor
     * @param {String} key
     * @param {String} secret
     */
    jsOAuth = function (key, secret, callback_url) {
        var args = arguments, args_callee = args.callee, args_length = args.length,
            token = EMPTY_STRING, jsoauth = this;

        if (!(jsoauth instanceof args_callee)) {
            return new args_callee(key, secret, callback_url);
        }

        jsoauth.key = key; /** @type {String|undefined} */
        jsoauth.secret = secret; /** @type {String|undefined} */
        jsoauth.callback_url = callback_url;  /** @type {String|undefined} */
        jsoauth.signature = EMPTY_STRING;
        jsoauth.signature_method = jsOAuth.SIGNATURE_METHOD['PLAINTEXT'];
        jsoauth.OAUTH_REALM = EMPTY_STRING;
        jsoauth.OAUTH_REQUEST_TOKEN_URL = EMPTY_STRING;
        jsoauth.OAUTH_REQUEST_AUTH_URL = EMPTY_STRING;
        jsoauth.OAUTH_GET_TOKEN_URL = EMPTY_STRING;

        jsoauth.oauth_parameters = {
            'oauth_consumer_key': key,
            'oauth_token': token,
            'oauth_signature_method': jsoauth.signature_method,
            'oauth_signature': jsoauth.signature,
            'oauth_timestamp': EMPTY_STRING,
            'oauth_nonce': EMPTY_STRING,
            'oauth_version': jsoauth.OAUTH_VERSION,
            'oauth_verifier': EMPTY_STRING
        };

        /**
         *
         * @param {String} url
         * @param {String} method
         * @param {Object} parameters
         */
        jsoauth.Request = function(url, method, parameters){
            var args = arguments, args_callee = args.callee, args_length = args.length,
                request = this;

            if (!(request instanceof args_callee)) {
                return new args_callee(url, method, parameters);
            }
            request.url = url;
            request.method = method;
            request.parameters = (parameters !== UNDEFINED) ? parameters : {};
            /**
             * Override toString to collect, sort and concatenate into a
             * normalized string
             */
            request.parameters.toString = function () {
                var i, params = this.parameters, params_arr = [],
                enc = this.urlEncode;

                params.sort();
                for(i in params){
                    if (params.hasOwnProperty(i) && params[i] !== UNDEFINED) {
                        params_arr.push(enc(i) + '=' + enc(params[i]));
                    }
                }

                return (params_arr.length > 0 ? params_arr.join('&') : UNDEFINED);
            };

            request.setParameter = function (parameter, value) {
                parameters[parameter] = value;
            };
            request.getParameter = function (parameter) {
                return parameters.hasOwnProperty(parameter) ? parameters[parameter] : UNDEFINED;
            };
            request.sign = function (consumer, token) {
                var params = consumer.oauth_parameters;
                params.oauth_signature_method = signature_method;
                params.oauth_timestamp = this.timestamp;
                params.oauth_nonce = this.nonce;
            };
            request.timestamp = (new Date).getTime(); // current timestamp
            request.nonce = generateKey(64); // 64-bit rendom key

            /**
             * rfc3986 compatable encode of a string
             *
             * @param {String} string
             */
            request.urlEncode = function (string){
                var reserved_chars = /( |\!|\*|\"|\'|\(|\)|\;|\:|\@|\&|\=|\+|\$|\,|\/|\?|\%|\#|\[|\]|\<|\>|\{|\}|\||\\|`|\^)/,
                    str_len = string.length, i, string_arr = string.split('');

                for (i = 0; i < str_len; i++) {
                    if (string_arr[i].match(reserved_chars)) {
                        string_arr[i] = '%' + (string_arr[i].charCodeAt(0)).toString(16).toUpperCase();
                    }
                }

                return string_arr.join('');
            };

            /**
             * Override standard toString function so that we can see the
             * request produced the correct URL
             */
            request.toString = function() {
                var params = jsoauth.oauth_parameters;
                params.sort(); // lexicographical byte value ordering

                console.debug(params);
            };

            return request;
        };

        /**
         *
         * @constructor
         * @param {String} key
         * @param {String} secret
         */
        jsoauth.Token = function(key, secret){

        };

        /**
         * @memberOf jsOAuth
         */
        jsoauth.getRequestToken = function () {
            var request = new this.Request(this.OAUTH_REQUEST_AUTH_URL,
                jsOAuth.HTTP_METHOD_POST, {});
            console.log(request);
        },

        /**
         * @memberOf jsOAuth
         */
        jsoauth.requestAuthorization = function () {},

        /**
         * @memberOf jsOAuth
         */
        jsoauth.getAccessToken = function () {}

        return jsoauth;
    };

    function generateKey(key_len) {
        key_len = key_len ? key_len : 64;

        var key_bytes = key_len / 8, value = '',
            chars = ['20', '21', '22', '23', '24', '25', '26', '27', '28', '29',
                     '2A', '2B', '2C', '2D', '2E', '2F', '30', '31', '32', '33',
                     '34', '35', '36', '37', '38', '39', '3A', '3B', '3C', '3D',
                     '3E', '3F', '40', '41', '42', '43', '44', '45', '46', '47',
                     '48', '49', '4A', '4B', '4C', '4D', '4E', '4F', '50', '51',
                     '52', '53', '54', '55', '56', '57', '58', '59', '5A', '5B',
                     '5C', '5D', '5E', '5F', '60', '61', '62', '63', '64', '65',
                     '66', '67', '68', '69', '6A', '6B', '6C', '6D', '6E', '6F',
                     '70', '71', '72', '73', '74', '75', '76', '77', '78', '79',
                     '7A', '7B', '7C', '7D', '7E'],
            i;
        for (i = 0; i < key_bytes; i++) {
            value += chars[rand()];
        }

        return value;

        function rand() {
            var math = Math;
            return math.floor(math.random() * chars.length);
        }
    }

    jsOAuth.HTTP_METHOD_GET          = 'GET';    /** @const */
    jsOAuth.HTTP_METHOD_POST         = 'POST';   /** @const */
    jsOAuth.HTTP_METHOD_PUT          = 'PUT';    /** @const */
    jsOAuth.HTTP_METHOD_DELETE       = 'DELETE'; /** @const */

    jsOAuth.OAUTH_VERSION            = '1.0';    /** @const */

    jsOAuth.SIGNATURE_METHOD = {
        'HMAC-SHA1': {
            /**
             * Override default toString method to use as a constant key
             */
            toString: function () {
                return 'HMAC-SHA1';
            }
        },
        'PLAINTEXT': {
            toString: function () {
                return 'PLAINTEXT';
            },
            sign: function(){
                var signature = '';
                return signature;
            }
        }
    };

    /** closure compiler "export" method, use quoted syntax */
    window['jsOAuth'] = jsOAuth;

<commit_msg>added a little documentation<commit_after>    /**
     * OAuth Consumer
     *
     * @constructor
     * @param {String} key
     * @param {String} secret
     */
    jsOAuth = function (key, secret, callback_url) {
        var args = arguments, args_callee = args.callee, args_length = args.length,
            token = EMPTY_STRING, jsoauth = this;

        if (!(jsoauth instanceof args_callee)) {
            return new args_callee(key, secret, callback_url);
        }

        jsoauth.key = key; /** @type {String|undefined} */
        jsoauth.secret = secret; /** @type {String|undefined} */
        jsoauth.callback_url = callback_url;  /** @type {String|undefined} */
        jsoauth.signature = EMPTY_STRING;
        jsoauth.signature_method = jsOAuth.SIGNATURE_METHOD['PLAINTEXT'];
        jsoauth.OAUTH_REALM = EMPTY_STRING;
        jsoauth.OAUTH_REQUEST_TOKEN_URL = EMPTY_STRING;
        jsoauth.OAUTH_REQUEST_AUTH_URL = EMPTY_STRING;
        jsoauth.OAUTH_GET_TOKEN_URL = EMPTY_STRING;

        jsoauth.oauth_parameters = {
            'oauth_consumer_key': key,
            'oauth_token': token,
            'oauth_signature_method': jsoauth.signature_method,
            'oauth_signature': jsoauth.signature,
            'oauth_timestamp': EMPTY_STRING,
            'oauth_nonce': EMPTY_STRING,
            'oauth_version': jsoauth.OAUTH_VERSION,
            'oauth_verifier': EMPTY_STRING
        };

        /**
         * Create a new http request
         *
         * @param {String} url
         * @param {String} method
         * @param {Object} parameters
         */
        jsoauth.Request = function(url, method, parameters){
            var args = arguments, args_callee = args.callee, args_length = args.length,
                request = this;

            if (!(request instanceof args_callee)) {
                return new args_callee(url, method, parameters);
            }

            /** @property */
            request.url = url;

            /** @property */
            request.method = method;

            /** @property */
            request.parameters = (parameters !== UNDEFINED) ? parameters : {};

            /**
             * Override toString to collect, sort and concatenate into a
             * normalized string
             */
            request.parameters.toString = function () {
                var i, params = this.parameters, params_arr = [],
                enc = this.urlEncode;

                params.sort();
                for(i in params){
                    if (params.hasOwnProperty(i) && params[i] !== UNDEFINED) {
                        params_arr.push(enc(i) + '=' + enc(params[i]));
                    }
                }

                return (params_arr.length > 0 ? params_arr.join('&') : UNDEFINED);
            };

            /**
             * Set or create a value in the stack
             *
             * @param {String} parameter
             * @param {String} value
             */
            request.setParameter = function (parameter, value) {
                parameters[parameter] = value;
            };

            /**
             * Get a parameter from the stack
             *
             * @param {String} parameter
             */
            request.getParameter = function (parameter) {
                return parameters.hasOwnProperty(parameter) ? parameters[parameter] : UNDEFINED;
            };

            /**
             * Sign a request using the signature_method
             *
             * @param {Object} consumer
             * @param {String} token
             */
            request.sign = function (consumer, token) {
                var params = consumer.oauth_parameters;
                params.oauth_signature_method = signature_method;
                params.oauth_timestamp = this.timestamp;
                params.oauth_nonce = this.nonce;
            };

            /** @property */
            request.timestamp = (new Date).getTime(); // current timestamp

            /** @property */
            request.nonce = generateKey(64); // 64-bit rendom key

            /**
             * rfc3986 compatable encode of a string
             *
             * @param {String} string
             */
            request.urlEncode = function (string){
                var reserved_chars = /( |\!|\*|\"|\'|\(|\)|\;|\:|\@|\&|\=|\+|\$|\,|\/|\?|\%|\#|\[|\]|\<|\>|\{|\}|\||\\|`|\^)/,
                    str_len = string.length, i, string_arr = string.split('');

                for (i = 0; i < str_len; i++) {
                    if (string_arr[i].match(reserved_chars)) {
                        string_arr[i] = '%' + (string_arr[i].charCodeAt(0)).toString(16).toUpperCase();
                    }
                }

                return string_arr.join('');
            };

            /**
             * Override standard toString function so that we can see the
             * request produced the correct URL
             */
            request.toString = function() {
                var params = jsoauth.oauth_parameters;
                params.sort(); // lexicographical byte value ordering

                console.debug(params);
            };

            return request;
        };

        /**
         *
         * @constructor
         * @param {String} key
         * @param {String} secret
         */
        jsoauth.Token = function(key, secret){

        };

        /**
         * @memberOf jsOAuth
         */
        jsoauth.getRequestToken = function () {
            var request = new this.Request(this.OAUTH_REQUEST_AUTH_URL,
                jsOAuth.HTTP_METHOD_POST, {});
            console.log(request);
        },

        /**
         * @memberOf jsOAuth
         */
        jsoauth.requestAuthorization = function () {},

        /**
         * @memberOf jsOAuth
         */
        jsoauth.getAccessToken = function () {}

        return jsoauth;
    };

    function generateKey(key_len) {
        key_len = key_len ? key_len : 64;

        var key_bytes = key_len / 8, value = '',
            chars = ['20', '21', '22', '23', '24', '25', '26', '27', '28', '29',
                     '2A', '2B', '2C', '2D', '2E', '2F', '30', '31', '32', '33',
                     '34', '35', '36', '37', '38', '39', '3A', '3B', '3C', '3D',
                     '3E', '3F', '40', '41', '42', '43', '44', '45', '46', '47',
                     '48', '49', '4A', '4B', '4C', '4D', '4E', '4F', '50', '51',
                     '52', '53', '54', '55', '56', '57', '58', '59', '5A', '5B',
                     '5C', '5D', '5E', '5F', '60', '61', '62', '63', '64', '65',
                     '66', '67', '68', '69', '6A', '6B', '6C', '6D', '6E', '6F',
                     '70', '71', '72', '73', '74', '75', '76', '77', '78', '79',
                     '7A', '7B', '7C', '7D', '7E'],
            i;
        for (i = 0; i < key_bytes; i++) {
            value += chars[rand()];
        }

        return value;

        function rand() {
            var math = Math;
            return math.floor(math.random() * chars.length);
        }
    }

    jsOAuth.HTTP_METHOD_GET          = 'GET';    /** @const */
    jsOAuth.HTTP_METHOD_POST         = 'POST';   /** @const */
    jsOAuth.HTTP_METHOD_PUT          = 'PUT';    /** @const */
    jsOAuth.HTTP_METHOD_DELETE       = 'DELETE'; /** @const */

    jsOAuth.OAUTH_VERSION            = '1.0';    /** @const */

    jsOAuth.SIGNATURE_METHOD = {
        'HMAC-SHA1': {
            /**
             * Override default toString method to use as a constant key
             */
            toString: function () {
                return 'HMAC-SHA1';
            }
        },
        'PLAINTEXT': {
            toString: function () {
                return 'PLAINTEXT';
            },
            sign: function(){
                var signature = '';
                return signature;
            }
        }
    };

    /** closure compiler "export" method, use quoted syntax */
    window['jsOAuth'] = jsOAuth;

<|endoftext|>


<commit_before>/* eslint-disable import/prefer-default-export */
/* eslint-disable react/forbid-prop-types */
/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

import React, { useCallback, useMemo } from 'react';
import PropTypes from 'prop-types';
import { View } from 'react-native';

import { BottomConfirmModal, DataTablePageModal } from '../widgets/modals';
import { PageButton, SearchBar, DataTablePageView } from '../widgets';
import { DataTable, DataTableHeaderRow, DataTableRow } from '../widgets/DataTable';

import { UIDatabase } from '../database';
import Settings from '../settings/MobileAppSettings';
import { MODAL_KEYS, getAllPrograms, newSortDataBy } from '../utilities';
import { usePageReducer } from '../hooks';
import { createSupplierRequisition, gotoSupplierRequisition } from '../navigation/actions';
import {
  selectRow,
  deselectRow,
  deselectAll,
  deleteRequisitions,
  sortData,
  filterData,
  openModal,
  closeModal,
  getItemLayout,
  recordKeyExtractor,
} from './dataTableUtilities';

import globalStyles, { SUSSOL_ORANGE, newDataTableStyles, newPageStyles } from '../globalStyles';
import { buttonStrings, modalStrings } from '../localization';

const initialiseState = () => {
  const backingData = UIDatabase.objects('RequestRequisition');
  const data = newSortDataBy(backingData.slice(), 'serialNumber');
  return {
    backingData,
    data,
    keyExtractor: recordKeyExtractor,
    dataState: new Map(),
    searchTerm: '',
    filterDataKeys: ['serialNumber', 'otherStoreName.name'],
    sortBy: 'serialNumber',
    isAscending: true,
    modalKey: '',
    hasSelection: false,
  };
};

/**
 * Renders a mSupply mobile page with a list of supplier requisitions.
 *
 * State:
 * Uses a reducer to manage state with `backingData` being a realm results
 * of items to display. `data` is a plain JS array of realm objects. data is
 * hydrated from `backingData` to display in the interface.
 * i.e: When filtering, data is populated from filtered items of `backingData`.
 *
 * dataState is a simple map of objects corresponding to a row being displayed,
 * holding the state of a given row. Each object has the shape :
 * { isSelected, isFocused },
 *
 * @prop {String} routeName     The current route name for the top of the navigation stack.
 * @prop {Object} currentUser   The currently logged in user.
 * @prop {Func}   reduxDispatch Dispatch method for the app-wide redux store.
 */
export const SupplierRequisitionsPage = ({ routeName, currentUser, dispatch: reduxDispatch }) => {
  const [state, dispatch, debouncedDispatch] = usePageReducer(routeName, {}, initialiseState);

  const {
    data,
    dataState,
    sortBy,
    isAscending,
    columns,
    modalKey,
    hasSelection,
    searchTerm,
  } = state;

  const usingPrograms = useMemo(() => getAllPrograms(Settings, UIDatabase).length > 0, []);
  const { SELECT_SUPPLIER, PROGRAM_REQUISITION } = MODAL_KEYS;
  const NEW_REQUISITON = usingPrograms ? PROGRAM_REQUISITION : SELECT_SUPPLIER;

  const onPressRow = rowData => () => reduxDispatch(gotoSupplierRequisition(rowData));
  const onConfirmDelete = () => dispatch(deleteRequisitions());
  const onCancelDelete = () => dispatch(deselectAll());
  const onSearchFiltering = value => dispatch(filterData(value));
  const onNewRequisition = () => dispatch(openModal(NEW_REQUISITON));
  const onCloseModal = () => dispatch(closeModal());

  const onCreateRequisition = otherStoreName => {
    reduxDispatch(createSupplierRequisition({ otherStoreName, currentUser }));
    onCloseModal();
  };

  const onCreateProgramRequisition = requisitionParameters => {
    reduxDispatch(createSupplierRequisition({ ...requisitionParameters, currentUser }));
    dispatch(closeModal());
  };

  const getAction = (colKey, propName) => {
    switch (colKey) {
      case 'remove':
        if (propName === 'onCheckAction') return selectRow;
        return deselectRow;
      default:
        return null;
    }
  };

  const getModalOnSelect = () => {
    switch (modalKey) {
      case SELECT_SUPPLIER:
        return onCreateRequisition;
      case PROGRAM_REQUISITION:
        return onCreateProgramRequisition;
      default:
        return null;
    }
  };

  const renderRow = useCallback(
    listItem => {
      const { item, index } = listItem;
      const rowKey = recordKeyExtractor(item);
      const { row, alternateRow } = newDataTableStyles;
      return (
        <DataTableRow
          rowData={data[index]}
          rowState={dataState.get(rowKey)}
          rowKey={rowKey}
          style={index % 2 === 0 ? alternateRow : row}
          columns={columns}
          dispatch={dispatch}
          getAction={getAction}
          onPress={onPressRow(item)}
          rowIndex={index}
        />
      );
    },
    [data, dataState]
  );

  const renderHeader = useCallback(
    () => (
      <DataTableHeaderRow
        columns={columns}
        dispatch={debouncedDispatch}
        sortAction={sortData}
        isAscending={isAscending}
        sortBy={sortBy}
      />
    ),
    [sortBy, isAscending]
  );

  const PageButtons = useCallback(() => {
    const { verticalContainer, topButton } = globalStyles;
    return (
      <View style={verticalContainer}>
        <PageButton
          style={topButton}
          text={buttonStrings.new_requisition}
          onPress={onNewRequisition}
        />
      </View>
    );
  }, []);

  const {
    newPageTopSectionContainer,
    newPageTopLeftSectionContainer,
    newPageTopRightSectionContainer,
    searchBar,
  } = newPageStyles;
  return (
    <DataTablePageView>
      <View style={newPageTopSectionContainer}>
        <View style={newPageTopLeftSectionContainer}>
          <SearchBar
            onChangeText={onSearchFiltering}
            style={searchBar}
            color={SUSSOL_ORANGE}
            value={searchTerm}
            placeholder=""
          />
        </View>
        <View style={newPageTopRightSectionContainer}>
          <PageButtons />
        </View>
      </View>
      <DataTable
        data={data}
        extraData={dataState}
        renderRow={renderRow}
        renderHeader={renderHeader}
        keyExtractor={recordKeyExtractor}
        getItemLayout={getItemLayout}
      />
      <BottomConfirmModal
        isOpen={hasSelection}
        questionText={modalStrings.remove_these_items}
        onCancel={onCancelDelete}
        onConfirm={onConfirmDelete}
        confirmText={modalStrings.remove}
      />
      <DataTablePageModal
        fullScreen={false}
        isOpen={!!modalKey}
        modalKey={modalKey}
        onClose={onCloseModal}
        onSelect={getModalOnSelect()}
        dispatch={dispatch}
      />
    </DataTablePageView>
  );
};

SupplierRequisitionsPage.propTypes = {
  routeName: PropTypes.string.isRequired,
  dispatch: PropTypes.func.isRequired,
  currentUser: PropTypes.object.isRequired,
};

SupplierRequisitionsPage.propTypes = { routeName: PropTypes.string.isRequired };
<commit_msg>Add update to SupplierRequisitionsPage with custom database hooks<commit_after>/* eslint-disable import/prefer-default-export */
/* eslint-disable react/forbid-prop-types */
/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

import React, { useCallback, useMemo } from 'react';
import PropTypes from 'prop-types';
import { View } from 'react-native';

import { BottomConfirmModal, DataTablePageModal } from '../widgets/modals';
import { PageButton, SearchBar, DataTablePageView } from '../widgets';
import { DataTable, DataTableHeaderRow, DataTableRow } from '../widgets/DataTable';

import { UIDatabase } from '../database';
import Settings from '../settings/MobileAppSettings';
import { MODAL_KEYS, getAllPrograms, newSortDataBy } from '../utilities';
import { usePageReducer, useNavigationFocusRefresh, useDatabaseListener } from '../hooks';
import { createSupplierRequisition, gotoSupplierRequisition } from '../navigation/actions';
import {
  selectRow,
  deselectRow,
  deselectAll,
  deleteRequisitions,
  sortData,
  filterData,
  openModal,
  closeModal,
  getItemLayout,
  recordKeyExtractor,
  refreshData,
} from './dataTableUtilities';

import globalStyles, { SUSSOL_ORANGE, newDataTableStyles, newPageStyles } from '../globalStyles';
import { buttonStrings, modalStrings } from '../localization';

const initialiseState = () => {
  const backingData = UIDatabase.objects('RequestRequisition');
  const data = newSortDataBy(backingData.slice(), 'serialNumber');
  return {
    backingData,
    data,
    keyExtractor: recordKeyExtractor,
    dataState: new Map(),
    searchTerm: '',
    filterDataKeys: ['serialNumber', 'otherStoreName.name'],
    sortBy: 'serialNumber',
    isAscending: true,
    modalKey: '',
    hasSelection: false,
  };
};

/**
 * Renders a mSupply mobile page with a list of supplier requisitions.
 *
 * State:
 * Uses a reducer to manage state with `backingData` being a realm results
 * of items to display. `data` is a plain JS array of realm objects. data is
 * hydrated from `backingData` to display in the interface.
 * i.e: When filtering, data is populated from filtered items of `backingData`.
 *
 * dataState is a simple map of objects corresponding to a row being displayed,
 * holding the state of a given row. Each object has the shape :
 * { isSelected, isFocused },
 *
 * @prop {String} routeName     The current route name for the top of the navigation stack.
 * @prop {Object} currentUser   The currently logged in user.
 * @prop {Func}   reduxDispatch Dispatch method for the app-wide redux store.
 * @prop {Object} navigation    Reference to the main application stack navigator.
 */
export const SupplierRequisitionsPage = ({
  routeName,
  currentUser,
  dispatch: reduxDispatch,
  navigation,
}) => {
  const [state, dispatch, debouncedDispatch] = usePageReducer(routeName, {}, initialiseState);

  const {
    data,
    dataState,
    sortBy,
    isAscending,
    columns,
    modalKey,
    hasSelection,
    searchTerm,
  } = state;

  // Custom hook to refresh data on this page when becoming the head of the stack again.
  useNavigationFocusRefresh(dispatch, navigation);

  // Custom hook to listen to sync changes - refreshing data when requisitions are synced.
  useDatabaseListener(() => dispatch(refreshData()), 'Requisition');

  const usingPrograms = useMemo(() => getAllPrograms(Settings, UIDatabase).length > 0, []);
  const { SELECT_SUPPLIER, PROGRAM_REQUISITION } = MODAL_KEYS;
  const NEW_REQUISITON = usingPrograms ? PROGRAM_REQUISITION : SELECT_SUPPLIER;

  const onPressRow = rowData => () => reduxDispatch(gotoSupplierRequisition(rowData));
  const onConfirmDelete = () => dispatch(deleteRequisitions());
  const onCancelDelete = () => dispatch(deselectAll());
  const onSearchFiltering = value => dispatch(filterData(value));
  const onNewRequisition = () => dispatch(openModal(NEW_REQUISITON));
  const onCloseModal = () => dispatch(closeModal());

  const onCreateRequisition = otherStoreName => {
    reduxDispatch(createSupplierRequisition({ otherStoreName, currentUser }));
    onCloseModal();
  };

  const onCreateProgramRequisition = requisitionParameters => {
    reduxDispatch(createSupplierRequisition({ ...requisitionParameters, currentUser }));
    dispatch(closeModal());
  };

  const getAction = (colKey, propName) => {
    switch (colKey) {
      case 'remove':
        if (propName === 'onCheckAction') return selectRow;
        return deselectRow;
      default:
        return null;
    }
  };

  const getModalOnSelect = () => {
    switch (modalKey) {
      case SELECT_SUPPLIER:
        return onCreateRequisition;
      case PROGRAM_REQUISITION:
        return onCreateProgramRequisition;
      default:
        return null;
    }
  };

  const renderRow = useCallback(
    listItem => {
      const { item, index } = listItem;
      const rowKey = recordKeyExtractor(item);
      const { row, alternateRow } = newDataTableStyles;
      return (
        <DataTableRow
          rowData={data[index]}
          rowState={dataState.get(rowKey)}
          rowKey={rowKey}
          style={index % 2 === 0 ? alternateRow : row}
          columns={columns}
          dispatch={dispatch}
          getAction={getAction}
          onPress={onPressRow(item)}
          rowIndex={index}
        />
      );
    },
    [data, dataState]
  );

  const renderHeader = useCallback(
    () => (
      <DataTableHeaderRow
        columns={columns}
        dispatch={debouncedDispatch}
        sortAction={sortData}
        isAscending={isAscending}
        sortBy={sortBy}
      />
    ),
    [sortBy, isAscending]
  );

  const PageButtons = useCallback(() => {
    const { verticalContainer, topButton } = globalStyles;
    return (
      <View style={verticalContainer}>
        <PageButton
          style={topButton}
          text={buttonStrings.new_requisition}
          onPress={onNewRequisition}
        />
      </View>
    );
  }, []);

  const {
    newPageTopSectionContainer,
    newPageTopLeftSectionContainer,
    newPageTopRightSectionContainer,
    searchBar,
  } = newPageStyles;
  return (
    <DataTablePageView>
      <View style={newPageTopSectionContainer}>
        <View style={newPageTopLeftSectionContainer}>
          <SearchBar
            onChangeText={onSearchFiltering}
            style={searchBar}
            color={SUSSOL_ORANGE}
            value={searchTerm}
            placeholder=""
          />
        </View>
        <View style={newPageTopRightSectionContainer}>
          <PageButtons />
        </View>
      </View>
      <DataTable
        data={data}
        extraData={dataState}
        renderRow={renderRow}
        renderHeader={renderHeader}
        keyExtractor={recordKeyExtractor}
        getItemLayout={getItemLayout}
      />
      <BottomConfirmModal
        isOpen={hasSelection}
        questionText={modalStrings.remove_these_items}
        onCancel={onCancelDelete}
        onConfirm={onConfirmDelete}
        confirmText={modalStrings.remove}
      />
      <DataTablePageModal
        fullScreen={false}
        isOpen={!!modalKey}
        modalKey={modalKey}
        onClose={onCloseModal}
        onSelect={getModalOnSelect()}
        dispatch={dispatch}
      />
    </DataTablePageView>
  );
};

SupplierRequisitionsPage.propTypes = {
  routeName: PropTypes.string.isRequired,
  dispatch: PropTypes.func.isRequired,
  currentUser: PropTypes.object.isRequired,
  navigation: PropTypes.object.isRequired,
};

SupplierRequisitionsPage.propTypes = { routeName: PropTypes.string.isRequired };
<|endoftext|>


<commit_before>  var BaseClient = require('./baseclient');
  var RemoteStorage = require('./remotestorage');

  RemoteStorage.prototype.MODULES = {};

  /*
   * Method: RemoteStorage.addModule
   *
   * Method for adding a remoteStorage data module
   *
   * Parameters:
   *   module - module object needs following properies:
	 *       name - Name of the module
	 *       builder    - Builder function defining the module
   *
   * The module builder function should return an object containing another
   * object called exports, which will be exported to this <RemoteStorage>
   * instance under the module's name. So when defining a locations module,
   * like in the example below, it would be accessible via
   * `remoteStorage.locations`, which would in turn have a `features` and a
   * `collections` property.
   *
   * The function receives a private and a public client, which are both
   * instances of <RemoteStorage.BaseClient>. In the following example, the
   * scope of privateClient is `/locations` and the scope of publicClient is
   * `/public/locations`.
   *
   * Example:
   *   (start code)
   *   RemoteStorage.addModule({name: 'locations', builder: function (privateClient, publicClient) {
   *     return {
   *       exports: {
   *         features: privateClient.scope('features/').defaultType('feature'),
   *         collections: privateClient.scope('collections/').defaultType('feature-collection')
   *       }
   *     };
   *   }});
   * (end code)
  */

  RemoteStorage.prototype.addModule = function (module) {
  	let moduleName = module.name;
  	let moduleBuilder = module.builder;
    Object.defineProperty(this, moduleName, {
      configurable: true,
      get: function () {
        var instance = this._loadModule(moduleName, moduleBuilder);
        Object.defineProperty(this, moduleName, {
          value: instance
        });
        return instance;
      }
    });

    if (moduleName.indexOf('-') !== -1) {
      var camelizedName = moduleName.replace(/\-[a-z]/g, function (s) {
        return s[1].toUpperCase();
      });
      Object.defineProperty(this, camelizedName, {
        get: function () {
          return this[moduleName];
        }
      });
    }
  };

  RemoteStorage.prototype._loadModule = function (moduleName, moduleBuilder) {
    if (moduleBuilder) {
      var module = moduleBuilder(new BaseClient(this, '/' + moduleName + '/'),
                           new BaseClient(this, '/public/' + moduleName + '/'));
      return module.exports;
    } else {
      throw "Unknown module: " + moduleName;
    }
  };
<commit_msg>remove unused MODULES rs property<commit_after>  var BaseClient = require('./baseclient');
  var RemoteStorage = require('./remotestorage');

  /*
   * Method: RemoteStorage.addModule
   *
   * Method for adding a remoteStorage data module
   *
   * Parameters:
   *   module - module object needs following properies:
	 *       name - Name of the module
	 *       builder    - Builder function defining the module
   *
   * The module builder function should return an object containing another
   * object called exports, which will be exported to this <RemoteStorage>
   * instance under the module's name. So when defining a locations module,
   * like in the example below, it would be accessible via
   * `remoteStorage.locations`, which would in turn have a `features` and a
   * `collections` property.
   *
   * The function receives a private and a public client, which are both
   * instances of <RemoteStorage.BaseClient>. In the following example, the
   * scope of privateClient is `/locations` and the scope of publicClient is
   * `/public/locations`.
   *
   * Example:
   *   (start code)
   *   RemoteStorage.addModule({name: 'locations', builder: function (privateClient, publicClient) {
   *     return {
   *       exports: {
   *         features: privateClient.scope('features/').defaultType('feature'),
   *         collections: privateClient.scope('collections/').defaultType('feature-collection')
   *       }
   *     };
   *   }});
   * (end code)
  */

  RemoteStorage.prototype.addModule = function (module) {
  	let moduleName = module.name;
  	let moduleBuilder = module.builder;
    Object.defineProperty(this, moduleName, {
      configurable: true,
      get: function () {
        var instance = this._loadModule(moduleName, moduleBuilder);
        Object.defineProperty(this, moduleName, {
          value: instance
        });
        return instance;
      }
    });

    if (moduleName.indexOf('-') !== -1) {
      var camelizedName = moduleName.replace(/\-[a-z]/g, function (s) {
        return s[1].toUpperCase();
      });
      Object.defineProperty(this, camelizedName, {
        get: function () {
          return this[moduleName];
        }
      });
    }
  };

  RemoteStorage.prototype._loadModule = function (moduleName, moduleBuilder) {
    if (moduleBuilder) {
      var module = moduleBuilder(new BaseClient(this, '/' + moduleName + '/'),
                           new BaseClient(this, '/public/' + moduleName + '/'));
      return module.exports;
    } else {
      throw "Unknown module: " + moduleName;
    }
  };
<|endoftext|>


<commit_before>(function ($) {
    var accel = 0;
    var overallslide = 0;
    var sensitivity = 20;
    var slides;
    var currentIndex = 0;
    //Waypoints check position relative to waypoint and decide if to scroll to or not...
    $.fn.initslide = function () {

        //var target = $(this).attr('id');
        //alert(target);
        //$(this)

        // (WIDTH of (this) - WIDTH of slide)/2

        slides = $(this); //Saves the object in a variable

        //alert("children: " + slides.length);
        /*$( this ).children( 'li.target' ).css("border", "3px double red");*/
        //alert(slides.children.length*slides.children('li').css("width").replace("px",""));

        console.log(slides.css("width"));

        slides.css("left", ($("body").css("width").replace("px","")-slides.css("left").replace("px","")-slides.children('li').css("width").replace("px",""))/2);//Centerize sliding area

        console.log(slides.css("left"));

        /*slides.css("width",slides.children.length*slides.children('li').css("width").replace("px","")*2);*/
        $('li:nth-child(' + (currentIndex + 1) + ')').attr('id', 'active');

        var mc = new Hammer(slides.get(0)); //Retrieve DOM Elements to create hammer.js object

        mc.on("panleft panright", function (ev) {
            //            console.log(ev.velocityX +" gesture detected.");
            //overallslide = Number(slides.css("left").replace("px",""))-ev.velocityX * sensitivity;

            slides.css("left", "-="+ev.velocityX * sensitivity);
        });
        mc.on("swipe", function (ev) {
            //            console.log(ev.velocityX +" gesture detected.");
            /*accel -= ev.velocityX/100;
            while(accel>0)
            {
                console.log("YAYY");
                overallslide+=accel;
                slides.css("left",overallslide);
                accel-=0.001;
            }*/

            console.log(ev.velocityX);
            /*var numbervel = ev.velocityX*250;*/
            slides.transition({ x: "-="+ ev.velocityX*250}, 'ease');

            /*slides.animate({
                left: "-="+(ev.velocityX*250)
            }, {
                    duration: 225,
                    easing: 'swing'
                });*/

            //$(this).next();
            //slides.css("left",overallslide);
        },{
            velocity: 0.05
            });
//asdasd
        /*overallslide += accel;
            accel-=0.01;*/

    }

    $.fn.next = function () { //Next slide
        changeActiveSlideTo(currentIndex + 1);
    }

    function changeActiveSlideTo(i) {
        $('li:nth-child(' + (currentIndex + 1) + ')').attr('id', '');
        currentIndex = i;
        console.log("new index: " + currentIndex);
        $('li:nth-child(' + (currentIndex + 1) + ')').attr('id', 'active');
    }

})(jQuery);
<commit_msg>threshold is now 0<commit_after>(function ($) {
    var accel = 0;
    var overallslide = 0;
    var sensitivity = 20;
    var slides;
    var currentIndex = 0;
    //Waypoints check position relative to waypoint and decide if to scroll to or not...
    $.fn.initslide = function () {

        //var target = $(this).attr('id');
        //alert(target);
        //$(this)

        // (WIDTH of (this) - WIDTH of slide)/2

        slides = $(this); //Saves the object in a variable

        //alert("children: " + slides.length);
        /*$( this ).children( 'li.target' ).css("border", "3px double red");*/
        //alert(slides.children.length*slides.children('li').css("width").replace("px",""));

        console.log(slides.css("width"));

        slides.css("left", ($("body").css("width").replace("px","")-slides.css("left").replace("px","")-slides.children('li').css("width").replace("px",""))/2);//Centerize sliding area

        console.log(slides.css("left"));

        /*slides.css("width",slides.children.length*slides.children('li').css("width").replace("px","")*2);*/
        $('li:nth-child(' + (currentIndex + 1) + ')').attr('id', 'active');

        var mc = new Hammer(slides.get(0)); //Retrieve DOM Elements to create hammer.js object

        mc.on("panleft panright", function (ev) {
            //            console.log(ev.velocityX +" gesture detected.");
            //overallslide = Number(slides.css("left").replace("px",""))-ev.velocityX * sensitivity;

            slides.css("left", "-="+ev.velocityX * sensitivity);
        });
        mc.on("swipe", function (ev) {
            //            console.log(ev.velocityX +" gesture detected.");
            /*accel -= ev.velocityX/100;
            while(accel>0)
            {
                console.log("YAYY");
                overallslide+=accel;
                slides.css("left",overallslide);
                accel-=0.001;
            }*/

            console.log(ev.velocityX);
            /*var numbervel = ev.velocityX*250;*/
            slides.transition({ x: "-="+ ev.velocityX*250}, 'ease');

            /*slides.animate({
                left: "-="+(ev.velocityX*250)
            }, {
                    duration: 225,
                    easing: 'swing'
                });*/

            //$(this).next();
            //slides.css("left",overallslide);
        },{
            velocity: 0.05,
            threshold: 0
            });
//asdasd
        /*overallslide += accel;
            accel-=0.01;*/

    }

    $.fn.next = function () { //Next slide
        changeActiveSlideTo(currentIndex + 1);
    }

    function changeActiveSlideTo(i) {
        $('li:nth-child(' + (currentIndex + 1) + ')').attr('id', '');
        currentIndex = i;
        console.log("new index: " + currentIndex);
        $('li:nth-child(' + (currentIndex + 1) + ')').attr('id', 'active');
    }

})(jQuery);
<|endoftext|>


<commit_before>var mouse = {};
var move;

var cskey = "";
var cskeycode = 0;

function getmover(mouse) {
    var mov = null;
    var adist = 1000000;
    var diff;
    for (var i = 0; i < csgeo.free.length; i++) {
        var el = csgeo.free[i];
        if (el.pinned || el.visible === false)
            continue;

        var dx, dy, dist;
        var sc = csport.drawingstate.matrix.sdet;
        if (el.kind === "P") {
            var p = List.normalizeZ(el.homog);
            if (!List._helper.isAlmostReal(p))
                continue;
            dx = p.value[0].value.real - mouse.x;
            dy = p.value[1].value.real - mouse.y;
            dist = Math.sqrt(dx * dx + dy * dy);
            if (el.narrow && dist > (typeof el.narrow === "number" ?
                    el.narrow : 20) / sc)
                continue;
        } else if (el.kind === "C") { //Must be CircleMr
            var mid = csgeo.csnames[el.args[0]];
            var rad = el.radius;
            var xx = CSNumber.div(mid.homog.value[0], mid.homog.value[2]).value.real;
            var yy = CSNumber.div(mid.homog.value[1], mid.homog.value[2]).value.real;
            dx = xx - mouse.x;
            dy = yy - mouse.y;
            var ref = Math.sqrt(dx * dx + dy * dy);
            dist = ref - rad.value.real;
            dx = 0;
            dy = 0;
            if (dist < 0) {
                dist = -dist;
            }
            dist = dist + 30 / sc;

        } else if (el.kind === "L") { //Must be ThroughPoint(Horizontal/Vertical not treated yet)
            var l = el.homog;
            var N = CSNumber;
            var nn = N.add(N.mult(l.value[0], N.conjugate(l.value[0])),
                N.mult(l.value[1], N.conjugate(l.value[1])));
            var ln = List.scaldiv(N.sqrt(nn), l);
            dist = ln.value[0].value.real * mouse.x + ln.value[1].value.real * mouse.y + ln.value[2].value.real;
            dx = -ln.value[0].value.real * dist;
            dy = -ln.value[1].value.real * dist;

            if (dist < 0) {
                dist = -dist;
            }
            dist = dist + 1;
        }

        if (dist < adist + 0.2 / sc) { //A bit a dirty hack, prefers new points
            adist = dist;
            mov = el;
            diff = {
                x: dx,
                y: dy
            };
        }
    }
    console.log("Moving " + (mov ? mov.name : "nothing"));
    if (mov === null)
        return null;
    return {
        mover: mov,
        offset: diff,
        prev: {
            x: mouse.x,
            y: mouse.y
        }
    };
}

function addAutoCleaningEventListener(target, type, listener, useCapture) {
    if (useCapture === undefined)
        useCapture = false;
    shutdownHooks.push(function() {
        target.removeEventListener(type, listener, useCapture);
    });
    target.addEventListener(type, listener, useCapture);
}

function setuplisteners(canvas, data) {

    var MO = null;
    if (typeof MutationObserver !== "undefined")
        MO = MutationObserver;
    if (!MO && typeof WebKitMutationObserver !== "undefined")
        MO = WebKitMutationObserver; // jshint ignore: line
    if (MO) {
        MO = new MO(function(mutations) {
            // Browsers which support MutationObserver likely support contains
            if (!document.body.contains(canvas))
                shutdown();
        });
        MO.observe(document.documentElement, {
            "childList": true,
            "subtree": true
        });
        shutdownHooks.push(function() {
            MO.disconnect();
        });
    } else {
        addAutoCleaningEventListener(canvas, "DOMNodeRemovedFromDocument", shutdown);
        addAutoCleaningEventListener(canvas, "DOMNodeRemoved", shutdown);
    }

    function updatePostition(event) {
        var rect = canvas.getBoundingClientRect();
        var x = event.clientX - rect.left - canvas.clientLeft + 0.5;
        var y = event.clientY - rect.top - canvas.clientTop + 0.5;
        var pos = csport.to(x, y);
        mouse.prevx = mouse.x;
        mouse.prevy = mouse.y;
        mouse.x = pos[0];
        mouse.y = pos[1];
        csmouse[0] = mouse.x;
        csmouse[1] = mouse.y;
        mouse.moved = true;
    }

    if (data.keylistener === true) {
        addAutoCleaningEventListener(document, "keydown", function(e) {
            cs_keypressed(e);
            return false;
        });
    } else if (cscompiled.keydown) {
        canvas.setAttribute("tabindex", "0");
        addAutoCleaningEventListener(canvas, "mousedown", function() {
            canvas.focus();
        });
        addAutoCleaningEventListener(canvas, "keydown", function(e) {
            // console.log("Got key " + e.charCode + " / " + e.keyCode);
            if (e.keyCode !== 9 /* tab */ ) {
                cs_keypressed(e);
                e.preventDefault();
            }
        });
    }

    addAutoCleaningEventListener(canvas, "mousedown", function(e) {
        mouse.button = e.which;
        updatePostition(e);
        cs_mousedown();
        move = getmover(mouse);
        startit(); //starts d3-timer

        mouse.down = true;
        e.preventDefault();
    });

    addAutoCleaningEventListener(canvas, "mouseup", function(e) {
        mouse.down = false;
        cindy_cancelmove();
        stateContinueFromHere();
        cs_mouseup();
        updateCindy();
        e.preventDefault();
    });

    addAutoCleaningEventListener(canvas, "mousemove", function(e) {
        updatePostition(e);
        if (mouse.down) {
            cs_mousedrag();
        } else {
            cs_mousemove();
        }
        e.preventDefault();
    });

    function touchMove(e) {
        updatePostition(e.targetTouches[0]);
        if (mouse.down) {
            cs_mousedrag();
        } else {
            cs_mousemove();
        }
        e.preventDefault();
    }

    function touchDown(e) {
        updatePostition(e.targetTouches[0]);
        cs_mousedown();
        mouse.down = true;
        move = getmover(mouse);
        startit();
        e.preventDefault();
    }

    function touchUp(e) {
        mouse.down = false;
        cindy_cancelmove();
        stateContinueFromHere();
        updateCindy();
        cs_mouseup();
        e.preventDefault();
    }

    addAutoCleaningEventListener(canvas, "touchstart", touchDown, false);
    addAutoCleaningEventListener(canvas, "touchmove", touchMove, true);
    addAutoCleaningEventListener(canvas, "touchend", touchUp, false);
    if (typeof document !== "undefined" && document.body) {
        addAutoCleaningEventListener(document.body, "touchcancel", touchUp, false);
        // addAutoCleaningEventListener(document.body, "mouseup", mouseUp, false);
    }

    updateCindy();
}

var requestAnimFrame;
if (instanceInvocationArguments.isNode) {
    requestAnimFrame = process.nextTick; // jshint ignore:line
} else {
    requestAnimFrame =
        window.requestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.oRequestAnimationFrame ||
        window.msRequestAnimationFrame ||
        function(callback) {
            //                window.setTimeout(callback, 1000 / 60);
            window.setTimeout(callback, 0);
        };
}

function doit() { //Callback for d3-timer
    if (isShutDown) return true;
    if (csanimating) {
        cs_tick();
    }
    if (csanimating || mouse.moved) {
        mouse.moved = false;
        updateCindy();
    }
    csticking = csanimating || mouse.down;
    return !csticking;
}

function startit() {
    if (!csticking) {
        csticking = true;
        d3.timer(doit);
    }
}

function updateCindy() {
    csport.reset();
    csctx.save();
    csctx.clearRect(0, 0, csw, csh);
    if (csgridsize !== 0)
        evaluate(csgridscript);
    traceMouseAndScripts();
    //   console.log("NOW UPDATING");
    //  drawgrid();
    csport.greset();
    draw_traces();
    render();
    csctx.restore();
}

function update() {
    if (isShutDown) return;
    updateCindy();
    if (mouse.down)
        requestAnimFrame(update);
}

function cs_keypressed(e) {
    var evtobj = window.event ? event : e;
    var unicode = evtobj.charCode ? evtobj.charCode : evtobj.keyCode;
    var actualkey = String.fromCharCode(unicode);
    cskey = actualkey;
    cskeycode = unicode;

    evaluate(cscompiled.keydown);
    updateCindy();

}

function cs_mousedown(e) {
    evaluate(cscompiled.mousedown);

}

function cs_mouseup(e) {
    evaluate(cscompiled.mouseup);

}

function cs_mousedrag(e) {
    evaluate(cscompiled.mousedrag);

}

function cs_mousemove(e) {
    evaluate(cscompiled.mousemove);

}

function cs_tick(e) {
    if (csPhysicsInited) { //TODO: Check here if physics is required
        if (typeof(lab) !== 'undefined') {
            lab.tick();
        }
    }
    if (csanimating) {
        evaluate(cscompiled.tick);
    }

}

function cs_simulationstep(e) {
    evaluate(cscompiled.simulationstep);
}

function cs_simulationstart(e) {
    evaluate(cscompiled.simulationstart);
}

function cs_simulationstop(e) {
    evaluate(cscompiled.simulationstop);
}

function cindy_cancelmove() {
    move = undefined;
}
<commit_msg>Keep track of touch event streams<commit_after>var mouse = {};
var move;

var cskey = "";
var cskeycode = 0;

function getmover(mouse) {
    var mov = null;
    var adist = 1000000;
    var diff;
    for (var i = 0; i < csgeo.free.length; i++) {
        var el = csgeo.free[i];
        if (el.pinned || el.visible === false)
            continue;

        var dx, dy, dist;
        var sc = csport.drawingstate.matrix.sdet;
        if (el.kind === "P") {
            var p = List.normalizeZ(el.homog);
            if (!List._helper.isAlmostReal(p))
                continue;
            dx = p.value[0].value.real - mouse.x;
            dy = p.value[1].value.real - mouse.y;
            dist = Math.sqrt(dx * dx + dy * dy);
            if (el.narrow && dist > (typeof el.narrow === "number" ?
                    el.narrow : 20) / sc)
                continue;
        } else if (el.kind === "C") { //Must be CircleMr
            var mid = csgeo.csnames[el.args[0]];
            var rad = el.radius;
            var xx = CSNumber.div(mid.homog.value[0], mid.homog.value[2]).value.real;
            var yy = CSNumber.div(mid.homog.value[1], mid.homog.value[2]).value.real;
            dx = xx - mouse.x;
            dy = yy - mouse.y;
            var ref = Math.sqrt(dx * dx + dy * dy);
            dist = ref - rad.value.real;
            dx = 0;
            dy = 0;
            if (dist < 0) {
                dist = -dist;
            }
            dist = dist + 30 / sc;

        } else if (el.kind === "L") { //Must be ThroughPoint(Horizontal/Vertical not treated yet)
            var l = el.homog;
            var N = CSNumber;
            var nn = N.add(N.mult(l.value[0], N.conjugate(l.value[0])),
                N.mult(l.value[1], N.conjugate(l.value[1])));
            var ln = List.scaldiv(N.sqrt(nn), l);
            dist = ln.value[0].value.real * mouse.x + ln.value[1].value.real * mouse.y + ln.value[2].value.real;
            dx = -ln.value[0].value.real * dist;
            dy = -ln.value[1].value.real * dist;

            if (dist < 0) {
                dist = -dist;
            }
            dist = dist + 1;
        }

        if (dist < adist + 0.2 / sc) { //A bit a dirty hack, prefers new points
            adist = dist;
            mov = el;
            diff = {
                x: dx,
                y: dy
            };
        }
    }
    console.log("Moving " + (mov ? mov.name : "nothing"));
    if (mov === null)
        return null;
    return {
        mover: mov,
        offset: diff,
        prev: {
            x: mouse.x,
            y: mouse.y
        }
    };
}

function addAutoCleaningEventListener(target, type, listener, useCapture) {
    if (useCapture === undefined)
        useCapture = false;
    shutdownHooks.push(function() {
        target.removeEventListener(type, listener, useCapture);
    });
    target.addEventListener(type, listener, useCapture);
}

function setuplisteners(canvas, data) {

    var MO = null;
    if (typeof MutationObserver !== "undefined")
        MO = MutationObserver;
    if (!MO && typeof WebKitMutationObserver !== "undefined")
        MO = WebKitMutationObserver; // jshint ignore: line
    if (MO) {
        MO = new MO(function(mutations) {
            // Browsers which support MutationObserver likely support contains
            if (!document.body.contains(canvas))
                shutdown();
        });
        MO.observe(document.documentElement, {
            "childList": true,
            "subtree": true
        });
        shutdownHooks.push(function() {
            MO.disconnect();
        });
    } else {
        addAutoCleaningEventListener(canvas, "DOMNodeRemovedFromDocument", shutdown);
        addAutoCleaningEventListener(canvas, "DOMNodeRemoved", shutdown);
    }

    function updatePostition(event) {
        var rect = canvas.getBoundingClientRect();
        var x = event.clientX - rect.left - canvas.clientLeft + 0.5;
        var y = event.clientY - rect.top - canvas.clientTop + 0.5;
        var pos = csport.to(x, y);
        mouse.prevx = mouse.x;
        mouse.prevy = mouse.y;
        mouse.x = pos[0];
        mouse.y = pos[1];
        csmouse[0] = mouse.x;
        csmouse[1] = mouse.y;
        mouse.moved = true;
    }

    if (data.keylistener === true) {
        addAutoCleaningEventListener(document, "keydown", function(e) {
            cs_keypressed(e);
            return false;
        });
    } else if (cscompiled.keydown) {
        canvas.setAttribute("tabindex", "0");
        addAutoCleaningEventListener(canvas, "mousedown", function() {
            canvas.focus();
        });
        addAutoCleaningEventListener(canvas, "keydown", function(e) {
            // console.log("Got key " + e.charCode + " / " + e.keyCode);
            if (e.keyCode !== 9 /* tab */ ) {
                cs_keypressed(e);
                e.preventDefault();
            }
        });
    }

    addAutoCleaningEventListener(canvas, "mousedown", function(e) {
        mouse.button = e.which;
        updatePostition(e);
        cs_mousedown();
        move = getmover(mouse);
        startit(); //starts d3-timer

        mouse.down = true;
        e.preventDefault();
    });

    addAutoCleaningEventListener(canvas, "mouseup", function(e) {
        mouse.down = false;
        cindy_cancelmove();
        stateContinueFromHere();
        cs_mouseup();
        updateCindy();
        e.preventDefault();
    });

    addAutoCleaningEventListener(canvas, "mousemove", function(e) {
        updatePostition(e);
        if (mouse.down) {
            cs_mousedrag();
        } else {
            cs_mousemove();
        }
        e.preventDefault();
    });

    function touchMove(e) {

        var activeTouchIDList = e.changedTouches;
        var gotit = false;
        for (var i = 0; i < activeTouchIDList.length; i++) {
            if (activeTouchIDList[i].identifier === activeTouchID) {
                gotit = true;
            }
        }
        if (!gotit) {
            return;
        }

        updatePostition(e.targetTouches[0]);
        if (mouse.down) {
            cs_mousedrag();
        } else {
            cs_mousemove();
        }
        e.preventDefault();
    }
    var activeTouchID = -1;

    function touchDown(e) {
        if (activeTouchID !== -1) {
            return;
        }

        var activeTouchIDList = e.changedTouches;
        if (activeTouchIDList.length === 0) {
            return;
        }
        activeTouchID = activeTouchIDList[0].identifier;

        updatePostition(e.targetTouches[0]);
        cs_mousedown();
        mouse.down = true;
        move = getmover(mouse);
        startit();
        e.preventDefault();
    }

    function touchUp(e) {
        var activeTouchIDList = e.changedTouches;
        var gotit = false;
        for (var i = 0; i < activeTouchIDList.length; i++) {
            if (activeTouchIDList[i].identifier === activeTouchID) {
                gotit = true;
            }
        }

        if (!gotit) {
            return;
        }
        activeTouchID = -1;
        mouse.down = false;
        cindy_cancelmove();
        stateContinueFromHere();
        updateCindy();
        cs_mouseup();
        e.preventDefault();
    }

    addAutoCleaningEventListener(canvas, "touchstart", touchDown, false);
    addAutoCleaningEventListener(canvas, "touchmove", touchMove, true);
    addAutoCleaningEventListener(canvas, "touchend", touchUp, false);
    if (typeof document !== "undefined" && document.body) {
        addAutoCleaningEventListener(document.body, "touchcancel", touchUp, false);
        // addAutoCleaningEventListener(document.body, "mouseup", mouseUp, false);
    }

    updateCindy();
}

var requestAnimFrame;
if (instanceInvocationArguments.isNode) {
    requestAnimFrame = process.nextTick; // jshint ignore:line
} else {
    requestAnimFrame =
        window.requestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.oRequestAnimationFrame ||
        window.msRequestAnimationFrame ||
        function(callback) {
            //                window.setTimeout(callback, 1000 / 60);
            window.setTimeout(callback, 0);
        };
}

function doit() { //Callback for d3-timer
    if (isShutDown) return true;
    if (csanimating) {
        cs_tick();
    }
    if (csanimating || mouse.moved) {
        mouse.moved = false;
        updateCindy();
    }
    csticking = csanimating || mouse.down;
    return !csticking;
}

function startit() {
    if (!csticking) {
        csticking = true;
        d3.timer(doit);
    }
}

function updateCindy() {
    csport.reset();
    csctx.save();
    csctx.clearRect(0, 0, csw, csh);
    if (csgridsize !== 0)
        evaluate(csgridscript);
    traceMouseAndScripts();
    //   console.log("NOW UPDATING");
    //  drawgrid();
    csport.greset();
    draw_traces();
    render();
    csctx.restore();
}

function update() {
    if (isShutDown) return;
    updateCindy();
    if (mouse.down)
        requestAnimFrame(update);
}

function cs_keypressed(e) {
    var evtobj = window.event ? event : e;
    var unicode = evtobj.charCode ? evtobj.charCode : evtobj.keyCode;
    var actualkey = String.fromCharCode(unicode);
    cskey = actualkey;
    cskeycode = unicode;

    evaluate(cscompiled.keydown);
    updateCindy();

}

function cs_mousedown(e) {
    evaluate(cscompiled.mousedown);

}

function cs_mouseup(e) {
    evaluate(cscompiled.mouseup);

}

function cs_mousedrag(e) {
    evaluate(cscompiled.mousedrag);

}

function cs_mousemove(e) {
    evaluate(cscompiled.mousemove);

}

function cs_tick(e) {
    if (csPhysicsInited) { //TODO: Check here if physics is required
        if (typeof(lab) !== 'undefined') {
            lab.tick();
        }
    }
    if (csanimating) {
        evaluate(cscompiled.tick);
    }

}

function cs_simulationstep(e) {
    evaluate(cscompiled.simulationstep);
}

function cs_simulationstart(e) {
    evaluate(cscompiled.simulationstart);
}

function cs_simulationstop(e) {
    evaluate(cscompiled.simulationstop);
}

function cindy_cancelmove() {
    move = undefined;
}
<|endoftext|>