<commit_before>//to access filesystem
const fs = require('fs');
//to format filepath
const path = require('path');

var commonUtil = require("./commonUtil");
var commonSearchUtil = require("./commonSearchUtil");

/*
//check the position of space(' ' or '　').
//return array of positions.

function checkPositionOfSpaces(targetString){
	var positionOfSpaces = [];
	for(var index = 0 , len = targetString.length ; index < len; index++){
		if((targetString[index] == ' ') || (targetString[index] == '　' )){
			positionOfSpaces.push(index);
		}
	}
	return positionOfSpaces;
}

//search available path (considering unnecessary space)
function searchAvailablePathConsideringUnnecessarySpaceWithDistance(
	availableCurrentPath, splittedRemainingPath,
	availableFullPathes, alreadyCheckedPathes,
	currentDistance
){
	//check if splittedRemainingPath is empty
	if(splittedRemainingPath.length == 0){
		//check if availableCurrentPath is empty
		if(availableCurrentPath == ""){
			//exit
			return;
		}else{
			var status = commonSearchUtil.checkFileOrFolder(availableCurrentPath);
			//check if availableCurrentPath is already added to availableFullPathes
			if(availableFullPathes.indexOf([availableCurrentPath , currentDistance , status]) == -1){
				//add availableCurrentPath to availableFullPathes
				availableFullPathes.push([availableCurrentPath , currentDistance , status]);
			}
		}
	}else{
		//store path to check next.
		var checkingPath = splittedRemainingPath[0];
		//check if checkingPath contain space
		var positionOfSpaces = checkPositionOfSpaces(checkingPath);
		if(positionOfSpaces.length > 0){
			for(var index = 0 , len = positionOfSpaces.length ; index < len ; index++){
				//remove one of the spaces from checkingPath.
				var checkingPathRemovedSpace =
					commonUtil.removeCharacterWithPosition(checkingPath, positionOfSpaces[index]);
				//copy splittedRemainingPath
				var nextSplittedRemainingPath = splittedRemainingPath.slice();
				//check if checkingPathRemovedSpace is empty
				if(checkingPathRemovedSpace == ""){
					nextSplittedRemainingPath.shift();
				}else{
					nextSplittedRemainingPath[0] = checkingPathRemovedSpace;
				}
				//increment distance
				var nextDistance = currentDistance + 1;
				var nextAvailableCurrentPath = availableCurrentPath;

				//set the full path to check
				var checkingFullPath = "";
				if(availableCurrentPath == ""){
					checkingFullPath = checkingPath;
				}else{
					checkingFullPath = availableCurrentPath + "\\" + checkingPath;
				}
				//check the path if it is checked at first
				var alreadyCheckedFlag = false;
				if(alreadyCheckedPathes.indexOf(checkingFullPath) >= 0){
					alreadyCheckedFlag = true;
				}

				if(alreadyCheckedFlag == true){
					//exit
					return;
				}else{
					//when the path is checked at first
					//call this function recursively
					searchAvailablePathConsideringUnnecessarySpaceWithDistance(
						nextAvailableCurrentPath , nextSplittedRemainingPath ,
						availableFullPathes, alreadyCheckedPathes , nextDistance
					);
				}
			}
		}
		//set the full path to check
		var checkingFullPath = "";
		if(availableCurrentPath == ""){
			checkingFullPath = checkingPath;
		}else{
			checkingFullPath = availableCurrentPath + "\\" + checkingPath;
		}
		//check the path if it is checked at first
		var alreadyCheckedFlag = false;
		if(alreadyCheckedPathes.indexOf(checkingFullPath) >= 0){
			alreadyCheckedFlag = true;
		}

		if(alreadyCheckedFlag == true){
			//exit
			return;
		}else{
			//when the path is checked at first
			//add to pathes which are already checked
			alreadyCheckedPathes.push(checkingFullPath);
			//check the existence of the full path
			switch(commonSearchUtil.checkFileOrFolder(checkingFullPath)){
				case -1:
				case 0:
					//do nothing
					return;
					break;
				case 1://Available File
				case 2://Available Folder
				case 3://Available File Server
					//copy splittedRemainingPath
					var shiftedSplittedRemainingPath = splittedRemainingPath.slice();
					shiftedSplittedRemainingPath.shift();
					var nextAvailableCurrentPath = checkingFullPath;
					var nextDistance = currentDistance;
					//call this function recursively
					searchAvailablePathConsideringUnnecessarySpaceWithDistance(
						nextAvailableCurrentPath , shiftedSplittedRemainingPath ,
						availableFullPathes, alreadyCheckedPathes , nextDistance
					);
					break;
				default:
					//do nothing
					return;
					break;
			}
		}
	}
}

function searchAvailablePathConsideringUnnecessarySpace(targetPath){
	var splittedPath = commonSearchUtil.separatePath(targetPath,true);
	var availableFullPathes = [];
	searchAvailablePathConsideringUnnecessarySpaceWithDistance(
		"", splittedPath, availableFullPathes,	[],	0
	);
	return availableFullPathes;
}

//search available path
function searchAvailablePath(query) {
	//search available file/folder name. (considering unnecessary space)
	var availableFullPathes =
	searchAvailablePathConsideringUnnecessarySpace(query);

	//sort available path candidates by distance between them and query.
	var sortedAvailableFullPathes = availableFullPathes.slice();
	if(sortedAvailableFullPathes.length >= 2){
		sortedAvailableFullPathes.sort(
			function(a,b){
				//compare the distance
				return ( (a[1] - b[1]) );
			}
		);
	}
	return sortedAvailableFullPathes;
}
*/

// param : "A A A\B BB\C  C"
// return ["AAA\BBB\CC","AAA\B BB\C C"] <- available pathes removed unnecessary spaces
exports.searchAvailablePathAsync = function(path, res){
	//inner function
	// param : "A A A\B BB\C  C"
	// return: [["A A A","AA A","A AA","AAA"],
	//          ["B BB","BBB"],
	//          ["C  C","C C","CC"]]
	function listupAllLayerSearchCandidates(path){
		// param : "A A A"
		// return: ["A A A","AA A","A AA","AAA"]
		function listupSearchCandidates(string){
			//inner function for recursive search
			// param : [], "A A A"
			// return:
			// process: [] <- ["A A A","AA A","A AA","AAA"]
			function innerListUp(list, string){
				// param : "a bc  "
				// return: [1,4,5]
				function checkPositionOfSpaces(targetString){
					var positionOfSpaces = [];
					for(var index = 0 , len = targetString.length ; index < len; index++){
						if((targetString[index] == ' ') || (targetString[index] == '　' )){
							positionOfSpaces.push(index);
						}
					}
					return positionOfSpaces;
				}

				//main process
				(function(list, string){
					if(list.indexOf(string) < 0){
						list.push(string);
					}
					var positionOfSpaces = checkPositionOfSpaces(string);
					for(var index = 0, len = positionOfSpaces.length; index < len; index++){
						var stringRemovedOneSpace = commonUtil.removeCharacterWithPosition(string, positionOfSpaces[index]);
						innerListUp(list, stringRemovedOneSpace);
					}
				})(list, string);
			}

			//main process
			return (function(string){
				var list = [];
				innerListUp(list, string);
				return list;
			})(string);
		}

		//main process
		return (function(path){
			var listAllLayer = [];
			var separatedPath = commonSearchUtil.separatePath(path, true);
			for(var index = 0, len = separatedPath.length; index < len; index++){
				listAllLayer.push(listupSearchCandidates(separatedPath[index]));
			}
			return listAllLayer;
		})(path);
	}

	//import fs library
	const fs = require('fs');

	//inner function for recursive search
	//add to res in this function
	function innerSearch(foundPathes, currentPath, listRemainingLayer, res){
		//add to res
		function innerAddOpenCommand(targetPath, res){
			//add to res.
			//Check state of formatted path (File or Folder or not).
			//and set Description Message according to the state.
			var descriptionMessage = "";
			//var availableFullPath = sortedAvailableFullPathes[index][0].slice();
			//var distance = sortedAvailableFullPathes[index][1];
			//var status = sortedAvailableFullPathes[index][2];

			var availableFullPath = targetPath;
			var distance = "X";
			var status = 1;

			var addToResFlag = false;
			var innerIcon = "";
			var group = "";
			switch(status){
				case -1://invalid
				case 0://invalid
					//descriptionMessage = "Not File/Folder. Cannot open."
					break;
				case 1://file
					//extract file name
					//(todo) extract BBB from "C:\AAA\BBB\" <- when unnecessary "\" exists.
					var filename = availableFullPath.slice().split(path.sep).pop();
					descriptionMessage = "Open this File : \"" + filename +
						"\" ( Distance = " + distance + " )";
					innerIcon = "#fa fa-file-o";
					addToResFlag = true;
					group = "Available Pathes : File";
					break;
				case 2://folder
					//extract folder name
					//(todo) extract BBB from "C:\AAA\BBB\" <- when unnecessary "\" exists.
					var foldername = availableFullPath.slice().split(path.sep).pop();
					descriptionMessage = "Open this Folder : \"" + foldername +
						"\" ( Distance = " + distance + " )";
					innerIcon = "#fa fa-folder-open-o";
					addToResFlag = true;
					group = "Available Pathes : Folder";
					break;
				case 3://file server
					//extract folder name
					var foldername = availableFullPath.slice().split(path.sep).pop();
					descriptionMessage = "Open this File Server : \"" + foldername +
						"\" ( Distance = " + distance + " )";
					//innerIcon = "#fa fa-folder-open-o";
					innerIcon = "#fa fa-server";
					addToResFlag = true;
					group = "Available Pathes : File Server";
					break;
				default:
					break;
			}
			//add to res.
			if(addToResFlag == true){
				res.add(
					{
						id: availableFullPath,
						payload: 'open',
						title: availableFullPath,
						icon: innerIcon,
						desc: descriptionMessage,
						redirect: commonUtil.commandHeader + " " + availableFullPath,
						group: group
					}
				);
			}
		}

		//main process
		(function(foundPathes, currentPath, listRemainingLayer, res){
			var len = listRemainingLayer.length;
			if(len > 0){
				var list = listRemainingLayer[0];
				let copy = listRemainingLayer.slice();
				copy.shift();
				for(var index = 0, len2 = list.length; index < len2; index++){
					let target = "";
					if(currentPath != ""){
						target = target + currentPath + "\\";
					}
					target = target + list[index];
					fs.stat(target, function(err, stats){
						if(err){
							//console.log("err");
						}else if(stats.isFile() || stats.isDirectory()){
							innerSearch(foundPathes, target ,copy, res);
						}else{
							//console.log("else");
						}
					});
				}
			}else{
				if(foundPathes.indexOf(currentPath) < 0){
					foundPathes.push(currentPath);
					//callback(currentPath, res);
					innerAddOpenCommand(currentPath, res);
				}
			}
		})(foundPathes, currentPath, listRemainingLayer, res);
	}

	//main process
	return (function(path, res){
		if(path.length > 0){
			var foundPathes = [];
			var listAllLayer = listupAllLayerSearchCandidates(path);
			innerSearch(foundPathes, "", listAllLayer, res);
			return foundPathes;
		}
	})(path, res);
}

