<commit_before>'use strict';
angular.module('documents')

	.directive('documentMgr', ['_', 'moment', 'Authentication', 'DocumentMgrService', 'AlertService', 'ConfirmService', 'CodeLists', 'TreeModel', 'ProjectModel', 'Document', 'FolderModel', function (_, moment, Authentication, DocumentMgrService, AlertService, ConfirmService, CodeLists, TreeModel, ProjectModel, Document, FolderModel) {
		return {
			restrict: 'E',
			scope: {
				project: '=',
				file: '=',
				folder: '='
			},
			templateUrl: 'modules/documents/client/views/document-manager.html',
			controller: function ($scope, $filter, $log, $modal, $timeout, _, moment, Authentication, DocumentMgrService, CodeLists, TreeModel, ProjectModel, Document) {
				var tree = new TreeModel();
				var self = this;
				self.busy = true;
				self.requestOpenDir = null;
				self.requestOpenFileID = null;

				$scope.copyClipboardSuccess = function(e) {
					var txt = e.trigger.getAttribute('data-doc-name');
					AlertService.success('Link copied for ' + txt);
					e.clearSelection();
				};

				$scope.copyClipboardError = function(e) {
					AlertService.error('Copy link failed.');
				};

				if ($scope.file) {
					self.requestOpenFileID = $scope.file; // objectId
				}
				if ($scope.folder) {
					try {
						self.requestOpenDir = parseInt($scope.folder); // tree Id
					} catch (e) {
						console.log("couldn't parse directory");
					}
				}

				$scope.authentication = Authentication;
				$scope.documentTypes = CodeLists.documentTypes;

				ProjectModel.getProjectDirectory($scope.project)
				.then( function (dir) {
					$scope.project.directoryStructure = dir || {
						id: 1,
						lastId: 1,
						name: 'ROOT',
						published: true
					};
					self.rootNode = tree.parse($scope.project.directoryStructure);
					if (self.requestOpenDir) {
						self.selectNode(self.requestOpenDir);
					} else if (self.requestOpenFileID) {
						self.gotoDoc(self.requestOpenFileID);
					} else {
						self.selectNode(self.rootNode);
					}

					$scope.$apply();
				});

				// default sort is by name ascending...
				self.sorting = {
					column: 'name',
					ascending: true
				};

				// self.rootNode = tree.parse($scope.project.directoryStructure);
				self.selectedNode = undefined;
				self.currentNode = undefined;
				self.currentPath = undefined;

				self.allChecked = false;
				self.checkedDirs = [];
				self.checkedFiles = [];
				self.lastChecked = {fileId: undefined, directoryID: undefined};

				self.unsortedDirs = [];

				self.currentFiles = [];
				self.currentDirs = [];

				self.batchMenuEnabled = false;

				self.infoPanel = {
					open: false,
					type: 'None',
					data: undefined,
					toggle: function() {
						self.infoPanel.open = !self.infoPanel.open;
					},
					close: function() {
						self.infoPanel.open = false;
					},
					reset: function() {
						//self.infoPanel.enabled = false;
						//self.infoPanel.open = false;
						self.infoPanel.type = 'None';
						self.infoPanel.data = undefined;
						self.infoPanel.link = undefined;
					},
					setData: function() {
						self.infoPanel.reset();
						// check to see if there is a single lastChecked item set first...
						if (self.lastChecked) {
							if (self.lastChecked.fileId) {
								self.infoPanel.type = 'File';
								var file = _.find(self.currentFiles, function(o) { return o._id.toString() === self.lastChecked.fileId; });
								self.infoPanel.data = file ? file : undefined;
								self.infoPanel.link =  window.location.protocol + '//' + window.location.host + '/api/document/'+ file._id+'/fetch';
							} else if (self.lastChecked.directoryID) {
								self.infoPanel.type = 'Directory';
								var node =_.find(self.currentDirs, function(o) { return o.model.id === self.lastChecked.directoryID; });
								self.infoPanel.data = node ? node.model : undefined;
							}
						} else {
							if (_.size(self.checkedDirs) + _.size(self.checkedFiles) > 1) {
								self.infoPanel.type = 'Multi';
								self.infoPanel.data = {
									checkedFiles: _.size(self.checkedFiles),
									checkedDirs: _.size(self.checkedDirs),
									totalFiles: _.size(self.currentFiles),
									totalDirs: _.size(self.currentDirs)
								}; // what to show here?
							}
						}
					}
				};

				self.sortBy = function(column) {
					//is this the current column?
					if (self.sorting.column.toLowerCase() === column.toLowerCase()){
						//so we reverse the order...
						self.sorting.ascending = !self.sorting.ascending;
					} else {
						// changing column, set to ascending...
						self.sorting.column = column.toLowerCase();
						self.sorting.ascending = true;
					}
					self.applySort();
				};

				self.applySort = function() {
					var direction = self.sorting.ascending ? 1 : -1;
					var collator = new Intl.Collator(undefined, {numeric: true, sensitivity: 'base'});
					function naturalSort(d1, d2) {
						var v = collator.compare(d1.displayName, d2.displayName);
						return v * direction;
					}
					if (self.sorting.column === 'name') {
						self.currentFiles.sort(naturalSort);
						self.currentDirs.sort(naturalSort);
					} else if (self.sorting.column === 'date') {
						self.currentFiles.sort(function(doc1, doc2){
							var d1 = doc1.dateUploaded || 0;
							var d2 = doc2.dateUploaded || 0;
							return (new Date(d1) - new Date(d2)) * direction;
						});
					} else if (self.sorting.column === 'pub') {
						self.currentFiles.sort(function(doc1, doc2){
							var d1 = doc1.isPublished ? 1 : 0;
							var d2 = doc2.isPublished ? 1 : 0;
							return (d1 - d2) * direction;
						});
					}
				};

				self.checkAll = function() {
					_.each(self.currentDirs, function(o) { o.selected = self.allChecked; });
					_.each(self.currentFiles, function(o) { o.selected = self.allChecked; });

					var doc;
					if (self.allChecked) {
						doc = _.last(self.currentFiles) || _.last(self.currentDirs);
					}

					self.syncCheckedItems(doc);
				};

				self.checkFile = function(doc) {
					// ADD/remove to the selected file list...
					self.syncCheckedItems(doc);
				};
				self.selectFile = function(doc) {
					// selected a file, make it the only item selected...
					var checked = doc.selected;
					_.each(self.currentDirs, function(o) { o.selected = false; });
					_.each(self.currentFiles, function(o) { o.selected = false; });
					doc.selected = !checked;
					self.syncCheckedItems(doc);
				};

				self.dblClick = function(doc){
					var pdfURL = window.location.protocol + "//" + window.location.host + "/api/document/" + doc._id + "/fetch";
					window.open(pdfURL, "_blank");
				};

				self.checkDir = function(doc) {
					self.syncCheckedItems(doc);
				};
				self.selectDir = function(doc) {
					// selected a dir, make it the only item selected...
					var checked = doc.selected;
					_.each(self.currentDirs, function(o) { o.selected = false; });
					_.each(self.currentFiles, function(o) { o.selected = false; });
					doc.selected = !checked;
					self.syncCheckedItems(doc);
				};
				self.openDir = function(doc) {
					//double clicked a dir, open it up!
					self.selectNode(doc.model.id);
				};

				self.gotoDoc = function(docID) {
					Document.lookup(docID) //docID is an objectID
					.then(function (doc) {
						self.selectNode(doc.directoryID, docID);
					});
				};

				// Select folder based on tree id (directoryID). If docID is present then select this doc.
				self.selectNode = function (nodeId, docID) {
					self.busy = true;
					var theNode = self.rootNode.first(function (n) {
						return n.model.id === nodeId;
					});
					if (!theNode) {
						theNode = self.rootNode;
					}

					self.currentNode = theNode; // this is the current Directory in the bread crumb basically...
					self.folderURL = window.location.protocol + "//" + window.location.host + "/p/" + $scope.project.code + "/docs?folder=" + self.currentNode.model.id;
					//self.currentPath = theNode.getPath() || [];
					self.unsortedDirs = [];
					self.currentFiles = [];
					self.currentDirs = [];

					var pathArray = theNode.getPath();
					_.each(pathArray, function (elem) {
						if (elem.model.id > 1) { //bail the root node cus we don't need to attatch the folderObj to it
							if (!elem.model.hasOwnProperty('folderObj')) { //trying to reduce the amount of API calls only by checking if node model does not have folderObj
								FolderModel.lookup($scope.project._id, elem.model.id)
								.then(function (folder) {
									elem.model.folderObj = folder;
								});
							}
						}
					});
					self.currentPath = pathArray || [];
					// path is published IFF all nodes are not unpublished.  This find returns first unpublished element.
					self.currentPathIsPublished = (undefined === _.find(self.currentPath, function(p) { return ! p.model.published; }));
					self.currentPathIsPublishedWarning = self.currentPathIsPublished ? null : "Warning. You will need to publish all parent folders to fully publish these document(s).";

					//$log.debug('currentNode (' + self.currentNode.model.name + ') get documents...');
					DocumentMgrService.getDirectoryDocuments($scope.project, self.currentNode.model.id)
					.then(
						function (result) {
							//$log.debug('...currentNode (' + self.currentNode.model.name + ') got '+ _.size(result.data ) + '.');

							self.currentFiles = _.map(result.data, function(f) {
								// making sure that the displayName is set...
								if (_.isEmpty(f.displayName)) {
									f.displayName = f.documentFileName || f.internalOriginalName;
								}
								f.link =  window.location.protocol + '//' + window.location.host + '/api/document/'+ f._id+'/fetch';

								if (_.isEmpty(f.dateUploaded) && !_.isEmpty(f.oldData)) {
									var od = JSON.parse(f.oldData);
									//console.log(od);
									try {
										f.dateUploaded = moment(od.WHEN_CREATED, "MM/DD/YYYY HH:mm").toDate();
									} catch(ex) {
										console.log('Error parsing WHEN_CREATED from oldData', JSON.stringify(f.oldData));
									}
								}
								return _.extend(f,{selected:  (_.find(self.checkedFiles, function(d) { return d._id.toString() === f._id.toString(); }) !== undefined), type: 'File'});
							});

							self.unsortedDirs = _.map(self.currentNode.children, function (n) {
								return _.extend(n,{selected: (_.find(self.checkedDirs, function(d) { return d.model.id === n.model.id; }) !== undefined), type: 'Directory'});
							});

							self.applySort();

							if (docID) {
								// search in the folder's document list to locate the target document
								_.each(self.currentFiles, function(doc) {
									if (doc._id === docID) {
										self.selectFile(doc);
										return;
									}
								});
							} else {
								// otherwise selected node (folder)
								self.selectedNode = self.currentNode;
							}

							// see what is currently checked
							self.syncCheckedItems();
							self.busy = false;
						},
						function (error) {
							$log.error('getDirectoryDocuments error: ', JSON.stringify(error));
							self.busy = false;
						}
					).then(function () {
						// Go through each of the currently available folders in view, and attach the object
						// to the model dynamically so that the permissions directive will work by using the
						// correct x-object=folderObject instead of a doc.
						FolderModel.lookupForProjectIn($scope.project._id, self.currentNode.model.id)
						.then(function (folder) {
							_.each(folder, function (fs) {
								// We do breadth-first because we like to talk to our neighbours before moving<commit_msg>Revert "EPIC-1112 - change sort order for display name to natural order."<commit_after>'use strict';
angular.module('documents')

	.directive('documentMgr', ['_', 'moment', 'Authentication', 'DocumentMgrService', 'AlertService', 'ConfirmService', 'CodeLists', 'TreeModel', 'ProjectModel', 'Document', 'FolderModel', function (_, moment, Authentication, DocumentMgrService, AlertService, ConfirmService, CodeLists, TreeModel, ProjectModel, Document, FolderModel) {
		return {
			restrict: 'E',
			scope: {
				project: '=',
				file: '=',
				folder: '='
			},
			templateUrl: 'modules/documents/client/views/document-manager.html',
			controller: function ($scope, $filter, $log, $modal, $timeout, _, moment, Authentication, DocumentMgrService, CodeLists, TreeModel, ProjectModel, Document) {
				var tree = new TreeModel();
				var self = this;
				self.busy = true;
				self.requestOpenDir = null;
				self.requestOpenFileID = null;

				$scope.copyClipboardSuccess = function(e) {
					var txt = e.trigger.getAttribute('data-doc-name');
					AlertService.success('Link copied for ' + txt);
					e.clearSelection();
				};

				$scope.copyClipboardError = function(e) {
					AlertService.error('Copy link failed.');
				};

				if ($scope.file) {
					self.requestOpenFileID = $scope.file; // objectId
				}
				if ($scope.folder) {
					try {
						self.requestOpenDir = parseInt($scope.folder); // tree Id
					} catch (e) {
						console.log("couldn't parse directory");
					}
				}

				$scope.authentication = Authentication;
				$scope.documentTypes = CodeLists.documentTypes;

				ProjectModel.getProjectDirectory($scope.project)
				.then( function (dir) {
					$scope.project.directoryStructure = dir || {
						id: 1,
						lastId: 1,
						name: 'ROOT',
						published: true
					};
					self.rootNode = tree.parse($scope.project.directoryStructure);
					if (self.requestOpenDir) {
						self.selectNode(self.requestOpenDir);
					} else if (self.requestOpenFileID) {
						self.gotoDoc(self.requestOpenFileID);
					} else {
						self.selectNode(self.rootNode);
					}

					$scope.$apply();
				});

				// default sort is by name ascending...
				self.sorting = {
					column: 'name',
					ascending: true
				};

				// self.rootNode = tree.parse($scope.project.directoryStructure);
				self.selectedNode = undefined;
				self.currentNode = undefined;
				self.currentPath = undefined;

				self.allChecked = false;
				self.checkedDirs = [];
				self.checkedFiles = [];
				self.lastChecked = {fileId: undefined, directoryID: undefined};

				self.unsortedFiles = [];
				self.unsortedDirs = [];

				self.currentFiles = [];
				self.currentDirs = [];

				self.batchMenuEnabled = false;

				self.infoPanel = {
					open: false,
					type: 'None',
					data: undefined,
					toggle: function() {
						self.infoPanel.open = !self.infoPanel.open;
					},
					close: function() {
						self.infoPanel.open = false;
					},
					reset: function() {
						//self.infoPanel.enabled = false;
						//self.infoPanel.open = false;
						self.infoPanel.type = 'None';
						self.infoPanel.data = undefined;
						self.infoPanel.link = undefined;
					},
					setData: function() {
						self.infoPanel.reset();
						// check to see if there is a single lastChecked item set first...
						if (self.lastChecked) {
							if (self.lastChecked.fileId) {
								self.infoPanel.type = 'File';
								var file = _.find(self.currentFiles, function(o) { return o._id.toString() === self.lastChecked.fileId; });
								self.infoPanel.data = file ? file : undefined;
								self.infoPanel.link =  window.location.protocol + '//' + window.location.host + '/api/document/'+ file._id+'/fetch';
							} else if (self.lastChecked.directoryID) {
								self.infoPanel.type = 'Directory';
								var node =_.find(self.currentDirs, function(o) { return o.model.id === self.lastChecked.directoryID; });
								self.infoPanel.data = node ? node.model : undefined;
							}
						} else {
							if (_.size(self.checkedDirs) + _.size(self.checkedFiles) > 1) {
								self.infoPanel.type = 'Multi';
								self.infoPanel.data = {
									checkedFiles: _.size(self.checkedFiles),
									checkedDirs: _.size(self.checkedDirs),
									totalFiles: _.size(self.currentFiles),
									totalDirs: _.size(self.currentDirs)
								}; // what to show here?
							}
						}
					}
				};

				self.sortBy = function(column) {
					//is this the current column?
					if (self.sorting.column.toLowerCase() === column.toLowerCase()){
						//so we reverse the order...
						self.sorting.ascending = !self.sorting.ascending;
					} else {
						// changing column, set to ascending...
						self.sorting.column = column.toLowerCase();
						self.sorting.ascending = true;
					}
					self.applySort();
				};

				self.applySort = function() {
					// sort ascending first...
					self.currentFiles = _(self.unsortedFiles).chain().sortBy(function (f) {
						// more making sure that the displayName is set...
						if (_.isEmpty(f.displayName)) {
							f.displayName = f.documentFileName || f.internalOriginalName;
						}
						f.order = f.order || 0;

						if (self.sorting.column === 'name') {
							return _.isEmpty(f.displayName) ? null : f.displayName.toLowerCase();
						} else if (self.sorting.column === 'author') {
							return _.isEmpty(f.documentAuthor) ? null : f.documentAuthor.toLowerCase();
						} else if (self.sorting.column === 'type') {
							return _.isEmpty(f.internalExt) ? null : f.internalExt.toLowerCase();
						} else if (self.sorting.column === 'size') {
							return _.isEmpty(f.internalExt) ? 0 : f.internalSize;
						} else if (self.sorting.column === 'date') {
							//date uploaded
							return _.isEmpty(f.dateUploaded) ? 0 : f.dateUploaded;
						} else if (self.sorting.column === 'pub') {
							//is published...
							return !f.isPublished;
						}
						// by name if none specified... or we incorrectly identified...
						return _.isEmpty(f.displayName) ? null : f.displayName.toLowerCase();
					}).sortBy(function (f) {
						return f.order;
					}).value();

					// directories always/only sorted by name
					self.currentDirs = _(self.unsortedDirs).chain().sortBy(function (d) {
						if (_.isEmpty(d.model.name)) {
							return null;
						}
						d.model.order = d.model.order || 0;
						return d.model.name.toLowerCase();
					}).sortBy(function (d) {
						return d.model.order;
					}).value();

					if (!self.sorting.ascending) {
						// and if we are not supposed to be ascending... then reverse it!
						self.currentFiles = _(self.currentFiles).reverse().value();
						if (self.sorting.column === 'name') {
							// name is the only sort that applies to Directories.
							// so if descending on name, then we need to reverse it.
							self.currentDirs = _(self.currentDirs).reverse().value();
						}
					}
				};

				self.checkAll = function() {
					_.each(self.currentDirs, function(o) { o.selected = self.allChecked; });
					_.each(self.currentFiles, function(o) { o.selected = self.allChecked; });

					var doc;
					if (self.allChecked) {
						doc = _.last(self.currentFiles) || _.last(self.currentDirs);
					}

					self.syncCheckedItems(doc);
				};

				self.checkFile = function(doc) {
					// ADD/remove to the selected file list...
					self.syncCheckedItems(doc);
				};
				self.selectFile = function(doc) {
					// selected a file, make it the only item selected...
					var checked = doc.selected;
					_.each(self.currentDirs, function(o) { o.selected = false; });
					_.each(self.currentFiles, function(o) { o.selected = false; });
					doc.selected = !checked;
					self.syncCheckedItems(doc);
				};

				self.dblClick = function(doc){
					var pdfURL = window.location.protocol + "//" + window.location.host + "/api/document/" + doc._id + "/fetch";
					window.open(pdfURL, "_blank");
				};

				self.checkDir = function(doc) {
					self.syncCheckedItems(doc);
				};
				self.selectDir = function(doc) {
					// selected a dir, make it the only item selected...
					var checked = doc.selected;
					_.each(self.currentDirs, function(o) { o.selected = false; });
					_.each(self.currentFiles, function(o) { o.selected = false; });
					doc.selected = !checked;
					self.syncCheckedItems(doc);
				};
				self.openDir = function(doc) {
					//double clicked a dir, open it up!
					self.selectNode(doc.model.id);
				};

				self.gotoDoc = function(docID) {
					Document.lookup(docID) //docID is an objectID
					.then(function (doc) {
						self.selectNode(doc.directoryID, docID);
					});
				};

				// Select folder based on tree id (directoryID). If docID is present then select this doc.
				self.selectNode = function (nodeId, docID) {
					self.busy = true;
					var theNode = self.rootNode.first(function (n) {
						return n.model.id === nodeId;
					});
					if (!theNode) {
						theNode = self.rootNode;
					}

					self.currentNode = theNode; // this is the current Directory in the bread crumb basically...
					self.folderURL = window.location.protocol + "//" + window.location.host + "/p/" + $scope.project.code + "/docs?folder=" + self.currentNode.model.id;
					//self.currentPath = theNode.getPath() || [];
					self.unsortedFiles = [];
					self.unsortedDirs = [];
					self.currentFiles = [];
					self.currentDirs = [];

					var pathArray = theNode.getPath();
					_.each(pathArray, function (elem) {
						if (elem.model.id > 1) { //bail the root node cus we don't need to attatch the folderObj to it
							if (!elem.model.hasOwnProperty('folderObj')) { //trying to reduce the amount of API calls only by checking if node model does not have folderObj
								FolderModel.lookup($scope.project._id, elem.model.id)
								.then(function (folder) {
									elem.model.folderObj = folder;
								});
							}
						}
					});
					self.currentPath = pathArray || [];
					// path is published IFF all nodes are not unpublished.  This find returns first unpublished element.
					self.currentPathIsPublished = (undefined === _.find(self.currentPath, function(p) { return ! p.model.published; }));
					self.currentPathIsPublishedWarning = self.currentPathIsPublished ? null : "Warning. You will need to publish all parent folders to fully publish these document(s).";

					//$log.debug('currentNode (' + self.currentNode.model.name + ') get documents...');
					DocumentMgrService.getDirectoryDocuments($scope.project, self.currentNode.model.id)
					.then(
						function (result) {
							//$log.debug('...currentNode (' + self.currentNode.model.name + ') got '+ _.size(result.data ) + '.');

							self.unsortedFiles = _.map(result.data, function(f) {
								// making sure that the displayName is set...
								if (_.isEmpty(f.displayName)) {
									f.displayName = f.documentFileName || f.internalOriginalName;
								}
								f.link =  window.location.protocol + '//' + window.location.host + '/api/document/'+ f._id+'/fetch';

								if (_.isEmpty(f.dateUploaded) && !_.isEmpty(f.oldData)) {
									var od = JSON.parse(f.oldData);
									//console.log(od);
									try {
										f.dateUploaded = moment(od.WHEN_CREATED, "MM/DD/YYYY HH:mm").toDate();
									} catch(ex) {
										console.log('Error parsing WHEN_CREATED from oldData', JSON.stringify(f.oldData));
									}
								}
								return _.extend(f,{selected:  (_.find(self.checkedFiles, function(d) { return d._id.toString() === f._id.toString(); }) !== undefined), type: 'File'});
							});

							self.unsortedDirs = _.map(self.currentNode.children, function (n) {
								return _.extend(n,{selected: (_.find(self.checkedDirs, function(d) { return d.model.id === n.model.id; }) !== undefined), type: 'Directory'});
							});

							self.applySort();

							if (docID) {
								// search in the folder's document list to locate the target document
								_.each(self.unsortedFiles, function(doc) {
									if (doc._id === docID) {
										self.selectFile(doc);
										return;
									}
								});
							} else {
								// otherwise selected node (folder)
								self.selectedNode = self.currentNode;
							}

							// see what is currently checked
							self.syncCheckedItems();
							self.busy = false;
						},
						function (error) {
							$log.error('getDirectoryDocuments error: ', JSON.stringify(error));
							self.busy = false;
						}
					).then(function () {
						// Go through each of the currently available folders in view, and attach the object
						// to the model dynamically so that the permissions directive will work by using the
						// correct x-object=folderObject instead of a doc.
						FolderModel.lookupForProjectIn($scope.project._id, self.currentNode.model.id)
						.then(function (folder) {
							_.each(folder, function (fs) {
								// We do breadth-first because we like to talk to our neighbours before moving
{'fraction_non_alphanumeric': 0.14428996615974232, 'fraction_numerical': 0.0017939413707342927, 'mean_word_length': 2.5527230590961763, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '138695', 'n_tokens_mistral': 10189, 'n_tokens_neox': 7336, 'n_words': 1665}