/*
//add open command
exports.addOpenCommand = function(targetPath, res){
//function addOpenCommand(targetPath, res){
	//search available path
	var sortedAvailableFullPathes = searchAvailablePath(targetPath)

	//add to res.
	for(var index = 0 , len = sortedAvailableFullPathes.length ; index < len ; index++){
		//Check state of formatted path (File or Folder or not).
		//and set Description Message according to the state.
		var descriptionMessage = "";
		var availableFullPath = sortedAvailableFullPathes[index][0].slice();
		var distance = sortedAvailableFullPathes[index][1];
		var status = sortedAvailableFullPathes[index][2];
		var addToResFlag = false;
		var innerIcon = "";
		var sortKey = {
			category: 1,//open command
			status: status,
			name: ""
		}
		//switch(checkFileOrFolder(availableFullPath)){
		switch(status){
			case -1://invalid
			case 0://invalid
				//descriptionMessage = "Not File/Folder. Cannot open."
				break;
			case 1://file
				//extract file name
				//(todo) extract BBB from "C:\AAA\BBB\" <- when unnecessary "\" exists.
				var filename = availableFullPath.slice().split(path.sep).pop();
				descriptionMessage = "Open this File : \"" + filename +
					"\" ( Distance = " + distance + " )";
				innerIcon = "#fa fa-file-o";
				addToResFlag = true;
				sortKey.name = filename;
				break;
			case 2://folder
				//extract folder name
				//(todo) extract BBB from "C:\AAA\BBB\" <- when unnecessary "\" exists.
				var foldername = availableFullPath.slice().split(path.sep).pop();
				descriptionMessage = "Open this Folder : \"" + foldername +
					"\" ( Distance = " + distance + " )";
				innerIcon = "#fa fa-folder-open-o";
				addToResFlag = true;
				sortKey.name = foldername;
				break;
			case 3://file server
				//extract folder name
				var foldername = availableFullPath.slice().split(path.sep).pop();
				descriptionMessage = "Open this File Server : \"" + foldername +
					"\" ( Distance = " + distance + " )";
				//innerIcon = "#fa fa-folder-open-o";
				innerIcon = "#fa fa-server";
				addToResFlag = true;
				sortKey.name = foldername;
				break;
			default:
				break;
		}
		//add to res.
		if(addToResFlag == true){
			res.add(
				{
					id: availableFullPath,
					payload: 'open',
					title: availableFullPath,
					icon: innerIcon,
					desc: descriptionMessage,
					redirect: commonUtil.commandHeader + " " + availableFullPath,
					sortKey: sortKey,
					group: "Available Pathes"
 				}
			);
		}
	}
}
*/<commit_msg>modify redirection<commit_after>//to access filesystem
const fs = require('fs');
//to format filepath
const path = require('path');

var commonUtil = require("./commonUtil");
var commonSearchUtil = require("./commonSearchUtil");

/*
//check the position of space(' ' or '　').
//return array of positions.

function checkPositionOfSpaces(targetString){
	var positionOfSpaces = [];
	for(var index = 0 , len = targetString.length ; index < len; index++){
		if((targetString[index] == ' ') || (targetString[index] == '　' )){
			positionOfSpaces.push(index);
		}
	}
	return positionOfSpaces;
}

//search available path (considering unnecessary space)
function searchAvailablePathConsideringUnnecessarySpaceWithDistance(
	availableCurrentPath, splittedRemainingPath,
	availableFullPathes, alreadyCheckedPathes,
	currentDistance
){
	//check if splittedRemainingPath is empty
	if(splittedRemainingPath.length == 0){
		//check if availableCurrentPath is empty
		if(availableCurrentPath == ""){
			//exit
			return;
		}else{
			var status = commonSearchUtil.checkFileOrFolder(availableCurrentPath);
			//check if availableCurrentPath is already added to availableFullPathes
			if(availableFullPathes.indexOf([availableCurrentPath , currentDistance , status]) == -1){
				//add availableCurrentPath to availableFullPathes
				availableFullPathes.push([availableCurrentPath , currentDistance , status]);
			}
		}
	}else{
		//store path to check next.
		var checkingPath = splittedRemainingPath[0];
		//check if checkingPath contain space
		var positionOfSpaces = checkPositionOfSpaces(checkingPath);
		if(positionOfSpaces.length > 0){
			for(var index = 0 , len = positionOfSpaces.length ; index < len ; index++){
				//remove one of the spaces from checkingPath.
				var checkingPathRemovedSpace =
					commonUtil.removeCharacterWithPosition(checkingPath, positionOfSpaces[index]);
				//copy splittedRemainingPath
				var nextSplittedRemainingPath = splittedRemainingPath.slice();
				//check if checkingPathRemovedSpace is empty
				if(checkingPathRemovedSpace == ""){
					nextSplittedRemainingPath.shift();
				}else{
					nextSplittedRemainingPath[0] = checkingPathRemovedSpace;
				}
				//increment distance
				var nextDistance = currentDistance + 1;
				var nextAvailableCurrentPath = availableCurrentPath;

				//set the full path to check
				var checkingFullPath = "";
				if(availableCurrentPath == ""){
					checkingFullPath = checkingPath;
				}else{
					checkingFullPath = availableCurrentPath + "\\" + checkingPath;
				}
				//check the path if it is checked at first
				var alreadyCheckedFlag = false;
				if(alreadyCheckedPathes.indexOf(checkingFullPath) >= 0){
					alreadyCheckedFlag = true;
				}

				if(alreadyCheckedFlag == true){
					//exit
					return;
				}else{
					//when the path is checked at first
					//call this function recursively
					searchAvailablePathConsideringUnnecessarySpaceWithDistance(
						nextAvailableCurrentPath , nextSplittedRemainingPath ,
						availableFullPathes, alreadyCheckedPathes , nextDistance
					);
				}
			}
		}
		//set the full path to check
		var checkingFullPath = "";
		if(availableCurrentPath == ""){
			checkingFullPath = checkingPath;
		}else{
			checkingFullPath = availableCurrentPath + "\\" + checkingPath;
		}
		//check the path if it is checked at first
		var alreadyCheckedFlag = false;
		if(alreadyCheckedPathes.indexOf(checkingFullPath) >= 0){
			alreadyCheckedFlag = true;
		}

		if(alreadyCheckedFlag == true){
			//exit
			return;
		}else{
			//when the path is checked at first
			//add to pathes which are already checked
			alreadyCheckedPathes.push(checkingFullPath);
			//check the existence of the full path
			switch(commonSearchUtil.checkFileOrFolder(checkingFullPath)){
				case -1:
				case 0:
					//do nothing
					return;
					break;
				case 1://Available File
				case 2://Available Folder
				case 3://Available File Server
					//copy splittedRemainingPath
					var shiftedSplittedRemainingPath = splittedRemainingPath.slice();
					shiftedSplittedRemainingPath.shift();
					var nextAvailableCurrentPath = checkingFullPath;
					var nextDistance = currentDistance;
					//call this function recursively
					searchAvailablePathConsideringUnnecessarySpaceWithDistance(
						nextAvailableCurrentPath , shiftedSplittedRemainingPath ,
						availableFullPathes, alreadyCheckedPathes , nextDistance
					);
					break;
				default:
					//do nothing
					return;
					break;
			}
		}
	}
}

function searchAvailablePathConsideringUnnecessarySpace(targetPath){
	var splittedPath = commonSearchUtil.separatePath(targetPath,true);
	var availableFullPathes = [];
	searchAvailablePathConsideringUnnecessarySpaceWithDistance(
		"", splittedPath, availableFullPathes,	[],	0
	);
	return availableFullPathes;
}

//search available path
function searchAvailablePath(query) {
	//search available file/folder name. (considering unnecessary space)
	var availableFullPathes =
	searchAvailablePathConsideringUnnecessarySpace(query);

	//sort available path candidates by distance between them and query.
	var sortedAvailableFullPathes = availableFullPathes.slice();
	if(sortedAvailableFullPathes.length >= 2){
		sortedAvailableFullPathes.sort(
			function(a,b){
				//compare the distance
				return ( (a[1] - b[1]) );
			}
		);
	}
	return sortedAvailableFullPathes;
}
*/

// param : "A A A\B BB\C  C"
// return ["AAA\BBB\CC","AAA\B BB\C C"] <- available pathes removed unnecessary spaces
exports.searchAvailablePathAsync = function(path, res){
	//inner function
	// param : "A A A\B BB\C  C"
	// return: [["A A A","AA A","A AA","AAA"],
	//          ["B BB","BBB"],
	//          ["C  C","C C","CC"]]
	function listupAllLayerSearchCandidates(path){
		// param : "A A A"
		// return: ["A A A","AA A","A AA","AAA"]
		function listupSearchCandidates(string){
			//inner function for recursive search
			// param : [], "A A A"
			// return:
			// process: [] <- ["A A A","AA A","A AA","AAA"]
			function innerListUp(list, string){
				// param : "a bc  "
				// return: [1,4,5]
				function checkPositionOfSpaces(targetString){
					var positionOfSpaces = [];
					for(var index = 0 , len = targetString.length ; index < len; index++){
						if((targetString[index] == ' ') || (targetString[index] == '　' )){
							positionOfSpaces.push(index);
						}
					}
					return positionOfSpaces;
				}

				//main process
				(function(list, string){
					if(list.indexOf(string) < 0){
						list.push(string);
					}
					var positionOfSpaces = checkPositionOfSpaces(string);
					for(var index = 0, len = positionOfSpaces.length; index < len; index++){
						var stringRemovedOneSpace = commonUtil.removeCharacterWithPosition(string, positionOfSpaces[index]);
						innerListUp(list, stringRemovedOneSpace);
					}
				})(list, string);
			}

			//main process
			return (function(string){
				var list = [];
				innerListUp(list, string);
				return list;
			})(string);
		}

		//main process
		return (function(path){
			var listAllLayer = [];
			var separatedPath = commonSearchUtil.separatePath(path, true);
			for(var index = 0, len = separatedPath.length; index < len; index++){
				listAllLayer.push(listupSearchCandidates(separatedPath[index]));
			}
			return listAllLayer;
		})(path);
	}

	//import fs library
	const fs = require('fs');

	//inner function for recursive search
	//add to res in this function
	function innerSearch(foundPathes, currentPath, listRemainingLayer, res){
		//add to res
		function innerAddOpenCommand(targetPath, res){
			//add to res.
			//Check state of formatted path (File or Folder or not).
			//and set Description Message according to the state.
			var descriptionMessage = "";
			//var availableFullPath = sortedAvailableFullPathes[index][0].slice();
			//var distance = sortedAvailableFullPathes[index][1];
			//var status = sortedAvailableFullPathes[index][2];

			var availableFullPath = targetPath;
			var distance = "X";
			var status = 1;

			var addToResFlag = false;
			var innerIcon = "";
			var innerGroup = "";
			var innerRedirect = "";
			switch(status){
				case -1://invalid
				case 0://invalid
					//descriptionMessage = "Not File/Folder. Cannot open."
					break;
				case 1://file
					//extract file name
					//(todo) extract BBB from "C:\AAA\BBB\" <- when unnecessary "\" exists.
					var filename = availableFullPath.slice().split(path.sep).pop();
					descriptionMessage = "Open this File : \"" + filename +
						"\" ( Distance = " + distance + " )";
					innerIcon = "#fa fa-file-o";
					addToResFlag = true;
					innerGroup = "Available Pathes : File";
					innerRedirect = commonUtil.commandHeader + " " + availableFullPath;
					break;
				case 2://folder
					//extract folder name
					//(todo) extract BBB from "C:\AAA\BBB\" <- when unnecessary "\" exists.
					var foldername = availableFullPath.slice().split(path.sep).pop();
					descriptionMessage = "Open this Folder : \"" + foldername +
						"\" ( Distance = " + distance + " )";
					innerIcon = "#fa fa-folder-open-o";
					addToResFlag = true;
					innerGroup = "Available Pathes : Folder";
					innerRedirect = commonUtil.commandHeader + " " + availableFullPath + "\\";
					break;
				case 3://file server
					//extract folder name
					var foldername = availableFullPath.slice().split(path.sep).pop();
					descriptionMessage = "Open this File Server : \"" + foldername +
						"\" ( Distance = " + distance + " )";
					//innerIcon = "#fa fa-folder-open-o";
					innerIcon = "#fa fa-server";
					addToResFlag = true;
					innerGroup = "Available Pathes : File Server";
					innerRedirect = commonUtil.commandHeader + " " + availableFullPath + "\\";
					break;
				default:
					break;
			}
			//add to res.
			if(addToResFlag == true){
				res.add(
					{
						id: availableFullPath,
						payload: 'open',
						title: availableFullPath,
						icon: innerIcon,
						desc: descriptionMessage,
						redirect: innerRedirect,
						group: innerGroup
					}
				);
			}
		}

		//main process
		(function(foundPathes, currentPath, listRemainingLayer, res){
			var len = listRemainingLayer.length;
			if(len > 0){
				var list = listRemainingLayer[0];
				let copy = listRemainingLayer.slice();
				copy.shift();
				for(var index = 0, len2 = list.length; index < len2; index++){
					let target = "";
					if(currentPath != ""){
						target = target + currentPath + "\\";
					}
					target = target + list[index];
					fs.stat(target, function(err, stats){
						if(err){
							//console.log("err");
						}else if(stats.isFile() || stats.isDirectory()){
							innerSearch(foundPathes, target ,copy, res);
						}else{
							//console.log("else");
						}
					});
				}
			}else{
				if(foundPathes.indexOf(currentPath) < 0){
					foundPathes.push(currentPath);
					//callback(currentPath, res);
					innerAddOpenCommand(currentPath, res);
				}
			}
		})(foundPathes, currentPath, listRemainingLayer, res);
	}

	//main process
	return (function(path, res){
		if(path.length > 0){
			var foundPathes = [];
			var listAllLayer = listupAllLayerSearchCandidates(path);
			innerSearch(foundPathes, "", listAllLayer, res);
			return foundPathes;
		}
	})(path, res);
}

/*
//add open command
exports.addOpenCommand = function(targetPath, res){
//function addOpenCommand(targetPath, res){
	//search available path
	var sortedAvailableFullPathes = searchAvailablePath(targetPath)

	//add to res.
	for(var index = 0 , len = sortedAvailableFullPathes.length ; index < len ; index++){
		//Check state of formatted path (File or Folder or not).
		//and set Description Message according to the state.
		var descriptionMessage = "";
		var availableFullPath = sortedAvailableFullPathes[index][0].slice();
		var distance = sortedAvailableFullPathes[index][1];
		var status = sortedAvailableFullPathes[index][2];
		var addToResFlag = false;
		var innerIcon = "";
		var sortKey = {
			category: 1,//open command
			status: status,
			name: ""
		}
		//switch(checkFileOrFolder(availableFullPath)){
		switch(status){
			case -1://invalid
			case 0://invalid
				//descriptionMessage = "Not File/Folder. Cannot open."
				break;
			case 1://file
				//extract file name
				//(todo) extract BBB from "C:\AAA\BBB\" <- when unnecessary "\" exists.
				var filename = availableFullPath.slice().split(path.sep).pop();
				descriptionMessage = "Open this File : \"" + filename +
					"\" ( Distance = " + distance + " )";
				innerIcon = "#fa fa-file-o";
				addToResFlag = true;
				sortKey.name = filename;
				break;
			case 2://folder
				//extract folder name
				//(todo) extract BBB from "C:\AAA\BBB\" <- when unnecessary "\" exists.
				var foldername = availableFullPath.slice().split(path.sep).pop();
				descriptionMessage = "Open this Folder : \"" + foldername +
					"\" ( Distance = " + distance + " )";
				innerIcon = "#fa fa-folder-open-o";
				addToResFlag = true;
				sortKey.name = foldername;
				break;
			case 3://file server
				//extract folder name
				var foldername = availableFullPath.slice().split(path.sep).pop();
				descriptionMessage = "Open this File Server : \"" + foldername +
					"\" ( Distance = " + distance + " )";
				//innerIcon = "#fa fa-folder-open-o";
				innerIcon = "#fa fa-server";
				addToResFlag = true;
				sortKey.name = foldername;
				break;
			default:
				break;
		}
		//add to res.
		if(addToResFlag == true){
			res.add(
				{
					id: availableFullPath,
					payload: 'open',
					title: availableFullPath,
					icon: innerIcon,
					desc: descriptionMessage,
					redirect: commonUtil.commandHeader + " " + availableFullPath,
					sortKey: sortKey,
					group: "Available Pathes"
 				}
			);
		}
	}
}
*/<|endoftext|>


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

$(document).ready(function() {
	// elements
	var fileList = $('.file-list');
	var editorArea = $('.editor-area');
	var editorResult = $('.editor-result');
	var saveBtn = $('#save-btn');
	var ajaxResult = $('.ajax-result');
	var createFile = $('.create-file');
	var createFileInput = $('.create-file-input input');
	var deleteBtn = $('.delete-btn');

	// suggestions index
	var suggestions = new Array();
	if (fileList.length) {
		var i = 0;
		fileList.find('a').each(function() {
			var text = $(this).html();
			if (suggestions.indexOf(text) === -1) {
				suggestions[i++] = text.replace('.html', '').replace('.tt2', '');
			}
		});
		fileList.find('td').each(function() {
			var text = $(this).html();
			if (text.indexOf('▼ ') === 0) {
				text = text.substr(2);
			} else {
				return;
			}
			if (suggestions.indexOf(text) === -1) {
				suggestions[i++] = text;
			}
		});
	}

	// events
	saveBtn.click(function() {
		ajaxResult.html('Saving...');
		var saveLoc = '';
		if (window.location.pathname.indexOf('/pages-edit') == 0) {
			saveLoc = '/pages-save';
		} else if (window.location.pathname.indexOf('/template-edit') == 0) {
			saveLoc = '/template-save';
		}
		$.ajax({
			url: saveLoc + '?file=' + getUrlParameter('file'),
			type: 'POST',
			data: editorArea.html(),
			dataType: 'text',
			timeout: 10000,
			error: function(jqXHR, text, error) {
				ajaxResult.html('Connection error ' + new Date().toTimeString() + '');
			},
			success: function(result) {
				ajaxResult.html(result + ' ' + new Date().toTimeString());
			}
		});
	});

	if (editorResult.length) {
		var updateEditor = function() {
			var text = marked(htmlDecode(editorArea.html()));
			text = text.replace(/\[%/g, '
').replace(/%\]/g, '
');
			editorResult.html(text);
		};
		updateEditor();
		editorArea.on('input', updateEditor);
	}

	createFile.click(function() {
		if (createFileInput.parent().css("display") == "none") {
			createFileInput.parent().fadeIn();
			return;
		}
		$(createFileInput).focus();

		var name = $(createFileInput).val();
		if (name == null || name == '') {
			return;
		}

		var editLoc = '';
		if (window.location.pathname.indexOf('/pages-list') == 0) {
			editLoc = '/pages-edit';
			name = (name + '.html').replace(/(\.html)+/g, '.html');
		} else if (window.location.pathname.indexOf('/template-list') == 0) {
			editLoc = '/template-edit';
			name = (name + '.tt2').replace(/(\.tt2)+/g, '.tt2');
		}
		window.location.href = editLoc + '?file=' + name.replace(/\//g, '%2f');
	});

	var wasDel = false;
	createFileInput.keypress(function(e) {
		wasDel = e.keyCode == 8 || e.keyCode == 46;
	});
	createFileInput.on('input propertychange', function() {
		if (wasDel) return;

		var val = $(createFileInput).val();
		if (val == null || val == '') return;

		var domElem = createFileInput[0];
		if (domElem.selection) return;

		var suggestion = '';
		var start = -1;
		var suggestStart = -1;
		for (var i = 0; i < suggestions.length; i++) {
			var item = suggestions[i];
			for (var j = item.length; j > 0; j--) {
				var s = val.lastIndexOf(item.substr(0, j));
				if (s !== -1) {
					if (suggestion.length == 0 || s + j > start) {
						suggestion = item;
						suggestStart = j;
						start = s + j;
					}
				}
			}
		}
		if (start >= val.length) {
			createFileInput.val(val + suggestion.substr(suggestStart));
			domElem.selectionStart = start;
			domElem.selectionEnd = val.length + suggestion.length;
		}
	});

	deleteBtn.click(function(e) {
		e.preventDefault();
		if (confirm('Are you sure you want to delete?')) {
			window.location.href = $(this).attr('href');
		}
	});
});

function getUrlParameter(param) {
	var pageUrl = window.location.search.substring(1);
	var args = pageUrl.split('&');
	for (var i = 0; i < args.length; i++) {
		var argName = args[i].split('=');
		if (argName[0] == param) {
			return argName[1];
		}
	}
}

function htmlEncode(text) {
	var result = text;
	result = result.replace('&', '&');
	result = result.replace(' ', ' ');
	result = result.replace('<', '<');
	result = result.replace('>', '>');
	result = result.replace('\n', '
');
	return result;
}

function htmlDecode(text) {
	var result = text;
	result = result.replace(/
/g, '\n');
	result = result.replace(/<[^>]*>/g, '');
	result = result.replace(/ /g, ' ');
	result = result.replace(/</g, '<');
	result = result.replace(/>/g, '>');
	result = result.replace(/&/g, '&');
	return result;
}
<commit_msg>Small usability fix<commit_after>/*
 * Copyright 2014 Reo_SP
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

$(document).ready(function() {
	// elements
	var fileList = $('.file-list');
	var editorArea = $('.editor-area');
	var editorResult = $('.editor-result');
	var saveBtn = $('#save-btn');
	var ajaxResult = $('.ajax-result');
	var createFile = $('.create-file');
	var createFileInput = $('.create-file-input input');
	var deleteBtn = $('.delete-btn');

	// suggestions index
	var suggestions = new Array();
	if (fileList.length) {
		var i = 0;
		fileList.find('a').each(function() {
			var text = $(this).html();
			if (suggestions.indexOf(text) === -1) {
				suggestions[i++] = text.replace('.html', '').replace('.tt2', '');
			}
		});
		fileList.find('td').each(function() {
			var text = $(this).html();
			if (text.indexOf('▼ ') === 0) {
				text = text.substr(2);
			} else {
				return;
			}
			if (suggestions.indexOf(text) === -1) {
				suggestions[i++] = text;
			}
		});
	}

	// events
	saveBtn.click(function() {
		ajaxResult.html('Saving...');
		var saveLoc = '';
		if (window.location.pathname.indexOf('/pages-edit') == 0) {
			saveLoc = '/pages-save';
		} else if (window.location.pathname.indexOf('/template-edit') == 0) {
			saveLoc = '/template-save';
		}
		$.ajax({
			url: saveLoc + '?file=' + getUrlParameter('file'),
			type: 'POST',
			data: editorArea.html(),
			dataType: 'text',
			timeout: 10000,
			error: function(jqXHR, text, error) {
				ajaxResult.html('Connection error ' + new Date().toTimeString() + '');
			},
			success: function(result) {
				ajaxResult.html(result + ' ' + new Date().toTimeString());
			}
		});
	});

	if (editorResult.length) {
		var updateEditor = function() {
			var text = marked(htmlDecode(editorArea.html()));
			text = text.replace(/\[%/g, '
').replace(/%\]/g, '
');
			editorResult.html(text);
		};
		updateEditor();
		editorArea.on('input', updateEditor);
	}

	createFile.click(function() {
		if (createFileInput.parent().css("display") == "none") {
			createFileInput.parent().fadeIn();
			createFileInput.focus();
			return;
		}
		$(createFileInput).focus();

		var name = $(createFileInput).val();
		if (name == null || name == '') {
			return;
		}

		var editLoc = '';
		if (window.location.pathname.indexOf('/pages-list') == 0) {
			editLoc = '/pages-edit';
			name = (name + '.html').replace(/(\.html)+/g, '.html');
		} else if (window.location.pathname.indexOf('/template-list') == 0) {
			editLoc = '/template-edit';
			name = (name + '.tt2').replace(/(\.tt2)+/g, '.tt2');
		}
		window.location.href = editLoc + '?file=' + name.replace(/\//g, '%2f');
	});

	var wasDel = false;
	createFileInput.keypress(function(e) {
		wasDel = e.keyCode == 8 || e.keyCode == 46;
		if (e.keyCode == 13) {
			createFile.click();
		}
	});
	createFileInput.on('input propertychange', function() {
		if (wasDel) return;

		var val = $(createFileInput).val();
		if (val == null || val == '') return;

		var domElem = createFileInput[0];
		if (domElem.selection) return;

		var suggestion = '';
		var start = -1;
		var suggestStart = -1;
		for (var i = 0; i < suggestions.length; i++) {
			var item = suggestions[i];
			for (var j = item.length; j > 0; j--) {
				var s = val.lastIndexOf(item.substr(0, j));
				if (s !== -1) {
					if (suggestion.length == 0 || s + j > start) {
						suggestion = item;
						suggestStart = j;
						start = s + j;
					}
				}
			}
		}
		if (start >= val.length) {
			createFileInput.val(val + suggestion.substr(suggestStart));
			domElem.selectionStart = start;
			domElem.selectionEnd = val.length + suggestion.length;
		}
	});

	deleteBtn.click(function(e) {
		e.preventDefault();
		if (confirm('Are you sure you want to delete?')) {
			window.location.href = $(this).attr('href');
		}
	});
});

function getUrlParameter(param) {
	var pageUrl = window.location.search.substring(1);
	var args = pageUrl.split('&');
	for (var i = 0; i < args.length; i++) {
		var argName = args[i].split('=');
		if (argName[0] == param) {
			return argName[1];
		}
	}
}

function htmlEncode(text) {
	var result = text;
	result = result.replace('&', '&');
	result = result.replace(' ', ' ');
	result = result.replace('<', '<');
	result = result.replace('>', '>');
	result = result.replace('\n', '
');
	return result;
}

function htmlDecode(text) {
	var result = text;
	result = result.replace(/
/g, '\n');
	result = result.replace(/<[^>]*>/g, '');
	result = result.replace(/ /g, ' ');
	result = result.replace(/</g, '<');
	result = result.replace(/>/g, '>');
	result = result.replace(/&/g, '&');
	return result;
}
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';

import Button from './Button';
import { getClasses } from '../../helpers/component';

const PrimaryButton = ({ children, className, ...rest }) => {
    return (<Button className={getClasses('pm-button-primary', className)} {...rest}>{children}</Button>);
};

PrimaryButton.propTypes = {
    children: PropTypes.node,
    className: PropTypes.string
};

export default PrimaryButton;<commit_msg>Review PrimaryButton mr1 needed<commit_after>import React from 'react';
import PropTypes from 'prop-types';

import Button from './Button';
import { getClasses } from '../../helpers/component';

const PrimaryButton = ({ children, className, ...rest }) => {
    return (<Button className={getClasses('pm-button-primary mr1', className)} {...rest}>{children}</Button>);
};

PrimaryButton.propTypes = {
    children: PropTypes.node,
    className: PropTypes.string
};

export default PrimaryButton;<|endoftext|>


<commit_before>/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2016
 */

import { BackHandler } from 'react-native';
import { batch } from 'react-redux';
import { NavigationActions, StackActions } from '@react-navigation/core';

import { UIDatabase } from '../database';
import { createRecord } from '../database/utilities';
import { navStrings } from '../localization';
import { ROUTES, FINALISABLE_PAGES } from './constants';
import { RootNavigator } from './RootNavigator';
import { PrescriptionActions } from '../actions/PrescriptionActions';
import { FinaliseActions } from '../actions/FinaliseActions';
import { PREFERENCE_KEYS } from '../database/utilities/constants';

/**
 * Navigation Action Creators.
 *
 * This file contains actions related to navigation which can be dispatched
 * to the redux store.
 *
 * Actions should return either a plain object or a thunk for side effects.
 *
 * NavigationActions are consists of action creators supplied by react-navigation.
 *
 * NavigationActions.navigate() - accepts an object as the payload, which should
 * have the fields:
 *
 * - `routeName` (See: pages/index - PAGES - keys are routeNames)
 * - `params` (See: Pages/pageContainer and pages/index FINALISABLE_PAGES for requirements)
 *
 */

export const goBack = () => dispatch => {
  if (!RootNavigator.canGoBack()) BackHandler.exitApp();
  else {
    UIDatabase.write(() => {
      const prescriptions = UIDatabase.objects('Prescription').filtered('status != "finalised"');
      UIDatabase.delete('Transaction', prescriptions);

      const prevRouteName = RootNavigator.getPrevRouteName();
      const currRouteName = RootNavigator.getCurrentRouteName();

      if (FINALISABLE_PAGES.includes(currRouteName)) dispatch(FinaliseActions.resetFinaliseItem());

      batch(() => {
        dispatch({
          ...NavigationActions.back(),
          payload: {
            prevRouteName,
          },
        });
        dispatch(PrescriptionActions.deletePrescription());
      });
    });
  }
};

/**
 * Action creator which first creates a prescription, and then navigates to it
 * for editing.
 *
 * @param {Object} patient     The other party of the invoice (Customer)
 * @param {Object} currentUser    The currently logged in user.
 */
export const createPrescription = patientID => (dispatch, getState) => {
  const { user } = getState();
  const { currentUser } = user;

  const patient = UIDatabase.get('Name', patientID);

  let newPrescription;
  UIDatabase.write(() => {
    newPrescription = createRecord(
      UIDatabase,
      'CustomerInvoice',
      patient,
      currentUser,
      'dispensary'
    );
  });

  dispatch(gotoPrescription(newPrescription));
};

export const goToCashRegister = () =>
  NavigationActions.navigate({
    routeName: ROUTES.CASH_REGISTER,
    params: { title: navStrings.cash_register },
  });

export const gotoPrescription = prescription =>
  NavigationActions.navigate({
    routeName: ROUTES.PRESCRIPTION,
    params: {
      title: `${navStrings.prescription} ${prescription.serialNumber}`,
      transaction: prescription,
      patient: prescription.otherParty,
      pageObject: prescription,
    },
  });

export const gotoPrescriptions = () =>
  NavigationActions.navigate({
    routeName: ROUTES.PRESCRIPTIONS,
    params: { title: 'Prescriptions' },
  });

export const gotoDispensingPage = () => dispatch => {
  UIDatabase.write(() => {
    UIDatabase.delete(
      'Transaction',
      UIDatabase.objects('Prescription').filtered('status != $0', 'finalised')
    );
  });

  dispatch(
    NavigationActions.navigate({
      routeName: ROUTES.DISPENSARY,
      params: { title: navStrings.dispensary },
    })
  );
};

/**
 * Pushes the Settings page route onto the main navigation stack.
 */
export const gotoDashboard = () =>
  NavigationActions.navigate({
    routeName: ROUTES.DASHBOARD,
    params: { title: navStrings.dashboard },
  });

/**
 * Pushes the Settings page route onto the main navigation stack.
 */
export const gotoSettings = () =>
  NavigationActions.navigate({
    routeName: ROUTES.SETTINGS,
    params: {
      title: navStrings.settings,
    },
  });

/**
 * Pushes the Realm explorer route onto the main navigation stack.
 */
export const gotoRealmExplorer = () =>
  NavigationActions.navigate({
    routeName: ROUTES.REALM_EXPLORER,
    params: {
      title: navStrings.database_contents,
    },
  });

/**
 * Pushes the Customer Invoices route onto the main navigation stack.
 */
export const gotoCustomerInvoices = () => dispatch => {
  UIDatabase.write(() => {
    UIDatabase.delete(
      'Transaction',
      UIDatabase.objects('Prescription').filtered('status != $0', 'finalised')
    );
  });

  dispatch(
    NavigationActions.navigate({
      routeName: ROUTES.CUSTOMER_INVOICES,
      params: { title: navStrings.customer_invoices },
    })
  );
};

/**
 * Pushes the Customer Requisitions route onto the main navigation stack.
 */
export const gotoCustomerRequisitions = () =>
  NavigationActions.navigate({
    routeName: ROUTES.CUSTOMER_REQUISITIONS,
    params: {
      title: navStrings.customer_requisitions,
    },
  });

/**
 * Pushes the Supplier Invoices route onto the main navigation stack.
 */
export const gotoSupplierInvoices = () => {
  NavigationActions.navigate({
    routeName: ROUTES.SUPPLIER_INVOICES,
    params: {
      title: navStrings.supplier_invoices,
    },
  });
};

/**
 * Pushes the Supplier Requisitions route onto the main navigation stack.
 */
export const gotoSupplierRequisitions = () =>
  NavigationActions.navigate({
    routeName: ROUTES.SUPPLIER_REQUISITIONS,
    params: {
      title: navStrings.supplier_requisitions,
    },
  });

/**
 * Pushes the Stocktakes route onto the main navigation stack.
 */
export const gotoStocktakes = () => {
  NavigationActions.navigate({
    routeName: ROUTES.STOCKTAKES,
    params: {
      title: navStrings.stocktakes,
    },
  });
};

/**
 * Pushes the Stock route onto the main navigation stack.
 */
export const gotoStock = () =>
  NavigationActions.navigate({
    routeName: ROUTES.STOCK,
    params: {
      title: navStrings.current_stock,
    },
  });

/**
 * Action creator for navigating to the SupplierRequisition screen.
 *
 * @param {Object} requisition The requisition to pass to the next screen.
 */
export const gotoStocktakeManagePage = (stocktakeName, stocktake) => dispatch => {
  const currentRouteName = RootNavigator.getCurrentRouteName();

  const navigationActionCreator =
    currentRouteName === ROUTES.STOCKTAKES ? NavigationActions.navigate : StackActions.replace;

  const navigationParameters = {
    routeName: ROUTES.STOCKTAKE_MANAGER,
    params: {
      stocktakeName,
      stocktake,
      pageObject: stocktake,
    },
  };

  dispatch(navigationActionCreator(navigationParameters));
};

/**
 * Navigate to the StocktakeEditPage.
 *
 * @param {Object} stocktake  The requisition to navigate to.
 */
export const gotoStocktakeEditPage = stocktake => dispatch => {
  const currentRouteName = RootNavigator.getCurrentRouteName();

  // If navigating from the stocktakesPage, go straight to the StocktakeEditPage. Otherwise,
  // replace the current page as the user is coming from StocktakeManagePage.
  const navigationActionCreator =
    currentRouteName === ROUTES.STOCKTAKES ? NavigationActions.navigate : StackActions.replace;

  const navigationParameters = {
    routeName: ROUTES.STOCKTAKE_EDITOR,
    params: { title: navStrings.stocktake, stocktake, pageObject: stocktake },
  };

  batch(() => {
    dispatch(navigationActionCreator(navigationParameters));
    dispatch(FinaliseActions.setFinaliseItem(stocktake));
  });
};

/**
 * Action creator for navigating to a customer invoice. Ensures the CI is at least
 * confirmed before navigating as if this is not enforced, it is possible for
 * a particular item being issued across multiple invoices in larger quantities
 * than are available.
 *
 * @param {Object} transaction The CI to navigate to.
 * @param {Object} dispatch    Redux dispatch method.
 */
export const gotoCustomerInvoice = transaction => dispatch => {
  const { isCredit, isConfirmed, isFinalised } = transaction;

  // Customer invoices are generally created with the status confirmed. This handles unexpected
  // cases of an incoming sycned invoice with status 'nw' or 'sg'.
  if (!isConfirmed && !isFinalised) {
    UIDatabase.write(() => {
      transaction.confirm(UIDatabase);
      UIDatabase.save('Transaction', transaction);
    });
  }

  const navigationAction = NavigationActions.navigate({
    routeName: ROUTES.CUSTOMER_INVOICE,
    params: {
      title: `${isCredit ? navStrings.credit : navStrings.invoice} ${transaction.serialNumber}`,
      transaction,
      pageObject: transaction,
    },
  });

  batch(() => {
    dispatch(navigationAction);
    dispatch(FinaliseActions.setFinaliseItem(transaction));
  });
};

/**
 * Action creator for navigating to a SupplierInvoice. Ensures the SI is finalised, if
 * confirmed before navigating. This should not occur, but if this is not enforced, a
 * user can reduce the amount of stock in a SI which has been used in a CI, which causes
 * inventory adjustments instantly.
 *
 * @param {Object} transaction The SI to navigate to.
 * @param {Object} dispatch    Redux dispatch method.
 */
export const gotoSupplierInvoice = transaction => dispatch => {
  const { isSupplierInvoice, isConfirmed } = transaction;

  // Supplier invoices are `new` or `finalised`. Ensure any `confirmed` invoices are
  // `finalised` before navigating.
  if (isConfirmed) {
    UIDatabase.write(() => {
      transaction.finalise(UIDatabase);
      UIDatabase.save('Transaction', transaction);
    });
  }

  const invoiceTitle = `${navStrings.invoice} ${transaction.serialNumber}`;
  const creditTitle = `${navStrings.supplier_credit} ${transaction.serialNumber}`;

  const navigationAction = NavigationActions.navigate({
    routeName: ROUTES.SUPPLIER_INVOICE,
    params: {
      title: isSupplierInvoice ? invoiceTitle : creditTitle,
      transaction,
      pageObject: transaction,
    },
  });

  batch(() => {
    dispatch(navigationAction);
    dispatch(FinaliseActions.setFinaliseItem(transaction));
  });
};

/**
 * Navigate to the SupplierRequisitionPage.
 *
 * @param {Object} requisition  SupplierRequisition to navigate to.
 */
export const gotoSupplierRequisition = requisition => dispatch => {
  batch(() => {
    dispatch(
      NavigationActions.navigate({
        routeName: ROUTES.SUPPLIER_REQUISITION,
        params: {
          title: `${navStrings.requisition} ${requisition.serialNumber}`,
          requisition,
          pageObject: requisition,
        },
      })
    );
    dispatch(FinaliseActions.setFinaliseItem(requisition));
  });
};

/**
 * Navigate to the CustomerRequisitionPage.
 *
 * @param {Object} requisition  Customer requisition to navigate to.
 */
export const gotoCustomerRequisition = requisition => dispatch => {
  batch(() => {
    dispatch(
      NavigationActions.navigate({
        routeName: ROUTES.CUSTOMER_REQUISITION,
        params: {
          title: `${navStrings.requisition} ${requisition.serialNumber}`,
          requisition,
          pageObject: requisition,
        },
      })
    );
    dispatch(FinaliseActions.setFinaliseItem(requisition));
  });
};

/**
 * Action creator for creating, and navigating to a Supplier Requsition.
 * Requisition is created by a thunk initially.
 *
 * @param {String} CurrentUser The currently logged in user.
 * @param {Object} requisitionParameters Parameters for the to-be-created object.
 * RequisitionParameters can be any fields in Requisition.js to pass to createRecord.
 */
export const createSupplierRequisition = ({
  currentUser,
  ...requisitionParameters
}) => dispatch => {
  // Months lead time has an effect on daysToSupply for a requisition.
  const monthsLeadTime = UIDatabase.getPreference(PREFERENCE_KEYS.MONTHS_LEAD_TIME) ?? 0;

  // Create the requisition. If a program was supplied, add items from that
  // program, otherwise just navigate to it.
  let requisition;
  UIDatabase.write(() => {
    requisition = createRecord(UIDatabase, 'Requisition', currentUser, {
      ...requisitionParameters,
      monthsLeadTime,
    });

    if (requisition.program) requisition.addItemsFromProgram(UIDatabase);
  });

  dispatch(gotoSupplierRequisition(requisition));
};

/**
 * Creates a stocktake and replaces the current route in the StackNavigator which
 * would be a StocktakeManagePage, with a StocktakeEditPage, with the newly
 * created Stocktake.
 *
 * @param {Object} StocktakeParameters  Parameters for the stocktake to create.
 */
export const createStocktake = ({ currentUser, stocktakeName, program, itemIds }) => dispatch => {
  let stocktake;

  UIDatabase.write(() => {
    stocktake = createRecord(UIDatabase, 'Stocktake', currentUser, stocktakeName, program);
    if (program) stocktake.addItemsFromProgram(UIDatabase);
    else if (itemIds) stocktake.setItemsByID(UIDatabase, itemIds);
  });

  // If creating a Stocktake with a program or with a list of itemIds, navigate to
  // the StocktakeEditPage. Otherwise, navigate to the StocktakeManagePage.
  return program || itemIds
    ? dispatch(gotoStocktakeEditPage(stocktake))
    : dispatch(gotoStocktakeManagePage(stocktakeName, stocktake));
};

/**
 * Action creator which first creates a customer invoice, and then navigates to it
 * for editing.
 *
 * @param {Object} otherParty     The other party of the invoice (Customer)
 * @param {Object} currentUser    The currently logged in user.
 */
export const createCustomerInvoice = (otherParty, currentUser) => dispatch => {
  let newCustomerInvoice;
  UIDatabase.write(() => {
    newCustomerInvoice = createRecord(UIDatabase, 'CustomerInvoice', otherParty, currentUser);
  });

  dispatch(gotoCustomerInvoice(newCustomerInvoice));
};

/**
 * Action creator which first creates a supplier invoice, and then navigates to it
 * for editing.
 *
 * @param {Object} otherParty     The other party of the invoice (Supplier)
 * @param {Object} currentUser    The currently logged in user.
 */
export const createSupplierInvoice = (otherParty, currentUser) => dispatch => {
  let newCustomerInvoice;
  UIDatabase.write(() => {
    newCustomerInvoice = createRecord(UIDatabase, 'SupplierInvoice', otherParty, currentUser);
  });

  dispatch(gotoSupplierInvoice(newCustomerInvoice));
};

/**
 * Updates a Stocktake with the passed array of itemIDs and navigates to a
 * StocktakeEditPage.
 *
 * @param {Object} stocktake realm Stocktake object
 * @param {Array}  itemIds   Array of item id strings that should be the new
 *                           Items in the stocktake.
 * @param {String} name   Name of the stocktake for updating. Cannot update to remove completely.
 */
export const updateStocktake = (stocktake, itemIds, name = '') => dispatch => {
  UIDatabase.write(() => {
    if (name) stocktake.name = name;

    stocktake.setItemsByID(UIDatabase, itemIds);
    UIDatabase.save('Stocktake', stocktake);
  });

  dispatch(gotoStocktakeEditPage(stocktake));
};
<commit_msg>Tidy up navigation actions<commit_after>/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2016
 */

import { BackHandler } from 'react-native';
import { batch } from 'react-redux';
import { NavigationActions, StackActions } from '@react-navigation/core';

import { UIDatabase } from '../database';
import { createRecord } from '../database/utilities';
import { navStrings } from '../localization';
import { ROUTES, FINALISABLE_PAGES } from './constants';
import { RootNavigator } from './RootNavigator';
import { PrescriptionActions } from '../actions/PrescriptionActions';
import { FinaliseActions } from '../actions/FinaliseActions';
import { PREFERENCE_KEYS } from '../database/utilities/constants';

/**
 * Navigation Action Creators.
 *
 * This file contains actions related to navigation which can be dispatched
 * to the redux store.
 *
 * Actions should return either a plain object or a thunk for side effects.
 *
 * NavigationActions are consists of action creators supplied by react-navigation.
 *
 * NavigationActions.navigate() - accepts an object as the payload, which should
 * have the fields:
 *
 * - `routeName` (See: pages/index - PAGES - keys are routeNames)
 * - `params` (See: Pages/pageContainer and pages/index FINALISABLE_PAGES for requirements)
 *
 */

/**
 * Action creator for handling back navigation.
 *
 * Triggers back navigation and cleans up current state.
 */
export const goBack = () => dispatch => {
  if (!RootNavigator.canGoBack()) BackHandler.exitApp();
  else {
    UIDatabase.write(() => {
      const prescriptions = UIDatabase.objects('Prescription').filtered('status != "finalised"');
      UIDatabase.delete('Transaction', prescriptions);

      const prevRouteName = RootNavigator.getPrevRouteName();
      const currRouteName = RootNavigator.getCurrentRouteName();

      const navigateBack = () =>
        dispatch({
          ...NavigationActions.back(),
          payload: { prevRouteName },
        });

      const cleanUp = () => {
        dispatch(PrescriptionActions.deletePrescription());
        if (FINALISABLE_PAGES.includes(currRouteName)) {
          dispatch(FinaliseActions.resetFinaliseItem());
        }
      };

      batch(() => {
        navigateBack();
        cleanUp();
      });
    });
  }
};

/**
 * Action creator which first creates a prescription, and then navigates to it
 * for editing.
 *
 * @param {Object} patient     The other party of the invoice (Customer)
 * @param {Object} currentUser    The currently logged in user.
 */
export const createPrescription = patientID => (dispatch, getState) => {
  const { user } = getState();
  const { currentUser } = user;

  const patient = UIDatabase.get('Name', patientID);

  let newPrescription;
  UIDatabase.write(() => {
    newPrescription = createRecord(
      UIDatabase,
      'CustomerInvoice',
      patient,
      currentUser,
      'dispensary'
    );
  });

  dispatch(gotoPrescription(newPrescription));
};

export const goToCashRegister = () =>
  NavigationActions.navigate({
    routeName: ROUTES.CASH_REGISTER,
    params: { title: navStrings.cash_register },
  });

export const gotoPrescription = prescription =>
  NavigationActions.navigate({
    routeName: ROUTES.PRESCRIPTION,
    params: {
      title: `${navStrings.prescription} ${prescription.serialNumber}`,
      transaction: prescription,
      patient: prescription.otherParty,
      pageObject: prescription,
    },
  });

export const gotoPrescriptions = () =>
  NavigationActions.navigate({
    routeName: ROUTES.PRESCRIPTIONS,
    params: { title: 'Prescriptions' },
  });

export const gotoDispensingPage = () => dispatch => {
  UIDatabase.write(() => {
    UIDatabase.delete(
      'Transaction',
      UIDatabase.objects('Prescription').filtered('status != $0', 'finalised')
    );
  });

  dispatch(
    NavigationActions.navigate({
      routeName: ROUTES.DISPENSARY,
      params: { title: navStrings.dispensary },
    })
  );
};

/**
 * Pushes the Settings page route onto the main navigation stack.
 */
export const gotoDashboard = () =>
  NavigationActions.navigate({
    routeName: ROUTES.DASHBOARD,
    params: { title: navStrings.dashboard },
  });

/**
 * Pushes the Settings page route onto the main navigation stack.
 */
export const gotoSettings = () =>
  NavigationActions.navigate({
    routeName: ROUTES.SETTINGS,
    params: {
      title: navStrings.settings,
    },
  });

/**
 * Pushes the Realm explorer route onto the main navigation stack.
 */
export const gotoRealmExplorer = () =>
  NavigationActions.navigate({
    routeName: ROUTES.REALM_EXPLORER,
    params: {
      title: navStrings.database_contents,
    },
  });

/**
 * Pushes the Customer Invoices route onto the main navigation stack.
 */
export const gotoCustomerInvoices = () => dispatch => {
  UIDatabase.write(() => {
    UIDatabase.delete(
      'Transaction',
      UIDatabase.objects('Prescription').filtered('status != $0', 'finalised')
    );
  });

  dispatch(
    NavigationActions.navigate({
      routeName: ROUTES.CUSTOMER_INVOICES,
      params: { title: navStrings.customer_invoices },
    })
  );
};

/**
 * Pushes the Customer Requisitions route onto the main navigation stack.
 */
export const gotoCustomerRequisitions = () =>
  NavigationActions.navigate({
    routeName: ROUTES.CUSTOMER_REQUISITIONS,
    params: {
      title: navStrings.customer_requisitions,
    },
  });

/**
 * Pushes the Supplier Invoices route onto the main navigation stack.
 */
export const gotoSupplierInvoices = () =>
  NavigationActions.navigate({
    routeName: ROUTES.SUPPLIER_INVOICES,
    params: {
      title: navStrings.supplier_invoices,
    },
  });

/**
 * Pushes the Supplier Requisitions route onto the main navigation stack.
 */
export const gotoSupplierRequisitions = () =>
  NavigationActions.navigate({
    routeName: ROUTES.SUPPLIER_REQUISITIONS,
    params: {
      title: navStrings.supplier_requisitions,
    },
  });

/**
 * Pushes the Stocktakes route onto the main navigation stack.
 */
export const gotoStocktakes = () =>
  NavigationActions.navigate({
    routeName: ROUTES.STOCKTAKES,
    params: {
      title: navStrings.stocktakes,
    },
  });

/**
 * Pushes the Stock route onto the main navigation stack.
 */
export const gotoStock = () =>
  NavigationActions.navigate({
    routeName: ROUTES.STOCK,
    params: {
      title: navStrings.current_stock,
    },
  });

/**
 * Action creator for navigating to the SupplierRequisition screen.
 *
 * @param {Object} requisition The requisition to pass to the next screen.
 */
export const gotoStocktakeManagePage = (stocktakeName, stocktake) => dispatch => {
  const currentRouteName = RootNavigator.getCurrentRouteName();

  const navigationActionCreator =
    currentRouteName === ROUTES.STOCKTAKES ? NavigationActions.navigate : StackActions.replace;

  const navigationParameters = {
    routeName: ROUTES.STOCKTAKE_MANAGER,
    params: {
      stocktakeName,
      stocktake,
      pageObject: stocktake,
    },
  };

  dispatch(navigationActionCreator(navigationParameters));
};

/**
 * Navigate to the StocktakeEditPage.
 *
 * @param {Object} stocktake  The requisition to navigate to.
 */
export const gotoStocktakeEditPage = stocktake => dispatch => {
  const currentRouteName = RootNavigator.getCurrentRouteName();

  // If navigating from the stocktakesPage, go straight to the StocktakeEditPage. Otherwise,
  // replace the current page as the user is coming from StocktakeManagePage.
  const navigationActionCreator =
    currentRouteName === ROUTES.STOCKTAKES ? NavigationActions.navigate : StackActions.replace;

  const navigationParameters = {
    routeName: ROUTES.STOCKTAKE_EDITOR,
    params: { title: navStrings.stocktake, stocktake, pageObject: stocktake },
  };

  batch(() => {
    dispatch(navigationActionCreator(navigationParameters));
    dispatch(FinaliseActions.setFinaliseItem(stocktake));
  });
};

/**
 * Action creator for navigating to a customer invoice. Ensures the CI is at least
 * confirmed before navigating as if this is not enforced, it is possible for
 * a particular item being issued across multiple invoices in larger quantities
 * than are available.
 *
 * @param {Object} transaction The CI to navigate to.
 * @param {Object} dispatch    Redux dispatch method.
 */
export const gotoCustomerInvoice = transaction => dispatch => {
  const { isCredit, isConfirmed, isFinalised } = transaction;

  // Customer invoices are generally created with the status confirmed. This handles unexpected
  // cases of an incoming sycned invoice with status 'nw' or 'sg'.
  if (!isConfirmed && !isFinalised) {
    UIDatabase.write(() => {
      transaction.confirm(UIDatabase);
      UIDatabase.save('Transaction', transaction);
    });
  }

  const navigationAction = NavigationActions.navigate({
    routeName: ROUTES.CUSTOMER_INVOICE,
    params: {
      title: `${isCredit ? navStrings.credit : navStrings.invoice} ${transaction.serialNumber}`,
      transaction,
      pageObject: transaction,
    },
  });

  batch(() => {
    dispatch(navigationAction);
    dispatch(FinaliseActions.setFinaliseItem(transaction));
  });
};

/**
 * Action creator for navigating to a SupplierInvoice. Ensures the SI is finalised, if
 * confirmed before navigating. This should not occur, but if this is not enforced, a
 * user can reduce the amount of stock in a SI which has been used in a CI, which causes
 * inventory adjustments instantly.
 *
 * @param {Object} transaction The SI to navigate to.
 * @param {Object} dispatch    Redux dispatch method.
 */
export const gotoSupplierInvoice = transaction => dispatch => {
  const { isSupplierInvoice, isConfirmed } = transaction;

  // Supplier invoices are `new` or `finalised`. Ensure any `confirmed` invoices are
  // `finalised` before navigating.
  if (isConfirmed) {
    UIDatabase.write(() => {
      transaction.finalise(UIDatabase);
      UIDatabase.save('Transaction', transaction);
    });
  }

  const invoiceTitle = `${navStrings.invoice} ${transaction.serialNumber}`;
  const creditTitle = `${navStrings.supplier_credit} ${transaction.serialNumber}`;

  const navigationAction = NavigationActions.navigate({
    routeName: ROUTES.SUPPLIER_INVOICE,
    params: {
      title: isSupplierInvoice ? invoiceTitle : creditTitle,
      transaction,
      pageObject: transaction,
    },
  });

  batch(() => {
    dispatch(navigationAction);
    dispatch(FinaliseActions.setFinaliseItem(transaction));
  });
};

/**
 * Navigate to the SupplierRequisitionPage.
 *
 * @param {Object} requisition  SupplierRequisition to navigate to.
 */
export const gotoSupplierRequisition = requisition => dispatch => {
  batch(() => {
    dispatch(
      NavigationActions.navigate({
        routeName: ROUTES.SUPPLIER_REQUISITION,
        params: {
          title: `${navStrings.requisition} ${requisition.serialNumber}`,
          requisition,
          pageObject: requisition,
        },
      })
    );
    dispatch(FinaliseActions.setFinaliseItem(requisition));
  });
};

/**
 * Navigate to the CustomerRequisitionPage.
 *
 * @param {Object} requisition  Customer requisition to navigate to.
 */
export const gotoCustomerRequisition = requisition => dispatch => {
  batch(() => {
    dispatch(
      NavigationActions.navigate({
        routeName: ROUTES.CUSTOMER_REQUISITION,
        params: {
          title: `${navStrings.requisition} ${requisition.serialNumber}`,
          requisition,
          pageObject: requisition,
        },
      })
    );
    dispatch(FinaliseActions.setFinaliseItem(requisition));
  });
};

/**
 * Action creator for creating, and navigating to a Supplier Requsition.
 * Requisition is created by a thunk initially.
 *
 * @param {String} CurrentUser The currently logged in user.
 * @param {Object} requisitionParameters Parameters for the to-be-created object.
 * RequisitionParameters can be any fields in Requisition.js to pass to createRecord.
 */
export const createSupplierRequisition = ({
  currentUser,
  ...requisitionParameters
}) => dispatch => {
  // Months lead time has an effect on daysToSupply for a requisition.
  const monthsLeadTime = UIDatabase.getPreference(PREFERENCE_KEYS.MONTHS_LEAD_TIME) ?? 0;

  // Create the requisition. If a program was supplied, add items from that
  // program, otherwise just navigate to it.
  let requisition;
  UIDatabase.write(() => {
    requisition = createRecord(UIDatabase, 'Requisition', currentUser, {
      ...requisitionParameters,
      monthsLeadTime,
    });

    if (requisition.program) requisition.addItemsFromProgram(UIDatabase);
  });

  dispatch(gotoSupplierRequisition(requisition));
};

/**
 * Creates a stocktake and replaces the current route in the StackNavigator which
 * would be a StocktakeManagePage, with a StocktakeEditPage, with the newly
 * created Stocktake.
 *
 * @param {Object} StocktakeParameters  Parameters for the stocktake to create.
 */
export const createStocktake = ({ currentUser, stocktakeName, program, itemIds }) => dispatch => {
  let stocktake;

  UIDatabase.write(() => {
    stocktake = createRecord(UIDatabase, 'Stocktake', currentUser, stocktakeName, program);
    if (program) stocktake.addItemsFromProgram(UIDatabase);
    else if (itemIds) stocktake.setItemsByID(UIDatabase, itemIds);
  });

  // If creating a Stocktake with a program or with a list of itemIds, navigate to
  // the StocktakeEditPage. Otherwise, navigate to the StocktakeManagePage.
  return program || itemIds
    ? dispatch(gotoStocktakeEditPage(stocktake))
    : dispatch(gotoStocktakeManagePage(stocktakeName, stocktake));
};

/**
 * Action creator which first creates a customer invoice, and then navigates to it
 * for editing.
 *
 * @param {Object} otherParty     The other party of the invoice (Customer)
 * @param {Object} currentUser    The currently logged in user.
 */
export const createCustomerInvoice = (otherParty, currentUser) => dispatch => {
  let newCustomerInvoice;
  UIDatabase.write(() => {
    newCustomerInvoice = createRecord(UIDatabase, 'CustomerInvoice', otherParty, currentUser);
  });

  dispatch(gotoCustomerInvoice(newCustomerInvoice));
};

/**
 * Action creator which first creates a supplier invoice, and then navigates to it
 * for editing.
 *
 * @param {Object} otherParty     The other party of the invoice (Supplier)
 * @param {Object} currentUser    The currently logged in user.
 */
export const createSupplierInvoice = (otherParty, currentUser) => dispatch => {
  let newCustomerInvoice;
  UIDatabase.write(() => {
    newCustomerInvoice = createRecord(UIDatabase, 'SupplierInvoice', otherParty, currentUser);
  });

  dispatch(gotoSupplierInvoice(newCustomerInvoice));
};

/**
 * Updates a Stocktake with the passed array of itemIDs and navigates to a
 * StocktakeEditPage.
 *
 * @param {Object} stocktake realm Stocktake object
 * @param {Array}  itemIds   Array of item id strings that should be the new
 *                           Items in the stocktake.
 * @param {String} name   Name of the stocktake for updating. Cannot update to remove completely.
 */
export const updateStocktake = (stocktake, itemIds, name = '') => dispatch => {
  UIDatabase.write(() => {
    if (name) stocktake.name = name;

    stocktake.setItemsByID(UIDatabase, itemIds);
    UIDatabase.save('Stocktake', stocktake);
  });

  dispatch(gotoStocktakeEditPage(stocktake));
};
<|endoftext|>


<commit_before>(function () {
	var root = this;

	var Datetime = function () {

	};

	Datetime.dayCount = 86400000;
	Datetime.hourCount = 3600000;
	Datetime.minuteCount = 60000;
	Datetime.secondCount = 1000;

	Datetime.dhmsRegex = /^(?:(\d+)d)?(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?$/i;

	Datetime.isDhms = function (s) {
		return Datetime.dhmsRegex.test(s);
	};

	Datetime.parseDhms = function (s) {
		var res = Datetime.dhmsRegex.exec(s);

		if (null === res) {
			return null;
		} else {
			return {
				day: res[1] ? +res[1] : 0,
				hour: res[2] ? +res[2] : 0,
				minute: res[3] ? +res[3] : 0,
				second: res[4] ? +res[4] : 0
			};
		}
	};

	var monthMapping = {
		'jan': 0,
		'feb': 1,
		'mar': 2,
		'apr': 3,
		'may': 4,
		'jun': 5,
		'jul': 6,
		'aug': 7,
		'sep': 8,
		'oct': 9,
		'nov': 10,
		'dev': 11
	};

	Datetime.dtRegex = /^(10|11|12|[0-9])([0-5][0-9])?([0-5][0-9])?([ap]m)(?:(\d{1,2})(jan|january|feb|feb|february|mar|march|apr|april|may|jun|june|jul|july|aug|august|sep|september|oct|october|nov|november|dec|december)(\d{4})?)?$/i;

	Datetime.isDt = function (s) {
		var res = Datetime.dtRegex.exec(s);

		if (null === res) {
			return false;
		} else {
			var now = new Date(),
				hour = +res[1] + ('pm' === res[4] ? 12 : 0),
				minute = res[2] ? +res[2] : 0,
				second = res[3] ? +res[3] : 0,
				day = res[5] ? +res[5] : now.getDate(),
				month = res[6] ? monthMapping[res[6].substring(0, 3).toLowerCase()] : now.getMonth(),
				year = res[7] ? +res[7] : now.getFullYear(),
				testDate = new Date(year, month, day, hour, minute, second);

			if (!(testDate.getFullYear() === year && testDate.getMonth() === month && testDate.getDate())) {
				return false;
			} else {
				return true;
			}
		}

	};

	Datetime.parseDt = function (s) {

		if (!Datetime.isDt(s)) {
			return null;
		} else {
			var res = Datetime.dtRegex.exec(s);

			return {
				hour: +res[1] + ('pm' === res[4] ? 12 : 0),
				minute: res[2] ? +res[2] : 0,
				second: res[3] ? +res[3] : 0,
				day: res[5] ? +res[5] : null,
				month: res[6] ? monthMapping[res[6].substring(0, 3).toLowerCase()] : null,
				year: res[7] ? +res[7] : null
			};
		}
	};

	Datetime.countRegex = /^(\d+)$/;

	Datetime.isCount = function (s) {
		return Datetime.countRegex.test(s);
	};

	Datetime.parseCount = function (s) {
		var res = Datetime.countRegex.exec(s);

		if (null === res) {
			return null;
		} else {
			return +res[1];
		}
	};

	Datetime.isParsable = function (s) {
		return Datetime.isDhms(s) || Datetime.isDt(s) || Datetime.isCount(s);
	};

	Datetime.makeDatetime = function (s) {

		if (Datetime.isCount(s)) {
			return new Date(Datetime.minuteCount * Datetime.parseCount(s));
		} else if (Datetime.isDhms(s)) {
			var dhms = Datetime.parseDhms(s);

			return new Date(Datetime.dayCount * dhms.day + Datetime.hourCount * dhms.hour + Datetime.minuteCount * dhms.minute + Datetime.secondCount * dhms.second);
		} else if (Datetime.isDt(s)) {
			var dt = Datetime.parseDt(s),
				now = new Date();

			if (!dt.year) {
				dt.year = now.getFullYear();
			}

			if (!dt.day) {
				var testDate = new Date();
				testDate.setHours(dt.hour);
				testDate.setMinutes(dt.minute);
				testDate.setSeconds(dt.second);
				if (testDate < now) {
					dt.day = now.getDate() + 1;
				}
				dt.month = now.getMonth();
			}

			return new Date(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second);
		} else {
			return null;
		}
	};

	Datetime.displayString = function (time, precision, purals) {
		time = Math.abs(time);
		precision = precision || 'second';
		var dhmsMapping = {
				day: 0,
				hour: 1,
				minute: 2,
				second: 3,
				millisecond: 4
			},
			dhms = [
				Math.floor(time / Datetime.dayCount),
				Math.floor(time / Datetime.hourCount) % 24,
				Math.floor(time / Datetime.minuteCount) % 60,
				Math.floor(time / Datetime.secondCount) % 60,
				time % 1000
			],
			dictionary = purals || [
				['day', 'days'],
				['hour', 'hours'],
				['minute', 'minutes'],
				['second', 'seconds'],
				['millisecond', 'milliseconds']
			],
			firstNonZero = 0;

		return dhms.map(function (val, index) {
			if ((val === 0 && firstNonZero === 0 && index < dhmsMapping[precision]) || index > dhmsMapping[precision]) {
				return false;
			} else {
				firstNonZero = index;
				return val + ' ' + (1 === val ? dictionary[index][0] : dictionary[index][1]);
			}
		}).filter(function (str) {return str;}).join(' ');
	};

	Datetime.shortDtFormat = 'HH:mm:ss';
	Datetime.longDtFormat = 'dd/MM/yyyy HH:mm:ss';

	var pad0 = function (n) {
		return n < 10 ? '0' + n : n;
	};

	var subFormat = function (pattern, sub, map) {
		map = map || function (val) {return val;};
		return function (format) {
			return format.split(pattern).map(map).join(sub);
		};
	};

	Datetime.shortString = function (date, format) {
		format = format || Datetime.shortDtFormat;
		if ('number' === typeof date)
			date = new Date(date);

		return subFormat('yyyy', date.getFullYear(),
			subFormat('MM', pad0(date.getMonth() + 1),
				subFormat('dd', pad0(date.getDate()),
					subFormat('HH', pad0(date.getHours()),
						subFormat('mm', pad0(date.getMinutes()),
							subFormat('ss', pad0(date.getSeconds()))
						)
					)
				)
			)
		)(format);
	};

	root.Datetime = Datetime;
}.call(this));
<commit_msg>modified variable name and expose long/short purals dictionary<commit_after>(function () {
	var root = this;

	var Datetime = function () {

	};

	Datetime.dayCount = 86400000;
	Datetime.hourCount = 3600000;
	Datetime.minuteCount = 60000;
	Datetime.secondCount = 1000;

	Datetime.dhmsRegex = /^(?:(\d+)d)?(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?$/i;

	Datetime.isDhms = function (s) {
		return Datetime.dhmsRegex.test(s);
	};

	Datetime.parseDhms = function (s) {
		var res = Datetime.dhmsRegex.exec(s);

		if (null === res) {
			return null;
		} else {
			return {
				day: res[1] ? +res[1] : 0,
				hour: res[2] ? +res[2] : 0,
				minute: res[3] ? +res[3] : 0,
				second: res[4] ? +res[4] : 0
			};
		}
	};

	var monthMapping = {
		'jan': 0,
		'feb': 1,
		'mar': 2,
		'apr': 3,
		'may': 4,
		'jun': 5,
		'jul': 6,
		'aug': 7,
		'sep': 8,
		'oct': 9,
		'nov': 10,
		'dev': 11
	};

	Datetime.dtRegex = /^(10|11|12|[0-9])([0-5][0-9])?([0-5][0-9])?([ap]m)(?:(\d{1,2})(jan|january|feb|feb|february|mar|march|apr|april|may|jun|june|jul|july|aug|august|sep|september|oct|october|nov|november|dec|december)(\d{4})?)?$/i;

	Datetime.isDt = function (s) {
		var res = Datetime.dtRegex.exec(s);

		if (null === res) {
			return false;
		} else {
			var now = new Date(),
				hour = +res[1] + ('pm' === res[4] ? 12 : 0),
				minute = res[2] ? +res[2] : 0,
				second = res[3] ? +res[3] : 0,
				day = res[5] ? +res[5] : now.getDate(),
				month = res[6] ? monthMapping[res[6].substring(0, 3).toLowerCase()] : now.getMonth(),
				year = res[7] ? +res[7] : now.getFullYear(),
				testDate = new Date(year, month, day, hour, minute, second);

			if (!(testDate.getFullYear() === year && testDate.getMonth() === month && testDate.getDate())) {
				return false;
			} else {
				return true;
			}
		}

	};

	Datetime.parseDt = function (s) {

		if (!Datetime.isDt(s)) {
			return null;
		} else {
			var res = Datetime.dtRegex.exec(s);

			return {
				hour: +res[1] + ('pm' === res[4] ? 12 : 0),
				minute: res[2] ? +res[2] : 0,
				second: res[3] ? +res[3] : 0,
				day: res[5] ? +res[5] : null,
				month: res[6] ? monthMapping[res[6].substring(0, 3).toLowerCase()] : null,
				year: res[7] ? +res[7] : null
			};
		}
	};

	Datetime.countRegex = /^(\d+)$/;

	Datetime.isCount = function (s) {
		return Datetime.countRegex.test(s);
	};

	Datetime.parseCount = function (s) {
		var res = Datetime.countRegex.exec(s);

		if (null === res) {
			return null;
		} else {
			return +res[1];
		}
	};

	Datetime.isParsable = function (s) {
		return Datetime.isDhms(s) || Datetime.isDt(s) || Datetime.isCount(s);
	};

	Datetime.makeDatetime = function (s) {

		if (Datetime.isCount(s)) {
			return new Date(Datetime.minuteCount * Datetime.parseCount(s));
		} else if (Datetime.isDhms(s)) {
			var dhms = Datetime.parseDhms(s);

			return new Date(Datetime.dayCount * dhms.day + Datetime.hourCount * dhms.hour + Datetime.minuteCount * dhms.minute + Datetime.secondCount * dhms.second);
		} else if (Datetime.isDt(s)) {
			var dt = Datetime.parseDt(s),
				now = new Date();

			if (!dt.year) {
				dt.year = now.getFullYear();
			}

			if (!dt.day) {
				var testDate = new Date();
				testDate.setHours(dt.hour);
				testDate.setMinutes(dt.minute);
				testDate.setSeconds(dt.second);
				if (testDate < now) {
					dt.day = now.getDate() + 1;
				}
				dt.month = now.getMonth();
			}

			return new Date(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second);
		} else {
			return null;
		}
	};

	Datetime.shortPurals = [
		['d', 'd'],
		['h', 'h'],
		['m', 'm'],
		['s', 's'],
		['ms', 'ms']
	];

	Datetime.longPurals = [
		['day', 'days'],
		['hour', 'hours'],
		['minute', 'minutes'],
		['second', 'seconds'],
		['millisecond', 'milliseconds']
	];

	Datetime.displayString = function (time, precision, purals) {
		time = Math.abs(time);
		precision = precision || 'second';
		var dhmsMapping = {
				day: 0,
				hour: 1,
				minute: 2,
				second: 3,
				millisecond: 4
			},
			dhms = [
				Math.floor(time / Datetime.dayCount),
				Math.floor(time / Datetime.hourCount) % 24,
				Math.floor(time / Datetime.minuteCount) % 60,
				Math.floor(time / Datetime.secondCount) % 60,
				time % 1000
			],
			purals = purals || Datetime.longPurals,
			firstNonZero = 0;

		return dhms.map(function (val, index) {
			if ((val === 0 && firstNonZero === 0 && index < dhmsMapping[precision]) || index > dhmsMapping[precision]) {
				return false;
			} else {
				firstNonZero = index;
				return val + ' ' + (1 === val ? purals[index][0] : purals[index][1]);
			}
		}).filter(function (str) {return str;}).join(' ');
	};

	Datetime.shortDtFormat = 'HH:mm:ss';
	Datetime.longDtFormat = 'dd/MM/yyyy HH:mm:ss';

	var pad0 = function (n) {
		return n < 10 ? '0' + n : n;
	};

	var subFormat = function (pattern, sub, map) {
		map = map || function (val) {return val;};
		return function (format) {
			return format.split(pattern).map(map).join(sub);
		};
	};

	Datetime.shortString = function (date, format) {
		format = format || Datetime.shortDtFormat;
		if ('number' === typeof date)
			date = new Date(date);

		return subFormat('yyyy', date.getFullYear(),
			subFormat('MM', pad0(date.getMonth() + 1),
				subFormat('dd', pad0(date.getDate()),
					subFormat('HH', pad0(date.getHours()),
						subFormat('mm', pad0(date.getMinutes()),
							subFormat('ss', pad0(date.getSeconds()))
						)
					)
				)
			)
		)(format);
	};

	root.Datetime = Datetime;
}.call(this));
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2012 Andreas Madsen
 * MIT License
 */

var common = require('../common'),
	thintalk = require(common.modulePath),
    assert = require('assert'),
    child_process = require('child_process');

module.exports = function (layer) {
	var lisenter = thintalk({
		add: function (a, b) {
			this.callback(a + b);
		},

		fail: function () {
			throw "error";
		}
	});

	var requesterProcess = child_process.fork(common.fixture('requester'), [layer]);

	if (layer === 'IPC') {
		lisenter.listen(layer, requesterProcess);
	} else if (layer === 'TCP') {
		lisenter.listen(layer, common.PORT);
	}

	lisenter.on('listening', function () {
		requesterProcess.send({what: 'connect'});
	});

	return [{
		// first batch of tests
		'when ready event is emitted': {
			topic: function () {
				var self = this;
				lisenter.on('listening', function () {
					self.callback(null, lisenter);
				});
			},

			'the online state is true': function (error, result) {
				assert.isTrue(result.online);
			}
		},

		'when connection has been made': {
			topic: function () {
				var self = this;
				lisenter.on('connection', function () {
					self.callback(null, lisenter);
				});
			},

			'connection event will emit': function (error, result) {
				assert.isNull(error);
			}
		}

	}, {
		'when we request a method there exist': {
			topic: function () {
				var self = this;
				requesterProcess.send({
					what: 'request',
					method: 'add',
					args : [2, 3]
				});

				lisenter.on('request', function (/* name, args, result */) {
					self.callback(this.error, arguments);
				});
			},

			'callback is called with no error': function (error, result) {
				assert.isNull(error);
			},

			'callback is called with name and args set': function (error, result) {
				assert.equal(result[0], 'add');
				assert.deepEqual(result[1], [2, 3]);
			},

			'callback is called with a result': function (error, result) {
				assert.equal(result[2], 6);
			}
		},

		'when we requiest a method there throw': {
			topic: function () {
				var self = this;
				requesterProcess.send({
					what: 'request',
					method: 'fail',
					args : [2, 3]
				});

				lisenter.on('request', function (/* name, args, result */) {
					self.callback(this.error, arguments);
				});
			},

			'callback is called with an error property': function (error, result) {
				assert.instanceOf(error, Error);
			},

			'callback is called with name and args set': function (error, result) {
				assert.equal(result[0], 'fail');
				assert.deepEqual(result[1], [2, 3]);
			},

			'callback is called without a result': function (error, result) {
				assert.isUndefined(result[2]);
				assert.length(result, 3);
			}
		}
	}, {
		// second batch of test
		// we will close in this batch so no more should follow

		'when closeing the lisenter': {
			topic: function () {
				var self = this;

				function close() {
					lisenter.removeListener('error', error);
					self.callback(null, lisenter);
				}

				function error(err) {
					lisenter.removeListener('close', close);
					self.callback(err, null);
				}

				lisenter.once('error', error);
				lisenter.once('close', close);
				lisenter.close();
			},

			'the close event will emit': function (error, result) {
				assert.ifError(error);
			},

			'the online state will become false': function (error, result) {
				assert.ifError(error);
				assert.isFalse(result.online);
			},

			'the requester will': {
				topic: function () {
					var self = this;

					requesterProcess.send({
						what: 'closed'
					});
					requesterProcess.on('message', function(msg) {
						if (msg.what === 'closed') {
							self.callback(msg.error, msg.online);
						}
					});
				},

				'emit close': function (error, result) {
					assert.ifError(error);
					assert.equal(result, false);
				}
			}
		},

		teardown: function () {
			// this will execute when all contents has been tested
			requesterProcess.kill();
		}
	}];
};<commit_msg>[testcase] comment purpose of batches<commit_after>/**
 * Copyright (c) 2012 Andreas Madsen
 * MIT License
 */

var common = require('../common'),
	thintalk = require(common.modulePath),
    assert = require('assert'),
    child_process = require('child_process');

module.exports = function (layer) {
	var lisenter = thintalk({
		add: function (a, b) {
			this.callback(a + b);
		},

		fail: function () {
			throw "error";
		}
	});

	var requesterProcess = child_process.fork(common.fixture('requester'), [layer]);

	if (layer === 'IPC') {
		lisenter.listen(layer, requesterProcess);
	} else if (layer === 'TCP') {
		lisenter.listen(layer, common.PORT);
	}

	lisenter.on('listening', function () {
		requesterProcess.send({what: 'connect'});
	});

	return [{
		// first batch of tests
		'when ready event is emitted': {
			topic: function () {
				var self = this;
				lisenter.on('listening', function () {
					self.callback(null, lisenter);
				});
			},

			'the online state is true': function (error, result) {
				assert.isTrue(result.online);
			}
		},

		'when connection has been made': {
			topic: function () {
				var self = this;
				lisenter.on('connection', function () {
					self.callback(null, lisenter);
				});
			},

			'connection event will emit': function (error, result) {
				assert.isNull(error);
			}
		}

	}, {
		// Second batch
		// we can assume the connection has been made

		'when we request a method there exist': {
			topic: function () {
				var self = this;
				requesterProcess.send({
					what: 'request',
					method: 'add',
					args : [2, 3]
				});

				lisenter.on('request', function (/* name, args, result */) {
					self.callback(this.error, arguments);
				});
			},

			'callback is called with no error': function (error, result) {
				assert.isNull(error);
			},

			'callback is called with name and args set': function (error, result) {
				assert.equal(result[0], 'add');
				assert.deepEqual(result[1], [2, 3]);
			},

			'callback is called with a result': function (error, result) {
				assert.equal(result[2], 6);
			}
		},

		'when we requiest a method there throw': {
			topic: function () {
				var self = this;
				requesterProcess.send({
					what: 'request',
					method: 'fail',
					args : [2, 3]
				});

				lisenter.on('request', function (/* name, args, result */) {
					self.callback(this.error, arguments);
				});
			},

			'callback is called with an error property': function (error, result) {
				assert.instanceOf(error, Error);
			},

			'callback is called with name and args set': function (error, result) {
				assert.equal(result[0], 'fail');
				assert.deepEqual(result[1], [2, 3]);
			},

			'callback is called without a result': function (error, result) {
				assert.isUndefined(result[2]);
				assert.length(result, 3);
			}
		}
	}, {
		// Third batch of test
		// we will close in this batch so no more should follow

		'when closeing the lisenter': {
			topic: function () {
				var self = this;

				function close() {
					lisenter.removeListener('error', error);
					self.callback(null, lisenter);
				}

				function error(err) {
					lisenter.removeListener('close', close);
					self.callback(err, null);
				}

				lisenter.once('error', error);
				lisenter.once('close', close);
				lisenter.close();
			},

			'the close event will emit': function (error, result) {
				assert.ifError(error);
			},

			'the online state will become false': function (error, result) {
				assert.ifError(error);
				assert.isFalse(result.online);
			},

			'the requester will': {
				topic: function () {
					var self = this;

					requesterProcess.send({
						what: 'closed'
					});
					requesterProcess.on('message', function(msg) {
						if (msg.what === 'closed') {
							self.callback(msg.error, msg.online);
						}
					});
				},

				'emit close': function (error, result) {
					assert.ifError(error);
					assert.equal(result, false);
				}
			}
		},

		teardown: function () {
			// this will execute when all contents has been tested
			requesterProcess.kill();
		}
	}];
};<|endoftext|>


<commit_before>'use strict';

var JID = require('node-xmpp-core').JID,
    ldap = require('ldapjs'),
    winston = require('winston'),
    logger = winston.loggers.get('authentication');

function LDAP(settings) {
    this.settings = settings || {};

    // LDAP settings
    this.suffix = settings.suffix;
    this.uidTag = settings.uidTag;

    // LDAP client
    this.client = ldap.createClient({
        url: settings.url,
        maxConnections: 5
    });
}

LDAP.prototype.name = 'LDAP';

LDAP.prototype.match = function (method) {
    if (method === 'PLAIN') {
        return true;
    } else {
        return false;
    }
};

LDAP.prototype.authenticateWithLDAP = function (username, password, callback) {
    // check that we got username and password
    if (!username || !password) {
        return callback(new Error('parameters are missing'));
    }

    // generate unix ldap like user name
    var uid = this.uidTag + '=' + username + ',' + this.suffix;

    // bind for authentication
    this.client.bind(uid, password, function (err) {
        // error
        if (err) {
            return callback(err);
        }

        callback(null, {
            uid: uid
        });
    });
};

LDAP.prototype.authenticate = function (opts, cb) {
    // generate ldap username
    var username = new JID(opts.jid.toString()).getLocal();
    logger.info('LDAP authenticate ' + opts.jid.toString());

    // authenticate with LDAP bind
    this.authenticateWithLDAP(username, opts.password, function (err, user) {
        if (err) {
            logger.error('Error with user ' + user + '. We got the following error: ' + err);
        }
        cb(err, opts);
    });
};

module.exports = LDAP;<commit_msg>output errors<commit_after>'use strict';

var JID = require('node-xmpp-core').JID,
    ldap = require('ldapjs'),
    winston = require('winston'),
    logger = winston.loggers.get('authentication');

function LDAP(settings) {
    this.settings = settings || {};

    // LDAP settings
    this.suffix = settings.suffix;
    this.uidTag = settings.uidTag;

    // LDAP client
    this.client = ldap.createClient({
        url: settings.url,
        maxConnections: 5
    });
}

LDAP.prototype.name = 'LDAP';

LDAP.prototype.match = function (method) {
    if (method === 'PLAIN') {
        return true;
    } else {
        return false;
    }
};

LDAP.prototype.authenticateWithLDAP = function (username, password, callback) {
    // check that we got username and password
    if (!username || !password) {
        return callback(new Error('parameters are missing'));
    }

    // generate unix ldap like user name
    var uid = this.uidTag + '=' + username + ',' + this.suffix;

    // bind for authentication
    this.client.bind(uid, password, function (err) {
        // error
        if (err) {
            logger.error(err);
            return callback(err);
        }

        callback(null, {
            uid: uid
        });
    });
};

LDAP.prototype.authenticate = function (opts, cb) {
    // generate ldap username
    var username = new JID(opts.jid.toString()).getLocal();
    logger.info('LDAP authenticate ' + opts.jid.toString());

    // authenticate with LDAP bind
    this.authenticateWithLDAP(username, opts.password, function (err, user) {
        if (err) {
            logger.error('Error with user ' + user + '. We got the following error: ' + err);
        }
        cb(err, opts);
    });
};

module.exports = LDAP;<|endoftext|>


<commit_before>import {TEST_SERVICE_NAME} from '../../environment'
import Automator from '../../util/cli-automator'

export default function () {

  this.When(/I launch a service from the CLI prompt$/, async function () {
    await Automator.spawn(['import']).waitForDone()

    const cmd = Automator.spawn(['launch'])
    await cmd.waitForLaunch()

    // key commands to select the service from the list
    await cmd.send('down')
    await cmd.send('space')
    await cmd.send('enter')

    await cmd.waitForDone()
  })

  this.When(/I launch a service from the CLI directly$/, async function () {
    await Automator.spawn(['import']).waitForDone()

    const cmd = Automator.spawn(['launch', TEST_SERVICE_NAME])
    await cmd.waitForDone()
  })

}
<commit_msg>Fix launch CLI test<commit_after>import {TEST_SERVICE_NAME} from '../../environment'
import Automator from '../../util/cli-automator'

export default function () {

  this.When(/I launch a service from the CLI prompt$/, async function () {
    await Automator.spawn(['import']).waitForDone()

    const cmd = Automator.spawn(['launch'])
    await cmd.waitForLaunch()

    // key commands to select the service from the list
    await cmd.send('space')
    await cmd.send('enter')

    await cmd.waitForDone()
  })

  this.When(/I launch a service from the CLI directly$/, async function () {
    await Automator.spawn(['import']).waitForDone()

    const cmd = Automator.spawn(['launch', TEST_SERVICE_NAME])
    await cmd.waitForDone()
  })

}
<|endoftext|>