diff --git a/.gitignore b/.gitignore index 954207ceec..faa1b92a94 100755 --- a/.gitignore +++ b/.gitignore @@ -1,91 +1,103 @@ -/dist/ -/build/ -/*/build/ -*/nbproject/private/* -/nbproject/private/* - -/Core/release/ -/Core/src/org/sleuthkit/autopsy/coreutils/Version.properties -/Core/src/org/sleuthkit/autopsy/casemodule/docs/QuickStart.html -/Core/src/org/sleuthkit/autopsy/casemodule/docs/screenshot.png -/Core/src/org/sleuthkit/autopsy/datamodel/ranges.csv -/Core/build/ -/Core/dist/ -/Core/nbproject/* -/Core/test/qa-functional/data/* -!/Core/nbproject/project.xml -!/Core/nbproject/project.properties - -/CoreLibs/release/ -/CoreLibs/build/ -/CoreLibs/dist/ -/CoreLibs/nbproject/* -!/CoreLibs/nbproject/project.xml -!/CoreLibs/nbproject/project.properties - -/Core/test/qa-functional/data/ - -/KeywordSearch/release/ -/KeywordSearch/build/ -/KeywordSearch/dist/ -/KeywordSearch/nbproject/* -!/KeywordSearch/nbproject/project.xml -!/KeywordSearch/nbproject/project.properties - -*/genfiles.properties -genfiles.properties - -/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties -/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties -/branding/build/ -/branding/dist/ -/branding/nbproject/* -!/branding/nbproject/project.xml -!/branding/nbproject/project.properties - -/test/input/* -!/test/input/notablehashes.txt-md5.idx -!/test/input/notablekeywords.xml -!/test/input/NSRL.txt-md5.idx -/test/output/* -!/test/output/gold -/test/script/output_dir_link.txt -/test/output/gold/tmp -/test/script/ScriptLog.txt -/test/script/__pycache__/ -/test/script/*.pyc -/test/script/DBDump-Diff.txt -/test/script/DBDump.txt -/test/script/SortedData-Diff.txt -/test/script/SortedData.txt -/test/script/myconfig.xml -/test/script/*/*.xml -/test/build/ -/test/dist/ -/test/nbproject/* - -!/Testing/nbproject/project.xml -!/Testing/nbproject/project.properties -*~ -/netbeans-plat -/docs/doxygen-user/user-docs -/jdiff-javadocs/* -/jdiff-logs/* -/gen_version.txt -hs_err_pid*.log - -.DS_Store -.*.swp -/Experimental/release/ -/ImageGallery/release/ -/thunderbirdparser/release/ -/RecentActivity/release/ -/CentralRepository/release/ -/Tika/release - -.idea/ -*.iml - -*.img -*.vhd -*.E01 +/dist/ +/build/ +/*/build/ +*/nbproject/private/* +/nbproject/private/* + +/Core/release/ +/Core/src/org/sleuthkit/autopsy/coreutils/Version.properties +/Core/src/org/sleuthkit/autopsy/casemodule/docs/QuickStart.html +/Core/src/org/sleuthkit/autopsy/casemodule/docs/screenshot.png +/Core/src/org/sleuthkit/autopsy/datamodel/ranges.csv +/Core/build/ +/Core/dist/ +/Core/nbproject/* +/Core/test/qa-functional/data/* +!/Core/nbproject/project.xml +!/Core/nbproject/project.properties + +/CoreLibs/release/ +/CoreLibs/build/ +/CoreLibs/dist/ +/CoreLibs/nbproject/* +!/CoreLibs/nbproject/project.xml +!/CoreLibs/nbproject/project.properties + +/Core/test/qa-functional/data/ + +/KeywordSearch/release/ +/KeywordSearch/build/ +/KeywordSearch/dist/ +/KeywordSearch/nbproject/* +!/KeywordSearch/nbproject/project.xml +!/KeywordSearch/nbproject/project.properties + +*/genfiles.properties +genfiles.properties + +/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties +/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties +/branding/build/ +/branding/dist/ +/branding/nbproject/* +!/branding/nbproject/project.xml +!/branding/nbproject/project.properties + +/test/input/* +!/test/input/notablehashes.txt-md5.idx +!/test/input/notablekeywords.xml +!/test/input/NSRL.txt-md5.idx +/test/output/* +!/test/output/gold +/test/script/output_dir_link.txt +/test/output/gold/tmp +/test/script/ScriptLog.txt +/test/script/__pycache__/ +/test/script/*.pyc +/test/script/DBDump-Diff.txt +/test/script/DBDump.txt +/test/script/SortedData-Diff.txt +/test/script/SortedData.txt +/test/script/myconfig.xml +/test/script/*/*.xml +/test/build/ +/test/dist/ +/test/nbproject/* + +!/Testing/nbproject/project.xml +!/Testing/nbproject/project.properties +*~ +/netbeans-plat +/docs/doxygen-user/user-docs +/jdiff-javadocs/* +/jdiff-logs/* +/gen_version.txt +hs_err_pid*.log + +.DS_Store +.*.swp +/Experimental/release/ +/ImageGallery/release/ +/thunderbirdparser/release/ +/RecentActivity/release/ +/CentralRepository/release/ +/Tika/release + +.idea/ +*.iml + +*.img +*.vhd +*.E01 + +/thirdparty/yara/yarabridge/yarabridge/x64/ +/thirdparty/yara/yarabridge/yarabridge.VC.db +/thirdparty/yara/yarabridge/yarabridge.VC.VC.opendb +/thirdparty/yara/yarabridge/x64/ +/thirdparty/yara/YaraWrapperTest/nbproject/private/ +/thirdparty/yara/YaraWrapperTest/build/ +/thirdparty/yara/YaraJNIWrapper/dist/ +/thirdparty/yara/YaraJNIWrapper/build/ +/thirdparty/yara/YaraJNIWrapper/nbproject/private/ +/thirdparty/yara/yarabridge/.vs/ + diff --git a/.travis.yml b/.travis.yml index dd6b50f34a..6c06754d37 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ jobs: - os: linux dist: bionic - os: osx + osx_image: xcode12.2 env: global: diff --git a/Core/build.xml b/Core/build.xml index e54c4eda97..73bda54795 100644 --- a/Core/build.xml +++ b/Core/build.xml @@ -54,6 +54,11 @@ + + + + + @@ -98,6 +103,11 @@ + + + + + @@ -118,8 +128,8 @@ tofile="${ext.dir}/sleuthkit-${TSK_VERSION}.jar"/> - + - + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/nbproject/project.properties b/Core/nbproject/project.properties index 0c83dc776e..cd50517eab 100644 --- a/Core/nbproject/project.properties +++ b/Core/nbproject/project.properties @@ -99,7 +99,7 @@ file.reference.opencensus-api-0.19.2.jar=release\\modules\\ext\\opencensus-api-0 file.reference.opencensus-contrib-grpc-metrics-0.19.2.jar=release\\modules\\ext\\opencensus-contrib-grpc-metrics-0.19.2.jar file.reference.opencensus-contrib-http-util-0.19.2.jar=release\\modules\\ext\\opencensus-contrib-http-util-0.19.2.jar file.reference.opennlp-tools-1.9.1.jar=release\\modules\\ext\\opennlp-tools-1.9.1.jar -file.reference.postgresql-9.4.1211.jre7.jar=release\\modules\\ext\\postgresql-9.4.1211.jre7.jar +file.reference.postgresql-42.2.18.jar=release\\modules\\ext\\postgresql-42.2.18.jar file.reference.proto-google-cloud-translate-v3beta1-0.53.0.jar=release\\modules\\ext\\proto-google-cloud-translate-v3beta1-0.53.0.jar file.reference.proto-google-common-protos-1.15.0.jar=release\\modules\\ext\\proto-google-common-protos-1.15.0.jar file.reference.proto-google-iam-v1-0.12.0.jar=release\\modules\\ext\\proto-google-iam-v1-0.12.0.jar @@ -118,6 +118,7 @@ file.reference.StixLib.jar=release\\modules\\ext\\StixLib.jar file.reference.threetenbp-1.3.3.jar=release\\modules\\ext\\threetenbp-1.3.3.jar file.reference.webp-imageio-sejda-0.1.0.jar=release\\modules\\ext\\webp-imageio-sejda-0.1.0.jar file.reference.xmpcore-5.1.3.jar=release\\modules\\ext\\xmpcore-5.1.3.jar +file.reference.YaraJNIWrapper.jar=release/modules/ext/YaraJNIWrapper.jar file.reference.zookeeper-3.4.6.jar=release\\modules\\ext\\zookeeper-3.4.6.jar javac.source=1.8 javac.compilerargs=-Xlint -Xlint:-serial diff --git a/Core/nbproject/project.xml b/Core/nbproject/project.xml index 2518563e0e..074d930725 100644 --- a/Core/nbproject/project.xml +++ b/Core/nbproject/project.xml @@ -336,6 +336,7 @@ org.sleuthkit.autopsy.textextractors.configs org.sleuthkit.autopsy.textsummarizer org.sleuthkit.autopsy.texttranslation + org.sleuthkit.autopsy.url.analytics org.sleuthkit.datamodel org.sleuthkit.datamodel.blackboardutils org.sleuthkit.datamodel.blackboardutils.attributes @@ -436,6 +437,10 @@ ext/commons-codec-1.11.jar release\modules\ext\commons-codec-1.11.jar + + ext/postgresql-42.2.18.jar + release\modules\ext\postgresql-42.2.18.jar + ext/commons-pool2-2.4.2.jar release\modules\ext\commons-pool2-2.4.2.jar @@ -548,18 +553,10 @@ ext/checker-compat-qual-2.5.3.jar release\modules\ext\checker-compat-qual-2.5.3.jar - - ext/sleuthkit-4.10.1.jar - release/modules/ext/sleuthkit-4.10.1.jar - ext/animal-sniffer-annotations-1.17.jar release\modules\ext\animal-sniffer-annotations-1.17.jar - - ext/sleuthkit-caseuco-4.10.1.jar - release/modules/ext/sleuthkit-caseuco-4.10.1.jar - ext/gax-1.44.0.jar release\modules\ext\gax-1.44.0.jar @@ -568,6 +565,10 @@ ext/jsoup-1.10.3.jar release\modules\ext\jsoup-1.10.3.jar + + ext/YaraJNIWrapper.jar + release/modules/ext/YaraJNIWrapper.jar + ext/grpc-context-1.19.0.jar release\modules\ext\grpc-context-1.19.0.jar @@ -672,6 +673,10 @@ ext/grpc-alts-1.19.0.jar release\modules\ext\grpc-alts-1.19.0.jar + + ext/sleuthkit-caseuco-4.10.1.jar + release/modules/ext/sleuthkit-caseuco-4.10.1.jar + ext/jdom-2.0.5.jar release\modules\ext\jdom-2.0.5.jar @@ -724,10 +729,6 @@ ext/jai_imageio-1.1.jar release\modules\ext\jai_imageio-1.1.jar - - ext/postgresql-9.4.1211.jre7.jar - release\modules\ext\postgresql-9.4.1211.jre7.jar - ext/junit-3.8.1.jar release\modules\ext\junit-3.8.1.jar @@ -796,6 +797,10 @@ ext/sevenzipjbinding-AllPlatforms.jar release\modules\ext\sevenzipjbinding-AllPlatforms.jar + + ext/sleuthkit-4.10.1.jar + release/modules/ext/sleuthkit-4.10.1.jar + ext/jutf7-1.0.0.jar release\modules\ext\jutf7-1.0.0.jar diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties index 204e475ef3..9019887a68 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties @@ -146,13 +146,14 @@ AddImageWizardIngestConfigPanel.CANCEL_BUTTON.text=Cancel NewCaseVisualPanel1.CaseFolderOnCDriveError.text=Warning: Path to multi-user case folder is on \"C:\" drive NewCaseVisualPanel1.CaseFolderOnInternalDriveWindowsError.text=Warning: Path to case folder is on \"C:\" drive. Case folder is created on the target system NewCaseVisualPanel1.CaseFolderOnInternalDriveLinuxError.text=Warning: Path to case folder is on the target system. Create case folder in mounted drive. +NewCaseVisualPanel1.uncPath.error=Error: UNC paths are not allowed for Single-User cases CollaborationMonitor.addingDataSourceStatus.msg={0} adding data source CollaborationMonitor.analyzingDataSourceStatus.msg={0} analyzing {1} MissingImageDialog.lbWarning.text= MissingImageDialog.lbWarning.toolTipText= NewCaseVisualPanel1.caseParentDirWarningLabel.text= -NewCaseVisualPanel1.multiUserCaseRadioButton.text=Multi-user -NewCaseVisualPanel1.singleUserCaseRadioButton.text=Single-user +NewCaseVisualPanel1.multiUserCaseRadioButton.text=Multi-User +NewCaseVisualPanel1.singleUserCaseRadioButton.text=Single-User NewCaseVisualPanel1.caseTypeLabel.text=Case Type: SingleUserCaseConverter.BadDatabaseFileName=Database file does not exist! SingleUserCaseConverter.AlreadyMultiUser=Case is already multi-user! @@ -253,3 +254,7 @@ UnpackagePortableCaseProgressDialog.resultLabel.text=resultLabel UnpackagePortableCaseDialog.extractLabel.text=Folder to extract to: UnpackagePortableCaseDialog.caseLabel.text=Portable Case: NewCaseVisualPanel1.caseDataStoredLabel.text_1=Case data will be stored in the following directory: +SolrNotConfiguredDialog.okButton.text=OK +SolrNotConfiguredDialog.title=Solr 8 Server Not Configured +SolrNotConfiguredDialog.EmptyKeywordSearchHostName=Solr 8 connection parameters are not configured. Please go to Tools->Options->Multi User. +SolrNotConfiguredDialog.messageLabel.text=Multi-User cases are enabled but Solr 8 server has not been configured.
\nNew cases can only be created with Solr 8. Please go to Tools->Options->Multi User.\n diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED index f5c18a1640..e59c37fea7 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED @@ -471,3 +471,7 @@ UnpackagePortableCaseProgressDialog.resultLabel.text=resultLabel UnpackagePortableCaseDialog.extractLabel.text=Folder to extract to: UnpackagePortableCaseDialog.caseLabel.text=Portable Case: NewCaseVisualPanel1.caseDataStoredLabel.text_1=Case data will be stored in the following directory: +SolrNotConfiguredDialog.okButton.text=OK +SolrNotConfiguredDialog.title=Solr 8 Server Not Configured +SolrNotConfiguredDialog.EmptyKeywordSearchHostName=Solr 8 connection parameters are not configured. Please go to Tools->Options->Multi User. +SolrNotConfiguredDialog.messageLabel.text=Multi-User cases are enabled but Solr 8 server has not been configured.
\nNew cases can only be created with Solr 8. Please go to Tools->Options->Multi User.\n diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java index 76f328a825..4c6126fcb7 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java @@ -469,7 +469,7 @@ public class ImageFilePanel extends JPanel implements DocumentListener { return false; } - if (!PathValidator.isValidForMultiUserCase(path, Case.getCurrentCase().getCaseType())) { + if (!PathValidator.isValidForCaseType(path, Case.getCurrentCase().getCaseType())) { errorLabel.setVisible(true); errorLabel.setText(Bundle.ImageFilePanel_validatePanel_dataSourceOnCDriveError()); } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.java index 9faa17f51c..4dcdc5260f 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.java @@ -89,8 +89,15 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel { return; } - if (CURRENT_CASE == Case.Events.valueOf(evt.getPropertyName())) { - refresh(); + // Check whether we have a case open or case close event. + if ((CURRENT_CASE == Case.Events.valueOf(evt.getPropertyName()))) { + if (evt.getNewValue() != null) { + // Case open + refresh(); + } else { + // Case close + reset(); + } } }); } @@ -124,7 +131,7 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel { */ private void refresh() { try { - if (Case.isCaseOpen()) { + if (Case.isCaseOpen()) { // Note - this will generally return true when handling a case close event SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); this.ingestJobs = skCase.getIngestJobs(); setDataSource(selectedDataSource); @@ -138,6 +145,14 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel { JOptionPane.showMessageDialog(this, Bundle.IngestJobInfoPanel_loadIngestJob_error_text(), Bundle.IngestJobInfoPanel_loadIngestJob_error_title(), JOptionPane.ERROR_MESSAGE); } } + + /** + * Reset the panel. + */ + private void reset() { + this.ingestJobs = new ArrayList<>(); + setDataSource(null); + } @Messages({"IngestJobInfoPanel.IngestJobTableModel.StartTime.header=Start Time", "IngestJobInfoPanel.IngestJobTableModel.EndTime.header=End Time", diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesPanel.java index 6703b16dc5..d55dab831e 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesPanel.java @@ -290,7 +290,7 @@ final class LocalFilesPanel extends javax.swing.JPanel { final Case.CaseType currentCaseType = Case.getCurrentCaseThrows().getCaseType(); for (String currentPath : pathsList) { - if (!PathValidator.isValidForMultiUserCase(currentPath, currentCaseType)) { + if (!PathValidator.isValidForCaseType(currentPath, currentCaseType)) { errorLabel.setVisible(true); errorLabel.setText(Bundle.LocalFilesPanel_pathValidation_dataSourceOnCDriveError()); return; diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalEvidenceFilePanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalEvidenceFilePanel.java index 103fd8fd6c..6d45be07b9 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalEvidenceFilePanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalEvidenceFilePanel.java @@ -191,7 +191,7 @@ final class LogicalEvidenceFilePanel extends javax.swing.JPanel implements Docum } // display warning if there is one (but don't disable "next" button) try { - if (!PathValidator.isValidForMultiUserCase(path, Case.getCurrentCaseThrows().getCaseType())) { + if (!PathValidator.isValidForCaseType(path, Case.getCurrentCaseThrows().getCaseType())) { errorLabel.setVisible(true); errorLabel.setText(Bundle.LogicalEvidenceFilePanel_pathValidation_dataSourceOnCDriveError()); return false; diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java index b9ac003d02..32812b56ce 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2018 Basis Technology Corp. + * Copyright 2011-2020 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -28,7 +28,6 @@ import javax.swing.JPanel; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import org.sleuthkit.autopsy.casemodule.Case.CaseType; -import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.PathValidator; import org.sleuthkit.autopsy.coreutils.PlatformUtil; @@ -149,13 +148,22 @@ final class NewCaseVisualPanel1 extends JPanel implements DocumentListener { private void validateSettings() { /** * Check the base case directory for the selected case type and show a - * warning if it is a dubious choice. + * warning if it is a dubious choice. For single user cases, disable + * the "Next" button if path is invalid. */ caseParentDirWarningLabel.setVisible(false); String parentDir = getCaseParentDir(); - if (!PathValidator.isValidForMultiUserCase(parentDir, getCaseType())) { - caseParentDirWarningLabel.setVisible(true); - caseParentDirWarningLabel.setText(NbBundle.getMessage(this.getClass(), "NewCaseVisualPanel1.CaseFolderOnCDriveError.text")); + if (!PathValidator.isValidForCaseType(parentDir, getCaseType())) { + if (getCaseType() == CaseType.MULTI_USER_CASE) { + caseParentDirWarningLabel.setVisible(true); + caseParentDirWarningLabel.setText(NbBundle.getMessage(this.getClass(), "NewCaseVisualPanel1.CaseFolderOnCDriveError.text")); + } else { + // disable the "Next" button + caseParentDirWarningLabel.setVisible(true); + caseParentDirWarningLabel.setText(NbBundle.getMessage(this.getClass(), "NewCaseVisualPanel1.uncPath.error")); + wizPanel.setIsFinish(false); + return; + } } /** diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/SolrNotConfiguredDialog.form b/Core/src/org/sleuthkit/autopsy/casemodule/SolrNotConfiguredDialog.form new file mode 100755 index 0000000000..15bf89bc47 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/SolrNotConfiguredDialog.form @@ -0,0 +1,78 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/SolrNotConfiguredDialog.java b/Core/src/org/sleuthkit/autopsy/casemodule/SolrNotConfiguredDialog.java new file mode 100755 index 0000000000..3adf647596 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/SolrNotConfiguredDialog.java @@ -0,0 +1,110 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * 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. + */ +package org.sleuthkit.autopsy.casemodule; + +import java.awt.Dimension; +import java.awt.Toolkit; +import javax.swing.JFrame; +import org.openide.util.ImageUtilities; +import org.openide.windows.WindowManager; + +/** + * A dialog to notify the user on startup when Solr 8 server is not configured. + */ +class SolrNotConfiguredDialog extends javax.swing.JDialog { + + private static final long serialVersionUID = 1L; + + /** + * Creates new form SolrNotConfiguredDialog + */ + SolrNotConfiguredDialog() { + super((JFrame) WindowManager.getDefault().getMainWindow(), true); + // Center the startup window. + Dimension screenDimension = Toolkit.getDefaultToolkit().getScreenSize(); + int width = getSize().width; + int height = getSize().height; + setLocation((screenDimension.width - width) / 2, (screenDimension.height - height) / 2); + initComponents(); + setIconImage(ImageUtilities.loadImage("org/sleuthkit/autopsy/images/warning16.png", false)); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + okButton = new javax.swing.JButton(); + messageLabel = new javax.swing.JLabel(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + setTitle(org.openide.util.NbBundle.getMessage(SolrNotConfiguredDialog.class, "SolrNotConfiguredDialog.title")); // NOI18N + setModal(true); + setName("toolsNotFound"); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(okButton, org.openide.util.NbBundle.getMessage(SolrNotConfiguredDialog.class, "SolrNotConfiguredDialog.okButton.text")); // NOI18N + okButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + okButtonActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(messageLabel, org.openide.util.NbBundle.getMessage(SolrNotConfiguredDialog.class, "SolrNotConfiguredDialog.messageLabel.text")); // NOI18N + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addGap(0, 15, Short.MAX_VALUE) + .addComponent(messageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 420, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(layout.createSequentialGroup() + .addGap(189, 189, 189) + .addComponent(okButton) + .addGap(0, 193, Short.MAX_VALUE))) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(messageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(okButton) + .addContainerGap()) + ); + + pack(); + }// //GEN-END:initComponents + + private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed + this.dispose(); + }//GEN-LAST:event_okButtonActionPerformed + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel messageLabel; + private javax.swing.JButton okButton; + // End of variables declaration//GEN-END:variables +} diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/StartupWindowProvider.java b/Core/src/org/sleuthkit/autopsy/casemodule/StartupWindowProvider.java index b532405ef9..9b62bdb0e0 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/StartupWindowProvider.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/StartupWindowProvider.java @@ -23,11 +23,15 @@ import java.util.Iterator; import java.util.logging.Level; import org.netbeans.spi.sendopts.OptionProcessor; import org.openide.util.Lookup; +import org.openide.util.NbBundle; import org.sleuthkit.autopsy.commandlineingest.CommandLineIngestManager; import org.sleuthkit.autopsy.commandlineingest.CommandLineOpenCaseManager; import org.sleuthkit.autopsy.commandlineingest.CommandLineOptionProcessor; import org.sleuthkit.autopsy.commandlineingest.CommandLineStartupWindow; +import org.sleuthkit.autopsy.core.RuntimeProperties; +import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; /** * Provides the start up window to rest of the application. It may return the @@ -76,6 +80,10 @@ public class StartupWindowProvider implements StartupWindowInterface { } } + if (RuntimeProperties.runningWithGUI()) { + checkSolr(); + } + //discover the registered windows Collection startupWindows = Lookup.getDefault().lookupAll(StartupWindowInterface.class); @@ -115,6 +123,21 @@ public class StartupWindowProvider implements StartupWindowInterface { } } + private void checkSolr() { + + // if Multi-User settings are enabled and Solr8 server is not configured, + // display an error message and a dialog + if (UserPreferences.getIsMultiUserModeEnabled() && UserPreferences.getIndexingServerHost().isEmpty()) { + // Solr 8 host name is not configured. This could be the first time user + // runs Autopsy with Solr 8. Display a message. + MessageNotifyUtil.Notify.error(NbBundle.getMessage(CueBannerPanel.class, "SolrNotConfiguredDialog.title"), + NbBundle.getMessage(SolrNotConfiguredDialog.class, "SolrNotConfiguredDialog.EmptyKeywordSearchHostName")); + + SolrNotConfiguredDialog dialog = new SolrNotConfiguredDialog(); + dialog.setVisible(true); + } + } + /** * Checks whether Autopsy is running from command line. There is an * OptionProcessor that is responsible for processing command line inputs. diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CoordinationServiceUtils.java b/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CoordinationServiceUtils.java index d4b0399ff8..c10e6b6fd2 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CoordinationServiceUtils.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/multiusercases/CoordinationServiceUtils.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2019-2019 Basis Technology Corp. + * Copyright 2020 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoAccount.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoAccount.java index 88c74a4fe8..e0b97dee53 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoAccount.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoAccount.java @@ -40,6 +40,7 @@ public final class CentralRepoAccount { private final CentralRepoAccountType accountType; // type specific unique account identifier + // Stores what is in the DB which should have been normalized before insertion. private final String typeSpecificIdentifier; /** @@ -106,7 +107,7 @@ public final class CentralRepoAccount { } - public CentralRepoAccount(long accountId, CentralRepoAccountType accountType, String typeSpecificIdentifier) { + CentralRepoAccount(long accountId, CentralRepoAccountType accountType, String typeSpecificIdentifier) { this.accountId = accountId; this.accountType = accountType; this.typeSpecificIdentifier = typeSpecificIdentifier; @@ -115,6 +116,8 @@ public final class CentralRepoAccount { /** * Gets unique identifier (assigned by a provider) for the account. Example * includes an email address, a phone number, or a website username. + * + * This is the normalized for of the ID. * * @return type specific account id. */ @@ -336,7 +339,7 @@ public final class CentralRepoAccount { normalizedAccountIdentifier = accountIdentifier.toLowerCase(); } } catch (CorrelationAttributeNormalizationException ex) { - throw new InvalidAccountIDException("Invalid account identifier", ex); + throw new InvalidAccountIDException(String.format("Account id normaization failed, invalid account identifier %s", accountIdentifier), ex); } return normalizedAccountIdentifier; diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoDbManager.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoDbManager.java index b0f288673b..d3a0e61ecf 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoDbManager.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoDbManager.java @@ -382,6 +382,31 @@ public class CentralRepoDbManager { CentralRepoDbUtil.setUseCentralRepo(true); saveNewCentralRepo(); } + + /** + * Set up a PostgresDb using the settings for the given database choice + * enum. + * + * @param choice Type of postgres DB to set up + * @throws CentralRepoException + */ + public void setupPostgresDb(CentralRepoDbChoice choice) throws CentralRepoException { + selectedDbChoice = choice; + DatabaseTestResult curStatus = testStatus(); + if (curStatus == DatabaseTestResult.DB_DOES_NOT_EXIST) { + createDb(); + curStatus = testStatus(); + } + + // the only successful setup status is tested ok + if (curStatus != DatabaseTestResult.TESTED_OK) { + throw new CentralRepoException("Unable to successfully create postgres database. Test failed with: " + curStatus); + } + + // if successfully got here, then save the settings + CentralRepoDbUtil.setUseCentralRepo(true); + saveNewCentralRepo(); + } /** * This method returns if changes to the central repository configuration diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeUtil.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeUtil.java index 88f76c393c..4867b4eb0a 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeUtil.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationAttributeUtil.java @@ -49,6 +49,15 @@ public class CorrelationAttributeUtil { private static final Logger logger = Logger.getLogger(CorrelationAttributeUtil.class.getName()); private static final List domainsToSkip = Arrays.asList("localhost", "127.0.0.1"); + // artifact ids that specifically have a TSK_DOMAIN attribute that should be handled by CR + private static Set DOMAIN_ARTIFACT_TYPE_IDS = new HashSet<>(Arrays.asList( + ARTIFACT_TYPE.TSK_WEB_BOOKMARK.getTypeID(), + ARTIFACT_TYPE.TSK_WEB_COOKIE.getTypeID(), + ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID(), + ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID(), + ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID() + )); + /** * Gets a string that is expected to be the same string that is stored in * the correlation_types table in the central repository as the display name @@ -68,40 +77,39 @@ public class CorrelationAttributeUtil { // Most notably, does not include KEYWORD HIT, CALLLOGS, MESSAGES, CONTACTS // TSK_INTERESTING_ARTIFACT_HIT (See JIRA-6129 for more details on the // interesting artifact hit). - // IMPORTANT: This set should be updated for new artifacts types that need to // be inserted into the CR. - private static final Set SOURCE_TYPES_FOR_CR_INSERT = new HashSet() {{ - add(ARTIFACT_TYPE.TSK_WEB_BOOKMARK.getTypeID()); - add(ARTIFACT_TYPE.TSK_WEB_COOKIE.getTypeID()); - add(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID()); - add(ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID()); - add(ARTIFACT_TYPE.TSK_DEVICE_ATTACHED.getTypeID()); - add(ARTIFACT_TYPE.TSK_WIFI_NETWORK.getTypeID()); - add(ARTIFACT_TYPE.TSK_WIFI_NETWORK_ADAPTER.getTypeID()); - add(ARTIFACT_TYPE.TSK_BLUETOOTH_PAIRING.getTypeID()); - add(ARTIFACT_TYPE.TSK_BLUETOOTH_ADAPTER.getTypeID()); - add(ARTIFACT_TYPE.TSK_DEVICE_INFO.getTypeID()); - add(ARTIFACT_TYPE.TSK_SIM_ATTACHED.getTypeID()); - add(ARTIFACT_TYPE.TSK_WEB_FORM_ADDRESS.getTypeID()); - add(ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()); - }}; + private static final Set SOURCE_TYPES_FOR_CR_INSERT = new HashSet() { + { + addAll(DOMAIN_ARTIFACT_TYPE_IDS); + + add(ARTIFACT_TYPE.TSK_DEVICE_ATTACHED.getTypeID()); + add(ARTIFACT_TYPE.TSK_WIFI_NETWORK.getTypeID()); + add(ARTIFACT_TYPE.TSK_WIFI_NETWORK_ADAPTER.getTypeID()); + add(ARTIFACT_TYPE.TSK_BLUETOOTH_PAIRING.getTypeID()); + add(ARTIFACT_TYPE.TSK_BLUETOOTH_ADAPTER.getTypeID()); + add(ARTIFACT_TYPE.TSK_DEVICE_INFO.getTypeID()); + add(ARTIFACT_TYPE.TSK_SIM_ATTACHED.getTypeID()); + add(ARTIFACT_TYPE.TSK_WEB_FORM_ADDRESS.getTypeID()); + add(ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()); + } + }; /** * Makes zero to many correlation attribute instances from the attributes of * artifacts that have correlatable data. The intention of this method is to - * use the results to save to the CR, not to correlate with them. If you - * want to correlate, please use makeCorrAttrsForCorrelation. An artifact that can - * have correlatable data != An artifact that should be the source of data - * in the CR, so results may be un-necessarily incomplete. - * - * @param artifact An artifact. + * use the results to save to the CR, not to correlate with them. If you + * want to correlate, please use makeCorrAttrsForCorrelation. An artifact + * that can have correlatable data != An artifact that should be the source + * of data in the CR, so results may be un-necessarily incomplete. + * + * @param artifact An artifact. * * @return A list, possibly empty, of correlation attribute instances for - * the artifact. + * the artifact. */ public static List makeCorrAttrsToSave(BlackboardArtifact artifact) { - if(SOURCE_TYPES_FOR_CR_INSERT.contains(artifact.getArtifactTypeID())) { + if (SOURCE_TYPES_FOR_CR_INSERT.contains(artifact.getArtifactTypeID())) { // Restrict the correlation attributes to use for saving. // The artifacts which are suitable for saving are a subset of the // artifacts that are suitable for correlating. @@ -114,10 +122,10 @@ public class CorrelationAttributeUtil { /** * Makes zero to many correlation attribute instances from the attributes of * artifacts that have correlatable data. The intention of this method is to - * use the results to correlate with, not to save. If you - * want to save, please use makeCorrAttrsToSave. An artifact that can - * have correlatable data != An artifact that should be the source of data - * in the CR, so results may be too lenient. + * use the results to correlate with, not to save. If you want to save, + * please use makeCorrAttrsToSave. An artifact that can have correlatable + * data != An artifact that should be the source of data in the CR, so + * results may be too lenient. * * IMPORTANT: The correlation attribute instances are NOT added to the * central repository by this method. @@ -130,10 +138,10 @@ public class CorrelationAttributeUtil { * whether receiving a null return value is an error or not, plus null * checking is easy to forget, while catching exceptions is enforced. * - * @param artifact An artifact. + * @param artifact An artifact. * * @return A list, possibly empty, of correlation attribute instances for - * the artifact. + * the artifact. */ public static List makeCorrAttrsForCorrelation(BlackboardArtifact artifact) { List correlationAttrs = new ArrayList<>(); @@ -146,13 +154,10 @@ public class CorrelationAttributeUtil { if (setNameAttr != null && CorrelationAttributeUtil.getEmailAddressAttrDisplayName().equals(setNameAttr.getValueString())) { makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_KEYWORD, CorrelationAttributeInstance.EMAIL_TYPE_ID); } - } else if (artifactTypeID == ARTIFACT_TYPE.TSK_WEB_BOOKMARK.getTypeID() - || artifactTypeID == ARTIFACT_TYPE.TSK_WEB_COOKIE.getTypeID() - || artifactTypeID == ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID() - || artifactTypeID == ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID()) { + } else if (DOMAIN_ARTIFACT_TYPE_IDS.contains(artifactTypeID)) { BlackboardAttribute domainAttr = sourceArtifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_DOMAIN)); if ((domainAttr != null) - && ! domainsToSkip.contains(domainAttr.getValueString())) { + && !domainsToSkip.contains(domainAttr.getValueString())) { makeCorrAttrFromArtifactAttr(correlationAttrs, sourceArtifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN, CorrelationAttributeInstance.DOMAIN_TYPE_ID); } } else if (artifactTypeID == ARTIFACT_TYPE.TSK_DEVICE_ATTACHED.getTypeID()) { @@ -192,12 +197,10 @@ public class CorrelationAttributeUtil { } catch (CorrelationAttributeNormalizationException ex) { logger.log(Level.WARNING, String.format("Error normalizing correlation attribute (%s)", artifact), ex); // NON-NLS return correlationAttrs; - } - catch (InvalidAccountIDException ex) { + } catch (InvalidAccountIDException ex) { logger.log(Level.WARNING, String.format("Invalid account identifier (artifactID: %d)", artifact.getId())); // NON-NLS return correlationAttrs; - } - catch (CentralRepoException ex) { + } catch (CentralRepoException ex) { logger.log(Level.SEVERE, String.format("Error querying central repository (%s)", artifact), ex); // NON-NLS return correlationAttrs; } catch (TskCoreException ex) { @@ -259,11 +262,11 @@ public class CorrelationAttributeUtil { * @param artifact An artifact. * * @return The associated artifact if the input artifact is a - * "meta-artifact", otherwise the input artifact. + * "meta-artifact", otherwise the input artifact. * * @throws NoCurrentCaseException If there is no open case. - * @throws TskCoreException If there is an error querying thew case - * database. + * @throws TskCoreException If there is an error querying thew case + * database. */ private static BlackboardArtifact getCorrAttrSourceArtifact(BlackboardArtifact artifact) throws NoCurrentCaseException, TskCoreException { BlackboardArtifact sourceArtifact = null; @@ -287,7 +290,7 @@ public class CorrelationAttributeUtil { * repository by this method. * * @param corrAttrInstances A list of correlation attribute instances. - * @param acctArtifact An account artifact. + * @param acctArtifact An account artifact. * * @return The correlation attribute instance. */ @@ -296,11 +299,11 @@ public class CorrelationAttributeUtil { // Get the account type from the artifact BlackboardAttribute accountTypeAttribute = acctArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE)); String accountTypeStr = accountTypeAttribute.getValueString(); - + // @@TODO Vik-6136: CR currently does not know of custom account types. // Ensure there is a predefined account type for this account. Account.Type predefinedAccountType = Account.Type.PREDEFINED_ACCOUNT_TYPES.stream().filter(type -> type.getTypeName().equalsIgnoreCase(accountTypeStr)).findAny().orElse(null); - + // do not create any correlation attribute instance for a Device account if (Account.Type.DEVICE.getTypeName().equalsIgnoreCase(accountTypeStr) == false && predefinedAccountType != null) { @@ -331,17 +334,14 @@ public class CorrelationAttributeUtil { * artifact. The correlation attribute instance is added to an input list. * * @param corrAttrInstances A list of correlation attribute instances. - * @param artifact An artifact. - * @param artAttrType The type of the atrribute of the artifact that - * is to be made into a correlatin attribute - * instance. - * @param typeId The type ID for the desired correlation - * attribute instance. + * @param artifact An artifact. + * @param artAttrType The type of the atrribute of the artifact that is to + * be made into a correlatin attribute instance. + * @param typeId The type ID for the desired correlation attribute instance. * * @throws CentralRepoException If there is an error querying the central - * repository. - * @throws TskCoreException If there is an error querying the case - * database. + * repository. + * @throws TskCoreException If there is an error querying the case database. */ private static void makeCorrAttrFromArtifactAttr(List corrAttrInstances, BlackboardArtifact artifact, ATTRIBUTE_TYPE artAttrType, int typeId) throws CentralRepoException, TskCoreException { BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(artAttrType)); @@ -359,9 +359,9 @@ public class CorrelationAttributeUtil { /** * Makes a correlation attribute instance of a given type from an artifact. * - * @param artifact The artifact. + * @param artifact The artifact. * @param correlationType the correlation attribute type. - * @param value The correlation attribute value. + * @param value The correlation attribute value. * * TODO (Jira-6088): The methods in this low-level, utility class should * throw exceptions instead of logging them. The reason for this is that the @@ -422,7 +422,7 @@ public class CorrelationAttributeUtil { * checking is easy to forget, while catching exceptions is enforced. * * @return The correlation attribute instance or null, if no such - * correlation attribute instance was found or an error occurred. + * correlation attribute instance was found or an error occurred. */ public static CorrelationAttributeInstance getCorrAttrForFile(AbstractFile file) { diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PersonaAccount.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PersonaAccount.java index 917c988e1c..afbb2fe4b5 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PersonaAccount.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PersonaAccount.java @@ -46,7 +46,7 @@ public class PersonaAccount { private final long dateAdded; private final CentralRepoExaminer examiner; - public PersonaAccount(long id, Persona persona, CentralRepoAccount account, String justification, Persona.Confidence confidence, long dateAdded, CentralRepoExaminer examiner) { + private PersonaAccount(long id, Persona persona, CentralRepoAccount account, String justification, Persona.Confidence confidence, long dateAdded, CentralRepoExaminer examiner) { this.id = id; this.persona = persona; this.account = account; @@ -326,12 +326,12 @@ public class PersonaAccount { */ public static Collection getPersonaAccountsForAccount(Account account) throws CentralRepoException { String querySQL = PERSONA_ACCOUNTS_QUERY_CLAUSE - + " WHERE LOWER(accounts.account_unique_identifier) LIKE LOWER(?)" + + " WHERE LOWER(accounts.account_unique_identifier) = LOWER(?)" + " AND type_name = ?" + " AND personas.status_id != ?"; List queryParams = new ArrayList<>(); - queryParams.add("%" + account.getTypeSpecificID() + "%"); // substring match + queryParams.add(account.getTypeSpecificID()); // substring match queryParams.add(account.getAccountType().getTypeName()); queryParams.add(Persona.PersonaStatus.DELETED.getStatusId()); diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresCentralRepo.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresCentralRepo.java index f1afb941bc..e72bfa9359 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresCentralRepo.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresCentralRepo.java @@ -198,11 +198,11 @@ final class PostgresCentralRepo extends RdbmsCentralRepo { if (connectionPool == null) { setupConnectionPool(); } - } - try { - return connectionPool.getConnection(); - } catch (SQLException ex) { - throw new CentralRepoException("Error getting connection from connection pool.", Bundle.PostgresEamDb_connectionFailed_message(), ex); // NON-NLS + try { + return connectionPool.getConnection(); + } catch (SQLException ex) { + throw new CentralRepoException("Error getting connection from connection pool.", Bundle.PostgresEamDb_connectionFailed_message(), ex); // NON-NLS + } } } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresCentralRepoSettings.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresCentralRepoSettings.java index 165d4ca6e0..dfb855a0b2 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresCentralRepoSettings.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/PostgresCentralRepoSettings.java @@ -121,10 +121,12 @@ public final class PostgresCentralRepoSettings implements CentralRepoDbConnectiv * @return */ String getConnectionURL(boolean usePostgresDb) { - StringBuilder url = new StringBuilder(); - url.append(getJDBCBaseURI()); - url.append(getHost()); - url.append("/"); // NON-NLS + StringBuilder url = new StringBuilder() + .append(getJDBCBaseURI()) + .append(getHost()) + .append(":") // NON-NLS + .append(getPort()) + .append("/"); // NON-NLS if (usePostgresDb) { url.append("postgres"); // NON-NLS } else { @@ -153,7 +155,7 @@ public final class PostgresCentralRepoSettings implements CentralRepoDbConnectiv } catch (ClassNotFoundException | SQLException ex) { // TODO: Determine why a connection failure (ConnectionException) re-throws // the SQLException and does not print this log message? - LOGGER.log(Level.SEVERE, "Failed to acquire ephemeral connection to postgresql."); // NON-NLS + LOGGER.log(Level.SEVERE, "Failed to acquire ephemeral connection to postgresql.", ex); // NON-NLS conn = null; } return conn; diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/Bundle.properties-MERGED index d4cca6c407..e95a759c4f 100755 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/Bundle.properties-MERGED @@ -1,4 +1,10 @@ caseeventlistener.evidencetag=Evidence +CentralRepositoryNotificationDialog.bulletHeader=This data is used to: +CentralRepositoryNotificationDialog.bulletOne=Ignore common items (files, domains, and accounts) +CentralRepositoryNotificationDialog.bulletThree=Create personas that group accounts +CentralRepositoryNotificationDialog.bulletTwo=Identify where an item was previously seen +CentralRepositoryNotificationDialog.finalRemarks=To limit what is stored, use the Central Repository options panel. +CentralRepositoryNotificationDialog.header=Autopsy stores data about each case in its Central Repository. IngestEventsListener.ingestmodule.name=Central Repository IngestEventsListener.prevCaseComment.text=Previous Case: # {0} - typeName @@ -7,6 +13,3 @@ IngestEventsListener.prevCount.text=Number of previous {0}: {1} IngestEventsListener.prevExists.text=Previously Seen Devices (Central Repository) IngestEventsListener.prevTaggedSet.text=Previously Tagged As Notable (Central Repository) Installer.centralRepoUpgradeFailed.title=Central repository disabled -Installer.initialCreateSqlite.messageDesc=It will store information about all hashes and identifiers that you process. You can use this to ignore previously seen files and make connections between cases. -Installer.initialCreateSqlite.messageHeader=The Central Repository is not enabled. Would you like to enable it? -Installer.initialCreateSqlite.title=Enable Central Repository? diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/CentralRepositoryNotificationDialog.java b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/CentralRepositoryNotificationDialog.java new file mode 100755 index 0000000000..883d979c90 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/CentralRepositoryNotificationDialog.java @@ -0,0 +1,73 @@ +/* + * Central Repository + * + * Copyright 2020 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * 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. + */ +package org.sleuthkit.autopsy.centralrepository.eventlisteners; + +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.core.RuntimeProperties; +import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; +import org.sleuthkit.autopsy.coreutils.Version; + +/** + * Notifies new installations or old upgrades that the central repository will + * be enabled by default. + */ +public class CentralRepositoryNotificationDialog { + + /** + * This dialog should display iff the mode is RELEASE and the + * application is running with a GUI. + */ + static boolean shouldDisplay() { + return Version.getBuildType() == Version.Type.RELEASE + && RuntimeProperties.runningWithGUI(); + } + + /** + * Displays an informational modal dialog to the user, which is dismissed by + * pressing 'OK'. + */ + @NbBundle.Messages({ + "CentralRepositoryNotificationDialog.header=Autopsy stores data about each case in its Central Repository.", + "CentralRepositoryNotificationDialog.bulletHeader=This data is used to:", + "CentralRepositoryNotificationDialog.bulletOne=Ignore common items (files, domains, and accounts)", + "CentralRepositoryNotificationDialog.bulletTwo=Identify where an item was previously seen", + "CentralRepositoryNotificationDialog.bulletThree=Create personas that group accounts", + "CentralRepositoryNotificationDialog.finalRemarks=To limit what is stored, use the Central Repository options panel." + }) + static void display() { + assert shouldDisplay(); + + MessageNotifyUtil.Message.info( + "" + + "" + + "
" + + "

" + Bundle.CentralRepositoryNotificationDialog_header() + "

" + + "

" + Bundle.CentralRepositoryNotificationDialog_bulletHeader() + "

" + + "
    " + + "
  • " + Bundle.CentralRepositoryNotificationDialog_bulletOne() + "
  • " + + "
  • " + Bundle.CentralRepositoryNotificationDialog_bulletTwo() + "
  • " + + "
  • " + Bundle.CentralRepositoryNotificationDialog_bulletThree() + "
  • " + + "
" + + "

" + Bundle.CentralRepositoryNotificationDialog_finalRemarks() + "

" + + "
" + + "" + + "" + ); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/Installer.java b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/Installer.java index d4f0253cd2..b2ef0d437e 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/Installer.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/Installer.java @@ -25,14 +25,13 @@ import javax.swing.JOptionPane; import javax.swing.SwingUtilities; import org.openide.modules.ModuleInstall; import org.openide.util.NbBundle; -import org.openide.windows.WindowManager; -import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbChoice; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoDbManager; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException; import org.sleuthkit.autopsy.core.RuntimeProperties; +import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ModuleSettings; -import org.sleuthkit.autopsy.coreutils.Version; /** * Adds/removes application event listeners responsible for adding data to the @@ -81,19 +80,10 @@ public class Installer extends ModuleInstall { * the org.sleuthkit.autopsy.core package when the already installed * Autopsy-Core module is restored (during application startup). */ - @NbBundle.Messages({ - "Installer.initialCreateSqlite.title=Enable Central Repository?", - "Installer.initialCreateSqlite.messageHeader=The Central Repository is not enabled. Would you like to enable it?", - "Installer.initialCreateSqlite.messageDesc=It will store information about all hashes and identifiers that you process. " - + "You can use this to ignore previously seen files and make connections between cases." - }) @Override public void restored() { addApplicationEventListeners(); - - if (Version.getBuildType() == Version.Type.RELEASE) { - setupDefaultCentralRepository(); - } + setupDefaultCentralRepository(); } /** @@ -107,9 +97,9 @@ public class Installer extends ModuleInstall { /** * Checks if the central repository has been set up and configured. If not, - * either offers to perform set up (running with a GUI) or does the set up - * unconditionally (not running with a GUI, e.g., in an automated ingest - * node). + * does the set up unconditionally. If the application is running with a + * GUI, a notification will be displayed to the user if the mode is RELEASE + * (in other words, developers are exempt from seeing the notification). */ private void setupDefaultCentralRepository() { Map centralRepoSettings = ModuleSettings.getConfigSettings("CentralRepository"); @@ -127,62 +117,30 @@ public class Installer extends ModuleInstall { ModuleSettings.setConfigSetting("CentralRepository", "initialized", "true"); } } - - // if central repository hasn't been previously initialized, initialize it - if (!initialized) { - // if running with a GUI, prompt the user - if (RuntimeProperties.runningWithGUI()) { - try { - SwingUtilities.invokeAndWait(() -> { - try { - String dialogText - = "" - + "
" - + "

" + NbBundle.getMessage(this.getClass(), "Installer.initialCreateSqlite.messageHeader") + "

" - + "

" + NbBundle.getMessage(this.getClass(), "Installer.initialCreateSqlite.messageDesc") + "

" - + "
" - + ""; - - if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(WindowManager.getDefault().getMainWindow(), - dialogText, - NbBundle.getMessage(this.getClass(), "Installer.initialCreateSqlite.title"), - JOptionPane.YES_NO_OPTION)) { - - setupDefaultSqliteCentralRepo(); - } - } catch (CentralRepoException ex) { - logger.log(Level.SEVERE, "There was an error while initializing the central repository database", ex); - - doMessageBoxIfRunningInGUI(ex); - } - }); - } catch (InterruptedException | InvocationTargetException ex) { - logger.log(Level.SEVERE, "There was an error while running the swing utility invoke later while creating the central repository database", ex); - } - } // if no GUI, just initialize - else { - try { - setupDefaultSqliteCentralRepo(); - } catch (CentralRepoException ex) { - logger.log(Level.SEVERE, "There was an error while initializing the central repository database", ex); - - doMessageBoxIfRunningInGUI(ex); - } - } - - ModuleSettings.setConfigSetting("CentralRepository", "initialized", "true"); + + if(initialized) { + return; // Nothing to do } - } - /** - * Sets up a default single-user SQLite central repository. - * - * @throws CentralRepoException If there is an error setting up teh central - * repository. - */ - private void setupDefaultSqliteCentralRepo() throws CentralRepoException { - CentralRepoDbManager manager = new CentralRepoDbManager(); - manager.setupDefaultSqliteDb(); + if (CentralRepositoryNotificationDialog.shouldDisplay()) { + CentralRepositoryNotificationDialog.display(); + } + + try { + CentralRepoDbManager manager = new CentralRepoDbManager(); + if (UserPreferences.getIsMultiUserModeEnabled()) { + // Set up using existing multi-user settings. + manager.setupPostgresDb(CentralRepoDbChoice.POSTGRESQL_MULTIUSER); + } else { + manager.setupDefaultSqliteDb(); + } + } catch (CentralRepoException ex) { + logger.log(Level.SEVERE, "There was an error while initializing the central repository database", ex); + + doMessageBoxIfRunningInGUI(ex); + } + + ModuleSettings.setConfigSetting("CentralRepository", "initialized", "true"); } /** diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/CentralRepoIngestModule.java b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/CentralRepoIngestModule.java index a7c3bd41e0..210e361261 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/CentralRepoIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/CentralRepoIngestModule.java @@ -67,7 +67,7 @@ final class CentralRepoIngestModule implements FileIngestModule { private static final String MODULE_NAME = CentralRepoIngestModuleFactory.getModuleName(); - static final boolean DEFAULT_FLAG_TAGGED_NOTABLE_ITEMS = true; + static final boolean DEFAULT_FLAG_TAGGED_NOTABLE_ITEMS = false; static final boolean DEFAULT_FLAG_PREVIOUS_DEVICES = false; static final boolean DEFAULT_CREATE_CR_PROPERTIES = true; diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties b/Core/src/org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties index 0016a075ee..d96b33357c 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties @@ -79,5 +79,5 @@ CreatePersonaAccountDialog.identifierTextField.text= CreatePersonaAccountDialog.identiferLbl.text=Identifier: CreatePersonaAccountDialog.okBtn.text=OK PersonasTopComponent.introText.text=Personas represent an online identity. They span cases and are stored in the Central Repository based on accounts that were found in artifacts. You can create, edit, and delete personas here. -PersonasTopComponent.cbFilterByKeyword.text=Filter personas by keyword +PersonasTopComponent.cbFilterByKeyword.text=Filter personas by name or account PersonaDetailsPanel.nameField.text= diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties-MERGED index a4a041c429..a7a8659131 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties-MERGED @@ -125,7 +125,7 @@ CreatePersonaAccountDialog.identifierTextField.text= CreatePersonaAccountDialog.identiferLbl.text=Identifier: CreatePersonaAccountDialog.okBtn.text=OK PersonasTopComponent.introText.text=Personas represent an online identity. They span cases and are stored in the Central Repository based on accounts that were found in artifacts. You can create, edit, and delete personas here. -PersonasTopComponent.cbFilterByKeyword.text=Filter personas by keyword +PersonasTopComponent.cbFilterByKeyword.text=Filter personas by name or account PersonaDetailsPanel.nameField.text= PersonasTopComponent_delete_confirmation_msg=Are you sure you want to delete this persona? PersonasTopComponent_delete_confirmation_Title=Are you sure? diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaDetailsDialog.form b/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaDetailsDialog.form index 70a0422367..d993262025 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaDetailsDialog.form +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaDetailsDialog.form @@ -8,6 +8,9 @@ + + + diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaDetailsDialog.java b/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaDetailsDialog.java index 94061921c1..b37a3d1c2b 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaDetailsDialog.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaDetailsDialog.java @@ -22,6 +22,7 @@ import java.awt.Component; import java.util.logging.Level; import javax.swing.JDialog; import javax.swing.JFrame; +import javax.swing.JOptionPane; import org.openide.util.NbBundle; import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.centralrepository.datamodel.Persona; @@ -38,6 +39,8 @@ public class PersonaDetailsDialog extends JDialog { private static final Logger logger = Logger.getLogger(PersonaDetailsDialog.class.getName()); private final PersonaDetailsDialogCallback callback; + + private String popupMessageOnStartup = ""; @NbBundle.Messages({ "PersonaDetailsDialogCreateTitle=Create Persona", @@ -91,6 +94,11 @@ public class PersonaDetailsDialog extends JDialog { pdp = new org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel(); setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + addWindowListener(new java.awt.event.WindowAdapter() { + public void windowOpened(java.awt.event.WindowEvent evt) { + formWindowOpened(evt); + } + }); org.openide.awt.Mnemonics.setLocalizedText(cancelBtn, org.openide.util.NbBundle.getMessage(PersonaDetailsDialog.class, "PersonaDetailsDialog.cancelBtn.text")); // NOI18N cancelBtn.setMaximumSize(new java.awt.Dimension(79, 23)); @@ -159,10 +167,20 @@ public class PersonaDetailsDialog extends JDialog { dispose(); }//GEN-LAST:event_cancelBtnActionPerformed + private void formWindowOpened(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowOpened + if(!popupMessageOnStartup.isEmpty()) { + JOptionPane.showMessageDialog(this, popupMessageOnStartup, "Persona Details", JOptionPane.INFORMATION_MESSAGE); + } + }//GEN-LAST:event_formWindowOpened + public PersonaDetailsPanel getDetailsPanel() { return this.pdp; } + public void setStartupPopupMessage(String message) { + popupMessageOnStartup = message; + } + // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton cancelBtn; private javax.swing.JScrollPane jScrollPane1; diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaDetailsPanel.form b/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaDetailsPanel.form index ad0a16878f..7d1bbcee85 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaDetailsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaDetailsPanel.form @@ -22,6 +22,9 @@ -->
+ + + diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaDetailsPanel.java b/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaDetailsPanel.java index bc4f6ec9c3..d8ce7e23b5 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaDetailsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/persona/PersonaDetailsPanel.java @@ -225,6 +225,10 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel { void addEditExistingAlias(PersonaAlias alias, String justification, Persona.Confidence confidence) { aliasesToEdit.put(alias, new PAlias(alias.getAlias(), justification, confidence)); } + + PersonaDetailsMode getMode() { + return mode; + } /** * A data bucket class for yet-to-be-created PersonaAccount @@ -384,6 +388,12 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel { casesTablePane = new javax.swing.JScrollPane(); casesTable = new javax.swing.JTable(); + addComponentListener(new java.awt.event.ComponentAdapter() { + public void componentShown(java.awt.event.ComponentEvent evt) { + formComponentShown(evt); + } + }); + org.openide.awt.Mnemonics.setLocalizedText(examinerLbl, org.openide.util.NbBundle.getMessage(PersonaDetailsPanel.class, "PersonaDetailsPanel.examinerLbl.text")); // NOI18N examinerField.setEditable(false); @@ -622,6 +632,10 @@ public final class PersonaDetailsPanel extends javax.swing.JPanel { ); }// //GEN-END:initComponents + private void formComponentShown(java.awt.event.ComponentEvent evt) {//GEN-FIRST:event_formComponentShown + + }//GEN-LAST:event_formComponentShown + // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JLabel accountsLbl; private javax.swing.JTable accountsTable; diff --git a/Core/src/org/sleuthkit/autopsy/commandlineingest/UserPreferences.java b/Core/src/org/sleuthkit/autopsy/commandlineingest/UserPreferences.java index 63c59ca852..f797a4cb1b 100755 --- a/Core/src/org/sleuthkit/autopsy/commandlineingest/UserPreferences.java +++ b/Core/src/org/sleuthkit/autopsy/commandlineingest/UserPreferences.java @@ -18,40 +18,14 @@ */ package org.sleuthkit.autopsy.commandlineingest; -import org.openide.util.NbPreferences; - /** * Provides convenient access to a UserPreferences node for user preferences * with default values. */ public final class UserPreferences { - private static final java.util.prefs.Preferences preferences = NbPreferences.forModule(UserPreferences.class); - private static final String COMMAND_LINE_MODE_RESULTS_FOLDER = "CommandLineModeResultsFolder"; // NON-NLS private static final String COMMAND_LINE_MODE_CONTEXT_STRING = "CommandLineModeContext"; // NON-NLS - // Prevent instantiation. - private UserPreferences() { - } - - /** - * Get results folder for command line mode from persistent storage. - * - * @return String Selected output folder. - */ - public static String getCommandLineModeResultsFolder() { - return preferences.get(COMMAND_LINE_MODE_RESULTS_FOLDER, ""); - } - - /** - * Set results folder for command line mode from persistent storage. - * - * @param folder Selected output folder. - */ - public static void setCommandLineModeResultsFolder(String folder) { - preferences.put(COMMAND_LINE_MODE_RESULTS_FOLDER, folder); - } - /** * Get context string for command line mode ingest module settings. * diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties index 6ecb170c10..32f2bf4849 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties @@ -28,3 +28,9 @@ SummaryViewer.contactsLabel.text=Book Entries: SummaryViewer.accountCountry.text= SummaryViewer.fileRefPane.border.title=File References in Current Case SummaryViewer.selectAccountFileRefLabel.text= SummaryViewer_FileRefNameColumn_Title=Path +SummaryViewer_Persona_Message= +SummaryViewer_Select_account_for_persona= +SummaryViewer.personaPanel.border.title=Personas +PersonaPanel.personaIDLabel.text=jLabel1 +SummaryPersonaPane.noPersonaLabel.text=No personas found +SummaryPersonaPane.messageLabel.text= +SummaryPersonaPane.createButton.text=Create +PersonaPanel.viewButton.text=View diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/PersonaPanel.form b/Core/src/org/sleuthkit/autopsy/communications/relationships/PersonaPanel.form new file mode 100755 index 0000000000..e76f4fe702 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/PersonaPanel.form @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/PersonaPanel.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/PersonaPanel.java new file mode 100755 index 0000000000..b6968f9cd2 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/PersonaPanel.java @@ -0,0 +1,115 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obt ain 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. + */ +package org.sleuthkit.autopsy.communications.relationships; + +import java.awt.Dimension; +import javax.swing.JButton; +import org.sleuthkit.autopsy.centralrepository.datamodel.Persona; + +/** + * + * Panel to show a single persona with a button for viewing persona details. + */ +public class PersonaPanel extends javax.swing.JPanel { + + private static final long serialVersionUID = 1L; + private final Persona persona; + + /** + * Creates new form PersonaPanel + */ + PersonaPanel(Persona persona) { + initComponents(); + this.persona = persona; + personaIDLabel.setText(persona.getName()); + } + + /** + * Returns the persona displayed by this panel. + * + * @return + */ + Persona getPersona() { + return persona; + } + + /** + * Returns the preferred width for the given persona label. + * + * @return + */ + int getPersonaLabelPreferedWidth() { + return personaIDLabel.getPreferredSize().width; + } + + /** + * Sets the preferred width for the persona name label. + * + * @param width + */ + void setPersonalLabelPreferredWidth(int width) { + Dimension currentDim = personaIDLabel.getPreferredSize(); + personaIDLabel.setPreferredSize(new Dimension(Math.max(currentDim.width, width), currentDim.height)); + } + + /** + * Returns the View button for this panel. + * + * @return + */ + JButton getViewButton() { + return viewButton; + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + java.awt.GridBagConstraints gridBagConstraints; + + personaIDLabel = new javax.swing.JLabel(); + viewButton = new javax.swing.JButton(); + + setLayout(new java.awt.GridBagLayout()); + + org.openide.awt.Mnemonics.setLocalizedText(personaIDLabel, org.openide.util.NbBundle.getMessage(PersonaPanel.class, "PersonaPanel.personaIDLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; + gridBagConstraints.insets = new java.awt.Insets(0, 5, 0, 0); + add(personaIDLabel, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(viewButton, org.openide.util.NbBundle.getMessage(PersonaPanel.class, "PersonaPanel.viewButton.text")); // NOI18N + viewButton.setMargin(new java.awt.Insets(0, 5, 0, 5)); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new java.awt.Insets(0, 5, 0, 0); + add(viewButton, gridBagConstraints); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel personaIDLabel; + private javax.swing.JButton viewButton; + // End of variables declaration//GEN-END:variables +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryPanelWorker.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryPanelWorker.java new file mode 100755 index 0000000000..bc6157f4cf --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryPanelWorker.java @@ -0,0 +1,146 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obt ain 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. + */ +package org.sleuthkit.autopsy.communications.relationships; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.logging.Level; +import javax.swing.SwingWorker; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount; +import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; +import org.sleuthkit.autopsy.centralrepository.datamodel.Persona; +import org.sleuthkit.autopsy.centralrepository.datamodel.PersonaAccount; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.Account; +import org.sleuthkit.datamodel.AccountFileInstance; +import org.sleuthkit.datamodel.InvalidAccountIDException; + +/** + * Runnable SwingWorker for gather the data that the Summary panel needs. + */ +class SummaryPanelWorker extends SwingWorker { + + private final static Logger logger = Logger.getLogger(SummaryPanelWorker.class.getName()); + + private final Account account; + + // Construct a instance + SummaryPanelWorker(Account account) { + this.account = account; + } + + /** + * Returns the account the worker is gathering data for. + * + * @return + */ + Account getAccount() { + return account; + } + + @Override + protected SummaryWorkerResults doInBackground() throws Exception { + CentralRepoAccount crAccount = null; + List stringList = new ArrayList<>(); + List accountFileInstanceList = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().getAccountFileInstances(account); + for (AccountFileInstance instance : accountFileInstanceList) { + stringList.add(instance.getFile().getUniquePath()); + } + + List personaList = new ArrayList<>(); + if (CentralRepository.isEnabled()) { + Collection personaAccountList = PersonaAccount.getPersonaAccountsForAccount(account); + PersonaAccount.getPersonaAccountsForAccount(account); + + for (PersonaAccount pAccount : personaAccountList) { + personaList.add(pAccount.getPersona()); + } + + try { + crAccount = CentralRepository.getInstance().getAccount(CentralRepository.getInstance().getAccountTypeByName(account.getAccountType().getTypeName()), account.getTypeSpecificID()); + } catch (InvalidAccountIDException unused) { + // This was probably caused to a phone number not making + // threw the normalization. + logger.log(Level.WARNING, String.format("Exception thrown from CR getAccount for account %s (%d)", account.getTypeSpecificID(), account.getAccountID())); + } + } + + return new SummaryWorkerResults(stringList, personaList, crAccount); + } + + /** + * Wraps the results of the worker for easy of returning and usage by the + * SummaryViewer. + */ + final static class SummaryWorkerResults { + + private final List accountFileInstancePaths; + private final List personaList; + private final CentralRepoAccount centralRepoAccount; + + /** + * Constructor. + * + * @param accountFileInstancePaths List of instance paths. + * @param personaList List of personas for the account + * @param centralRepoAccount CentralRepoAccount for the given + * account, maybe null if CR is not + * enabled. + */ + SummaryWorkerResults(List accountFileInstancePaths, List personaList, CentralRepoAccount centralRepoAccount) { + this.accountFileInstancePaths = accountFileInstancePaths; + this.personaList = personaList; + this.centralRepoAccount = centralRepoAccount; + } + + /** + * Returns the list of instance paths for the account given to the + * worker. + * + * @return + */ + List getPaths() { + return accountFileInstancePaths; + } + + /** + * Returns the list of personas found for the account given to the + * worker. This list maybe empty if none were found or cr is not + * enabled. + * + * @return + */ + List getPersonaList() { + return personaList; + } + + /** + * Return the cr account for the account given to the worker. This maybe + * null if the cr was not enabled. + * + * @return + */ + CentralRepoAccount getCRAccount() { + return centralRepoAccount; + } + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryPersonaPane.form b/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryPersonaPane.form new file mode 100755 index 0000000000..2fd6f636be --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryPersonaPane.form @@ -0,0 +1,104 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryPersonaPane.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryPersonaPane.java new file mode 100755 index 0000000000..9c07a050ea --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryPersonaPane.java @@ -0,0 +1,295 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obt ain 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. + */ +package org.sleuthkit.autopsy.communications.relationships; + +import java.awt.CardLayout; +import java.awt.Component; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import javax.swing.Box; +import javax.swing.JButton; +import javax.swing.JPanel; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoAccount; +import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException; +import org.sleuthkit.autopsy.centralrepository.datamodel.Persona; +import org.sleuthkit.autopsy.centralrepository.datamodel.PersonaAccount; +import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsDialog; +import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsDialogCallback; +import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsMode; +import org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.Account; + +/** + * Panel to show the Personas for a given account. That is apart SummaryViewer. + */ +public final class SummaryPersonaPane extends javax.swing.JPanel { + + private static final long serialVersionUID = 1L; + + private final static Logger logger = Logger.getLogger(SummaryPersonaPane.class.getName()); + + private final Map personaMap; + private final ViewButtonHandler viewButtonHandler = new ViewButtonHandler(); + private CentralRepoAccount currentCRAccount = null; + private Account currentAccount = null; + + /** + * Creates new form SummaryPersonaPane + */ + SummaryPersonaPane() { + initComponents(); + personaScrollPane.setViewportView(new JPanel()); + + personaMap = new HashMap<>(); + } + + /** + * Clear the persona list. + */ + void clearList() { + personaScrollPane.setViewportView(new JPanel()); + personaMap.clear(); + } + + /** + * Show the message panel. + */ + void showMessagePanel() { + CardLayout layout = (CardLayout) getLayout(); + layout.show(this, "message"); + } + + /** + * Set the message that appears when the message panel is visible. + * + * @param message Message to show. + */ + void setMessage(String message) { + messageLabel.setText(message); + } + + /** + * Update the list of personas to the new given list. + * + * @param personaList New list of personas to show + */ + void updatePersonaList(Account account, CentralRepoAccount crAccount, List personaList) { + JPanel panel = new JPanel(); + currentCRAccount = crAccount; + currentAccount = account; + + CardLayout layout = (CardLayout) getLayout(); + if (personaList.isEmpty()) { + layout.show(this, "create"); + } else { + panel.setLayout(new GridLayout(personaList.size() + 1, 1)); + int maxWidth = 0; + List panelList = new ArrayList<>(); + for (Persona persona : personaList) { + PersonaPanel personaPanel = new PersonaPanel(persona); + JButton viewButton = personaPanel.getViewButton(); + panel.add(personaPanel); + + personaMap.put(viewButton, persona); + viewButton.addActionListener(viewButtonHandler); + + panelList.add(personaPanel); + maxWidth = Math.max(personaPanel.getPersonaLabelPreferedWidth(), maxWidth); + } + + //Set the preferred width of the labeles to the buttons line up. + if (panelList.size() > 1) { + for (PersonaPanel ppanel : panelList) { + ppanel.setPersonalLabelPreferredWidth(maxWidth); + } + } + + panel.add(Box.createVerticalGlue()); + personaScrollPane.setViewportView(panel); + layout.show(this, "persona"); + } + + } + + /** + * ActionListener to handle the launching of the view dialog for the given + * persona. + */ + final private class ViewButtonHandler implements ActionListener { + + @Override + public void actionPerformed(ActionEvent e) { + Persona persona = personaMap.get((Component) e.getSource()); + new PersonaDetailsDialog(SummaryPersonaPane.this, + PersonaDetailsMode.VIEW, persona, new PersonaViewCallbackImpl()); + } + } + + /** + * Callback method for the view mode of the PersonaDetailsDialog + */ + private final class PersonaViewCallbackImpl implements PersonaDetailsDialogCallback { + + @Override + public void callback(Persona persona) { + // nothing to do + } + } + + /** + * Callback class to handle the creation of a new person for the given + * account + */ + private final class PersonaCreateCallbackImpl implements PersonaDetailsDialogCallback { + + @Override + public void callback(Persona persona) { + if (persona != null) { + List list = new ArrayList<>(); + list.add(persona); + + CentralRepoAccount crAccount = null; + Collection personaAccounts = null; + try { + personaAccounts = persona.getPersonaAccounts(); + } catch (CentralRepoException ex) { + logger.log(Level.WARNING, String.format("Failed to get cr account from person %s (%d)", persona.getName(), persona.getId()), ex); + } + + if (personaAccounts != null) { + Iterator iterator = personaAccounts.iterator(); + if (iterator.hasNext()) { + crAccount = iterator.next().getAccount(); + } + } + + updatePersonaList(currentAccount, crAccount, list); + } + } + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + java.awt.GridBagConstraints gridBagConstraints; + + personaScrollPane = new javax.swing.JScrollPane(); + messagePane = new javax.swing.JPanel(); + messageLabel = new javax.swing.JLabel(); + createPersonaPanel = new javax.swing.JPanel(); + noPersonaLabel = new javax.swing.JLabel(); + createButton = new javax.swing.JButton(); + + setLayout(new java.awt.CardLayout()); + + personaScrollPane.setBorder(null); + add(personaScrollPane, "persona"); + + messagePane.setLayout(new java.awt.GridBagLayout()); + + org.openide.awt.Mnemonics.setLocalizedText(messageLabel, org.openide.util.NbBundle.getMessage(SummaryPersonaPane.class, "SummaryPersonaPane.messageLabel.text")); // NOI18N + messageLabel.setEnabled(false); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.weighty = 1.0; + messagePane.add(messageLabel, gridBagConstraints); + + add(messagePane, "message"); + + createPersonaPanel.setPreferredSize(new java.awt.Dimension(200, 100)); + createPersonaPanel.setLayout(new java.awt.GridBagLayout()); + + org.openide.awt.Mnemonics.setLocalizedText(noPersonaLabel, org.openide.util.NbBundle.getMessage(SummaryPersonaPane.class, "SummaryPersonaPane.noPersonaLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weighty = 1.0; + gridBagConstraints.insets = new java.awt.Insets(7, 5, 0, 0); + createPersonaPanel.add(noPersonaLabel, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(createButton, org.openide.util.NbBundle.getMessage(SummaryPersonaPane.class, "SummaryPersonaPane.createButton.text")); // NOI18N + createButton.setMargin(new java.awt.Insets(0, 5, 0, 5)); + createButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + createButtonActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 0; + gridBagConstraints.gridheight = 2; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.weighty = 1.0; + gridBagConstraints.insets = new java.awt.Insets(5, 5, 0, 0); + createPersonaPanel.add(createButton, gridBagConstraints); + + add(createPersonaPanel, "create"); + }// //GEN-END:initComponents + + @NbBundle.Messages({ + "# {0} - accountIdentifer", + "SummaryPersonaPane_not_account_in_cr=Unable to find an account with identifier {0} in the Central Repository." + }) + + private void createButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_createButtonActionPerformed + PersonaDetailsDialog createPersonaDialog = new PersonaDetailsDialog(SummaryPersonaPane.this, + PersonaDetailsMode.CREATE, null, new PersonaCreateCallbackImpl(), false); + + // Pre populate the persona name and accounts if we have them. + PersonaDetailsPanel personaPanel = createPersonaDialog.getDetailsPanel(); + if (currentCRAccount != null) { + personaPanel.addAccount(currentCRAccount, "", Persona.Confidence.HIGH); + + } else { + createPersonaDialog.setStartupPopupMessage(Bundle.SummaryPersonaPane_not_account_in_cr(currentAccount.getTypeSpecificID())); + } + personaPanel.setPersonaName(currentAccount.getTypeSpecificID()); + // display the dialog now + createPersonaDialog.display(); + }//GEN-LAST:event_createButtonActionPerformed + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton createButton; + private javax.swing.JPanel createPersonaPanel; + private javax.swing.JLabel messageLabel; + private javax.swing.JPanel messagePane; + private javax.swing.JLabel noPersonaLabel; + private javax.swing.JScrollPane personaScrollPane; + // End of variables declaration//GEN-END:variables +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryViewer.form b/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryViewer.form index ec718e9bd1..271f9ab901 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryViewer.form +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryViewer.form @@ -265,7 +265,7 @@ - + @@ -281,7 +281,7 @@ - + @@ -363,5 +363,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryViewer.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryViewer.java index 94fb1bca86..4cf917eac1 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryViewer.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/SummaryViewer.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2019 Basis Technology Corp. + * Copyright 2019-2020 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,13 +19,11 @@ package org.sleuthkit.autopsy.communications.relationships; import java.awt.CardLayout; -import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.logging.Level; import javax.swing.DefaultListModel; import javax.swing.JPanel; -import javax.swing.SwingWorker; import org.netbeans.swing.outline.DefaultOutlineModel; import org.netbeans.swing.outline.Outline; import org.openide.explorer.view.OutlineView; @@ -33,11 +31,10 @@ import org.openide.nodes.AbstractNode; import org.openide.nodes.Children; import org.openide.util.Lookup; import org.openide.util.NbBundle.Messages; -import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.datamodel.Account; import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository; +import org.sleuthkit.autopsy.centralrepository.datamodel.Persona; import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.datamodel.AccountFileInstance; /** * Account Summary View Panel. This panel shows a list of various counts related @@ -47,6 +44,8 @@ import org.sleuthkit.datamodel.AccountFileInstance; */ public class SummaryViewer extends javax.swing.JPanel implements RelationshipsViewer { + private static final long serialVersionUID = 1L; + private final Lookup lookup; private final DefaultListModel fileRefListModel; @@ -62,7 +61,8 @@ public class SummaryViewer extends javax.swing.JPanel implements RelationshipsVi "SummaryViewer_Device_Account_Description=This account was referenced by a device in the case.", "SummaryViewer_Account_Description=This account represents a device in the case.", "SummaryViewer_Account_Description_MuliSelect=Summary information is not available when multiple accounts are selected.", - "SummaryViewer_Country_Code=Country: " + "SummaryViewer_Country_Code=Country: ", + "SummaryViewer_Select_account_for_persona= + +

+ No collections available + Go and create one +

+ + + + + + + + + + + + + + + + diff --git a/KeywordSearch/solr/server/solr-webapp/webapp/js/angular/app.js b/KeywordSearch/solr/server/solr-webapp/webapp/js/angular/app.js new file mode 100755 index 0000000000..c2b4cbcd6b --- /dev/null +++ b/KeywordSearch/solr/server/solr-webapp/webapp/js/angular/app.js @@ -0,0 +1,592 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You 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. +*/ + +/* SOLR-14120: Providing a manual definition for the methods 'includes' and 'startsWith' to support Internet Explorer 11. */ +if (!String.prototype.includes) { + String.prototype.includes = function(search, start) { 'use strict'; + if (search instanceof RegExp) { + throw TypeError('first argument must not be a RegExp'); + } + if (start === undefined) { start = 0; } + return this.indexOf(search, start) !== -1; + }; +} +if (!Array.prototype.includes) { + Object.defineProperty(Array.prototype, "includes", { + enumerable: false, + value: function(obj) { + var newArr = this.filter(function(el) { + return el == obj; + }); + return newArr.length > 0; + } + }); +} +if (!String.prototype.startsWith) { + Object.defineProperty(String.prototype, 'startsWith', { + value: function(search, rawPos) { + var pos = rawPos > 0 ? rawPos|0 : 0; + return this.substring(pos, pos + search.length) === search; + } + }); +} + +var solrAdminApp = angular.module("solrAdminApp", [ + "ngResource", + "ngRoute", + "ngCookies", + "ngtimeago", + "solrAdminServices", + "localytics.directives", + "ab-base64" +]); + +solrAdminApp.config([ + '$locationProvider', function($locationProvider) { + $locationProvider.hashPrefix(''); +}]) +.config([ + '$routeProvider', function($routeProvider) { + $routeProvider. + when('/', { + templateUrl: 'partials/index.html', + controller: 'IndexController' + }). + when('/unknown', { + templateUrl: 'partials/unknown.html', + controller: 'UnknownController' + }). + when('/login', { + templateUrl: 'partials/login.html', + controller: 'LoginController' + }). + when('/login/:route', { + templateUrl: 'partials/login.html', + controller: 'LoginController' + }). + when('/~logging', { + templateUrl: 'partials/logging.html', + controller: 'LoggingController' + }). + when('/~logging/level', { + templateUrl: 'partials/logging-levels.html', + controller: 'LoggingLevelController' + }). + when('/~cloud', { + templateUrl: 'partials/cloud.html', + controller: 'CloudController' + }). + when('/~cores', { + templateUrl: 'partials/cores.html', + controller: 'CoreAdminController' + }). + when('/~cores/:corename', { + templateUrl: 'partials/cores.html', + controller: 'CoreAdminController' + }). + when('/~collections', { + templateUrl: 'partials/collections.html', + controller: 'CollectionsController' + }). + when('/~collections/:collection', { + templateUrl: 'partials/collections.html', + controller: 'CollectionsController' + }). + when('/~threads', { + templateUrl: 'partials/threads.html', + controller: 'ThreadsController' + }). + when('/~java-properties', { + templateUrl: 'partials/java-properties.html', + controller: 'JavaPropertiesController' + }). + when('/~cluster-suggestions', { + templateUrl: 'partials/cluster_suggestions.html', + controller: 'ClusterSuggestionsController' + }). + when('/:core/core-overview', { + templateUrl: 'partials/core_overview.html', + controller: 'CoreOverviewController' + }). + when('/:core/alias-overview', { + templateUrl: 'partials/alias_overview.html', + controller: 'AliasOverviewController' + }). + when('/:core/collection-overview', { + templateUrl: 'partials/collection_overview.html', + controller: 'CollectionOverviewController' + }). + when('/:core/analysis', { + templateUrl: 'partials/analysis.html', + controller: 'AnalysisController' + }). + when('/:core/dataimport', { + templateUrl: 'partials/dataimport.html', + controller: 'DataImportController' + }). + when('/:core/dataimport/:handler*', { + templateUrl: 'partials/dataimport.html', + controller: 'DataImportController' + }). + when('/:core/documents', { + templateUrl: 'partials/documents.html', + controller: 'DocumentsController' + }). + when('/:core/files', { + templateUrl: 'partials/files.html', + controller: 'FilesController' + }). + when('/:core/plugins', { + templateUrl: 'partials/plugins.html', + controller: 'PluginsController', + reloadOnSearch: false + }). + when('/:core/plugins/:legacytype', { + templateUrl: 'partials/plugins.html', + controller: 'PluginsController', + reloadOnSearch: false + }). + when('/:core/query', { + templateUrl: 'partials/query.html', + controller: 'QueryController' + }). + when('/:core/stream', { + templateUrl: 'partials/stream.html', + controller: 'StreamController' + }). + when('/:core/replication', { + templateUrl: 'partials/replication.html', + controller: 'ReplicationController' + }). + when('/:core/dataimport', { + templateUrl: 'partials/dataimport.html', + controller: 'DataImportController' + }). + when('/:core/dataimport/:handler*', { + templateUrl: 'partials/dataimport.html', + controller: 'DataImportController' + }). + when('/:core/schema', { + templateUrl: 'partials/schema.html', + controller: 'SchemaController' + }). + when('/:core/segments', { + templateUrl: 'partials/segments.html', + controller: 'SegmentsController' + }). + otherwise({ + templateUrl: 'partials/unknown.html', + controller: 'UnknownController' + }); +}]) +.constant('Constants', { + IS_ROOT_PAGE: 1, + IS_CORE_PAGE: 2, + IS_COLLECTION_PAGE: 3, + ROOT_URL: "/" +}) +.filter('uriencode', function() { + return window.encodeURIComponent; +}) +.filter('highlight', function($sce) { + return function(input, lang) { + if (lang && input && lang!="txt" && lang!="csv") return hljs.highlight(lang, input).value; + return input; + } +}) +.filter('unsafe', function($sce) { return $sce.trustAsHtml; }) +.directive('loadingStatusMessage', function() { + return { + link: function($scope, $element, attrs) { + var show = function() {$element.css('display', 'block')}; + var hide = function() {$element.css('display', 'none')}; + $scope.$on('loadingStatusActive', show); + $scope.$on('loadingStatusInactive', hide); + } + }; +}) +.directive('escapePressed', function () { + return function (scope, element, attrs) { + element.bind("keydown keypress", function (event) { + if(event.which === 27) { + scope.$apply(function (){ + scope.$eval(attrs.escapePressed); + }); + event.preventDefault(); + } + }); + }; +}) +.directive('focusWhen', function($timeout) { + return { + link: function(scope, element, attrs) { + scope.$watch(attrs.focusWhen, function(value) { + if(value === true) { + $timeout(function() { + element[0].focus(); + }, 100); + } + }); + } + }; +}) +.directive('scrollableWhenSmall', function($window) { + return { + link: function(scope, element, attrs) { + var w = angular.element($window); + + var checkFixedMenu = function() { + var shouldScroll = w.height() < (element.height() + $('#header').height() + 40); + element.toggleClass( 'scroll', shouldScroll); + }; + w.bind('resize', checkFixedMenu); + w.bind('load', checkFixedMenu); + } + } +}) +.filter('readableSeconds', function() { + return function(input) { + seconds = parseInt(input||0, 10); + var minutes = Math.floor( seconds / 60 ); + var hours = Math.floor( minutes / 60 ); + + var text = []; + if( 0 !== hours ) { + text.push( hours + 'h' ); + seconds -= hours * 60 * 60; + minutes -= hours * 60; + } + + if( 0 !== minutes ) { + text.push( minutes + 'm' ); + seconds -= minutes * 60; + } + + if( 0 !== seconds ) { + text.push( ( '0' + seconds ).substr( -2 ) + 's' ); + } + return text.join(' '); + }; +}) +.filter('number', function($locale) { + return function(input) { + var sep = { + 'de_CH' : '\'', + 'de' : '.', + 'en' : ',', + 'es' : '.', + 'it' : '.', + 'ja' : ',', + 'sv' : ' ', + 'tr' : '.', + '_' : '' // fallback + }; + + var browser = {}; + var match = $locale.id.match( /^(\w{2})([-_](\w{2}))?$/ ); + if (match[1]) { + browser.language = match[1].toLowerCase(); + } + if (match[1] && match[3]) { + browser.locale = match[1] + '_' + match[3]; + } + + return ( input || 0 ).toString().replace(/\B(?=(\d{3})+(?!\d))/g, + sep[ browser.locale ] || sep[ browser.language ] || sep['_']); + }; +}) +.filter('orderObjectBy', function() { + return function(items, field, reverse) { + var filtered = []; + angular.forEach(items, function(item) { + filtered.push(item); + }); + filtered.sort(function (a, b) { + return (a[field] > b[field] ? 1 : -1); + }); + if(reverse) filtered.reverse(); + return filtered; + }; +}) +.directive('jstree', function($parse) { + return { + restrict: 'EA', + scope: { + data: '=', + onSelect: '&' + }, + link: function(scope, element, attrs) { + scope.$watch("data", function(newValue, oldValue) { + if (newValue && !jQuery.isEmptyObject(newValue)) { + var treeConfig = { + 'core' : { + 'animation' : 0, + 'data': scope.data, + 'worker': false + } + }; + + var tree = $(element).jstree(treeConfig); + $(element).jstree('open_node','li:first'); + if (tree) { + element.bind("select_node.jstree", function (event, data) { + scope.$apply(function() { + scope.onSelect({url: data.node.a_attr.href, data: data}); + }); + }); + } + } + }, true); + } + }; +}) +.directive('connectionMessage', function() { + return { + link: function($scope, $element, attrs) { + var show = function() {$element.css('display', 'block')}; + var hide = function() {$element.css('display', 'none')}; + $scope.$on('connectionStatusActive', show); + $scope.$on('connectionStatusInactive', hide); + } + }; +}) +.factory('httpInterceptor', function($q, $rootScope, $location, $timeout, $injector) { + var activeRequests = 0; + + var started = function(config) { + if (activeRequests == 0) { + $rootScope.$broadcast('loadingStatusActive'); + } + if ($rootScope.exceptions[config.url]) { + delete $rootScope.exceptions[config.url]; + } + activeRequests++; + if (sessionStorage.getItem("auth.header")) { + config.headers['Authorization'] = sessionStorage.getItem("auth.header"); + } + config.timeout = 10000; + return config || $q.when(config); + }; + + var ended = function(response) { + activeRequests--; + if (activeRequests == 0) { + $rootScope.$broadcast('loadingStatusInactive'); + } + if ($rootScope.retryCount>0) { + $rootScope.connectionRecovered = true; + $rootScope.retryCount=0; + $timeout(function() { + $rootScope.connectionRecovered=false; + $rootScope.$broadcast('connectionStatusInactive'); + },2000); + } + if (!$location.path().startsWith('/login') && !$location.path().startsWith('/unknown')) { + sessionStorage.removeItem("http401"); + sessionStorage.removeItem("auth.state"); + sessionStorage.removeItem("auth.statusText"); + } + return response || $q.when(response); + }; + + var failed = function(rejection) { + activeRequests--; + if (activeRequests == 0) { + $rootScope.$broadcast('loadingStatusInactive'); + } + if (rejection.config.headers.doNotIntercept) { + return rejection; + } + if (rejection.status === 0) { + $rootScope.$broadcast('connectionStatusActive'); + if (!$rootScope.retryCount) $rootScope.retryCount=0; + $rootScope.retryCount ++; + var $http = $injector.get('$http'); + var result = $http(rejection.config); + return result; + } else if (rejection.status === 401) { + // Authentication redirect + var headers = rejection.headers(); + var wwwAuthHeader = headers['www-authenticate']; + sessionStorage.setItem("auth.wwwAuthHeader", wwwAuthHeader); + sessionStorage.setItem("auth.authDataHeader", headers['x-solr-authdata']); + sessionStorage.setItem("auth.statusText", rejection.statusText); + sessionStorage.setItem("http401", "true"); + sessionStorage.removeItem("auth.scheme"); + sessionStorage.removeItem("auth.realm"); + sessionStorage.removeItem("auth.username"); + sessionStorage.removeItem("auth.header"); + sessionStorage.removeItem("auth.state"); + if ($location.path().includes('/login')) { + if (!sessionStorage.getItem("auth.location")) { + sessionStorage.setItem("auth.location", "/"); + } + } else { + sessionStorage.setItem("auth.location", $location.path()); + $location.path('/login'); + } + } else { + $rootScope.exceptions[rejection.config.url] = rejection.data.error; + } + return $q.reject(rejection); + }; + + return {request: started, response: ended, responseError: failed}; +}) +.config(function($httpProvider) { + $httpProvider.interceptors.push("httpInterceptor"); + // Force BasicAuth plugin to serve us a 'Authorization: xBasic xxxx' header so browser will not pop up login dialogue + $httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; +}) +.directive('fileModel', function ($parse) { + return { + restrict: 'A', + link: function(scope, element, attrs) { + var model = $parse(attrs.fileModel); + var modelSetter = model.assign; + + element.bind('change', function(){ + scope.$apply(function(){ + modelSetter(scope, element[0].files[0]); + }); + }); + } + }; +}); + +solrAdminApp.controller('MainController', function($scope, $route, $rootScope, $location, Cores, Collections, System, Ping, Constants) { + + $rootScope.exceptions={}; + + $rootScope.toggleException = function() { + $scope.showException=!$scope.showException; + }; + + $scope.refresh = function() { + $scope.cores = []; + $scope.collections = []; + $scope.aliases = []; + } + + $scope.refresh(); + $scope.resetMenu = function(page, pageType) { + Cores.list(function(data) { + $scope.cores = []; + var currentCoreName = $route.current.params.core; + delete $scope.currentCore; + for (key in data.status) { + var core = data.status[key]; + $scope.cores.push(core); + if ((!$scope.isSolrCloud || pageType == Constants.IS_CORE_PAGE) && core.name == currentCoreName) { + $scope.currentCore = core; + } + } + $scope.showInitFailures = Object.keys(data.initFailures).length>0; + $scope.initFailures = data.initFailures; + }); + + System.get(function(data) { + $scope.isCloudEnabled = data.mode.match( /solrcloud/i ); + + var currentCollectionName = $route.current.params.core; + delete $scope.currentCollection; + if ($scope.isCloudEnabled) { + Collections.list(function (cdata) { + Collections.listaliases(function (adata) { + $scope.aliases = []; + for (var key in adata.aliases) { + props = {}; + if (key in adata.properties) { + props = adata.properties[key]; + } + var alias = {name: key, collections: adata.aliases[key], type: 'alias', properties: props}; + $scope.aliases.push(alias); + if (pageType == Constants.IS_COLLECTION_PAGE && alias.name == currentCollectionName) { + $scope.currentCollection = alias; + } + } + $scope.collections = []; + for (key in cdata.collections) { + var collection = {name: cdata.collections[key], type: 'collection'}; + $scope.collections.push(collection); + if (pageType == Constants.IS_COLLECTION_PAGE && collection.name == currentCollectionName) { + $scope.currentCollection = collection; + } + } + + $scope.aliases_and_collections = $scope.aliases; + if ($scope.aliases.length > 0) { + $scope.aliases_and_collections = $scope.aliases_and_collections.concat({name:'-----'}); + } + $scope.aliases_and_collections = $scope.aliases_and_collections.concat($scope.collections); + }); + }); + } + + $scope.showEnvironment = data.environment !== undefined; + if (data.environment) { + $scope.environment = data.environment; + var env_labels = {'prod': 'Production', 'stage': 'Staging', 'test': 'Test', 'dev': 'Development'}; + $scope.environment_label = env_labels[data.environment]; + if (data.environment_label) { + $scope.environment_label = data.environment_label; + } + if (data.environment_color) { + $scope.environment_color = data.environment_color; + } + } + }); + + $scope.showingLogging = page.lastIndexOf("logging", 0) === 0; + $scope.showingCloud = page.lastIndexOf("cloud", 0) === 0; + $scope.page = page; + $scope.currentUser = sessionStorage.getItem("auth.username"); + $scope.http401 = sessionStorage.getItem("http401"); + }; + + $scope.isMultiDestAlias = function(selectedColl) { + return selectedColl && selectedColl.type === 'alias' && selectedColl.collections.includes(','); + }; + + $scope.ping = function() { + Ping.ping({core: $scope.currentCore.name}, function(data) { + $scope.showPing = true; + $scope.pingMS = data.responseHeader.QTime; + }); + // @todo .attr( 'title', '/admin/ping is not configured (' + xhr.status + ': ' + error_thrown + ')' ); + }; + + $scope.dumpCloud = function() { + $scope.$broadcast("cloud-dump"); + } + + $scope.showCore = function(core) { + $location.url("/" + core.name + "/core-overview"); + } + + $scope.showCollection = function(collection) { + if (collection.type === 'collection') { + $location.url("/" + collection.name + "/collection-overview") + } else if (collection.type === 'alias') { + $location.url("/" + collection.name + "/alias-overview") + } + }; + + $scope.$on('$routeChangeStart', function() { + $rootScope.exceptions = {}; + }); +}); diff --git a/KeywordSearch/solr/server/solr-webapp/webapp/js/angular/controllers/alias-overview.js b/KeywordSearch/solr/server/solr-webapp/webapp/js/angular/controllers/alias-overview.js new file mode 100755 index 0000000000..a68a036231 --- /dev/null +++ b/KeywordSearch/solr/server/solr-webapp/webapp/js/angular/controllers/alias-overview.js @@ -0,0 +1,27 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You 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. +*/ + +solrAdminApp.controller('AliasOverviewController', +function($scope, $routeParams, Collections, Constants) { + $scope.resetMenu("collection-overview", Constants.IS_COLLECTION_PAGE); + + $scope.refresh = function() { + $scope.selectedCollection = $scope.currentCollection; + }; + + $scope.refresh(); +}); diff --git a/KeywordSearch/solr/server/solr-webapp/webapp/js/angular/controllers/analysis.js b/KeywordSearch/solr/server/solr-webapp/webapp/js/angular/controllers/analysis.js new file mode 100755 index 0000000000..cf04c3bcad --- /dev/null +++ b/KeywordSearch/solr/server/solr-webapp/webapp/js/angular/controllers/analysis.js @@ -0,0 +1,201 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You 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. +*/ + +solrAdminApp.controller('AnalysisController', + function($scope, $location, $routeParams, Luke, Analysis, Constants) { + $scope.resetMenu("analysis", Constants.IS_COLLECTION_PAGE); + + $scope.refresh = function() { + Luke.schema({core: $routeParams.core}, function(data) { + $scope.fieldsAndTypes = []; + for (var field in data.schema.fields) { + $scope.fieldsAndTypes.push({ + group: "Fields", + value: "fieldname=" + field, + label: field}); + } + for (var type in data.schema.types) { + $scope.fieldsAndTypes.push({ + group: "Types", + value: "fieldtype=" + type, + label: type}); + } + $scope.core = $routeParams.core; + }); + + $scope.parseQueryString(); + // @todo - set defaultSearchField either to context["analysis.fieldname"] or context["analysis.fieldtype"] + + }; + $scope.verbose = true; + + var getShortComponentName = function(longname) { + var short = -1 !== longname.indexOf( '$' ) + ? longname.split( '$' )[1] + : longname.split( '.' ).pop(); + return short.match( /[A-Z]/g ).join( '' ); + }; + + var getCaptionsForComponent = function(data) { + var captions = []; + for (var key in data[0]) { + key = key.replace(/.*#/,''); + if (key != "match" && key!="positionHistory") { + captions.push(key.replace(/.*#/,'')); + } + } + return captions; + }; + + var getTokensForComponent = function(data) { + var tokens = []; + var previousPosition = 0; + var index=0; + for (var i in data) { + var tokenhash = data[i]; + var positionDifference = tokenhash.position - previousPosition; + for (var j=positionDifference; j>1; j--) { + tokens.push({position: tokenhash.position - j+1, blank:true, index:index++}); + } + + var token = {position: tokenhash.position, keys:[], index:index++}; + + for (key in tokenhash) { + if (key == "match" || key=="positionHistory") { + //skip, to not display these keys in the UI + } else { + var tokenInfo = new Object(); + tokenInfo.name = key; + tokenInfo.value = tokenhash[key]; + if ('text' === key || 'raw_bytes' === key ) { + if (tokenhash.match) { + tokenInfo.extraclass = 'match'; //to highlight matching text strings + } + } + token.keys.push(tokenInfo); + } + } + tokens.push(token); + previousPosition = tokenhash.position; + } + return tokens; + }; + + var extractComponents = function(data, result, name) { + if (data) { + result[name] = []; + for (var i = 0; i < data.length; i += 2) { + var component = { + name: data[i], + short: getShortComponentName(data[i]), + captions: getCaptionsForComponent(data[i + 1]), + tokens: getTokensForComponent(data[i + 1]) + }; + result[name].push(component); + } + } + }; + + var processAnalysisData = function(analysis, fieldOrType) { + var fieldname; + for (fieldname in analysis[fieldOrType]) {console.log(fieldname);break;} + var response = {}; + extractComponents(analysis[fieldOrType][fieldname].index, response, "index"); + extractComponents(analysis[fieldOrType][fieldname].query, response, "query"); + return response; + }; + + $scope.updateQueryString = function() { + + var parts = $scope.fieldOrType.split("="); + var fieldOrType = parts[0]; + var name = parts[1]; + + if ($scope.indexText) { + $location.search("analysis.fieldvalue", $scope.indexText); + } else if ($location.search()["analysis.fieldvalue"]) { + $location.search("analysis.fieldvalue", null); + } + if ($scope.queryText) { + $location.search("analysis.query", $scope.queryText); + } else if ($location.search()["analysis.query"]) { + $location.search("analysis.query", null); + } + + if (fieldOrType == "fieldname") { + $location.search("analysis.fieldname", name); + $location.search("analysis.fieldtype", null); + } else { + $location.search("analysis.fieldtype", name); + $location.search("analysis.fieldname", null); + } + $location.search("verbose_output", $scope.verbose ? "1" : "0"); + }; + + $scope.parseQueryString = function () { + var params = {}; + var search = $location.search(); + + if (Object.keys(search).length == 0) { + return; + } + for (var key in search) { + params[key]=search[key]; + } + $scope.indexText = search["analysis.fieldvalue"]; + $scope.queryText = search["analysis.query"]; + if (search["analysis.fieldname"]) { + $scope.fieldOrType = "fieldname=" + search["analysis.fieldname"]; + $scope.schemaBrowserUrl = "field=" + search["analysis.fieldname"]; + } else { + $scope.fieldOrType = "fieldtype=" + search["analysis.fieldtype"]; + $scope.schemaBrowserUrl = "type=" + search["analysis.fieldtype"]; + } + if (search["verbose_output"] == undefined) { + $scope.verbose = true; + } else { + $scope.verbose = search["verbose_output"] == "1"; + } + + if ($scope.fieldOrType || $scope.indexText || $scope.queryText) { + params.core = $routeParams.core; + var parts = $scope.fieldOrType.split("="); + var fieldOrType = parts[0] == "fieldname" ? "field_names" : "field_types"; + + Analysis.field(params, function(data) { + $scope.result = processAnalysisData(data.analysis, fieldOrType); + }); + } + }; + + $scope.changeFieldOrType = function() { + var parts = $scope.fieldOrType.split("="); + if (parts[0]=='fieldname') { + $scope.schemaBrowserUrl = "field=" + parts[1]; + } else { + $scope.schemaBrowserUrl = "type=" + parts[1]; + } + }; + + $scope.toggleVerbose = function() { + $scope.verbose = !$scope.verbose; + $scope.updateQueryString(); + }; + + $scope.refresh(); + } +); diff --git a/KeywordSearch/solr/server/solr-webapp/webapp/js/angular/controllers/cloud.js b/KeywordSearch/solr/server/solr-webapp/webapp/js/angular/controllers/cloud.js new file mode 100755 index 0000000000..976c048c96 --- /dev/null +++ b/KeywordSearch/solr/server/solr-webapp/webapp/js/angular/controllers/cloud.js @@ -0,0 +1,1020 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You 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. +*/ + +solrAdminApp.controller('CloudController', + function($scope, $location, Zookeeper, Constants, Collections, System, Metrics, ZookeeperStatus) { + + $scope.showDebug = false; + + $scope.$on("cloud-dump", function(event) { + $scope.showDebug = true; + }); + + $scope.closeDebug = function() { + $scope.showDebug = false; + }; + + var view = $location.search().view ? $location.search().view : "nodes"; + if (view === "tree") { + $scope.resetMenu("cloud-tree", Constants.IS_ROOT_PAGE); + treeSubController($scope, Zookeeper); + } else if (view === "graph") { + $scope.resetMenu("cloud-graph", Constants.IS_ROOT_PAGE); + graphSubController($scope, Zookeeper, false); + } else if (view === "nodes") { + $scope.resetMenu("cloud-nodes", Constants.IS_ROOT_PAGE); + nodesSubController($scope, Collections, System, Metrics); + } else if (view === "zkstatus") { + $scope.resetMenu("cloud-zkstatus", Constants.IS_ROOT_PAGE); + zkStatusSubController($scope, ZookeeperStatus, false); + } + } +); + +function getOrCreateObj(name, object) { + if (name in object) { + entry = object[name]; + } else { + entry = {}; + object[name] = entry; + } + return entry; +} + +function getOrCreateList(name, object) { + if (name in object) { + entry = object[name]; + } else { + entry = []; + object[name] = entry; + } + return entry; +} + +function ensureInList(string, list) { + if (list.indexOf(string) === -1) { + list.push(string); + } +} + +/* Puts a node name into the hosts structure */ +function ensureNodeInHosts(node_name, hosts) { + var hostName = node_name.split(":")[0]; + var host = getOrCreateObj(hostName, hosts); + var hostNodes = getOrCreateList("nodes", host); + ensureInList(node_name, hostNodes); +} + +// from http://scratch99.com/web-development/javascript/convert-bytes-to-mb-kb/ +function bytesToSize(bytes) { + var sizes = ['b', 'Kb', 'Mb', 'Gb', 'Tb']; + if (bytes === 0) return '0b'; + var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024))); + if (bytes === 0) return bytes + '' + sizes[i]; + return (bytes / Math.pow(1024, i)).toFixed(1) + '' + sizes[i]; +} + +function numDocsHuman(docs) { + var sizes = ['', 'k', 'mn', 'bn', 'tn']; + if (docs === 0) return '0'; + var i = parseInt(Math.floor(Math.log(docs) / Math.log(1000))); + if (i === 0) return docs + '' + sizes[i]; + return (docs / Math.pow(1000, i)).toFixed(1) + '' + sizes[i]; +} + +/* Returns a style class depending on percentage */ +var styleForPct = function (pct) { + if (pct < 60) return "pct-normal"; + if (pct < 80) return "pct-warn"; + return "pct-critical" +}; + +function isNumeric(n) { + return !isNaN(parseFloat(n)) && isFinite(n); +} + +function coreNameToLabel(name) { + return name.replace(/(.*?)_shard((\d+_?)+)_replica_?[ntp]?(\d+)/, '\$1_s\$2r\$4'); +} + +var nodesSubController = function($scope, Collections, System, Metrics) { + $scope.pageSize = 10; + $scope.showNodes = true; + $scope.showTree = false; + $scope.showGraph = false; + $scope.showData = false; + $scope.showAllDetails = false; + $scope.showDetails = {}; + $scope.from = 0; + $scope.to = $scope.pageSize - 1; + $scope.filterType = "node"; // Pre-initialize dropdown + + $scope.toggleAllDetails = function() { + $scope.showAllDetails = !$scope.showAllDetails; + for (var node in $scope.nodes) { + $scope.showDetails[node] = $scope.showAllDetails; + } + for (var host in $scope.hosts) { + $scope.showDetails[host] = $scope.showAllDetails; + } + }; + + $scope.toggleDetails = function(key) { + $scope.showDetails[key] = !$scope.showDetails[key] === true; + }; + + $scope.toggleHostDetails = function(key) { + $scope.showDetails[key] = !$scope.showDetails[key] === true; + for (var nodeId in $scope.hosts[key].nodes) { + var node = $scope.hosts[key].nodes[nodeId]; + $scope.showDetails[node] = $scope.showDetails[key]; + } + }; + + $scope.nextPage = function() { + $scope.from += parseInt($scope.pageSize); + $scope.reload(); + }; + + $scope.previousPage = function() { + $scope.from = Math.max(0, $scope.from - parseInt($scope.pageSize)); + $scope.reload(); + }; + + // Checks if this node is the first (alphabetically) for a given host. Used to decide rowspan in table + $scope.isFirstNodeForHost = function(node) { + var hostName = node.split(":")[0]; + var nodesInHost = $scope.filteredNodes.filter(function (node) { + return node.startsWith(hostName); + }); + return nodesInHost[0] === node; + }; + + // Returns the first live node for this host, to make sure we pick host-level metrics from a live node + $scope.firstLiveNodeForHost = function(key) { + var hostName = key.split(":")[0]; + var liveNodesInHost = $scope.filteredNodes.filter(function (key) { + return key.startsWith(hostName); + }).filter(function (key) { + return $scope.live_nodes.includes(key); + }); + return liveNodesInHost.length > 0 ? liveNodesInHost[0] : key; + }; + + // Initializes the cluster state, list of nodes, collections etc + $scope.initClusterState = function() { + var nodes = {}; + var hosts = {}; + var live_nodes = []; + + // We build a node-centric view of the cluster state which we can easily consume to render the table + Collections.status(function (data) { + // Fetch cluster state from collections API and invert to a nodes structure + for (var name in data.cluster.collections) { + var collection = data.cluster.collections[name]; + collection.name = name; + var shards = collection.shards; + collection.shards = []; + for (var shardName in shards) { + var shard = shards[shardName]; + shard.name = shardName; + shard.collection = collection.name; + var replicas = shard.replicas; + shard.replicas = []; + for (var replicaName in replicas) { + var core = replicas[replicaName]; + core.name = replicaName; + core.label = coreNameToLabel(core['core']); + core.collection = collection.name; + core.shard = shard.name; + core.shard_state = shard.state; + + var node_name = core['node_name']; + var node = getOrCreateObj(node_name, nodes); + var cores = getOrCreateList("cores", node); + cores.push(core); + node['base_url'] = core.base_url; + node['id'] = core.base_url.replace(/[^\w\d]/g, ''); + node['host'] = node_name.split(":")[0]; + var collections = getOrCreateList("collections", node); + ensureInList(core.collection, collections); + ensureNodeInHosts(node_name, hosts); + } + } + } + + live_nodes = data.cluster.live_nodes; + for (n in data.cluster.live_nodes) { + node = data.cluster.live_nodes[n]; + if (!(node in nodes)) { + var hostName = node.split(":")[0]; + nodes[node] = {}; + nodes[node]['host'] = hostName; + } + ensureNodeInHosts(node, hosts); + } + + // Make sure nodes are sorted alphabetically to align with rowspan in table + for (var host in hosts) { + hosts[host].nodes.sort(); + } + + $scope.nodes = nodes; + $scope.hosts = hosts; + $scope.live_nodes = live_nodes; + + $scope.Math = window.Math; + $scope.reload(); + }); + }; + + $scope.filterInput = function() { + $scope.from = 0; + $scope.to = $scope.pageSize - 1; + $scope.reload(); + }; + + /* + Reload will fetch data for the current page of the table and thus refresh numbers. + It is also called whenever a filter or paging action is executed + */ + $scope.reload = function() { + var nodes = $scope.nodes; + var node_keys = Object.keys(nodes); + var hosts = $scope.hosts; + var live_nodes = $scope.live_nodes; + var hostNames = Object.keys(hosts); + hostNames.sort(); + var pageSize = isNumeric($scope.pageSize) ? $scope.pageSize : 10; + + // Calculate what nodes that will show on this page + var nodesToShow = []; + var nodesParam; + var hostsToShow = []; + var filteredNodes; + var filteredHosts; + var isFiltered = false; + switch ($scope.filterType) { + case "node": // Find what nodes match the node filter + if ($scope.nodeFilter) { + filteredNodes = node_keys.filter(function (node) { + return node.indexOf($scope.nodeFilter) !== -1; + }); + } + break; + + case "collection": // Find what collections match the collection filter and what nodes that have these collections + if ($scope.collectionFilter) { + candidateNodes = {}; + nodesCollections = []; + for (var i = 0 ; i < node_keys.length ; i++) { + var node_name = node_keys[i]; + var node = nodes[node_name]; + nodeColl = {}; + nodeColl['node'] = node_name; + collections = {}; + node.cores.forEach(function(core) { + collections[core.collection] = true; + }); + nodeColl['collections'] = Object.keys(collections); + nodesCollections.push(nodeColl); + } + nodesCollections.forEach(function(nc) { + matchingColls = nc['collections'].filter(function (collection) { + return collection.indexOf($scope.collectionFilter) !== -1; + }); + if (matchingColls.length > 0) { + candidateNodes[nc.node] = true; + } + }); + filteredNodes = Object.keys(candidateNodes); + } + break; + + case "health": + + } + + if (filteredNodes) { + // If filtering is active, calculate what hosts contain the nodes that match the filters + isFiltered = true; + filteredHosts = filteredNodes.map(function (node) { + return node.split(":")[0]; + }).filter(function (item, index, self) { + return self.indexOf(item) === index; + }); + } else { + filteredNodes = node_keys; + filteredHosts = hostNames; + } + filteredNodes.sort(); + filteredHosts.sort(); + + // Find what hosts & nodes (from the filtered set) that should be displayed on current page + for (var id = $scope.from ; id < $scope.from + pageSize && filteredHosts[id] ; id++) { + var hostName = filteredHosts[id]; + hostsToShow.push(hostName); + if (isFiltered) { // Only show the nodes per host matching active filter + nodesToShow = nodesToShow.concat(filteredNodes.filter(function (node) { + return node.startsWith(hostName); + })); + } else { + nodesToShow = nodesToShow.concat(hosts[hostName]['nodes']); + } + } + nodesParam = nodesToShow.filter(function (node) { + return live_nodes.includes(node); + }).join(','); + var deadNodes = nodesToShow.filter(function (node) { + return !live_nodes.includes(node); + }); + deadNodes.forEach(function (node) { + nodes[node]['dead'] = true; + }); + $scope.nextEnabled = $scope.from + pageSize < filteredHosts.length; + $scope.prevEnabled = $scope.from - pageSize >= 0; + nodesToShow.sort(); + hostsToShow.sort(); + + /* + Fetch system info for all selected nodes + Pick the data we want to display and add it to the node-centric data structure + */ + System.get({"nodes": nodesParam}, function (systemResponse) { + for (var node in systemResponse) { + if (node in nodes) { + var s = systemResponse[node]; + nodes[node]['system'] = s; + var memTotal = s.system.totalPhysicalMemorySize; + var memFree = s.system.freePhysicalMemorySize; + var memPercentage = Math.floor((memTotal - memFree) / memTotal * 100); + nodes[node]['memUsedPct'] = memPercentage; + nodes[node]['memUsedPctStyle'] = styleForPct(memPercentage); + nodes[node]['memTotal'] = bytesToSize(memTotal); + nodes[node]['memFree'] = bytesToSize(memFree); + nodes[node]['memUsed'] = bytesToSize(memTotal - memFree); + + var heapTotal = s.jvm.memory.raw.total; + var heapFree = s.jvm.memory.raw.free; + var heapPercentage = Math.floor((heapTotal - heapFree) / heapTotal * 100); + nodes[node]['heapUsed'] = bytesToSize(heapTotal - heapFree); + nodes[node]['heapUsedPct'] = heapPercentage; + nodes[node]['heapUsedPctStyle'] = styleForPct(heapPercentage); + nodes[node]['heapTotal'] = bytesToSize(heapTotal); + nodes[node]['heapFree'] = bytesToSize(heapFree); + + var jvmUptime = s.jvm.jmx.upTimeMS / 1000; // Seconds + nodes[node]['jvmUptime'] = secondsForHumans(jvmUptime); + nodes[node]['jvmUptimeSec'] = jvmUptime; + + nodes[node]['uptime'] = (s.system.uptime || "unknown").replace(/.*up (.*?,.*?),.*/, "$1"); + nodes[node]['loadAvg'] = Math.round(s.system.systemLoadAverage * 100) / 100; + nodes[node]['cpuPct'] = Math.ceil(s.system.processCpuLoad); + nodes[node]['cpuPctStyle'] = styleForPct(Math.ceil(s.system.processCpuLoad)); + nodes[node]['maxFileDescriptorCount'] = s.system.maxFileDescriptorCount; + nodes[node]['openFileDescriptorCount'] = s.system.openFileDescriptorCount; + } + } + }); + + /* + Fetch metrics for all selected nodes. Only pull the metrics that we'll show to save bandwidth + Pick the data we want to display and add it to the node-centric data structure + */ + Metrics.get({ + "nodes": nodesParam, + "prefix": "CONTAINER.fs,org.eclipse.jetty.server.handler.DefaultHandler.get-requests,INDEX.sizeInBytes,SEARCHER.searcher.numDocs,SEARCHER.searcher.deletedDocs,SEARCHER.searcher.warmupTime" + }, + function (metricsResponse) { + for (var node in metricsResponse) { + if (node in nodes) { + var m = metricsResponse[node]; + nodes[node]['metrics'] = m; + var diskTotal = m.metrics['solr.node']['CONTAINER.fs.totalSpace']; + var diskFree = m.metrics['solr.node']['CONTAINER.fs.usableSpace']; + var diskPercentage = Math.floor((diskTotal - diskFree) / diskTotal * 100); + nodes[node]['diskUsedPct'] = diskPercentage; + nodes[node]['diskUsedPctStyle'] = styleForPct(diskPercentage); + nodes[node]['diskTotal'] = bytesToSize(diskTotal); + nodes[node]['diskFree'] = bytesToSize(diskFree); + + var r = m.metrics['solr.jetty']['org.eclipse.jetty.server.handler.DefaultHandler.get-requests']; + nodes[node]['req'] = r.count; + nodes[node]['req1minRate'] = Math.floor(r['1minRate'] * 100) / 100; + nodes[node]['req5minRate'] = Math.floor(r['5minRate'] * 100) / 100; + nodes[node]['req15minRate'] = Math.floor(r['15minRate'] * 100) / 100; + nodes[node]['reqp75_ms'] = Math.floor(r['p75_ms']); + nodes[node]['reqp95_ms'] = Math.floor(r['p95_ms']); + nodes[node]['reqp99_ms'] = Math.floor(r['p99_ms']); + + var cores = nodes[node]['cores']; + var indexSizeTotal = 0; + var docsTotal = 0; + var graphData = []; + if (cores) { + for (coreId in cores) { + var core = cores[coreId]; + var keyName = "solr.core." + core['core'].replace(/(.*?)_(shard(\d+_?)+)_(replica.*?)/, '\$1.\$2.\$4'); + var nodeMetric = m.metrics[keyName]; + var size = nodeMetric['INDEX.sizeInBytes']; + size = (typeof size !== 'undefined') ? size : 0; + core['sizeInBytes'] = size; + core['size'] = bytesToSize(size); + if (core['shard_state'] !== 'active' || core['state'] !== 'active') { + // If core state is not active, display the real state, or if shard is inactive, display that + var labelState = (core['state'] !== 'active') ? core['state'] : core['shard_state']; + core['label'] += "_(" + labelState + ")"; + } + indexSizeTotal += size; + var numDocs = nodeMetric['SEARCHER.searcher.numDocs']; + numDocs = (typeof numDocs !== 'undefined') ? numDocs : 0; + core['numDocs'] = numDocs; + core['numDocsHuman'] = numDocsHuman(numDocs); + core['avgSizePerDoc'] = bytesToSize(numDocs === 0 ? 0 : size / numDocs); + var deletedDocs = nodeMetric['SEARCHER.searcher.deletedDocs']; + deletedDocs = (typeof deletedDocs !== 'undefined') ? deletedDocs : 0; + core['deletedDocs'] = deletedDocs; + core['deletedDocsHuman'] = numDocsHuman(deletedDocs); + var warmupTime = nodeMetric['SEARCHER.searcher.warmupTime']; + warmupTime = (typeof warmupTime !== 'undefined') ? warmupTime : 0; + core['warmupTime'] = warmupTime; + docsTotal += core['numDocs']; + } + for (coreId in cores) { + core = cores[coreId]; + var graphObj = {}; + graphObj['label'] = core['label']; + graphObj['size'] = core['sizeInBytes']; + graphObj['sizeHuman'] = core['size']; + graphObj['pct'] = (core['sizeInBytes'] / indexSizeTotal) * 100; + graphData.push(graphObj); + } + cores.sort(function (a, b) { + return b.sizeInBytes - a.sizeInBytes + }); + } else { + cores = {}; + } + graphData.sort(function (a, b) { + return b.size - a.size + }); + nodes[node]['graphData'] = graphData; + nodes[node]['numDocs'] = numDocsHuman(docsTotal); + nodes[node]['sizeInBytes'] = indexSizeTotal; + nodes[node]['size'] = bytesToSize(indexSizeTotal); + nodes[node]['sizePerDoc'] = docsTotal === 0 ? '0b' : bytesToSize(indexSizeTotal / docsTotal); + + // Build the d3 powered bar chart + $('#chart' + nodes[node]['id']).empty(); + var chart = d3.select('#chart' + nodes[node]['id']).append('div').attr('class', 'chart'); + + // Add one div per bar which will group together both labels and bars + var g = chart.selectAll('div') + .data(nodes[node]['graphData']).enter() + .append('div'); + + // Add the bars + var bars = g.append("div") + .attr("class", "rect") + .text(function (d) { + return d.label + ':\u00A0\u00A0' + d.sizeHuman; + }); + + // Execute the transition to show the bars + bars.transition() + .ease('elastic') + .style('width', function (d) { + return d.pct + '%'; + }); + } + } + }); + $scope.nodes = nodes; + $scope.hosts = hosts; + $scope.live_nodes = live_nodes; + $scope.nodesToShow = nodesToShow; + $scope.hostsToShow = hostsToShow; + $scope.filteredNodes = filteredNodes; + $scope.filteredHosts = filteredHosts; + }; + $scope.initClusterState(); +}; + +var zkStatusSubController = function($scope, ZookeeperStatus) { + $scope.showZkStatus = true; + $scope.showNodes = false; + $scope.showTree = false; + $scope.showGraph = false; + $scope.tree = {}; + $scope.showData = false; + $scope.showDetails = false; + + $scope.toggleDetails = function() { + $scope.showDetails = !$scope.showDetails === true; + }; + + $scope.initZookeeper = function() { + ZookeeperStatus.monitor({}, function(data) { + $scope.zkState = data.zkStatus; + $scope.mainKeys = ["ok", "clientPort", "secureClientPort", "zk_server_state", "zk_version", + "zk_approximate_data_size", "zk_znode_count", "zk_num_alive_connections"]; + $scope.detailKeys = ["dataDir", "dataLogDir", + "zk_avg_latency", "zk_max_file_descriptor_count", "zk_watch_count", + "zk_packets_sent", "zk_packets_received", + "tickTime", "maxClientCnxns", "minSessionTimeout", "maxSessionTimeout"]; + $scope.ensembleMainKeys = ["serverId", "electionPort", "quorumPort", "role"]; + $scope.ensembleDetailKeys = ["peerType", "electionAlg", "initLimit", "syncLimit", + "zk_followers", "zk_synced_followers", "zk_pending_syncs"]; + $scope.notEmptyRow = function(key) { + for (hostId in $scope.zkState.details) { + if (key in $scope.zkState.details[hostId]) return true; + } + return false; + }; + }); + }; + + $scope.initZookeeper(); +}; + +var treeSubController = function($scope, Zookeeper) { + $scope.showZkStatus = false; + $scope.showTree = true; + $scope.showGraph = false; + $scope.tree = {}; + $scope.showData = false; + + $scope.showTreeLink = function(link) { + var path = decodeURIComponent(link.replace(/.*[\\?&]path=([^&#]*).*/, "$1")); + Zookeeper.detail({path: path}, function(data) { + $scope.znode = data.znode; + var path = data.znode.path.split( '.' ); + if(path.length >1) { + $scope.lang = path.pop(); + } else { + var lastPathElement = data.znode.path.split( '/' ).pop(); + if (lastPathElement == "managed-schema") { + $scope.lang = "xml"; + } + } + $scope.showData = true; + }); + }; + + $scope.hideData = function() { + $scope.showData = false; + }; + + $scope.initTree = function() { + Zookeeper.simple(function(data) { + $scope.tree = data.tree; + }); + }; + + $scope.initTree(); +}; + +/** + * Translates seconds into human readable format of seconds, minutes, hours, days, and years + * + * @param {number} seconds The number of seconds to be processed + * @return {string} The phrase describing the the amount of time + */ +function secondsForHumans ( seconds ) { + var levels = [ + [Math.floor(seconds / 31536000), 'y'], + [Math.floor((seconds % 31536000) / 86400), 'd'], + [Math.floor(((seconds % 31536000) % 86400) / 3600), 'h'], + [Math.floor((((seconds % 31536000) % 86400) % 3600) / 60), 'm'] + ]; + var returntext = ''; + + for (var i = 0, max = levels.length; i < max; i++) { + if ( levels[i][0] === 0 ) continue; + returntext += ' ' + levels[i][0] + levels[i][1]; + } + return returntext.trim() === '' ? '0m' : returntext.trim(); +} + +var graphSubController = function ($scope, Zookeeper) { + $scope.showZkStatus = false; + $scope.showTree = false; + $scope.showGraph = true; + + $scope.filterType = "status"; + + $scope.helperData = { + protocol: [], + host: [], + hostname: [], + port: [], + pathname: [], + replicaType: [], + base_url: [], + core: [], + node_name: [], + state: [], + core_node: [] + }; + + $scope.next = function() { + $scope.pos += $scope.rows; + $scope.initGraph(); + }; + + $scope.previous = function() { + $scope.pos = Math.max(0, $scope.pos - $scope.rows); + $scope.initGraph(); + }; + + $scope.resetGraph = function() { + $scope.pos = 0; + $scope.initGraph(); + }; + + $scope.initGraph = function() { + Zookeeper.liveNodes(function (data) { + var live_nodes = {}; + for (var c in data.tree[0].children) { + live_nodes[data.tree[0].children[c].text] = true; + } + + var params = {view: "graph"}; + if ($scope.rows) { + params.start = $scope.pos; + params.rows = $scope.rows; + } + + var filter = ($scope.filterType=='status') ? $scope.pagingStatusFilter : $scope.pagingFilter; + + if (filter) { + params.filterType = $scope.filterType; + params.filter = filter; + } + + Zookeeper.clusterState(params, function (data) { + var state = $.parseJSON(data.znode.data); + + var leaf_count = 0; + var graph_data = { + name: null, + children: [] + }; + + for (var c in state) { + var shards = []; + for (var s in state[c].shards) { + var shard_status = state[c].shards[s].state; + shard_status = shard_status == 'inactive' ? 'shard-inactive' : shard_status; + var nodes = []; + for (var n in state[c].shards[s].replicas) { + leaf_count++; + var replica = state[c].shards[s].replicas[n] + + var uri = replica.base_url; + var parts = uri.match(/^(\w+:)\/\/(([\w\d\.-]+)(:(\d+))?)(.+)$/); + var uri_parts = { + protocol: parts[1], + host: parts[2], + hostname: parts[3], + port: parseInt(parts[5] || 80, 10), + pathname: parts[6], + replicaType: replica.type, + base_url: replica.base_url, + core: replica.core, + node_name: replica.node_name, + state: replica.state, + core_node: n + }; + + $scope.helperData.protocol.push(uri_parts.protocol); + $scope.helperData.host.push(uri_parts.host); + $scope.helperData.hostname.push(uri_parts.hostname); + $scope.helperData.port.push(uri_parts.port); + $scope.helperData.pathname.push(uri_parts.pathname); + $scope.helperData.replicaType.push(uri_parts.replicaType); + $scope.helperData.base_url.push(uri_parts.base_url); + $scope.helperData.core.push(uri_parts.core); + $scope.helperData.node_name.push(uri_parts.node_name); + $scope.helperData.state.push(uri_parts.state); + $scope.helperData.core_node.push(uri_parts.core_node); + + var replica_status = replica.state; + + if (!live_nodes[replica.node_name]) { + replica_status = 'gone'; + } else if(shard_status=='shard-inactive') { + replica_status += ' ' + shard_status; + } + + var node = { + name: uri, + data: { + type: 'node', + state: replica_status, + leader: 'true' === replica.leader, + uri: uri_parts + } + }; + nodes.push(node); + } + + var shard = { + name: shard_status == "shard-inactive" ? s + ' (inactive)' : s, + data: { + type: 'shard', + state: shard_status, + range: state[c].shards[s].range + + }, + children: nodes + }; + shards.push(shard); + } + + var collection = { + name: c, + data: { + type: 'collection', + pullReplicas: state[c].pullReplicas, + replicationFactor: state[c].replicationFactor, + router: state[c].router.name, + maxShardsPerNode: state[c].maxShardsPerNode, + autoAddReplicas: state[c].autoAddReplicas, + nrtReplicas: state[c].nrtReplicas, + tlogReplicas: state[c].tlogReplicas, + numShards: shards.length + }, + children: shards + }; + graph_data.children.push(collection); + } + $scope.helperData.protocol = $.unique($scope.helperData.protocol); + $scope.helperData.host = $.unique($scope.helperData.host); + $scope.helperData.hostname = $.unique($scope.helperData.hostname); + $scope.helperData.port = $.unique($scope.helperData.port); + $scope.helperData.pathname = $.unique($scope.helperData.pathname); + $scope.helperData.replicaType = $.unique($scope.helperData.replicaType); + $scope.helperData.base_url = $.unique($scope.helperData.base_url); + $scope.helperData.core = $.unique($scope.helperData.core); + $scope.helperData.node_name = $.unique($scope.helperData.node_name); + $scope.helperData.state = $.unique($scope.helperData.state); + $scope.helperData.core_node = $.unique($scope.helperData.core_node); + + if (data.znode && data.znode.paging) { + $scope.showPaging = true; + + var parr = data.znode.paging.split('|'); + if (parr.length < 3) { + $scope.showPaging = false; + } else { + $scope.start = Math.max(parseInt(parr[0]), 0); + $scope.prevEnabled = ($scope.start > 0); + $scope.rows = parseInt(parr[1]); + $scope.total = parseInt(parr[2]); + + if ($scope.rows == -1) { + $scope.showPaging = false; + } else { + var filterType = parr.length > 3 ? parr[3] : ''; + + if (filterType == '' || filterType == 'none') filterType = 'status'; + + +$('#cloudGraphPagingFilterType').val(filterType); + + var filter = parr.length > 4 ? parr[4] : ''; + var page = Math.floor($scope.start / $scope.rows) + 1; + var pages = Math.ceil($scope.total / $scope.rows); + $scope.last = Math.min($scope.start + $scope.rows, $scope.total); + $scope.nextEnabled = ($scope.last < $scope.total); + } + } + } + else { + $scope.showPaging = false; + } + $scope.graphData = graph_data; + $scope.leafCount = leaf_count; + }); + }); + }; + + $scope.initGraph(); + $scope.pos = 0; +}; + +solrAdminApp.directive('graph', function(Constants) { + return { + restrict: 'EA', + scope: { + data: "=", + leafCount: "=", + helperData: "=", + }, + link: function (scope, element, attrs) { + var helper_path_class = function (p) { + var classes = ['link']; + classes.push('lvl-' + p.target.depth); + + if (p.target.data && p.target.data.leader) { + classes.push('leader'); + } + + if (p.target.data && p.target.data.state) { + classes.push(p.target.data.state); + } + + return classes.join(' '); + }; + + var helper_node_class = function (d) { + var classes = ['node']; + classes.push('lvl-' + d.depth); + + if (d.data && d.data.leader) { + classes.push('leader'); + } + + if (d.data && d.data.state) { + if(!(d.data.type=='shard' && d.data.state=='active')){ + classes.push(d.data.state); + } + } + + return classes.join(' '); + }; + + var helper_tooltip_text = function (d) { + if (!d.data) { + return tooltip; + } + var tooltip; + + if (! d.data.type) { + return tooltip; + } + + + if (d.data.type == 'collection') { + tooltip = d.name + " {
"; + tooltip += "numShards: [" + d.data.numShards + "],
"; + tooltip += "maxShardsPerNode: [" + d.data.maxShardsPerNode + "],
"; + tooltip += "router: [" + d.data.router + "],
"; + tooltip += "autoAddReplicas: [" + d.data.autoAddReplicas + "],
"; + tooltip += "replicationFactor: [" + d.data.replicationFactor + "],
"; + tooltip += "nrtReplicas: [" + d.data.nrtReplicas + "],
"; + tooltip += "pullReplicas: [" + d.data.pullReplicas + "],
"; + tooltip += "tlogReplicas: [" + d.data.tlogReplicas + "],
"; + tooltip += "}"; + } else if (d.data.type == 'shard') { + tooltip = d.name + " {
"; + tooltip += "range: [" + d.data.range + "],
"; + tooltip += "state: [" + d.data.state + "],
"; + tooltip += "}"; + } else if (d.data.type == 'node') { + tooltip = d.data.uri.core_node + " {
"; + + if (0 !== scope.helperData.core.length) { + tooltip += "core: [" + d.data.uri.core + "],
"; + } + + if (0 !== scope.helperData.node_name.length) { + tooltip += "node_name: [" + d.data.uri.node_name + "],
"; + } + tooltip += "}"; + } + + return tooltip; + }; + + var helper_node_text = function (d) { + if (!d.data || !d.data.uri) { + return d.name; + } + + var name = d.data.uri.hostname; + if (1 !== scope.helperData.protocol.length) { + name = d.data.uri.protocol + '//' + name; + } + + if (1 !== scope.helperData.port.length) { + name += ':' + d.data.uri.port; + } + + if (1 !== scope.helperData.pathname.length) { + name += d.data.uri.pathname; + } + + if(0 !== scope.helperData.replicaType.length) { + name += ' (' + d.data.uri.replicaType[0] + ')'; + } + + return name; + }; + + scope.$watch("data", function(newValue, oldValue) { + if (newValue) { + flatGraph(element, scope.data, scope.leafCount); + } + + $('text').tooltip({ + content: function() { + return $(this).attr('title'); + } + }); + }); + + + function setNodeNavigationBehavior(node, view){ + node + .attr('data-href', function (d) { + if (d.type == "node"){ + return getNodeUrl(d, view); + } + else{ + return ""; + } + }) + .on('click', function(d) { + if (d.data.type == "node"){ + location.href = getNodeUrl(d, view); + } + }); + } + + function getNodeUrl(d, view){ + var url = d.name + Constants.ROOT_URL + "#/~cloud"; + if (view != undefined){ + url += "?view=" + view; + } + return url; + } + + var flatGraph = function(element, graphData, leafCount) { + var w = element.width(), + h = leafCount * 20; + + var tree = d3.layout.tree().size([h, w - 400]); + + var diagonal = d3.svg.diagonal().projection(function (d) { + return [d.y, d.x]; + }); + + d3.select('#canvas', element).html(''); + var vis = d3.select('#canvas', element).append('svg') + .attr('width', w) + .attr('height', h) + .append('g') + .attr('transform', 'translate(100, 0)'); + + var nodes = tree.nodes(graphData); + + var link = vis.selectAll('path.link') + .data(tree.links(nodes)) + .enter().append('path') + .attr('class', helper_path_class) + .attr('d', diagonal); + + var node = vis.selectAll('g.node') + .data(nodes) + .enter().append('g') + .attr('class', helper_node_class) + .attr('transform', function (d) { + return 'translate(' + d.y + ',' + d.x + ')'; + }) + + node.append('circle') + .attr('r', 4.5); + + node.append('text') + .attr('dx', function (d) { + return 0 === d.depth ? -8 : 8; + }) + .attr('dy', function (d) { + return 5; + }) + .attr('text-anchor', function (d) { + return 0 === d.depth ? 'end' : 'start'; + }) + .attr("title", helper_tooltip_text) + .text(helper_node_text); + + setNodeNavigationBehavior(node); + }; + } + }; +}); diff --git a/KeywordSearch/solr/server/solr-webapp/webapp/js/angular/controllers/cluster-suggestions.js b/KeywordSearch/solr/server/solr-webapp/webapp/js/angular/controllers/cluster-suggestions.js new file mode 100755 index 0000000000..4e39129f2c --- /dev/null +++ b/KeywordSearch/solr/server/solr-webapp/webapp/js/angular/controllers/cluster-suggestions.js @@ -0,0 +1,62 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You 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. +*/ +solrAdminApp.controller('ClusterSuggestionsController', +function($scope, $http, Constants) { + $scope.resetMenu("cluster-suggestion", Constants.IS_COLLECTION_PAGE); + $scope.data={}; + var dataArr =[]; + var dataJson = {}; + //function to display suggestion + $http({ + method: 'GET', + url: '/api/cluster/autoscaling/suggestions' + }).then(function successCallback(response) { + $scope.data = response.data; + $scope.parsedData = $scope.data.suggestions; + }, function errorCallback(response) { + }); + //function to perform operation + $scope.postdata = function (x) { + x.loading = true; + var path=x.operation.path; + var command=x.operation.command; + var fullPath='/api/'+path; + console.log(fullPath); + console.log(command); + $http.post(fullPath, JSON.stringify(command)).then(function (response) { + if (response.data) + console.log(response.data); + x.loading = false; + x.done = true; + x.run=true; + $scope.msg = "Command Submitted Successfully!"; + }, function (response) { + x.failed=true; + $scope.msg = "Service does not exist"; + $scope.statusval = response.status; + $scope.statustext = response.statusText; + $scope.headers = response.headers(); + }); + }; + $scope.showPopover = function() { + $scope.popup = true; + }; + + $scope.hidePopover = function () { + $scope.popup = false; + }; +}); diff --git a/KeywordSearch/solr/server/solr-webapp/webapp/js/angular/controllers/collection-overview.js b/KeywordSearch/solr/server/solr-webapp/webapp/js/angular/controllers/collection-overview.js new file mode 100755 index 0000000000..a98456a7b3 --- /dev/null +++ b/KeywordSearch/solr/server/solr-webapp/webapp/js/angular/controllers/collection-overview.js @@ -0,0 +1,39 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You 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. +*/ + +solrAdminApp.controller('CollectionOverviewController', +function($scope, $routeParams, Collections, Constants) { + $scope.resetMenu("collection-overview", Constants.IS_COLLECTION_PAGE); + + $scope.refresh = function() { + Collections.status({}, function(data) { + $scope.selectedCollection = data.cluster.collections[$routeParams.core]; + $scope.selectedCollection.name = $routeParams.core; + $scope.rootUrl = Constants.ROOT_URL; + }); + }; + + $scope.showReplica = function(replica) { + replica.show = !replica.show; + } + + $scope.hideShard = function(shard) { + shard.hide = !shard.hide; + } + + $scope.refresh(); +}); diff --git a/KeywordSearch/solr/server/solr-webapp/webapp/js/angular/controllers/collections.js b/KeywordSearch/solr/server/solr-webapp/webapp/js/angular/controllers/collections.js new file mode 100755 index 0000000000..480a8b2fa0 --- /dev/null +++ b/KeywordSearch/solr/server/solr-webapp/webapp/js/angular/controllers/collections.js @@ -0,0 +1,289 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You 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. +*/ + +solrAdminApp.controller('CollectionsController', + function($scope, $routeParams, $location, $timeout, Collections, Zookeeper, Constants){ + $scope.resetMenu("collections", Constants.IS_ROOT_PAGE); + + $scope.refresh = function() { + + $scope.rootUrl = Constants.ROOT_URL + "#/~collections/" + $routeParams.collection; + + Collections.status(function (data) { + $scope.collections = []; + for (var name in data.cluster.collections) { + var collection = data.cluster.collections[name]; + collection.name = name; + collection.type = 'collection'; + var shards = collection.shards; + collection.shards = []; + for (var shardName in shards) { + var shard = shards[shardName]; + shard.name = shardName; + shard.collection = collection.name; + var replicas = shard.replicas; + shard.replicas = []; + for (var replicaName in replicas) { + var replica = replicas[replicaName]; + replica.name = replicaName; + replica.collection = collection.name; + replica.shard = shard.name; + shard.replicas.push(replica); + } + collection.shards.push(shard); + } + $scope.collections.push(collection); + if ($routeParams.collection == name) { + $scope.collection = collection; + } + } + // Fetch aliases using LISTALIASES to get properties + Collections.listaliases(function (adata) { + // TODO: Population of aliases array duplicated in app.js + $scope.aliases = []; + for (var key in adata.aliases) { + props = {}; + if (key in adata.properties) { + props = adata.properties[key]; + } + var alias = {name: key, collections: adata.aliases[key], type: 'alias', properties: props}; + $scope.aliases.push(alias); + if ($routeParams.collection == 'alias_' + key) { + $scope.collection = alias; + } + } + // Decide what is selected in list + if ($routeParams.collection && !$scope.collection) { + alert("No collection or alias called " + $routeParams.collection); + $location.path("/~collections"); + } + }); + + $scope.liveNodes = data.cluster.liveNodes; + }); + Zookeeper.configs(function(data) { + $scope.configs = []; + var items = data.tree[0].children; + for (var i in items) { + $scope.configs.push({name: items[i].text}); + } + }); + }; + + $scope.hideAll = function() { + $scope.showRename = false; + $scope.showAdd = false; + $scope.showDelete = false; + $scope.showSwap = false; + $scope.showCreateAlias = false; + $scope.showDeleteAlias = false; + }; + + $scope.showAddCollection = function() { + $scope.hideAll(); + $scope.showAdd = true; + $scope.newCollection = { + name: "", + routerName: "compositeId", + numShards: 1, + configName: "", + replicationFactor: 1, + maxShardsPerNode: 1, + autoAddReplicas: 'false' + }; + }; + + $scope.toggleCreateAlias = function() { + $scope.hideAll(); + $scope.showCreateAlias = true; + } + + $scope.toggleDeleteAlias = function() { + $scope.hideAll(); + $scope.showDeleteAlias = true; + } + + $scope.cancelCreateAlias = $scope.cancelDeleteAlias = function() { + $scope.hideAll(); + } + + $scope.createAlias = function() { + var collections = []; + for (var i in $scope.aliasCollections) { + collections.push($scope.aliasCollections[i].name); + } + Collections.createAlias({name: $scope.aliasToCreate, collections: collections.join(",")}, function(data) { + $scope.cancelCreateAlias(); + $scope.resetMenu("collections", Constants.IS_ROOT_PAGE); + $location.path("/~collections/alias_" + $scope.aliasToCreate); + }); + } + $scope.deleteAlias = function() { + Collections.deleteAlias({name: $scope.collection.name}, function(data) { + $scope.hideAll(); + $scope.resetMenu("collections", Constants.IS_ROOT_PAGE); + $location.path("/~collections/"); + }); + + }; + $scope.addCollection = function() { + if (!$scope.newCollection.name) { + $scope.addMessage = "Please provide a core name"; + } else if (false) { //@todo detect whether core exists + $scope.AddMessage = "A core with that name already exists"; + } else { + var coll = $scope.newCollection; + var params = { + name: coll.name, + "router.name": coll.routerName, + numShards: coll.numShards, + "collection.configName": coll.configName, + replicationFactor: coll.replicationFactor, + maxShardsPerNode: coll.maxShardsPerNode, + autoAddReplicas: coll.autoAddReplicas + }; + if (coll.shards) params.shards = coll.shards; + if (coll.routerField) params["router.field"] = coll.routerField; + Collections.add(params, function(data) { + $scope.cancelAddCollection(); + $scope.resetMenu("collections", Constants.IS_ROOT_PAGE); + $location.path("/~collections/" + $scope.newCollection.name); + }); + } + }; + + $scope.cancelAddCollection = function() { + delete $scope.addMessage; + $scope.showAdd = false; + }; + + $scope.showDeleteCollection = function() { + $scope.hideAll(); + if ($scope.collection) { + $scope.showDelete = true; + } else { + alert("No collection selected."); + } + }; + + $scope.deleteCollection = function() { + if ($scope.collection.name == $scope.collectionDeleteConfirm) { + Collections.delete({name: $scope.collection.name}, function (data) { + $location.path("/~collections"); + }); + } else { + $scope.deleteMessage = "Collection names do not match."; + } + }; + + $scope.reloadCollection = function() { + if (!$scope.collection) { + alert("No collection selected."); + return; + } + Collections.reload({name: $scope.collection.name}, + function(successData) { + $scope.reloadSuccess = true; + $timeout(function() {$scope.reloadSuccess=false}, 1000); + }, + function(failureData) { + $scope.reloadFailure = true; + $timeout(function() {$scope.reloadFailure=false}, 1000); + $location.path("/~collections"); + }); + }; + + $scope.toggleAddReplica = function(shard) { + $scope.hideAll(); + shard.showAdd = !shard.showAdd; + delete $scope.addReplicaMessage; + + Zookeeper.liveNodes({}, function(data) { + $scope.nodes = []; + var children = data.tree[0].children; + for (var child in children) { + $scope.nodes.push(children[child].data.title); + } + }); + }; + + $scope.toggleRemoveReplica = function(replica) { + $scope.hideAll(); + replica.showRemove = !replica.showRemove; + }; + + $scope.toggleRemoveShard = function(shard) { + $scope.hideAll(); + shard.showRemove = !shard.showRemove; + }; + + $scope.deleteShard = function(shard) { + Collections.deleteShard({collection: shard.collection, shard:shard.name}, function(data) { + shard.deleted = true; + $timeout(function() { + $scope.refresh(); + }, 2000); + }); + } + + $scope.deleteReplica = function(replica) { + Collections.deleteReplica({collection: replica.collection, shard:replica.shard, replica:replica.name}, function(data) { + replica.deleted = true; + $timeout(function() { + $scope.refresh(); + }, 2000); + }); + } + $scope.addReplica = function(shard) { + var params = { + collection: shard.collection, + shard: shard.name, + } + if (shard.replicaNodeName && shard.replicaNodeName != "") { + params.node = shard.replicaNodeName; + } + Collections.addReplica(params, function(data) { + shard.replicaAdded = true; + $timeout(function () { + shard.replicaAdded = false; + shard.showAdd = false; + $$scope.refresh(); + }, 2000); + }); + }; + + $scope.toggleShard = function(shard) { + shard.show = !shard.show; + } + + $scope.toggleReplica = function(replica) { + replica.show = !replica.show; + } + + $scope.refresh(); + } +); + +var flatten = function(data) { + var list = []; + for (var name in data) { + var entry = data[name]; + entry.name = name; + list.push(entry); + } + return list; +} diff --git a/KeywordSearch/solr/server/solr-webapp/webapp/js/angular/controllers/core-overview.js b/KeywordSearch/solr/server/solr-webapp/webapp/js/angular/controllers/core-overview.js new file mode 100755 index 0000000000..8f86575669 --- /dev/null +++ b/KeywordSearch/solr/server/solr-webapp/webapp/js/angular/controllers/core-overview.js @@ -0,0 +1,93 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You 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. +*/ + +solrAdminApp.controller('CoreOverviewController', +function($scope, $rootScope, $routeParams, Luke, CoreSystem, Update, Replication, Ping, Constants) { + $scope.resetMenu("overview", Constants.IS_CORE_PAGE); + $scope.refreshIndex = function() { + Luke.index({core: $routeParams.core}, + function(data) { + $scope.index = data.index; + delete $scope.statsMessage; + }, + function(error) { + $scope.statsMessage = "Luke is not configured"; + } + ); + }; + + $scope.refreshReplication = function() { + Replication.details({core: $routeParams.core}, + function(data) { + $scope.isSlave = data.details.isSlave == "true"; + $scope.isMaster = data.details.isMaster == "true"; + $scope.replication = data.details; + }, + function(error) { + $scope.replicationMessage = "Replication is not configured"; + }); + }; + + $scope.refreshSystem = function() { + CoreSystem.get({core: $routeParams.core}, + function(data) { + $scope.core = data.core; + delete $scope.systemMessage; + }, + function(error) { + $scope.systemMessage = "/admin/system Handler is not configured"; + } + ); + }; + + $scope.refreshPing = function() { + Ping.status({core: $routeParams.core}, function(data) { + if (data.error) { + $scope.healthcheckStatus = false; + if (data.error.code == 503) { + $scope.healthcheckMessage = 'Ping request handler is not configured with a healthcheck file.'; + } + } else { + $scope.healthcheckStatus = data.status == "enabled"; + } + }); + }; + + $scope.toggleHealthcheck = function() { + if ($scope.healthcheckStatus) { + Ping.disable( + function(data) {$scope.healthcheckStatus = false}, + function(error) {$scope.healthcheckMessage = error} + ); + } else { + Ping.enable( + function(data) {$scope.healthcheckStatus = true}, + function(error) {$scope.healthcheckMessage = error} + ); + } + }; + + $scope.refresh = function() { + $scope.refreshIndex(); + $scope.refreshReplication(); + $scope.refreshSystem(); + $scope.refreshPing(); + }; + + $scope.refresh(); +}); + diff --git a/KeywordSearch/solr/server/solr-webapp/webapp/js/angular/controllers/cores.js b/KeywordSearch/solr/server/solr-webapp/webapp/js/angular/controllers/cores.js new file mode 100755 index 0000000000..d5c4513eb6 --- /dev/null +++ b/KeywordSearch/solr/server/solr-webapp/webapp/js/angular/controllers/cores.js @@ -0,0 +1,180 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You 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. +*/ + +solrAdminApp.controller('CoreAdminController', + function($scope, $routeParams, $location, $timeout, $route, Cores, Update, Constants){ + $scope.resetMenu("cores", Constants.IS_ROOT_PAGE); + $scope.selectedCore = $routeParams.corename; // use 'corename' not 'core' to distinguish from /solr/:core/ + $scope.refresh = function() { + Cores.get(function(data) { + var coreCount = 0; + var cores = data.status; + for (_obj in cores) coreCount++; + $scope.hasCores = coreCount >0; + if (!$scope.selectedCore && coreCount==0) { + $scope.showAddCore(); + return; + } else if (!$scope.selectedCore) { + for (firstCore in cores) break; + $scope.selectedCore = firstCore; + $location.path("/~cores/" + $scope.selectedCore).replace(); + } + $scope.core = cores[$scope.selectedCore]; + $scope.corelist = []; + $scope.swapCorelist = []; + for (var core in cores) { + $scope.corelist.push(cores[core]); + if (cores[core] != $scope.core) { + $scope.swapCorelist.push(cores[core]); + } + } + if ($scope.swapCorelist.length>0) { + $scope.swapOther = $scope.swapCorelist[0].name; + } + }); + }; + $scope.showAddCore = function() { + $scope.hideAll(); + $scope.showAdd = true; + $scope.newCore = { + name: "new_core", + dataDir: "data", + instanceDir: "new_core", + config: "solrconfig.xml", + schema: "schema.xml", + collection: "", + shard: "" + }; + }; + + $scope.addCore = function() { + if (!$scope.newCore.name) { + $scope.addMessage = "Please provide a core name"; + } else if (false) { //@todo detect whether core exists + $scope.AddMessage = "A core with that name already exists"; + } else { + var params = { + name: $scope.newCore.name, + instanceDir: $scope.newCore.instanceDir, + config: $scope.newCore.config, + schema: $scope.newCore.schema, + dataDir: $scope.newCore.dataDir + }; + if ($scope.isCloud) { + params.collection = $scope.newCore.collection; + params.shard = $scope.newCore.shard; + } + Cores.add(params, function(data) { + $location.path("/~cores/" + $scope.newCore.name); + $scope.cancelAddCore(); + }); + } + }; + + $scope.cancelAddCore = function() { + delete $scope.addMessage; + $scope.showAdd = false + }; + + $scope.unloadCore = function() { + var answer = confirm( 'Do you really want to unload Core "' + $scope.selectedCore + '"?' ); + if( !answer ) return; + Cores.unload({core: $scope.selectedCore}, function(data) { + $location.path("/~cores"); + }); + }; + + $scope.showRenameCore = function() { + $scope.hideAll(); + $scope.showRename = true; + }; + + $scope.renameCore = function() { + if (!$scope.other) { + $scope.renameMessage = "Please provide a new name for the " + $scope.selectedCore + " core"; + } else if ($scope.other == $scope.selectedCore) { + $scope.renameMessage = "New name must be different from the current one"; + } else { + Cores.rename({core:$scope.selectedCore, other: $scope.other}, function(data) { + $location.path("/~cores/" + $scope.other); + $scope.cancelRename(); + }); + } + }; + + $scope.cancelRenameCore = function() { + $scope.showRename = false; + delete $scope.renameMessage; + $scope.other = ""; + }; + + $scope.showSwapCores = function() { + $scope.hideAll(); + $scope.showSwap = true; + }; + + $scope.swapCores = function() { + if (!$scope.swapOther) { + $scope.swapMessage = "Please select a core to swap with"; + } else if ($scope.swapOther == $scope.selectedCore) { + $scope.swapMessage = "Cannot swap with the same core"; + } else { + Cores.swap({core: $scope.selectedCore, other: $scope.swapOther}, function(data) { + $location.path("/~cores/" + $scope.swapOther); + delete $scope.swapOther; + $scope.cancelSwapCores(); + }); + } + }; + + $scope.cancelSwapCores = function() { + delete $scope.swapMessage; + $scope.showSwap = false; + } + + $scope.reloadCore = function() { + if ($scope.initFailures[$scope.selectedCore]) { + delete $scope.initFailures[$scope.selectedCore]; + $scope.showInitFailures = Object.keys(data.initFailures).length>0; + } + Cores.reload({core: $scope.selectedCore}, + function(data) { + if (data.error) { + $scope.reloadFailure = true; + $timeout(function() { + $scope.reloadFailure = false; + $route.reload(); + }, 1000); + } else { + $scope.reloadSuccess = true; + $timeout(function () { + $scope.reloadSuccess = false; + $route.reload(); + }, 1000); + } + }); + }; + + $scope.hideAll = function() { + $scope.showRename = false; + $scope.showAdd = false; + $scope.showSwap = false; + }; + + $scope.refresh(); + } +); diff --git a/KeywordSearch/solr/server/solr-webapp/webapp/js/angular/controllers/dataimport.js b/KeywordSearch/solr/server/solr-webapp/webapp/js/angular/controllers/dataimport.js new file mode 100755 index 0000000000..da6199eb01 --- /dev/null +++ b/KeywordSearch/solr/server/solr-webapp/webapp/js/angular/controllers/dataimport.js @@ -0,0 +1,302 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You 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. +*/ + +var dataimport_timeout = 2000; + +solrAdminApp.controller('DataImportController', + function($scope, $rootScope, $routeParams, $location, $timeout, $interval, $cookies, Mbeans, DataImport, Constants) { + $scope.resetMenu("dataimport", Constants.IS_COLLECTION_PAGE); + + $scope.refresh = function () { + Mbeans.info({core: $routeParams.core, cat: 'QUERY'}, function (data) { + var mbeans = data['solr-mbeans'][1]; + $scope.handlers = []; + for (var key in mbeans) { + if (mbeans[key]['class'] !== key && mbeans[key]['class'] === 'org.apache.solr.handler.dataimport.DataImportHandler') { + $scope.handlers.push(key); + } + } + $scope.hasHandlers = $scope.handlers.length > 0; + + if (!$routeParams.handler) { + $location.path("/" + $routeParams.core + "/dataimport/" + $scope.handlers[0]); + } else { + $scope.currentHandler = $routeParams.handler; + } + }); + + $scope.handler = $routeParams.handler; + if ($scope.handler && $scope.handler[0]=="/") { + $scope.handler = $scope.handler.substr(1); + } + if ($scope.handler) { + DataImport.config({core: $routeParams.core, name: $scope.handler}, function (data) { + try { + $scope.config = data.config; + var xml = $.parseXML(data.config); + $scope.entities = []; + $('document > entity', xml).each(function (i, element) { + $scope.entities.push($(element).attr('name')); + }); + $scope.refreshStatus(); + } catch (err) { + console.log(err); + } + }); + } + $scope.lastUpdate = "unknown"; + $scope.lastUpdateUTC = ""; + }; + + $scope.toggleDebug = function () { + $scope.isDebugMode = !$scope.isDebugMode; + if ($scope.isDebugMode) { + // also enable Debug checkbox + $scope.form.showDebug = true; + } + $scope.showConfiguration = true; + } + + $scope.toggleConfiguration = function () { + $scope.showConfiguration = !$scope.showConfiguration; + } + + $scope.toggleRawStatus = function () { + $scope.showRawStatus = !$scope.showRawStatus; + } + + $scope.toggleRawDebug = function () { + $scope.showRawDebug = !$scope.showRawDebug; + } + + $scope.reload = function () { + DataImport.reload({core: $routeParams.core, name: $scope.handler}, function () { + $scope.reloaded = true; + $timeout(function () { + $scope.reloaded = false; + }, 5000); + $scope.refresh(); + }); + } + + $scope.form = { + command: "full-import", + verbose: false, + clean: false, + commit: true, + showDebug: false, + custom: "", + core: $routeParams.core + }; + + $scope.submit = function () { + var params = {}; + for (var key in $scope.form) { + if (key == "showDebug") { + if ($scope.form.showDebug) { + params["debug"] = true; + } + } else { + params[key] = $scope.form[key]; + } + } + if (params.custom.length) { + var customParams = $scope.form.custom.split("&"); + for (var i in customParams) { + var parts = customParams[i].split("="); + params[parts[0]] = parts[1]; + } + } + delete params.custom; + + if ($scope.isDebugMode) { + params.dataConfig = $scope.config; + } + + params.core = $routeParams.core; + params.name = $scope.handler; + + DataImport.post(params, function (data) { + $scope.rawResponse = JSON.stringify(data, null, 2); + $scope.refreshStatus(); + }); + }; + + $scope.abort = function () { + $scope.isAborting = true; + DataImport.abort({core: $routeParams.core, name: $scope.handler}, function () { + $timeout(function () { + $scope.isAborting = false; + $scope.refreshStatus(); + }, 4000); + }); + } + + $scope.refreshStatus = function () { + + console.log("Refresh Status"); + + $scope.isStatusLoading = true; + DataImport.status({core: $routeParams.core, name: $scope.handler}, function (data) { + if (data[0] == "<") { + $scope.hasHandlers = false; + return; + } + + var now = new Date(); + $scope.lastUpdate = now.toTimeString().split(' ').shift(); + $scope.lastUpdateUTC = now.toUTCString(); + var messages = data.statusMessages; + var messagesCount = 0; + for( var key in messages ) { messagesCount++; } + + if (data.status == 'busy') { + $scope.status = "indexing"; + + $scope.timeElapsed = data.statusMessages['Time Elapsed']; + $scope.elapsedSeconds = parseSeconds($scope.timeElapsed); + + var info = $scope.timeElapsed ? 'Indexing since ' + $scope.timeElapsed : 'Indexing ...'; + $scope.info = showInfo(messages, true, info, $scope.elapsedSeconds); + + } else if (messages.RolledBack) { + $scope.status = "failure"; + $scope.info = showInfo(messages, true); + } else if (messages.Aborted) { + $scope.status = "aborted"; + $scope.info = showInfo(messages, true, 'Aborting current Import ...'); + } else if (data.status == "idle" && messagesCount != 0) { + $scope.status = "success"; + $scope.info = showInfo(messages, true); + } else { + $scope.status = "idle"; + $scope.info = showInfo(messages, false, 'No information available (idle)'); + } + + delete data.$promise; + delete data.$resolved; + + $scope.rawStatus = JSON.stringify(data, null, 2); + + $scope.isStatusLoading = false; + $scope.statusUpdated = true; + $timeout(function () { + $scope.statusUpdated = false; + }, dataimport_timeout / 2); + }); + }; + + $scope.updateAutoRefresh = function () { + $scope.autorefresh = !$scope.autorefresh; + $cookies.dataimport_autorefresh = $scope.autorefresh ? true : null; + if ($scope.autorefresh) { + $scope.refreshTimeout = $interval($scope.refreshStatus, dataimport_timeout); + var onRouteChangeOff = $scope.$on('$routeChangeStart', function() { + $interval.cancel($scope.refreshTimeout); + onRouteChangeOff(); + }); + + } else if ($scope.refreshTimeout) { + $interval.cancel($scope.refreshTimeout); + } + $scope.refreshStatus(); + }; + + $scope.refresh(); + +}); + +var showInfo = function (messages, showFull, info_text, elapsed_seconds) { + + var info = {}; + if (info_text) { + info.text = info_text; + } else { + info.text = messages[''] || ''; + // format numbers included in status nicely + /* @todo this pretty printing is hard to work out how to do in an Angularesque way: + info.text = info.text.replace(/\d{4,}/g, + function (match, position, string) { + return app.format_number(parseInt(match, 10)); + } + ); + */ + + var time_taken_text = messages['Time taken']; + info.timeTaken = parseSeconds(time_taken_text); + } + info.showDetails = false; + + if (showFull) { + if (!elapsed_seconds) { + var time_taken_text = messages['Time taken']; + elapsed_seconds = parseSeconds(time_taken_text); + } + + info.showDetails = true; + + var document_config = { + 'Requests': 'Total Requests made to DataSource', + 'Fetched': 'Total Rows Fetched', + 'Skipped': 'Total Documents Skipped', + 'Processed': 'Total Documents Processed' + }; + + info.docs = []; + for (var key in document_config) { + var value = parseInt(messages[document_config[key]], 10); + var doc = {desc: document_config[key], name: key, value: value}; + if (elapsed_seconds && key != 'Skipped') { + doc.speed = Math.round(value / elapsed_seconds); + } + info.docs.push(doc); + } + + var dates_config = { + 'Started': 'Full Dump Started', + 'Aborted': 'Aborted', + 'Rolledback': 'Rolledback' + }; + + info.dates = []; + for (var key in dates_config) { + var value = messages[dates_config[key]]; + if (value) { + value = value.replace(" ", "T")+".000Z"; + console.log(value); + var date = {desc: dates_config[key], name: key, value: value}; + info.dates.push(date); + } + } + } + return info; +} + +var parseSeconds = function(time) { + var seconds = 0; + var arr = new String(time || '').split('.'); + var parts = arr[0].split(':').reverse(); + + for (var i = 0; i < parts.length; i++) { + seconds += ( parseInt(parts[i], 10) || 0 ) * Math.pow(60, i); + } + + if (arr[1] && 5 <= parseInt(arr[1][0], 10)) { + seconds++; // treat more or equal than .5 as additional second + } + return seconds; +} diff --git a/KeywordSearch/solr/server/solr-webapp/webapp/js/angular/controllers/documents.js b/KeywordSearch/solr/server/solr-webapp/webapp/js/angular/controllers/documents.js new file mode 100755 index 0000000000..69f763cd44 --- /dev/null +++ b/KeywordSearch/solr/server/solr-webapp/webapp/js/angular/controllers/documents.js @@ -0,0 +1,137 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You 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. + */ +//helper for formatting JSON and others + +var DOC_PLACEHOLDER = '\n' + + 'change.me' + + 'change.me' + + ''; + +var ADD_PLACEHOLDER = '\n' + DOC_PLACEHOLDER + '\n'; + +solrAdminApp.controller('DocumentsController', + function($scope, $rootScope, $routeParams, $location, Luke, Update, FileUpload, Constants) { + $scope.resetMenu("documents", Constants.IS_COLLECTION_PAGE); + + $scope.refresh = function () { + Luke.schema({core: $routeParams.core}, function(data) { + //TODO: handle dynamic fields + delete data.schema.fields._version_; + $scope.fields = Object.keys(data.schema.fields); + }); + $scope.document = ""; + $scope.handler = "/update"; + $scope.type = "json"; + $scope.commitWithin = 1000; + $scope.overwrite = true; + }; + + $scope.refresh(); + + $scope.changeDocumentType = function () { + $scope.placeholder = ""; + if ($scope.type == 'json') { + $scope.placeholder = '{"id":"change.me","title":"change.me"}'; + } else if ($scope.type == 'csv') { + $scope.placeholder = "id,title\nchange.me,change.me"; + } else if ($scope.type == 'solr') { + $scope.placeholder = ADD_PLACEHOLDER; + } else if ($scope.type == 'xml') { + $scope.placeholder = DOC_PLACEHOLDER; + } + }; + + $scope.addWizardField = function () { + if ($scope.document == "") $scope.document = "{}"; + var doc = JSON.parse($scope.document); + doc[$scope.fieldName] = $scope.fieldData; + $scope.document = JSON.stringify(doc, null, '\t'); + $scope.fieldData = ""; + }; + + $scope.submit = function () { + var contentType = ""; + var postData = ""; + var params = {}; + var doingFileUpload = false; + + if ($scope.handler[0] == '/') { + params.handler = $scope.handler.substring(1); + } else { + params.handler = 'update'; + params.qt = $scope.handler; + } + + params.commitWithin = $scope.commitWithin; + params.overwrite = $scope.overwrite; + params.core = $routeParams.core; + params.wt = "json"; + + if ($scope.type == "json" || $scope.type == "wizard") { + postData = "[" + $scope.document + "]"; + contentType = "json"; + } else if ($scope.type == "csv") { + postData = $scope.document; + contentType = "csv"; + } else if ($scope.type == "xml") { + postData = "" + $scope.document + ""; + contentType = "xml"; + } else if ($scope.type == "upload") { + doingFileUpload = true; + params.raw = $scope.literalParams; + } else if ($scope.type == "solr") { + postData = $scope.document; + if (postData[0] == "<") { + contentType = "xml"; + } else if (postData[0] == "{" || postData[0] == '[') { + contentType = "json"; + } else { + alert("Cannot identify content type") + } + } + if (!doingFileUpload) { + var callback = function (success) { + $scope.responseStatus = "success"; + delete success.$promise; + delete success.$resolved; + $scope.response = JSON.stringify(success, null, ' '); + }; + var failure = function (failure) { + $scope.responseStatus = failure; + }; + if (contentType == "json") { + Update.postJson(params, postData, callback, failure); + } else if (contentType == "xml") { + Update.postXml(params, postData, callback, failure); + } else if (contentType == "csv") { + Update.postCsv(params, postData, callback, failure); + } + } else { + var file = $scope.fileUpload; + console.log('file is ' + JSON.stringify(file)); + var uploadUrl = "/fileUpload"; + FileUpload.upload(params, $scope.fileUpload, function (success) { + $scope.responseStatus = "success"; + $scope.response = JSON.stringify(success, null, ' '); + }, function (failure) { + $scope.responseStatus = "failure"; + $scope.response = JSON.stringify(failure, null, ' '); + }); + } + } + }); + diff --git a/KeywordSearch/solr/server/solr-webapp/webapp/js/angular/controllers/files.js b/KeywordSearch/solr/server/solr-webapp/webapp/js/angular/controllers/files.js new file mode 100755 index 0000000000..6155d3ea96 --- /dev/null +++ b/KeywordSearch/solr/server/solr-webapp/webapp/js/angular/controllers/files.js @@ -0,0 +1,98 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You 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. +*/ + +var contentTypeMap = { xml : 'text/xml', html : 'text/html', js : 'text/javascript', json : 'application/json', 'css' : 'text/css' }; +var languages = {js: "javascript", xml:"xml", xsl:"xml", vm: "xml", html: "xml", json: "json", css: "css"}; + +solrAdminApp.controller('FilesController', + function($scope, $rootScope, $routeParams, $location, Files, Constants) { + $scope.resetMenu("files", Constants.IS_COLLECTION_PAGE); + + $scope.file = $location.search().file; + $scope.content = null; + + $scope.baseurl = $location.absUrl().substr(0,$location.absUrl().indexOf("#")); // Including /solr/ context + + $scope.refresh = function () { + + var process = function (path, tree) { + var params = {core: $routeParams.core}; + if (path.slice(-1) == '/') { + params.file = path.slice(0, -1); + } else if (path!='') { + params.file = path; + } + + Files.list(params, function (data) { + var filenames = Object.keys(data.files); + filenames.sort(); + for (var i in filenames) { + var file = filenames[i]; + var filedata = data.files[file]; + var state = undefined; + var children = undefined; + + if (filedata.directory) { + file = file + "/"; + if ($scope.file && $scope.file.indexOf(path + file) == 0) { + state = "open"; + } else { + state = "closed"; + } + children = []; + process(path + file, children); + } + tree.push({ + text: file, + a_attr: { id: path + file}, + children: children, + state: state + }); + } + }); + }; + $scope.tree = []; + process("", $scope.tree); + + if ($scope.file && $scope.file != '' && $scope.file.split('').pop()!='/') { + var extension; + if ($scope.file == "managed-schema") { + extension = contentTypeMap['xml']; + } else { + extension = $scope.file.match( /\.(\w+)$/)[1] || ''; + } + var contentType = (contentTypeMap[extension] || 'text/plain' ) + ';charset=utf-8'; + + Files.get({core: $routeParams.core, file: $scope.file, contentType: contentType}, function(data) { + $scope.content = data.data; + $scope.url = data.config.url + "?" + $.param(data.config.params); // relative URL + if (contentType.indexOf("text/plain") && (data.data.indexOf("=0) || data.data.indexOf("",r:10},{cN:"cdata",b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{cN:"tag",b:"|$)",e:">",k:{title:"style"},c:[b],starts:{e:"",rE:true,sL:"css"}},{cN:"tag",b:"|$)",e:">",k:{title:"script"},c:[b],starts:{e:"<\/script>",rE:true,sL:"javascript"}},{b:"<%",e:"%>",sL:"vbscript"},{cN:"tag",b:"",c:[{cN:"title",b:"[^ />]+"},b]}]}}(hljs);hljs.LANGUAGES.php=function(a){var e={cN:"variable",b:"\\$+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*"};var b=[a.inherit(a.ASM,{i:null}),a.inherit(a.QSM,{i:null}),{cN:"string",b:'b"',e:'"',c:[a.BE]},{cN:"string",b:"b'",e:"'",c:[a.BE]}];var c=[a.BNM,a.CNM];var d={cN:"title",b:a.UIR};return{cI:true,k:"and include_once list abstract global private echo interface as static endswitch array null if endwhile or const for endforeach self var while isset public protected exit foreach throw elseif include __FILE__ empty require_once do xor return implements parent clone use __CLASS__ __LINE__ else break print eval new catch __METHOD__ case exception php_user_filter default die require __FUNCTION__ enddeclare final try this switch continue endfor endif declare unset true false namespace trait goto instanceof insteadof __DIR__ __NAMESPACE__ __halt_compiler",c:[a.CLCM,a.HCM,{cN:"comment",b:"/\\*",e:"\\*/",c:[{cN:"phpdoc",b:"\\s@[A-Za-z]+"}]},{cN:"comment",eB:true,b:"__halt_compiler.+?;",eW:true},{cN:"string",b:"<<<['\"]?\\w+['\"]?$",e:"^\\w+;",c:[a.BE]},{cN:"preprocessor",b:"<\\?php",r:10},{cN:"preprocessor",b:"\\?>"},e,{cN:"function",bWK:true,e:"{",k:"function",i:"\\$|\\[|%",c:[d,{cN:"params",b:"\\(",e:"\\)",c:["self",e,a.CBLCLM].concat(b).concat(c)}]},{cN:"class",bWK:true,e:"{",k:"class",i:"[:\\(\\$]",c:[{bWK:true,eW:true,k:"extends",c:[d]},d]},{b:"=>"}].concat(b).concat(c)}}(hljs);hljs.LANGUAGES.python=function(a){var f={cN:"prompt",b:"^(>>>|\\.\\.\\.) "};var c=[{cN:"string",b:"(u|b)?r?'''",e:"'''",c:[f],r:10},{cN:"string",b:'(u|b)?r?"""',e:'"""',c:[f],r:10},{cN:"string",b:"(u|r|ur)'",e:"'",c:[a.BE],r:10},{cN:"string",b:'(u|r|ur)"',e:'"',c:[a.BE],r:10},{cN:"string",b:"(b|br)'",e:"'",c:[a.BE]},{cN:"string",b:'(b|br)"',e:'"',c:[a.BE]}].concat([a.ASM,a.QSM]);var e={cN:"title",b:a.UIR};var d={cN:"params",b:"\\(",e:"\\)",c:["self",a.CNM,f].concat(c)};var b={bWK:true,e:":",i:"[${=;\\n]",c:[e,d],r:10};return{k:{keyword:"and elif is global as in if from raise for except finally print import pass return exec else break not with class assert yield try while continue del or def lambda nonlocal|10",built_in:"None True False Ellipsis NotImplemented"},i:"(|\\?)",c:c.concat([f,a.HCM,a.inherit(b,{cN:"function",k:"def"}),a.inherit(b,{cN:"class",k:"class"}),a.CNM,{cN:"decorator",b:"@",e:"$"},{b:"\\b(print|exec)\\("}])}}(hljs);hljs.LANGUAGES.json=function(a){var e={literal:"true false null"};var d=[a.QSM,a.CNM];var c={cN:"value",e:",",eW:true,eE:true,c:d,k:e};var b={b:"{",e:"}",c:[{cN:"attribute",b:'\\s*"',e:'"\\s*:\\s*',eB:true,eE:true,c:[a.BE],i:"\\n",starts:c}],i:"\\S"};var f={b:"\\[",e:"\\]",c:[a.inherit(c,{cN:null})],i:"\\S"};d.splice(d.length,0,b,f);return{c:d,k:e,i:"\\S"}}(hljs); diff --git a/KeywordSearch/solr/server/solr-webapp/webapp/libs/jquery-3.4.1.min.js b/KeywordSearch/solr/server/solr-webapp/webapp/libs/jquery-3.4.1.min.js new file mode 100755 index 0000000000..b612235b75 --- /dev/null +++ b/KeywordSearch/solr/server/solr-webapp/webapp/libs/jquery-3.4.1.min.js @@ -0,0 +1,28 @@ +/*! + +Copyright 2020 jQuery Foundation and other contributors +http://jquery.com/ + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +/*! jQuery v3.4.1 | (c) JS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],E=C.document,r=Object.getPrototypeOf,s=t.slice,g=t.concat,u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.4.1",k=function(e,t){return new k.fn.init(e,t)},p=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;function d(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp($),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+$),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ne=function(e,t,n){var r="0x"+t-65536;return r!=r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(m.childNodes),m.childNodes),t[m.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&((e?e.ownerDocument||e:m)!==C&&T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!A[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&U.test(t)){(s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=k),o=(l=h(t)).length;while(o--)l[o]="#"+s+" "+xe(l[o]);c=l.join(","),f=ee.test(t)&&ye(e.parentNode)||e}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){A(t,!0)}finally{s===k&&e.removeAttribute("id")}}}return g(t.replace(B,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[k]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:m;return r!==C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),m!==C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=k,!C.getElementsByName||!C.getElementsByName(k).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+k+"-]").length||v.push("~="),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+k+"+*").length||v.push(".#.+[+~]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",$)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e===C||e.ownerDocument===m&&y(m,e)?-1:t===C||t.ownerDocument===m&&y(m,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===C?-1:t===C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]===m?-1:s[r]===m?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if((e.ownerDocument||e)!==C&&T(e),d.matchesSelector&&E&&!A[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){A(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=p[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&p(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?k.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?k.grep(e,function(e){return e===n!==r}):"string"!=typeof n?k.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(k.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:L.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof k?t[0]:t,k.merge(this,k.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),D.test(r[1])&&k.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(k):k.makeArray(e,this)}).prototype=k.fn,q=k(E);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}k.fn.extend({has:function(e){var t=k(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?k.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;nx",y.noCloneChecked=!!me.cloneNode(!0).lastChild.defaultValue;var Te=/^key/,Ce=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ee=/^([^.]*)(?:\.(.+)|)/;function ke(){return!0}function Se(){return!1}function Ne(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function Ae(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)Ae(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Se;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return k().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=k.guid++)),e.each(function(){k.event.add(this,t,i,r,n)})}function De(e,i,o){o?(Q.set(e,i,!1),k.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Q.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(k.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Q.set(this,i,r),t=o(this,i),this[i](),r!==(n=Q.get(this,i))||t?Q.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Q.set(this,i,{value:k.event.trigger(k.extend(r[0],k.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Q.get(e,i)&&k.event.add(e,i,ke)}k.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.get(t);if(v){n.handler&&(n=(o=n).handler,i=o.selector),i&&k.find.matchesSelector(ie,i),n.guid||(n.guid=k.guid++),(u=v.events)||(u=v.events={}),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof k&&k.event.triggered!==e.type?k.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(R)||[""]).length;while(l--)d=g=(s=Ee.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=k.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=k.event.special[d]||{},c=k.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&k.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),k.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.hasData(e)&&Q.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(R)||[""]).length;while(l--)if(d=g=(s=Ee.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=k.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||k.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)k.event.remove(e,d+t[l],n,r,!0);k.isEmptyObject(u)&&Q.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=k.event.fix(e),u=new Array(arguments.length),l=(Q.get(this,"events")||{})[s.type]||[],c=k.event.special[s.type]||{};for(u[0]=s,t=1;t\x20\t\r\n\f]*)[^>]*)\/>/gi,qe=/\s*$/g;function Oe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&k(e).children("tbody")[0]||e}function Pe(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Re(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Me(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(Q.hasData(e)&&(o=Q.access(e),a=Q.set(t,o),l=o.events))for(i in delete a.handle,a.events={},l)for(n=0,r=l[i].length;n")},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=oe(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||k.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Vt,Gt=[],Yt=/(=)\?(?=&|$)|\?\?/;k.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Gt.pop()||k.expando+"_"+kt++;return this[e]=!0,e}}),k.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Yt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Yt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Yt,"$1"+r):!1!==e.jsonp&&(e.url+=(St.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||k.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?k(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Gt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Vt=E.implementation.createHTMLDocument("").body).innerHTML="
",2===Vt.childNodes.length),k.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=D.exec(e))?[t.createElement(i[1])]:(i=we([e],t,o),o&&o.length&&k(o).remove(),k.merge([],i.childNodes)));var r,i,o},k.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(k.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},k.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){k.fn[t]=function(e){return this.on(t,e)}}),k.expr.pseudos.animated=function(t){return k.grep(k.timers,function(e){return t===e.elem}).length},k.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=k.css(e,"position"),c=k(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=k.css(e,"top"),u=k.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,k.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},k.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){k.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===k.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===k.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=k(e).offset()).top+=k.css(e,"borderTopWidth",!0),i.left+=k.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-k.css(r,"marginTop",!0),left:t.left-i.left-k.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===k.css(e,"position"))e=e.offsetParent;return e||ie})}}),k.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;k.fn[t]=function(e){return _(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),k.each(["top","left"],function(e,n){k.cssHooks[n]=ze(y.pixelPosition,function(e,t){if(t)return t=_e(e,n),$e.test(t)?k(e).position()[n]+"px":t})}),k.each({Height:"height",Width:"width"},function(a,s){k.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){k.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return _(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?k.css(e,t,i):k.style(e,t,n,i)},s,n?e:void 0,n)}})}),k.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){k.fn[n]=function(e,t){return 0a;a++)for(i in o[a])n=o[a][i],o[a].hasOwnProperty(i)&&void 0!==n&&(e[i]=t.isPlainObject(n)?t.isPlainObject(e[i])?t.widget.extend({},e[i],n):t.widget.extend({},n):n);return e},t.widget.bridge=function(e,i){var n=i.prototype.widgetFullName||e;t.fn[e]=function(o){var a="string"==typeof o,r=s.call(arguments,1),l=this;return a?this.length||"instance"!==o?this.each(function(){var i,s=t.data(this,n);return"instance"===o?(l=s,!1):s?t.isFunction(s[o])&&"_"!==o.charAt(0)?(i=s[o].apply(s,r),i!==s&&void 0!==i?(l=i&&i.jquery?l.pushStack(i.get()):i,!1):void 0):t.error("no such method '"+o+"' for "+e+" widget instance"):t.error("cannot call methods on "+e+" prior to initialization; "+"attempted to call method '"+o+"'")}):l=void 0:(r.length&&(o=t.widget.extend.apply(null,[o].concat(r))),this.each(function(){var e=t.data(this,n);e?(e.option(o||{}),e._init&&e._init()):t.data(this,n,new i(o,this))})),l}},t.Widget=function(){},t.Widget._childConstructors=[],t.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"
",options:{classes:{},disabled:!1,create:null},_createWidget:function(e,s){s=t(s||this.defaultElement||this)[0],this.element=t(s),this.uuid=i++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=t(),this.hoverable=t(),this.focusable=t(),this.classesElementLookup={},s!==this&&(t.data(s,this.widgetFullName,this),this._on(!0,this.element,{remove:function(t){t.target===s&&this.destroy()}}),this.document=t(s.style?s.ownerDocument:s.document||s),this.window=t(this.document[0].defaultView||this.document[0].parentWindow)),this.options=t.widget.extend({},this.options,this._getCreateOptions(),e),this._create(),this.options.disabled&&this._setOptionDisabled(this.options.disabled),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:function(){return{}},_getCreateEventData:t.noop,_create:t.noop,_init:t.noop,destroy:function(){var e=this;this._destroy(),t.each(this.classesElementLookup,function(t,i){e._removeClass(i,t)}),this.element.off(this.eventNamespace).removeData(this.widgetFullName),this.widget().off(this.eventNamespace).removeAttr("aria-disabled"),this.bindings.off(this.eventNamespace)},_destroy:t.noop,widget:function(){return this.element},option:function(e,i){var s,n,o,a=e;if(0===arguments.length)return t.widget.extend({},this.options);if("string"==typeof e)if(a={},s=e.split("."),e=s.shift(),s.length){for(n=a[e]=t.widget.extend({},this.options[e]),o=0;s.length-1>o;o++)n[s[o]]=n[s[o]]||{},n=n[s[o]];if(e=s.pop(),1===arguments.length)return void 0===n[e]?null:n[e];n[e]=i}else{if(1===arguments.length)return void 0===this.options[e]?null:this.options[e];a[e]=i}return this._setOptions(a),this},_setOptions:function(t){var e;for(e in t)this._setOption(e,t[e]);return this},_setOption:function(t,e){return"classes"===t&&this._setOptionClasses(e),this.options[t]=e,"disabled"===t&&this._setOptionDisabled(e),this},_setOptionClasses:function(e){var i,s,n;for(i in e)n=this.classesElementLookup[i],e[i]!==this.options.classes[i]&&n&&n.length&&(s=t(n.get()),this._removeClass(n,i),s.addClass(this._classes({element:s,keys:i,classes:e,add:!0})))},_setOptionDisabled:function(t){this._toggleClass(this.widget(),this.widgetFullName+"-disabled",null,!!t),t&&(this._removeClass(this.hoverable,null,"ui-state-hover"),this._removeClass(this.focusable,null,"ui-state-focus"))},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_classes:function(e){function i(i,o){var a,r;for(r=0;i.length>r;r++)a=n.classesElementLookup[i[r]]||t(),a=e.add?t(t.unique(a.get().concat(e.element.get()))):t(a.not(e.element).get()),n.classesElementLookup[i[r]]=a,s.push(i[r]),o&&e.classes[i[r]]&&s.push(e.classes[i[r]])}var s=[],n=this;return e=t.extend({element:this.element,classes:this.options.classes||{}},e),this._on(e.element,{remove:"_untrackClassesElement"}),e.keys&&i(e.keys.match(/\S+/g)||[],!0),e.extra&&i(e.extra.match(/\S+/g)||[]),s.join(" ")},_untrackClassesElement:function(e){var i=this;t.each(i.classesElementLookup,function(s,n){-1!==t.inArray(e.target,n)&&(i.classesElementLookup[s]=t(n.not(e.target).get()))})},_removeClass:function(t,e,i){return this._toggleClass(t,e,i,!1)},_addClass:function(t,e,i){return this._toggleClass(t,e,i,!0)},_toggleClass:function(t,e,i,s){s="boolean"==typeof s?s:i;var n="string"==typeof t||null===t,o={extra:n?e:i,keys:n?t:e,element:n?this.element:t,add:s};return o.element.toggleClass(this._classes(o),s),this},_on:function(e,i,s){var n,o=this;"boolean"!=typeof e&&(s=i,i=e,e=!1),s?(i=n=t(i),this.bindings=this.bindings.add(i)):(s=i,i=this.element,n=this.widget()),t.each(s,function(s,a){function r(){return e||o.options.disabled!==!0&&!t(this).hasClass("ui-state-disabled")?("string"==typeof a?o[a]:a).apply(o,arguments):void 0}"string"!=typeof a&&(r.guid=a.guid=a.guid||r.guid||t.guid++);var l=s.match(/^([\w:-]*)\s*(.*)$/),h=l[1]+o.eventNamespace,c=l[2];c?n.on(h,c,r):i.on(h,r)})},_off:function(e,i){i=(i||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,e.off(i).off(i),this.bindings=t(this.bindings.not(e).get()),this.focusable=t(this.focusable.not(e).get()),this.hoverable=t(this.hoverable.not(e).get())},_delay:function(t,e){function i(){return("string"==typeof t?s[t]:t).apply(s,arguments)}var s=this;return setTimeout(i,e||0)},_hoverable:function(e){this.hoverable=this.hoverable.add(e),this._on(e,{mouseenter:function(e){this._addClass(t(e.currentTarget),null,"ui-state-hover")},mouseleave:function(e){this._removeClass(t(e.currentTarget),null,"ui-state-hover")}})},_focusable:function(e){this.focusable=this.focusable.add(e),this._on(e,{focusin:function(e){this._addClass(t(e.currentTarget),null,"ui-state-focus")},focusout:function(e){this._removeClass(t(e.currentTarget),null,"ui-state-focus")}})},_trigger:function(e,i,s){var n,o,a=this.options[e];if(s=s||{},i=t.Event(i),i.type=(e===this.widgetEventPrefix?e:this.widgetEventPrefix+e).toLowerCase(),i.target=this.element[0],o=i.originalEvent)for(n in o)n in i||(i[n]=o[n]);return this.element.trigger(i,s),!(t.isFunction(a)&&a.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},t.each({show:"fadeIn",hide:"fadeOut"},function(e,i){t.Widget.prototype["_"+e]=function(s,n,o){"string"==typeof n&&(n={effect:n});var a,r=n?n===!0||"number"==typeof n?i:n.effect||i:e;n=n||{},"number"==typeof n&&(n={duration:n}),a=!t.isEmptyObject(n),n.complete=o,n.delay&&s.delay(n.delay),a&&t.effects&&t.effects.effect[r]?s[e](n):r!==e&&s[r]?s[r](n.duration,n.easing,o):s.queue(function(i){t(this)[e](),o&&o.call(s[0]),i()})}}),t.widget,function(){function e(t,e,i){return[parseFloat(t[0])*(u.test(t[0])?e/100:1),parseFloat(t[1])*(u.test(t[1])?i/100:1)]}function i(e,i){return parseInt(t.css(e,i),10)||0}function s(e){var i=e[0];return 9===i.nodeType?{width:e.width(),height:e.height(),offset:{top:0,left:0}}:t.isWindow(i)?{width:e.width(),height:e.height(),offset:{top:e.scrollTop(),left:e.scrollLeft()}}:i.preventDefault?{width:0,height:0,offset:{top:i.pageY,left:i.pageX}}:{width:e.outerWidth(),height:e.outerHeight(),offset:e.offset()}}var n,o=Math.max,a=Math.abs,r=/left|center|right/,l=/top|center|bottom/,h=/[\+\-]\d+(\.[\d]+)?%?/,c=/^\w+/,u=/%$/,d=t.fn.position;t.position={scrollbarWidth:function(){if(void 0!==n)return n;var e,i,s=t("
"),o=s.children()[0];return t("body").append(s),e=o.offsetWidth,s.css("overflow","scroll"),i=o.offsetWidth,e===i&&(i=s[0].clientWidth),s.remove(),n=e-i},getScrollInfo:function(e){var i=e.isWindow||e.isDocument?"":e.element.css("overflow-x"),s=e.isWindow||e.isDocument?"":e.element.css("overflow-y"),n="scroll"===i||"auto"===i&&e.widthi?"left":e>0?"right":"center",vertical:0>r?"top":s>0?"bottom":"middle"};h>p&&p>a(e+i)&&(u.horizontal="center"),c>f&&f>a(s+r)&&(u.vertical="middle"),u.important=o(a(e),a(i))>o(a(s),a(r))?"horizontal":"vertical",n.using.call(this,t,u)}),l.offset(t.extend(D,{using:r}))})},t.ui.position={fit:{left:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollLeft:s.offset.left,a=s.width,r=t.left-e.collisionPosition.marginLeft,l=n-r,h=r+e.collisionWidth-a-n;e.collisionWidth>a?l>0&&0>=h?(i=t.left+l+e.collisionWidth-a-n,t.left+=l-i):t.left=h>0&&0>=l?n:l>h?n+a-e.collisionWidth:n:l>0?t.left+=l:h>0?t.left-=h:t.left=o(t.left-r,t.left)},top:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollTop:s.offset.top,a=e.within.height,r=t.top-e.collisionPosition.marginTop,l=n-r,h=r+e.collisionHeight-a-n;e.collisionHeight>a?l>0&&0>=h?(i=t.top+l+e.collisionHeight-a-n,t.top+=l-i):t.top=h>0&&0>=l?n:l>h?n+a-e.collisionHeight:n:l>0?t.top+=l:h>0?t.top-=h:t.top=o(t.top-r,t.top)}},flip:{left:function(t,e){var i,s,n=e.within,o=n.offset.left+n.scrollLeft,r=n.width,l=n.isWindow?n.scrollLeft:n.offset.left,h=t.left-e.collisionPosition.marginLeft,c=h-l,u=h+e.collisionWidth-r-l,d="left"===e.my[0]?-e.elemWidth:"right"===e.my[0]?e.elemWidth:0,p="left"===e.at[0]?e.targetWidth:"right"===e.at[0]?-e.targetWidth:0,f=-2*e.offset[0];0>c?(i=t.left+d+p+f+e.collisionWidth-r-o,(0>i||a(c)>i)&&(t.left+=d+p+f)):u>0&&(s=t.left-e.collisionPosition.marginLeft+d+p+f-l,(s>0||u>a(s))&&(t.left+=d+p+f))},top:function(t,e){var i,s,n=e.within,o=n.offset.top+n.scrollTop,r=n.height,l=n.isWindow?n.scrollTop:n.offset.top,h=t.top-e.collisionPosition.marginTop,c=h-l,u=h+e.collisionHeight-r-l,d="top"===e.my[1],p=d?-e.elemHeight:"bottom"===e.my[1]?e.elemHeight:0,f="top"===e.at[1]?e.targetHeight:"bottom"===e.at[1]?-e.targetHeight:0,g=-2*e.offset[1];0>c?(s=t.top+p+f+g+e.collisionHeight-r-o,(0>s||a(c)>s)&&(t.top+=p+f+g)):u>0&&(i=t.top-e.collisionPosition.marginTop+p+f+g-l,(i>0||u>a(i))&&(t.top+=p+f+g))}},flipfit:{left:function(){t.ui.position.flip.left.apply(this,arguments),t.ui.position.fit.left.apply(this,arguments)},top:function(){t.ui.position.flip.top.apply(this,arguments),t.ui.position.fit.top.apply(this,arguments)}}}}(),t.ui.position,t.extend(t.expr[":"],{data:t.expr.createPseudo?t.expr.createPseudo(function(e){return function(i){return!!t.data(i,e)}}):function(e,i,s){return!!t.data(e,s[3])}}),t.fn.extend({disableSelection:function(){var t="onselectstart"in document.createElement("div")?"selectstart":"mousedown";return function(){return this.on(t+".ui-disableSelection",function(t){t.preventDefault()})}}(),enableSelection:function(){return this.off(".ui-disableSelection")}}),t.ui.focusable=function(i,s){var n,o,a,r,l,h=i.nodeName.toLowerCase();return"area"===h?(n=i.parentNode,o=n.name,i.href&&o&&"map"===n.nodeName.toLowerCase()?(a=t("img[usemap='#"+o+"']"),a.length>0&&a.is(":visible")):!1):(/^(input|select|textarea|button|object)$/.test(h)?(r=!i.disabled,r&&(l=t(i).closest("fieldset")[0],l&&(r=!l.disabled))):r="a"===h?i.href||s:s,r&&t(i).is(":visible")&&e(t(i)))},t.extend(t.expr[":"],{focusable:function(e){return t.ui.focusable(e,null!=t.attr(e,"tabindex"))}}),t.ui.focusable,t.fn.form=function(){return"string"==typeof this[0].form?this.closest("form"):t(this[0].form)},t.ui.formResetMixin={_formResetHandler:function(){var e=t(this);setTimeout(function(){var i=e.data("ui-form-reset-instances");t.each(i,function(){this.refresh()})})},_bindFormResetHandler:function(){if(this.form=this.element.form(),this.form.length){var t=this.form.data("ui-form-reset-instances")||[];t.length||this.form.on("reset.ui-form-reset",this._formResetHandler),t.push(this),this.form.data("ui-form-reset-instances",t)}},_unbindFormResetHandler:function(){if(this.form.length){var e=this.form.data("ui-form-reset-instances");e.splice(t.inArray(this,e),1),e.length?this.form.data("ui-form-reset-instances",e):this.form.removeData("ui-form-reset-instances").off("reset.ui-form-reset")}}},"1.7"===t.fn.jquery.substring(0,3)&&(t.each(["Width","Height"],function(e,i){function s(e,i,s,o){return t.each(n,function(){i-=parseFloat(t.css(e,"padding"+this))||0,s&&(i-=parseFloat(t.css(e,"border"+this+"Width"))||0),o&&(i-=parseFloat(t.css(e,"margin"+this))||0)}),i}var n="Width"===i?["Left","Right"]:["Top","Bottom"],o=i.toLowerCase(),a={innerWidth:t.fn.innerWidth,innerHeight:t.fn.innerHeight,outerWidth:t.fn.outerWidth,outerHeight:t.fn.outerHeight};t.fn["inner"+i]=function(e){return void 0===e?a["inner"+i].call(this):this.each(function(){t(this).css(o,s(this,e)+"px")})},t.fn["outer"+i]=function(e,n){return"number"!=typeof e?a["outer"+i].call(this,e):this.each(function(){t(this).css(o,s(this,e,!0,n)+"px")})}}),t.fn.addBack=function(t){return this.add(null==t?this.prevObject:this.prevObject.filter(t))}),t.ui.keyCode={BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38},t.ui.escapeSelector=function(){var t=/([!"#$%&'()*+,./:;<=>?@[\]^`{|}~])/g;return function(e){return e.replace(t,"\\$1")}}(),t.fn.labels=function(){var e,i,s,n,o;return this[0].labels&&this[0].labels.length?this.pushStack(this[0].labels):(n=this.eq(0).parents("label"),s=this.attr("id"),s&&(e=this.eq(0).parents().last(),o=e.add(e.length?e.siblings():this.siblings()),i="label[for='"+t.ui.escapeSelector(s)+"']",n=n.add(o.find(i).addBack(i))),this.pushStack(n))},t.fn.scrollParent=function(e){var i=this.css("position"),s="absolute"===i,n=e?/(auto|scroll|hidden)/:/(auto|scroll)/,o=this.parents().filter(function(){var e=t(this);return s&&"static"===e.css("position")?!1:n.test(e.css("overflow")+e.css("overflow-y")+e.css("overflow-x"))}).eq(0);return"fixed"!==i&&o.length?o:t(this[0].ownerDocument||document)},t.extend(t.expr[":"],{tabbable:function(e){var i=t.attr(e,"tabindex"),s=null!=i;return(!s||i>=0)&&t.ui.focusable(e,s)}}),t.fn.extend({uniqueId:function(){var t=0;return function(){return this.each(function(){this.id||(this.id="ui-id-"+ ++t)})}}(),removeUniqueId:function(){return this.each(function(){/^ui-id-\d+$/.test(this.id)&&t(this).removeAttr("id")})}}),t.widget("ui.tooltip",{version:"1.12.1",options:{classes:{"ui-tooltip":"ui-corner-all ui-widget-shadow"},content:function(){var e=t(this).attr("title")||"";return t("").text(e).html()},hide:!0,items:"[title]:not([disabled])",position:{my:"left top+15",at:"left bottom",collision:"flipfit flip"},show:!0,track:!1,close:null,open:null},_addDescribedBy:function(e,i){var s=(e.attr("aria-describedby")||"").split(/\s+/);s.push(i),e.data("ui-tooltip-id",i).attr("aria-describedby",t.trim(s.join(" ")))},_removeDescribedBy:function(e){var i=e.data("ui-tooltip-id"),s=(e.attr("aria-describedby")||"").split(/\s+/),n=t.inArray(i,s);-1!==n&&s.splice(n,1),e.removeData("ui-tooltip-id"),s=t.trim(s.join(" ")),s?e.attr("aria-describedby",s):e.removeAttr("aria-describedby")},_create:function(){this._on({mouseover:"open",focusin:"open"}),this.tooltips={},this.parents={},this.liveRegion=t("
").attr({role:"log","aria-live":"assertive","aria-relevant":"additions"}).appendTo(this.document[0].body),this._addClass(this.liveRegion,null,"ui-helper-hidden-accessible"),this.disabledTitles=t([])},_setOption:function(e,i){var s=this;this._super(e,i),"content"===e&&t.each(this.tooltips,function(t,e){s._updateContent(e.element)})},_setOptionDisabled:function(t){this[t?"_disable":"_enable"]()},_disable:function(){var e=this;t.each(this.tooltips,function(i,s){var n=t.Event("blur");n.target=n.currentTarget=s.element[0],e.close(n,!0)}),this.disabledTitles=this.disabledTitles.add(this.element.find(this.options.items).addBack().filter(function(){var e=t(this);return e.is("[title]")?e.data("ui-tooltip-title",e.attr("title")).removeAttr("title"):void 0}))},_enable:function(){this.disabledTitles.each(function(){var e=t(this);e.data("ui-tooltip-title")&&e.attr("title",e.data("ui-tooltip-title"))}),this.disabledTitles=t([])},open:function(e){var i=this,s=t(e?e.target:this.element).closest(this.options.items);s.length&&!s.data("ui-tooltip-id")&&(s.attr("title")&&s.data("ui-tooltip-title",s.attr("title")),s.data("ui-tooltip-open",!0),e&&"mouseover"===e.type&&s.parents().each(function(){var e,s=t(this);s.data("ui-tooltip-open")&&(e=t.Event("blur"),e.target=e.currentTarget=this,i.close(e,!0)),s.attr("title")&&(s.uniqueId(),i.parents[this.id]={element:this,title:s.attr("title")},s.attr("title",""))}),this._registerCloseHandlers(e,s),this._updateContent(s,e))},_updateContent:function(t,e){var i,s=this.options.content,n=this,o=e?e.type:null;return"string"==typeof s||s.nodeType||s.jquery?this._open(e,t,s):(i=s.call(t[0],function(i){n._delay(function(){t.data("ui-tooltip-open")&&(e&&(e.type=o),this._open(e,t,i))})}),i&&this._open(e,t,i),void 0)},_open:function(e,i,s){function n(t){h.of=t,a.is(":hidden")||a.position(h)}var o,a,r,l,h=t.extend({},this.options.position);if(s){if(o=this._find(i))return o.tooltip.find(".ui-tooltip-content").html(s),void 0;i.is("[title]")&&(e&&"mouseover"===e.type?i.attr("title",""):i.removeAttr("title")),o=this._tooltip(i),a=o.tooltip,this._addDescribedBy(i,a.attr("id")),a.find(".ui-tooltip-content").html(s),this.liveRegion.children().hide(),l=t("
").html(a.find(".ui-tooltip-content").html()),l.removeAttr("name").find("[name]").removeAttr("name"),l.removeAttr("id").find("[id]").removeAttr("id"),l.appendTo(this.liveRegion),this.options.track&&e&&/^mouse/.test(e.type)?(this._on(this.document,{mousemove:n}),n(e)):a.position(t.extend({of:i},this.options.position)),a.hide(),this._show(a,this.options.show),this.options.track&&this.options.show&&this.options.show.delay&&(r=this.delayedShow=setInterval(function(){a.is(":visible")&&(n(h.of),clearInterval(r))},t.fx.interval)),this._trigger("open",e,{tooltip:a})}},_registerCloseHandlers:function(e,i){var s={keyup:function(e){if(e.keyCode===t.ui.keyCode.ESCAPE){var s=t.Event(e);s.currentTarget=i[0],this.close(s,!0)}}};i[0]!==this.element[0]&&(s.remove=function(){this._removeTooltip(this._find(i).tooltip)}),e&&"mouseover"!==e.type||(s.mouseleave="close"),e&&"focusin"!==e.type||(s.focusout="close"),this._on(!0,i,s)},close:function(e){var i,s=this,n=t(e?e.currentTarget:this.element),o=this._find(n);return o?(i=o.tooltip,o.closing||(clearInterval(this.delayedShow),n.data("ui-tooltip-title")&&!n.attr("title")&&n.attr("title",n.data("ui-tooltip-title")),this._removeDescribedBy(n),o.hiding=!0,i.stop(!0),this._hide(i,this.options.hide,function(){s._removeTooltip(t(this))}),n.removeData("ui-tooltip-open"),this._off(n,"mouseleave focusout keyup"),n[0]!==this.element[0]&&this._off(n,"remove"),this._off(this.document,"mousemove"),e&&"mouseleave"===e.type&&t.each(this.parents,function(e,i){t(i.element).attr("title",i.title),delete s.parents[e]}),o.closing=!0,this._trigger("close",e,{tooltip:i}),o.hiding||(o.closing=!1)),void 0):(n.removeData("ui-tooltip-open"),void 0)},_tooltip:function(e){var i=t("
").attr("role","tooltip"),s=t("
").appendTo(i),n=i.uniqueId().attr("id");return this._addClass(s,"ui-tooltip-content"),this._addClass(i,"ui-tooltip","ui-widget ui-widget-content"),i.appendTo(this._appendTo(e)),this.tooltips[n]={element:e,tooltip:i}},_find:function(t){var e=t.data("ui-tooltip-id");return e?this.tooltips[e]:null},_removeTooltip:function(t){t.remove(),delete this.tooltips[t.attr("id")]},_appendTo:function(t){var e=t.closest(".ui-front, dialog");return e.length||(e=this.document[0].body),e},_destroy:function(){var e=this;t.each(this.tooltips,function(i,s){var n=t.Event("blur"),o=s.element;n.target=n.currentTarget=o[0],e.close(n,!0),t("#"+i).remove(),o.data("ui-tooltip-title")&&(o.attr("title")||o.attr("title",o.data("ui-tooltip-title")),o.removeData("ui-tooltip-title"))}),this.liveRegion.remove()}}),t.uiBackCompat!==!1&&t.widget("ui.tooltip",t.ui.tooltip,{options:{tooltipClass:null},_tooltip:function(){var t=this._superApply(arguments);return this.options.tooltipClass&&t.tooltip.addClass(this.options.tooltipClass),t}}),t.ui.tooltip}); diff --git a/KeywordSearch/solr/server/solr-webapp/webapp/libs/jstree.min.js b/KeywordSearch/solr/server/solr-webapp/webapp/libs/jstree.min.js new file mode 100755 index 0000000000..e3cc3441e3 --- /dev/null +++ b/KeywordSearch/solr/server/solr-webapp/webapp/libs/jstree.min.js @@ -0,0 +1,29 @@ +/* + +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ +/*! jsTree - v3.3.8 - 2019-04-29 - (MIT) */ +!function(a){"use strict";"function"==typeof define&&define.amd?define(["jquery"],a):"undefined"!=typeof module&&module.exports?module.exports=a(require("jquery")):a(jQuery)}(function(a,b){"use strict";if(!a.jstree){var c=0,d=!1,e=!1,f=!1,g=[],h=a("script:last").attr("src"),i=window.document;a.jstree={version:"3.3.8",defaults:{plugins:[]},plugins:{},path:h&&-1!==h.indexOf("/")?h.replace(/\/[^\/]+$/,""):"",idregex:/[\\:&!^|()\[\]<>@*'+~#";.,=\- \/${}%?`]/g,root:"#"},a.jstree.create=function(b,d){var e=new a.jstree.core(++c),f=d;return d=a.extend(!0,{},a.jstree.defaults,d),f&&f.plugins&&(d.plugins=f.plugins),a.each(d.plugins,function(a,b){"core"!==a&&(e=e.plugin(b,d[b]))}),a(b).data("jstree",e),e.init(b,d),e},a.jstree.destroy=function(){a(".jstree:jstree").jstree("destroy"),a(i).off(".jstree")},a.jstree.core=function(a){this._id=a,this._cnt=0,this._wrk=null,this._data={core:{themes:{name:!1,dots:!1,icons:!1,ellipsis:!1},selected:[],last_error:{},working:!1,worker_queue:[],focused:null}}},a.jstree.reference=function(b){var c=null,d=null;if(!b||!b.id||b.tagName&&b.nodeType||(b=b.id),!d||!d.length)try{d=a(b)}catch(e){}if(!d||!d.length)try{d=a("#"+b.replace(a.jstree.idregex,"\\$&"))}catch(e){}return d&&d.length&&(d=d.closest(".jstree")).length&&(d=d.data("jstree"))?c=d:a(".jstree").each(function(){var d=a(this).data("jstree");return d&&d._model.data[b]?(c=d,!1):void 0}),c},a.fn.jstree=function(c){var d="string"==typeof c,e=Array.prototype.slice.call(arguments,1),f=null;return c!==!0||this.length?(this.each(function(){var g=a.jstree.reference(this),h=d&&g?g[c]:null;return f=d&&h?h.apply(g,e):null,g||d||c!==b&&!a.isPlainObject(c)||a.jstree.create(this,c),(g&&!d||c===!0)&&(f=g||!1),null!==f&&f!==b?!1:void 0}),null!==f&&f!==b?f:this):!1},a.expr.pseudos.jstree=a.expr.createPseudo(function(c){return function(c){return a(c).hasClass("jstree")&&a(c).data("jstree")!==b}}),a.jstree.defaults.core={data:!1,strings:!1,check_callback:!1,error:a.noop,animation:200,multiple:!0,themes:{name:!1,url:!1,dir:!1,dots:!0,icons:!0,ellipsis:!1,stripes:!1,variant:!1,responsive:!1},expand_selected_onload:!0,worker:!0,force_text:!1,dblclick_toggle:!0,loaded_state:!1,restore_focus:!0,keyboard:{"ctrl-space":function(b){b.type="click",a(b.currentTarget).trigger(b)},enter:function(b){b.type="click",a(b.currentTarget).trigger(b)},left:function(b){if(b.preventDefault(),this.is_open(b.currentTarget))this.close_node(b.currentTarget);else{var c=this.get_parent(b.currentTarget);c&&c.id!==a.jstree.root&&this.get_node(c,!0).children(".jstree-anchor").focus()}},up:function(a){a.preventDefault();var b=this.get_prev_dom(a.currentTarget);b&&b.length&&b.children(".jstree-anchor").focus()},right:function(b){if(b.preventDefault(),this.is_closed(b.currentTarget))this.open_node(b.currentTarget,function(a){this.get_node(a,!0).children(".jstree-anchor").focus()});else if(this.is_open(b.currentTarget)){var c=this.get_node(b.currentTarget,!0).children(".jstree-children")[0];c&&a(this._firstChild(c)).children(".jstree-anchor").focus()}},down:function(a){a.preventDefault();var b=this.get_next_dom(a.currentTarget);b&&b.length&&b.children(".jstree-anchor").focus()},"*":function(a){this.open_all()},home:function(b){b.preventDefault();var c=this._firstChild(this.get_container_ul()[0]);c&&a(c).children(".jstree-anchor").filter(":visible").focus()},end:function(a){a.preventDefault(),this.element.find(".jstree-anchor").filter(":visible").last().focus()},f2:function(a){a.preventDefault(),this.edit(a.currentTarget)}}},a.jstree.core.prototype={plugin:function(b,c){var d=a.jstree.plugins[b];return d?(this._data[b]={},d.prototype=this,new d(c,this)):this},init:function(b,c){this._model={data:{},changed:[],force_full_redraw:!1,redraw_timeout:!1,default_state:{loaded:!0,opened:!1,selected:!1,disabled:!1}},this._model.data[a.jstree.root]={id:a.jstree.root,parent:null,parents:[],children:[],children_d:[],state:{loaded:!1}},this.element=a(b).addClass("jstree jstree-"+this._id),this.settings=c,this._data.core.ready=!1,this._data.core.loaded=!1,this._data.core.rtl="rtl"===this.element.css("direction"),this.element[this._data.core.rtl?"addClass":"removeClass"]("jstree-rtl"),this.element.attr("role","tree"),this.settings.core.multiple&&this.element.attr("aria-multiselectable",!0),this.element.attr("tabindex")||this.element.attr("tabindex","0"),this.bind(),this.trigger("init"),this._data.core.original_container_html=this.element.find(" > ul > li").clone(!0),this._data.core.original_container_html.find("li").addBack().contents().filter(function(){return 3===this.nodeType&&(!this.nodeValue||/^\s+$/.test(this.nodeValue))}).remove(),this.element.html(""),this.element.attr("aria-activedescendant","j"+this._id+"_loading"),this._data.core.li_height=this.get_container_ul().children("li").first().outerHeight()||24,this._data.core.node=this._create_prototype_node(),this.trigger("loading"),this.load_node(a.jstree.root)},destroy:function(a){if(this.trigger("destroy"),this._wrk)try{window.URL.revokeObjectURL(this._wrk),this._wrk=null}catch(b){}a||this.element.empty(),this.teardown()},_create_prototype_node:function(){var a=i.createElement("LI"),b,c;return a.setAttribute("role","treeitem"),b=i.createElement("I"),b.className="jstree-icon jstree-ocl",b.setAttribute("role","presentation"),a.appendChild(b),b=i.createElement("A"),b.className="jstree-anchor",b.setAttribute("href","#"),b.setAttribute("tabindex","-1"),c=i.createElement("I"),c.className="jstree-icon jstree-themeicon",c.setAttribute("role","presentation"),b.appendChild(c),a.appendChild(b),b=c=null,a},_kbevent_to_func:function(a){var b={8:"Backspace",9:"Tab",13:"Enter",19:"Pause",27:"Esc",32:"Space",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"Left",38:"Up",39:"Right",40:"Down",44:"Print",45:"Insert",46:"Delete",96:"Numpad0",97:"Numpad1",98:"Numpad2",99:"Numpad3",100:"Numpad4",101:"Numpad5",102:"Numpad6",103:"Numpad7",104:"Numpad8",105:"Numpad9","-13":"NumpadEnter",112:"F1",113:"F2",114:"F3",115:"F4",116:"F5",117:"F6",118:"F7",119:"F8",120:"F9",121:"F10",122:"F11",123:"F12",144:"Numlock",145:"Scrolllock",16:"Shift",17:"Ctrl",18:"Alt",48:"0",49:"1",50:"2",51:"3",52:"4",53:"5",54:"6",55:"7",56:"8",57:"9",59:";",61:"=",65:"a",66:"b",67:"c",68:"d",69:"e",70:"f",71:"g",72:"h",73:"i",74:"j",75:"k",76:"l",77:"m",78:"n",79:"o",80:"p",81:"q",82:"r",83:"s",84:"t",85:"u",86:"v",87:"w",88:"x",89:"y",90:"z",107:"+",109:"-",110:".",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'",111:"/",106:"*",173:"-"},c=[];a.ctrlKey&&c.push("ctrl"),a.altKey&&c.push("alt"),a.shiftKey&&c.push("shift"),c.push(b[a.which]||a.which),c=c.sort().join("-").toLowerCase();var d=this.settings.core.keyboard,e,f;for(e in d)if(d.hasOwnProperty(e)&&(f=e,"-"!==f&&"+"!==f&&(f=f.replace("--","-MINUS").replace("+-","-MINUS").replace("++","-PLUS").replace("-+","-PLUS"),f=f.split(/-|\+/).sort().join("-").replace("MINUS","-").replace("PLUS","+").toLowerCase()),f===c))return d[e];return null},teardown:function(){this.unbind(),this.element.removeClass("jstree").removeData("jstree").find("[class^='jstree']").addBack().attr("class",function(){return this.className.replace(/jstree[^ ]*|$/gi,"")}),this.element=null},bind:function(){var b="",c=null,d=0;this.element.on("dblclick.jstree",function(a){if(a.target.tagName&&"input"===a.target.tagName.toLowerCase())return!0;if(i.selection&&i.selection.empty)i.selection.empty();else if(window.getSelection){var b=window.getSelection();try{b.removeAllRanges(),b.collapse()}catch(c){}}}).on("mousedown.jstree",a.proxy(function(a){a.target===this.element[0]&&(a.preventDefault(),d=+new Date)},this)).on("mousedown.jstree",".jstree-ocl",function(a){a.preventDefault()}).on("click.jstree",".jstree-ocl",a.proxy(function(a){this.toggle_node(a.target)},this)).on("dblclick.jstree",".jstree-anchor",a.proxy(function(a){return a.target.tagName&&"input"===a.target.tagName.toLowerCase()?!0:void(this.settings.core.dblclick_toggle&&this.toggle_node(a.target))},this)).on("click.jstree",".jstree-anchor",a.proxy(function(b){b.preventDefault(),b.currentTarget!==i.activeElement&&a(b.currentTarget).focus(),this.activate_node(b.currentTarget,b)},this)).on("keydown.jstree",".jstree-anchor",a.proxy(function(a){if(a.target.tagName&&"input"===a.target.tagName.toLowerCase())return!0;this._data.core.rtl&&(37===a.which?a.which=39:39===a.which&&(a.which=37));var b=this._kbevent_to_func(a);if(b){var c=b.call(this,a);if(c===!1||c===!0)return c}},this)).on("load_node.jstree",a.proxy(function(b,c){c.status&&(c.node.id!==a.jstree.root||this._data.core.loaded||(this._data.core.loaded=!0,this._firstChild(this.get_container_ul()[0])&&this.element.attr("aria-activedescendant",this._firstChild(this.get_container_ul()[0]).id),this.trigger("loaded")),this._data.core.ready||setTimeout(a.proxy(function(){if(this.element&&!this.get_container_ul().find(".jstree-loading").length){if(this._data.core.ready=!0,this._data.core.selected.length){if(this.settings.core.expand_selected_onload){var b=[],c,d;for(c=0,d=this._data.core.selected.length;d>c;c++)b=b.concat(this._model.data[this._data.core.selected[c]].parents);for(b=a.vakata.array_unique(b),c=0,d=b.length;d>c;c++)this.open_node(b[c],!1,0)}this.trigger("changed",{action:"ready",selected:this._data.core.selected})}this.trigger("ready")}},this),0))},this)).on("keypress.jstree",a.proxy(function(d){if(d.target.tagName&&"input"===d.target.tagName.toLowerCase())return!0;c&&clearTimeout(c),c=setTimeout(function(){b=""},500);var e=String.fromCharCode(d.which).toLowerCase(),f=this.element.find(".jstree-anchor").filter(":visible"),g=f.index(i.activeElement)||0,h=!1;if(b+=e,b.length>1){if(f.slice(g).each(a.proxy(function(c,d){return 0===a(d).text().toLowerCase().indexOf(b)?(a(d).focus(),h=!0,!1):void 0},this)),h)return;if(f.slice(0,g).each(a.proxy(function(c,d){return 0===a(d).text().toLowerCase().indexOf(b)?(a(d).focus(),h=!0,!1):void 0},this)),h)return}if(new RegExp("^"+e.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")+"+$").test(b)){if(f.slice(g+1).each(a.proxy(function(b,c){return a(c).text().toLowerCase().charAt(0)===e?(a(c).focus(),h=!0,!1):void 0},this)),h)return;if(f.slice(0,g+1).each(a.proxy(function(b,c){return a(c).text().toLowerCase().charAt(0)===e?(a(c).focus(),h=!0,!1):void 0},this)),h)return}},this)).on("init.jstree",a.proxy(function(){var a=this.settings.core.themes;this._data.core.themes.dots=a.dots,this._data.core.themes.stripes=a.stripes,this._data.core.themes.icons=a.icons,this._data.core.themes.ellipsis=a.ellipsis,this.set_theme(a.name||"default",a.url),this.set_theme_variant(a.variant)},this)).on("loading.jstree",a.proxy(function(){this[this._data.core.themes.dots?"show_dots":"hide_dots"](),this[this._data.core.themes.icons?"show_icons":"hide_icons"](),this[this._data.core.themes.stripes?"show_stripes":"hide_stripes"](),this[this._data.core.themes.ellipsis?"show_ellipsis":"hide_ellipsis"]()},this)).on("blur.jstree",".jstree-anchor",a.proxy(function(b){this._data.core.focused=null,a(b.currentTarget).filter(".jstree-hovered").trigger("mouseleave"),this.element.attr("tabindex","0")},this)).on("focus.jstree",".jstree-anchor",a.proxy(function(b){var c=this.get_node(b.currentTarget);c&&c.id&&(this._data.core.focused=c.id),this.element.find(".jstree-hovered").not(b.currentTarget).trigger("mouseleave"),a(b.currentTarget).trigger("mouseenter"),this.element.attr("tabindex","-1")},this)).on("focus.jstree",a.proxy(function(){if(+new Date-d>500&&!this._data.core.focused&&this.settings.core.restore_focus){d=0;var a=this.get_node(this.element.attr("aria-activedescendant"),!0);a&&a.find("> .jstree-anchor").focus()}},this)).on("mouseenter.jstree",".jstree-anchor",a.proxy(function(a){this.hover_node(a.currentTarget)},this)).on("mouseleave.jstree",".jstree-anchor",a.proxy(function(a){this.dehover_node(a.currentTarget)},this))},unbind:function(){this.element.off(".jstree"),a(i).off(".jstree-"+this._id)},trigger:function(a,b){b||(b={}),b.instance=this,this.element.triggerHandler(a.replace(".jstree","")+".jstree",b)},get_container:function(){return this.element},get_container_ul:function(){return this.element.children(".jstree-children").first()},get_string:function(b){var c=this.settings.core.strings;return a.isFunction(c)?c.call(this,b):c&&c[b]?c[b]:b},_firstChild:function(a){a=a?a.firstChild:null;while(null!==a&&1!==a.nodeType)a=a.nextSibling;return a},_nextSibling:function(a){a=a?a.nextSibling:null;while(null!==a&&1!==a.nodeType)a=a.nextSibling;return a},_previousSibling:function(a){a=a?a.previousSibling:null;while(null!==a&&1!==a.nodeType)a=a.previousSibling;return a},get_node:function(b,c){b&&b.id&&(b=b.id),b instanceof a&&b.length&&b[0].id&&(b=b[0].id);var d;try{if(this._model.data[b])b=this._model.data[b];else if("string"==typeof b&&this._model.data[b.replace(/^#/,"")])b=this._model.data[b.replace(/^#/,"")];else if("string"==typeof b&&(d=a("#"+b.replace(a.jstree.idregex,"\\$&"),this.element)).length&&this._model.data[d.closest(".jstree-node").attr("id")])b=this._model.data[d.closest(".jstree-node").attr("id")];else if((d=this.element.find(b)).length&&this._model.data[d.closest(".jstree-node").attr("id")])b=this._model.data[d.closest(".jstree-node").attr("id")];else{if(!(d=this.element.find(b)).length||!d.hasClass("jstree"))return!1;b=this._model.data[a.jstree.root]}return c&&(b=b.id===a.jstree.root?this.element:a("#"+b.id.replace(a.jstree.idregex,"\\$&"),this.element)),b}catch(e){return!1}},get_path:function(b,c,d){if(b=b.parents?b:this.get_node(b),!b||b.id===a.jstree.root||!b.parents)return!1;var e,f,g=[];for(g.push(d?b.id:b.text),e=0,f=b.parents.length;f>e;e++)g.push(d?b.parents[e]:this.get_text(b.parents[e]));return g=g.reverse().slice(1),c?g.join(c):g},get_next_dom:function(b,c){var d;if(b=this.get_node(b,!0),b[0]===this.element[0]){d=this._firstChild(this.get_container_ul()[0]);while(d&&0===d.offsetHeight)d=this._nextSibling(d);return d?a(d):!1}if(!b||!b.length)return!1;if(c){d=b[0];do d=this._nextSibling(d);while(d&&0===d.offsetHeight);return d?a(d):!1}if(b.hasClass("jstree-open")){d=this._firstChild(b.children(".jstree-children")[0]);while(d&&0===d.offsetHeight)d=this._nextSibling(d);if(null!==d)return a(d)}d=b[0];do d=this._nextSibling(d);while(d&&0===d.offsetHeight);return null!==d?a(d):b.parentsUntil(".jstree",".jstree-node").nextAll(".jstree-node:visible").first()},get_prev_dom:function(b,c){var d;if(b=this.get_node(b,!0),b[0]===this.element[0]){d=this.get_container_ul()[0].lastChild;while(d&&0===d.offsetHeight)d=this._previousSibling(d);return d?a(d):!1}if(!b||!b.length)return!1;if(c){d=b[0];do d=this._previousSibling(d);while(d&&0===d.offsetHeight);return d?a(d):!1}d=b[0];do d=this._previousSibling(d);while(d&&0===d.offsetHeight);if(null!==d){b=a(d);while(b.hasClass("jstree-open"))b=b.children(".jstree-children").first().children(".jstree-node:visible:last");return b}return d=b[0].parentNode.parentNode,d&&d.className&&-1!==d.className.indexOf("jstree-node")?a(d):!1},get_parent:function(b){return b=this.get_node(b),b&&b.id!==a.jstree.root?b.parent:!1},get_children_dom:function(a){return a=this.get_node(a,!0),a[0]===this.element[0]?this.get_container_ul().children(".jstree-node"):a&&a.length?a.children(".jstree-children").children(".jstree-node"):!1},is_parent:function(a){return a=this.get_node(a),a&&(a.state.loaded===!1||a.children.length>0)},is_loaded:function(a){return a=this.get_node(a),a&&a.state.loaded},is_loading:function(a){return a=this.get_node(a),a&&a.state&&a.state.loading},is_open:function(a){return a=this.get_node(a),a&&a.state.opened},is_closed:function(a){return a=this.get_node(a),a&&this.is_parent(a)&&!a.state.opened},is_leaf:function(a){return!this.is_parent(a)},load_node:function(b,c){var d,e,f,g,h;if(a.isArray(b))return this._load_nodes(b.slice(),c),!0;if(b=this.get_node(b),!b)return c&&c.call(this,b,!1),!1;if(b.state.loaded){for(b.state.loaded=!1,f=0,g=b.parents.length;g>f;f++)this._model.data[b.parents[f]].children_d=a.vakata.array_filter(this._model.data[b.parents[f]].children_d,function(c){return-1===a.inArray(c,b.children_d)});for(d=0,e=b.children_d.length;e>d;d++)this._model.data[b.children_d[d]].state.selected&&(h=!0),delete this._model.data[b.children_d[d]];h&&(this._data.core.selected=a.vakata.array_filter(this._data.core.selected,function(c){return-1===a.inArray(c,b.children_d)})),b.children=[],b.children_d=[],h&&this.trigger("changed",{action:"load_node",node:b,selected:this._data.core.selected})}return b.state.failed=!1,b.state.loading=!0,this.get_node(b,!0).addClass("jstree-loading").attr("aria-busy",!0),this._load_node(b,a.proxy(function(a){b=this._model.data[b.id],b.state.loading=!1,b.state.loaded=a,b.state.failed=!b.state.loaded;var d=this.get_node(b,!0),e=0,f=0,g=this._model.data,h=!1;for(e=0,f=b.children.length;f>e;e++)if(g[b.children[e]]&&!g[b.children[e]].state.hidden){h=!0;break}b.state.loaded&&d&&d.length&&(d.removeClass("jstree-closed jstree-open jstree-leaf"),h?"#"!==b.id&&d.addClass(b.state.opened?"jstree-open":"jstree-closed"):d.addClass("jstree-leaf")),d.removeClass("jstree-loading").attr("aria-busy",!1),this.trigger("load_node",{node:b,status:a}),c&&c.call(this,b,a)},this)),!0},_load_nodes:function(a,b,c,d){var e=!0,f=function(){this._load_nodes(a,b,!0)},g=this._model.data,h,i,j=[];for(h=0,i=a.length;i>h;h++)g[a[h]]&&(!g[a[h]].state.loaded&&!g[a[h]].state.failed||!c&&d)&&(this.is_loading(a[h])||this.load_node(a[h],f),e=!1);if(e){for(h=0,i=a.length;i>h;h++)g[a[h]]&&g[a[h]].state.loaded&&j.push(a[h]);b&&!b.done&&(b.call(this,j),b.done=!0)}},load_all:function(b,c){if(b||(b=a.jstree.root),b=this.get_node(b),!b)return!1;var d=[],e=this._model.data,f=e[b.id].children_d,g,h;for(b.state&&!b.state.loaded&&d.push(b.id),g=0,h=f.length;h>g;g++)e[f[g]]&&e[f[g]].state&&!e[f[g]].state.loaded&&d.push(f[g]);d.length?this._load_nodes(d,function(){this.load_all(b,c)}):(c&&c.call(this,b),this.trigger("load_all",{node:b}))},_load_node:function(b,c){var d=this.settings.core.data,e,f=function g(){return 3!==this.nodeType&&8!==this.nodeType};return d?a.isFunction(d)?d.call(this,b,a.proxy(function(d){d===!1?c.call(this,!1):this["string"==typeof d?"_append_html_data":"_append_json_data"](b,"string"==typeof d?a(a.parseHTML(d)).filter(f):d,function(a){c.call(this,a)})},this)):"object"==typeof d?d.url?(d=a.extend(!0,{},d),a.isFunction(d.url)&&(d.url=d.url.call(this,b)),a.isFunction(d.data)&&(d.data=d.data.call(this,b)),a.ajax(d).done(a.proxy(function(d,e,g){var h=g.getResponseHeader("Content-Type");return h&&-1!==h.indexOf("json")||"object"==typeof d?this._append_json_data(b,d,function(a){c.call(this,a)}):h&&-1!==h.indexOf("html")||"string"==typeof d?this._append_html_data(b,a(a.parseHTML(d)).filter(f),function(a){c.call(this,a)}):(this._data.core.last_error={error:"ajax",plugin:"core",id:"core_04",reason:"Could not load node",data:JSON.stringify({id:b.id,xhr:g})},this.settings.core.error.call(this,this._data.core.last_error),c.call(this,!1))},this)).fail(a.proxy(function(a){this._data.core.last_error={error:"ajax",plugin:"core",id:"core_04",reason:"Could not load node",data:JSON.stringify({id:b.id,xhr:a})},c.call(this,!1),this.settings.core.error.call(this,this._data.core.last_error)},this))):(e=a.isArray(d)?a.extend(!0,[],d):a.isPlainObject(d)?a.extend(!0,{},d):d,b.id===a.jstree.root?this._append_json_data(b,e,function(a){c.call(this,a)}):(this._data.core.last_error={error:"nodata",plugin:"core",id:"core_05",reason:"Could not load node",data:JSON.stringify({id:b.id})},this.settings.core.error.call(this,this._data.core.last_error),c.call(this,!1))):"string"==typeof d?b.id===a.jstree.root?this._append_html_data(b,a(a.parseHTML(d)).filter(f),function(a){c.call(this,a)}):(this._data.core.last_error={error:"nodata",plugin:"core",id:"core_06",reason:"Could not load node",data:JSON.stringify({id:b.id})},this.settings.core.error.call(this,this._data.core.last_error),c.call(this,!1)):c.call(this,!1):b.id===a.jstree.root?this._append_html_data(b,this._data.core.original_container_html.clone(!0),function(a){c.call(this,a)}):c.call(this,!1)},_node_changed:function(b){b=this.get_node(b),b&&-1===a.inArray(b.id,this._model.changed)&&this._model.changed.push(b.id)},_append_html_data:function(b,c,d){b=this.get_node(b),b.children=[],b.children_d=[];var e=c.is("ul")?c.children():c,f=b.id,g=[],h=[],i=this._model.data,j=i[f],k=this._data.core.selected.length,l,m,n;for(e.each(a.proxy(function(b,c){l=this._parse_model_from_html(a(c),f,j.parents.concat()),l&&(g.push(l),h.push(l),i[l].children_d.length&&(h=h.concat(i[l].children_d)))},this)),j.children=g,j.children_d=h,m=0,n=j.parents.length;n>m;m++)i[j.parents[m]].children_d=i[j.parents[m]].children_d.concat(h);this.trigger("model",{nodes:h,parent:f}),f!==a.jstree.root?(this._node_changed(f),this.redraw()):(this.get_container_ul().children(".jstree-initial-node").remove(),this.redraw(!0)),this._data.core.selected.length!==k&&this.trigger("changed",{action:"model",selected:this._data.core.selected}),d.call(this,!0)},_append_json_data:function(b,c,d,e){if(null!==this.element){b=this.get_node(b),b.children=[],b.children_d=[],c.d&&(c=c.d,"string"==typeof c&&(c=JSON.parse(c))),a.isArray(c)||(c=[c]);var f=null,g={df:this._model.default_state,dat:c,par:b.id,m:this._model.data,t_id:this._id,t_cnt:this._cnt,sel:this._data.core.selected},h=this,i=function(a,b){a.data&&(a=a.data);var c=a.dat,d=a.par,e=[],f=[],g=[],i=a.df,j=a.t_id,k=a.t_cnt,l=a.m,m=l[d],n=a.sel,o,p,q,r,s=function(a,c,d){d=d?d.concat():[],c&&d.unshift(c);var e=a.id.toString(),f,h,j,k,m={id:e,text:a.text||"",icon:a.icon!==b?a.icon:!0,parent:c,parents:d,children:a.children||[],children_d:a.children_d||[],data:a.data,state:{},li_attr:{id:!1},a_attr:{href:"#"},original:!1};for(f in i)i.hasOwnProperty(f)&&(m.state[f]=i[f]);if(a&&a.data&&a.data.jstree&&a.data.jstree.icon&&(m.icon=a.data.jstree.icon),(m.icon===b||null===m.icon||""===m.icon)&&(m.icon=!0),a&&a.data&&(m.data=a.data,a.data.jstree))for(f in a.data.jstree)a.data.jstree.hasOwnProperty(f)&&(m.state[f]=a.data.jstree[f]);if(a&&"object"==typeof a.state)for(f in a.state)a.state.hasOwnProperty(f)&&(m.state[f]=a.state[f]);if(a&&"object"==typeof a.li_attr)for(f in a.li_attr)a.li_attr.hasOwnProperty(f)&&(m.li_attr[f]=a.li_attr[f]);if(m.li_attr.id||(m.li_attr.id=e),a&&"object"==typeof a.a_attr)for(f in a.a_attr)a.a_attr.hasOwnProperty(f)&&(m.a_attr[f]=a.a_attr[f]);for(a&&a.children&&a.children===!0&&(m.state.loaded=!1,m.children=[],m.children_d=[]),l[m.id]=m,f=0,h=m.children.length;h>f;f++)j=s(l[m.children[f]],m.id,d),k=l[j],m.children_d.push(j),k.children_d.length&&(m.children_d=m.children_d.concat(k.children_d));return delete a.data,delete a.children,l[m.id].original=a,m.state.selected&&g.push(m.id),m.id},t=function(a,c,d){d=d?d.concat():[],c&&d.unshift(c);var e=!1,f,h,m,n,o;do e="j"+j+"_"+ ++k;while(l[e]);o={id:!1,text:"string"==typeof a?a:"",icon:"object"==typeof a&&a.icon!==b?a.icon:!0,parent:c,parents:d,children:[],children_d:[],data:null,state:{},li_attr:{id:!1},a_attr:{href:"#"},original:!1};for(f in i)i.hasOwnProperty(f)&&(o.state[f]=i[f]);if(a&&a.id&&(o.id=a.id.toString()),a&&a.text&&(o.text=a.text),a&&a.data&&a.data.jstree&&a.data.jstree.icon&&(o.icon=a.data.jstree.icon),(o.icon===b||null===o.icon||""===o.icon)&&(o.icon=!0),a&&a.data&&(o.data=a.data,a.data.jstree))for(f in a.data.jstree)a.data.jstree.hasOwnProperty(f)&&(o.state[f]=a.data.jstree[f]);if(a&&"object"==typeof a.state)for(f in a.state)a.state.hasOwnProperty(f)&&(o.state[f]=a.state[f]);if(a&&"object"==typeof a.li_attr)for(f in a.li_attr)a.li_attr.hasOwnProperty(f)&&(o.li_attr[f]=a.li_attr[f]);if(o.li_attr.id&&!o.id&&(o.id=o.li_attr.id.toString()),o.id||(o.id=e),o.li_attr.id||(o.li_attr.id=o.id),a&&"object"==typeof a.a_attr)for(f in a.a_attr)a.a_attr.hasOwnProperty(f)&&(o.a_attr[f]=a.a_attr[f]);if(a&&a.children&&a.children.length){for(f=0,h=a.children.length;h>f;f++)m=t(a.children[f],o.id,d),n=l[m],o.children.push(m),n.children_d.length&&(o.children_d=o.children_d.concat(n.children_d));o.children_d=o.children_d.concat(o.children)}return a&&a.children&&a.children===!0&&(o.state.loaded=!1,o.children=[],o.children_d=[]),delete a.data,delete a.children,o.original=a,l[o.id]=o,o.state.selected&&g.push(o.id),o.id};if(c.length&&c[0].id!==b&&c[0].parent!==b){for(p=0,q=c.length;q>p;p++)c[p].children||(c[p].children=[]),c[p].state||(c[p].state={}),l[c[p].id.toString()]=c[p];for(p=0,q=c.length;q>p;p++)l[c[p].parent.toString()]?(l[c[p].parent.toString()].children.push(c[p].id.toString()),m.children_d.push(c[p].id.toString())):"undefined"!=typeof h&&(h._data.core.last_error={error:"parse",plugin:"core",id:"core_07",reason:"Node with invalid parent",data:JSON.stringify({id:c[p].id.toString(),parent:c[p].parent.toString()})},h.settings.core.error.call(h,h._data.core.last_error));for(p=0,q=m.children.length;q>p;p++)o=s(l[m.children[p]],d,m.parents.concat()),f.push(o),l[o].children_d.length&&(f=f.concat(l[o].children_d));for(p=0,q=m.parents.length;q>p;p++)l[m.parents[p]].children_d=l[m.parents[p]].children_d.concat(f);r={cnt:k,mod:l,sel:n,par:d,dpc:f,add:g}}else{for(p=0,q=c.length;q>p;p++)o=t(c[p],d,m.parents.concat()),o&&(e.push(o),f.push(o),l[o].children_d.length&&(f=f.concat(l[o].children_d)));for(m.children=e,m.children_d=f,p=0,q=m.parents.length;q>p;p++)l[m.parents[p]].children_d=l[m.parents[p]].children_d.concat(f);r={cnt:k,mod:l,sel:n,par:d,dpc:f,add:g}}return"undefined"!=typeof window&&"undefined"!=typeof window.document?r:void postMessage(r)},j=function(b,c){if(null!==this.element){this._cnt=b.cnt;var e,f=this._model.data;for(e in f)f.hasOwnProperty(e)&&f[e].state&&f[e].state.loading&&b.mod[e]&&(b.mod[e].state.loading=!0);if(this._model.data=b.mod,c){var g,h=b.add,i=b.sel,j=this._data.core.selected.slice();if(f=this._model.data,i.length!==j.length||a.vakata.array_unique(i.concat(j)).length!==i.length){for(e=0,g=i.length;g>e;e++)-1===a.inArray(i[e],h)&&-1===a.inArray(i[e],j)&&(f[i[e]].state.selected=!1);for(e=0,g=j.length;g>e;e++)-1===a.inArray(j[e],i)&&(f[j[e]].state.selected=!0)}}b.add.length&&(this._data.core.selected=this._data.core.selected.concat(b.add)),this.trigger("model",{nodes:b.dpc,parent:b.par}),b.par!==a.jstree.root?(this._node_changed(b.par),this.redraw()):this.redraw(!0),b.add.length&&this.trigger("changed",{action:"model",selected:this._data.core.selected}),d.call(this,!0)}};if(this.settings.core.worker&&window.Blob&&window.URL&&window.Worker)try{null===this._wrk&&(this._wrk=window.URL.createObjectURL(new window.Blob(["self.onmessage = "+i.toString()],{type:"text/javascript"}))),!this._data.core.working||e?(this._data.core.working=!0,f=new window.Worker(this._wrk),f.onmessage=a.proxy(function(a){j.call(this,a.data,!0);try{f.terminate(),f=null}catch(b){}this._data.core.worker_queue.length?this._append_json_data.apply(this,this._data.core.worker_queue.shift()):this._data.core.working=!1},this),g.par?f.postMessage(g):this._data.core.worker_queue.length?this._append_json_data.apply(this,this._data.core.worker_queue.shift()):this._data.core.working=!1):this._data.core.worker_queue.push([b,c,d,!0])}catch(k){j.call(this,i(g),!1),this._data.core.worker_queue.length?this._append_json_data.apply(this,this._data.core.worker_queue.shift()):this._data.core.working=!1}else j.call(this,i(g),!1)}},_parse_model_from_html:function(c,d,e){e=e?[].concat(e):[],d&&e.unshift(d);var f,g,h=this._model.data,i={id:!1,text:!1,icon:!0,parent:d,parents:e,children:[],children_d:[],data:null,state:{},li_attr:{id:!1},a_attr:{href:"#"},original:!1},j,k,l;for(j in this._model.default_state)this._model.default_state.hasOwnProperty(j)&&(i.state[j]=this._model.default_state[j]);if(k=a.vakata.attributes(c,!0),a.each(k,function(b,c){return c=a.trim(c),c.length?(i.li_attr[b]=c,void("id"===b&&(i.id=c.toString()))):!0}),k=c.children("a").first(),k.length&&(k=a.vakata.attributes(k,!0),a.each(k,function(b,c){c=a.trim(c),c.length&&(i.a_attr[b]=c)})),k=c.children("a").first().length?c.children("a").first().clone():c.clone(),k.children("ins, i, ul").remove(),k=k.html(),k=a("
").html(k),i.text=this.settings.core.force_text?k.text():k.html(),k=c.data(),i.data=k?a.extend(!0,{},k):null,i.state.opened=c.hasClass("jstree-open"),i.state.selected=c.children("a").hasClass("jstree-clicked"),i.state.disabled=c.children("a").hasClass("jstree-disabled"),i.data&&i.data.jstree)for(j in i.data.jstree)i.data.jstree.hasOwnProperty(j)&&(i.state[j]=i.data.jstree[j]);k=c.children("a").children(".jstree-themeicon"),k.length&&(i.icon=k.hasClass("jstree-themeicon-hidden")?!1:k.attr("rel")),i.state.icon!==b&&(i.icon=i.state.icon),(i.icon===b||null===i.icon||""===i.icon)&&(i.icon=!0),k=c.children("ul").children("li");do l="j"+this._id+"_"+ ++this._cnt;while(h[l]);return i.id=i.li_attr.id?i.li_attr.id.toString():l,k.length?(k.each(a.proxy(function(b,c){f=this._parse_model_from_html(a(c),i.id,e),g=this._model.data[f],i.children.push(f),g.children_d.length&&(i.children_d=i.children_d.concat(g.children_d))},this)),i.children_d=i.children_d.concat(i.children)):c.hasClass("jstree-closed")&&(i.state.loaded=!1),i.li_attr["class"]&&(i.li_attr["class"]=i.li_attr["class"].replace("jstree-closed","").replace("jstree-open","")),i.a_attr["class"]&&(i.a_attr["class"]=i.a_attr["class"].replace("jstree-clicked","").replace("jstree-disabled","")),h[i.id]=i,i.state.selected&&this._data.core.selected.push(i.id),i.id},_parse_model_from_flat_json:function(a,c,d){d=d?d.concat():[],c&&d.unshift(c);var e=a.id.toString(),f=this._model.data,g=this._model.default_state,h,i,j,k,l={id:e,text:a.text||"",icon:a.icon!==b?a.icon:!0,parent:c,parents:d,children:a.children||[],children_d:a.children_d||[],data:a.data,state:{},li_attr:{id:!1},a_attr:{href:"#"},original:!1};for(h in g)g.hasOwnProperty(h)&&(l.state[h]=g[h]);if(a&&a.data&&a.data.jstree&&a.data.jstree.icon&&(l.icon=a.data.jstree.icon),(l.icon===b||null===l.icon||""===l.icon)&&(l.icon=!0),a&&a.data&&(l.data=a.data,a.data.jstree))for(h in a.data.jstree)a.data.jstree.hasOwnProperty(h)&&(l.state[h]=a.data.jstree[h]);if(a&&"object"==typeof a.state)for(h in a.state)a.state.hasOwnProperty(h)&&(l.state[h]=a.state[h]);if(a&&"object"==typeof a.li_attr)for(h in a.li_attr)a.li_attr.hasOwnProperty(h)&&(l.li_attr[h]=a.li_attr[h]);if(l.li_attr.id||(l.li_attr.id=e),a&&"object"==typeof a.a_attr)for(h in a.a_attr)a.a_attr.hasOwnProperty(h)&&(l.a_attr[h]=a.a_attr[h]);for(a&&a.children&&a.children===!0&&(l.state.loaded=!1,l.children=[],l.children_d=[]),f[l.id]=l,h=0,i=l.children.length;i>h;h++)j=this._parse_model_from_flat_json(f[l.children[h]],l.id,d),k=f[j],l.children_d.push(j),k.children_d.length&&(l.children_d=l.children_d.concat(k.children_d));return delete a.data,delete a.children,f[l.id].original=a,l.state.selected&&this._data.core.selected.push(l.id),l.id},_parse_model_from_json:function(a,c,d){d=d?d.concat():[],c&&d.unshift(c);var e=!1,f,g,h,i,j=this._model.data,k=this._model.default_state,l;do e="j"+this._id+"_"+ ++this._cnt;while(j[e]);l={id:!1,text:"string"==typeof a?a:"",icon:"object"==typeof a&&a.icon!==b?a.icon:!0,parent:c,parents:d,children:[],children_d:[],data:null,state:{},li_attr:{id:!1},a_attr:{href:"#"},original:!1};for(f in k)k.hasOwnProperty(f)&&(l.state[f]=k[f]);if(a&&a.id&&(l.id=a.id.toString()),a&&a.text&&(l.text=a.text),a&&a.data&&a.data.jstree&&a.data.jstree.icon&&(l.icon=a.data.jstree.icon),(l.icon===b||null===l.icon||""===l.icon)&&(l.icon=!0),a&&a.data&&(l.data=a.data,a.data.jstree))for(f in a.data.jstree)a.data.jstree.hasOwnProperty(f)&&(l.state[f]=a.data.jstree[f]);if(a&&"object"==typeof a.state)for(f in a.state)a.state.hasOwnProperty(f)&&(l.state[f]=a.state[f]);if(a&&"object"==typeof a.li_attr)for(f in a.li_attr)a.li_attr.hasOwnProperty(f)&&(l.li_attr[f]=a.li_attr[f]); +if(l.li_attr.id&&!l.id&&(l.id=l.li_attr.id.toString()),l.id||(l.id=e),l.li_attr.id||(l.li_attr.id=l.id),a&&"object"==typeof a.a_attr)for(f in a.a_attr)a.a_attr.hasOwnProperty(f)&&(l.a_attr[f]=a.a_attr[f]);if(a&&a.children&&a.children.length){for(f=0,g=a.children.length;g>f;f++)h=this._parse_model_from_json(a.children[f],l.id,d),i=j[h],l.children.push(h),i.children_d.length&&(l.children_d=l.children_d.concat(i.children_d));l.children_d=l.children.concat(l.children_d)}return a&&a.children&&a.children===!0&&(l.state.loaded=!1,l.children=[],l.children_d=[]),delete a.data,delete a.children,l.original=a,j[l.id]=l,l.state.selected&&this._data.core.selected.push(l.id),l.id},_redraw:function(){var b=this._model.force_full_redraw?this._model.data[a.jstree.root].children.concat([]):this._model.changed.concat([]),c=i.createElement("UL"),d,e,f,g=this._data.core.focused;for(e=0,f=b.length;f>e;e++)d=this.redraw_node(b[e],!0,this._model.force_full_redraw),d&&this._model.force_full_redraw&&c.appendChild(d);this._model.force_full_redraw&&(c.className=this.get_container_ul()[0].className,c.setAttribute("role","group"),this.element.empty().append(c)),null!==g&&this.settings.core.restore_focus&&(d=this.get_node(g,!0),d&&d.length&&d.children(".jstree-anchor")[0]!==i.activeElement?d.children(".jstree-anchor").focus():this._data.core.focused=null),this._model.force_full_redraw=!1,this._model.changed=[],this.trigger("redraw",{nodes:b})},redraw:function(a){a&&(this._model.force_full_redraw=!0),this._redraw()},draw_children:function(b){var c=this.get_node(b),d=!1,e=!1,f=!1,g=i;if(!c)return!1;if(c.id===a.jstree.root)return this.redraw(!0);if(b=this.get_node(b,!0),!b||!b.length)return!1;if(b.children(".jstree-children").remove(),b=b[0],c.children.length&&c.state.loaded){for(f=g.createElement("UL"),f.setAttribute("role","group"),f.className="jstree-children",d=0,e=c.children.length;e>d;d++)f.appendChild(this.redraw_node(c.children[d],!0,!0));b.appendChild(f)}},redraw_node:function(b,c,d,e){var f=this.get_node(b),g=!1,h=!1,j=!1,k=!1,l=!1,m=!1,n="",o=i,p=this._model.data,q=!1,r=!1,s=null,t=0,u=0,v=!1,w=!1;if(!f)return!1;if(f.id===a.jstree.root)return this.redraw(!0);if(c=c||0===f.children.length,b=i.querySelector?this.element[0].querySelector("#"+(-1!=="0123456789".indexOf(f.id[0])?"\\3"+f.id[0]+" "+f.id.substr(1).replace(a.jstree.idregex,"\\$&"):f.id.replace(a.jstree.idregex,"\\$&"))):i.getElementById(f.id))b=a(b),d||(g=b.parent().parent()[0],g===this.element[0]&&(g=null),h=b.index()),c||!f.children.length||b.children(".jstree-children").length||(c=!0),c||(j=b.children(".jstree-children")[0]),q=b.children(".jstree-anchor")[0]===i.activeElement,b.remove();else if(c=!0,!d){if(g=f.parent!==a.jstree.root?a("#"+f.parent.replace(a.jstree.idregex,"\\$&"),this.element)[0]:null,!(null===g||g&&p[f.parent].state.opened))return!1;h=a.inArray(f.id,null===g?p[a.jstree.root].children:p[f.parent].children)}b=this._data.core.node.cloneNode(!0),n="jstree-node ";for(k in f.li_attr)if(f.li_attr.hasOwnProperty(k)){if("id"===k)continue;"class"!==k?b.setAttribute(k,f.li_attr[k]):n+=f.li_attr[k]}for(f.a_attr.id||(f.a_attr.id=f.id+"_anchor"),b.setAttribute("aria-selected",!!f.state.selected),b.setAttribute("aria-level",f.parents.length),b.setAttribute("aria-labelledby",f.a_attr.id),f.state.disabled&&b.setAttribute("aria-disabled",!0),k=0,l=f.children.length;l>k;k++)if(!p[f.children[k]].state.hidden){v=!0;break}if(null!==f.parent&&p[f.parent]&&!f.state.hidden&&(k=a.inArray(f.id,p[f.parent].children),w=f.id,-1!==k))for(k++,l=p[f.parent].children.length;l>k;k++)if(p[p[f.parent].children[k]].state.hidden||(w=p[f.parent].children[k]),w!==f.id)break;f.state.hidden&&(n+=" jstree-hidden"),f.state.loading&&(n+=" jstree-loading"),f.state.loaded&&!v?n+=" jstree-leaf":(n+=f.state.opened&&f.state.loaded?" jstree-open":" jstree-closed",b.setAttribute("aria-expanded",f.state.opened&&f.state.loaded)),w===f.id&&(n+=" jstree-last"),b.id=f.id,b.className=n,n=(f.state.selected?" jstree-clicked":"")+(f.state.disabled?" jstree-disabled":"");for(l in f.a_attr)if(f.a_attr.hasOwnProperty(l)){if("href"===l&&"#"===f.a_attr[l])continue;"class"!==l?b.childNodes[1].setAttribute(l,f.a_attr[l]):n+=" "+f.a_attr[l]}if(n.length&&(b.childNodes[1].className="jstree-anchor "+n),(f.icon&&f.icon!==!0||f.icon===!1)&&(f.icon===!1?b.childNodes[1].childNodes[0].className+=" jstree-themeicon-hidden":-1===f.icon.indexOf("/")&&-1===f.icon.indexOf(".")?b.childNodes[1].childNodes[0].className+=" "+f.icon+" jstree-themeicon-custom":(b.childNodes[1].childNodes[0].style.backgroundImage='url("'+f.icon+'")',b.childNodes[1].childNodes[0].style.backgroundPosition="center center",b.childNodes[1].childNodes[0].style.backgroundSize="auto",b.childNodes[1].childNodes[0].className+=" jstree-themeicon-custom")),this.settings.core.force_text?b.childNodes[1].appendChild(o.createTextNode(f.text)):b.childNodes[1].innerHTML+=f.text,c&&f.children.length&&(f.state.opened||e)&&f.state.loaded){for(m=o.createElement("UL"),m.setAttribute("role","group"),m.className="jstree-children",k=0,l=f.children.length;l>k;k++)m.appendChild(this.redraw_node(f.children[k],c,!0));b.appendChild(m)}if(j&&b.appendChild(j),!d){for(g||(g=this.element[0]),k=0,l=g.childNodes.length;l>k;k++)if(g.childNodes[k]&&g.childNodes[k].className&&-1!==g.childNodes[k].className.indexOf("jstree-children")){s=g.childNodes[k];break}s||(s=o.createElement("UL"),s.setAttribute("role","group"),s.className="jstree-children",g.appendChild(s)),g=s,hf;f++)this.open_node(c[f],d,e);return!0}return c=this.get_node(c),c&&c.id!==a.jstree.root?(e=e===b?this.settings.core.animation:e,this.is_closed(c)?this.is_loaded(c)?(h=this.get_node(c,!0),i=this,h.length&&(e&&h.children(".jstree-children").length&&h.children(".jstree-children").stop(!0,!0),c.children.length&&!this._firstChild(h.children(".jstree-children")[0])&&this.draw_children(c),e?(this.trigger("before_open",{node:c}),h.children(".jstree-children").css("display","none").end().removeClass("jstree-closed").addClass("jstree-open").attr("aria-expanded",!0).children(".jstree-children").stop(!0,!0).slideDown(e,function(){this.style.display="",i.element&&i.trigger("after_open",{node:c})})):(this.trigger("before_open",{node:c}),h[0].className=h[0].className.replace("jstree-closed","jstree-open"),h[0].setAttribute("aria-expanded",!0))),c.state.opened=!0,d&&d.call(this,c,!0),h.length||this.trigger("before_open",{node:c}),this.trigger("open_node",{node:c}),e&&h.length||this.trigger("after_open",{node:c}),!0):this.is_loading(c)?setTimeout(a.proxy(function(){this.open_node(c,d,e)},this),500):void this.load_node(c,function(a,b){return b?this.open_node(a,d,e):d?d.call(this,a,!1):!1}):(d&&d.call(this,c,!1),!1)):!1},_open_to:function(b){if(b=this.get_node(b),!b||b.id===a.jstree.root)return!1;var c,d,e=b.parents;for(c=0,d=e.length;d>c;c+=1)c!==a.jstree.root&&this.open_node(e[c],!1,0);return a("#"+b.id.replace(a.jstree.idregex,"\\$&"),this.element)},close_node:function(c,d){var e,f,g,h;if(a.isArray(c)){for(c=c.slice(),e=0,f=c.length;f>e;e++)this.close_node(c[e],d);return!0}return c=this.get_node(c),c&&c.id!==a.jstree.root?this.is_closed(c)?!1:(d=d===b?this.settings.core.animation:d,g=this,h=this.get_node(c,!0),c.state.opened=!1,this.trigger("close_node",{node:c}),void(h.length?d?h.children(".jstree-children").attr("style","display:block !important").end().removeClass("jstree-open").addClass("jstree-closed").attr("aria-expanded",!1).children(".jstree-children").stop(!0,!0).slideUp(d,function(){this.style.display="",h.children(".jstree-children").remove(),g.element&&g.trigger("after_close",{node:c})}):(h[0].className=h[0].className.replace("jstree-open","jstree-closed"),h.attr("aria-expanded",!1).children(".jstree-children").remove(),this.trigger("after_close",{node:c})):this.trigger("after_close",{node:c}))):!1},toggle_node:function(b){var c,d;if(a.isArray(b)){for(b=b.slice(),c=0,d=b.length;d>c;c++)this.toggle_node(b[c]);return!0}return this.is_closed(b)?this.open_node(b):this.is_open(b)?this.close_node(b):void 0},open_all:function(b,c,d){if(b||(b=a.jstree.root),b=this.get_node(b),!b)return!1;var e=b.id===a.jstree.root?this.get_container_ul():this.get_node(b,!0),f,g,h;if(!e.length){for(f=0,g=b.children_d.length;g>f;f++)this.is_closed(this._model.data[b.children_d[f]])&&(this._model.data[b.children_d[f]].state.opened=!0);return this.trigger("open_all",{node:b})}d=d||e,h=this,e=this.is_closed(b)?e.find(".jstree-closed").addBack():e.find(".jstree-closed"),e.each(function(){h.open_node(this,function(a,b){b&&this.is_parent(a)&&this.open_all(a,c,d)},c||0)}),0===d.find(".jstree-closed").length&&this.trigger("open_all",{node:this.get_node(d)})},close_all:function(b,c){if(b||(b=a.jstree.root),b=this.get_node(b),!b)return!1;var d=b.id===a.jstree.root?this.get_container_ul():this.get_node(b,!0),e=this,f,g;for(d.length&&(d=this.is_open(b)?d.find(".jstree-open").addBack():d.find(".jstree-open"),a(d.get().reverse()).each(function(){e.close_node(this,c||0)})),f=0,g=b.children_d.length;g>f;f++)this._model.data[b.children_d[f]].state.opened=!1;this.trigger("close_all",{node:b})},is_disabled:function(a){return a=this.get_node(a),a&&a.state&&a.state.disabled},enable_node:function(b){var c,d;if(a.isArray(b)){for(b=b.slice(),c=0,d=b.length;d>c;c++)this.enable_node(b[c]);return!0}return b=this.get_node(b),b&&b.id!==a.jstree.root?(b.state.disabled=!1,this.get_node(b,!0).children(".jstree-anchor").removeClass("jstree-disabled").attr("aria-disabled",!1),void this.trigger("enable_node",{node:b})):!1},disable_node:function(b){var c,d;if(a.isArray(b)){for(b=b.slice(),c=0,d=b.length;d>c;c++)this.disable_node(b[c]);return!0}return b=this.get_node(b),b&&b.id!==a.jstree.root?(b.state.disabled=!0,this.get_node(b,!0).children(".jstree-anchor").addClass("jstree-disabled").attr("aria-disabled",!0),void this.trigger("disable_node",{node:b})):!1},is_hidden:function(a){return a=this.get_node(a),a.state.hidden===!0},hide_node:function(b,c){var d,e;if(a.isArray(b)){for(b=b.slice(),d=0,e=b.length;e>d;d++)this.hide_node(b[d],!0);return c||this.redraw(),!0}return b=this.get_node(b),b&&b.id!==a.jstree.root?void(b.state.hidden||(b.state.hidden=!0,this._node_changed(b.parent),c||this.redraw(),this.trigger("hide_node",{node:b}))):!1},show_node:function(b,c){var d,e;if(a.isArray(b)){for(b=b.slice(),d=0,e=b.length;e>d;d++)this.show_node(b[d],!0);return c||this.redraw(),!0}return b=this.get_node(b),b&&b.id!==a.jstree.root?void(b.state.hidden&&(b.state.hidden=!1,this._node_changed(b.parent),c||this.redraw(),this.trigger("show_node",{node:b}))):!1},hide_all:function(b){var c,d=this._model.data,e=[];for(c in d)d.hasOwnProperty(c)&&c!==a.jstree.root&&!d[c].state.hidden&&(d[c].state.hidden=!0,e.push(c));return this._model.force_full_redraw=!0,b||this.redraw(),this.trigger("hide_all",{nodes:e}),e},show_all:function(b){var c,d=this._model.data,e=[];for(c in d)d.hasOwnProperty(c)&&c!==a.jstree.root&&d[c].state.hidden&&(d[c].state.hidden=!1,e.push(c));return this._model.force_full_redraw=!0,b||this.redraw(),this.trigger("show_all",{nodes:e}),e},activate_node:function(a,c){if(this.is_disabled(a))return!1;if(c&&"object"==typeof c||(c={}),this._data.core.last_clicked=this._data.core.last_clicked&&this._data.core.last_clicked.id!==b?this.get_node(this._data.core.last_clicked.id):null,this._data.core.last_clicked&&!this._data.core.last_clicked.state.selected&&(this._data.core.last_clicked=null),!this._data.core.last_clicked&&this._data.core.selected.length&&(this._data.core.last_clicked=this.get_node(this._data.core.selected[this._data.core.selected.length-1])),this.settings.core.multiple&&(c.metaKey||c.ctrlKey||c.shiftKey)&&(!c.shiftKey||this._data.core.last_clicked&&this.get_parent(a)&&this.get_parent(a)===this._data.core.last_clicked.parent))if(c.shiftKey){var d=this.get_node(a).id,e=this._data.core.last_clicked.id,f=this.get_node(this._data.core.last_clicked.parent).children,g=!1,h,i;for(h=0,i=f.length;i>h;h+=1)f[h]===d&&(g=!g),f[h]===e&&(g=!g),this.is_disabled(f[h])||!g&&f[h]!==d&&f[h]!==e?this.deselect_node(f[h],!0,c):this.is_hidden(f[h])||this.select_node(f[h],!0,!1,c);this.trigger("changed",{action:"select_node",node:this.get_node(a),selected:this._data.core.selected,event:c})}else this.is_selected(a)?this.deselect_node(a,!1,c):this.select_node(a,!1,!1,c);else!this.settings.core.multiple&&(c.metaKey||c.ctrlKey||c.shiftKey)&&this.is_selected(a)?this.deselect_node(a,!1,c):(this.deselect_all(!0),this.select_node(a,!1,!1,c),this._data.core.last_clicked=this.get_node(a));this.trigger("activate_node",{node:this.get_node(a),event:c})},hover_node:function(a){if(a=this.get_node(a,!0),!a||!a.length||a.children(".jstree-hovered").length)return!1;var b=this.element.find(".jstree-hovered"),c=this.element;b&&b.length&&this.dehover_node(b),a.children(".jstree-anchor").addClass("jstree-hovered"),this.trigger("hover_node",{node:this.get_node(a)}),setTimeout(function(){c.attr("aria-activedescendant",a[0].id)},0)},dehover_node:function(a){return a=this.get_node(a,!0),a&&a.length&&a.children(".jstree-hovered").length?(a.children(".jstree-anchor").removeClass("jstree-hovered"),void this.trigger("dehover_node",{node:this.get_node(a)})):!1},select_node:function(b,c,d,e){var f,g,h,i;if(a.isArray(b)){for(b=b.slice(),g=0,h=b.length;h>g;g++)this.select_node(b[g],c,d,e);return!0}return b=this.get_node(b),b&&b.id!==a.jstree.root?(f=this.get_node(b,!0),void(b.state.selected||(b.state.selected=!0,this._data.core.selected.push(b.id),d||(f=this._open_to(b)),f&&f.length&&f.attr("aria-selected",!0).children(".jstree-anchor").addClass("jstree-clicked"),this.trigger("select_node",{node:b,selected:this._data.core.selected,event:e}),c||this.trigger("changed",{action:"select_node",node:b,selected:this._data.core.selected,event:e})))):!1},deselect_node:function(b,c,d){var e,f,g;if(a.isArray(b)){for(b=b.slice(),e=0,f=b.length;f>e;e++)this.deselect_node(b[e],c,d);return!0}return b=this.get_node(b),b&&b.id!==a.jstree.root?(g=this.get_node(b,!0),void(b.state.selected&&(b.state.selected=!1,this._data.core.selected=a.vakata.array_remove_item(this._data.core.selected,b.id),g.length&&g.attr("aria-selected",!1).children(".jstree-anchor").removeClass("jstree-clicked"),this.trigger("deselect_node",{node:b,selected:this._data.core.selected,event:d}),c||this.trigger("changed",{action:"deselect_node",node:b,selected:this._data.core.selected,event:d})))):!1},select_all:function(b){var c=this._data.core.selected.concat([]),d,e;for(this._data.core.selected=this._model.data[a.jstree.root].children_d.concat(),d=0,e=this._data.core.selected.length;e>d;d++)this._model.data[this._data.core.selected[d]]&&(this._model.data[this._data.core.selected[d]].state.selected=!0);this.redraw(!0),this.trigger("select_all",{selected:this._data.core.selected}),b||this.trigger("changed",{action:"select_all",selected:this._data.core.selected,old_selection:c})},deselect_all:function(a){var b=this._data.core.selected.concat([]),c,d;for(c=0,d=this._data.core.selected.length;d>c;c++)this._model.data[this._data.core.selected[c]]&&(this._model.data[this._data.core.selected[c]].state.selected=!1);this._data.core.selected=[],this.element.find(".jstree-clicked").removeClass("jstree-clicked").parent().attr("aria-selected",!1),this.trigger("deselect_all",{selected:this._data.core.selected,node:b}),a||this.trigger("changed",{action:"deselect_all",selected:this._data.core.selected,old_selection:b})},is_selected:function(b){return b=this.get_node(b),b&&b.id!==a.jstree.root?b.state.selected:!1},get_selected:function(b){return b?a.map(this._data.core.selected,a.proxy(function(a){return this.get_node(a)},this)):this._data.core.selected.slice()},get_top_selected:function(b){var c=this.get_selected(!0),d={},e,f,g,h;for(e=0,f=c.length;f>e;e++)d[c[e].id]=c[e];for(e=0,f=c.length;f>e;e++)for(g=0,h=c[e].children_d.length;h>g;g++)d[c[e].children_d[g]]&&delete d[c[e].children_d[g]];c=[];for(e in d)d.hasOwnProperty(e)&&c.push(e);return b?a.map(c,a.proxy(function(a){return this.get_node(a)},this)):c},get_bottom_selected:function(b){var c=this.get_selected(!0),d=[],e,f;for(e=0,f=c.length;f>e;e++)c[e].children.length||d.push(c[e].id);return b?a.map(d,a.proxy(function(a){return this.get_node(a)},this)):d},get_state:function(){var b={core:{open:[],loaded:[],scroll:{left:this.element.scrollLeft(),top:this.element.scrollTop()},selected:[]}},c;for(c in this._model.data)this._model.data.hasOwnProperty(c)&&c!==a.jstree.root&&(this._model.data[c].state.loaded&&this.settings.core.loaded_state&&b.core.loaded.push(c),this._model.data[c].state.opened&&b.core.open.push(c),this._model.data[c].state.selected&&b.core.selected.push(c));return b},set_state:function(c,d){if(c){if(c.core&&c.core.selected&&c.core.initial_selection===b&&(c.core.initial_selection=this._data.core.selected.concat([]).sort().join(",")),c.core){var e,f,g,h,i;if(c.core.loaded)return this.settings.core.loaded_state&&a.isArray(c.core.loaded)&&c.core.loaded.length?this._load_nodes(c.core.loaded,function(a){delete c.core.loaded,this.set_state(c,d)}):(delete c.core.loaded,this.set_state(c,d)),!1;if(c.core.open)return a.isArray(c.core.open)&&c.core.open.length?this._load_nodes(c.core.open,function(a){this.open_node(a,!1,0),delete c.core.open,this.set_state(c,d)}):(delete c.core.open,this.set_state(c,d)),!1;if(c.core.scroll)return c.core.scroll&&c.core.scroll.left!==b&&this.element.scrollLeft(c.core.scroll.left),c.core.scroll&&c.core.scroll.top!==b&&this.element.scrollTop(c.core.scroll.top),delete c.core.scroll,this.set_state(c,d),!1;if(c.core.selected)return h=this,(c.core.initial_selection===b||c.core.initial_selection===this._data.core.selected.concat([]).sort().join(","))&&(this.deselect_all(),a.each(c.core.selected,function(a,b){h.select_node(b,!1,!0)})),delete c.core.initial_selection,delete c.core.selected,this.set_state(c,d),!1;for(i in c)c.hasOwnProperty(i)&&"core"!==i&&-1===a.inArray(i,this.settings.plugins)&&delete c[i];if(a.isEmptyObject(c.core))return delete c.core,this.set_state(c,d),!1}return a.isEmptyObject(c)?(c=null,d&&d.call(this),this.trigger("set_state"),!1):!0}return!1},refresh:function(b,c){this._data.core.state=c===!0?{}:this.get_state(),c&&a.isFunction(c)&&(this._data.core.state=c.call(this,this._data.core.state)),this._cnt=0,this._model.data={},this._model.data[a.jstree.root]={id:a.jstree.root,parent:null,parents:[],children:[],children_d:[],state:{loaded:!1}},this._data.core.selected=[],this._data.core.last_clicked=null,this._data.core.focused=null;var d=this.get_container_ul()[0].className;b||(this.element.html(""),this.element.attr("aria-activedescendant","j"+this._id+"_loading")),this.load_node(a.jstree.root,function(b,c){c&&(this.get_container_ul()[0].className=d,this._firstChild(this.get_container_ul()[0])&&this.element.attr("aria-activedescendant",this._firstChild(this.get_container_ul()[0]).id),this.set_state(a.extend(!0,{},this._data.core.state),function(){this.trigger("refresh")})),this._data.core.state=null})},refresh_node:function(b){if(b=this.get_node(b),!b||b.id===a.jstree.root)return!1;var c=[],d=[],e=this._data.core.selected.concat([]);d.push(b.id),b.state.opened===!0&&c.push(b.id),this.get_node(b,!0).find(".jstree-open").each(function(){d.push(this.id),c.push(this.id)}),this._load_nodes(d,a.proxy(function(a){this.open_node(c,!1,0),this.select_node(e),this.trigger("refresh_node",{node:b,nodes:a})},this),!1,!0)},set_id:function(b,c){if(b=this.get_node(b),!b||b.id===a.jstree.root)return!1;var d,e,f=this._model.data,g=b.id;for(c=c.toString(),f[b.parent].children[a.inArray(b.id,f[b.parent].children)]=c,d=0,e=b.parents.length;e>d;d++)f[b.parents[d]].children_d[a.inArray(b.id,f[b.parents[d]].children_d)]=c;for(d=0,e=b.children.length;e>d;d++)f[b.children[d]].parent=c;for(d=0,e=b.children_d.length;e>d;d++)f[b.children_d[d]].parents[a.inArray(b.id,f[b.children_d[d]].parents)]=c;return d=a.inArray(b.id,this._data.core.selected),-1!==d&&(this._data.core.selected[d]=c),d=this.get_node(b.id,!0),d&&(d.attr("id",c),this.element.attr("aria-activedescendant")===b.id&&this.element.attr("aria-activedescendant",c)),delete f[b.id],b.id=c,b.li_attr.id=c,f[c]=b,this.trigger("set_id",{node:b,"new":b.id,old:g}),!0},get_text:function(b){return b=this.get_node(b),b&&b.id!==a.jstree.root?b.text:!1},set_text:function(b,c){var d,e;if(a.isArray(b)){for(b=b.slice(),d=0,e=b.length;e>d;d++)this.set_text(b[d],c);return!0}return b=this.get_node(b),b&&b.id!==a.jstree.root?(b.text=c,this.get_node(b,!0).length&&this.redraw_node(b.id),this.trigger("set_text",{obj:b,text:c}),!0):!1},get_json:function(b,c,d){if(b=this.get_node(b||a.jstree.root),!b)return!1;c&&c.flat&&!d&&(d=[]);var e={id:b.id,text:b.text,icon:this.get_icon(b),li_attr:a.extend(!0,{},b.li_attr),a_attr:a.extend(!0,{},b.a_attr),state:{},data:c&&c.no_data?!1:a.extend(!0,a.isArray(b.data)?[]:{},b.data)},f,g;if(c&&c.flat?e.parent=b.parent:e.children=[],c&&c.no_state)delete e.state;else for(f in b.state)b.state.hasOwnProperty(f)&&(e.state[f]=b.state[f]);if(c&&c.no_li_attr&&delete e.li_attr,c&&c.no_a_attr&&delete e.a_attr,c&&c.no_id&&(delete e.id,e.li_attr&&e.li_attr.id&&delete e.li_attr.id,e.a_attr&&e.a_attr.id&&delete e.a_attr.id),c&&c.flat&&b.id!==a.jstree.root&&d.push(e),!c||!c.no_children)for(f=0,g=b.children.length;g>f;f++)c&&c.flat?this.get_json(b.children[f],c,d):e.children.push(this.get_json(b.children[f],c));return c&&c.flat?d:b.id===a.jstree.root?e.children:e},create_node:function(c,d,e,f,g){if(null===c&&(c=a.jstree.root),c=this.get_node(c),!c)return!1;if(e=e===b?"last":e,!e.toString().match(/^(before|after)$/)&&!g&&!this.is_loaded(c))return this.load_node(c,function(){this.create_node(c,d,e,f,!0)});d||(d={text:this.get_string("New node")}),d="string"==typeof d?{text:d}:a.extend(!0,{},d),d.text===b&&(d.text=this.get_string("New node"));var h,i,j,k;switch(c.id===a.jstree.root&&("before"===e&&(e="first"),"after"===e&&(e="last")),e){case"before":h=this.get_node(c.parent),e=a.inArray(c.id,h.children),c=h;break;case"after":h=this.get_node(c.parent),e=a.inArray(c.id,h.children)+1,c=h;break;case"inside":case"first":e=0;break;case"last":e=c.children.length;break;default:e||(e=0)}if(e>c.children.length&&(e=c.children.length),d.id||(d.id=!0),!this.check("create_node",d,c,e))return this.settings.core.error.call(this,this._data.core.last_error),!1;if(d.id===!0&&delete d.id,d=this._parse_model_from_json(d,c.id,c.parents.concat()),!d)return!1;for(h=this.get_node(d),i=[],i.push(d),i=i.concat(h.children_d),this.trigger("model",{nodes:i,parent:c.id}),c.children_d=c.children_d.concat(i),j=0,k=c.parents.length;k>j;j++)this._model.data[c.parents[j]].children_d=this._model.data[c.parents[j]].children_d.concat(i);for(d=h,h=[],j=0,k=c.children.length;k>j;j++)h[j>=e?j+1:j]=c.children[j];return h[e]=d.id,c.children=h,this.redraw_node(c,!0),this.trigger("create_node",{node:this.get_node(d),parent:c.id,position:e}),f&&f.call(this,this.get_node(d)),d.id},rename_node:function(b,c){var d,e,f;if(a.isArray(b)){for(b=b.slice(),d=0,e=b.length;e>d;d++)this.rename_node(b[d],c);return!0}return b=this.get_node(b),b&&b.id!==a.jstree.root?(f=b.text,this.check("rename_node",b,this.get_parent(b),c)?(this.set_text(b,c),this.trigger("rename_node",{node:b,text:c,old:f}),!0):(this.settings.core.error.call(this,this._data.core.last_error),!1)):!1},delete_node:function(b){var c,d,e,f,g,h,i,j,k,l,m,n;if(a.isArray(b)){for(b=b.slice(),c=0,d=b.length;d>c;c++)this.delete_node(b[c]);return!0}if(b=this.get_node(b),!b||b.id===a.jstree.root)return!1;if(e=this.get_node(b.parent),f=a.inArray(b.id,e.children),l=!1,!this.check("delete_node",b,e,f))return this.settings.core.error.call(this,this._data.core.last_error),!1;for(-1!==f&&(e.children=a.vakata.array_remove(e.children,f)),g=b.children_d.concat([]),g.push(b.id),h=0,i=b.parents.length;i>h;h++)this._model.data[b.parents[h]].children_d=a.vakata.array_filter(this._model.data[b.parents[h]].children_d,function(b){return-1===a.inArray(b,g)});for(j=0,k=g.length;k>j;j++)if(this._model.data[g[j]].state.selected){l=!0;break}for(l&&(this._data.core.selected=a.vakata.array_filter(this._data.core.selected,function(b){return-1===a.inArray(b,g)})),this.trigger("delete_node",{node:b,parent:e.id}),l&&this.trigger("changed",{action:"delete_node",node:b,selected:this._data.core.selected,parent:e.id}),j=0,k=g.length;k>j;j++)delete this._model.data[g[j]];return-1!==a.inArray(this._data.core.focused,g)&&(this._data.core.focused=null,m=this.element[0].scrollTop,n=this.element[0].scrollLeft,e.id===a.jstree.root?this._model.data[a.jstree.root].children[0]&&this.get_node(this._model.data[a.jstree.root].children[0],!0).children(".jstree-anchor").focus():this.get_node(e,!0).children(".jstree-anchor").focus(),this.element[0].scrollTop=m,this.element[0].scrollLeft=n),this.redraw_node(e,!0),!0},check:function(b,c,d,e,f){c=c&&c.id?c:this.get_node(c),d=d&&d.id?d:this.get_node(d);var g=b.match(/^move_node|copy_node|create_node$/i)?d:c,h=this.settings.core.check_callback;if("move_node"===b||"copy_node"===b){if(!(f&&f.is_multi||"move_node"!==b||a.inArray(c.id,d.children)!==e))return this._data.core.last_error={error:"check",plugin:"core",id:"core_08",reason:"Moving node to its current position",data:JSON.stringify({chk:b,pos:e,obj:c&&c.id?c.id:!1,par:d&&d.id?d.id:!1})},!1;if(!(f&&f.is_multi||c.id!==d.id&&("move_node"!==b||a.inArray(c.id,d.children)!==e)&&-1===a.inArray(d.id,c.children_d)))return this._data.core.last_error={error:"check",plugin:"core",id:"core_01",reason:"Moving parent inside child",data:JSON.stringify({chk:b,pos:e,obj:c&&c.id?c.id:!1,par:d&&d.id?d.id:!1})},!1}return g&&g.data&&(g=g.data),g&&g.functions&&(g.functions[b]===!1||g.functions[b]===!0)?(g.functions[b]===!1&&(this._data.core.last_error={error:"check",plugin:"core",id:"core_02",reason:"Node data prevents function: "+b,data:JSON.stringify({chk:b,pos:e,obj:c&&c.id?c.id:!1,par:d&&d.id?d.id:!1})}),g.functions[b]):h===!1||a.isFunction(h)&&h.call(this,b,c,d,e,f)===!1||h&&h[b]===!1?(this._data.core.last_error={error:"check",plugin:"core",id:"core_03",reason:"User config for core.check_callback prevents function: "+b,data:JSON.stringify({chk:b,pos:e,obj:c&&c.id?c.id:!1,par:d&&d.id?d.id:!1})},!1):!0},last_error:function(){return this._data.core.last_error},move_node:function(c,d,e,f,g,h,i){var j,k,l,m,n,o,p,q,r,s,t,u,v,w;if(d=this.get_node(d),e=e===b?0:e,!d)return!1;if(!e.toString().match(/^(before|after)$/)&&!g&&!this.is_loaded(d))return this.load_node(d,function(){this.move_node(c,d,e,f,!0,!1,i)});if(a.isArray(c)){if(1!==c.length){for(j=0,k=c.length;k>j;j++)(r=this.move_node(c[j],d,e,f,g,!1,i))&&(d=r,e="after");return this.redraw(),!0}c=c[0]}if(c=c&&c.id?c:this.get_node(c),!c||c.id===a.jstree.root)return!1;if(l=(c.parent||a.jstree.root).toString(),n=e.toString().match(/^(before|after)$/)&&d.id!==a.jstree.root?this.get_node(d.parent):d,o=i?i:this._model.data[c.id]?this:a.jstree.reference(c.id),p=!o||!o._id||this._id!==o._id,m=o&&o._id&&l&&o._model.data[l]&&o._model.data[l].children?a.inArray(c.id,o._model.data[l].children):-1,o&&o._id&&(c=o._model.data[c.id]),p)return(r=this.copy_node(c,d,e,f,g,!1,i))?(o&&o.delete_node(c),r):!1;switch(d.id===a.jstree.root&&("before"===e&&(e="first"),"after"===e&&(e="last")),e){case"before":e=a.inArray(d.id,n.children);break;case"after":e=a.inArray(d.id,n.children)+1;break;case"inside":case"first":e=0;break;case"last":e=n.children.length;break;default:e||(e=0)}if(e>n.children.length&&(e=n.children.length),!this.check("move_node",c,n,e,{core:!0,origin:i,is_multi:o&&o._id&&o._id!==this._id,is_foreign:!o||!o._id}))return this.settings.core.error.call(this,this._data.core.last_error),!1;if(c.parent===n.id){for(q=n.children.concat(),r=a.inArray(c.id,q),-1!==r&&(q=a.vakata.array_remove(q,r),e>r&&e--),r=[],s=0,t=q.length;t>s;s++)r[s>=e?s+1:s]=q[s];r[e]=c.id,n.children=r,this._node_changed(n.id),this.redraw(n.id===a.jstree.root)}else{for(r=c.children_d.concat(),r.push(c.id),s=0,t=c.parents.length;t>s;s++){for(q=[],w=o._model.data[c.parents[s]].children_d,u=0,v=w.length;v>u;u++)-1===a.inArray(w[u],r)&&q.push(w[u]);o._model.data[c.parents[s]].children_d=q}for(o._model.data[l].children=a.vakata.array_remove_item(o._model.data[l].children,c.id),s=0,t=n.parents.length;t>s;s++)this._model.data[n.parents[s]].children_d=this._model.data[n.parents[s]].children_d.concat(r);for(q=[],s=0,t=n.children.length;t>s;s++)q[s>=e?s+1:s]=n.children[s];for(q[e]=c.id,n.children=q,n.children_d.push(c.id),n.children_d=n.children_d.concat(c.children_d),c.parent=n.id,r=n.parents.concat(),r.unshift(n.id),w=c.parents.length,c.parents=r,r=r.concat(),s=0,t=c.children_d.length;t>s;s++)this._model.data[c.children_d[s]].parents=this._model.data[c.children_d[s]].parents.slice(0,-1*w),Array.prototype.push.apply(this._model.data[c.children_d[s]].parents,r);(l===a.jstree.root||n.id===a.jstree.root)&&(this._model.force_full_redraw=!0),this._model.force_full_redraw||(this._node_changed(l),this._node_changed(n.id)),h||this.redraw()}return f&&f.call(this,c,n,e),this.trigger("move_node",{node:c,parent:n.id,position:e,old_parent:l,old_position:m,is_multi:o&&o._id&&o._id!==this._id,is_foreign:!o||!o._id,old_instance:o,new_instance:this}),c.id},copy_node:function(c,d,e,f,g,h,i){var j,k,l,m,n,o,p,q,r,s,t;if(d=this.get_node(d),e=e===b?0:e,!d)return!1;if(!e.toString().match(/^(before|after)$/)&&!g&&!this.is_loaded(d))return this.load_node(d,function(){this.copy_node(c,d,e,f,!0,!1,i)});if(a.isArray(c)){if(1!==c.length){for(j=0,k=c.length;k>j;j++)(m=this.copy_node(c[j],d,e,f,g,!0,i))&&(d=m,e="after");return this.redraw(),!0}c=c[0]}if(c=c&&c.id?c:this.get_node(c),!c||c.id===a.jstree.root)return!1;switch(q=(c.parent||a.jstree.root).toString(),r=e.toString().match(/^(before|after)$/)&&d.id!==a.jstree.root?this.get_node(d.parent):d,s=i?i:this._model.data[c.id]?this:a.jstree.reference(c.id),t=!s||!s._id||this._id!==s._id,s&&s._id&&(c=s._model.data[c.id]),d.id===a.jstree.root&&("before"===e&&(e="first"),"after"===e&&(e="last")),e){case"before":e=a.inArray(d.id,r.children);break;case"after":e=a.inArray(d.id,r.children)+1;break;case"inside":case"first":e=0;break;case"last":e=r.children.length;break;default:e||(e=0)}if(e>r.children.length&&(e=r.children.length),!this.check("copy_node",c,r,e,{core:!0,origin:i,is_multi:s&&s._id&&s._id!==this._id,is_foreign:!s||!s._id}))return this.settings.core.error.call(this,this._data.core.last_error),!1;if(p=s?s.get_json(c,{no_id:!0,no_data:!0,no_state:!0}):c,!p)return!1;if(p.id===!0&&delete p.id,p=this._parse_model_from_json(p,r.id,r.parents.concat()),!p)return!1;for(m=this.get_node(p),c&&c.state&&c.state.loaded===!1&&(m.state.loaded=!1),l=[],l.push(p),l=l.concat(m.children_d),this.trigger("model",{nodes:l,parent:r.id}),n=0,o=r.parents.length;o>n;n++)this._model.data[r.parents[n]].children_d=this._model.data[r.parents[n]].children_d.concat(l);for(l=[],n=0,o=r.children.length;o>n;n++)l[n>=e?n+1:n]=r.children[n];return l[e]=m.id,r.children=l,r.children_d.push(m.id),r.children_d=r.children_d.concat(m.children_d),r.id===a.jstree.root&&(this._model.force_full_redraw=!0),this._model.force_full_redraw||this._node_changed(r.id),h||this.redraw(r.id===a.jstree.root),f&&f.call(this,m,r,e),this.trigger("copy_node",{node:m,original:c,parent:r.id,position:e,old_parent:q,old_position:s&&s._id&&q&&s._model.data[q]&&s._model.data[q].children?a.inArray(c.id,s._model.data[q].children):-1, +is_multi:s&&s._id&&s._id!==this._id,is_foreign:!s||!s._id,old_instance:s,new_instance:this}),m.id},cut:function(b){if(b||(b=this._data.core.selected.concat()),a.isArray(b)||(b=[b]),!b.length)return!1;var c=[],g,h,i;for(h=0,i=b.length;i>h;h++)g=this.get_node(b[h]),g&&g.id&&g.id!==a.jstree.root&&c.push(g);return c.length?(d=c,f=this,e="move_node",void this.trigger("cut",{node:b})):!1},copy:function(b){if(b||(b=this._data.core.selected.concat()),a.isArray(b)||(b=[b]),!b.length)return!1;var c=[],g,h,i;for(h=0,i=b.length;i>h;h++)g=this.get_node(b[h]),g&&g.id&&g.id!==a.jstree.root&&c.push(g);return c.length?(d=c,f=this,e="copy_node",void this.trigger("copy",{node:b})):!1},get_buffer:function(){return{mode:e,node:d,inst:f}},can_paste:function(){return e!==!1&&d!==!1},paste:function(a,b){return a=this.get_node(a),a&&e&&e.match(/^(copy_node|move_node)$/)&&d?(this[e](d,a,b,!1,!1,!1,f)&&this.trigger("paste",{parent:a.id,node:d,mode:e}),d=!1,e=!1,void(f=!1)):!1},clear_buffer:function(){d=!1,e=!1,f=!1,this.trigger("clear_buffer")},edit:function(b,c,d){var e,f,g,h,j,k,l,m,n,o=!1;return(b=this.get_node(b))?this.check("edit",b,this.get_parent(b))?(n=b,c="string"==typeof c?c:b.text,this.set_text(b,""),b=this._open_to(b),n.text=c,e=this._data.core.rtl,f=this.element.width(),this._data.core.focused=n.id,g=b.children(".jstree-anchor").focus(),h=a(""),j=c,k=a("
",{css:{position:"absolute",top:"-200px",left:e?"0px":"-1000px",visibility:"hidden"}}).appendTo(i.body),l=a("",{value:j,"class":"jstree-rename-input",css:{padding:"0",border:"1px solid silver","box-sizing":"border-box",display:"inline-block",height:this._data.core.li_height+"px",lineHeight:this._data.core.li_height+"px",width:"150px"},blur:a.proxy(function(c){c.stopImmediatePropagation(),c.preventDefault();var e=h.children(".jstree-rename-input"),f=e.val(),i=this.settings.core.force_text,m;""===f&&(f=j),k.remove(),h.replaceWith(g),h.remove(),j=i?j:a("
").append(a.parseHTML(j)).html(),b=this.get_node(b),this.set_text(b,j),m=!!this.rename_node(b,i?a("
").text(f).text():a("
").append(a.parseHTML(f)).html()),m||this.set_text(b,j),this._data.core.focused=n.id,setTimeout(a.proxy(function(){var a=this.get_node(n.id,!0);a.length&&(this._data.core.focused=n.id,a.children(".jstree-anchor").focus())},this),0),d&&d.call(this,n,m,o),l=null},this),keydown:function(a){var b=a.which;27===b&&(o=!0,this.value=j),(27===b||13===b||37===b||38===b||39===b||40===b||32===b)&&a.stopImmediatePropagation(),(27===b||13===b)&&(a.preventDefault(),this.blur())},click:function(a){a.stopImmediatePropagation()},mousedown:function(a){a.stopImmediatePropagation()},keyup:function(a){l.width(Math.min(k.text("pW"+this.value).width(),f))},keypress:function(a){return 13===a.which?!1:void 0}}),m={fontFamily:g.css("fontFamily")||"",fontSize:g.css("fontSize")||"",fontWeight:g.css("fontWeight")||"",fontStyle:g.css("fontStyle")||"",fontStretch:g.css("fontStretch")||"",fontVariant:g.css("fontVariant")||"",letterSpacing:g.css("letterSpacing")||"",wordSpacing:g.css("wordSpacing")||""},h.attr("class",g.attr("class")).append(g.contents().clone()).append(l),g.replaceWith(h),k.css(m),l.css(m).width(Math.min(k.text("pW"+l[0].value).width(),f))[0].select(),void a(i).one("mousedown.jstree touchstart.jstree dnd_start.vakata",function(b){l&&b.target!==l&&a(l).blur()})):(this.settings.core.error.call(this,this._data.core.last_error),!1):!1},set_theme:function(b,c){if(!b)return!1;if(c===!0){var d=this.settings.core.themes.dir;d||(d=a.jstree.path+"/themes"),c=d+"/"+b+"/style.css"}c&&-1===a.inArray(c,g)&&(a("head").append(''),g.push(c)),this._data.core.themes.name&&this.element.removeClass("jstree-"+this._data.core.themes.name),this._data.core.themes.name=b,this.element.addClass("jstree-"+b),this.element[this.settings.core.themes.responsive?"addClass":"removeClass"]("jstree-"+b+"-responsive"),this.trigger("set_theme",{theme:b})},get_theme:function(){return this._data.core.themes.name},set_theme_variant:function(a){this._data.core.themes.variant&&this.element.removeClass("jstree-"+this._data.core.themes.name+"-"+this._data.core.themes.variant),this._data.core.themes.variant=a,a&&this.element.addClass("jstree-"+this._data.core.themes.name+"-"+this._data.core.themes.variant)},get_theme_variant:function(){return this._data.core.themes.variant},show_stripes:function(){this._data.core.themes.stripes=!0,this.get_container_ul().addClass("jstree-striped"),this.trigger("show_stripes")},hide_stripes:function(){this._data.core.themes.stripes=!1,this.get_container_ul().removeClass("jstree-striped"),this.trigger("hide_stripes")},toggle_stripes:function(){this._data.core.themes.stripes?this.hide_stripes():this.show_stripes()},show_dots:function(){this._data.core.themes.dots=!0,this.get_container_ul().removeClass("jstree-no-dots"),this.trigger("show_dots")},hide_dots:function(){this._data.core.themes.dots=!1,this.get_container_ul().addClass("jstree-no-dots"),this.trigger("hide_dots")},toggle_dots:function(){this._data.core.themes.dots?this.hide_dots():this.show_dots()},show_icons:function(){this._data.core.themes.icons=!0,this.get_container_ul().removeClass("jstree-no-icons"),this.trigger("show_icons")},hide_icons:function(){this._data.core.themes.icons=!1,this.get_container_ul().addClass("jstree-no-icons"),this.trigger("hide_icons")},toggle_icons:function(){this._data.core.themes.icons?this.hide_icons():this.show_icons()},show_ellipsis:function(){this._data.core.themes.ellipsis=!0,this.get_container_ul().addClass("jstree-ellipsis"),this.trigger("show_ellipsis")},hide_ellipsis:function(){this._data.core.themes.ellipsis=!1,this.get_container_ul().removeClass("jstree-ellipsis"),this.trigger("hide_ellipsis")},toggle_ellipsis:function(){this._data.core.themes.ellipsis?this.hide_ellipsis():this.show_ellipsis()},set_icon:function(c,d){var e,f,g,h;if(a.isArray(c)){for(c=c.slice(),e=0,f=c.length;f>e;e++)this.set_icon(c[e],d);return!0}return c=this.get_node(c),c&&c.id!==a.jstree.root?(h=c.icon,c.icon=d===!0||null===d||d===b||""===d?!0:d,g=this.get_node(c,!0).children(".jstree-anchor").children(".jstree-themeicon"),d===!1?(g.removeClass("jstree-themeicon-custom "+h).css("background","").removeAttr("rel"),this.hide_icon(c)):d===!0||null===d||d===b||""===d?(g.removeClass("jstree-themeicon-custom "+h).css("background","").removeAttr("rel"),h===!1&&this.show_icon(c)):-1===d.indexOf("/")&&-1===d.indexOf(".")?(g.removeClass(h).css("background",""),g.addClass(d+" jstree-themeicon-custom").attr("rel",d),h===!1&&this.show_icon(c)):(g.removeClass(h).css("background",""),g.addClass("jstree-themeicon-custom").css("background","url('"+d+"') center center no-repeat").attr("rel",d),h===!1&&this.show_icon(c)),!0):!1},get_icon:function(b){return b=this.get_node(b),b&&b.id!==a.jstree.root?b.icon:!1},hide_icon:function(b){var c,d;if(a.isArray(b)){for(b=b.slice(),c=0,d=b.length;d>c;c++)this.hide_icon(b[c]);return!0}return b=this.get_node(b),b&&b!==a.jstree.root?(b.icon=!1,this.get_node(b,!0).children(".jstree-anchor").children(".jstree-themeicon").addClass("jstree-themeicon-hidden"),!0):!1},show_icon:function(b){var c,d,e;if(a.isArray(b)){for(b=b.slice(),c=0,d=b.length;d>c;c++)this.show_icon(b[c]);return!0}return b=this.get_node(b),b&&b!==a.jstree.root?(e=this.get_node(b,!0),b.icon=e.length?e.children(".jstree-anchor").children(".jstree-themeicon").attr("rel"):!0,b.icon||(b.icon=!0),e.children(".jstree-anchor").children(".jstree-themeicon").removeClass("jstree-themeicon-hidden"),!0):!1}},a.vakata={},a.vakata.attributes=function(b,c){b=a(b)[0];var d=c?{}:[];return b&&b.attributes&&a.each(b.attributes,function(b,e){-1===a.inArray(e.name.toLowerCase(),["style","contenteditable","hasfocus","tabindex"])&&null!==e.value&&""!==a.trim(e.value)&&(c?d[e.name]=e.value:d.push(e.name))}),d},a.vakata.array_unique=function(a){var c=[],d,e,f,g={};for(d=0,f=a.length;f>d;d++)g[a[d]]===b&&(c.push(a[d]),g[a[d]]=!0);return c},a.vakata.array_remove=function(a,b){return a.splice(b,1),a},a.vakata.array_remove_item=function(b,c){var d=a.inArray(c,b);return-1!==d?a.vakata.array_remove(b,d):b},a.vakata.array_filter=function(a,b,c,d,e){if(a.filter)return a.filter(b,c);d=[];for(e in a)~~e+""==e+""&&e>=0&&b.call(c,a[e],+e,a)&&d.push(a[e]);return d},a.jstree.plugins.changed=function(a,b){var c=[];this.trigger=function(a,d){var e,f;if(d||(d={}),"changed"===a.replace(".jstree","")){d.changed={selected:[],deselected:[]};var g={};for(e=0,f=c.length;f>e;e++)g[c[e]]=1;for(e=0,f=d.selected.length;f>e;e++)g[d.selected[e]]?g[d.selected[e]]=2:d.changed.selected.push(d.selected[e]);for(e=0,f=c.length;f>e;e++)1===g[c[e]]&&d.changed.deselected.push(c[e]);c=d.selected.slice()}b.trigger.call(this,a,d)},this.refresh=function(a,d){return c=[],b.refresh.apply(this,arguments)}};var j=i.createElement("I");j.className="jstree-icon jstree-checkbox",j.setAttribute("role","presentation"),a.jstree.defaults.checkbox={visible:!0,three_state:!0,whole_node:!0,keep_selected_style:!0,cascade:"",tie_selection:!0,cascade_to_disabled:!0,cascade_to_hidden:!0},a.jstree.plugins.checkbox=function(c,d){this.bind=function(){d.bind.call(this),this._data.checkbox.uto=!1,this._data.checkbox.selected=[],this.settings.checkbox.three_state&&(this.settings.checkbox.cascade="up+down+undetermined"),this.element.on("init.jstree",a.proxy(function(){this._data.checkbox.visible=this.settings.checkbox.visible,this.settings.checkbox.keep_selected_style||this.element.addClass("jstree-checkbox-no-clicked"),this.settings.checkbox.tie_selection&&this.element.addClass("jstree-checkbox-selection")},this)).on("loading.jstree",a.proxy(function(){this[this._data.checkbox.visible?"show_checkboxes":"hide_checkboxes"]()},this)),-1!==this.settings.checkbox.cascade.indexOf("undetermined")&&this.element.on("changed.jstree uncheck_node.jstree check_node.jstree uncheck_all.jstree check_all.jstree move_node.jstree copy_node.jstree redraw.jstree open_node.jstree",a.proxy(function(){this._data.checkbox.uto&&clearTimeout(this._data.checkbox.uto),this._data.checkbox.uto=setTimeout(a.proxy(this._undetermined,this),50)},this)),this.settings.checkbox.tie_selection||this.element.on("model.jstree",a.proxy(function(a,b){var c=this._model.data,d=c[b.parent],e=b.nodes,f,g;for(f=0,g=e.length;g>f;f++)c[e[f]].state.checked=c[e[f]].state.checked||c[e[f]].original&&c[e[f]].original.state&&c[e[f]].original.state.checked,c[e[f]].state.checked&&this._data.checkbox.selected.push(e[f])},this)),(-1!==this.settings.checkbox.cascade.indexOf("up")||-1!==this.settings.checkbox.cascade.indexOf("down"))&&this.element.on("model.jstree",a.proxy(function(b,c){var d=this._model.data,e=d[c.parent],f=c.nodes,g=[],h,i,j,k,l,m,n=this.settings.checkbox.cascade,o=this.settings.checkbox.tie_selection;if(-1!==n.indexOf("down"))if(e.state[o?"selected":"checked"]){for(i=0,j=f.length;j>i;i++)d[f[i]].state[o?"selected":"checked"]=!0;this._data[o?"core":"checkbox"].selected=this._data[o?"core":"checkbox"].selected.concat(f)}else for(i=0,j=f.length;j>i;i++)if(d[f[i]].state[o?"selected":"checked"]){for(k=0,l=d[f[i]].children_d.length;l>k;k++)d[d[f[i]].children_d[k]].state[o?"selected":"checked"]=!0;this._data[o?"core":"checkbox"].selected=this._data[o?"core":"checkbox"].selected.concat(d[f[i]].children_d)}if(-1!==n.indexOf("up")){for(i=0,j=e.children_d.length;j>i;i++)d[e.children_d[i]].children.length||g.push(d[e.children_d[i]].parent);for(g=a.vakata.array_unique(g),k=0,l=g.length;l>k;k++){e=d[g[k]];while(e&&e.id!==a.jstree.root){for(h=0,i=0,j=e.children.length;j>i;i++)h+=d[e.children[i]].state[o?"selected":"checked"];if(h!==j)break;e.state[o?"selected":"checked"]=!0,this._data[o?"core":"checkbox"].selected.push(e.id),m=this.get_node(e,!0),m&&m.length&&m.attr("aria-selected",!0).children(".jstree-anchor").addClass(o?"jstree-clicked":"jstree-checked"),e=this.get_node(e.parent)}}}this._data[o?"core":"checkbox"].selected=a.vakata.array_unique(this._data[o?"core":"checkbox"].selected)},this)).on(this.settings.checkbox.tie_selection?"select_node.jstree":"check_node.jstree",a.proxy(function(b,c){var d=this,e=c.node,f=this._model.data,g=this.get_node(e.parent),h,i,j,k,l=this.settings.checkbox.cascade,m=this.settings.checkbox.tie_selection,n={},o=this._data[m?"core":"checkbox"].selected;for(h=0,i=o.length;i>h;h++)n[o[h]]=!0;if(-1!==l.indexOf("down")){var p=this._cascade_new_checked_state(e.id,!0),q=e.children_d.concat(e.id);for(h=0,i=q.length;i>h;h++)p.indexOf(q[h])>-1?n[q[h]]=!0:delete n[q[h]]}if(-1!==l.indexOf("up"))while(g&&g.id!==a.jstree.root){for(j=0,h=0,i=g.children.length;i>h;h++)j+=f[g.children[h]].state[m?"selected":"checked"];if(j!==i)break;g.state[m?"selected":"checked"]=!0,n[g.id]=!0,k=this.get_node(g,!0),k&&k.length&&k.attr("aria-selected",!0).children(".jstree-anchor").addClass(m?"jstree-clicked":"jstree-checked"),g=this.get_node(g.parent)}o=[];for(h in n)n.hasOwnProperty(h)&&o.push(h);this._data[m?"core":"checkbox"].selected=o},this)).on(this.settings.checkbox.tie_selection?"deselect_all.jstree":"uncheck_all.jstree",a.proxy(function(b,c){var d=this.get_node(a.jstree.root),e=this._model.data,f,g,h;for(f=0,g=d.children_d.length;g>f;f++)h=e[d.children_d[f]],h&&h.original&&h.original.state&&h.original.state.undetermined&&(h.original.state.undetermined=!1)},this)).on(this.settings.checkbox.tie_selection?"deselect_node.jstree":"uncheck_node.jstree",a.proxy(function(b,c){var d=this,e=c.node,f=this.get_node(e,!0),g,h,i,j=this.settings.checkbox.cascade,k=this.settings.checkbox.tie_selection,l=this._data[k?"core":"checkbox"].selected,m={},n=[],o=e.children_d.concat(e.id);if(-1!==j.indexOf("down")){var p=this._cascade_new_checked_state(e.id,!1);l=a.vakata.array_filter(l,function(a){return-1===o.indexOf(a)||p.indexOf(a)>-1})}if(-1!==j.indexOf("up")&&-1===l.indexOf(e.id)){for(g=0,h=e.parents.length;h>g;g++)i=this._model.data[e.parents[g]],i.state[k?"selected":"checked"]=!1,i&&i.original&&i.original.state&&i.original.state.undetermined&&(i.original.state.undetermined=!1),i=this.get_node(e.parents[g],!0),i&&i.length&&i.attr("aria-selected",!1).children(".jstree-anchor").removeClass(k?"jstree-clicked":"jstree-checked");l=a.vakata.array_filter(l,function(a){return-1===e.parents.indexOf(a)})}this._data[k?"core":"checkbox"].selected=l},this)),-1!==this.settings.checkbox.cascade.indexOf("up")&&this.element.on("delete_node.jstree",a.proxy(function(b,c){var d=this.get_node(c.parent),e=this._model.data,f,g,h,i,j=this.settings.checkbox.tie_selection;while(d&&d.id!==a.jstree.root&&!d.state[j?"selected":"checked"]){for(h=0,f=0,g=d.children.length;g>f;f++)h+=e[d.children[f]].state[j?"selected":"checked"];if(!(g>0&&h===g))break;d.state[j?"selected":"checked"]=!0,this._data[j?"core":"checkbox"].selected.push(d.id),i=this.get_node(d,!0),i&&i.length&&i.attr("aria-selected",!0).children(".jstree-anchor").addClass(j?"jstree-clicked":"jstree-checked"),d=this.get_node(d.parent)}},this)).on("move_node.jstree",a.proxy(function(b,c){var d=c.is_multi,e=c.old_parent,f=this.get_node(c.parent),g=this._model.data,h,i,j,k,l,m=this.settings.checkbox.tie_selection;if(!d){h=this.get_node(e);while(h&&h.id!==a.jstree.root&&!h.state[m?"selected":"checked"]){for(i=0,j=0,k=h.children.length;k>j;j++)i+=g[h.children[j]].state[m?"selected":"checked"];if(!(k>0&&i===k))break;h.state[m?"selected":"checked"]=!0,this._data[m?"core":"checkbox"].selected.push(h.id),l=this.get_node(h,!0),l&&l.length&&l.attr("aria-selected",!0).children(".jstree-anchor").addClass(m?"jstree-clicked":"jstree-checked"),h=this.get_node(h.parent)}}h=f;while(h&&h.id!==a.jstree.root){for(i=0,j=0,k=h.children.length;k>j;j++)i+=g[h.children[j]].state[m?"selected":"checked"];if(i===k)h.state[m?"selected":"checked"]||(h.state[m?"selected":"checked"]=!0,this._data[m?"core":"checkbox"].selected.push(h.id),l=this.get_node(h,!0),l&&l.length&&l.attr("aria-selected",!0).children(".jstree-anchor").addClass(m?"jstree-clicked":"jstree-checked"));else{if(!h.state[m?"selected":"checked"])break;h.state[m?"selected":"checked"]=!1,this._data[m?"core":"checkbox"].selected=a.vakata.array_remove_item(this._data[m?"core":"checkbox"].selected,h.id),l=this.get_node(h,!0),l&&l.length&&l.attr("aria-selected",!1).children(".jstree-anchor").removeClass(m?"jstree-clicked":"jstree-checked")}h=this.get_node(h.parent)}},this))},this.get_undetermined=function(c){if(-1===this.settings.checkbox.cascade.indexOf("undetermined"))return[];var d,e,f,g,h={},i=this._model.data,j=this.settings.checkbox.tie_selection,k=this._data[j?"core":"checkbox"].selected,l=[],m=this,n=[];for(d=0,e=k.length;e>d;d++)if(i[k[d]]&&i[k[d]].parents)for(f=0,g=i[k[d]].parents.length;g>f;f++){if(h[i[k[d]].parents[f]]!==b)break;i[k[d]].parents[f]!==a.jstree.root&&(h[i[k[d]].parents[f]]=!0,l.push(i[k[d]].parents[f]))}for(this.element.find(".jstree-closed").not(":has(.jstree-children)").each(function(){var c=m.get_node(this),j;if(c)if(c.state.loaded){for(d=0,e=c.children_d.length;e>d;d++)if(j=i[c.children_d[d]],!j.state.loaded&&j.original&&j.original.state&&j.original.state.undetermined&&j.original.state.undetermined===!0)for(h[j.id]===b&&j.id!==a.jstree.root&&(h[j.id]=!0,l.push(j.id)),f=0,g=j.parents.length;g>f;f++)h[j.parents[f]]===b&&j.parents[f]!==a.jstree.root&&(h[j.parents[f]]=!0,l.push(j.parents[f]))}else if(c.original&&c.original.state&&c.original.state.undetermined&&c.original.state.undetermined===!0)for(h[c.id]===b&&c.id!==a.jstree.root&&(h[c.id]=!0,l.push(c.id)),f=0,g=c.parents.length;g>f;f++)h[c.parents[f]]===b&&c.parents[f]!==a.jstree.root&&(h[c.parents[f]]=!0,l.push(c.parents[f]))}),d=0,e=l.length;e>d;d++)i[l[d]].state[j?"selected":"checked"]||n.push(c?i[l[d]]:l[d]);return n},this._undetermined=function(){if(null!==this.element){var a=this.get_undetermined(!1),b,c,d;for(this.element.find(".jstree-undetermined").removeClass("jstree-undetermined"),b=0,c=a.length;c>b;b++)d=this.get_node(a[b],!0),d&&d.length&&d.children(".jstree-anchor").children(".jstree-checkbox").addClass("jstree-undetermined")}},this.redraw_node=function(b,c,e,f){if(b=d.redraw_node.apply(this,arguments)){var g,h,i=null,k=null;for(g=0,h=b.childNodes.length;h>g;g++)if(b.childNodes[g]&&b.childNodes[g].className&&-1!==b.childNodes[g].className.indexOf("jstree-anchor")){i=b.childNodes[g];break}i&&(!this.settings.checkbox.tie_selection&&this._model.data[b.id].state.checked&&(i.className+=" jstree-checked"),k=j.cloneNode(!1),this._model.data[b.id].state.checkbox_disabled&&(k.className+=" jstree-checkbox-disabled"),i.insertBefore(k,i.childNodes[0]))}return e||-1===this.settings.checkbox.cascade.indexOf("undetermined")||(this._data.checkbox.uto&&clearTimeout(this._data.checkbox.uto),this._data.checkbox.uto=setTimeout(a.proxy(this._undetermined,this),50)),b},this.show_checkboxes=function(){this._data.core.themes.checkboxes=!0,this.get_container_ul().removeClass("jstree-no-checkboxes")},this.hide_checkboxes=function(){this._data.core.themes.checkboxes=!1,this.get_container_ul().addClass("jstree-no-checkboxes")},this.toggle_checkboxes=function(){this._data.core.themes.checkboxes?this.hide_checkboxes():this.show_checkboxes()},this.is_undetermined=function(b){b=this.get_node(b);var c=this.settings.checkbox.cascade,d,e,f=this.settings.checkbox.tie_selection,g=this._data[f?"core":"checkbox"].selected,h=this._model.data;if(!b||b.state[f?"selected":"checked"]===!0||-1===c.indexOf("undetermined")||-1===c.indexOf("down")&&-1===c.indexOf("up"))return!1;if(!b.state.loaded&&b.original.state.undetermined===!0)return!0;for(d=0,e=b.children_d.length;e>d;d++)if(-1!==a.inArray(b.children_d[d],g)||!h[b.children_d[d]].state.loaded&&h[b.children_d[d]].original.state.undetermined)return!0;return!1},this.disable_checkbox=function(b){var c,d,e;if(a.isArray(b)){for(b=b.slice(),c=0,d=b.length;d>c;c++)this.disable_checkbox(b[c]);return!0}return b=this.get_node(b),b&&b.id!==a.jstree.root?(e=this.get_node(b,!0),void(b.state.checkbox_disabled||(b.state.checkbox_disabled=!0,e&&e.length&&e.children(".jstree-anchor").children(".jstree-checkbox").addClass("jstree-checkbox-disabled"),this.trigger("disable_checkbox",{node:b})))):!1},this.enable_checkbox=function(b){var c,d,e;if(a.isArray(b)){for(b=b.slice(),c=0,d=b.length;d>c;c++)this.enable_checkbox(b[c]);return!0}return b=this.get_node(b),b&&b.id!==a.jstree.root?(e=this.get_node(b,!0),void(b.state.checkbox_disabled&&(b.state.checkbox_disabled=!1,e&&e.length&&e.children(".jstree-anchor").children(".jstree-checkbox").removeClass("jstree-checkbox-disabled"),this.trigger("enable_checkbox",{node:b})))):!1},this.activate_node=function(b,c){return a(c.target).hasClass("jstree-checkbox-disabled")?!1:(this.settings.checkbox.tie_selection&&(this.settings.checkbox.whole_node||a(c.target).hasClass("jstree-checkbox"))&&(c.ctrlKey=!0),this.settings.checkbox.tie_selection||!this.settings.checkbox.whole_node&&!a(c.target).hasClass("jstree-checkbox")?d.activate_node.call(this,b,c):this.is_disabled(b)?!1:(this.is_checked(b)?this.uncheck_node(b,c):this.check_node(b,c),void this.trigger("activate_node",{node:this.get_node(b)})))},this._cascade_new_checked_state=function(a,b){var c=this,d=this.settings.checkbox.tie_selection,e=this._model.data[a],f=[],g=[],h,i,j;if(!this.settings.checkbox.cascade_to_disabled&&e.state.disabled||!this.settings.checkbox.cascade_to_hidden&&e.state.hidden)j=this.get_checked_descendants(a),e.state[d?"selected":"checked"]&&j.push(e.id),f=f.concat(j);else{if(e.children)for(h=0,i=e.children.length;i>h;h++){var k=e.children[h];j=c._cascade_new_checked_state(k,b),f=f.concat(j),j.indexOf(k)>-1&&g.push(k)}var l=c.get_node(e,!0),m=g.length>0&&g.lengthe;e++)this.check_node(b[e],c);return!0}return b=this.get_node(b),b&&b.id!==a.jstree.root?(d=this.get_node(b,!0),void(b.state.checked||(b.state.checked=!0,this._data.checkbox.selected.push(b.id),d&&d.length&&d.children(".jstree-anchor").addClass("jstree-checked"),this.trigger("check_node",{node:b,selected:this._data.checkbox.selected,event:c})))):!1},this.uncheck_node=function(b,c){if(this.settings.checkbox.tie_selection)return this.deselect_node(b,!1,c);var d,e,f;if(a.isArray(b)){for(b=b.slice(),d=0,e=b.length;e>d;d++)this.uncheck_node(b[d],c);return!0}return b=this.get_node(b),b&&b.id!==a.jstree.root?(f=this.get_node(b,!0),void(b.state.checked&&(b.state.checked=!1,this._data.checkbox.selected=a.vakata.array_remove_item(this._data.checkbox.selected,b.id),f.length&&f.children(".jstree-anchor").removeClass("jstree-checked"),this.trigger("uncheck_node",{node:b,selected:this._data.checkbox.selected,event:c})))):!1},this.check_all=function(){if(this.settings.checkbox.tie_selection)return this.select_all();var b=this._data.checkbox.selected.concat([]),c,d;for(this._data.checkbox.selected=this._model.data[a.jstree.root].children_d.concat(),c=0,d=this._data.checkbox.selected.length;d>c;c++)this._model.data[this._data.checkbox.selected[c]]&&(this._model.data[this._data.checkbox.selected[c]].state.checked=!0);this.redraw(!0),this.trigger("check_all",{selected:this._data.checkbox.selected})},this.uncheck_all=function(){if(this.settings.checkbox.tie_selection)return this.deselect_all();var a=this._data.checkbox.selected.concat([]),b,c;for(b=0,c=this._data.checkbox.selected.length;c>b;b++)this._model.data[this._data.checkbox.selected[b]]&&(this._model.data[this._data.checkbox.selected[b]].state.checked=!1);this._data.checkbox.selected=[],this.element.find(".jstree-checked").removeClass("jstree-checked"),this.trigger("uncheck_all",{selected:this._data.checkbox.selected,node:a})},this.is_checked=function(b){return this.settings.checkbox.tie_selection?this.is_selected(b):(b=this.get_node(b),b&&b.id!==a.jstree.root?b.state.checked:!1)},this.get_checked=function(b){return this.settings.checkbox.tie_selection?this.get_selected(b):b?a.map(this._data.checkbox.selected,a.proxy(function(a){return this.get_node(a)},this)):this._data.checkbox.selected.slice()},this.get_top_checked=function(b){if(this.settings.checkbox.tie_selection)return this.get_top_selected(b);var c=this.get_checked(!0),d={},e,f,g,h;for(e=0,f=c.length;f>e;e++)d[c[e].id]=c[e];for(e=0,f=c.length;f>e;e++)for(g=0,h=c[e].children_d.length;h>g;g++)d[c[e].children_d[g]]&&delete d[c[e].children_d[g]];c=[];for(e in d)d.hasOwnProperty(e)&&c.push(e);return b?a.map(c,a.proxy(function(a){return this.get_node(a)},this)):c},this.get_bottom_checked=function(b){if(this.settings.checkbox.tie_selection)return this.get_bottom_selected(b);var c=this.get_checked(!0),d=[],e,f;for(e=0,f=c.length;f>e;e++)c[e].children.length||d.push(c[e].id);return b?a.map(d,a.proxy(function(a){return this.get_node(a)},this)):d},this.load_node=function(b,c){var e,f,g,h,i,j;if(!a.isArray(b)&&!this.settings.checkbox.tie_selection&&(j=this.get_node(b),j&&j.state.loaded))for(e=0,f=j.children_d.length;f>e;e++)this._model.data[j.children_d[e]].state.checked&&(i=!0,this._data.checkbox.selected=a.vakata.array_remove_item(this._data.checkbox.selected,j.children_d[e]));return d.load_node.apply(this,arguments)},this.get_state=function(){var a=d.get_state.apply(this,arguments);return this.settings.checkbox.tie_selection?a:(a.checkbox=this._data.checkbox.selected.slice(),a)},this.set_state=function(b,c){var e=d.set_state.apply(this,arguments);if(e&&b.checkbox){if(!this.settings.checkbox.tie_selection){this.uncheck_all();var f=this;a.each(b.checkbox,function(a,b){f.check_node(b)})}return delete b.checkbox,this.set_state(b,c),!1}return e},this.refresh=function(a,b){return this.settings.checkbox.tie_selection&&(this._data.checkbox.selected=[]),d.refresh.apply(this,arguments)}},a.jstree.defaults.conditionalselect=function(){return!0},a.jstree.plugins.conditionalselect=function(a,b){this.activate_node=function(a,c){return this.settings.conditionalselect.call(this,this.get_node(a),c)?b.activate_node.call(this,a,c):void 0}},a.jstree.defaults.contextmenu={select_node:!0,show_at_node:!0,items:function(b,c){return{create:{separator_before:!1,separator_after:!0,_disabled:!1,label:"Create",action:function(b){var c=a.jstree.reference(b.reference),d=c.get_node(b.reference);c.create_node(d,{},"last",function(a){try{c.edit(a)}catch(b){setTimeout(function(){c.edit(a)},0)}})}},rename:{separator_before:!1,separator_after:!1,_disabled:!1,label:"Rename",action:function(b){var c=a.jstree.reference(b.reference),d=c.get_node(b.reference);c.edit(d)}},remove:{separator_before:!1,icon:!1,separator_after:!1,_disabled:!1,label:"Delete",action:function(b){var c=a.jstree.reference(b.reference),d=c.get_node(b.reference);c.is_selected(d)?c.delete_node(c.get_selected()):c.delete_node(d)}},ccp:{separator_before:!0,icon:!1,separator_after:!1,label:"Edit",action:!1,submenu:{cut:{separator_before:!1,separator_after:!1,label:"Cut",action:function(b){var c=a.jstree.reference(b.reference),d=c.get_node(b.reference);c.is_selected(d)?c.cut(c.get_top_selected()):c.cut(d)}},copy:{separator_before:!1,icon:!1,separator_after:!1,label:"Copy",action:function(b){var c=a.jstree.reference(b.reference),d=c.get_node(b.reference);c.is_selected(d)?c.copy(c.get_top_selected()):c.copy(d)}},paste:{separator_before:!1,icon:!1,_disabled:function(b){return!a.jstree.reference(b.reference).can_paste()},separator_after:!1,label:"Paste",action:function(b){var c=a.jstree.reference(b.reference),d=c.get_node(b.reference);c.paste(d)}}}}}}},a.jstree.plugins.contextmenu=function(c,d){this.bind=function(){d.bind.call(this);var b=0,c=null,e,f;this.element.on("init.jstree loading.jstree ready.jstree",a.proxy(function(){this.get_container_ul().addClass("jstree-contextmenu")},this)).on("contextmenu.jstree",".jstree-anchor",a.proxy(function(a,d){"input"!==a.target.tagName.toLowerCase()&&(a.preventDefault(),b=a.ctrlKey?+new Date:0,(d||c)&&(b=+new Date+1e4),c&&clearTimeout(c),this.is_loading(a.currentTarget)||this.show_contextmenu(a.currentTarget,a.pageX,a.pageY,a))},this)).on("click.jstree",".jstree-anchor",a.proxy(function(c){this._data.contextmenu.visible&&(!b||+new Date-b>250)&&a.vakata.context.hide(),b=0},this)).on("touchstart.jstree",".jstree-anchor",function(b){b.originalEvent&&b.originalEvent.changedTouches&&b.originalEvent.changedTouches[0]&&(e=b.originalEvent.changedTouches[0].clientX,f=b.originalEvent.changedTouches[0].clientY,c=setTimeout(function(){a(b.currentTarget).trigger("contextmenu",!0)},750))}).on("touchmove.vakata.jstree",function(b){c&&b.originalEvent&&b.originalEvent.changedTouches&&b.originalEvent.changedTouches[0]&&(Math.abs(e-b.originalEvent.changedTouches[0].clientX)>10||Math.abs(f-b.originalEvent.changedTouches[0].clientY)>10)&&(clearTimeout(c),a.vakata.context.hide())}).on("touchend.vakata.jstree",function(a){c&&clearTimeout(c)}),a(i).on("context_hide.vakata.jstree",a.proxy(function(b,c){this._data.contextmenu.visible=!1,a(c.reference).removeClass("jstree-context")},this))},this.teardown=function(){this._data.contextmenu.visible&&a.vakata.context.hide(),d.teardown.call(this)},this.show_contextmenu=function(c,d,e,f){if(c=this.get_node(c),!c||c.id===a.jstree.root)return!1;var g=this.settings.contextmenu,h=this.get_node(c,!0),i=h.children(".jstree-anchor"),j=!1,k=!1;(g.show_at_node||d===b||e===b)&&(j=i.offset(),d=j.left,e=j.top+this._data.core.li_height),this.settings.contextmenu.select_node&&!this.is_selected(c)&&this.activate_node(c,f),k=g.items,a.isFunction(k)&&(k=k.call(this,c,a.proxy(function(a){this._show_contextmenu(c,d,e,a)},this))),a.isPlainObject(k)&&this._show_contextmenu(c,d,e,k)},this._show_contextmenu=function(b,c,d,e){var f=this.get_node(b,!0),g=f.children(".jstree-anchor");a(i).one("context_show.vakata.jstree",a.proxy(function(b,c){var d="jstree-contextmenu jstree-"+this.get_theme()+"-contextmenu";a(c.element).addClass(d),g.addClass("jstree-context")},this)),this._data.contextmenu.visible=!0,a.vakata.context.show(g,{x:c,y:d},e),this.trigger("show_contextmenu",{node:b,x:c,y:d})}},function(a){var b=!1,c={element:!1,reference:!1,position_x:0,position_y:0,items:[],html:"",is_visible:!1};a.vakata.context={settings:{hide_onmouseleave:0,icons:!0},_trigger:function(b){a(i).triggerHandler("context_"+b+".vakata",{reference:c.reference,element:c.element,position:{x:c.position_x,y:c.position_y}})},_execute:function(b){return b=c.items[b],b&&(!b._disabled||a.isFunction(b._disabled)&&!b._disabled({item:b,reference:c.reference,element:c.element}))&&b.action?b.action.call(null,{item:b,reference:c.reference,element:c.element,position:{x:c.position_x,y:c.position_y}}):!1},_parse:function(b,d){if(!b)return!1;d||(c.html="",c.items=[]);var e="",f=!1,g;return d&&(e+=""),d||(c.html=e,a.vakata.context._trigger("parse")),e.length>10?e:!1},_show_submenu:function(c){if(c=a(c),c.length&&c.children("ul").length){var d=c.children("ul"),e=c.offset().left,f=e+c.outerWidth(),g=c.offset().top,h=d.width(),i=d.height(),j=a(window).width()+a(window).scrollLeft(),k=a(window).height()+a(window).scrollTop();b?c[f-(h+10+c.outerWidth())<0?"addClass":"removeClass"]("vakata-context-left"):c[f+h>j&&e>j-f?"addClass":"removeClass"]("vakata-context-right"),g+i+10>k&&d.css("bottom","-1px"),c.hasClass("vakata-context-right")?h>e&&d.css("margin-right",e-h):h>j-f&&d.css("margin-left",j-f-h),d.show()}},show:function(d,e,f){var g,h,j,k,l,m,n,o,p=!0;switch(c.element&&c.element.length&&c.element.width(""),p){case!e&&!d:return!1;case!!e&&!!d:c.reference=d,c.position_x=e.x,c.position_y=e.y;break;case!e&&!!d:c.reference=d,g=d.offset(),c.position_x=g.left+d.outerHeight(),c.position_y=g.top;break;case!!e&&!d:c.position_x=e.x,c.position_y=e.y}d&&!f&&a(d).data("vakata_contextmenu")&&(f=a(d).data("vakata_contextmenu")),a.vakata.context._parse(f)&&c.element.html(c.html),c.items.length&&(c.element.appendTo(i.body),h=c.element,j=c.position_x,k=c.position_y,l=h.width(),m=h.height(),n=a(window).width()+a(window).scrollLeft(),o=a(window).height()+a(window).scrollTop(),b&&(j-=h.outerWidth()-a(d).outerWidth(),jn&&(j=n-(l+20)),k+m+20>o&&(k=o-(m+20)),c.element.css({left:j,top:k}).show().find("a").first().focus().parent().addClass("vakata-context-hover"),c.is_visible=!0,a.vakata.context._trigger("show"))},hide:function(){c.is_visible&&(c.element.hide().find("ul").hide().end().find(":focus").blur().end().detach(),c.is_visible=!1,a.vakata.context._trigger("hide"))}},a(function(){b="rtl"===a(i.body).css("direction");var d=!1;c.element=a("
    "),c.element.on("mouseenter","li",function(b){b.stopImmediatePropagation(),a.contains(this,b.relatedTarget)||(d&&clearTimeout(d),c.element.find(".vakata-context-hover").removeClass("vakata-context-hover").end(),a(this).siblings().find("ul").hide().end().end().parentsUntil(".vakata-context","li").addBack().addClass("vakata-context-hover"),a.vakata.context._show_submenu(this))}).on("mouseleave","li",function(b){a.contains(this,b.relatedTarget)||a(this).find(".vakata-context-hover").addBack().removeClass("vakata-context-hover")}).on("mouseleave",function(b){a(this).find(".vakata-context-hover").removeClass("vakata-context-hover"),a.vakata.context.settings.hide_onmouseleave&&(d=setTimeout(function(b){return function(){a.vakata.context.hide()}}(this),a.vakata.context.settings.hide_onmouseleave))}).on("click","a",function(b){b.preventDefault(),a(this).blur().parent().hasClass("vakata-context-disabled")||a.vakata.context._execute(a(this).attr("rel"))===!1||a.vakata.context.hide()}).on("keydown","a",function(b){var d=null;switch(b.which){case 13:case 32:b.type="click",b.preventDefault(),a(b.currentTarget).trigger(b);break;case 37:c.is_visible&&(c.element.find(".vakata-context-hover").last().closest("li").first().find("ul").hide().find(".vakata-context-hover").removeClass("vakata-context-hover").end().end().children("a").focus(),b.stopImmediatePropagation(),b.preventDefault());break;case 38:c.is_visible&&(d=c.element.find("ul:visible").addBack().last().children(".vakata-context-hover").removeClass("vakata-context-hover").prevAll("li:not(.vakata-context-separator)").first(),d.length||(d=c.element.find("ul:visible").addBack().last().children("li:not(.vakata-context-separator)").last()),d.addClass("vakata-context-hover").children("a").focus(),b.stopImmediatePropagation(),b.preventDefault());break;case 39:c.is_visible&&(c.element.find(".vakata-context-hover").last().children("ul").show().children("li:not(.vakata-context-separator)").removeClass("vakata-context-hover").first().addClass("vakata-context-hover").children("a").focus(),b.stopImmediatePropagation(),b.preventDefault());break;case 40:c.is_visible&&(d=c.element.find("ul:visible").addBack().last().children(".vakata-context-hover").removeClass("vakata-context-hover").nextAll("li:not(.vakata-context-separator)").first(),d.length||(d=c.element.find("ul:visible").addBack().last().children("li:not(.vakata-context-separator)").first()),d.addClass("vakata-context-hover").children("a").focus(),b.stopImmediatePropagation(),b.preventDefault());break;case 27:a.vakata.context.hide(),b.preventDefault()}}).on("keydown",function(a){a.preventDefault();var b=c.element.find(".vakata-contextmenu-shortcut-"+a.which).parent();b.parent().not(".vakata-context-disabled")&&b.click()}),a(i).on("mousedown.vakata.jstree",function(b){c.is_visible&&c.element[0]!==b.target&&!a.contains(c.element[0],b.target)&&a.vakata.context.hide()}).on("context_show.vakata.jstree",function(a,d){c.element.find("li:has(ul)").children("a").addClass("vakata-context-parent"),b&&c.element.addClass("vakata-context-rtl").css("direction","rtl"),c.element.find("ul").hide().end()})})}(a),a.jstree.defaults.dnd={copy:!0,open_timeout:500,is_draggable:!0,check_while_dragging:!0,always_copy:!1,inside_pos:0,drag_selection:!0,touch:!0,large_drop_target:!1,large_drag_target:!1,use_html5:!1};var k,l;a.jstree.plugins.dnd=function(b,c){this.init=function(a,b){c.init.call(this,a,b),this.settings.dnd.use_html5=this.settings.dnd.use_html5&&"draggable"in i.createElement("span")},this.bind=function(){c.bind.call(this),this.element.on(this.settings.dnd.use_html5?"dragstart.jstree":"mousedown.jstree touchstart.jstree",this.settings.dnd.large_drag_target?".jstree-node":".jstree-anchor",a.proxy(function(b){if(this.settings.dnd.large_drag_target&&a(b.target).closest(".jstree-node")[0]!==b.currentTarget)return!0;if("touchstart"===b.type&&(!this.settings.dnd.touch||"selected"===this.settings.dnd.touch&&!a(b.currentTarget).closest(".jstree-node").children(".jstree-anchor").hasClass("jstree-clicked")))return!0;var c=this.get_node(b.target),d=this.is_selected(c)&&this.settings.dnd.drag_selection?this.get_top_selected().length:1,e=d>1?d+" "+this.get_string("nodes"):this.get_text(b.currentTarget);if(this.settings.core.force_text&&(e=a.vakata.html.escape(e)),c&&c.id&&c.id!==a.jstree.root&&(1===b.which||"touchstart"===b.type||"dragstart"===b.type)&&(this.settings.dnd.is_draggable===!0||a.isFunction(this.settings.dnd.is_draggable)&&this.settings.dnd.is_draggable.call(this,d>1?this.get_top_selected(!0):[c],b))){if(k={jstree:!0,origin:this,obj:this.get_node(c,!0),nodes:d>1?this.get_top_selected():[c.id]},l=b.currentTarget,!this.settings.dnd.use_html5)return this.element.trigger("mousedown.jstree"),a.vakata.dnd.start(b,k,'
    '+e+'
    ');a.vakata.dnd._trigger("start",b,{helper:a(),element:l,data:k})}},this)),this.settings.dnd.use_html5&&this.element.on("dragover.jstree",function(b){return b.preventDefault(),a.vakata.dnd._trigger("move",b,{helper:a(),element:l,data:k}),!1}).on("drop.jstree",a.proxy(function(b){return b.preventDefault(),a.vakata.dnd._trigger("stop",b,{helper:a(),element:l,data:k}),!1},this))},this.redraw_node=function(a,b,d,e){if(a=c.redraw_node.apply(this,arguments),a&&this.settings.dnd.use_html5)if(this.settings.dnd.large_drag_target)a.setAttribute("draggable",!0);else{var f,g,h=null;for(f=0,g=a.childNodes.length;g>f;f++)if(a.childNodes[f]&&a.childNodes[f].className&&-1!==a.childNodes[f].className.indexOf("jstree-anchor")){h=a.childNodes[f];break}h&&h.setAttribute("draggable",!0)}return a}},a(function(){var c=!1,d=!1,e=!1,f=!1,g=a('
     
    ').hide();a(i).on("dragover.vakata.jstree",function(b){l&&a.vakata.dnd._trigger("move",b,{helper:a(),element:l,data:k})}).on("drop.vakata.jstree",function(b){l&&(a.vakata.dnd._trigger("stop",b,{helper:a(),element:l,data:k}),l=null,k=null)}).on("dnd_start.vakata.jstree",function(a,b){c=!1,e=!1,b&&b.data&&b.data.jstree&&g.appendTo(i.body)}).on("dnd_move.vakata.jstree",function(h,i){var j=i.event.target!==e.target;if(f&&(!i.event||"dragover"!==i.event.type||j)&&clearTimeout(f),i&&i.data&&i.data.jstree&&(!i.event.target.id||"jstree-marker"!==i.event.target.id)){e=i.event;var k=a.jstree.reference(i.event.target),l=!1,m=!1,n=!1,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E;if(k&&k._data&&k._data.dnd)if(g.attr("class","jstree-"+k.get_theme()+(k.settings.core.themes.responsive?" jstree-dnd-responsive":"")),D=i.data.origin&&(i.data.origin.settings.dnd.always_copy||i.data.origin.settings.dnd.copy&&(i.event.metaKey||i.event.ctrlKey)),i.helper.children().attr("class","jstree-"+k.get_theme()+" jstree-"+k.get_theme()+"-"+k.get_theme_variant()+" "+(k.settings.core.themes.responsive?" jstree-dnd-responsive":"")).find(".jstree-copy").first()[D?"show":"hide"](),i.event.target!==k.element[0]&&i.event.target!==k.get_container_ul()[0]||0!==k.get_container_ul().children().length){if(l=k.settings.dnd.large_drop_target?a(i.event.target).closest(".jstree-node").children(".jstree-anchor"):a(i.event.target).closest(".jstree-anchor"),l&&l.length&&l.parent().is(".jstree-closed, .jstree-open, .jstree-leaf")&&(m=l.offset(),n=(i.event.pageY!==b?i.event.pageY:i.event.originalEvent.pageY)-m.top,r=l.outerHeight(),u=r/3>n?["b","i","a"]:n>r-r/3?["a","i","b"]:n>r/2?["i","a","b"]:["i","b","a"],a.each(u,function(b,e){switch(e){case"b":p=m.left-6,q=m.top,s=k.get_parent(l),t=l.parent().index();break;case"i":B=k.settings.dnd.inside_pos,C=k.get_node(l.parent()),p=m.left-2,q=m.top+r/2+1,s=C.id,t="first"===B?0:"last"===B?C.children.length:Math.min(B,C.children.length);break;case"a":p=m.left-6,q=m.top+r,s=k.get_parent(l),t=l.parent().index()+1}for(v=!0,w=0,x=i.data.nodes.length;x>w;w++)if(y=i.data.origin&&(i.data.origin.settings.dnd.always_copy||i.data.origin.settings.dnd.copy&&(i.event.metaKey||i.event.ctrlKey))?"copy_node":"move_node",z=t,"move_node"===y&&"a"===e&&i.data.origin&&i.data.origin===k&&s===k.get_parent(i.data.nodes[w])&&(A=k.get_node(s),z>a.inArray(i.data.nodes[w],A.children)&&(z-=1)),v=v&&(k&&k.settings&&k.settings.dnd&&k.settings.dnd.check_while_dragging===!1||k.check(y,i.data.origin&&i.data.origin!==k?i.data.origin.get_node(i.data.nodes[w]):i.data.nodes[w],s,z,{dnd:!0,ref:k.get_node(l.parent()),pos:e,origin:i.data.origin,is_multi:i.data.origin&&i.data.origin!==k,is_foreign:!i.data.origin})),!v){k&&k.last_error&&(d=k.last_error());break}return"i"===e&&l.parent().is(".jstree-closed")&&k.settings.dnd.open_timeout&&(!i.event||"dragover"!==i.event.type||j)&&(f&&clearTimeout(f),f=setTimeout(function(a,b){return function(){a.open_node(b)}}(k,l),k.settings.dnd.open_timeout)),v?(E=k.get_node(s,!0),E.hasClass(".jstree-dnd-parent")||(a(".jstree-dnd-parent").removeClass("jstree-dnd-parent"),E.addClass("jstree-dnd-parent")),c={ins:k,par:s,pos:"i"!==e||"last"!==B||0!==t||k.is_loaded(C)?t:"last"},g.css({left:p+"px",top:q+"px"}).show(),i.helper.find(".jstree-icon").first().removeClass("jstree-er").addClass("jstree-ok"),i.event.originalEvent&&i.event.originalEvent.dataTransfer&&(i.event.originalEvent.dataTransfer.dropEffect=D?"copy":"move"),d={},u=!0,!1):void 0}),u===!0))return}else{for(v=!0,w=0,x=i.data.nodes.length;x>w;w++)if(v=v&&k.check(i.data.origin&&(i.data.origin.settings.dnd.always_copy||i.data.origin.settings.dnd.copy&&(i.event.metaKey||i.event.ctrlKey))?"copy_node":"move_node",i.data.origin&&i.data.origin!==k?i.data.origin.get_node(i.data.nodes[w]):i.data.nodes[w],a.jstree.root,"last",{dnd:!0,ref:k.get_node(a.jstree.root),pos:"i",origin:i.data.origin,is_multi:i.data.origin&&i.data.origin!==k,is_foreign:!i.data.origin}),!v)break;if(v)return c={ins:k,par:a.jstree.root,pos:"last"},g.hide(),i.helper.find(".jstree-icon").first().removeClass("jstree-er").addClass("jstree-ok"),void(i.event.originalEvent&&i.event.originalEvent.dataTransfer&&(i.event.originalEvent.dataTransfer.dropEffect=D?"copy":"move"))}a(".jstree-dnd-parent").removeClass("jstree-dnd-parent"),c=!1,i.helper.find(".jstree-icon").removeClass("jstree-ok").addClass("jstree-er"),i.event.originalEvent&&i.event.originalEvent.dataTransfer,g.hide()}}).on("dnd_scroll.vakata.jstree",function(a,b){b&&b.data&&b.data.jstree&&(g.hide(),c=!1,e=!1,b.helper.find(".jstree-icon").first().removeClass("jstree-ok").addClass("jstree-er"))}).on("dnd_stop.vakata.jstree",function(b,h){if(a(".jstree-dnd-parent").removeClass("jstree-dnd-parent"),f&&clearTimeout(f),h&&h.data&&h.data.jstree){g.hide().detach();var i,j,k=[];if(c){for(i=0,j=h.data.nodes.length;j>i;i++)k[i]=h.data.origin?h.data.origin.get_node(h.data.nodes[i]):h.data.nodes[i];c.ins[h.data.origin&&(h.data.origin.settings.dnd.always_copy||h.data.origin.settings.dnd.copy&&(h.event.metaKey||h.event.ctrlKey))?"copy_node":"move_node"](k,c.par,c.pos,!1,!1,!1,h.data.origin)}else i=a(h.event.target).closest(".jstree"),i.length&&d&&d.error&&"check"===d.error&&(i=i.jstree(!0),i&&i.settings.core.error.call(this,d));e=!1,c=!1}}).on("keyup.jstree keydown.jstree",function(b,h){h=a.vakata.dnd._get(),h&&h.data&&h.data.jstree&&("keyup"===b.type&&27===b.which?(f&&clearTimeout(f),c=!1,d=!1,e=!1,f=!1,g.hide().detach(),a.vakata.dnd._clean()):(h.helper.find(".jstree-copy").first()[h.data.origin&&(h.data.origin.settings.dnd.always_copy||h.data.origin.settings.dnd.copy&&(b.metaKey||b.ctrlKey))?"show":"hide"](),e&&(e.metaKey=b.metaKey,e.ctrlKey=b.ctrlKey,a.vakata.dnd._trigger("move",e))))})}),function(a){a.vakata.html={div:a("
    "),escape:function(b){return a.vakata.html.div.text(b).html()},strip:function(b){return a.vakata.html.div.empty().append(a.parseHTML(b)).text()}};var c={element:!1,target:!1,is_down:!1,is_drag:!1,helper:!1,helper_w:0,data:!1,init_x:0,init_y:0,scroll_l:0,scroll_t:0,scroll_e:!1,scroll_i:!1,is_touch:!1};a.vakata.dnd={settings:{scroll_speed:10,scroll_proximity:20,helper_left:5,helper_top:10,threshold:5,threshold_touch:10},_trigger:function(c,d,e){e===b&&(e=a.vakata.dnd._get()),e.event=d,a(i).triggerHandler("dnd_"+c+".vakata",e)},_get:function(){return{data:c.data,element:c.element,helper:c.helper}},_clean:function(){c.helper&&c.helper.remove(),c.scroll_i&&(clearInterval(c.scroll_i),c.scroll_i=!1),c={element:!1,target:!1,is_down:!1,is_drag:!1,helper:!1,helper_w:0,data:!1,init_x:0,init_y:0,scroll_l:0,scroll_t:0,scroll_e:!1,scroll_i:!1,is_touch:!1},a(i).off("mousemove.vakata.jstree touchmove.vakata.jstree",a.vakata.dnd.drag),a(i).off("mouseup.vakata.jstree touchend.vakata.jstree",a.vakata.dnd.stop)},_scroll:function(b){if(!c.scroll_e||!c.scroll_l&&!c.scroll_t)return c.scroll_i&&(clearInterval(c.scroll_i),c.scroll_i=!1),!1;if(!c.scroll_i)return c.scroll_i=setInterval(a.vakata.dnd._scroll,100),!1;if(b===!0)return!1;var d=c.scroll_e.scrollTop(),e=c.scroll_e.scrollLeft();c.scroll_e.scrollTop(d+c.scroll_t*a.vakata.dnd.settings.scroll_speed),c.scroll_e.scrollLeft(e+c.scroll_l*a.vakata.dnd.settings.scroll_speed),(d!==c.scroll_e.scrollTop()||e!==c.scroll_e.scrollLeft())&&a.vakata.dnd._trigger("scroll",c.scroll_e)},start:function(b,d,e){"touchstart"===b.type&&b.originalEvent&&b.originalEvent.changedTouches&&b.originalEvent.changedTouches[0]&&(b.pageX=b.originalEvent.changedTouches[0].pageX,b.pageY=b.originalEvent.changedTouches[0].pageY,b.target=i.elementFromPoint(b.originalEvent.changedTouches[0].pageX-window.pageXOffset,b.originalEvent.changedTouches[0].pageY-window.pageYOffset)),c.is_drag&&a.vakata.dnd.stop({});try{b.currentTarget.unselectable="on",b.currentTarget.onselectstart=function(){return!1},b.currentTarget.style&&(b.currentTarget.style.touchAction="none",b.currentTarget.style.msTouchAction="none",b.currentTarget.style.MozUserSelect="none")}catch(f){}return c.init_x=b.pageX,c.init_y=b.pageY,c.data=d,c.is_down=!0,c.element=b.currentTarget,c.target=b.target,c.is_touch="touchstart"===b.type,e!==!1&&(c.helper=a("
    ").html(e).css({display:"block",margin:"0",padding:"0",position:"absolute",top:"-2000px",lineHeight:"16px",zIndex:"10000"})),a(i).on("mousemove.vakata.jstree touchmove.vakata.jstree",a.vakata.dnd.drag),a(i).on("mouseup.vakata.jstree touchend.vakata.jstree",a.vakata.dnd.stop),!1},drag:function(b){if("touchmove"===b.type&&b.originalEvent&&b.originalEvent.changedTouches&&b.originalEvent.changedTouches[0]&&(b.pageX=b.originalEvent.changedTouches[0].pageX,b.pageY=b.originalEvent.changedTouches[0].pageY,b.target=i.elementFromPoint(b.originalEvent.changedTouches[0].pageX-window.pageXOffset,b.originalEvent.changedTouches[0].pageY-window.pageYOffset)),c.is_down){if(!c.is_drag){if(!(Math.abs(b.pageX-c.init_x)>(c.is_touch?a.vakata.dnd.settings.threshold_touch:a.vakata.dnd.settings.threshold)||Math.abs(b.pageY-c.init_y)>(c.is_touch?a.vakata.dnd.settings.threshold_touch:a.vakata.dnd.settings.threshold)))return;c.helper&&(c.helper.appendTo(i.body),c.helper_w=c.helper.outerWidth()),c.is_drag=!0,a(c.target).one("click.vakata",!1),a.vakata.dnd._trigger("start",b)}var d=!1,e=!1,f=!1,g=!1,h=!1,j=!1,k=!1,l=!1,m=!1,n=!1;return c.scroll_t=0,c.scroll_l=0,c.scroll_e=!1,a(a(b.target).parentsUntil("body").addBack().get().reverse()).filter(function(){return/^auto|scroll$/.test(a(this).css("overflow"))&&(this.scrollHeight>this.offsetHeight||this.scrollWidth>this.offsetWidth)}).each(function(){var d=a(this),e=d.offset();return this.scrollHeight>this.offsetHeight&&(e.top+d.height()-b.pageYthis.offsetWidth&&(e.left+d.width()-b.pageXg&&b.pageY-kg&&g-(b.pageY-k)j&&b.pageX-lj&&j-(b.pageX-l)f&&(m=f-50),h&&n+c.helper_w>h&&(n=h-(c.helper_w+2)),c.helper.css({left:n+"px",top:m+"px"})),a.vakata.dnd._trigger("move",b),!1}},stop:function(b){if("touchend"===b.type&&b.originalEvent&&b.originalEvent.changedTouches&&b.originalEvent.changedTouches[0]&&(b.pageX=b.originalEvent.changedTouches[0].pageX,b.pageY=b.originalEvent.changedTouches[0].pageY,b.target=i.elementFromPoint(b.originalEvent.changedTouches[0].pageX-window.pageXOffset,b.originalEvent.changedTouches[0].pageY-window.pageYOffset)),c.is_drag)b.target!==c.target&&a(c.target).off("click.vakata"),a.vakata.dnd._trigger("stop",b);else if("touchend"===b.type&&b.target===c.target){var d=setTimeout(function(){a(b.target).click()},100);a(b.target).one("click",function(){d&&clearTimeout(d)})}return a.vakata.dnd._clean(),!1}}}(a),a.jstree.defaults.massload=null,a.jstree.plugins.massload=function(b,c){this.init=function(a,b){this._data.massload={},c.init.call(this,a,b)},this._load_nodes=function(b,d,e,f){var g=this.settings.massload,h=JSON.stringify(b),i=[],j=this._model.data,k,l,m;if(!e){for(k=0,l=b.length;l>k;k++)(!j[b[k]]||!j[b[k]].state.loaded&&!j[b[k]].state.failed||f)&&(i.push(b[k]),m=this.get_node(b[k],!0),m&&m.length&&m.addClass("jstree-loading").attr("aria-busy",!0));if(this._data.massload={},i.length){if(a.isFunction(g))return g.call(this,i,a.proxy(function(a){var g,h;if(a)for(g in a)a.hasOwnProperty(g)&&(this._data.massload[g]=a[g]);for(g=0,h=b.length;h>g;g++)m=this.get_node(b[g],!0),m&&m.length&&m.removeClass("jstree-loading").attr("aria-busy",!1);c._load_nodes.call(this,b,d,e,f)},this));if("object"==typeof g&&g&&g.url)return g=a.extend(!0,{},g),a.isFunction(g.url)&&(g.url=g.url.call(this,i)),a.isFunction(g.data)&&(g.data=g.data.call(this,i)),a.ajax(g).done(a.proxy(function(a,g,h){var i,j;if(a)for(i in a)a.hasOwnProperty(i)&&(this._data.massload[i]=a[i]);for(i=0,j=b.length;j>i;i++)m=this.get_node(b[i],!0),m&&m.length&&m.removeClass("jstree-loading").attr("aria-busy",!1);c._load_nodes.call(this,b,d,e,f)},this)).fail(a.proxy(function(a){c._load_nodes.call(this,b,d,e,f)},this))}}return c._load_nodes.call(this,b,d,e,f)},this._load_node=function(b,d){var e=this._data.massload[b.id],f=null,g;return e?(f=this["string"==typeof e?"_append_html_data":"_append_json_data"](b,"string"==typeof e?a(a.parseHTML(e)).filter(function(){return 3!==this.nodeType}):e,function(a){d.call(this,a)}),g=this.get_node(b.id,!0),g&&g.length&&g.removeClass("jstree-loading").attr("aria-busy",!1),delete this._data.massload[b.id],f):c._load_node.call(this,b,d)}},a.jstree.defaults.search={ajax:!1,fuzzy:!1,case_sensitive:!1,show_only_matches:!1,show_only_matches_children:!1,close_opened_onclear:!0,search_leaves_only:!1,search_callback:!1},a.jstree.plugins.search=function(c,d){this.bind=function(){d.bind.call(this),this._data.search.str="",this._data.search.dom=a(),this._data.search.res=[],this._data.search.opn=[],this._data.search.som=!1,this._data.search.smc=!1,this._data.search.hdn=[],this.element.on("search.jstree",a.proxy(function(b,c){if(this._data.search.som&&c.res.length){var d=this._model.data,e,f,g=[],h,i;for(e=0,f=c.res.length;f>e;e++)if(d[c.res[e]]&&!d[c.res[e]].state.hidden&&(g.push(c.res[e]),g=g.concat(d[c.res[e]].parents),this._data.search.smc))for(h=0,i=d[c.res[e]].children_d.length;i>h;h++)d[d[c.res[e]].children_d[h]]&&!d[d[c.res[e]].children_d[h]].state.hidden&&g.push(d[c.res[e]].children_d[h]);g=a.vakata.array_remove_item(a.vakata.array_unique(g),a.jstree.root),this._data.search.hdn=this.hide_all(!0),this.show_node(g,!0),this.redraw(!0)}},this)).on("clear_search.jstree",a.proxy(function(a,b){this._data.search.som&&b.res.length&&(this.show_node(this._data.search.hdn,!0),this.redraw(!0))},this))},this.search=function(c,d,e,f,g,h){if(c===!1||""===a.trim(c.toString()))return this.clear_search();f=this.get_node(f),f=f&&f.id?f.id:null,c=c.toString();var i=this.settings.search,j=i.ajax?i.ajax:!1,k=this._model.data,l=null,m=[],n=[],o,p;if(this._data.search.res.length&&!g&&this.clear_search(),e===b&&(e=i.show_only_matches),h===b&&(h=i.show_only_matches_children),!d&&j!==!1)return a.isFunction(j)?j.call(this,c,a.proxy(function(b){b&&b.d&&(b=b.d),this._load_nodes(a.isArray(b)?a.vakata.array_unique(b):[],function(){this.search(c,!0,e,f,g,h)})},this),f):(j=a.extend({},j),j.data||(j.data={}),j.data.str=c,f&&(j.data.inside=f),this._data.search.lastRequest&&this._data.search.lastRequest.abort(),this._data.search.lastRequest=a.ajax(j).fail(a.proxy(function(){this._data.core.last_error={error:"ajax",plugin:"search",id:"search_01",reason:"Could not load search parents",data:JSON.stringify(j)},this.settings.core.error.call(this,this._data.core.last_error)},this)).done(a.proxy(function(b){b&&b.d&&(b=b.d),this._load_nodes(a.isArray(b)?a.vakata.array_unique(b):[],function(){this.search(c,!0,e,f,g,h)})},this)),this._data.search.lastRequest);if(g||(this._data.search.str=c,this._data.search.dom=a(),this._data.search.res=[],this._data.search.opn=[],this._data.search.som=e,this._data.search.smc=h),l=new a.vakata.search(c,!0,{caseSensitive:i.case_sensitive,fuzzy:i.fuzzy}),a.each(k[f?f:a.jstree.root].children_d,function(a,b){var d=k[b];d.text&&!d.state.hidden&&(!i.search_leaves_only||d.state.loaded&&0===d.children.length)&&(i.search_callback&&i.search_callback.call(this,c,d)||!i.search_callback&&l.search(d.text).isMatch)&&(m.push(b),n=n.concat(d.parents))}),m.length){for(n=a.vakata.array_unique(n),o=0,p=n.length;p>o;o++)n[o]!==a.jstree.root&&k[n[o]]&&this.open_node(n[o],null,0)===!0&&this._data.search.opn.push(n[o]);g?(this._data.search.dom=this._data.search.dom.add(a(this.element[0].querySelectorAll("#"+a.map(m,function(b){return-1!=="0123456789".indexOf(b[0])?"\\3"+b[0]+" "+b.substr(1).replace(a.jstree.idregex,"\\$&"):b.replace(a.jstree.idregex,"\\$&")}).join(", #")))),this._data.search.res=a.vakata.array_unique(this._data.search.res.concat(m))):(this._data.search.dom=a(this.element[0].querySelectorAll("#"+a.map(m,function(b){return-1!=="0123456789".indexOf(b[0])?"\\3"+b[0]+" "+b.substr(1).replace(a.jstree.idregex,"\\$&"):b.replace(a.jstree.idregex,"\\$&")}).join(", #"))),this._data.search.res=m),this._data.search.dom.children(".jstree-anchor").addClass("jstree-search")}this.trigger("search",{nodes:this._data.search.dom,str:c,res:this._data.search.res,show_only_matches:e})},this.clear_search=function(){this.settings.search.close_opened_onclear&&this.close_node(this._data.search.opn,0),this.trigger("clear_search",{nodes:this._data.search.dom,str:this._data.search.str,res:this._data.search.res}),this._data.search.res.length&&(this._data.search.dom=a(this.element[0].querySelectorAll("#"+a.map(this._data.search.res,function(b){return-1!=="0123456789".indexOf(b[0])?"\\3"+b[0]+" "+b.substr(1).replace(a.jstree.idregex,"\\$&"):b.replace(a.jstree.idregex,"\\$&")}).join(", #"))),this._data.search.dom.children(".jstree-anchor").removeClass("jstree-search")),this._data.search.str="",this._data.search.res=[],this._data.search.opn=[],this._data.search.dom=a()},this.redraw_node=function(b,c,e,f){if(b=d.redraw_node.apply(this,arguments),b&&-1!==a.inArray(b.id,this._data.search.res)){var g,h,i=null;for(g=0,h=b.childNodes.length;h>g;g++)if(b.childNodes[g]&&b.childNodes[g].className&&-1!==b.childNodes[g].className.indexOf("jstree-anchor")){i=b.childNodes[g];break}i&&(i.className+=" jstree-search")}return b}},function(a){a.vakata.search=function(b,c,d){d=d||{},d=a.extend({},a.vakata.search.defaults,d),d.fuzzy!==!1&&(d.fuzzy=!0),b=d.caseSensitive?b:b.toLowerCase();var e=d.location,f=d.distance,g=d.threshold,h=b.length,i,j,k,l;return h>32&&(d.fuzzy=!1),d.fuzzy&&(i=1<c;c++)a[b.charAt(c)]=0;for(c=0;h>c;c++)a[b.charAt(c)]|=1<c;c++){o=0,p=q;while(p>o)k(c,e+p)<=m?o=p:q=p,p=Math.floor((q-o)/2+o);for(q=p,s=Math.max(1,e-p+1),t=Math.min(e+p,l)+h,u=new Array(t+2),u[t+1]=(1<=s;f--)if(v=j[a.charAt(f-1)],0===c?u[f]=(u[f+1]<<1|1)&v:u[f]=(u[f+1]<<1|1)&v|((r[f+1]|r[f])<<1|1)|r[f+1],u[f]&i&&(w=k(c,f-1),m>=w)){if(m=w,n=f-1,x.push(n),!(n>e))break;s=Math.max(1,2*e-n)}if(k(c+1,e)>m)break;r=u}return{isMatch:n>=0,score:w}},c===!0?{search:l}:l(c)},a.vakata.search.defaults={location:0,distance:100,threshold:.6,fuzzy:!1,caseSensitive:!1}}(a),a.jstree.defaults.sort=function(a,b){return this.get_text(a)>this.get_text(b)?1:-1},a.jstree.plugins.sort=function(b,c){this.bind=function(){c.bind.call(this),this.element.on("model.jstree",a.proxy(function(a,b){this.sort(b.parent,!0)},this)).on("rename_node.jstree create_node.jstree",a.proxy(function(a,b){this.sort(b.parent||b.node.parent,!1),this.redraw_node(b.parent||b.node.parent,!0)},this)).on("move_node.jstree copy_node.jstree",a.proxy(function(a,b){this.sort(b.parent,!1),this.redraw_node(b.parent,!0)},this))},this.sort=function(b,c){var d,e;if(b=this.get_node(b),b&&b.children&&b.children.length&&(b.children.sort(a.proxy(this.settings.sort,this)),c))for(d=0,e=b.children_d.length;e>d;d++)this.sort(b.children_d[d],!1)}};var m=!1;a.jstree.defaults.state={key:"jstree",events:"changed.jstree open_node.jstree close_node.jstree check_node.jstree uncheck_node.jstree",ttl:!1,filter:!1,preserve_loaded:!1},a.jstree.plugins.state=function(b,c){this.bind=function(){c.bind.call(this);var b=a.proxy(function(){this.element.on(this.settings.state.events,a.proxy(function(){m&&clearTimeout(m),m=setTimeout(a.proxy(function(){this.save_state()},this),100)},this)),this.trigger("state_ready")},this);this.element.on("ready.jstree",a.proxy(function(a,c){this.element.one("restore_state.jstree",b),this.restore_state()||b()},this))},this.save_state=function(){var b=this.get_state();this.settings.state.preserve_loaded||delete b.core.loaded;var c={state:b,ttl:this.settings.state.ttl,sec:+new Date};a.vakata.storage.set(this.settings.state.key,JSON.stringify(c))},this.restore_state=function(){var b=a.vakata.storage.get(this.settings.state.key);if(b)try{b=JSON.parse(b)}catch(c){return!1}return b&&b.ttl&&b.sec&&+new Date-b.sec>b.ttl?!1:(b&&b.state&&(b=b.state),b&&a.isFunction(this.settings.state.filter)&&(b=this.settings.state.filter.call(this,b)),b?(this.settings.state.preserve_loaded||delete b.core.loaded,this.element.one("set_state.jstree",function(c,d){d.instance.trigger("restore_state",{state:a.extend(!0,{},b)})}),this.set_state(b),!0):!1)},this.clear_state=function(){return a.vakata.storage.del(this.settings.state.key)}},function(a,b){a.vakata.storage={set:function(a,b){return window.localStorage.setItem(a,b)},get:function(a){return window.localStorage.getItem(a)},del:function(a){return window.localStorage.removeItem(a)}}}(a),a.jstree.defaults.types={"default":{}},a.jstree.defaults.types[a.jstree.root]={},a.jstree.plugins.types=function(c,d){this.init=function(c,e){var f,g;if(e&&e.types&&e.types["default"])for(f in e.types)if("default"!==f&&f!==a.jstree.root&&e.types.hasOwnProperty(f))for(g in e.types["default"])e.types["default"].hasOwnProperty(g)&&e.types[f][g]===b&&(e.types[f][g]=e.types["default"][g]);d.init.call(this,c,e),this._model.data[a.jstree.root].type=a.jstree.root},this.refresh=function(b,c){d.refresh.call(this,b,c),this._model.data[a.jstree.root].type=a.jstree.root},this.bind=function(){this.element.on("model.jstree",a.proxy(function(c,d){var e=this._model.data,f=d.nodes,g=this.settings.types,h,i,j="default",k;for(h=0,i=f.length;i>h;h++){if(j="default",e[f[h]].original&&e[f[h]].original.type&&g[e[f[h]].original.type]&&(j=e[f[h]].original.type),e[f[h]].data&&e[f[h]].data.jstree&&e[f[h]].data.jstree.type&&g[e[f[h]].data.jstree.type]&&(j=e[f[h]].data.jstree.type),e[f[h]].type=j,e[f[h]].icon===!0&&g[j].icon!==b&&(e[f[h]].icon=g[j].icon),g[j].li_attr!==b&&"object"==typeof g[j].li_attr)for(k in g[j].li_attr)if(g[j].li_attr.hasOwnProperty(k)){if("id"===k)continue;e[f[h]].li_attr[k]===b?e[f[h]].li_attr[k]=g[j].li_attr[k]:"class"===k&&(e[f[h]].li_attr["class"]=g[j].li_attr["class"]+" "+e[f[h]].li_attr["class"])}if(g[j].a_attr!==b&&"object"==typeof g[j].a_attr)for(k in g[j].a_attr)if(g[j].a_attr.hasOwnProperty(k)){if("id"===k)continue;e[f[h]].a_attr[k]===b?e[f[h]].a_attr[k]=g[j].a_attr[k]:"href"===k&&"#"===e[f[h]].a_attr[k]?e[f[h]].a_attr.href=g[j].a_attr.href:"class"===k&&(e[f[h]].a_attr["class"]=g[j].a_attr["class"]+" "+e[f[h]].a_attr["class"])}}e[a.jstree.root].type=a.jstree.root},this)),d.bind.call(this)},this.get_json=function(b,c,e){var f,g,h=this._model.data,i=c?a.extend(!0,{},c,{no_id:!1}):{},j=d.get_json.call(this,b,i,e);if(j===!1)return!1;if(a.isArray(j))for(f=0,g=j.length;g>f;f++)j[f].type=j[f].id&&h[j[f].id]&&h[j[f].id].type?h[j[f].id].type:"default", +c&&c.no_id&&(delete j[f].id,j[f].li_attr&&j[f].li_attr.id&&delete j[f].li_attr.id,j[f].a_attr&&j[f].a_attr.id&&delete j[f].a_attr.id);else j.type=j.id&&h[j.id]&&h[j.id].type?h[j.id].type:"default",c&&c.no_id&&(j=this._delete_ids(j));return j},this._delete_ids=function(b){if(a.isArray(b)){for(var c=0,d=b.length;d>c;c++)b[c]=this._delete_ids(b[c]);return b}return delete b.id,b.li_attr&&b.li_attr.id&&delete b.li_attr.id,b.a_attr&&b.a_attr.id&&delete b.a_attr.id,b.children&&a.isArray(b.children)&&(b.children=this._delete_ids(b.children)),b},this.check=function(c,e,f,g,h){if(d.check.call(this,c,e,f,g,h)===!1)return!1;e=e&&e.id?e:this.get_node(e),f=f&&f.id?f:this.get_node(f);var i=e&&e.id?h&&h.origin?h.origin:a.jstree.reference(e.id):null,j,k,l,m;switch(i=i&&i._model&&i._model.data?i._model.data:null,c){case"create_node":case"move_node":case"copy_node":if("move_node"!==c||-1===a.inArray(e.id,f.children)){if(j=this.get_rules(f),j.max_children!==b&&-1!==j.max_children&&j.max_children===f.children.length)return this._data.core.last_error={error:"check",plugin:"types",id:"types_01",reason:"max_children prevents function: "+c,data:JSON.stringify({chk:c,pos:g,obj:e&&e.id?e.id:!1,par:f&&f.id?f.id:!1})},!1;if(j.valid_children!==b&&-1!==j.valid_children&&-1===a.inArray(e.type||"default",j.valid_children))return this._data.core.last_error={error:"check",plugin:"types",id:"types_02",reason:"valid_children prevents function: "+c,data:JSON.stringify({chk:c,pos:g,obj:e&&e.id?e.id:!1,par:f&&f.id?f.id:!1})},!1;if(i&&e.children_d&&e.parents){for(k=0,l=0,m=e.children_d.length;m>l;l++)k=Math.max(k,i[e.children_d[l]].parents.length);k=k-e.parents.length+1}(0>=k||k===b)&&(k=1);do{if(j.max_depth!==b&&-1!==j.max_depth&&j.max_depthg;g++)this.set_type(c[g],d);return!0}if(f=this.settings.types,c=this.get_node(c),!f[d]||!c)return!1;if(l=this.get_node(c,!0),l&&l.length&&(m=l.children(".jstree-anchor")),i=c.type,j=this.get_icon(c),c.type=d,(j===!0||!f[i]||f[i].icon!==b&&j===f[i].icon)&&this.set_icon(c,f[d].icon!==b?f[d].icon:!0),f[i]&&f[i].li_attr!==b&&"object"==typeof f[i].li_attr)for(k in f[i].li_attr)if(f[i].li_attr.hasOwnProperty(k)){if("id"===k)continue;"class"===k?(e[c.id].li_attr["class"]=(e[c.id].li_attr["class"]||"").replace(f[i].li_attr[k],""),l&&l.removeClass(f[i].li_attr[k])):e[c.id].li_attr[k]===f[i].li_attr[k]&&(e[c.id].li_attr[k]=null,l&&l.removeAttr(k))}if(f[i]&&f[i].a_attr!==b&&"object"==typeof f[i].a_attr)for(k in f[i].a_attr)if(f[i].a_attr.hasOwnProperty(k)){if("id"===k)continue;"class"===k?(e[c.id].a_attr["class"]=(e[c.id].a_attr["class"]||"").replace(f[i].a_attr[k],""),m&&m.removeClass(f[i].a_attr[k])):e[c.id].a_attr[k]===f[i].a_attr[k]&&("href"===k?(e[c.id].a_attr[k]="#",m&&m.attr("href","#")):(delete e[c.id].a_attr[k],m&&m.removeAttr(k)))}if(f[d].li_attr!==b&&"object"==typeof f[d].li_attr)for(k in f[d].li_attr)if(f[d].li_attr.hasOwnProperty(k)){if("id"===k)continue;e[c.id].li_attr[k]===b?(e[c.id].li_attr[k]=f[d].li_attr[k],l&&("class"===k?l.addClass(f[d].li_attr[k]):l.attr(k,f[d].li_attr[k]))):"class"===k&&(e[c.id].li_attr["class"]=f[d].li_attr[k]+" "+e[c.id].li_attr["class"],l&&l.addClass(f[d].li_attr[k]))}if(f[d].a_attr!==b&&"object"==typeof f[d].a_attr)for(k in f[d].a_attr)if(f[d].a_attr.hasOwnProperty(k)){if("id"===k)continue;e[c.id].a_attr[k]===b?(e[c.id].a_attr[k]=f[d].a_attr[k],m&&("class"===k?m.addClass(f[d].a_attr[k]):m.attr(k,f[d].a_attr[k]))):"href"===k&&"#"===e[c.id].a_attr[k]?(e[c.id].a_attr.href=f[d].a_attr.href,m&&m.attr("href",f[d].a_attr.href)):"class"===k&&(e[c.id].a_attr["class"]=f[d].a_attr["class"]+" "+e[c.id].a_attr["class"],m&&m.addClass(f[d].a_attr[k]))}return!0}},a.jstree.defaults.unique={case_sensitive:!1,trim_whitespace:!1,duplicate:function(a,b){return a+" ("+b+")"}},a.jstree.plugins.unique=function(c,d){this.check=function(b,c,e,f,g){if(d.check.call(this,b,c,e,f,g)===!1)return!1;if(c=c&&c.id?c:this.get_node(c),e=e&&e.id?e:this.get_node(e),!e||!e.children)return!0;var h="rename_node"===b?f:c.text,i=[],j=this.settings.unique.case_sensitive,k=this.settings.unique.trim_whitespace,l=this._model.data,m,n,o;for(m=0,n=e.children.length;n>m;m++)o=l[e.children[m]].text,j||(o=o.toLowerCase()),k&&(o=o.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"")),i.push(o);switch(j||(h=h.toLowerCase()),k&&(h=h.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"")),b){case"delete_node":return!0;case"rename_node":return o=c.text||"",j||(o=o.toLowerCase()),k&&(o=o.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"")),m=-1===a.inArray(h,i)||c.text&&o===h,m||(this._data.core.last_error={error:"check",plugin:"unique",id:"unique_01",reason:"Child with name "+h+" already exists. Preventing: "+b,data:JSON.stringify({chk:b,pos:f,obj:c&&c.id?c.id:!1,par:e&&e.id?e.id:!1})}),m;case"create_node":return m=-1===a.inArray(h,i),m||(this._data.core.last_error={error:"check",plugin:"unique",id:"unique_04",reason:"Child with name "+h+" already exists. Preventing: "+b,data:JSON.stringify({chk:b,pos:f,obj:c&&c.id?c.id:!1,par:e&&e.id?e.id:!1})}),m;case"copy_node":return m=-1===a.inArray(h,i),m||(this._data.core.last_error={error:"check",plugin:"unique",id:"unique_02",reason:"Child with name "+h+" already exists. Preventing: "+b,data:JSON.stringify({chk:b,pos:f,obj:c&&c.id?c.id:!1,par:e&&e.id?e.id:!1})}),m;case"move_node":return m=c.parent===e.id&&(!g||!g.is_multi)||-1===a.inArray(h,i),m||(this._data.core.last_error={error:"check",plugin:"unique",id:"unique_03",reason:"Child with name "+h+" already exists. Preventing: "+b,data:JSON.stringify({chk:b,pos:f,obj:c&&c.id?c.id:!1,par:e&&e.id?e.id:!1})}),m}return!0},this.create_node=function(c,e,f,g,h){if(!e||e.text===b){if(null===c&&(c=a.jstree.root),c=this.get_node(c),!c)return d.create_node.call(this,c,e,f,g,h);if(f=f===b?"last":f,!f.toString().match(/^(before|after)$/)&&!h&&!this.is_loaded(c))return d.create_node.call(this,c,e,f,g,h);e||(e={});var i,j,k,l,m,n=this._model.data,o=this.settings.unique.case_sensitive,p=this.settings.unique.trim_whitespace,q=this.settings.unique.duplicate,r;for(j=i=this.get_string("New node"),k=[],l=0,m=c.children.length;m>l;l++)r=n[c.children[l]].text,o||(r=r.toLowerCase()),p&&(r=r.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"")),k.push(r);l=1,r=j,o||(r=r.toLowerCase()),p&&(r=r.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,""));while(-1!==a.inArray(r,k))j=q.call(this,i,++l).toString(),r=j,o||(r=r.toLowerCase()),p&&(r=r.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,""));e.text=j}return d.create_node.call(this,c,e,f,g,h)}};var n=i.createElement("DIV");if(n.setAttribute("unselectable","on"),n.setAttribute("role","presentation"),n.className="jstree-wholerow",n.innerHTML=" ",a.jstree.plugins.wholerow=function(b,c){this.bind=function(){c.bind.call(this),this.element.on("ready.jstree set_state.jstree",a.proxy(function(){this.hide_dots()},this)).on("init.jstree loading.jstree ready.jstree",a.proxy(function(){this.get_container_ul().addClass("jstree-wholerow-ul")},this)).on("deselect_all.jstree",a.proxy(function(a,b){this.element.find(".jstree-wholerow-clicked").removeClass("jstree-wholerow-clicked")},this)).on("changed.jstree",a.proxy(function(a,b){this.element.find(".jstree-wholerow-clicked").removeClass("jstree-wholerow-clicked");var c=!1,d,e;for(d=0,e=b.selected.length;e>d;d++)c=this.get_node(b.selected[d],!0),c&&c.length&&c.children(".jstree-wholerow").addClass("jstree-wholerow-clicked")},this)).on("open_node.jstree",a.proxy(function(a,b){this.get_node(b.node,!0).find(".jstree-clicked").parent().children(".jstree-wholerow").addClass("jstree-wholerow-clicked")},this)).on("hover_node.jstree dehover_node.jstree",a.proxy(function(a,b){"hover_node"===a.type&&this.is_disabled(b.node)||this.get_node(b.node,!0).children(".jstree-wholerow")["hover_node"===a.type?"addClass":"removeClass"]("jstree-wholerow-hovered")},this)).on("contextmenu.jstree",".jstree-wholerow",a.proxy(function(b){if(this._data.contextmenu){b.preventDefault();var c=a.Event("contextmenu",{metaKey:b.metaKey,ctrlKey:b.ctrlKey,altKey:b.altKey,shiftKey:b.shiftKey,pageX:b.pageX,pageY:b.pageY});a(b.currentTarget).closest(".jstree-node").children(".jstree-anchor").first().trigger(c)}},this)).on("click.jstree",".jstree-wholerow",function(b){b.stopImmediatePropagation();var c=a.Event("click",{metaKey:b.metaKey,ctrlKey:b.ctrlKey,altKey:b.altKey,shiftKey:b.shiftKey});a(b.currentTarget).closest(".jstree-node").children(".jstree-anchor").first().trigger(c).focus()}).on("dblclick.jstree",".jstree-wholerow",function(b){b.stopImmediatePropagation();var c=a.Event("dblclick",{metaKey:b.metaKey,ctrlKey:b.ctrlKey,altKey:b.altKey,shiftKey:b.shiftKey});a(b.currentTarget).closest(".jstree-node").children(".jstree-anchor").first().trigger(c).focus()}).on("click.jstree",".jstree-leaf > .jstree-ocl",a.proxy(function(b){b.stopImmediatePropagation();var c=a.Event("click",{metaKey:b.metaKey,ctrlKey:b.ctrlKey,altKey:b.altKey,shiftKey:b.shiftKey});a(b.currentTarget).closest(".jstree-node").children(".jstree-anchor").first().trigger(c).focus()},this)).on("mouseover.jstree",".jstree-wholerow, .jstree-icon",a.proxy(function(a){return a.stopImmediatePropagation(),this.is_disabled(a.currentTarget)||this.hover_node(a.currentTarget),!1},this)).on("mouseleave.jstree",".jstree-node",a.proxy(function(a){this.dehover_node(a.currentTarget)},this))},this.teardown=function(){this.settings.wholerow&&this.element.find(".jstree-wholerow").remove(),c.teardown.call(this)},this.redraw_node=function(b,d,e,f){if(b=c.redraw_node.apply(this,arguments)){var g=n.cloneNode(!0);-1!==a.inArray(b.id,this._data.core.selected)&&(g.className+=" jstree-wholerow-clicked"),this._data.core.focused&&this._data.core.focused===b.id&&(g.className+=" jstree-wholerow-hovered"),b.insertBefore(g,b.childNodes[0])}return b}},window.customElements&&Object&&Object.create){var o=Object.create(HTMLElement.prototype);o.createdCallback=function(){var b={core:{},plugins:[]},c;for(c in a.jstree.plugins)a.jstree.plugins.hasOwnProperty(c)&&this.attributes[c]&&(b.plugins.push(c),this.getAttribute(c)&&JSON.parse(this.getAttribute(c))&&(b[c]=JSON.parse(this.getAttribute(c))));for(c in a.jstree.defaults.core)a.jstree.defaults.core.hasOwnProperty(c)&&this.attributes[c]&&(b.core[c]=JSON.parse(this.getAttribute(c))||this.getAttribute(c));a(this).jstree(b)};try{window.customElements.define("vakata-jstree",function(){},{prototype:o})}catch(p){}}}}); diff --git a/KeywordSearch/solr/server/solr-webapp/webapp/libs/ngtimeago.js b/KeywordSearch/solr/server/solr-webapp/webapp/libs/ngtimeago.js new file mode 100755 index 0000000000..46a9d4b435 --- /dev/null +++ b/KeywordSearch/solr/server/solr-webapp/webapp/libs/ngtimeago.js @@ -0,0 +1,100 @@ +/* +Copyright (c) 2014 Uttesh Kumar + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +'use strict'; + +var catalyst = angular.module('ngtimeago', []); + + +catalyst.filter('timeago', function() { + return function(input, p_allowFuture) { + + if (input === undefined) { + return "-"; + } + + var substitute = function (stringOrFunction, number, strings) { + var string = angular.isFunction(stringOrFunction) ? stringOrFunction(number, dateDifference) : stringOrFunction; + var value = (strings.numbers && strings.numbers[number]) || number; + return string.replace(/%d/i, value); + }, + nowTime = (new Date()).getTime(), + date = (new Date(input)).getTime(), + //refreshMillis= 6e4, //A minute + allowFuture = p_allowFuture || false, + strings= { + prefixAgo: ' ', + prefixFromNow: '', + suffixAgo: "ago", + suffixFromNow: "from now", + seconds: "less than a minute", + minute: "about a minute", + minutes: "%d minutes", + hour: "about an hour", + hours: "about %d hours", + day: "a day", + days: "%d days", + month: "about a month", + months: "%d months", + year: "about a year", + years: "%d years" + }, + dateDifference = nowTime - date, + words, + seconds = Math.abs(dateDifference) / 1000, + minutes = seconds / 60, + hours = minutes / 60, + days = hours / 24, + years = days / 365, + separator = strings.wordSeparator === undefined ? " " : strings.wordSeparator, + + + prefix = strings.prefixAgo, + suffix = strings.suffixAgo; + + if (allowFuture) { + if (dateDifference < 0) { + prefix = strings.prefixFromNow; + suffix = strings.suffixFromNow; + } + } + + words = seconds < 45 && substitute(strings.seconds, Math.round(seconds), strings) || + seconds < 90 && substitute(strings.minute, 1, strings) || + minutes < 45 && substitute(strings.minutes, Math.round(minutes), strings) || + minutes < 90 && substitute(strings.hour, 1, strings) || + hours < 24 && substitute(strings.hours, Math.round(hours), strings) || + hours < 42 && substitute(strings.day, 1, strings) || + days < 30 && substitute(strings.days, Math.round(days), strings) || + days < 45 && substitute(strings.month, 1, strings) || + days < 365 && substitute(strings.months, Math.round(days / 30), strings) || + years < 1.5 && substitute(strings.year, 1, strings) || + substitute(strings.years, Math.round(years), strings); + prefix.replace(/ /g, ''); + words.replace(/ /g, ''); + suffix.replace(/ /g, ''); + return (prefix+' '+words+' '+suffix+' '+separator); + }; + }); + + + diff --git a/KeywordSearch/solr/server/solr-webapp/webapp/partials/alias_overview.html b/KeywordSearch/solr/server/solr-webapp/webapp/partials/alias_overview.html new file mode 100755 index 0000000000..0cf8124b53 --- /dev/null +++ b/KeywordSearch/solr/server/solr-webapp/webapp/partials/alias_overview.html @@ -0,0 +1,46 @@ + +
    + +
    + +
    + +

    Alias: {{selectedCollection.name}}

    + +
    + +
    + +
    Collections:
    +
    + + {{coll}}{{$last ? '' : ', '}} + +
    + + +
    {{ key }}
    +
    {{ value }}
    +
    +
    + +
    +
    +
    + +
    diff --git a/KeywordSearch/solr/server/solr-webapp/webapp/partials/analysis.html b/KeywordSearch/solr/server/solr-webapp/webapp/partials/analysis.html new file mode 100755 index 0000000000..23527f7351 --- /dev/null +++ b/KeywordSearch/solr/server/solr-webapp/webapp/partials/analysis.html @@ -0,0 +1,128 @@ + +
    + +
    +
    This Functionality requires the /analysis/field Handler to be registered and active!
    +
    + +
    +
    {{analysisError}}
    +
    + +
    + +
    + +
    + +
      + +
    • + + + + +
    • + +
    • + + + + +
    • + +
    • +
      + + + + Schema Browser  + +
      + + + + + +
      + +
      +
    • + +
    + +
    + +
    + +
    +
    + + + + + + + + + +
    +
    + {{component.short}} +
    +
    +
    + + + + +
    + + + + +
    {{ caption }}
    +
    +
    +
    +
    + + + + + + +
    + + + + + + +
    {{value.value}}
    +
    +
    +
     
    +
    +
    +
    +
    + +
    diff --git a/KeywordSearch/solr/server/solr-webapp/webapp/partials/cloud.html b/KeywordSearch/solr/server/solr-webapp/webapp/partials/cloud.html new file mode 100755 index 0000000000..01d63499ee --- /dev/null +++ b/KeywordSearch/solr/server/solr-webapp/webapp/partials/cloud.html @@ -0,0 +1,303 @@ + +
    + +
    + +
    + + +
    Status: {{zkState.status}}
    +
    + Errors: +
      +
    • {{error}}
    • +
    +
    +
    ZK connection string: {{zkState.zkHost}}
    +
    Ensemble size: {{zkState.ensembleSize}}
    +
    Ensemble mode: {{zkState.mode}}
    +
    Dynamic reconfig enabled: {{zkState.dynamicReconfig}}
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    {{host.host}}
    {{key}} + {{key === 'zk_version' ? host[key].split("-")[0] : host[key]}} +
    {{key}} + {{host[key]}} +
    {{key}} + {{host[key]}} +
    {{key}} + {{host[key]}} +
    +
    + +
    + + +
    + + Metadata +
    +
      +
    • +
      +
      {{ key }}
      +
      {{ prop }}
      +
      +
    • +
    +
    + +
    + Node "{{znode.path }}" has no utf8 Content +
    +          
    +   +
    + + +
    + +
    + +
    + +
    +
    + + No hosts found. + Hosts {{from+1}} - {{from + hostsToShow.length}} of {{filteredHosts.length}}.  + + Filter by:   + + +   + + +   + + Show hosts per page. + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    HostNodeCPUHeapDisk usageRequestsCollectionsReplicas
    +
    {{h.host}}
    + + {{h.system.system.name}} + {{h.memTotal}} + Java {{h.system.jvm.spec.version}} +
    Load: {{h.loadAvg}} +
    +
    + {{h.system.system.name}} {{h.system.system.version}}, {{h.system.system.availableProcessors}}cpu
    + Uptime: {{h.uptime}}
    + Memory: {{h.memTotal}}
    + File descriptors: {{h.openFileDescriptorCount}}/{{h.maxFileDescriptorCount}}
    + Disk: {{h.diskTotal}} used: {{h.diskUsedPct}}%
    + Load: {{h.loadAvg}} +
    + +
    +
    (DEAD)
    +
    + Uptime: {{n.jvmUptime}}
    +
    + Java {{n.system.jvm.jre.version}}
    + Solr {{n.system.lucene['solr-impl-version'].split(" ")[0]}}
    +
    + +
    +
    +
    + {{n.cpuPct}}% +
    +
    +
    + {{n.heapUsedPct}}% +
    +
    + Max: {{n.heapTotal}}
    + Used: {{n.heapUsed}} +
    +
    +
    +
    + {{n.size}} +
    +
    + Total #docs: {{n.numDocs}}
    + Avg size/doc: {{n.sizePerDoc}} +
    +
    +
    +
    + RPM: {{n.req15minRate}}
    p95: {{n.reqp95_ms}}ms
    +
    +
    (none)
    +
    + {{ c }} +
    + +
    +
    (none)
    +
    +
    {{ core.label }} ({{core.numDocsHuman}} docs)
    +
    {{ core.label }}
    +
      +
    • deleted: {{core.deletedDocsHuman}}
    • +
    • warmupTime: {{core.warmupTime}}
    • +
    • avg size/doc: {{core.avgSizePerDoc}}
    • +
    +
    + +
    +
    + +
    + +
    + +
    +
      +
    • Leader
    • +
    • Active
    • +
    • Recovering
    • +
    • Down
    • +
    • Recovery Failed
    • +
    • Inactive
    • +
    • Gone
    • +
    • Replica Type:
      N(NRT),T(TLOG),
      P(PULL)
    • +
    +
    +
    +
    + + No collections found. + Collections {{start}} - {{last}} of {{total}}.  + + Filter by:  T:{{filterType}} + + + + + +   + + Show per page. + +
    +
    + +
    + +
    + + + +
    diff --git a/KeywordSearch/solr/server/solr-webapp/webapp/partials/cluster_suggestions.html b/KeywordSearch/solr/server/solr-webapp/webapp/partials/cluster_suggestions.html new file mode 100755 index 0000000000..892b0bbc97 --- /dev/null +++ b/KeywordSearch/solr/server/solr-webapp/webapp/partials/cluster_suggestions.html @@ -0,0 +1,49 @@ + + +
    +

    Cluster Suggestions

    +
    + + + + + + + + + + + + + + + + +
    TypeReasonDetailsAction
    NA
    {{ x.type }}{{ x.violation.clause }}node: {{ x.violation.node }}, collection : {{ x.violation.collection }}, shard : {{ x.violation.shard }}   +
    + +
    +
    +
    +
    +
    + +
    + diff --git a/KeywordSearch/solr/server/solr-webapp/webapp/partials/collection_overview.html b/KeywordSearch/solr/server/solr-webapp/webapp/partials/collection_overview.html new file mode 100755 index 0000000000..cf1c18c314 --- /dev/null +++ b/KeywordSearch/solr/server/solr-webapp/webapp/partials/collection_overview.html @@ -0,0 +1,85 @@ + +
    + +
    + +
    + +

    Collection: {{selectedCollection.name}}

    + +
    + +
    + +
    Config name:
    +
    {{selectedCollection.configName}}
    + +
    Max shards per node:
    +
    {{selectedCollection.maxShardsPerNode}}
    + +
    Replication factor:
    +
    {{selectedCollection.replicationFactor}}
    + +
    Auto-add replicas:
    +
    yes
    + +
    Router name:
    +
    {{selectedCollection.router.name}}
    + +
    + +
    +
    + +
    + +

    Shards

    + +
    + +
    +

    {{name}}

    +
    +
    Range:
    +
    {{ shard.range }}
    + +
    Active:
    +
    yes
    + +
    Replicas:
    +
    +
    +

    {{replica.core}}

    +
    +
    Base URL:
    {{replica.base_url}}
    +
    Core:
    {{replica.core}}
    +
    Active:
    +
    yes
    +
    Leader:
    +
    yes
    +
    +
    + +
    +
    +
    +
    + +
    + +
    diff --git a/KeywordSearch/solr/server/solr-webapp/webapp/partials/collections.html b/KeywordSearch/solr/server/solr-webapp/webapp/partials/collections.html new file mode 100755 index 0000000000..198030c744 --- /dev/null +++ b/KeywordSearch/solr/server/solr-webapp/webapp/partials/collections.html @@ -0,0 +1,395 @@ + +
    + + + +
    +
    + +
    + +

    +

    + +

      + +

    + +

    +

    + +

    +

    + +

    + Show advanced

    +
    + +

    Advanced options:

    +

    + +

    + +

    +

    + +

    +

    + +

    +

    + +

    + +

    + +
    +

    + {{addMessage}} +

    + +

    + + +

    + +
    + +
    +
    + +
    + + + +

    +

    + +

    +

    + + +

    + {{renameMessage}} +

    + +

    + + +

    +
    + +
    +
    + +
    + +
    + + + + + +
    + +
    + +

    Please type collection name to confirm deletion:

    +

    +

    + +

    + {{deleteMessage}} +

    + +

    + + +

    +
    + +
    + +
    + +
    +

    Are you sure you want to delete alias {{ collection.name }}?

    + +

    + + +

    +
    + +
    + +
    + + +
    +

    Please select a collection or alias

    +
    + +
    + +
    + +

    Alias: {{collection.name}}

    + +
    +
    +
    + +
    + +
      + +
    • +
      +
      Collections:
      +
      + + {{coll}}{{$last ? '' : ', '}} + +
      +
      +
      {{ key }}
      +
      {{ value }}
      +
      +
      +
    • +
    +
    +
    + +
    + +

    Collection: {{collection.name}}

    + +
    +
    +
    + +
    + +
      + +
    • +
      +
      Shard count:
      +
      {{collection.shards.length}}
      +
      +
    • +
    • +
      configName:
      +
      {{collection.configName}}
      +
    • + +
    • +
      replicationFactor:
      +
      {{collection.replicationFactor}}
      +
    • + +
    • +
      maxShardsPerNode:
      +
      {{collection.maxShardsPerNode}}
      +
    • + +
    • +
      router:
      +
      {{collection.router.name}}
      +
    • + +
    • +
      autoAddReplicas:
      +
      {{collection.autoAddReplicas}}
      +
    • + +
    • +
      aliases:
      +
      + + {{coll}}{{$last ? '' : ', '}} + +
      +
    • +
    + +
    +
    + +
    +
    + +

    + Shard: {{shard.name}} +
    +

    + +
      +
    • +
      +

      Are you sure you want to delete this shard?

      +

      + + +

      +
      +
    • +
    +
      +
    • +
        +
      • +
        +
        state:
        +
        {{shard.state}}
        +
        +
      • +
      • +
        +
        range:
        +
        {{shard.range}}
        +
        +
        +
      • +
      • +
          +
        • +

          + + Replica: {{replica.name}} + +
          +

          +
        • +
        • +
            +
          • +
            + +

            Are you sure you want to delete this replica?

            + +

            + + +

            +
            +
          • +
          +
            +
          • +
            +
            core:
            +
            {{replica.core}}
            +
            +
          • + +
          • +
            +
            base URL:
            +
            {{replica.base_url}}
            +
            +
          • + +
          • +
            +
            node name:
            +
            {{replica.node_name}}
            +
            +
          • + +
          • +
            +
            state:
            +
            {{replica.state}}
            +
            +
          • + +
          • +
            +
            leader:
            +
            +
            +
          • +
          +
        • + +
        +
      • +
      • + + + +
      • +
      +
      + +
      + +

      + + node: {{shard.replicaNodeName}} +

      + +

      + {{createReplicaMessage}} +

      + +

      + + +

      +

       

      +
      + +
      +
    • +
    +
    +
    + +
    + +
    + + + +
    diff --git a/KeywordSearch/solr/server/solr-webapp/webapp/partials/core_overview.html b/KeywordSearch/solr/server/solr-webapp/webapp/partials/core_overview.html new file mode 100755 index 0000000000..f1826f65c0 --- /dev/null +++ b/KeywordSearch/solr/server/solr-webapp/webapp/partials/core_overview.html @@ -0,0 +1,206 @@ + +
    + +
    + +
    + +

    Statistics

    + +
    +
    {{statsMessage}}
    +
    + +
    + +
    + +
    Last Modified:
    +
    {{index.lastModified | timeago}}
    + +
    Num Docs:
    +
    {{index.numDocs}}
    + +
    Max Doc:
    +
    {{index.maxDoc}}
    + +
    Heap Memory Usage:
    +
    {{index.indexHeapUsageBytes}}
    + +
    Deleted Docs:
    +
    {{index.deletedDocs}}
    + +
    Version:
    +
    {{index.version}}
    + +
    Segment Count:
    +
    {{index.segmentCount}}
    + +
    Current:
    +
    + +
    + +
    +
    + +
    + +

    Instance

    + +
    +
    {{indexMessage}}
    +
    + +
    + +
    + +
    CWD:
    +
    {{ core.directory.cwd }}
    + +
    Instance:
    +
    {{ core.directory.instance }}
    + +
    Data:
    +
    {{ core.directory.data }}
    + +
    Index:
    +
    {{ core.directory.index }}
    + +
    Impl:
    +
    {{ core.directory.dirimpl }}
    + +
    + +
    +
    + +
    +
    + +
    + +

    + + Replication + (Slave) + (Master) + +

    + +
    +
    {{replicationMessage}}
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    IndexVersionGenSize
    Master (Searching)
    {{replication.indexVersion}}
    {{replication.generation}}
    {{replication.indexSize || '-'}}
    Master (Replicable)
    {{replication.master.replicableVersion || '-'}}
    {{replication.master.replicableGeneration || '-'}}
    -
    Master (Replicable)
    {{replication.master.replicableVersion || '-'}}
    {{replication.master.replicableGeneration || '-'}}
    -
    Master (Searching)
    {{replication.slave.masterDetails.indexVersion}}
    {{replication.slave.masterDetails.generation}}
    {{replication.slave.masterDetails.indexSize || '-'}}
    Slave (Searching)
    {{replication.indexVersion}}
    {{replication.generation}}
    {{replication.indexSize || '-'}}
    + +
    +
    + +
    + +

    Healthcheck

    + +
    +
    {{healthcheckMessage}}
    +
    + +
    +
    + +
    Status:
    +
    + +
    +
    + +
    +
    +
    + +
    + +
    + + +
    diff --git a/KeywordSearch/solr/server/solr-webapp/webapp/partials/cores.html b/KeywordSearch/solr/server/solr-webapp/webapp/partials/cores.html new file mode 100755 index 0000000000..1615769564 --- /dev/null +++ b/KeywordSearch/solr/server/solr-webapp/webapp/partials/cores.html @@ -0,0 +1,224 @@ + +
    + + + +
    + +
    + + + + + + + + +
    + +
    + +

    +

    + +

    +

    + +

    +

    + +

    +

    + +

    +

    + +
    + +

    +

    + +

    +

    + +
    + +

    + + instanceDir and dataDir need to exist before you can create the core + +

    + +

    + {{addMessage}} +

    + +

    + + +

    + +
    + +
    + +
    + +
    + + + +

    +

    + +

    + {{renameMessage}} +

    + +

    + + +

    +
    + +
    + +
    + +
    + +

    +

    + +

    +

    + +

    + {{swapMessage}} +

    + +

    + + +

    + +
    + +
    + +
    + +
    + +
    + +

    Core

    + +
    +
    +
    + +
    + +
      + +
    • +
      startTime:
      +
      {{core.startTime | timeago}}
      +
    • + +
    • +
      instanceDir:
      +
      {{core.instanceDir}}
      +
    • + +
    • +
      dataDir:
      +
      {{core.dataDir}}
      +
    • + +
    + +
    +
    + +
    + +

    Index

    + +
    +
    {{core.message}}
    +
    + +
    + +
      + +
    • +
      lastModified:
      +
      {{core.index.lastModified | timeago}}
      +
    • + +
    • +
      version:
      +
      {{core.index.version}}
      +
    • + +
    • +
      numDocs:
      +
      {{core.index.numDocs}}
      +
    • + +
    • +
      maxDoc:
      +
      {{core.index.maxDoc}}
      +
    • + +
    • +
      deletedDocs:
      +
      {{core.index.deletedDocs}}
      +
    • + +
    • +
      current:
      +
      +
    • + +
    • +
      directory:
      +
      {{core.index.directory}}
      +
    • + +
    + +
    +
    + +
    + +
    + + + +
    diff --git a/KeywordSearch/solr/server/solr-webapp/webapp/partials/dataimport.html b/KeywordSearch/solr/server/solr-webapp/webapp/partials/dataimport.html new file mode 100755 index 0000000000..a27be07a5a --- /dev/null +++ b/KeywordSearch/solr/server/solr-webapp/webapp/partials/dataimport.html @@ -0,0 +1,210 @@ + +
    + +
    The solrconfig.xml file for this index does not have an operational DataImportHandler defined!
    +
    +
    The Data Import Handler is deprecated as of Solr 8.6 and may be removed in a future release. A community supported package for may be used instead (See SOLR-14066 for details).
    + +
    + +
    + +

    Last Update: {{lastUpdate}}

    +
    + + {{info.text}} (Duration: {{info.timeTaken | readableSeconds }}) + +
    +
    + + {{ doc.name }}: {{doc.value | number}} {{ doc.speed | number}}/s, + +
    +
    + + {{ date.name }}: + {{ date.value | timeago }} + +
    +
    + + + +
    + +
    + +
    + +

    + Raw Status-Output +

    + +
    +
    +
    + +
    + +
    + +
    + +
    + +
    + +

    + Configuration + Reload + Debug-Mode +

    + +
    +
    +
    + +
    +
    + +
    + +
    + +
    + +
    + + + +
    + +
    + +
    + +
    + +
    + +

    + Raw Debug-Response +

    + +
    +
    +
    + +
    + + No Request executed + + +
    +
    +
    + +
    + +
    + +
    + + + +
    + + + + + + + + + + + + + + + + + + +
    + + +
    + + + +
    + + + +

    Auto-Refresh Status

    + +
    + +
    diff --git a/KeywordSearch/solr/server/solr-webapp/webapp/partials/documents.html b/KeywordSearch/solr/server/solr-webapp/webapp/partials/documents.html new file mode 100755 index 0000000000..2bf3f12982 --- /dev/null +++ b/KeywordSearch/solr/server/solr-webapp/webapp/partials/documents.html @@ -0,0 +1,111 @@ + + +
    +
    +
    + + + + +
    +
    +
    +
    +
    +
    Field: +
    +
    Field Data: + +
    +
    + +
    + + + +
    + +
    +
    + +
    + +
    + +
    + + + + +
    +
    +
    + + +
    +
    +
    +
    +
    + Status: {{ responseStatus }} +
    +
    + Response: +
    +
    +
    + +
    +
    + + diff --git a/KeywordSearch/solr/server/solr-webapp/webapp/partials/files.html b/KeywordSearch/solr/server/solr-webapp/webapp/partials/files.html new file mode 100755 index 0000000000..6a31096f09 --- /dev/null +++ b/KeywordSearch/solr/server/solr-webapp/webapp/partials/files.html @@ -0,0 +1,47 @@ + +
    + +
    + +
    + + + +
    +
    + + + +
    + +
    +
    +
    + + +
    + +
    + +
    + +
    diff --git a/KeywordSearch/solr/server/solr-webapp/webapp/partials/index.html b/KeywordSearch/solr/server/solr-webapp/webapp/partials/index.html new file mode 100755 index 0000000000..5f77bffae3 --- /dev/null +++ b/KeywordSearch/solr/server/solr-webapp/webapp/partials/index.html @@ -0,0 +1,295 @@ + +
    + +
    + +
    + +
    + +

    Instance

    + +
    + +
      + +
    • +
      Start
      +
      {{system.jvm.jmx.startTime | timeago}}
      +
    • + + +
    + +
    + +
    + +
    + +

    Versions

    + +
    + +
      + +
    • +
      solr-spec
      +
      {{system.lucene["solr-spec-version"]}}
      +
    • + +
    • +
      solr-impl
      +
      {{system.lucene["solr-impl-version"]}}
      +
    • + +
    • +
      lucene-spec
      +
      {{system.lucene["lucene-spec-version"]}}
      +
    • + +
    • +
      lucene-impl
      +
      {{system.lucene["lucene-impl-version"]}}
      +
    • + +
    + +
    + +
    + +
    + +
    + +
    + +

    System + {{load_average}} +

    + reload + +
    + +
    + +

    Physical Memory + {{memoryPercentage}} +

    +
    + +
    + {{memoryMax}} + +
    + {{memoryTotalDisplay}} + +
    + +
    + +
    + +
    + +
    + +

    Swap Space + {{swapPercentage}} +

    +
    + +
    + {{swapMax}} + +
    + {{swapTotalDisplay}} + +
    + +
    + +
    + +
    + +
    + +

    File Descriptor Count + {{fileDescriptorPercentage}} +

    +
    + +
    + {{system.system.maxFileDescriptorCount}} + +
    + {{system.system.openFileDescriptorCount}} + +
    + +
    + +
    + +
    + +

    Sorry, no information available

    + +
    + +
    + +
    + +
    + +
    + +
    + +
    + +

    JVM

    + +
    + +
      + +
    • +
      Runtime
      +
      {{system.jvm.name}} {{system.jvm.version}}
      +
    • + +
    • +
      Processors
      +
      {{system.jvm.processors}}
      +
    • + +
    • +
      Args
      +
      {{arg}}
      +
    • + +
    + +
    + +
    + +
    +
    + +
    + +

    JVM-Memory + {{javaMemoryPercentage}} +

    + +
    + +
    +
    + +
    + {{javaMemoryMax}} + +
    + {{javaMemoryTotalDisplay}} + +
    + {{javaMemoryUsedDisplay}} + +
    + +
    + +
    + +
    +
    + +
    + +
    +
    + +

    Security

    + +
    + +
      + +
    • +
      Authentication Plugin
      +
      {{system.security["authenticationPlugin"]}}
      +
    • + +
    • +
      Authorization Plugin
      +
      {{system.security["authorizationPlugin"]}}
      +
    • + +
    • +
      Current Username
      +
      {{system.security["username"]}}
      +
    • + +
    • +
      User Roles
      +
      {{system.security["roles"]}}
      +
    • + +
    + + +
    + +
    + +
    + +
    + +
    diff --git a/KeywordSearch/solr/server/solr-webapp/webapp/partials/java-properties.html b/KeywordSearch/solr/server/solr-webapp/webapp/partials/java-properties.html new file mode 100755 index 0000000000..9c67c7b759 --- /dev/null +++ b/KeywordSearch/solr/server/solr-webapp/webapp/partials/java-properties.html @@ -0,0 +1,27 @@ + +
    +
      +
    • +
      +
      +
      {{val}}{{pathSeparator}}{{"​"}}
      +
      +
    • +
    +
    + diff --git a/KeywordSearch/solr/server/solr-webapp/webapp/partials/logging-levels.html b/KeywordSearch/solr/server/solr-webapp/webapp/partials/logging-levels.html new file mode 100755 index 0000000000..14c6c085dd --- /dev/null +++ b/KeywordSearch/solr/server/solr-webapp/webapp/partials/logging-levels.html @@ -0,0 +1,55 @@ + +
    +
    +
    +

    {{watcher}}

    +
    +
      +
    • +
    +
    +
    +
    +
    + + diff --git a/KeywordSearch/solr/server/solr-webapp/webapp/partials/logging.html b/KeywordSearch/solr/server/solr-webapp/webapp/partials/logging.html new file mode 100755 index 0000000000..9952360b30 --- /dev/null +++ b/KeywordSearch/solr/server/solr-webapp/webapp/partials/logging.html @@ -0,0 +1,57 @@ + +
    + +
    +
    +

    {{watcher}}

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Time ({{timezone}})LevelCoreLoggerMessage
    {{ timezone == "UTC" ? event.utc_time : event.local_time }}{{ event.level }} {{event.showTrace}}{{ event.core }}{{event.loggerBase}}{{ event.message }}
    {{event.trace}}
    No Events available
    + +
    +
    diff --git a/KeywordSearch/solr/server/solr-webapp/webapp/partials/login.html b/KeywordSearch/solr/server/solr-webapp/webapp/partials/login.html new file mode 100755 index 0000000000..29c8c71501 --- /dev/null +++ b/KeywordSearch/solr/server/solr-webapp/webapp/partials/login.html @@ -0,0 +1,160 @@ + +
    + +
    +

    Basic Authentication

    + +
    +

    + Solr requires authentication for resource {{authLocation === '/' ? 'Dashboard' : authLocation}}.
    + Please log in with your username and password for realm {{authRealm}}. +

    +
    +
    {{error}}
    +
    +
    + + + Username is required +
    +
    + + + Password is required +
    +
    +
    + +
    +
    +
    + +
    +

    + Logged in as user {{authLoggedinUser}}. Realm={{authRealm}}.
    +

    +
    +
    +
    + +
    +
    +
    + +
    + +
    +

    Kerberos Authentication

    +

    Your browser did not provide the required information to authenticate using Kerberos. + Please check that your computer has a valid ticket for communicating with Solr, + and that your browser is properly configured to provide that ticket when required. + For more information, consult + + Solr's Kerberos documentation + . +

    + The response from the server was: +
    +
    HTTP 401 {{statusText}}
    +WWW-Authenticate: {{wwwAuthHeader}}
    +
    +
    + +
    +

    OpenID Connect (JWT) authentication

    + +
    + Callback from ID Provider received. +

    + There were errors during login with ID Provider. Please try again.
    +

    +
    +
    +

    + Solr requires authentication for resource {{authLocation === '/' ? 'Dashboard' : authLocation}}. +

    +
    +

    + Please log in with your Identity Provider (IdP) for realm {{authRealm}}. +

    +

    + Clicking the button below, you will be redirected to the authorization endpoint of the ID provider:
    + {{authData['authorizationEndpoint']}} +

    +
    +
    {{error}}
    +
    +
    + +
    +
    +
    +
    +

    + In order to log in to the identity provider, you need to load this page from the Solr node registered as callback node:
    + {{jwtFindLoginNode()}}
    + After successful login you will be able to navigate to other nodes. +

    +

    +

    +
    + +
    +
    +

    +
    + +
    + +
    +

    + Logged in as user {{authLoggedinUser}}. Realm={{authRealm}}.
    +

    +
    +
    +
    + +
    +
    +
    + +
    + +
    +

    Authentication scheme not supported

    + + + +

    Some or all Solr operations are protected by an authentication scheme that is not yet supported by this Admin UI ({{authScheme}}).

    +

    Solr returned an error response: +


    +
    HTTP 401 {{statusText}}
    +WWW-Authenticate: {{wwwAuthHeader}}
    +
    +

    +

    A possible workaround may be to use another client that supports this scheme.

    +
    +
    diff --git a/KeywordSearch/solr/server/solr-webapp/webapp/partials/plugins.html b/KeywordSearch/solr/server/solr-webapp/webapp/partials/plugins.html new file mode 100755 index 0000000000..aaa424d3b8 --- /dev/null +++ b/KeywordSearch/solr/server/solr-webapp/webapp/partials/plugins.html @@ -0,0 +1,72 @@ + +
    + +
    +
      +
    • + + {{ plugin.name }} + +
        +
      • +
        +
        {{ key }}:
        + +
        {{value}}
        +
        +
      • +
      • + stats: +
          +
        • +
          +
          {{key}}:
          +
          {{value}}
          +
          +
        • +
        +
      • +
      +
    +
    + + + +
    +
    + +

    Watching for Changes

    + + +
    +
    +
    + +
    diff --git a/KeywordSearch/solr/server/solr-webapp/webapp/partials/query.html b/KeywordSearch/solr/server/solr-webapp/webapp/partials/query.html new file mode 100755 index 0000000000..3d329ea77e --- /dev/null +++ b/KeywordSearch/solr/server/solr-webapp/webapp/partials/query.html @@ -0,0 +1,369 @@ + +
    + +
    +
    + + + +
    + common +
    + + + + + +
    +
    + +
    + + +
    +
    +
    + + + + + +
    + + +
    + + + + + + + + + + + + + + + +
    +
    + +
    + + + +
    + + + +
    +
    + +
    + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + +
    + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + +
    + + + +
    + + + + + + + + + + + + + + + + +
    +
    + +
    + + + +
    + + + + + + + + + + +
    +
    + +
    + + + +
    + + + + + + + + + + +
    +
    + +
    + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + +
    +
    + +
    + + {{hostPortContext}}{{url}} + +
    +
    +
    + +
    + +
    diff --git a/KeywordSearch/solr/server/solr-webapp/webapp/partials/replication.html b/KeywordSearch/solr/server/solr-webapp/webapp/partials/replication.html new file mode 100755 index 0000000000..b3d668422a --- /dev/null +++ b/KeywordSearch/solr/server/solr-webapp/webapp/partials/replication.html @@ -0,0 +1,239 @@ + +
    + +
    + +
    {{ progress.ERROR }}
    + +
    + +
    + +
    + + {{progress.replicationStartTime}} + +
    + +
    + + 5.1 MB/s + +
    + +
    + +
    + +
    {{progress.numFilesToDownload}} Files
    +
    {{progress.bytesToDownload}}
    + +
    + +
    + + ETA: {{progress.timeRemaining | readableSeconds }} + +
    + +
    + +
    + + {{progress.totalPercent}}% + +
    + +
    + +
    {{progress.numFilesDownloaded}} Files
    +
    {{progress.bytesDownloaded}}
    + +
    + +
    + +
    + +
    + +
    + +
    Current File:
    +
    {{progress.currentFile}}
    +
    + {{progress.currentFileSizeDownloaded}} / {{progress.currentFileSize}} [{{progress.currentFileSizePercent}}%] +
    + +
    + +
    + +
    + +
    Iterations:
    +
    +
      +
    • {{iteration.date}}
    • +
    + + Show all Iterations + Hide past Iterations + + +
    + +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    IndexVersionGenSize
    Master (Searching) +
    {{versions.masterSearch.version}}
    +
    +
    {{versions.masterSearch.generation}}
    +
    +
    {{versions.masterSearch.size}}
    +
    Master (Replicable) +
    {{versions.master.version}}
    +
    +
    {{versions.master.generation}}
    +
    +
    {{versions.master.size}}
    +
    Slave (Searching) +
    {{versions.slave.version}}
    +
    +
    {{versions.slave.generation}}
    +
    +
    {{versions.slave.size}}
    +
    + +
    + +
    + +
    Settings:
    +
      +
    • +
      +
      master url:
      +
      {{settings.masterUrl}}
      +
      +
    • +
    • +
      polling enable:
      +
      + (interval: {{settings.pollInterval}})  +
      +
    • +
    + +
    + +
    + +
    Settings (Master):
    +
      +
    • +
      replication enable:
      +
       
      +
    • +
    • +
      replicateAfter:
      +
      {{master.replicateAfter}}
      +
    • +
    • +
      confFiles:
      +
      {{file.name}}{{ $last ? '' :', '}}
      +
    • +
    + +
    + +
    + + + +
    diff --git a/KeywordSearch/solr/server/solr-webapp/webapp/partials/schema.html b/KeywordSearch/solr/server/solr-webapp/webapp/partials/schema.html new file mode 100755 index 0000000000..e94b6dab47 --- /dev/null +++ b/KeywordSearch/solr/server/solr-webapp/webapp/partials/schema.html @@ -0,0 +1,455 @@ + +
    + +
    + +
    +
    + + + + +
    + +

    +

    + +

    + +

    + +

    +

    + +

    + +

    + +

    + +

    + +

    + +

    + +

    + +

    + +

    + +

    + +

    + +

    + +

    + + Show omit options + Hide omit options + +

    + +
    + +

    + +

    + +

    + +

    + +

    + +

    +
    + +

    + + Show term vector options + Hide term vector options + +

    +
    + +

    + +

    + +

    + +

    + +

    + +

    + +

    + +

    + +
    + +

    + + Show sort options + Show sort options + +

    +
    +

    + +

    + +

    + +

    + +
    + +
    + {{error}}
    + +

    + + + +

    + + + +
    + +
    + +
    + +

    +

    + +

    +

    + +
    + {{error}}
    + +

    + + +

    + +
    + +
    +
    + +
    + +
    + +
    + +
    +

    + {{selectedType}}: + {{name}} +

    +
    + +
    + +

    Because your Index is empty, we do not have enough Information about this Field

    + +
    + +
    + +
    Field-Type:
    +
    {{analysis.data.className}}
    + +
    Similarity:
    +
    {{ display.similarity.details }}
    + +
    PI Gap:
    +
    {{ display.positionIncrementGap }}
    + +
    Docs:
    +
    {{display.docs | number}}
    + +
    Distinct:
    +
    {{display.distinct}}
    + +
    + + + + + + + + + + + + + + + + + + + + + +
    Flags:{{key.name}}
    {{row.name}}{{row.comment}} + +   +
    + + + +
    + +
    + +
    + + +
    + N.B. Loaded from a single core - not from the whole collection. + + Autoload + +
    + +

    Sorry, no Term Info available :(

    + +
    + +
    +

    + + /{{termInfo.maxTerms | number}} Top-Terms: + Query  +

    +
    + +
      +
    • +

      {{countGroup.count}}

      + +
    • + +
    + +
    + +
    + +

    Histogram:

    +
      +
    • +
      +
      {{ row.key | number}}
      +
      {{ row.value | number }}
      +
      +
    • + +
    + +
    + +
    + +
    + +
    + + +
    + +
    + +
    diff --git a/KeywordSearch/solr/server/solr-webapp/webapp/partials/segments.html b/KeywordSearch/solr/server/solr-webapp/webapp/partials/segments.html new file mode 100755 index 0000000000..e1174aba56 --- /dev/null +++ b/KeywordSearch/solr/server/solr-webapp/webapp/partials/segments.html @@ -0,0 +1,99 @@ + +
    +
    + +
    + +

    Segments

    +

    Auto-Refresh

    + reload + +
    +
    +
    + +
    + +
    + +
    + +
    + +
      +
    • +
      +
      +
      Size
      +
      +
      +
      0
      +
      + {{x.value}} +   +
      +
      {{segmentMB | number}} MB
      +
      +
      +
    • +
    • +
      +
      +
      {{ segment.name }}
      +
      +
      +
       
      +
      +
      Segment {{segment.name}}:
      +
      #docs:
      +
      {{ segment.size | number }}
      +
      #dels:
      +
      {{ segment.delCount | number }}
      +
      size:
      +
      {{ segment.sizeInBytes | number }} bytes
      +
      age:
      +
      {{ segment.age }}
      +
      source:
      +
      {{ segment.source }}
      +
      +
      +  
      +
      +
      +
    • +
    • +
      +
      +
      Deletions: {{ deletionsPercentage }}%
      +
      +
    • +
    + +
    + +
    + +
    +
    +
    + +
    + +
    diff --git a/KeywordSearch/solr/server/solr-webapp/webapp/partials/stream.html b/KeywordSearch/solr/server/solr-webapp/webapp/partials/stream.html new file mode 100755 index 0000000000..683cf76d94 --- /dev/null +++ b/KeywordSearch/solr/server/solr-webapp/webapp/partials/stream.html @@ -0,0 +1,64 @@ + +
    + +
    +
    + + + + + + + +
    +
    + +
    + + {{hostPortContext}}{{url}} + +
    +
    +
    +
    + + + Stream Decorator + + Stream Source + + Graph Source + + Datastore + +
    +
    +
    +
    +
    + +
    +
    +
    +
    + +
    diff --git a/KeywordSearch/solr/server/solr-webapp/webapp/partials/threads.html b/KeywordSearch/solr/server/solr-webapp/webapp/partials/threads.html new file mode 100755 index 0000000000..f1e6fdeb29 --- /dev/null +++ b/KeywordSearch/solr/server/solr-webapp/webapp/partials/threads.html @@ -0,0 +1,65 @@ + +
    + + + +
    + + + + + + + + + + + + + + + + + + + + + +
    namecpuTime / userTime
    + {{thread.name}} ({{thread.id}}) +

    {{thread.lock}}

    +
    +
      +
    • {{trace.trace}}
    • +
    +
    +
    {{thread.cpuTime}}
    {{thread.userTime}}
    + +
    + + + +
    diff --git a/KeywordSearch/solr/server/solr-webapp/webapp/partials/unknown.html b/KeywordSearch/solr/server/solr-webapp/webapp/partials/unknown.html new file mode 100755 index 0000000000..51895ab8b5 --- /dev/null +++ b/KeywordSearch/solr/server/solr-webapp/webapp/partials/unknown.html @@ -0,0 +1,23 @@ + +
    + +
    + Oops, this URL is unknown to us, redirecting you back to Dashboard +
    + +
    diff --git a/KeywordSearch/solr/solr/configsets/AutopsyConfig/conf/admin-extra.html b/KeywordSearch/solr/server/solr/configsets/AutopsyConfig/conf/admin-extra.html old mode 100644 new mode 100755 similarity index 100% rename from KeywordSearch/solr/solr/configsets/AutopsyConfig/conf/admin-extra.html rename to KeywordSearch/solr/server/solr/configsets/AutopsyConfig/conf/admin-extra.html diff --git a/KeywordSearch/solr/solr/configsets/AutopsyConfig/conf/elevate.xml b/KeywordSearch/solr/server/solr/configsets/AutopsyConfig/conf/elevate.xml old mode 100644 new mode 100755 similarity index 100% rename from KeywordSearch/solr/solr/configsets/AutopsyConfig/conf/elevate.xml rename to KeywordSearch/solr/server/solr/configsets/AutopsyConfig/conf/elevate.xml diff --git a/KeywordSearch/solr/solr/configsets/AutopsyConfig/conf/lang/stoptags_ja.txt b/KeywordSearch/solr/server/solr/configsets/AutopsyConfig/conf/lang/stoptags_ja.txt similarity index 100% rename from KeywordSearch/solr/solr/configsets/AutopsyConfig/conf/lang/stoptags_ja.txt rename to KeywordSearch/solr/server/solr/configsets/AutopsyConfig/conf/lang/stoptags_ja.txt diff --git a/KeywordSearch/solr/solr/configsets/AutopsyConfig/conf/lang/stopwords_ja.txt b/KeywordSearch/solr/server/solr/configsets/AutopsyConfig/conf/lang/stopwords_ja.txt similarity index 100% rename from KeywordSearch/solr/solr/configsets/AutopsyConfig/conf/lang/stopwords_ja.txt rename to KeywordSearch/solr/server/solr/configsets/AutopsyConfig/conf/lang/stopwords_ja.txt diff --git a/KeywordSearch/solr/solr/configsets/AutopsyConfig/conf/logging-development.properties b/KeywordSearch/solr/server/solr/configsets/AutopsyConfig/conf/logging-development.properties old mode 100644 new mode 100755 similarity index 100% rename from KeywordSearch/solr/solr/configsets/AutopsyConfig/conf/logging-development.properties rename to KeywordSearch/solr/server/solr/configsets/AutopsyConfig/conf/logging-development.properties diff --git a/KeywordSearch/solr/solr/configsets/AutopsyConfig/conf/logging-release.properties b/KeywordSearch/solr/server/solr/configsets/AutopsyConfig/conf/logging-release.properties old mode 100644 new mode 100755 similarity index 100% rename from KeywordSearch/solr/solr/configsets/AutopsyConfig/conf/logging-release.properties rename to KeywordSearch/solr/server/solr/configsets/AutopsyConfig/conf/logging-release.properties diff --git a/KeywordSearch/solr/solr/configsets/AutopsyConfig/conf/mapping-FoldToASCII.txt b/KeywordSearch/solr/server/solr/configsets/AutopsyConfig/conf/mapping-FoldToASCII.txt old mode 100644 new mode 100755 similarity index 100% rename from KeywordSearch/solr/solr/configsets/AutopsyConfig/conf/mapping-FoldToASCII.txt rename to KeywordSearch/solr/server/solr/configsets/AutopsyConfig/conf/mapping-FoldToASCII.txt diff --git a/KeywordSearch/solr/solr/configsets/AutopsyConfig/conf/mapping-ISOLatin1Accent.txt b/KeywordSearch/solr/server/solr/configsets/AutopsyConfig/conf/mapping-ISOLatin1Accent.txt old mode 100644 new mode 100755 similarity index 100% rename from KeywordSearch/solr/solr/configsets/AutopsyConfig/conf/mapping-ISOLatin1Accent.txt rename to KeywordSearch/solr/server/solr/configsets/AutopsyConfig/conf/mapping-ISOLatin1Accent.txt diff --git a/KeywordSearch/solr/solr/configsets/AutopsyConfig/conf/protwords.txt b/KeywordSearch/solr/server/solr/configsets/AutopsyConfig/conf/protwords.txt old mode 100644 new mode 100755 similarity index 100% rename from KeywordSearch/solr/solr/configsets/AutopsyConfig/conf/protwords.txt rename to KeywordSearch/solr/server/solr/configsets/AutopsyConfig/conf/protwords.txt diff --git a/KeywordSearch/solr/server/solr/configsets/AutopsyConfig/conf/schema.xml b/KeywordSearch/solr/server/solr/configsets/AutopsyConfig/conf/schema.xml new file mode 100755 index 0000000000..3bebad7be7 --- /dev/null +++ b/KeywordSearch/solr/server/solr/configsets/AutopsyConfig/conf/schema.xml @@ -0,0 +1,702 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + id + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/KeywordSearch/solr/solr/configsets/AutopsyConfig/conf/scripts.conf b/KeywordSearch/solr/server/solr/configsets/AutopsyConfig/conf/scripts.conf old mode 100644 new mode 100755 similarity index 100% rename from KeywordSearch/solr/solr/configsets/AutopsyConfig/conf/scripts.conf rename to KeywordSearch/solr/server/solr/configsets/AutopsyConfig/conf/scripts.conf diff --git a/KeywordSearch/solr/server/solr/configsets/AutopsyConfig/conf/solrconfig.xml b/KeywordSearch/solr/server/solr/configsets/AutopsyConfig/conf/solrconfig.xml new file mode 100755 index 0000000000..9fde79cd36 --- /dev/null +++ b/KeywordSearch/solr/server/solr/configsets/AutopsyConfig/conf/solrconfig.xml @@ -0,0 +1,1789 @@ + + + + + + + + + 8.6.3 + + + + + + + + + + + + + + + + + + + + + + + + ${solr.data.dir:} + + + + + + + + + + + + + 10000 + + + false + + + 256 + + + + + + + + + + + + native + + + + + + true + + + + + + + 1 + + 0 + + + + + + false + + + + + + + + + + + + + + + + 300000 + + true + + + + + + + + + + + + + ${solr.ulog.dir:} + ${solr.ulog.numVersionBuckets:65536} + + + + + + + + + + + + + 3000 + + + 1024 + + + + + + + + + + + + + + + + + + + + + + true + + + + + + 5 + + + 16 + + + + + + + + + + + + static firstSearcher warming in solrconfig.xml + + + + + + false + + + 2 + + + + + + + + + + + + + + + + + + + + + + + explicit + 10 + text + + + + + + + + + + + + + + explicit + json + true + text + + + + + + + + true + json + true + + + + + + + + explicit + + + velocity + browse + layout + Solritas + + + edismax + + text^0.5 features^1.0 name^1.2 sku^1.5 id^10.0 manu^1.1 cat^1.4 + title^10.0 description^5.0 keywords^5.0 author^2.0 resourcename^1.0 + + text + 100% + *:* + 10 + *,score + + + text^0.5 features^1.0 name^1.2 sku^1.5 id^10.0 manu^1.1 cat^1.4 + title^10.0 description^5.0 keywords^5.0 author^2.0 resourcename^1.0 + + text,features,name,sku,id,manu,cat,title,description,keywords,author,resourcename + 3 + + + + + + on + text content file_name + html + 0 + file_name + + <b> + </b> + 0 + title + + 3 + 200 + content + 750 + + + + + + + + + spellcheck + + + + + + + + + + + + + + + + + application/json + + + + + application/csv + + + + + + + true + ignored_ + + + true + links + ignored_ + + + + + + + + + + + + + + + + + + + + + + + + search + solrpingquery + + + all + + + + + + + + + explicit + true + + + + + + + + + + + + + + + + textSpell + + + + + + default + name + solr.DirectSolrSpellChecker + + internal + + 0.5 + + 2 + + 1 + + 5 + + 4 + + 0.01 + + + + + + wordbreak + solr.WordBreakSolrSpellChecker + name + true + true + 10 + + + + + + + + + + + + + + + + text + + default + wordbreak + on + true + 10 + 5 + 5 + true + true + 10 + 5 + + + spellcheck + + + + + + + + + + text + true + + + tvComponent + + + + + + + + + default + + + org.carrot2.clustering.lingo.LingoClusteringAlgorithm + + + 20 + + + clustering/carrot2 + + + ENGLISH + + + stc + org.carrot2.clustering.stc.STCClusteringAlgorithm + + + + + + + true + default + true + + name + id + + features + + true + + + + false + + edismax + + text^0.5 features^1.0 name^1.2 sku^1.5 id^10.0 manu^1.1 cat^1.4 + + *:* + 10 + *,score + + + clustering + + + + + + + + + + true + false + + + terms + + + + + + + + string + elevate.xml + + + + + + explicit + text + + + elevator + + + + + + + + + + + 100 + + + + + + + + 70 + + 0.5 + + [-\w ,/\n\"']{20,200} + + + + + + + ]]> + ]]> + + + + + + + + + + + + + + + + + + + + + + + + ,, + ,, + ,, + ,, + ,]]> + ]]> + + + + + + 10 + .,!? + + + + + + + WORD + + + en + US + + + + + + + + + + + + + + + + + + + + + + text/plain; charset=UTF-8 + + + + + + + + + 5 + + + + + + + + + + + + + + + + + + *:* + + + diff --git a/KeywordSearch/solr/solr/configsets/AutopsyConfig/conf/spellings.txt b/KeywordSearch/solr/server/solr/configsets/AutopsyConfig/conf/spellings.txt old mode 100644 new mode 100755 similarity index 100% rename from KeywordSearch/solr/solr/configsets/AutopsyConfig/conf/spellings.txt rename to KeywordSearch/solr/server/solr/configsets/AutopsyConfig/conf/spellings.txt diff --git a/KeywordSearch/solr/solr/configsets/AutopsyConfig/conf/stopwords.txt b/KeywordSearch/solr/server/solr/configsets/AutopsyConfig/conf/stopwords.txt old mode 100644 new mode 100755 similarity index 100% rename from KeywordSearch/solr/solr/configsets/AutopsyConfig/conf/stopwords.txt rename to KeywordSearch/solr/server/solr/configsets/AutopsyConfig/conf/stopwords.txt diff --git a/KeywordSearch/solr/solr/configsets/AutopsyConfig/conf/stopwords_en.txt b/KeywordSearch/solr/server/solr/configsets/AutopsyConfig/conf/stopwords_en.txt old mode 100644 new mode 100755 similarity index 100% rename from KeywordSearch/solr/solr/configsets/AutopsyConfig/conf/stopwords_en.txt rename to KeywordSearch/solr/server/solr/configsets/AutopsyConfig/conf/stopwords_en.txt diff --git a/KeywordSearch/solr/solr/configsets/AutopsyConfig/conf/synonyms.txt b/KeywordSearch/solr/server/solr/configsets/AutopsyConfig/conf/synonyms.txt old mode 100644 new mode 100755 similarity index 100% rename from KeywordSearch/solr/solr/configsets/AutopsyConfig/conf/synonyms.txt rename to KeywordSearch/solr/server/solr/configsets/AutopsyConfig/conf/synonyms.txt diff --git a/KeywordSearch/solr/server/solr/solr.xml b/KeywordSearch/solr/server/solr/solr.xml new file mode 100755 index 0000000000..1341e89836 --- /dev/null +++ b/KeywordSearch/solr/server/solr/solr.xml @@ -0,0 +1,58 @@ + + + + + + + + ${solr.max.booleanClauses:1024} + ${solr.sharedLib:} + ${solr.allowPaths:*} + + + + ${host:} + ${jetty.port:8983} + ${hostContext:solr} + + ${genericCoreNodeNames:true} + + ${zkClientTimeout:60000} + ${distribUpdateSoTimeout:600000} + ${distribUpdateConnTimeout:600000} + ${zkCredentialsProvider:org.apache.solr.common.cloud.DefaultZkCredentialsProvider} + ${zkACLProvider:org.apache.solr.common.cloud.DefaultZkACLProvider} + + + + + ${socketTimeout:600000} + ${connTimeout:600000} + ${solr.shardsWhitelist:} + + + diff --git a/KeywordSearch/solr/server/solr/zoo.cfg b/KeywordSearch/solr/server/solr/zoo.cfg new file mode 100755 index 0000000000..d0cfaefbf8 --- /dev/null +++ b/KeywordSearch/solr/server/solr/zoo.cfg @@ -0,0 +1,34 @@ +# The number of milliseconds of each tick +tickTime=2000 +# The number of ticks that the initial +# synchronization phase can take +initLimit=10 +# The number of ticks that can pass between +# sending a request and getting an acknowledgement +syncLimit=5 + +# the directory where the snapshot is stored. +# dataDir=/opt/zookeeper/data +# NOTE: Solr defaults the dataDir to /zoo_data + +# the port at which the clients will connect +# clientPort=2181 +# NOTE: Solr sets this based on zkRun / zkHost params + +# the maximum number of client connections. +# increase this if you need to handle more clients +#maxClientCnxns=60 +# +# Be sure to read the maintenance section of the +# administrator guide before turning on autopurge. +# +# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance +# +# The number of snapshots to retain in dataDir +#autopurge.snapRetainCount=3 +# Purge task interval in hours +# Set to "0" to disable auto purge feature +#autopurge.purgeInterval=1 + +# Disable ZK AdminServer since we do not use it +admin.enableServer=false diff --git a/KeywordSearch/solr/server/start.jar b/KeywordSearch/solr/server/start.jar new file mode 100755 index 0000000000..49aa128dd7 Binary files /dev/null and b/KeywordSearch/solr/server/start.jar differ diff --git a/KeywordSearch/solr/contexts/solr-jetty-context.xml b/KeywordSearch/solr4/contexts/solr-jetty-context.xml old mode 100644 new mode 100755 similarity index 100% rename from KeywordSearch/solr/contexts/solr-jetty-context.xml rename to KeywordSearch/solr4/contexts/solr-jetty-context.xml diff --git a/KeywordSearch/solr/etc/jetty.xml b/KeywordSearch/solr4/etc/jetty.xml old mode 100644 new mode 100755 similarity index 100% rename from KeywordSearch/solr/etc/jetty.xml rename to KeywordSearch/solr4/etc/jetty.xml diff --git a/KeywordSearch/solr/etc/webdefault.xml b/KeywordSearch/solr4/etc/webdefault.xml old mode 100644 new mode 100755 similarity index 100% rename from KeywordSearch/solr/etc/webdefault.xml rename to KeywordSearch/solr4/etc/webdefault.xml diff --git a/KeywordSearch/solr/resources/log4j.properties b/KeywordSearch/solr4/resources/log4j.properties old mode 100644 new mode 100755 similarity index 100% rename from KeywordSearch/solr/resources/log4j.properties rename to KeywordSearch/solr4/resources/log4j.properties diff --git a/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/admin-extra.html b/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/admin-extra.html new file mode 100755 index 0000000000..aa739da862 --- /dev/null +++ b/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/admin-extra.html @@ -0,0 +1,31 @@ + + + diff --git a/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/elevate.xml b/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/elevate.xml new file mode 100755 index 0000000000..7630ebe20f --- /dev/null +++ b/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/elevate.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + diff --git a/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/lang/stoptags_ja.txt b/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/lang/stoptags_ja.txt new file mode 100755 index 0000000000..71b750845e --- /dev/null +++ b/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/lang/stoptags_ja.txt @@ -0,0 +1,420 @@ +# +# This file defines a Japanese stoptag set for JapanesePartOfSpeechStopFilter. +# +# Any token with a part-of-speech tag that exactly matches those defined in this +# file are removed from the token stream. +# +# Set your own stoptags by uncommenting the lines below. Note that comments are +# not allowed on the same line as a stoptag. See LUCENE-3745 for frequency lists, +# etc. that can be useful for building you own stoptag set. +# +# The entire possible tagset is provided below for convenience. +# +##### +# noun: unclassified nouns +#名詞 +# +# noun-common: Common nouns or nouns where the sub-classification is undefined +#名詞-一般 +# +# noun-proper: Proper nouns where the sub-classification is undefined +#名詞-固有名詞 +# +# noun-proper-misc: miscellaneous proper nouns +#名詞-固有名詞-一般 +# +# noun-proper-person: Personal names where the sub-classification is undefined +#名詞-固有名詞-人名 +# +# noun-proper-person-misc: names that cannot be divided into surname and +# given name; foreign names; names where the surname or given name is unknown. +# e.g. お市の方 +#名詞-固有名詞-人名-一般 +# +# noun-proper-person-surname: Mainly Japanese surnames. +# e.g. 山田 +#名詞-固有名詞-人名-姓 +# +# noun-proper-person-given_name: Mainly Japanese given names. +# e.g. 太郎 +#名詞-固有名詞-人名-名 +# +# noun-proper-organization: Names representing organizations. +# e.g. 通産省, NHK +#名詞-固有名詞-組織 +# +# noun-proper-place: Place names where the sub-classification is undefined +#名詞-固有名詞-地域 +# +# noun-proper-place-misc: Place names excluding countries. +# e.g. アジア, バルセロナ, 京都 +#名詞-固有名詞-地域-一般 +# +# noun-proper-place-country: Country names. +# e.g. 日本, オーストラリア +#名詞-固有名詞-地域-国 +# +# noun-pronoun: Pronouns where the sub-classification is undefined +#名詞-代名詞 +# +# noun-pronoun-misc: miscellaneous pronouns: +# e.g. それ, ここ, あいつ, あなた, あちこち, いくつ, どこか, なに, みなさん, みんな, わたくし, われわれ +#名詞-代名詞-一般 +# +# noun-pronoun-contraction: Spoken language contraction made by combining a +# pronoun and the particle 'wa'. +# e.g. ありゃ, こりゃ, こりゃあ, そりゃ, そりゃあ +#名詞-代名詞-縮約 +# +# noun-adverbial: Temporal nouns such as names of days or months that behave +# like adverbs. Nouns that represent amount or ratios and can be used adverbially, +# e.g. 金曜, 一月, 午後, 少量 +#名詞-副詞可能 +# +# noun-verbal: Nouns that take arguments with case and can appear followed by +# 'suru' and related verbs (する, できる, なさる, くださる) +# e.g. インプット, 愛着, 悪化, 悪戦苦闘, 一安心, 下取り +#名詞-サ変接続 +# +# noun-adjective-base: The base form of adjectives, words that appear before な ("na") +# e.g. 健康, 安易, 駄目, だめ +#名詞-形容動詞語幹 +# +# noun-numeric: Arabic numbers, Chinese numerals, and counters like 何 (回), 数. +# e.g. 0, 1, 2, 何, 数, 幾 +#名詞-数 +# +# noun-affix: noun affixes where the sub-classification is undefined +#名詞-非自立 +# +# noun-affix-misc: Of adnominalizers, the case-marker の ("no"), and words that +# attach to the base form of inflectional words, words that cannot be classified +# into any of the other categories below. This category includes indefinite nouns. +# e.g. あかつき, 暁, かい, 甲斐, 気, きらい, 嫌い, くせ, 癖, こと, 事, ごと, 毎, しだい, 次第, +# 順, せい, 所為, ついで, 序で, つもり, 積もり, 点, どころ, の, はず, 筈, はずみ, 弾み, +# 拍子, ふう, ふり, 振り, ほう, 方, 旨, もの, 物, 者, ゆえ, 故, ゆえん, 所以, わけ, 訳, +# わり, 割り, 割, ん-口語/, もん-口語/ +#名詞-非自立-一般 +# +# noun-affix-adverbial: noun affixes that that can behave as adverbs. +# e.g. あいだ, 間, あげく, 挙げ句, あと, 後, 余り, 以外, 以降, 以後, 以上, 以前, 一方, うえ, +# 上, うち, 内, おり, 折り, かぎり, 限り, きり, っきり, 結果, ころ, 頃, さい, 際, 最中, さなか, +# 最中, じたい, 自体, たび, 度, ため, 為, つど, 都度, とおり, 通り, とき, 時, ところ, 所, +# とたん, 途端, なか, 中, のち, 後, ばあい, 場合, 日, ぶん, 分, ほか, 他, まえ, 前, まま, +# 儘, 侭, みぎり, 矢先 +#名詞-非自立-副詞可能 +# +# noun-affix-aux: noun affixes treated as 助動詞 ("auxiliary verb") in school grammars +# with the stem よう(だ) ("you(da)"). +# e.g. よう, やう, 様 (よう) +#名詞-非自立-助動詞語幹 +# +# noun-affix-adjective-base: noun affixes that can connect to the indeclinable +# connection form な (aux "da"). +# e.g. みたい, ふう +#名詞-非自立-形容動詞語幹 +# +# noun-special: special nouns where the sub-classification is undefined. +#名詞-特殊 +# +# noun-special-aux: The そうだ ("souda") stem form that is used for reporting news, is +# treated as 助動詞 ("auxiliary verb") in school grammars, and attach to the base +# form of inflectional words. +# e.g. そう +#名詞-特殊-助動詞語幹 +# +# noun-suffix: noun suffixes where the sub-classification is undefined. +#名詞-接尾 +# +# noun-suffix-misc: Of the nouns or stem forms of other parts of speech that connect +# to ガル or タイ and can combine into compound nouns, words that cannot be classified into +# any of the other categories below. In general, this category is more inclusive than +# 接尾語 ("suffix") and is usually the last element in a compound noun. +# e.g. おき, かた, 方, 甲斐 (がい), がかり, ぎみ, 気味, ぐるみ, (~した) さ, 次第, 済 (ず) み, +# よう, (でき)っこ, 感, 観, 性, 学, 類, 面, 用 +#名詞-接尾-一般 +# +# noun-suffix-person: Suffixes that form nouns and attach to person names more often +# than other nouns. +# e.g. 君, 様, 著 +#名詞-接尾-人名 +# +# noun-suffix-place: Suffixes that form nouns and attach to place names more often +# than other nouns. +# e.g. 町, 市, 県 +#名詞-接尾-地域 +# +# noun-suffix-verbal: Of the suffixes that attach to nouns and form nouns, those that +# can appear before スル ("suru"). +# e.g. 化, 視, 分け, 入り, 落ち, 買い +#名詞-接尾-サ変接続 +# +# noun-suffix-aux: The stem form of そうだ (様態) that is used to indicate conditions, +# is treated as 助動詞 ("auxiliary verb") in school grammars, and attach to the +# conjunctive form of inflectional words. +# e.g. そう +#名詞-接尾-助動詞語幹 +# +# noun-suffix-adjective-base: Suffixes that attach to other nouns or the conjunctive +# form of inflectional words and appear before the copula だ ("da"). +# e.g. 的, げ, がち +#名詞-接尾-形容動詞語幹 +# +# noun-suffix-adverbial: Suffixes that attach to other nouns and can behave as adverbs. +# e.g. 後 (ご), 以後, 以降, 以前, 前後, 中, 末, 上, 時 (じ) +#名詞-接尾-副詞可能 +# +# noun-suffix-classifier: Suffixes that attach to numbers and form nouns. This category +# is more inclusive than 助数詞 ("classifier") and includes common nouns that attach +# to numbers. +# e.g. 個, つ, 本, 冊, パーセント, cm, kg, カ月, か国, 区画, 時間, 時半 +#名詞-接尾-助数詞 +# +# noun-suffix-special: Special suffixes that mainly attach to inflecting words. +# e.g. (楽し) さ, (考え) 方 +#名詞-接尾-特殊 +# +# noun-suffix-conjunctive: Nouns that behave like conjunctions and join two words +# together. +# e.g. (日本) 対 (アメリカ), 対 (アメリカ), (3) 対 (5), (女優) 兼 (主婦) +#名詞-接続詞的 +# +# noun-verbal_aux: Nouns that attach to the conjunctive particle て ("te") and are +# semantically verb-like. +# e.g. ごらん, ご覧, 御覧, 頂戴 +#名詞-動詞非自立的 +# +# noun-quotation: text that cannot be segmented into words, proverbs, Chinese poetry, +# dialects, English, etc. Currently, the only entry for 名詞 引用文字列 ("noun quotation") +# is いわく ("iwaku"). +#名詞-引用文字列 +# +# noun-nai_adjective: Words that appear before the auxiliary verb ない ("nai") and +# behave like an adjective. +# e.g. 申し訳, 仕方, とんでも, 違い +#名詞-ナイ形容詞語幹 +# +##### +# prefix: unclassified prefixes +#接頭詞 +# +# prefix-nominal: Prefixes that attach to nouns (including adjective stem forms) +# excluding numerical expressions. +# e.g. お (水), 某 (氏), 同 (社), 故 (~氏), 高 (品質), お (見事), ご (立派) +#接頭詞-名詞接続 +# +# prefix-verbal: Prefixes that attach to the imperative form of a verb or a verb +# in conjunctive form followed by なる/なさる/くださる. +# e.g. お (読みなさい), お (座り) +#接頭詞-動詞接続 +# +# prefix-adjectival: Prefixes that attach to adjectives. +# e.g. お (寒いですねえ), バカ (でかい) +#接頭詞-形容詞接続 +# +# prefix-numerical: Prefixes that attach to numerical expressions. +# e.g. 約, およそ, 毎時 +#接頭詞-数接続 +# +##### +# verb: unclassified verbs +#動詞 +# +# verb-main: +#動詞-自立 +# +# verb-auxiliary: +#動詞-非自立 +# +# verb-suffix: +#動詞-接尾 +# +##### +# adjective: unclassified adjectives +#形容詞 +# +# adjective-main: +#形容詞-自立 +# +# adjective-auxiliary: +#形容詞-非自立 +# +# adjective-suffix: +#形容詞-接尾 +# +##### +# adverb: unclassified adverbs +#副詞 +# +# adverb-misc: Words that can be segmented into one unit and where adnominal +# modification is not possible. +# e.g. あいかわらず, 多分 +#副詞-一般 +# +# adverb-particle_conjunction: Adverbs that can be followed by の, は, に, +# な, する, だ, etc. +# e.g. こんなに, そんなに, あんなに, なにか, なんでも +#副詞-助詞類接続 +# +##### +# adnominal: Words that only have noun-modifying forms. +# e.g. この, その, あの, どの, いわゆる, なんらかの, 何らかの, いろんな, こういう, そういう, ああいう, +# どういう, こんな, そんな, あんな, どんな, 大きな, 小さな, おかしな, ほんの, たいした, +# 「(, も) さる (ことながら)」, 微々たる, 堂々たる, 単なる, いかなる, 我が」「同じ, 亡き +#連体詞 +# +##### +# conjunction: Conjunctions that can occur independently. +# e.g. が, けれども, そして, じゃあ, それどころか +接続詞 +# +##### +# particle: unclassified particles. +助詞 +# +# particle-case: case particles where the subclassification is undefined. +助詞-格助詞 +# +# particle-case-misc: Case particles. +# e.g. から, が, で, と, に, へ, より, を, の, にて +助詞-格助詞-一般 +# +# particle-case-quote: the "to" that appears after nouns, a person’s speech, +# quotation marks, expressions of decisions from a meeting, reasons, judgements, +# conjectures, etc. +# e.g. ( だ) と (述べた.), ( である) と (して執行猶予...) +助詞-格助詞-引用 +# +# particle-case-compound: Compounds of particles and verbs that mainly behave +# like case particles. +# e.g. という, といった, とかいう, として, とともに, と共に, でもって, にあたって, に当たって, に当って, +# にあたり, に当たり, に当り, に当たる, にあたる, において, に於いて,に於て, における, に於ける, +# にかけ, にかけて, にかんし, に関し, にかんして, に関して, にかんする, に関する, に際し, +# に際して, にしたがい, に従い, に従う, にしたがって, に従って, にたいし, に対し, にたいして, +# に対して, にたいする, に対する, について, につき, につけ, につけて, につれ, につれて, にとって, +# にとり, にまつわる, によって, に依って, に因って, により, に依り, に因り, による, に依る, に因る, +# にわたって, にわたる, をもって, を以って, を通じ, を通じて, を通して, をめぐって, をめぐり, をめぐる, +# って-口語/, ちゅう-関西弁「という」/, (何) ていう (人)-口語/, っていう-口語/, といふ, とかいふ +助詞-格助詞-連語 +# +# particle-conjunctive: +# e.g. から, からには, が, けれど, けれども, けど, し, つつ, て, で, と, ところが, どころか, とも, ども, +# ながら, なり, ので, のに, ば, ものの, や ( した), やいなや, (ころん) じゃ(いけない)-口語/, +# (行っ) ちゃ(いけない)-口語/, (言っ) たって (しかたがない)-口語/, (それがなく)ったって (平気)-口語/ +助詞-接続助詞 +# +# particle-dependency: +# e.g. こそ, さえ, しか, すら, は, も, ぞ +助詞-係助詞 +# +# particle-adverbial: +# e.g. がてら, かも, くらい, 位, ぐらい, しも, (学校) じゃ(これが流行っている)-口語/, +# (それ)じゃあ (よくない)-口語/, ずつ, (私) なぞ, など, (私) なり (に), (先生) なんか (大嫌い)-口語/, +# (私) なんぞ, (先生) なんて (大嫌い)-口語/, のみ, だけ, (私) だって-口語/, だに, +# (彼)ったら-口語/, (お茶) でも (いかが), 等 (とう), (今後) とも, ばかり, ばっか-口語/, ばっかり-口語/, +# ほど, 程, まで, 迄, (誰) も (が)([助詞-格助詞] および [助詞-係助詞] の前に位置する「も」) +助詞-副助詞 +# +# particle-interjective: particles with interjective grammatical roles. +# e.g. (松島) や +助詞-間投助詞 +# +# particle-coordinate: +# e.g. と, たり, だの, だり, とか, なり, や, やら +助詞-並立助詞 +# +# particle-final: +# e.g. かい, かしら, さ, ぜ, (だ)っけ-口語/, (とまってる) で-方言/, な, ナ, なあ-口語/, ぞ, ね, ネ, +# ねぇ-口語/, ねえ-口語/, ねん-方言/, の, のう-口語/, や, よ, ヨ, よぉ-口語/, わ, わい-口語/ +助詞-終助詞 +# +# particle-adverbial/conjunctive/final: The particle "ka" when unknown whether it is +# adverbial, conjunctive, or sentence final. For example: +# (a) 「A か B か」. Ex:「(国内で運用する) か,(海外で運用する) か (.)」 +# (b) Inside an adverb phrase. Ex:「(幸いという) か (, 死者はいなかった.)」 +# 「(祈りが届いたせい) か (, 試験に合格した.)」 +# (c) 「かのように」. Ex:「(何もなかった) か (のように振る舞った.)」 +# e.g. か +助詞-副助詞/並立助詞/終助詞 +# +# particle-adnominalizer: The "no" that attaches to nouns and modifies +# non-inflectional words. +助詞-連体化 +# +# particle-adnominalizer: The "ni" and "to" that appear following nouns and adverbs +# that are giongo, giseigo, or gitaigo. +# e.g. に, と +助詞-副詞化 +# +# particle-special: A particle that does not fit into one of the above classifications. +# This includes particles that are used in Tanka, Haiku, and other poetry. +# e.g. かな, けむ, ( しただろう) に, (あんた) にゃ(わからん), (俺) ん (家) +助詞-特殊 +# +##### +# auxiliary-verb: +助動詞 +# +##### +# interjection: Greetings and other exclamations. +# e.g. おはよう, おはようございます, こんにちは, こんばんは, ありがとう, どうもありがとう, ありがとうございます, +# いただきます, ごちそうさま, さよなら, さようなら, はい, いいえ, ごめん, ごめんなさい +#感動詞 +# +##### +# symbol: unclassified Symbols. +記号 +# +# symbol-misc: A general symbol not in one of the categories below. +# e.g. [○◎@$〒→+] +記号-一般 +# +# symbol-comma: Commas +# e.g. [,、] +記号-読点 +# +# symbol-period: Periods and full stops. +# e.g. [..。] +記号-句点 +# +# symbol-space: Full-width whitespace. +記号-空白 +# +# symbol-open_bracket: +# e.g. [({‘“『【] +記号-括弧開 +# +# symbol-close_bracket: +# e.g. [)}’”』」】] +記号-括弧閉 +# +# symbol-alphabetic: +#記号-アルファベット +# +##### +# other: unclassified other +#その他 +# +# other-interjection: Words that are hard to classify as noun-suffixes or +# sentence-final particles. +# e.g. (だ)ァ +その他-間投 +# +##### +# filler: Aizuchi that occurs during a conversation or sounds inserted as filler. +# e.g. あの, うんと, えと +フィラー +# +##### +# non-verbal: non-verbal sound. +非言語音 +# +##### +# fragment: +#語断片 +# +##### +# unknown: unknown part of speech. +#未知語 +# +##### End of file diff --git a/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/lang/stopwords_ja.txt b/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/lang/stopwords_ja.txt new file mode 100755 index 0000000000..d4321be6b1 --- /dev/null +++ b/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/lang/stopwords_ja.txt @@ -0,0 +1,127 @@ +# +# This file defines a stopword set for Japanese. +# +# This set is made up of hand-picked frequent terms from segmented Japanese Wikipedia. +# Punctuation characters and frequent kanji have mostly been left out. See LUCENE-3745 +# for frequency lists, etc. that can be useful for making your own set (if desired) +# +# Note that there is an overlap between these stopwords and the terms stopped when used +# in combination with the JapanesePartOfSpeechStopFilter. When editing this file, note +# that comments are not allowed on the same line as stopwords. +# +# Also note that stopping is done in a case-insensitive manner. Change your StopFilter +# configuration if you need case-sensitive stopping. Lastly, note that stopping is done +# using the same character width as the entries in this file. Since this StopFilter is +# normally done after a CJKWidthFilter in your chain, you would usually want your romaji +# entries to be in half-width and your kana entries to be in full-width. +# +の +に +は +を +た +が +で +て +と +し +れ +さ +ある +いる +も +する +から +な +こと +として +い +や +れる +など +なっ +ない +この +ため +その +あっ +よう +また +もの +という +あり +まで +られ +なる +へ +か +だ +これ +によって +により +おり +より +による +ず +なり +られる +において +ば +なかっ +なく +しかし +について +せ +だっ +その後 +できる +それ +う +ので +なお +のみ +でき +き +つ +における +および +いう +さらに +でも +ら +たり +その他 +に関する +たち +ます +ん +なら +に対して +特に +せる +及び +これら +とき +では +にて +ほか +ながら +うち +そして +とともに +ただし +かつて +それぞれ +または +お +ほど +ものの +に対する +ほとんど +と共に +といった +です +とも +ところ +ここ +##### End of file diff --git a/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/logging-development.properties b/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/logging-development.properties new file mode 100755 index 0000000000..34bb6c9b96 --- /dev/null +++ b/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/logging-development.properties @@ -0,0 +1,4 @@ +.level = INFO + +# Write to the console, we will forward it to a file determined at runtime +handlers = java.util.logging.ConsoleHandler \ No newline at end of file diff --git a/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/logging-release.properties b/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/logging-release.properties new file mode 100755 index 0000000000..1aaff5afa2 --- /dev/null +++ b/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/logging-release.properties @@ -0,0 +1,4 @@ +.level = WARNING + +# Write to the console, we will forward it to a file determined at runtime +handlers = java.util.logging.ConsoleHandler \ No newline at end of file diff --git a/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/mapping-FoldToASCII.txt b/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/mapping-FoldToASCII.txt new file mode 100755 index 0000000000..9a84b6eac3 --- /dev/null +++ b/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/mapping-FoldToASCII.txt @@ -0,0 +1,3813 @@ +# The ASF licenses this file to You 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. + + +# This map converts alphabetic, numeric, and symbolic Unicode characters +# which are not in the first 127 ASCII characters (the "Basic Latin" Unicode +# block) into their ASCII equivalents, if one exists. +# +# Characters from the following Unicode blocks are converted; however, only +# those characters with reasonable ASCII alternatives are converted: +# +# - C1 Controls and Latin-1 Supplement: http://www.unicode.org/charts/PDF/U0080.pdf +# - Latin Extended-A: http://www.unicode.org/charts/PDF/U0100.pdf +# - Latin Extended-B: http://www.unicode.org/charts/PDF/U0180.pdf +# - Latin Extended Additional: http://www.unicode.org/charts/PDF/U1E00.pdf +# - Latin Extended-C: http://www.unicode.org/charts/PDF/U2C60.pdf +# - Latin Extended-D: http://www.unicode.org/charts/PDF/UA720.pdf +# - IPA Extensions: http://www.unicode.org/charts/PDF/U0250.pdf +# - Phonetic Extensions: http://www.unicode.org/charts/PDF/U1D00.pdf +# - Phonetic Extensions Supplement: http://www.unicode.org/charts/PDF/U1D80.pdf +# - General Punctuation: http://www.unicode.org/charts/PDF/U2000.pdf +# - Superscripts and Subscripts: http://www.unicode.org/charts/PDF/U2070.pdf +# - Enclosed Alphanumerics: http://www.unicode.org/charts/PDF/U2460.pdf +# - Dingbats: http://www.unicode.org/charts/PDF/U2700.pdf +# - Supplemental Punctuation: http://www.unicode.org/charts/PDF/U2E00.pdf +# - Alphabetic Presentation Forms: http://www.unicode.org/charts/PDF/UFB00.pdf +# - Halfwidth and Fullwidth Forms: http://www.unicode.org/charts/PDF/UFF00.pdf +# +# See: http://en.wikipedia.org/wiki/Latin_characters_in_Unicode +# +# The set of character conversions supported by this map is a superset of +# those supported by the map represented by mapping-ISOLatin1Accent.txt. +# +# See the bottom of this file for the Perl script used to generate the contents +# of this file (without this header) from ASCIIFoldingFilter.java. + + +# Syntax: +# "source" => "target" +# "source".length() > 0 (source cannot be empty.) +# "target".length() >= 0 (target can be empty.) + + +# À [LATIN CAPITAL LETTER A WITH GRAVE] +"\u00C0" => "A" + +# Á [LATIN CAPITAL LETTER A WITH ACUTE] +"\u00C1" => "A" + +#  [LATIN CAPITAL LETTER A WITH CIRCUMFLEX] +"\u00C2" => "A" + +# à [LATIN CAPITAL LETTER A WITH TILDE] +"\u00C3" => "A" + +# Ä [LATIN CAPITAL LETTER A WITH DIAERESIS] +"\u00C4" => "A" + +# Å [LATIN CAPITAL LETTER A WITH RING ABOVE] +"\u00C5" => "A" + +# Ā [LATIN CAPITAL LETTER A WITH MACRON] +"\u0100" => "A" + +# Ă [LATIN CAPITAL LETTER A WITH BREVE] +"\u0102" => "A" + +# Ą [LATIN CAPITAL LETTER A WITH OGONEK] +"\u0104" => "A" + +# Ə http://en.wikipedia.org/wiki/Schwa [LATIN CAPITAL LETTER SCHWA] +"\u018F" => "A" + +# Ǎ [LATIN CAPITAL LETTER A WITH CARON] +"\u01CD" => "A" + +# Ǟ [LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON] +"\u01DE" => "A" + +# Ǡ [LATIN CAPITAL LETTER A WITH DOT ABOVE AND MACRON] +"\u01E0" => "A" + +# Ǻ [LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE] +"\u01FA" => "A" + +# Ȁ [LATIN CAPITAL LETTER A WITH DOUBLE GRAVE] +"\u0200" => "A" + +# Ȃ [LATIN CAPITAL LETTER A WITH INVERTED BREVE] +"\u0202" => "A" + +# Ȧ [LATIN CAPITAL LETTER A WITH DOT ABOVE] +"\u0226" => "A" + +# Ⱥ [LATIN CAPITAL LETTER A WITH STROKE] +"\u023A" => "A" + +# ᴀ [LATIN LETTER SMALL CAPITAL A] +"\u1D00" => "A" + +# Ḁ [LATIN CAPITAL LETTER A WITH RING BELOW] +"\u1E00" => "A" + +# Ạ [LATIN CAPITAL LETTER A WITH DOT BELOW] +"\u1EA0" => "A" + +# Ả [LATIN CAPITAL LETTER A WITH HOOK ABOVE] +"\u1EA2" => "A" + +# Ấ [LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND ACUTE] +"\u1EA4" => "A" + +# Ầ [LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND GRAVE] +"\u1EA6" => "A" + +# Ẩ [LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE] +"\u1EA8" => "A" + +# Ẫ [LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND TILDE] +"\u1EAA" => "A" + +# Ậ [LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND DOT BELOW] +"\u1EAC" => "A" + +# Ắ [LATIN CAPITAL LETTER A WITH BREVE AND ACUTE] +"\u1EAE" => "A" + +# Ằ [LATIN CAPITAL LETTER A WITH BREVE AND GRAVE] +"\u1EB0" => "A" + +# Ẳ [LATIN CAPITAL LETTER A WITH BREVE AND HOOK ABOVE] +"\u1EB2" => "A" + +# Ẵ [LATIN CAPITAL LETTER A WITH BREVE AND TILDE] +"\u1EB4" => "A" + +# Ặ [LATIN CAPITAL LETTER A WITH BREVE AND DOT BELOW] +"\u1EB6" => "A" + +# Ⓐ [CIRCLED LATIN CAPITAL LETTER A] +"\u24B6" => "A" + +# A [FULLWIDTH LATIN CAPITAL LETTER A] +"\uFF21" => "A" + +# à [LATIN SMALL LETTER A WITH GRAVE] +"\u00E0" => "a" + +# á [LATIN SMALL LETTER A WITH ACUTE] +"\u00E1" => "a" + +# â [LATIN SMALL LETTER A WITH CIRCUMFLEX] +"\u00E2" => "a" + +# ã [LATIN SMALL LETTER A WITH TILDE] +"\u00E3" => "a" + +# ä [LATIN SMALL LETTER A WITH DIAERESIS] +"\u00E4" => "a" + +# å [LATIN SMALL LETTER A WITH RING ABOVE] +"\u00E5" => "a" + +# ā [LATIN SMALL LETTER A WITH MACRON] +"\u0101" => "a" + +# ă [LATIN SMALL LETTER A WITH BREVE] +"\u0103" => "a" + +# ą [LATIN SMALL LETTER A WITH OGONEK] +"\u0105" => "a" + +# ǎ [LATIN SMALL LETTER A WITH CARON] +"\u01CE" => "a" + +# ǟ [LATIN SMALL LETTER A WITH DIAERESIS AND MACRON] +"\u01DF" => "a" + +# ǡ [LATIN SMALL LETTER A WITH DOT ABOVE AND MACRON] +"\u01E1" => "a" + +# ǻ [LATIN SMALL LETTER A WITH RING ABOVE AND ACUTE] +"\u01FB" => "a" + +# ȁ [LATIN SMALL LETTER A WITH DOUBLE GRAVE] +"\u0201" => "a" + +# ȃ [LATIN SMALL LETTER A WITH INVERTED BREVE] +"\u0203" => "a" + +# ȧ [LATIN SMALL LETTER A WITH DOT ABOVE] +"\u0227" => "a" + +# ɐ [LATIN SMALL LETTER TURNED A] +"\u0250" => "a" + +# ə [LATIN SMALL LETTER SCHWA] +"\u0259" => "a" + +# ɚ [LATIN SMALL LETTER SCHWA WITH HOOK] +"\u025A" => "a" + +# ᶏ [LATIN SMALL LETTER A WITH RETROFLEX HOOK] +"\u1D8F" => "a" + +# ᶕ [LATIN SMALL LETTER SCHWA WITH RETROFLEX HOOK] +"\u1D95" => "a" + +# ạ [LATIN SMALL LETTER A WITH RING BELOW] +"\u1E01" => "a" + +# ả [LATIN SMALL LETTER A WITH RIGHT HALF RING] +"\u1E9A" => "a" + +# ạ [LATIN SMALL LETTER A WITH DOT BELOW] +"\u1EA1" => "a" + +# ả [LATIN SMALL LETTER A WITH HOOK ABOVE] +"\u1EA3" => "a" + +# ấ [LATIN SMALL LETTER A WITH CIRCUMFLEX AND ACUTE] +"\u1EA5" => "a" + +# ầ [LATIN SMALL LETTER A WITH CIRCUMFLEX AND GRAVE] +"\u1EA7" => "a" + +# ẩ [LATIN SMALL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE] +"\u1EA9" => "a" + +# ẫ [LATIN SMALL LETTER A WITH CIRCUMFLEX AND TILDE] +"\u1EAB" => "a" + +# ậ [LATIN SMALL LETTER A WITH CIRCUMFLEX AND DOT BELOW] +"\u1EAD" => "a" + +# ắ [LATIN SMALL LETTER A WITH BREVE AND ACUTE] +"\u1EAF" => "a" + +# ằ [LATIN SMALL LETTER A WITH BREVE AND GRAVE] +"\u1EB1" => "a" + +# ẳ [LATIN SMALL LETTER A WITH BREVE AND HOOK ABOVE] +"\u1EB3" => "a" + +# ẵ [LATIN SMALL LETTER A WITH BREVE AND TILDE] +"\u1EB5" => "a" + +# ặ [LATIN SMALL LETTER A WITH BREVE AND DOT BELOW] +"\u1EB7" => "a" + +# ₐ [LATIN SUBSCRIPT SMALL LETTER A] +"\u2090" => "a" + +# ₔ [LATIN SUBSCRIPT SMALL LETTER SCHWA] +"\u2094" => "a" + +# ⓐ [CIRCLED LATIN SMALL LETTER A] +"\u24D0" => "a" + +# ⱥ [LATIN SMALL LETTER A WITH STROKE] +"\u2C65" => "a" + +# Ɐ [LATIN CAPITAL LETTER TURNED A] +"\u2C6F" => "a" + +# a [FULLWIDTH LATIN SMALL LETTER A] +"\uFF41" => "a" + +# Ꜳ [LATIN CAPITAL LETTER AA] +"\uA732" => "AA" + +# Æ [LATIN CAPITAL LETTER AE] +"\u00C6" => "AE" + +# Ǣ [LATIN CAPITAL LETTER AE WITH MACRON] +"\u01E2" => "AE" + +# Ǽ [LATIN CAPITAL LETTER AE WITH ACUTE] +"\u01FC" => "AE" + +# ᴁ [LATIN LETTER SMALL CAPITAL AE] +"\u1D01" => "AE" + +# Ꜵ [LATIN CAPITAL LETTER AO] +"\uA734" => "AO" + +# Ꜷ [LATIN CAPITAL LETTER AU] +"\uA736" => "AU" + +# Ꜹ [LATIN CAPITAL LETTER AV] +"\uA738" => "AV" + +# Ꜻ [LATIN CAPITAL LETTER AV WITH HORIZONTAL BAR] +"\uA73A" => "AV" + +# Ꜽ [LATIN CAPITAL LETTER AY] +"\uA73C" => "AY" + +# ⒜ [PARENTHESIZED LATIN SMALL LETTER A] +"\u249C" => "(a)" + +# ꜳ [LATIN SMALL LETTER AA] +"\uA733" => "aa" + +# æ [LATIN SMALL LETTER AE] +"\u00E6" => "ae" + +# ǣ [LATIN SMALL LETTER AE WITH MACRON] +"\u01E3" => "ae" + +# ǽ [LATIN SMALL LETTER AE WITH ACUTE] +"\u01FD" => "ae" + +# ᴂ [LATIN SMALL LETTER TURNED AE] +"\u1D02" => "ae" + +# ꜵ [LATIN SMALL LETTER AO] +"\uA735" => "ao" + +# ꜷ [LATIN SMALL LETTER AU] +"\uA737" => "au" + +# ꜹ [LATIN SMALL LETTER AV] +"\uA739" => "av" + +# ꜻ [LATIN SMALL LETTER AV WITH HORIZONTAL BAR] +"\uA73B" => "av" + +# ꜽ [LATIN SMALL LETTER AY] +"\uA73D" => "ay" + +# Ɓ [LATIN CAPITAL LETTER B WITH HOOK] +"\u0181" => "B" + +# Ƃ [LATIN CAPITAL LETTER B WITH TOPBAR] +"\u0182" => "B" + +# Ƀ [LATIN CAPITAL LETTER B WITH STROKE] +"\u0243" => "B" + +# ʙ [LATIN LETTER SMALL CAPITAL B] +"\u0299" => "B" + +# ᴃ [LATIN LETTER SMALL CAPITAL BARRED B] +"\u1D03" => "B" + +# Ḃ [LATIN CAPITAL LETTER B WITH DOT ABOVE] +"\u1E02" => "B" + +# Ḅ [LATIN CAPITAL LETTER B WITH DOT BELOW] +"\u1E04" => "B" + +# Ḇ [LATIN CAPITAL LETTER B WITH LINE BELOW] +"\u1E06" => "B" + +# Ⓑ [CIRCLED LATIN CAPITAL LETTER B] +"\u24B7" => "B" + +# B [FULLWIDTH LATIN CAPITAL LETTER B] +"\uFF22" => "B" + +# ƀ [LATIN SMALL LETTER B WITH STROKE] +"\u0180" => "b" + +# ƃ [LATIN SMALL LETTER B WITH TOPBAR] +"\u0183" => "b" + +# ɓ [LATIN SMALL LETTER B WITH HOOK] +"\u0253" => "b" + +# ᵬ [LATIN SMALL LETTER B WITH MIDDLE TILDE] +"\u1D6C" => "b" + +# ᶀ [LATIN SMALL LETTER B WITH PALATAL HOOK] +"\u1D80" => "b" + +# ḃ [LATIN SMALL LETTER B WITH DOT ABOVE] +"\u1E03" => "b" + +# ḅ [LATIN SMALL LETTER B WITH DOT BELOW] +"\u1E05" => "b" + +# ḇ [LATIN SMALL LETTER B WITH LINE BELOW] +"\u1E07" => "b" + +# ⓑ [CIRCLED LATIN SMALL LETTER B] +"\u24D1" => "b" + +# b [FULLWIDTH LATIN SMALL LETTER B] +"\uFF42" => "b" + +# ⒝ [PARENTHESIZED LATIN SMALL LETTER B] +"\u249D" => "(b)" + +# Ç [LATIN CAPITAL LETTER C WITH CEDILLA] +"\u00C7" => "C" + +# Ć [LATIN CAPITAL LETTER C WITH ACUTE] +"\u0106" => "C" + +# Ĉ [LATIN CAPITAL LETTER C WITH CIRCUMFLEX] +"\u0108" => "C" + +# Ċ [LATIN CAPITAL LETTER C WITH DOT ABOVE] +"\u010A" => "C" + +# Č [LATIN CAPITAL LETTER C WITH CARON] +"\u010C" => "C" + +# Ƈ [LATIN CAPITAL LETTER C WITH HOOK] +"\u0187" => "C" + +# Ȼ [LATIN CAPITAL LETTER C WITH STROKE] +"\u023B" => "C" + +# ʗ [LATIN LETTER STRETCHED C] +"\u0297" => "C" + +# ᴄ [LATIN LETTER SMALL CAPITAL C] +"\u1D04" => "C" + +# Ḉ [LATIN CAPITAL LETTER C WITH CEDILLA AND ACUTE] +"\u1E08" => "C" + +# Ⓒ [CIRCLED LATIN CAPITAL LETTER C] +"\u24B8" => "C" + +# C [FULLWIDTH LATIN CAPITAL LETTER C] +"\uFF23" => "C" + +# ç [LATIN SMALL LETTER C WITH CEDILLA] +"\u00E7" => "c" + +# ć [LATIN SMALL LETTER C WITH ACUTE] +"\u0107" => "c" + +# ĉ [LATIN SMALL LETTER C WITH CIRCUMFLEX] +"\u0109" => "c" + +# ċ [LATIN SMALL LETTER C WITH DOT ABOVE] +"\u010B" => "c" + +# č [LATIN SMALL LETTER C WITH CARON] +"\u010D" => "c" + +# ƈ [LATIN SMALL LETTER C WITH HOOK] +"\u0188" => "c" + +# ȼ [LATIN SMALL LETTER C WITH STROKE] +"\u023C" => "c" + +# ɕ [LATIN SMALL LETTER C WITH CURL] +"\u0255" => "c" + +# ḉ [LATIN SMALL LETTER C WITH CEDILLA AND ACUTE] +"\u1E09" => "c" + +# ↄ [LATIN SMALL LETTER REVERSED C] +"\u2184" => "c" + +# ⓒ [CIRCLED LATIN SMALL LETTER C] +"\u24D2" => "c" + +# Ꜿ [LATIN CAPITAL LETTER REVERSED C WITH DOT] +"\uA73E" => "c" + +# ꜿ [LATIN SMALL LETTER REVERSED C WITH DOT] +"\uA73F" => "c" + +# c [FULLWIDTH LATIN SMALL LETTER C] +"\uFF43" => "c" + +# ⒞ [PARENTHESIZED LATIN SMALL LETTER C] +"\u249E" => "(c)" + +# Ð [LATIN CAPITAL LETTER ETH] +"\u00D0" => "D" + +# Ď [LATIN CAPITAL LETTER D WITH CARON] +"\u010E" => "D" + +# Đ [LATIN CAPITAL LETTER D WITH STROKE] +"\u0110" => "D" + +# Ɖ [LATIN CAPITAL LETTER AFRICAN D] +"\u0189" => "D" + +# Ɗ [LATIN CAPITAL LETTER D WITH HOOK] +"\u018A" => "D" + +# Ƌ [LATIN CAPITAL LETTER D WITH TOPBAR] +"\u018B" => "D" + +# ᴅ [LATIN LETTER SMALL CAPITAL D] +"\u1D05" => "D" + +# ᴆ [LATIN LETTER SMALL CAPITAL ETH] +"\u1D06" => "D" + +# Ḋ [LATIN CAPITAL LETTER D WITH DOT ABOVE] +"\u1E0A" => "D" + +# Ḍ [LATIN CAPITAL LETTER D WITH DOT BELOW] +"\u1E0C" => "D" + +# Ḏ [LATIN CAPITAL LETTER D WITH LINE BELOW] +"\u1E0E" => "D" + +# Ḑ [LATIN CAPITAL LETTER D WITH CEDILLA] +"\u1E10" => "D" + +# Ḓ [LATIN CAPITAL LETTER D WITH CIRCUMFLEX BELOW] +"\u1E12" => "D" + +# Ⓓ [CIRCLED LATIN CAPITAL LETTER D] +"\u24B9" => "D" + +# Ꝺ [LATIN CAPITAL LETTER INSULAR D] +"\uA779" => "D" + +# D [FULLWIDTH LATIN CAPITAL LETTER D] +"\uFF24" => "D" + +# ð [LATIN SMALL LETTER ETH] +"\u00F0" => "d" + +# ď [LATIN SMALL LETTER D WITH CARON] +"\u010F" => "d" + +# đ [LATIN SMALL LETTER D WITH STROKE] +"\u0111" => "d" + +# ƌ [LATIN SMALL LETTER D WITH TOPBAR] +"\u018C" => "d" + +# ȡ [LATIN SMALL LETTER D WITH CURL] +"\u0221" => "d" + +# ɖ [LATIN SMALL LETTER D WITH TAIL] +"\u0256" => "d" + +# ɗ [LATIN SMALL LETTER D WITH HOOK] +"\u0257" => "d" + +# ᵭ [LATIN SMALL LETTER D WITH MIDDLE TILDE] +"\u1D6D" => "d" + +# ᶁ [LATIN SMALL LETTER D WITH PALATAL HOOK] +"\u1D81" => "d" + +# ᶑ [LATIN SMALL LETTER D WITH HOOK AND TAIL] +"\u1D91" => "d" + +# ḋ [LATIN SMALL LETTER D WITH DOT ABOVE] +"\u1E0B" => "d" + +# ḍ [LATIN SMALL LETTER D WITH DOT BELOW] +"\u1E0D" => "d" + +# ḏ [LATIN SMALL LETTER D WITH LINE BELOW] +"\u1E0F" => "d" + +# ḑ [LATIN SMALL LETTER D WITH CEDILLA] +"\u1E11" => "d" + +# ḓ [LATIN SMALL LETTER D WITH CIRCUMFLEX BELOW] +"\u1E13" => "d" + +# ⓓ [CIRCLED LATIN SMALL LETTER D] +"\u24D3" => "d" + +# ꝺ [LATIN SMALL LETTER INSULAR D] +"\uA77A" => "d" + +# d [FULLWIDTH LATIN SMALL LETTER D] +"\uFF44" => "d" + +# DŽ [LATIN CAPITAL LETTER DZ WITH CARON] +"\u01C4" => "DZ" + +# DZ [LATIN CAPITAL LETTER DZ] +"\u01F1" => "DZ" + +# Dž [LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON] +"\u01C5" => "Dz" + +# Dz [LATIN CAPITAL LETTER D WITH SMALL LETTER Z] +"\u01F2" => "Dz" + +# ⒟ [PARENTHESIZED LATIN SMALL LETTER D] +"\u249F" => "(d)" + +# ȸ [LATIN SMALL LETTER DB DIGRAPH] +"\u0238" => "db" + +# dž [LATIN SMALL LETTER DZ WITH CARON] +"\u01C6" => "dz" + +# dz [LATIN SMALL LETTER DZ] +"\u01F3" => "dz" + +# ʣ [LATIN SMALL LETTER DZ DIGRAPH] +"\u02A3" => "dz" + +# ʥ [LATIN SMALL LETTER DZ DIGRAPH WITH CURL] +"\u02A5" => "dz" + +# È [LATIN CAPITAL LETTER E WITH GRAVE] +"\u00C8" => "E" + +# É [LATIN CAPITAL LETTER E WITH ACUTE] +"\u00C9" => "E" + +# Ê [LATIN CAPITAL LETTER E WITH CIRCUMFLEX] +"\u00CA" => "E" + +# Ë [LATIN CAPITAL LETTER E WITH DIAERESIS] +"\u00CB" => "E" + +# Ē [LATIN CAPITAL LETTER E WITH MACRON] +"\u0112" => "E" + +# Ĕ [LATIN CAPITAL LETTER E WITH BREVE] +"\u0114" => "E" + +# Ė [LATIN CAPITAL LETTER E WITH DOT ABOVE] +"\u0116" => "E" + +# Ę [LATIN CAPITAL LETTER E WITH OGONEK] +"\u0118" => "E" + +# Ě [LATIN CAPITAL LETTER E WITH CARON] +"\u011A" => "E" + +# Ǝ [LATIN CAPITAL LETTER REVERSED E] +"\u018E" => "E" + +# Ɛ [LATIN CAPITAL LETTER OPEN E] +"\u0190" => "E" + +# Ȅ [LATIN CAPITAL LETTER E WITH DOUBLE GRAVE] +"\u0204" => "E" + +# Ȇ [LATIN CAPITAL LETTER E WITH INVERTED BREVE] +"\u0206" => "E" + +# Ȩ [LATIN CAPITAL LETTER E WITH CEDILLA] +"\u0228" => "E" + +# Ɇ [LATIN CAPITAL LETTER E WITH STROKE] +"\u0246" => "E" + +# ᴇ [LATIN LETTER SMALL CAPITAL E] +"\u1D07" => "E" + +# Ḕ [LATIN CAPITAL LETTER E WITH MACRON AND GRAVE] +"\u1E14" => "E" + +# Ḗ [LATIN CAPITAL LETTER E WITH MACRON AND ACUTE] +"\u1E16" => "E" + +# Ḙ [LATIN CAPITAL LETTER E WITH CIRCUMFLEX BELOW] +"\u1E18" => "E" + +# Ḛ [LATIN CAPITAL LETTER E WITH TILDE BELOW] +"\u1E1A" => "E" + +# Ḝ [LATIN CAPITAL LETTER E WITH CEDILLA AND BREVE] +"\u1E1C" => "E" + +# Ẹ [LATIN CAPITAL LETTER E WITH DOT BELOW] +"\u1EB8" => "E" + +# Ẻ [LATIN CAPITAL LETTER E WITH HOOK ABOVE] +"\u1EBA" => "E" + +# Ẽ [LATIN CAPITAL LETTER E WITH TILDE] +"\u1EBC" => "E" + +# Ế [LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND ACUTE] +"\u1EBE" => "E" + +# Ề [LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND GRAVE] +"\u1EC0" => "E" + +# Ể [LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE] +"\u1EC2" => "E" + +# Ễ [LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND TILDE] +"\u1EC4" => "E" + +# Ệ [LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND DOT BELOW] +"\u1EC6" => "E" + +# Ⓔ [CIRCLED LATIN CAPITAL LETTER E] +"\u24BA" => "E" + +# ⱻ [LATIN LETTER SMALL CAPITAL TURNED E] +"\u2C7B" => "E" + +# E [FULLWIDTH LATIN CAPITAL LETTER E] +"\uFF25" => "E" + +# è [LATIN SMALL LETTER E WITH GRAVE] +"\u00E8" => "e" + +# é [LATIN SMALL LETTER E WITH ACUTE] +"\u00E9" => "e" + +# ê [LATIN SMALL LETTER E WITH CIRCUMFLEX] +"\u00EA" => "e" + +# ë [LATIN SMALL LETTER E WITH DIAERESIS] +"\u00EB" => "e" + +# ē [LATIN SMALL LETTER E WITH MACRON] +"\u0113" => "e" + +# ĕ [LATIN SMALL LETTER E WITH BREVE] +"\u0115" => "e" + +# ė [LATIN SMALL LETTER E WITH DOT ABOVE] +"\u0117" => "e" + +# ę [LATIN SMALL LETTER E WITH OGONEK] +"\u0119" => "e" + +# ě [LATIN SMALL LETTER E WITH CARON] +"\u011B" => "e" + +# ǝ [LATIN SMALL LETTER TURNED E] +"\u01DD" => "e" + +# ȅ [LATIN SMALL LETTER E WITH DOUBLE GRAVE] +"\u0205" => "e" + +# ȇ [LATIN SMALL LETTER E WITH INVERTED BREVE] +"\u0207" => "e" + +# ȩ [LATIN SMALL LETTER E WITH CEDILLA] +"\u0229" => "e" + +# ɇ [LATIN SMALL LETTER E WITH STROKE] +"\u0247" => "e" + +# ɘ [LATIN SMALL LETTER REVERSED E] +"\u0258" => "e" + +# ɛ [LATIN SMALL LETTER OPEN E] +"\u025B" => "e" + +# ɜ [LATIN SMALL LETTER REVERSED OPEN E] +"\u025C" => "e" + +# ɝ [LATIN SMALL LETTER REVERSED OPEN E WITH HOOK] +"\u025D" => "e" + +# ɞ [LATIN SMALL LETTER CLOSED REVERSED OPEN E] +"\u025E" => "e" + +# ʚ [LATIN SMALL LETTER CLOSED OPEN E] +"\u029A" => "e" + +# ᴈ [LATIN SMALL LETTER TURNED OPEN E] +"\u1D08" => "e" + +# ᶒ [LATIN SMALL LETTER E WITH RETROFLEX HOOK] +"\u1D92" => "e" + +# ᶓ [LATIN SMALL LETTER OPEN E WITH RETROFLEX HOOK] +"\u1D93" => "e" + +# ᶔ [LATIN SMALL LETTER REVERSED OPEN E WITH RETROFLEX HOOK] +"\u1D94" => "e" + +# ḕ [LATIN SMALL LETTER E WITH MACRON AND GRAVE] +"\u1E15" => "e" + +# ḗ [LATIN SMALL LETTER E WITH MACRON AND ACUTE] +"\u1E17" => "e" + +# ḙ [LATIN SMALL LETTER E WITH CIRCUMFLEX BELOW] +"\u1E19" => "e" + +# ḛ [LATIN SMALL LETTER E WITH TILDE BELOW] +"\u1E1B" => "e" + +# ḝ [LATIN SMALL LETTER E WITH CEDILLA AND BREVE] +"\u1E1D" => "e" + +# ẹ [LATIN SMALL LETTER E WITH DOT BELOW] +"\u1EB9" => "e" + +# ẻ [LATIN SMALL LETTER E WITH HOOK ABOVE] +"\u1EBB" => "e" + +# ẽ [LATIN SMALL LETTER E WITH TILDE] +"\u1EBD" => "e" + +# ế [LATIN SMALL LETTER E WITH CIRCUMFLEX AND ACUTE] +"\u1EBF" => "e" + +# ề [LATIN SMALL LETTER E WITH CIRCUMFLEX AND GRAVE] +"\u1EC1" => "e" + +# ể [LATIN SMALL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE] +"\u1EC3" => "e" + +# ễ [LATIN SMALL LETTER E WITH CIRCUMFLEX AND TILDE] +"\u1EC5" => "e" + +# ệ [LATIN SMALL LETTER E WITH CIRCUMFLEX AND DOT BELOW] +"\u1EC7" => "e" + +# ₑ [LATIN SUBSCRIPT SMALL LETTER E] +"\u2091" => "e" + +# ⓔ [CIRCLED LATIN SMALL LETTER E] +"\u24D4" => "e" + +# ⱸ [LATIN SMALL LETTER E WITH NOTCH] +"\u2C78" => "e" + +# e [FULLWIDTH LATIN SMALL LETTER E] +"\uFF45" => "e" + +# ⒠ [PARENTHESIZED LATIN SMALL LETTER E] +"\u24A0" => "(e)" + +# Ƒ [LATIN CAPITAL LETTER F WITH HOOK] +"\u0191" => "F" + +# Ḟ [LATIN CAPITAL LETTER F WITH DOT ABOVE] +"\u1E1E" => "F" + +# Ⓕ [CIRCLED LATIN CAPITAL LETTER F] +"\u24BB" => "F" + +# ꜰ [LATIN LETTER SMALL CAPITAL F] +"\uA730" => "F" + +# Ꝼ [LATIN CAPITAL LETTER INSULAR F] +"\uA77B" => "F" + +# ꟻ [LATIN EPIGRAPHIC LETTER REVERSED F] +"\uA7FB" => "F" + +# F [FULLWIDTH LATIN CAPITAL LETTER F] +"\uFF26" => "F" + +# ƒ [LATIN SMALL LETTER F WITH HOOK] +"\u0192" => "f" + +# ᵮ [LATIN SMALL LETTER F WITH MIDDLE TILDE] +"\u1D6E" => "f" + +# ᶂ [LATIN SMALL LETTER F WITH PALATAL HOOK] +"\u1D82" => "f" + +# ḟ [LATIN SMALL LETTER F WITH DOT ABOVE] +"\u1E1F" => "f" + +# ẛ [LATIN SMALL LETTER LONG S WITH DOT ABOVE] +"\u1E9B" => "f" + +# ⓕ [CIRCLED LATIN SMALL LETTER F] +"\u24D5" => "f" + +# ꝼ [LATIN SMALL LETTER INSULAR F] +"\uA77C" => "f" + +# f [FULLWIDTH LATIN SMALL LETTER F] +"\uFF46" => "f" + +# ⒡ [PARENTHESIZED LATIN SMALL LETTER F] +"\u24A1" => "(f)" + +# ff [LATIN SMALL LIGATURE FF] +"\uFB00" => "ff" + +# ffi [LATIN SMALL LIGATURE FFI] +"\uFB03" => "ffi" + +# ffl [LATIN SMALL LIGATURE FFL] +"\uFB04" => "ffl" + +# fi [LATIN SMALL LIGATURE FI] +"\uFB01" => "fi" + +# fl [LATIN SMALL LIGATURE FL] +"\uFB02" => "fl" + +# Ĝ [LATIN CAPITAL LETTER G WITH CIRCUMFLEX] +"\u011C" => "G" + +# Ğ [LATIN CAPITAL LETTER G WITH BREVE] +"\u011E" => "G" + +# Ġ [LATIN CAPITAL LETTER G WITH DOT ABOVE] +"\u0120" => "G" + +# Ģ [LATIN CAPITAL LETTER G WITH CEDILLA] +"\u0122" => "G" + +# Ɠ [LATIN CAPITAL LETTER G WITH HOOK] +"\u0193" => "G" + +# Ǥ [LATIN CAPITAL LETTER G WITH STROKE] +"\u01E4" => "G" + +# ǥ [LATIN SMALL LETTER G WITH STROKE] +"\u01E5" => "G" + +# Ǧ [LATIN CAPITAL LETTER G WITH CARON] +"\u01E6" => "G" + +# ǧ [LATIN SMALL LETTER G WITH CARON] +"\u01E7" => "G" + +# Ǵ [LATIN CAPITAL LETTER G WITH ACUTE] +"\u01F4" => "G" + +# ɢ [LATIN LETTER SMALL CAPITAL G] +"\u0262" => "G" + +# ʛ [LATIN LETTER SMALL CAPITAL G WITH HOOK] +"\u029B" => "G" + +# Ḡ [LATIN CAPITAL LETTER G WITH MACRON] +"\u1E20" => "G" + +# Ⓖ [CIRCLED LATIN CAPITAL LETTER G] +"\u24BC" => "G" + +# Ᵹ [LATIN CAPITAL LETTER INSULAR G] +"\uA77D" => "G" + +# Ꝿ [LATIN CAPITAL LETTER TURNED INSULAR G] +"\uA77E" => "G" + +# G [FULLWIDTH LATIN CAPITAL LETTER G] +"\uFF27" => "G" + +# ĝ [LATIN SMALL LETTER G WITH CIRCUMFLEX] +"\u011D" => "g" + +# ğ [LATIN SMALL LETTER G WITH BREVE] +"\u011F" => "g" + +# ġ [LATIN SMALL LETTER G WITH DOT ABOVE] +"\u0121" => "g" + +# ģ [LATIN SMALL LETTER G WITH CEDILLA] +"\u0123" => "g" + +# ǵ [LATIN SMALL LETTER G WITH ACUTE] +"\u01F5" => "g" + +# ɠ [LATIN SMALL LETTER G WITH HOOK] +"\u0260" => "g" + +# ɡ [LATIN SMALL LETTER SCRIPT G] +"\u0261" => "g" + +# ᵷ [LATIN SMALL LETTER TURNED G] +"\u1D77" => "g" + +# ᵹ [LATIN SMALL LETTER INSULAR G] +"\u1D79" => "g" + +# ᶃ [LATIN SMALL LETTER G WITH PALATAL HOOK] +"\u1D83" => "g" + +# ḡ [LATIN SMALL LETTER G WITH MACRON] +"\u1E21" => "g" + +# ⓖ [CIRCLED LATIN SMALL LETTER G] +"\u24D6" => "g" + +# ꝿ [LATIN SMALL LETTER TURNED INSULAR G] +"\uA77F" => "g" + +# g [FULLWIDTH LATIN SMALL LETTER G] +"\uFF47" => "g" + +# ⒢ [PARENTHESIZED LATIN SMALL LETTER G] +"\u24A2" => "(g)" + +# Ĥ [LATIN CAPITAL LETTER H WITH CIRCUMFLEX] +"\u0124" => "H" + +# Ħ [LATIN CAPITAL LETTER H WITH STROKE] +"\u0126" => "H" + +# Ȟ [LATIN CAPITAL LETTER H WITH CARON] +"\u021E" => "H" + +# ʜ [LATIN LETTER SMALL CAPITAL H] +"\u029C" => "H" + +# Ḣ [LATIN CAPITAL LETTER H WITH DOT ABOVE] +"\u1E22" => "H" + +# Ḥ [LATIN CAPITAL LETTER H WITH DOT BELOW] +"\u1E24" => "H" + +# Ḧ [LATIN CAPITAL LETTER H WITH DIAERESIS] +"\u1E26" => "H" + +# Ḩ [LATIN CAPITAL LETTER H WITH CEDILLA] +"\u1E28" => "H" + +# Ḫ [LATIN CAPITAL LETTER H WITH BREVE BELOW] +"\u1E2A" => "H" + +# Ⓗ [CIRCLED LATIN CAPITAL LETTER H] +"\u24BD" => "H" + +# Ⱨ [LATIN CAPITAL LETTER H WITH DESCENDER] +"\u2C67" => "H" + +# Ⱶ [LATIN CAPITAL LETTER HALF H] +"\u2C75" => "H" + +# H [FULLWIDTH LATIN CAPITAL LETTER H] +"\uFF28" => "H" + +# ĥ [LATIN SMALL LETTER H WITH CIRCUMFLEX] +"\u0125" => "h" + +# ħ [LATIN SMALL LETTER H WITH STROKE] +"\u0127" => "h" + +# ȟ [LATIN SMALL LETTER H WITH CARON] +"\u021F" => "h" + +# ɥ [LATIN SMALL LETTER TURNED H] +"\u0265" => "h" + +# ɦ [LATIN SMALL LETTER H WITH HOOK] +"\u0266" => "h" + +# ʮ [LATIN SMALL LETTER TURNED H WITH FISHHOOK] +"\u02AE" => "h" + +# ʯ [LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL] +"\u02AF" => "h" + +# ḣ [LATIN SMALL LETTER H WITH DOT ABOVE] +"\u1E23" => "h" + +# ḥ [LATIN SMALL LETTER H WITH DOT BELOW] +"\u1E25" => "h" + +# ḧ [LATIN SMALL LETTER H WITH DIAERESIS] +"\u1E27" => "h" + +# ḩ [LATIN SMALL LETTER H WITH CEDILLA] +"\u1E29" => "h" + +# ḫ [LATIN SMALL LETTER H WITH BREVE BELOW] +"\u1E2B" => "h" + +# ẖ [LATIN SMALL LETTER H WITH LINE BELOW] +"\u1E96" => "h" + +# ⓗ [CIRCLED LATIN SMALL LETTER H] +"\u24D7" => "h" + +# ⱨ [LATIN SMALL LETTER H WITH DESCENDER] +"\u2C68" => "h" + +# ⱶ [LATIN SMALL LETTER HALF H] +"\u2C76" => "h" + +# h [FULLWIDTH LATIN SMALL LETTER H] +"\uFF48" => "h" + +# Ƕ http://en.wikipedia.org/wiki/Hwair [LATIN CAPITAL LETTER HWAIR] +"\u01F6" => "HV" + +# ⒣ [PARENTHESIZED LATIN SMALL LETTER H] +"\u24A3" => "(h)" + +# ƕ [LATIN SMALL LETTER HV] +"\u0195" => "hv" + +# Ì [LATIN CAPITAL LETTER I WITH GRAVE] +"\u00CC" => "I" + +# Í [LATIN CAPITAL LETTER I WITH ACUTE] +"\u00CD" => "I" + +# Î [LATIN CAPITAL LETTER I WITH CIRCUMFLEX] +"\u00CE" => "I" + +# Ï [LATIN CAPITAL LETTER I WITH DIAERESIS] +"\u00CF" => "I" + +# Ĩ [LATIN CAPITAL LETTER I WITH TILDE] +"\u0128" => "I" + +# Ī [LATIN CAPITAL LETTER I WITH MACRON] +"\u012A" => "I" + +# Ĭ [LATIN CAPITAL LETTER I WITH BREVE] +"\u012C" => "I" + +# Į [LATIN CAPITAL LETTER I WITH OGONEK] +"\u012E" => "I" + +# İ [LATIN CAPITAL LETTER I WITH DOT ABOVE] +"\u0130" => "I" + +# Ɩ [LATIN CAPITAL LETTER IOTA] +"\u0196" => "I" + +# Ɨ [LATIN CAPITAL LETTER I WITH STROKE] +"\u0197" => "I" + +# Ǐ [LATIN CAPITAL LETTER I WITH CARON] +"\u01CF" => "I" + +# Ȉ [LATIN CAPITAL LETTER I WITH DOUBLE GRAVE] +"\u0208" => "I" + +# Ȋ [LATIN CAPITAL LETTER I WITH INVERTED BREVE] +"\u020A" => "I" + +# ɪ [LATIN LETTER SMALL CAPITAL I] +"\u026A" => "I" + +# ᵻ [LATIN SMALL CAPITAL LETTER I WITH STROKE] +"\u1D7B" => "I" + +# Ḭ [LATIN CAPITAL LETTER I WITH TILDE BELOW] +"\u1E2C" => "I" + +# Ḯ [LATIN CAPITAL LETTER I WITH DIAERESIS AND ACUTE] +"\u1E2E" => "I" + +# Ỉ [LATIN CAPITAL LETTER I WITH HOOK ABOVE] +"\u1EC8" => "I" + +# Ị [LATIN CAPITAL LETTER I WITH DOT BELOW] +"\u1ECA" => "I" + +# Ⓘ [CIRCLED LATIN CAPITAL LETTER I] +"\u24BE" => "I" + +# ꟾ [LATIN EPIGRAPHIC LETTER I LONGA] +"\uA7FE" => "I" + +# I [FULLWIDTH LATIN CAPITAL LETTER I] +"\uFF29" => "I" + +# ì [LATIN SMALL LETTER I WITH GRAVE] +"\u00EC" => "i" + +# í [LATIN SMALL LETTER I WITH ACUTE] +"\u00ED" => "i" + +# î [LATIN SMALL LETTER I WITH CIRCUMFLEX] +"\u00EE" => "i" + +# ï [LATIN SMALL LETTER I WITH DIAERESIS] +"\u00EF" => "i" + +# ĩ [LATIN SMALL LETTER I WITH TILDE] +"\u0129" => "i" + +# ī [LATIN SMALL LETTER I WITH MACRON] +"\u012B" => "i" + +# ĭ [LATIN SMALL LETTER I WITH BREVE] +"\u012D" => "i" + +# į [LATIN SMALL LETTER I WITH OGONEK] +"\u012F" => "i" + +# ı [LATIN SMALL LETTER DOTLESS I] +"\u0131" => "i" + +# ǐ [LATIN SMALL LETTER I WITH CARON] +"\u01D0" => "i" + +# ȉ [LATIN SMALL LETTER I WITH DOUBLE GRAVE] +"\u0209" => "i" + +# ȋ [LATIN SMALL LETTER I WITH INVERTED BREVE] +"\u020B" => "i" + +# ɨ [LATIN SMALL LETTER I WITH STROKE] +"\u0268" => "i" + +# ᴉ [LATIN SMALL LETTER TURNED I] +"\u1D09" => "i" + +# ᵢ [LATIN SUBSCRIPT SMALL LETTER I] +"\u1D62" => "i" + +# ᵼ [LATIN SMALL LETTER IOTA WITH STROKE] +"\u1D7C" => "i" + +# ᶖ [LATIN SMALL LETTER I WITH RETROFLEX HOOK] +"\u1D96" => "i" + +# ḭ [LATIN SMALL LETTER I WITH TILDE BELOW] +"\u1E2D" => "i" + +# ḯ [LATIN SMALL LETTER I WITH DIAERESIS AND ACUTE] +"\u1E2F" => "i" + +# ỉ [LATIN SMALL LETTER I WITH HOOK ABOVE] +"\u1EC9" => "i" + +# ị [LATIN SMALL LETTER I WITH DOT BELOW] +"\u1ECB" => "i" + +# ⁱ [SUPERSCRIPT LATIN SMALL LETTER I] +"\u2071" => "i" + +# ⓘ [CIRCLED LATIN SMALL LETTER I] +"\u24D8" => "i" + +# i [FULLWIDTH LATIN SMALL LETTER I] +"\uFF49" => "i" + +# IJ [LATIN CAPITAL LIGATURE IJ] +"\u0132" => "IJ" + +# ⒤ [PARENTHESIZED LATIN SMALL LETTER I] +"\u24A4" => "(i)" + +# ij [LATIN SMALL LIGATURE IJ] +"\u0133" => "ij" + +# Ĵ [LATIN CAPITAL LETTER J WITH CIRCUMFLEX] +"\u0134" => "J" + +# Ɉ [LATIN CAPITAL LETTER J WITH STROKE] +"\u0248" => "J" + +# ᴊ [LATIN LETTER SMALL CAPITAL J] +"\u1D0A" => "J" + +# Ⓙ [CIRCLED LATIN CAPITAL LETTER J] +"\u24BF" => "J" + +# J [FULLWIDTH LATIN CAPITAL LETTER J] +"\uFF2A" => "J" + +# ĵ [LATIN SMALL LETTER J WITH CIRCUMFLEX] +"\u0135" => "j" + +# ǰ [LATIN SMALL LETTER J WITH CARON] +"\u01F0" => "j" + +# ȷ [LATIN SMALL LETTER DOTLESS J] +"\u0237" => "j" + +# ɉ [LATIN SMALL LETTER J WITH STROKE] +"\u0249" => "j" + +# ɟ [LATIN SMALL LETTER DOTLESS J WITH STROKE] +"\u025F" => "j" + +# ʄ [LATIN SMALL LETTER DOTLESS J WITH STROKE AND HOOK] +"\u0284" => "j" + +# ʝ [LATIN SMALL LETTER J WITH CROSSED-TAIL] +"\u029D" => "j" + +# ⓙ [CIRCLED LATIN SMALL LETTER J] +"\u24D9" => "j" + +# ⱼ [LATIN SUBSCRIPT SMALL LETTER J] +"\u2C7C" => "j" + +# j [FULLWIDTH LATIN SMALL LETTER J] +"\uFF4A" => "j" + +# ⒥ [PARENTHESIZED LATIN SMALL LETTER J] +"\u24A5" => "(j)" + +# Ķ [LATIN CAPITAL LETTER K WITH CEDILLA] +"\u0136" => "K" + +# Ƙ [LATIN CAPITAL LETTER K WITH HOOK] +"\u0198" => "K" + +# Ǩ [LATIN CAPITAL LETTER K WITH CARON] +"\u01E8" => "K" + +# ᴋ [LATIN LETTER SMALL CAPITAL K] +"\u1D0B" => "K" + +# Ḱ [LATIN CAPITAL LETTER K WITH ACUTE] +"\u1E30" => "K" + +# Ḳ [LATIN CAPITAL LETTER K WITH DOT BELOW] +"\u1E32" => "K" + +# Ḵ [LATIN CAPITAL LETTER K WITH LINE BELOW] +"\u1E34" => "K" + +# Ⓚ [CIRCLED LATIN CAPITAL LETTER K] +"\u24C0" => "K" + +# Ⱪ [LATIN CAPITAL LETTER K WITH DESCENDER] +"\u2C69" => "K" + +# Ꝁ [LATIN CAPITAL LETTER K WITH STROKE] +"\uA740" => "K" + +# Ꝃ [LATIN CAPITAL LETTER K WITH DIAGONAL STROKE] +"\uA742" => "K" + +# Ꝅ [LATIN CAPITAL LETTER K WITH STROKE AND DIAGONAL STROKE] +"\uA744" => "K" + +# K [FULLWIDTH LATIN CAPITAL LETTER K] +"\uFF2B" => "K" + +# ķ [LATIN SMALL LETTER K WITH CEDILLA] +"\u0137" => "k" + +# ƙ [LATIN SMALL LETTER K WITH HOOK] +"\u0199" => "k" + +# ǩ [LATIN SMALL LETTER K WITH CARON] +"\u01E9" => "k" + +# ʞ [LATIN SMALL LETTER TURNED K] +"\u029E" => "k" + +# ᶄ [LATIN SMALL LETTER K WITH PALATAL HOOK] +"\u1D84" => "k" + +# ḱ [LATIN SMALL LETTER K WITH ACUTE] +"\u1E31" => "k" + +# ḳ [LATIN SMALL LETTER K WITH DOT BELOW] +"\u1E33" => "k" + +# ḵ [LATIN SMALL LETTER K WITH LINE BELOW] +"\u1E35" => "k" + +# ⓚ [CIRCLED LATIN SMALL LETTER K] +"\u24DA" => "k" + +# ⱪ [LATIN SMALL LETTER K WITH DESCENDER] +"\u2C6A" => "k" + +# ꝁ [LATIN SMALL LETTER K WITH STROKE] +"\uA741" => "k" + +# ꝃ [LATIN SMALL LETTER K WITH DIAGONAL STROKE] +"\uA743" => "k" + +# ꝅ [LATIN SMALL LETTER K WITH STROKE AND DIAGONAL STROKE] +"\uA745" => "k" + +# k [FULLWIDTH LATIN SMALL LETTER K] +"\uFF4B" => "k" + +# ⒦ [PARENTHESIZED LATIN SMALL LETTER K] +"\u24A6" => "(k)" + +# Ĺ [LATIN CAPITAL LETTER L WITH ACUTE] +"\u0139" => "L" + +# Ļ [LATIN CAPITAL LETTER L WITH CEDILLA] +"\u013B" => "L" + +# Ľ [LATIN CAPITAL LETTER L WITH CARON] +"\u013D" => "L" + +# Ŀ [LATIN CAPITAL LETTER L WITH MIDDLE DOT] +"\u013F" => "L" + +# Ł [LATIN CAPITAL LETTER L WITH STROKE] +"\u0141" => "L" + +# Ƚ [LATIN CAPITAL LETTER L WITH BAR] +"\u023D" => "L" + +# ʟ [LATIN LETTER SMALL CAPITAL L] +"\u029F" => "L" + +# ᴌ [LATIN LETTER SMALL CAPITAL L WITH STROKE] +"\u1D0C" => "L" + +# Ḷ [LATIN CAPITAL LETTER L WITH DOT BELOW] +"\u1E36" => "L" + +# Ḹ [LATIN CAPITAL LETTER L WITH DOT BELOW AND MACRON] +"\u1E38" => "L" + +# Ḻ [LATIN CAPITAL LETTER L WITH LINE BELOW] +"\u1E3A" => "L" + +# Ḽ [LATIN CAPITAL LETTER L WITH CIRCUMFLEX BELOW] +"\u1E3C" => "L" + +# Ⓛ [CIRCLED LATIN CAPITAL LETTER L] +"\u24C1" => "L" + +# Ⱡ [LATIN CAPITAL LETTER L WITH DOUBLE BAR] +"\u2C60" => "L" + +# Ɫ [LATIN CAPITAL LETTER L WITH MIDDLE TILDE] +"\u2C62" => "L" + +# Ꝇ [LATIN CAPITAL LETTER BROKEN L] +"\uA746" => "L" + +# Ꝉ [LATIN CAPITAL LETTER L WITH HIGH STROKE] +"\uA748" => "L" + +# Ꞁ [LATIN CAPITAL LETTER TURNED L] +"\uA780" => "L" + +# L [FULLWIDTH LATIN CAPITAL LETTER L] +"\uFF2C" => "L" + +# ĺ [LATIN SMALL LETTER L WITH ACUTE] +"\u013A" => "l" + +# ļ [LATIN SMALL LETTER L WITH CEDILLA] +"\u013C" => "l" + +# ľ [LATIN SMALL LETTER L WITH CARON] +"\u013E" => "l" + +# ŀ [LATIN SMALL LETTER L WITH MIDDLE DOT] +"\u0140" => "l" + +# ł [LATIN SMALL LETTER L WITH STROKE] +"\u0142" => "l" + +# ƚ [LATIN SMALL LETTER L WITH BAR] +"\u019A" => "l" + +# ȴ [LATIN SMALL LETTER L WITH CURL] +"\u0234" => "l" + +# ɫ [LATIN SMALL LETTER L WITH MIDDLE TILDE] +"\u026B" => "l" + +# ɬ [LATIN SMALL LETTER L WITH BELT] +"\u026C" => "l" + +# ɭ [LATIN SMALL LETTER L WITH RETROFLEX HOOK] +"\u026D" => "l" + +# ᶅ [LATIN SMALL LETTER L WITH PALATAL HOOK] +"\u1D85" => "l" + +# ḷ [LATIN SMALL LETTER L WITH DOT BELOW] +"\u1E37" => "l" + +# ḹ [LATIN SMALL LETTER L WITH DOT BELOW AND MACRON] +"\u1E39" => "l" + +# ḻ [LATIN SMALL LETTER L WITH LINE BELOW] +"\u1E3B" => "l" + +# ḽ [LATIN SMALL LETTER L WITH CIRCUMFLEX BELOW] +"\u1E3D" => "l" + +# ⓛ [CIRCLED LATIN SMALL LETTER L] +"\u24DB" => "l" + +# ⱡ [LATIN SMALL LETTER L WITH DOUBLE BAR] +"\u2C61" => "l" + +# ꝇ [LATIN SMALL LETTER BROKEN L] +"\uA747" => "l" + +# ꝉ [LATIN SMALL LETTER L WITH HIGH STROKE] +"\uA749" => "l" + +# ꞁ [LATIN SMALL LETTER TURNED L] +"\uA781" => "l" + +# l [FULLWIDTH LATIN SMALL LETTER L] +"\uFF4C" => "l" + +# LJ [LATIN CAPITAL LETTER LJ] +"\u01C7" => "LJ" + +# Ỻ [LATIN CAPITAL LETTER MIDDLE-WELSH LL] +"\u1EFA" => "LL" + +# Lj [LATIN CAPITAL LETTER L WITH SMALL LETTER J] +"\u01C8" => "Lj" + +# ⒧ [PARENTHESIZED LATIN SMALL LETTER L] +"\u24A7" => "(l)" + +# lj [LATIN SMALL LETTER LJ] +"\u01C9" => "lj" + +# ỻ [LATIN SMALL LETTER MIDDLE-WELSH LL] +"\u1EFB" => "ll" + +# ʪ [LATIN SMALL LETTER LS DIGRAPH] +"\u02AA" => "ls" + +# ʫ [LATIN SMALL LETTER LZ DIGRAPH] +"\u02AB" => "lz" + +# Ɯ [LATIN CAPITAL LETTER TURNED M] +"\u019C" => "M" + +# ᴍ [LATIN LETTER SMALL CAPITAL M] +"\u1D0D" => "M" + +# Ḿ [LATIN CAPITAL LETTER M WITH ACUTE] +"\u1E3E" => "M" + +# Ṁ [LATIN CAPITAL LETTER M WITH DOT ABOVE] +"\u1E40" => "M" + +# Ṃ [LATIN CAPITAL LETTER M WITH DOT BELOW] +"\u1E42" => "M" + +# Ⓜ [CIRCLED LATIN CAPITAL LETTER M] +"\u24C2" => "M" + +# Ɱ [LATIN CAPITAL LETTER M WITH HOOK] +"\u2C6E" => "M" + +# ꟽ [LATIN EPIGRAPHIC LETTER INVERTED M] +"\uA7FD" => "M" + +# ꟿ [LATIN EPIGRAPHIC LETTER ARCHAIC M] +"\uA7FF" => "M" + +# M [FULLWIDTH LATIN CAPITAL LETTER M] +"\uFF2D" => "M" + +# ɯ [LATIN SMALL LETTER TURNED M] +"\u026F" => "m" + +# ɰ [LATIN SMALL LETTER TURNED M WITH LONG LEG] +"\u0270" => "m" + +# ɱ [LATIN SMALL LETTER M WITH HOOK] +"\u0271" => "m" + +# ᵯ [LATIN SMALL LETTER M WITH MIDDLE TILDE] +"\u1D6F" => "m" + +# ᶆ [LATIN SMALL LETTER M WITH PALATAL HOOK] +"\u1D86" => "m" + +# ḿ [LATIN SMALL LETTER M WITH ACUTE] +"\u1E3F" => "m" + +# ṁ [LATIN SMALL LETTER M WITH DOT ABOVE] +"\u1E41" => "m" + +# ṃ [LATIN SMALL LETTER M WITH DOT BELOW] +"\u1E43" => "m" + +# ⓜ [CIRCLED LATIN SMALL LETTER M] +"\u24DC" => "m" + +# m [FULLWIDTH LATIN SMALL LETTER M] +"\uFF4D" => "m" + +# ⒨ [PARENTHESIZED LATIN SMALL LETTER M] +"\u24A8" => "(m)" + +# Ñ [LATIN CAPITAL LETTER N WITH TILDE] +"\u00D1" => "N" + +# Ń [LATIN CAPITAL LETTER N WITH ACUTE] +"\u0143" => "N" + +# Ņ [LATIN CAPITAL LETTER N WITH CEDILLA] +"\u0145" => "N" + +# Ň [LATIN CAPITAL LETTER N WITH CARON] +"\u0147" => "N" + +# Ŋ http://en.wikipedia.org/wiki/Eng_(letter) [LATIN CAPITAL LETTER ENG] +"\u014A" => "N" + +# Ɲ [LATIN CAPITAL LETTER N WITH LEFT HOOK] +"\u019D" => "N" + +# Ǹ [LATIN CAPITAL LETTER N WITH GRAVE] +"\u01F8" => "N" + +# Ƞ [LATIN CAPITAL LETTER N WITH LONG RIGHT LEG] +"\u0220" => "N" + +# ɴ [LATIN LETTER SMALL CAPITAL N] +"\u0274" => "N" + +# ᴎ [LATIN LETTER SMALL CAPITAL REVERSED N] +"\u1D0E" => "N" + +# Ṅ [LATIN CAPITAL LETTER N WITH DOT ABOVE] +"\u1E44" => "N" + +# Ṇ [LATIN CAPITAL LETTER N WITH DOT BELOW] +"\u1E46" => "N" + +# Ṉ [LATIN CAPITAL LETTER N WITH LINE BELOW] +"\u1E48" => "N" + +# Ṋ [LATIN CAPITAL LETTER N WITH CIRCUMFLEX BELOW] +"\u1E4A" => "N" + +# Ⓝ [CIRCLED LATIN CAPITAL LETTER N] +"\u24C3" => "N" + +# N [FULLWIDTH LATIN CAPITAL LETTER N] +"\uFF2E" => "N" + +# ñ [LATIN SMALL LETTER N WITH TILDE] +"\u00F1" => "n" + +# ń [LATIN SMALL LETTER N WITH ACUTE] +"\u0144" => "n" + +# ņ [LATIN SMALL LETTER N WITH CEDILLA] +"\u0146" => "n" + +# ň [LATIN SMALL LETTER N WITH CARON] +"\u0148" => "n" + +# ʼn [LATIN SMALL LETTER N PRECEDED BY APOSTROPHE] +"\u0149" => "n" + +# ŋ http://en.wikipedia.org/wiki/Eng_(letter) [LATIN SMALL LETTER ENG] +"\u014B" => "n" + +# ƞ [LATIN SMALL LETTER N WITH LONG RIGHT LEG] +"\u019E" => "n" + +# ǹ [LATIN SMALL LETTER N WITH GRAVE] +"\u01F9" => "n" + +# ȵ [LATIN SMALL LETTER N WITH CURL] +"\u0235" => "n" + +# ɲ [LATIN SMALL LETTER N WITH LEFT HOOK] +"\u0272" => "n" + +# ɳ [LATIN SMALL LETTER N WITH RETROFLEX HOOK] +"\u0273" => "n" + +# ᵰ [LATIN SMALL LETTER N WITH MIDDLE TILDE] +"\u1D70" => "n" + +# ᶇ [LATIN SMALL LETTER N WITH PALATAL HOOK] +"\u1D87" => "n" + +# ṅ [LATIN SMALL LETTER N WITH DOT ABOVE] +"\u1E45" => "n" + +# ṇ [LATIN SMALL LETTER N WITH DOT BELOW] +"\u1E47" => "n" + +# ṉ [LATIN SMALL LETTER N WITH LINE BELOW] +"\u1E49" => "n" + +# ṋ [LATIN SMALL LETTER N WITH CIRCUMFLEX BELOW] +"\u1E4B" => "n" + +# ⁿ [SUPERSCRIPT LATIN SMALL LETTER N] +"\u207F" => "n" + +# ⓝ [CIRCLED LATIN SMALL LETTER N] +"\u24DD" => "n" + +# n [FULLWIDTH LATIN SMALL LETTER N] +"\uFF4E" => "n" + +# NJ [LATIN CAPITAL LETTER NJ] +"\u01CA" => "NJ" + +# Nj [LATIN CAPITAL LETTER N WITH SMALL LETTER J] +"\u01CB" => "Nj" + +# ⒩ [PARENTHESIZED LATIN SMALL LETTER N] +"\u24A9" => "(n)" + +# nj [LATIN SMALL LETTER NJ] +"\u01CC" => "nj" + +# Ò [LATIN CAPITAL LETTER O WITH GRAVE] +"\u00D2" => "O" + +# Ó [LATIN CAPITAL LETTER O WITH ACUTE] +"\u00D3" => "O" + +# Ô [LATIN CAPITAL LETTER O WITH CIRCUMFLEX] +"\u00D4" => "O" + +# Õ [LATIN CAPITAL LETTER O WITH TILDE] +"\u00D5" => "O" + +# Ö [LATIN CAPITAL LETTER O WITH DIAERESIS] +"\u00D6" => "O" + +# Ø [LATIN CAPITAL LETTER O WITH STROKE] +"\u00D8" => "O" + +# Ō [LATIN CAPITAL LETTER O WITH MACRON] +"\u014C" => "O" + +# Ŏ [LATIN CAPITAL LETTER O WITH BREVE] +"\u014E" => "O" + +# Ő [LATIN CAPITAL LETTER O WITH DOUBLE ACUTE] +"\u0150" => "O" + +# Ɔ [LATIN CAPITAL LETTER OPEN O] +"\u0186" => "O" + +# Ɵ [LATIN CAPITAL LETTER O WITH MIDDLE TILDE] +"\u019F" => "O" + +# Ơ [LATIN CAPITAL LETTER O WITH HORN] +"\u01A0" => "O" + +# Ǒ [LATIN CAPITAL LETTER O WITH CARON] +"\u01D1" => "O" + +# Ǫ [LATIN CAPITAL LETTER O WITH OGONEK] +"\u01EA" => "O" + +# Ǭ [LATIN CAPITAL LETTER O WITH OGONEK AND MACRON] +"\u01EC" => "O" + +# Ǿ [LATIN CAPITAL LETTER O WITH STROKE AND ACUTE] +"\u01FE" => "O" + +# Ȍ [LATIN CAPITAL LETTER O WITH DOUBLE GRAVE] +"\u020C" => "O" + +# Ȏ [LATIN CAPITAL LETTER O WITH INVERTED BREVE] +"\u020E" => "O" + +# Ȫ [LATIN CAPITAL LETTER O WITH DIAERESIS AND MACRON] +"\u022A" => "O" + +# Ȭ [LATIN CAPITAL LETTER O WITH TILDE AND MACRON] +"\u022C" => "O" + +# Ȯ [LATIN CAPITAL LETTER O WITH DOT ABOVE] +"\u022E" => "O" + +# Ȱ [LATIN CAPITAL LETTER O WITH DOT ABOVE AND MACRON] +"\u0230" => "O" + +# ᴏ [LATIN LETTER SMALL CAPITAL O] +"\u1D0F" => "O" + +# ᴐ [LATIN LETTER SMALL CAPITAL OPEN O] +"\u1D10" => "O" + +# Ṍ [LATIN CAPITAL LETTER O WITH TILDE AND ACUTE] +"\u1E4C" => "O" + +# Ṏ [LATIN CAPITAL LETTER O WITH TILDE AND DIAERESIS] +"\u1E4E" => "O" + +# Ṑ [LATIN CAPITAL LETTER O WITH MACRON AND GRAVE] +"\u1E50" => "O" + +# Ṓ [LATIN CAPITAL LETTER O WITH MACRON AND ACUTE] +"\u1E52" => "O" + +# Ọ [LATIN CAPITAL LETTER O WITH DOT BELOW] +"\u1ECC" => "O" + +# Ỏ [LATIN CAPITAL LETTER O WITH HOOK ABOVE] +"\u1ECE" => "O" + +# Ố [LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND ACUTE] +"\u1ED0" => "O" + +# Ồ [LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND GRAVE] +"\u1ED2" => "O" + +# Ổ [LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE] +"\u1ED4" => "O" + +# Ỗ [LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND TILDE] +"\u1ED6" => "O" + +# Ộ [LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND DOT BELOW] +"\u1ED8" => "O" + +# Ớ [LATIN CAPITAL LETTER O WITH HORN AND ACUTE] +"\u1EDA" => "O" + +# Ờ [LATIN CAPITAL LETTER O WITH HORN AND GRAVE] +"\u1EDC" => "O" + +# Ở [LATIN CAPITAL LETTER O WITH HORN AND HOOK ABOVE] +"\u1EDE" => "O" + +# Ỡ [LATIN CAPITAL LETTER O WITH HORN AND TILDE] +"\u1EE0" => "O" + +# Ợ [LATIN CAPITAL LETTER O WITH HORN AND DOT BELOW] +"\u1EE2" => "O" + +# Ⓞ [CIRCLED LATIN CAPITAL LETTER O] +"\u24C4" => "O" + +# Ꝋ [LATIN CAPITAL LETTER O WITH LONG STROKE OVERLAY] +"\uA74A" => "O" + +# Ꝍ [LATIN CAPITAL LETTER O WITH LOOP] +"\uA74C" => "O" + +# O [FULLWIDTH LATIN CAPITAL LETTER O] +"\uFF2F" => "O" + +# ò [LATIN SMALL LETTER O WITH GRAVE] +"\u00F2" => "o" + +# ó [LATIN SMALL LETTER O WITH ACUTE] +"\u00F3" => "o" + +# ô [LATIN SMALL LETTER O WITH CIRCUMFLEX] +"\u00F4" => "o" + +# õ [LATIN SMALL LETTER O WITH TILDE] +"\u00F5" => "o" + +# ö [LATIN SMALL LETTER O WITH DIAERESIS] +"\u00F6" => "o" + +# ø [LATIN SMALL LETTER O WITH STROKE] +"\u00F8" => "o" + +# ō [LATIN SMALL LETTER O WITH MACRON] +"\u014D" => "o" + +# ŏ [LATIN SMALL LETTER O WITH BREVE] +"\u014F" => "o" + +# ő [LATIN SMALL LETTER O WITH DOUBLE ACUTE] +"\u0151" => "o" + +# ơ [LATIN SMALL LETTER O WITH HORN] +"\u01A1" => "o" + +# ǒ [LATIN SMALL LETTER O WITH CARON] +"\u01D2" => "o" + +# ǫ [LATIN SMALL LETTER O WITH OGONEK] +"\u01EB" => "o" + +# ǭ [LATIN SMALL LETTER O WITH OGONEK AND MACRON] +"\u01ED" => "o" + +# ǿ [LATIN SMALL LETTER O WITH STROKE AND ACUTE] +"\u01FF" => "o" + +# ȍ [LATIN SMALL LETTER O WITH DOUBLE GRAVE] +"\u020D" => "o" + +# ȏ [LATIN SMALL LETTER O WITH INVERTED BREVE] +"\u020F" => "o" + +# ȫ [LATIN SMALL LETTER O WITH DIAERESIS AND MACRON] +"\u022B" => "o" + +# ȭ [LATIN SMALL LETTER O WITH TILDE AND MACRON] +"\u022D" => "o" + +# ȯ [LATIN SMALL LETTER O WITH DOT ABOVE] +"\u022F" => "o" + +# ȱ [LATIN SMALL LETTER O WITH DOT ABOVE AND MACRON] +"\u0231" => "o" + +# ɔ [LATIN SMALL LETTER OPEN O] +"\u0254" => "o" + +# ɵ [LATIN SMALL LETTER BARRED O] +"\u0275" => "o" + +# ᴖ [LATIN SMALL LETTER TOP HALF O] +"\u1D16" => "o" + +# ᴗ [LATIN SMALL LETTER BOTTOM HALF O] +"\u1D17" => "o" + +# ᶗ [LATIN SMALL LETTER OPEN O WITH RETROFLEX HOOK] +"\u1D97" => "o" + +# ṍ [LATIN SMALL LETTER O WITH TILDE AND ACUTE] +"\u1E4D" => "o" + +# ṏ [LATIN SMALL LETTER O WITH TILDE AND DIAERESIS] +"\u1E4F" => "o" + +# ṑ [LATIN SMALL LETTER O WITH MACRON AND GRAVE] +"\u1E51" => "o" + +# ṓ [LATIN SMALL LETTER O WITH MACRON AND ACUTE] +"\u1E53" => "o" + +# ọ [LATIN SMALL LETTER O WITH DOT BELOW] +"\u1ECD" => "o" + +# ỏ [LATIN SMALL LETTER O WITH HOOK ABOVE] +"\u1ECF" => "o" + +# ố [LATIN SMALL LETTER O WITH CIRCUMFLEX AND ACUTE] +"\u1ED1" => "o" + +# ồ [LATIN SMALL LETTER O WITH CIRCUMFLEX AND GRAVE] +"\u1ED3" => "o" + +# ổ [LATIN SMALL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE] +"\u1ED5" => "o" + +# ỗ [LATIN SMALL LETTER O WITH CIRCUMFLEX AND TILDE] +"\u1ED7" => "o" + +# ộ [LATIN SMALL LETTER O WITH CIRCUMFLEX AND DOT BELOW] +"\u1ED9" => "o" + +# ớ [LATIN SMALL LETTER O WITH HORN AND ACUTE] +"\u1EDB" => "o" + +# ờ [LATIN SMALL LETTER O WITH HORN AND GRAVE] +"\u1EDD" => "o" + +# ở [LATIN SMALL LETTER O WITH HORN AND HOOK ABOVE] +"\u1EDF" => "o" + +# ỡ [LATIN SMALL LETTER O WITH HORN AND TILDE] +"\u1EE1" => "o" + +# ợ [LATIN SMALL LETTER O WITH HORN AND DOT BELOW] +"\u1EE3" => "o" + +# ₒ [LATIN SUBSCRIPT SMALL LETTER O] +"\u2092" => "o" + +# ⓞ [CIRCLED LATIN SMALL LETTER O] +"\u24DE" => "o" + +# ⱺ [LATIN SMALL LETTER O WITH LOW RING INSIDE] +"\u2C7A" => "o" + +# ꝋ [LATIN SMALL LETTER O WITH LONG STROKE OVERLAY] +"\uA74B" => "o" + +# ꝍ [LATIN SMALL LETTER O WITH LOOP] +"\uA74D" => "o" + +# o [FULLWIDTH LATIN SMALL LETTER O] +"\uFF4F" => "o" + +# Œ [LATIN CAPITAL LIGATURE OE] +"\u0152" => "OE" + +# ɶ [LATIN LETTER SMALL CAPITAL OE] +"\u0276" => "OE" + +# Ꝏ [LATIN CAPITAL LETTER OO] +"\uA74E" => "OO" + +# Ȣ http://en.wikipedia.org/wiki/OU [LATIN CAPITAL LETTER OU] +"\u0222" => "OU" + +# ᴕ [LATIN LETTER SMALL CAPITAL OU] +"\u1D15" => "OU" + +# ⒪ [PARENTHESIZED LATIN SMALL LETTER O] +"\u24AA" => "(o)" + +# œ [LATIN SMALL LIGATURE OE] +"\u0153" => "oe" + +# ᴔ [LATIN SMALL LETTER TURNED OE] +"\u1D14" => "oe" + +# ꝏ [LATIN SMALL LETTER OO] +"\uA74F" => "oo" + +# ȣ http://en.wikipedia.org/wiki/OU [LATIN SMALL LETTER OU] +"\u0223" => "ou" + +# Ƥ [LATIN CAPITAL LETTER P WITH HOOK] +"\u01A4" => "P" + +# ᴘ [LATIN LETTER SMALL CAPITAL P] +"\u1D18" => "P" + +# Ṕ [LATIN CAPITAL LETTER P WITH ACUTE] +"\u1E54" => "P" + +# Ṗ [LATIN CAPITAL LETTER P WITH DOT ABOVE] +"\u1E56" => "P" + +# Ⓟ [CIRCLED LATIN CAPITAL LETTER P] +"\u24C5" => "P" + +# Ᵽ [LATIN CAPITAL LETTER P WITH STROKE] +"\u2C63" => "P" + +# Ꝑ [LATIN CAPITAL LETTER P WITH STROKE THROUGH DESCENDER] +"\uA750" => "P" + +# Ꝓ [LATIN CAPITAL LETTER P WITH FLOURISH] +"\uA752" => "P" + +# Ꝕ [LATIN CAPITAL LETTER P WITH SQUIRREL TAIL] +"\uA754" => "P" + +# P [FULLWIDTH LATIN CAPITAL LETTER P] +"\uFF30" => "P" + +# ƥ [LATIN SMALL LETTER P WITH HOOK] +"\u01A5" => "p" + +# ᵱ [LATIN SMALL LETTER P WITH MIDDLE TILDE] +"\u1D71" => "p" + +# ᵽ [LATIN SMALL LETTER P WITH STROKE] +"\u1D7D" => "p" + +# ᶈ [LATIN SMALL LETTER P WITH PALATAL HOOK] +"\u1D88" => "p" + +# ṕ [LATIN SMALL LETTER P WITH ACUTE] +"\u1E55" => "p" + +# ṗ [LATIN SMALL LETTER P WITH DOT ABOVE] +"\u1E57" => "p" + +# ⓟ [CIRCLED LATIN SMALL LETTER P] +"\u24DF" => "p" + +# ꝑ [LATIN SMALL LETTER P WITH STROKE THROUGH DESCENDER] +"\uA751" => "p" + +# ꝓ [LATIN SMALL LETTER P WITH FLOURISH] +"\uA753" => "p" + +# ꝕ [LATIN SMALL LETTER P WITH SQUIRREL TAIL] +"\uA755" => "p" + +# ꟼ [LATIN EPIGRAPHIC LETTER REVERSED P] +"\uA7FC" => "p" + +# p [FULLWIDTH LATIN SMALL LETTER P] +"\uFF50" => "p" + +# ⒫ [PARENTHESIZED LATIN SMALL LETTER P] +"\u24AB" => "(p)" + +# Ɋ [LATIN CAPITAL LETTER SMALL Q WITH HOOK TAIL] +"\u024A" => "Q" + +# Ⓠ [CIRCLED LATIN CAPITAL LETTER Q] +"\u24C6" => "Q" + +# Ꝗ [LATIN CAPITAL LETTER Q WITH STROKE THROUGH DESCENDER] +"\uA756" => "Q" + +# Ꝙ [LATIN CAPITAL LETTER Q WITH DIAGONAL STROKE] +"\uA758" => "Q" + +# Q [FULLWIDTH LATIN CAPITAL LETTER Q] +"\uFF31" => "Q" + +# ĸ http://en.wikipedia.org/wiki/Kra_(letter) [LATIN SMALL LETTER KRA] +"\u0138" => "q" + +# ɋ [LATIN SMALL LETTER Q WITH HOOK TAIL] +"\u024B" => "q" + +# ʠ [LATIN SMALL LETTER Q WITH HOOK] +"\u02A0" => "q" + +# ⓠ [CIRCLED LATIN SMALL LETTER Q] +"\u24E0" => "q" + +# ꝗ [LATIN SMALL LETTER Q WITH STROKE THROUGH DESCENDER] +"\uA757" => "q" + +# ꝙ [LATIN SMALL LETTER Q WITH DIAGONAL STROKE] +"\uA759" => "q" + +# q [FULLWIDTH LATIN SMALL LETTER Q] +"\uFF51" => "q" + +# ⒬ [PARENTHESIZED LATIN SMALL LETTER Q] +"\u24AC" => "(q)" + +# ȹ [LATIN SMALL LETTER QP DIGRAPH] +"\u0239" => "qp" + +# Ŕ [LATIN CAPITAL LETTER R WITH ACUTE] +"\u0154" => "R" + +# Ŗ [LATIN CAPITAL LETTER R WITH CEDILLA] +"\u0156" => "R" + +# Ř [LATIN CAPITAL LETTER R WITH CARON] +"\u0158" => "R" + +# Ȓ [LATIN CAPITAL LETTER R WITH DOUBLE GRAVE] +"\u0210" => "R" + +# Ȓ [LATIN CAPITAL LETTER R WITH INVERTED BREVE] +"\u0212" => "R" + +# Ɍ [LATIN CAPITAL LETTER R WITH STROKE] +"\u024C" => "R" + +# ʀ [LATIN LETTER SMALL CAPITAL R] +"\u0280" => "R" + +# ʁ [LATIN LETTER SMALL CAPITAL INVERTED R] +"\u0281" => "R" + +# ᴙ [LATIN LETTER SMALL CAPITAL REVERSED R] +"\u1D19" => "R" + +# ᴚ [LATIN LETTER SMALL CAPITAL TURNED R] +"\u1D1A" => "R" + +# Ṙ [LATIN CAPITAL LETTER R WITH DOT ABOVE] +"\u1E58" => "R" + +# Ṛ [LATIN CAPITAL LETTER R WITH DOT BELOW] +"\u1E5A" => "R" + +# Ṝ [LATIN CAPITAL LETTER R WITH DOT BELOW AND MACRON] +"\u1E5C" => "R" + +# Ṟ [LATIN CAPITAL LETTER R WITH LINE BELOW] +"\u1E5E" => "R" + +# Ⓡ [CIRCLED LATIN CAPITAL LETTER R] +"\u24C7" => "R" + +# Ɽ [LATIN CAPITAL LETTER R WITH TAIL] +"\u2C64" => "R" + +# Ꝛ [LATIN CAPITAL LETTER R ROTUNDA] +"\uA75A" => "R" + +# Ꞃ [LATIN CAPITAL LETTER INSULAR R] +"\uA782" => "R" + +# R [FULLWIDTH LATIN CAPITAL LETTER R] +"\uFF32" => "R" + +# ŕ [LATIN SMALL LETTER R WITH ACUTE] +"\u0155" => "r" + +# ŗ [LATIN SMALL LETTER R WITH CEDILLA] +"\u0157" => "r" + +# ř [LATIN SMALL LETTER R WITH CARON] +"\u0159" => "r" + +# ȑ [LATIN SMALL LETTER R WITH DOUBLE GRAVE] +"\u0211" => "r" + +# ȓ [LATIN SMALL LETTER R WITH INVERTED BREVE] +"\u0213" => "r" + +# ɍ [LATIN SMALL LETTER R WITH STROKE] +"\u024D" => "r" + +# ɼ [LATIN SMALL LETTER R WITH LONG LEG] +"\u027C" => "r" + +# ɽ [LATIN SMALL LETTER R WITH TAIL] +"\u027D" => "r" + +# ɾ [LATIN SMALL LETTER R WITH FISHHOOK] +"\u027E" => "r" + +# ɿ [LATIN SMALL LETTER REVERSED R WITH FISHHOOK] +"\u027F" => "r" + +# ᵣ [LATIN SUBSCRIPT SMALL LETTER R] +"\u1D63" => "r" + +# ᵲ [LATIN SMALL LETTER R WITH MIDDLE TILDE] +"\u1D72" => "r" + +# ᵳ [LATIN SMALL LETTER R WITH FISHHOOK AND MIDDLE TILDE] +"\u1D73" => "r" + +# ᶉ [LATIN SMALL LETTER R WITH PALATAL HOOK] +"\u1D89" => "r" + +# ṙ [LATIN SMALL LETTER R WITH DOT ABOVE] +"\u1E59" => "r" + +# ṛ [LATIN SMALL LETTER R WITH DOT BELOW] +"\u1E5B" => "r" + +# ṝ [LATIN SMALL LETTER R WITH DOT BELOW AND MACRON] +"\u1E5D" => "r" + +# ṟ [LATIN SMALL LETTER R WITH LINE BELOW] +"\u1E5F" => "r" + +# ⓡ [CIRCLED LATIN SMALL LETTER R] +"\u24E1" => "r" + +# ꝛ [LATIN SMALL LETTER R ROTUNDA] +"\uA75B" => "r" + +# ꞃ [LATIN SMALL LETTER INSULAR R] +"\uA783" => "r" + +# r [FULLWIDTH LATIN SMALL LETTER R] +"\uFF52" => "r" + +# ⒭ [PARENTHESIZED LATIN SMALL LETTER R] +"\u24AD" => "(r)" + +# Ś [LATIN CAPITAL LETTER S WITH ACUTE] +"\u015A" => "S" + +# Ŝ [LATIN CAPITAL LETTER S WITH CIRCUMFLEX] +"\u015C" => "S" + +# Ş [LATIN CAPITAL LETTER S WITH CEDILLA] +"\u015E" => "S" + +# Š [LATIN CAPITAL LETTER S WITH CARON] +"\u0160" => "S" + +# Ș [LATIN CAPITAL LETTER S WITH COMMA BELOW] +"\u0218" => "S" + +# Ṡ [LATIN CAPITAL LETTER S WITH DOT ABOVE] +"\u1E60" => "S" + +# Ṣ [LATIN CAPITAL LETTER S WITH DOT BELOW] +"\u1E62" => "S" + +# Ṥ [LATIN CAPITAL LETTER S WITH ACUTE AND DOT ABOVE] +"\u1E64" => "S" + +# Ṧ [LATIN CAPITAL LETTER S WITH CARON AND DOT ABOVE] +"\u1E66" => "S" + +# Ṩ [LATIN CAPITAL LETTER S WITH DOT BELOW AND DOT ABOVE] +"\u1E68" => "S" + +# Ⓢ [CIRCLED LATIN CAPITAL LETTER S] +"\u24C8" => "S" + +# ꜱ [LATIN LETTER SMALL CAPITAL S] +"\uA731" => "S" + +# ꞅ [LATIN SMALL LETTER INSULAR S] +"\uA785" => "S" + +# S [FULLWIDTH LATIN CAPITAL LETTER S] +"\uFF33" => "S" + +# ś [LATIN SMALL LETTER S WITH ACUTE] +"\u015B" => "s" + +# ŝ [LATIN SMALL LETTER S WITH CIRCUMFLEX] +"\u015D" => "s" + +# ş [LATIN SMALL LETTER S WITH CEDILLA] +"\u015F" => "s" + +# š [LATIN SMALL LETTER S WITH CARON] +"\u0161" => "s" + +# ſ http://en.wikipedia.org/wiki/Long_S [LATIN SMALL LETTER LONG S] +"\u017F" => "s" + +# ș [LATIN SMALL LETTER S WITH COMMA BELOW] +"\u0219" => "s" + +# ȿ [LATIN SMALL LETTER S WITH SWASH TAIL] +"\u023F" => "s" + +# ʂ [LATIN SMALL LETTER S WITH HOOK] +"\u0282" => "s" + +# ᵴ [LATIN SMALL LETTER S WITH MIDDLE TILDE] +"\u1D74" => "s" + +# ᶊ [LATIN SMALL LETTER S WITH PALATAL HOOK] +"\u1D8A" => "s" + +# ṡ [LATIN SMALL LETTER S WITH DOT ABOVE] +"\u1E61" => "s" + +# ṣ [LATIN SMALL LETTER S WITH DOT BELOW] +"\u1E63" => "s" + +# ṥ [LATIN SMALL LETTER S WITH ACUTE AND DOT ABOVE] +"\u1E65" => "s" + +# ṧ [LATIN SMALL LETTER S WITH CARON AND DOT ABOVE] +"\u1E67" => "s" + +# ṩ [LATIN SMALL LETTER S WITH DOT BELOW AND DOT ABOVE] +"\u1E69" => "s" + +# ẜ [LATIN SMALL LETTER LONG S WITH DIAGONAL STROKE] +"\u1E9C" => "s" + +# ẝ [LATIN SMALL LETTER LONG S WITH HIGH STROKE] +"\u1E9D" => "s" + +# ⓢ [CIRCLED LATIN SMALL LETTER S] +"\u24E2" => "s" + +# Ꞅ [LATIN CAPITAL LETTER INSULAR S] +"\uA784" => "s" + +# s [FULLWIDTH LATIN SMALL LETTER S] +"\uFF53" => "s" + +# ẞ [LATIN CAPITAL LETTER SHARP S] +"\u1E9E" => "SS" + +# ⒮ [PARENTHESIZED LATIN SMALL LETTER S] +"\u24AE" => "(s)" + +# ß [LATIN SMALL LETTER SHARP S] +"\u00DF" => "ss" + +# st [LATIN SMALL LIGATURE ST] +"\uFB06" => "st" + +# Ţ [LATIN CAPITAL LETTER T WITH CEDILLA] +"\u0162" => "T" + +# Ť [LATIN CAPITAL LETTER T WITH CARON] +"\u0164" => "T" + +# Ŧ [LATIN CAPITAL LETTER T WITH STROKE] +"\u0166" => "T" + +# Ƭ [LATIN CAPITAL LETTER T WITH HOOK] +"\u01AC" => "T" + +# Ʈ [LATIN CAPITAL LETTER T WITH RETROFLEX HOOK] +"\u01AE" => "T" + +# Ț [LATIN CAPITAL LETTER T WITH COMMA BELOW] +"\u021A" => "T" + +# Ⱦ [LATIN CAPITAL LETTER T WITH DIAGONAL STROKE] +"\u023E" => "T" + +# ᴛ [LATIN LETTER SMALL CAPITAL T] +"\u1D1B" => "T" + +# Ṫ [LATIN CAPITAL LETTER T WITH DOT ABOVE] +"\u1E6A" => "T" + +# Ṭ [LATIN CAPITAL LETTER T WITH DOT BELOW] +"\u1E6C" => "T" + +# Ṯ [LATIN CAPITAL LETTER T WITH LINE BELOW] +"\u1E6E" => "T" + +# Ṱ [LATIN CAPITAL LETTER T WITH CIRCUMFLEX BELOW] +"\u1E70" => "T" + +# Ⓣ [CIRCLED LATIN CAPITAL LETTER T] +"\u24C9" => "T" + +# Ꞇ [LATIN CAPITAL LETTER INSULAR T] +"\uA786" => "T" + +# T [FULLWIDTH LATIN CAPITAL LETTER T] +"\uFF34" => "T" + +# ţ [LATIN SMALL LETTER T WITH CEDILLA] +"\u0163" => "t" + +# ť [LATIN SMALL LETTER T WITH CARON] +"\u0165" => "t" + +# ŧ [LATIN SMALL LETTER T WITH STROKE] +"\u0167" => "t" + +# ƫ [LATIN SMALL LETTER T WITH PALATAL HOOK] +"\u01AB" => "t" + +# ƭ [LATIN SMALL LETTER T WITH HOOK] +"\u01AD" => "t" + +# ț [LATIN SMALL LETTER T WITH COMMA BELOW] +"\u021B" => "t" + +# ȶ [LATIN SMALL LETTER T WITH CURL] +"\u0236" => "t" + +# ʇ [LATIN SMALL LETTER TURNED T] +"\u0287" => "t" + +# ʈ [LATIN SMALL LETTER T WITH RETROFLEX HOOK] +"\u0288" => "t" + +# ᵵ [LATIN SMALL LETTER T WITH MIDDLE TILDE] +"\u1D75" => "t" + +# ṫ [LATIN SMALL LETTER T WITH DOT ABOVE] +"\u1E6B" => "t" + +# ṭ [LATIN SMALL LETTER T WITH DOT BELOW] +"\u1E6D" => "t" + +# ṯ [LATIN SMALL LETTER T WITH LINE BELOW] +"\u1E6F" => "t" + +# ṱ [LATIN SMALL LETTER T WITH CIRCUMFLEX BELOW] +"\u1E71" => "t" + +# ẗ [LATIN SMALL LETTER T WITH DIAERESIS] +"\u1E97" => "t" + +# ⓣ [CIRCLED LATIN SMALL LETTER T] +"\u24E3" => "t" + +# ⱦ [LATIN SMALL LETTER T WITH DIAGONAL STROKE] +"\u2C66" => "t" + +# t [FULLWIDTH LATIN SMALL LETTER T] +"\uFF54" => "t" + +# Þ [LATIN CAPITAL LETTER THORN] +"\u00DE" => "TH" + +# Ꝧ [LATIN CAPITAL LETTER THORN WITH STROKE THROUGH DESCENDER] +"\uA766" => "TH" + +# Ꜩ [LATIN CAPITAL LETTER TZ] +"\uA728" => "TZ" + +# ⒯ [PARENTHESIZED LATIN SMALL LETTER T] +"\u24AF" => "(t)" + +# ʨ [LATIN SMALL LETTER TC DIGRAPH WITH CURL] +"\u02A8" => "tc" + +# þ [LATIN SMALL LETTER THORN] +"\u00FE" => "th" + +# ᵺ [LATIN SMALL LETTER TH WITH STRIKETHROUGH] +"\u1D7A" => "th" + +# ꝧ [LATIN SMALL LETTER THORN WITH STROKE THROUGH DESCENDER] +"\uA767" => "th" + +# ʦ [LATIN SMALL LETTER TS DIGRAPH] +"\u02A6" => "ts" + +# ꜩ [LATIN SMALL LETTER TZ] +"\uA729" => "tz" + +# Ù [LATIN CAPITAL LETTER U WITH GRAVE] +"\u00D9" => "U" + +# Ú [LATIN CAPITAL LETTER U WITH ACUTE] +"\u00DA" => "U" + +# Û [LATIN CAPITAL LETTER U WITH CIRCUMFLEX] +"\u00DB" => "U" + +# Ü [LATIN CAPITAL LETTER U WITH DIAERESIS] +"\u00DC" => "U" + +# Ũ [LATIN CAPITAL LETTER U WITH TILDE] +"\u0168" => "U" + +# Ū [LATIN CAPITAL LETTER U WITH MACRON] +"\u016A" => "U" + +# Ŭ [LATIN CAPITAL LETTER U WITH BREVE] +"\u016C" => "U" + +# Ů [LATIN CAPITAL LETTER U WITH RING ABOVE] +"\u016E" => "U" + +# Ű [LATIN CAPITAL LETTER U WITH DOUBLE ACUTE] +"\u0170" => "U" + +# Ų [LATIN CAPITAL LETTER U WITH OGONEK] +"\u0172" => "U" + +# Ư [LATIN CAPITAL LETTER U WITH HORN] +"\u01AF" => "U" + +# Ǔ [LATIN CAPITAL LETTER U WITH CARON] +"\u01D3" => "U" + +# Ǖ [LATIN CAPITAL LETTER U WITH DIAERESIS AND MACRON] +"\u01D5" => "U" + +# Ǘ [LATIN CAPITAL LETTER U WITH DIAERESIS AND ACUTE] +"\u01D7" => "U" + +# Ǚ [LATIN CAPITAL LETTER U WITH DIAERESIS AND CARON] +"\u01D9" => "U" + +# Ǜ [LATIN CAPITAL LETTER U WITH DIAERESIS AND GRAVE] +"\u01DB" => "U" + +# Ȕ [LATIN CAPITAL LETTER U WITH DOUBLE GRAVE] +"\u0214" => "U" + +# Ȗ [LATIN CAPITAL LETTER U WITH INVERTED BREVE] +"\u0216" => "U" + +# Ʉ [LATIN CAPITAL LETTER U BAR] +"\u0244" => "U" + +# ᴜ [LATIN LETTER SMALL CAPITAL U] +"\u1D1C" => "U" + +# ᵾ [LATIN SMALL CAPITAL LETTER U WITH STROKE] +"\u1D7E" => "U" + +# Ṳ [LATIN CAPITAL LETTER U WITH DIAERESIS BELOW] +"\u1E72" => "U" + +# Ṵ [LATIN CAPITAL LETTER U WITH TILDE BELOW] +"\u1E74" => "U" + +# Ṷ [LATIN CAPITAL LETTER U WITH CIRCUMFLEX BELOW] +"\u1E76" => "U" + +# Ṹ [LATIN CAPITAL LETTER U WITH TILDE AND ACUTE] +"\u1E78" => "U" + +# Ṻ [LATIN CAPITAL LETTER U WITH MACRON AND DIAERESIS] +"\u1E7A" => "U" + +# Ụ [LATIN CAPITAL LETTER U WITH DOT BELOW] +"\u1EE4" => "U" + +# Ủ [LATIN CAPITAL LETTER U WITH HOOK ABOVE] +"\u1EE6" => "U" + +# Ứ [LATIN CAPITAL LETTER U WITH HORN AND ACUTE] +"\u1EE8" => "U" + +# Ừ [LATIN CAPITAL LETTER U WITH HORN AND GRAVE] +"\u1EEA" => "U" + +# Ử [LATIN CAPITAL LETTER U WITH HORN AND HOOK ABOVE] +"\u1EEC" => "U" + +# Ữ [LATIN CAPITAL LETTER U WITH HORN AND TILDE] +"\u1EEE" => "U" + +# Ự [LATIN CAPITAL LETTER U WITH HORN AND DOT BELOW] +"\u1EF0" => "U" + +# Ⓤ [CIRCLED LATIN CAPITAL LETTER U] +"\u24CA" => "U" + +# U [FULLWIDTH LATIN CAPITAL LETTER U] +"\uFF35" => "U" + +# ù [LATIN SMALL LETTER U WITH GRAVE] +"\u00F9" => "u" + +# ú [LATIN SMALL LETTER U WITH ACUTE] +"\u00FA" => "u" + +# û [LATIN SMALL LETTER U WITH CIRCUMFLEX] +"\u00FB" => "u" + +# ü [LATIN SMALL LETTER U WITH DIAERESIS] +"\u00FC" => "u" + +# ũ [LATIN SMALL LETTER U WITH TILDE] +"\u0169" => "u" + +# ū [LATIN SMALL LETTER U WITH MACRON] +"\u016B" => "u" + +# ŭ [LATIN SMALL LETTER U WITH BREVE] +"\u016D" => "u" + +# ů [LATIN SMALL LETTER U WITH RING ABOVE] +"\u016F" => "u" + +# ű [LATIN SMALL LETTER U WITH DOUBLE ACUTE] +"\u0171" => "u" + +# ų [LATIN SMALL LETTER U WITH OGONEK] +"\u0173" => "u" + +# ư [LATIN SMALL LETTER U WITH HORN] +"\u01B0" => "u" + +# ǔ [LATIN SMALL LETTER U WITH CARON] +"\u01D4" => "u" + +# ǖ [LATIN SMALL LETTER U WITH DIAERESIS AND MACRON] +"\u01D6" => "u" + +# ǘ [LATIN SMALL LETTER U WITH DIAERESIS AND ACUTE] +"\u01D8" => "u" + +# ǚ [LATIN SMALL LETTER U WITH DIAERESIS AND CARON] +"\u01DA" => "u" + +# ǜ [LATIN SMALL LETTER U WITH DIAERESIS AND GRAVE] +"\u01DC" => "u" + +# ȕ [LATIN SMALL LETTER U WITH DOUBLE GRAVE] +"\u0215" => "u" + +# ȗ [LATIN SMALL LETTER U WITH INVERTED BREVE] +"\u0217" => "u" + +# ʉ [LATIN SMALL LETTER U BAR] +"\u0289" => "u" + +# ᵤ [LATIN SUBSCRIPT SMALL LETTER U] +"\u1D64" => "u" + +# ᶙ [LATIN SMALL LETTER U WITH RETROFLEX HOOK] +"\u1D99" => "u" + +# ṳ [LATIN SMALL LETTER U WITH DIAERESIS BELOW] +"\u1E73" => "u" + +# ṵ [LATIN SMALL LETTER U WITH TILDE BELOW] +"\u1E75" => "u" + +# ṷ [LATIN SMALL LETTER U WITH CIRCUMFLEX BELOW] +"\u1E77" => "u" + +# ṹ [LATIN SMALL LETTER U WITH TILDE AND ACUTE] +"\u1E79" => "u" + +# ṻ [LATIN SMALL LETTER U WITH MACRON AND DIAERESIS] +"\u1E7B" => "u" + +# ụ [LATIN SMALL LETTER U WITH DOT BELOW] +"\u1EE5" => "u" + +# ủ [LATIN SMALL LETTER U WITH HOOK ABOVE] +"\u1EE7" => "u" + +# ứ [LATIN SMALL LETTER U WITH HORN AND ACUTE] +"\u1EE9" => "u" + +# ừ [LATIN SMALL LETTER U WITH HORN AND GRAVE] +"\u1EEB" => "u" + +# ử [LATIN SMALL LETTER U WITH HORN AND HOOK ABOVE] +"\u1EED" => "u" + +# ữ [LATIN SMALL LETTER U WITH HORN AND TILDE] +"\u1EEF" => "u" + +# ự [LATIN SMALL LETTER U WITH HORN AND DOT BELOW] +"\u1EF1" => "u" + +# ⓤ [CIRCLED LATIN SMALL LETTER U] +"\u24E4" => "u" + +# u [FULLWIDTH LATIN SMALL LETTER U] +"\uFF55" => "u" + +# ⒰ [PARENTHESIZED LATIN SMALL LETTER U] +"\u24B0" => "(u)" + +# ᵫ [LATIN SMALL LETTER UE] +"\u1D6B" => "ue" + +# Ʋ [LATIN CAPITAL LETTER V WITH HOOK] +"\u01B2" => "V" + +# Ʌ [LATIN CAPITAL LETTER TURNED V] +"\u0245" => "V" + +# ᴠ [LATIN LETTER SMALL CAPITAL V] +"\u1D20" => "V" + +# Ṽ [LATIN CAPITAL LETTER V WITH TILDE] +"\u1E7C" => "V" + +# Ṿ [LATIN CAPITAL LETTER V WITH DOT BELOW] +"\u1E7E" => "V" + +# Ỽ [LATIN CAPITAL LETTER MIDDLE-WELSH V] +"\u1EFC" => "V" + +# Ⓥ [CIRCLED LATIN CAPITAL LETTER V] +"\u24CB" => "V" + +# Ꝟ [LATIN CAPITAL LETTER V WITH DIAGONAL STROKE] +"\uA75E" => "V" + +# Ꝩ [LATIN CAPITAL LETTER VEND] +"\uA768" => "V" + +# V [FULLWIDTH LATIN CAPITAL LETTER V] +"\uFF36" => "V" + +# ʋ [LATIN SMALL LETTER V WITH HOOK] +"\u028B" => "v" + +# ʌ [LATIN SMALL LETTER TURNED V] +"\u028C" => "v" + +# ᵥ [LATIN SUBSCRIPT SMALL LETTER V] +"\u1D65" => "v" + +# ᶌ [LATIN SMALL LETTER V WITH PALATAL HOOK] +"\u1D8C" => "v" + +# ṽ [LATIN SMALL LETTER V WITH TILDE] +"\u1E7D" => "v" + +# ṿ [LATIN SMALL LETTER V WITH DOT BELOW] +"\u1E7F" => "v" + +# ⓥ [CIRCLED LATIN SMALL LETTER V] +"\u24E5" => "v" + +# ⱱ [LATIN SMALL LETTER V WITH RIGHT HOOK] +"\u2C71" => "v" + +# ⱴ [LATIN SMALL LETTER V WITH CURL] +"\u2C74" => "v" + +# ꝟ [LATIN SMALL LETTER V WITH DIAGONAL STROKE] +"\uA75F" => "v" + +# v [FULLWIDTH LATIN SMALL LETTER V] +"\uFF56" => "v" + +# Ꝡ [LATIN CAPITAL LETTER VY] +"\uA760" => "VY" + +# ⒱ [PARENTHESIZED LATIN SMALL LETTER V] +"\u24B1" => "(v)" + +# ꝡ [LATIN SMALL LETTER VY] +"\uA761" => "vy" + +# Ŵ [LATIN CAPITAL LETTER W WITH CIRCUMFLEX] +"\u0174" => "W" + +# Ƿ http://en.wikipedia.org/wiki/Wynn [LATIN CAPITAL LETTER WYNN] +"\u01F7" => "W" + +# ᴡ [LATIN LETTER SMALL CAPITAL W] +"\u1D21" => "W" + +# Ẁ [LATIN CAPITAL LETTER W WITH GRAVE] +"\u1E80" => "W" + +# Ẃ [LATIN CAPITAL LETTER W WITH ACUTE] +"\u1E82" => "W" + +# Ẅ [LATIN CAPITAL LETTER W WITH DIAERESIS] +"\u1E84" => "W" + +# Ẇ [LATIN CAPITAL LETTER W WITH DOT ABOVE] +"\u1E86" => "W" + +# Ẉ [LATIN CAPITAL LETTER W WITH DOT BELOW] +"\u1E88" => "W" + +# Ⓦ [CIRCLED LATIN CAPITAL LETTER W] +"\u24CC" => "W" + +# Ⱳ [LATIN CAPITAL LETTER W WITH HOOK] +"\u2C72" => "W" + +# W [FULLWIDTH LATIN CAPITAL LETTER W] +"\uFF37" => "W" + +# ŵ [LATIN SMALL LETTER W WITH CIRCUMFLEX] +"\u0175" => "w" + +# ƿ http://en.wikipedia.org/wiki/Wynn [LATIN LETTER WYNN] +"\u01BF" => "w" + +# ʍ [LATIN SMALL LETTER TURNED W] +"\u028D" => "w" + +# ẁ [LATIN SMALL LETTER W WITH GRAVE] +"\u1E81" => "w" + +# ẃ [LATIN SMALL LETTER W WITH ACUTE] +"\u1E83" => "w" + +# ẅ [LATIN SMALL LETTER W WITH DIAERESIS] +"\u1E85" => "w" + +# ẇ [LATIN SMALL LETTER W WITH DOT ABOVE] +"\u1E87" => "w" + +# ẉ [LATIN SMALL LETTER W WITH DOT BELOW] +"\u1E89" => "w" + +# ẘ [LATIN SMALL LETTER W WITH RING ABOVE] +"\u1E98" => "w" + +# ⓦ [CIRCLED LATIN SMALL LETTER W] +"\u24E6" => "w" + +# ⱳ [LATIN SMALL LETTER W WITH HOOK] +"\u2C73" => "w" + +# w [FULLWIDTH LATIN SMALL LETTER W] +"\uFF57" => "w" + +# ⒲ [PARENTHESIZED LATIN SMALL LETTER W] +"\u24B2" => "(w)" + +# Ẋ [LATIN CAPITAL LETTER X WITH DOT ABOVE] +"\u1E8A" => "X" + +# Ẍ [LATIN CAPITAL LETTER X WITH DIAERESIS] +"\u1E8C" => "X" + +# Ⓧ [CIRCLED LATIN CAPITAL LETTER X] +"\u24CD" => "X" + +# X [FULLWIDTH LATIN CAPITAL LETTER X] +"\uFF38" => "X" + +# ᶍ [LATIN SMALL LETTER X WITH PALATAL HOOK] +"\u1D8D" => "x" + +# ẋ [LATIN SMALL LETTER X WITH DOT ABOVE] +"\u1E8B" => "x" + +# ẍ [LATIN SMALL LETTER X WITH DIAERESIS] +"\u1E8D" => "x" + +# ₓ [LATIN SUBSCRIPT SMALL LETTER X] +"\u2093" => "x" + +# ⓧ [CIRCLED LATIN SMALL LETTER X] +"\u24E7" => "x" + +# x [FULLWIDTH LATIN SMALL LETTER X] +"\uFF58" => "x" + +# ⒳ [PARENTHESIZED LATIN SMALL LETTER X] +"\u24B3" => "(x)" + +# Ý [LATIN CAPITAL LETTER Y WITH ACUTE] +"\u00DD" => "Y" + +# Ŷ [LATIN CAPITAL LETTER Y WITH CIRCUMFLEX] +"\u0176" => "Y" + +# Ÿ [LATIN CAPITAL LETTER Y WITH DIAERESIS] +"\u0178" => "Y" + +# Ƴ [LATIN CAPITAL LETTER Y WITH HOOK] +"\u01B3" => "Y" + +# Ȳ [LATIN CAPITAL LETTER Y WITH MACRON] +"\u0232" => "Y" + +# Ɏ [LATIN CAPITAL LETTER Y WITH STROKE] +"\u024E" => "Y" + +# ʏ [LATIN LETTER SMALL CAPITAL Y] +"\u028F" => "Y" + +# Ẏ [LATIN CAPITAL LETTER Y WITH DOT ABOVE] +"\u1E8E" => "Y" + +# Ỳ [LATIN CAPITAL LETTER Y WITH GRAVE] +"\u1EF2" => "Y" + +# Ỵ [LATIN CAPITAL LETTER Y WITH DOT BELOW] +"\u1EF4" => "Y" + +# Ỷ [LATIN CAPITAL LETTER Y WITH HOOK ABOVE] +"\u1EF6" => "Y" + +# Ỹ [LATIN CAPITAL LETTER Y WITH TILDE] +"\u1EF8" => "Y" + +# Ỿ [LATIN CAPITAL LETTER Y WITH LOOP] +"\u1EFE" => "Y" + +# Ⓨ [CIRCLED LATIN CAPITAL LETTER Y] +"\u24CE" => "Y" + +# Y [FULLWIDTH LATIN CAPITAL LETTER Y] +"\uFF39" => "Y" + +# ý [LATIN SMALL LETTER Y WITH ACUTE] +"\u00FD" => "y" + +# ÿ [LATIN SMALL LETTER Y WITH DIAERESIS] +"\u00FF" => "y" + +# ŷ [LATIN SMALL LETTER Y WITH CIRCUMFLEX] +"\u0177" => "y" + +# ƴ [LATIN SMALL LETTER Y WITH HOOK] +"\u01B4" => "y" + +# ȳ [LATIN SMALL LETTER Y WITH MACRON] +"\u0233" => "y" + +# ɏ [LATIN SMALL LETTER Y WITH STROKE] +"\u024F" => "y" + +# ʎ [LATIN SMALL LETTER TURNED Y] +"\u028E" => "y" + +# ẏ [LATIN SMALL LETTER Y WITH DOT ABOVE] +"\u1E8F" => "y" + +# ẙ [LATIN SMALL LETTER Y WITH RING ABOVE] +"\u1E99" => "y" + +# ỳ [LATIN SMALL LETTER Y WITH GRAVE] +"\u1EF3" => "y" + +# ỵ [LATIN SMALL LETTER Y WITH DOT BELOW] +"\u1EF5" => "y" + +# ỷ [LATIN SMALL LETTER Y WITH HOOK ABOVE] +"\u1EF7" => "y" + +# ỹ [LATIN SMALL LETTER Y WITH TILDE] +"\u1EF9" => "y" + +# ỿ [LATIN SMALL LETTER Y WITH LOOP] +"\u1EFF" => "y" + +# ⓨ [CIRCLED LATIN SMALL LETTER Y] +"\u24E8" => "y" + +# y [FULLWIDTH LATIN SMALL LETTER Y] +"\uFF59" => "y" + +# ⒴ [PARENTHESIZED LATIN SMALL LETTER Y] +"\u24B4" => "(y)" + +# Ź [LATIN CAPITAL LETTER Z WITH ACUTE] +"\u0179" => "Z" + +# Ż [LATIN CAPITAL LETTER Z WITH DOT ABOVE] +"\u017B" => "Z" + +# Ž [LATIN CAPITAL LETTER Z WITH CARON] +"\u017D" => "Z" + +# Ƶ [LATIN CAPITAL LETTER Z WITH STROKE] +"\u01B5" => "Z" + +# Ȝ http://en.wikipedia.org/wiki/Yogh [LATIN CAPITAL LETTER YOGH] +"\u021C" => "Z" + +# Ȥ [LATIN CAPITAL LETTER Z WITH HOOK] +"\u0224" => "Z" + +# ᴢ [LATIN LETTER SMALL CAPITAL Z] +"\u1D22" => "Z" + +# Ẑ [LATIN CAPITAL LETTER Z WITH CIRCUMFLEX] +"\u1E90" => "Z" + +# Ẓ [LATIN CAPITAL LETTER Z WITH DOT BELOW] +"\u1E92" => "Z" + +# Ẕ [LATIN CAPITAL LETTER Z WITH LINE BELOW] +"\u1E94" => "Z" + +# Ⓩ [CIRCLED LATIN CAPITAL LETTER Z] +"\u24CF" => "Z" + +# Ⱬ [LATIN CAPITAL LETTER Z WITH DESCENDER] +"\u2C6B" => "Z" + +# Ꝣ [LATIN CAPITAL LETTER VISIGOTHIC Z] +"\uA762" => "Z" + +# Z [FULLWIDTH LATIN CAPITAL LETTER Z] +"\uFF3A" => "Z" + +# ź [LATIN SMALL LETTER Z WITH ACUTE] +"\u017A" => "z" + +# ż [LATIN SMALL LETTER Z WITH DOT ABOVE] +"\u017C" => "z" + +# ž [LATIN SMALL LETTER Z WITH CARON] +"\u017E" => "z" + +# ƶ [LATIN SMALL LETTER Z WITH STROKE] +"\u01B6" => "z" + +# ȝ http://en.wikipedia.org/wiki/Yogh [LATIN SMALL LETTER YOGH] +"\u021D" => "z" + +# ȥ [LATIN SMALL LETTER Z WITH HOOK] +"\u0225" => "z" + +# ɀ [LATIN SMALL LETTER Z WITH SWASH TAIL] +"\u0240" => "z" + +# ʐ [LATIN SMALL LETTER Z WITH RETROFLEX HOOK] +"\u0290" => "z" + +# ʑ [LATIN SMALL LETTER Z WITH CURL] +"\u0291" => "z" + +# ᵶ [LATIN SMALL LETTER Z WITH MIDDLE TILDE] +"\u1D76" => "z" + +# ᶎ [LATIN SMALL LETTER Z WITH PALATAL HOOK] +"\u1D8E" => "z" + +# ẑ [LATIN SMALL LETTER Z WITH CIRCUMFLEX] +"\u1E91" => "z" + +# ẓ [LATIN SMALL LETTER Z WITH DOT BELOW] +"\u1E93" => "z" + +# ẕ [LATIN SMALL LETTER Z WITH LINE BELOW] +"\u1E95" => "z" + +# ⓩ [CIRCLED LATIN SMALL LETTER Z] +"\u24E9" => "z" + +# ⱬ [LATIN SMALL LETTER Z WITH DESCENDER] +"\u2C6C" => "z" + +# ꝣ [LATIN SMALL LETTER VISIGOTHIC Z] +"\uA763" => "z" + +# z [FULLWIDTH LATIN SMALL LETTER Z] +"\uFF5A" => "z" + +# ⒵ [PARENTHESIZED LATIN SMALL LETTER Z] +"\u24B5" => "(z)" + +# ⁰ [SUPERSCRIPT ZERO] +"\u2070" => "0" + +# ₀ [SUBSCRIPT ZERO] +"\u2080" => "0" + +# ⓪ [CIRCLED DIGIT ZERO] +"\u24EA" => "0" + +# ⓿ [NEGATIVE CIRCLED DIGIT ZERO] +"\u24FF" => "0" + +# 0 [FULLWIDTH DIGIT ZERO] +"\uFF10" => "0" + +# ¹ [SUPERSCRIPT ONE] +"\u00B9" => "1" + +# ₁ [SUBSCRIPT ONE] +"\u2081" => "1" + +# ① [CIRCLED DIGIT ONE] +"\u2460" => "1" + +# ⓵ [DOUBLE CIRCLED DIGIT ONE] +"\u24F5" => "1" + +# ❶ [DINGBAT NEGATIVE CIRCLED DIGIT ONE] +"\u2776" => "1" + +# ➀ [DINGBAT CIRCLED SANS-SERIF DIGIT ONE] +"\u2780" => "1" + +# ➊ [DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT ONE] +"\u278A" => "1" + +# 1 [FULLWIDTH DIGIT ONE] +"\uFF11" => "1" + +# ⒈ [DIGIT ONE FULL STOP] +"\u2488" => "1." + +# ⑴ [PARENTHESIZED DIGIT ONE] +"\u2474" => "(1)" + +# ² [SUPERSCRIPT TWO] +"\u00B2" => "2" + +# ₂ [SUBSCRIPT TWO] +"\u2082" => "2" + +# ② [CIRCLED DIGIT TWO] +"\u2461" => "2" + +# ⓶ [DOUBLE CIRCLED DIGIT TWO] +"\u24F6" => "2" + +# ❷ [DINGBAT NEGATIVE CIRCLED DIGIT TWO] +"\u2777" => "2" + +# ➁ [DINGBAT CIRCLED SANS-SERIF DIGIT TWO] +"\u2781" => "2" + +# ➋ [DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT TWO] +"\u278B" => "2" + +# 2 [FULLWIDTH DIGIT TWO] +"\uFF12" => "2" + +# ⒉ [DIGIT TWO FULL STOP] +"\u2489" => "2." + +# ⑵ [PARENTHESIZED DIGIT TWO] +"\u2475" => "(2)" + +# ³ [SUPERSCRIPT THREE] +"\u00B3" => "3" + +# ₃ [SUBSCRIPT THREE] +"\u2083" => "3" + +# ③ [CIRCLED DIGIT THREE] +"\u2462" => "3" + +# ⓷ [DOUBLE CIRCLED DIGIT THREE] +"\u24F7" => "3" + +# ❸ [DINGBAT NEGATIVE CIRCLED DIGIT THREE] +"\u2778" => "3" + +# ➂ [DINGBAT CIRCLED SANS-SERIF DIGIT THREE] +"\u2782" => "3" + +# ➌ [DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT THREE] +"\u278C" => "3" + +# 3 [FULLWIDTH DIGIT THREE] +"\uFF13" => "3" + +# ⒊ [DIGIT THREE FULL STOP] +"\u248A" => "3." + +# ⑶ [PARENTHESIZED DIGIT THREE] +"\u2476" => "(3)" + +# ⁴ [SUPERSCRIPT FOUR] +"\u2074" => "4" + +# ₄ [SUBSCRIPT FOUR] +"\u2084" => "4" + +# ④ [CIRCLED DIGIT FOUR] +"\u2463" => "4" + +# ⓸ [DOUBLE CIRCLED DIGIT FOUR] +"\u24F8" => "4" + +# ❹ [DINGBAT NEGATIVE CIRCLED DIGIT FOUR] +"\u2779" => "4" + +# ➃ [DINGBAT CIRCLED SANS-SERIF DIGIT FOUR] +"\u2783" => "4" + +# ➍ [DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT FOUR] +"\u278D" => "4" + +# 4 [FULLWIDTH DIGIT FOUR] +"\uFF14" => "4" + +# ⒋ [DIGIT FOUR FULL STOP] +"\u248B" => "4." + +# ⑷ [PARENTHESIZED DIGIT FOUR] +"\u2477" => "(4)" + +# ⁵ [SUPERSCRIPT FIVE] +"\u2075" => "5" + +# ₅ [SUBSCRIPT FIVE] +"\u2085" => "5" + +# ⑤ [CIRCLED DIGIT FIVE] +"\u2464" => "5" + +# ⓹ [DOUBLE CIRCLED DIGIT FIVE] +"\u24F9" => "5" + +# ❺ [DINGBAT NEGATIVE CIRCLED DIGIT FIVE] +"\u277A" => "5" + +# ➄ [DINGBAT CIRCLED SANS-SERIF DIGIT FIVE] +"\u2784" => "5" + +# ➎ [DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT FIVE] +"\u278E" => "5" + +# 5 [FULLWIDTH DIGIT FIVE] +"\uFF15" => "5" + +# ⒌ [DIGIT FIVE FULL STOP] +"\u248C" => "5." + +# ⑸ [PARENTHESIZED DIGIT FIVE] +"\u2478" => "(5)" + +# ⁶ [SUPERSCRIPT SIX] +"\u2076" => "6" + +# ₆ [SUBSCRIPT SIX] +"\u2086" => "6" + +# ⑥ [CIRCLED DIGIT SIX] +"\u2465" => "6" + +# ⓺ [DOUBLE CIRCLED DIGIT SIX] +"\u24FA" => "6" + +# ❻ [DINGBAT NEGATIVE CIRCLED DIGIT SIX] +"\u277B" => "6" + +# ➅ [DINGBAT CIRCLED SANS-SERIF DIGIT SIX] +"\u2785" => "6" + +# ➏ [DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT SIX] +"\u278F" => "6" + +# 6 [FULLWIDTH DIGIT SIX] +"\uFF16" => "6" + +# ⒍ [DIGIT SIX FULL STOP] +"\u248D" => "6." + +# ⑹ [PARENTHESIZED DIGIT SIX] +"\u2479" => "(6)" + +# ⁷ [SUPERSCRIPT SEVEN] +"\u2077" => "7" + +# ₇ [SUBSCRIPT SEVEN] +"\u2087" => "7" + +# ⑦ [CIRCLED DIGIT SEVEN] +"\u2466" => "7" + +# ⓻ [DOUBLE CIRCLED DIGIT SEVEN] +"\u24FB" => "7" + +# ❼ [DINGBAT NEGATIVE CIRCLED DIGIT SEVEN] +"\u277C" => "7" + +# ➆ [DINGBAT CIRCLED SANS-SERIF DIGIT SEVEN] +"\u2786" => "7" + +# ➐ [DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT SEVEN] +"\u2790" => "7" + +# 7 [FULLWIDTH DIGIT SEVEN] +"\uFF17" => "7" + +# ⒎ [DIGIT SEVEN FULL STOP] +"\u248E" => "7." + +# ⑺ [PARENTHESIZED DIGIT SEVEN] +"\u247A" => "(7)" + +# ⁸ [SUPERSCRIPT EIGHT] +"\u2078" => "8" + +# ₈ [SUBSCRIPT EIGHT] +"\u2088" => "8" + +# ⑧ [CIRCLED DIGIT EIGHT] +"\u2467" => "8" + +# ⓼ [DOUBLE CIRCLED DIGIT EIGHT] +"\u24FC" => "8" + +# ❽ [DINGBAT NEGATIVE CIRCLED DIGIT EIGHT] +"\u277D" => "8" + +# ➇ [DINGBAT CIRCLED SANS-SERIF DIGIT EIGHT] +"\u2787" => "8" + +# ➑ [DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT EIGHT] +"\u2791" => "8" + +# 8 [FULLWIDTH DIGIT EIGHT] +"\uFF18" => "8" + +# ⒏ [DIGIT EIGHT FULL STOP] +"\u248F" => "8." + +# ⑻ [PARENTHESIZED DIGIT EIGHT] +"\u247B" => "(8)" + +# ⁹ [SUPERSCRIPT NINE] +"\u2079" => "9" + +# ₉ [SUBSCRIPT NINE] +"\u2089" => "9" + +# ⑨ [CIRCLED DIGIT NINE] +"\u2468" => "9" + +# ⓽ [DOUBLE CIRCLED DIGIT NINE] +"\u24FD" => "9" + +# ❾ [DINGBAT NEGATIVE CIRCLED DIGIT NINE] +"\u277E" => "9" + +# ➈ [DINGBAT CIRCLED SANS-SERIF DIGIT NINE] +"\u2788" => "9" + +# ➒ [DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT NINE] +"\u2792" => "9" + +# 9 [FULLWIDTH DIGIT NINE] +"\uFF19" => "9" + +# ⒐ [DIGIT NINE FULL STOP] +"\u2490" => "9." + +# ⑼ [PARENTHESIZED DIGIT NINE] +"\u247C" => "(9)" + +# ⑩ [CIRCLED NUMBER TEN] +"\u2469" => "10" + +# ⓾ [DOUBLE CIRCLED NUMBER TEN] +"\u24FE" => "10" + +# ❿ [DINGBAT NEGATIVE CIRCLED NUMBER TEN] +"\u277F" => "10" + +# ➉ [DINGBAT CIRCLED SANS-SERIF NUMBER TEN] +"\u2789" => "10" + +# ➓ [DINGBAT NEGATIVE CIRCLED SANS-SERIF NUMBER TEN] +"\u2793" => "10" + +# ⒑ [NUMBER TEN FULL STOP] +"\u2491" => "10." + +# ⑽ [PARENTHESIZED NUMBER TEN] +"\u247D" => "(10)" + +# ⑪ [CIRCLED NUMBER ELEVEN] +"\u246A" => "11" + +# ⓫ [NEGATIVE CIRCLED NUMBER ELEVEN] +"\u24EB" => "11" + +# ⒒ [NUMBER ELEVEN FULL STOP] +"\u2492" => "11." + +# ⑾ [PARENTHESIZED NUMBER ELEVEN] +"\u247E" => "(11)" + +# ⑫ [CIRCLED NUMBER TWELVE] +"\u246B" => "12" + +# ⓬ [NEGATIVE CIRCLED NUMBER TWELVE] +"\u24EC" => "12" + +# ⒓ [NUMBER TWELVE FULL STOP] +"\u2493" => "12." + +# ⑿ [PARENTHESIZED NUMBER TWELVE] +"\u247F" => "(12)" + +# ⑬ [CIRCLED NUMBER THIRTEEN] +"\u246C" => "13" + +# ⓭ [NEGATIVE CIRCLED NUMBER THIRTEEN] +"\u24ED" => "13" + +# ⒔ [NUMBER THIRTEEN FULL STOP] +"\u2494" => "13." + +# ⒀ [PARENTHESIZED NUMBER THIRTEEN] +"\u2480" => "(13)" + +# ⑭ [CIRCLED NUMBER FOURTEEN] +"\u246D" => "14" + +# ⓮ [NEGATIVE CIRCLED NUMBER FOURTEEN] +"\u24EE" => "14" + +# ⒕ [NUMBER FOURTEEN FULL STOP] +"\u2495" => "14." + +# ⒁ [PARENTHESIZED NUMBER FOURTEEN] +"\u2481" => "(14)" + +# ⑮ [CIRCLED NUMBER FIFTEEN] +"\u246E" => "15" + +# ⓯ [NEGATIVE CIRCLED NUMBER FIFTEEN] +"\u24EF" => "15" + +# ⒖ [NUMBER FIFTEEN FULL STOP] +"\u2496" => "15." + +# ⒂ [PARENTHESIZED NUMBER FIFTEEN] +"\u2482" => "(15)" + +# ⑯ [CIRCLED NUMBER SIXTEEN] +"\u246F" => "16" + +# ⓰ [NEGATIVE CIRCLED NUMBER SIXTEEN] +"\u24F0" => "16" + +# ⒗ [NUMBER SIXTEEN FULL STOP] +"\u2497" => "16." + +# ⒃ [PARENTHESIZED NUMBER SIXTEEN] +"\u2483" => "(16)" + +# ⑰ [CIRCLED NUMBER SEVENTEEN] +"\u2470" => "17" + +# ⓱ [NEGATIVE CIRCLED NUMBER SEVENTEEN] +"\u24F1" => "17" + +# ⒘ [NUMBER SEVENTEEN FULL STOP] +"\u2498" => "17." + +# ⒄ [PARENTHESIZED NUMBER SEVENTEEN] +"\u2484" => "(17)" + +# ⑱ [CIRCLED NUMBER EIGHTEEN] +"\u2471" => "18" + +# ⓲ [NEGATIVE CIRCLED NUMBER EIGHTEEN] +"\u24F2" => "18" + +# ⒙ [NUMBER EIGHTEEN FULL STOP] +"\u2499" => "18." + +# ⒅ [PARENTHESIZED NUMBER EIGHTEEN] +"\u2485" => "(18)" + +# ⑲ [CIRCLED NUMBER NINETEEN] +"\u2472" => "19" + +# ⓳ [NEGATIVE CIRCLED NUMBER NINETEEN] +"\u24F3" => "19" + +# ⒚ [NUMBER NINETEEN FULL STOP] +"\u249A" => "19." + +# ⒆ [PARENTHESIZED NUMBER NINETEEN] +"\u2486" => "(19)" + +# ⑳ [CIRCLED NUMBER TWENTY] +"\u2473" => "20" + +# ⓴ [NEGATIVE CIRCLED NUMBER TWENTY] +"\u24F4" => "20" + +# ⒛ [NUMBER TWENTY FULL STOP] +"\u249B" => "20." + +# ⒇ [PARENTHESIZED NUMBER TWENTY] +"\u2487" => "(20)" + +# « [LEFT-POINTING DOUBLE ANGLE QUOTATION MARK] +"\u00AB" => "\"" + +# » [RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK] +"\u00BB" => "\"" + +# “ [LEFT DOUBLE QUOTATION MARK] +"\u201C" => "\"" + +# ” [RIGHT DOUBLE QUOTATION MARK] +"\u201D" => "\"" + +# „ [DOUBLE LOW-9 QUOTATION MARK] +"\u201E" => "\"" + +# ″ [DOUBLE PRIME] +"\u2033" => "\"" + +# ‶ [REVERSED DOUBLE PRIME] +"\u2036" => "\"" + +# ❝ [HEAVY DOUBLE TURNED COMMA QUOTATION MARK ORNAMENT] +"\u275D" => "\"" + +# ❞ [HEAVY DOUBLE COMMA QUOTATION MARK ORNAMENT] +"\u275E" => "\"" + +# ❮ [HEAVY LEFT-POINTING ANGLE QUOTATION MARK ORNAMENT] +"\u276E" => "\"" + +# ❯ [HEAVY RIGHT-POINTING ANGLE QUOTATION MARK ORNAMENT] +"\u276F" => "\"" + +# " [FULLWIDTH QUOTATION MARK] +"\uFF02" => "\"" + +# ‘ [LEFT SINGLE QUOTATION MARK] +"\u2018" => "\'" + +# ’ [RIGHT SINGLE QUOTATION MARK] +"\u2019" => "\'" + +# ‚ [SINGLE LOW-9 QUOTATION MARK] +"\u201A" => "\'" + +# ‛ [SINGLE HIGH-REVERSED-9 QUOTATION MARK] +"\u201B" => "\'" + +# ′ [PRIME] +"\u2032" => "\'" + +# ‵ [REVERSED PRIME] +"\u2035" => "\'" + +# ‹ [SINGLE LEFT-POINTING ANGLE QUOTATION MARK] +"\u2039" => "\'" + +# › [SINGLE RIGHT-POINTING ANGLE QUOTATION MARK] +"\u203A" => "\'" + +# ❛ [HEAVY SINGLE TURNED COMMA QUOTATION MARK ORNAMENT] +"\u275B" => "\'" + +# ❜ [HEAVY SINGLE COMMA QUOTATION MARK ORNAMENT] +"\u275C" => "\'" + +# ' [FULLWIDTH APOSTROPHE] +"\uFF07" => "\'" + +# ‐ [HYPHEN] +"\u2010" => "-" + +# ‑ [NON-BREAKING HYPHEN] +"\u2011" => "-" + +# ‒ [FIGURE DASH] +"\u2012" => "-" + +# – [EN DASH] +"\u2013" => "-" + +# — [EM DASH] +"\u2014" => "-" + +# ⁻ [SUPERSCRIPT MINUS] +"\u207B" => "-" + +# ₋ [SUBSCRIPT MINUS] +"\u208B" => "-" + +# - [FULLWIDTH HYPHEN-MINUS] +"\uFF0D" => "-" + +# ⁅ [LEFT SQUARE BRACKET WITH QUILL] +"\u2045" => "[" + +# ❲ [LIGHT LEFT TORTOISE SHELL BRACKET ORNAMENT] +"\u2772" => "[" + +# [ [FULLWIDTH LEFT SQUARE BRACKET] +"\uFF3B" => "[" + +# ⁆ [RIGHT SQUARE BRACKET WITH QUILL] +"\u2046" => "]" + +# ❳ [LIGHT RIGHT TORTOISE SHELL BRACKET ORNAMENT] +"\u2773" => "]" + +# ] [FULLWIDTH RIGHT SQUARE BRACKET] +"\uFF3D" => "]" + +# ⁽ [SUPERSCRIPT LEFT PARENTHESIS] +"\u207D" => "(" + +# ₍ [SUBSCRIPT LEFT PARENTHESIS] +"\u208D" => "(" + +# ❨ [MEDIUM LEFT PARENTHESIS ORNAMENT] +"\u2768" => "(" + +# ❪ [MEDIUM FLATTENED LEFT PARENTHESIS ORNAMENT] +"\u276A" => "(" + +# ( [FULLWIDTH LEFT PARENTHESIS] +"\uFF08" => "(" + +# ⸨ [LEFT DOUBLE PARENTHESIS] +"\u2E28" => "((" + +# ⁾ [SUPERSCRIPT RIGHT PARENTHESIS] +"\u207E" => ")" + +# ₎ [SUBSCRIPT RIGHT PARENTHESIS] +"\u208E" => ")" + +# ❩ [MEDIUM RIGHT PARENTHESIS ORNAMENT] +"\u2769" => ")" + +# ❫ [MEDIUM FLATTENED RIGHT PARENTHESIS ORNAMENT] +"\u276B" => ")" + +# ) [FULLWIDTH RIGHT PARENTHESIS] +"\uFF09" => ")" + +# ⸩ [RIGHT DOUBLE PARENTHESIS] +"\u2E29" => "))" + +# ❬ [MEDIUM LEFT-POINTING ANGLE BRACKET ORNAMENT] +"\u276C" => "<" + +# ❰ [HEAVY LEFT-POINTING ANGLE BRACKET ORNAMENT] +"\u2770" => "<" + +# < [FULLWIDTH LESS-THAN SIGN] +"\uFF1C" => "<" + +# ❭ [MEDIUM RIGHT-POINTING ANGLE BRACKET ORNAMENT] +"\u276D" => ">" + +# ❱ [HEAVY RIGHT-POINTING ANGLE BRACKET ORNAMENT] +"\u2771" => ">" + +# > [FULLWIDTH GREATER-THAN SIGN] +"\uFF1E" => ">" + +# ❴ [MEDIUM LEFT CURLY BRACKET ORNAMENT] +"\u2774" => "{" + +# { [FULLWIDTH LEFT CURLY BRACKET] +"\uFF5B" => "{" + +# ❵ [MEDIUM RIGHT CURLY BRACKET ORNAMENT] +"\u2775" => "}" + +# } [FULLWIDTH RIGHT CURLY BRACKET] +"\uFF5D" => "}" + +# ⁺ [SUPERSCRIPT PLUS SIGN] +"\u207A" => "+" + +# ₊ [SUBSCRIPT PLUS SIGN] +"\u208A" => "+" + +# + [FULLWIDTH PLUS SIGN] +"\uFF0B" => "+" + +# ⁼ [SUPERSCRIPT EQUALS SIGN] +"\u207C" => "=" + +# ₌ [SUBSCRIPT EQUALS SIGN] +"\u208C" => "=" + +# = [FULLWIDTH EQUALS SIGN] +"\uFF1D" => "=" + +# ! [FULLWIDTH EXCLAMATION MARK] +"\uFF01" => "!" + +# ‼ [DOUBLE EXCLAMATION MARK] +"\u203C" => "!!" + +# ⁉ [EXCLAMATION QUESTION MARK] +"\u2049" => "!?" + +# # [FULLWIDTH NUMBER SIGN] +"\uFF03" => "#" + +# $ [FULLWIDTH DOLLAR SIGN] +"\uFF04" => "$" + +# ⁒ [COMMERCIAL MINUS SIGN] +"\u2052" => "%" + +# % [FULLWIDTH PERCENT SIGN] +"\uFF05" => "%" + +# & [FULLWIDTH AMPERSAND] +"\uFF06" => "&" + +# ⁎ [LOW ASTERISK] +"\u204E" => "*" + +# * [FULLWIDTH ASTERISK] +"\uFF0A" => "*" + +# , [FULLWIDTH COMMA] +"\uFF0C" => "," + +# . [FULLWIDTH FULL STOP] +"\uFF0E" => "." + +# ⁄ [FRACTION SLASH] +"\u2044" => "/" + +# / [FULLWIDTH SOLIDUS] +"\uFF0F" => "/" + +# : [FULLWIDTH COLON] +"\uFF1A" => ":" + +# ⁏ [REVERSED SEMICOLON] +"\u204F" => ";" + +# ; [FULLWIDTH SEMICOLON] +"\uFF1B" => ";" + +# ? [FULLWIDTH QUESTION MARK] +"\uFF1F" => "?" + +# ⁇ [DOUBLE QUESTION MARK] +"\u2047" => "??" + +# ⁈ [QUESTION EXCLAMATION MARK] +"\u2048" => "?!" + +# @ [FULLWIDTH COMMERCIAL AT] +"\uFF20" => "@" + +# \ [FULLWIDTH REVERSE SOLIDUS] +"\uFF3C" => "\\" + +# ‸ [CARET] +"\u2038" => "^" + +# ^ [FULLWIDTH CIRCUMFLEX ACCENT] +"\uFF3E" => "^" + +# _ [FULLWIDTH LOW LINE] +"\uFF3F" => "_" + +# ⁓ [SWUNG DASH] +"\u2053" => "~" + +# ~ [FULLWIDTH TILDE] +"\uFF5E" => "~" + +################################################################ +# Below is the Perl script used to generate the above mappings # +# from ASCIIFoldingFilter.java: # +################################################################ +# +# #!/usr/bin/perl +# +# use warnings; +# use strict; +# +# my @source_chars = (); +# my @source_char_descriptions = (); +# my $target = ''; +# +# while (<>) { +# if (/case\s+'(\\u[A-F0-9]+)':\s*\/\/\s*(.*)/i) { +# push @source_chars, $1; +# push @source_char_descriptions, $2; +# next; +# } +# if (/output\[[^\]]+\]\s*=\s*'(\\'|\\\\|.)'/) { +# $target .= $1; +# next; +# } +# if (/break;/) { +# $target = "\\\"" if ($target eq '"'); +# for my $source_char_num (0..$#source_chars) { +# print "# $source_char_descriptions[$source_char_num]\n"; +# print "\"$source_chars[$source_char_num]\" => \"$target\"\n\n"; +# } +# @source_chars = (); +# @source_char_descriptions = (); +# $target = ''; +# } +# } diff --git a/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/mapping-ISOLatin1Accent.txt b/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/mapping-ISOLatin1Accent.txt new file mode 100755 index 0000000000..ede7742581 --- /dev/null +++ b/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/mapping-ISOLatin1Accent.txt @@ -0,0 +1,246 @@ +# The ASF licenses this file to You 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. + +# Syntax: +# "source" => "target" +# "source".length() > 0 (source cannot be empty.) +# "target".length() >= 0 (target can be empty.) + +# example: +# "À" => "A" +# "\u00C0" => "A" +# "\u00C0" => "\u0041" +# "ß" => "ss" +# "\t" => " " +# "\n" => "" + +# À => A +"\u00C0" => "A" + +# Á => A +"\u00C1" => "A" + +#  => A +"\u00C2" => "A" + +# à => A +"\u00C3" => "A" + +# Ä => A +"\u00C4" => "A" + +# Å => A +"\u00C5" => "A" + +# Æ => AE +"\u00C6" => "AE" + +# Ç => C +"\u00C7" => "C" + +# È => E +"\u00C8" => "E" + +# É => E +"\u00C9" => "E" + +# Ê => E +"\u00CA" => "E" + +# Ë => E +"\u00CB" => "E" + +# Ì => I +"\u00CC" => "I" + +# Í => I +"\u00CD" => "I" + +# Î => I +"\u00CE" => "I" + +# Ï => I +"\u00CF" => "I" + +# IJ => IJ +"\u0132" => "IJ" + +# Ð => D +"\u00D0" => "D" + +# Ñ => N +"\u00D1" => "N" + +# Ò => O +"\u00D2" => "O" + +# Ó => O +"\u00D3" => "O" + +# Ô => O +"\u00D4" => "O" + +# Õ => O +"\u00D5" => "O" + +# Ö => O +"\u00D6" => "O" + +# Ø => O +"\u00D8" => "O" + +# Œ => OE +"\u0152" => "OE" + +# Þ +"\u00DE" => "TH" + +# Ù => U +"\u00D9" => "U" + +# Ú => U +"\u00DA" => "U" + +# Û => U +"\u00DB" => "U" + +# Ü => U +"\u00DC" => "U" + +# Ý => Y +"\u00DD" => "Y" + +# Ÿ => Y +"\u0178" => "Y" + +# à => a +"\u00E0" => "a" + +# á => a +"\u00E1" => "a" + +# â => a +"\u00E2" => "a" + +# ã => a +"\u00E3" => "a" + +# ä => a +"\u00E4" => "a" + +# å => a +"\u00E5" => "a" + +# æ => ae +"\u00E6" => "ae" + +# ç => c +"\u00E7" => "c" + +# è => e +"\u00E8" => "e" + +# é => e +"\u00E9" => "e" + +# ê => e +"\u00EA" => "e" + +# ë => e +"\u00EB" => "e" + +# ì => i +"\u00EC" => "i" + +# í => i +"\u00ED" => "i" + +# î => i +"\u00EE" => "i" + +# ï => i +"\u00EF" => "i" + +# ij => ij +"\u0133" => "ij" + +# ð => d +"\u00F0" => "d" + +# ñ => n +"\u00F1" => "n" + +# ò => o +"\u00F2" => "o" + +# ó => o +"\u00F3" => "o" + +# ô => o +"\u00F4" => "o" + +# õ => o +"\u00F5" => "o" + +# ö => o +"\u00F6" => "o" + +# ø => o +"\u00F8" => "o" + +# œ => oe +"\u0153" => "oe" + +# ß => ss +"\u00DF" => "ss" + +# þ => th +"\u00FE" => "th" + +# ù => u +"\u00F9" => "u" + +# ú => u +"\u00FA" => "u" + +# û => u +"\u00FB" => "u" + +# ü => u +"\u00FC" => "u" + +# ý => y +"\u00FD" => "y" + +# ÿ => y +"\u00FF" => "y" + +# ff => ff +"\uFB00" => "ff" + +# fi => fi +"\uFB01" => "fi" + +# fl => fl +"\uFB02" => "fl" + +# ffi => ffi +"\uFB03" => "ffi" + +# ffl => ffl +"\uFB04" => "ffl" + +# ſt => ft +"\uFB05" => "ft" + +# st => st +"\uFB06" => "st" diff --git a/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/protwords.txt b/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/protwords.txt new file mode 100755 index 0000000000..1dfc0abecb --- /dev/null +++ b/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/protwords.txt @@ -0,0 +1,21 @@ +# The ASF licenses this file to You 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. + +#----------------------------------------------------------------------- +# Use a protected word file to protect against the stemmer reducing two +# unrelated words to the same base word. + +# Some non-words that normally won't be encountered, +# just to test that they won't be stemmed. +dontstems +zwhacky + diff --git a/KeywordSearch/solr/solr/configsets/AutopsyConfig/conf/schema.xml b/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/schema.xml old mode 100644 new mode 100755 similarity index 100% rename from KeywordSearch/solr/solr/configsets/AutopsyConfig/conf/schema.xml rename to KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/schema.xml diff --git a/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/scripts.conf b/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/scripts.conf new file mode 100755 index 0000000000..7be01a6bee --- /dev/null +++ b/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/scripts.conf @@ -0,0 +1,24 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. + +user= +solr_hostname=localhost +solr_port=9293 +rsyncd_port=19293 +data_dir= +webapp_name=solr +master_host= +master_data_dir= +master_status_dir= diff --git a/KeywordSearch/solr/solr/configsets/AutopsyConfig/conf/solrconfig.xml b/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/solrconfig.xml old mode 100644 new mode 100755 similarity index 100% rename from KeywordSearch/solr/solr/configsets/AutopsyConfig/conf/solrconfig.xml rename to KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/solrconfig.xml diff --git a/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/spellings.txt b/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/spellings.txt new file mode 100755 index 0000000000..162a044d56 --- /dev/null +++ b/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/spellings.txt @@ -0,0 +1,2 @@ +pizza +history diff --git a/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/stopwords.txt b/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/stopwords.txt new file mode 100755 index 0000000000..ae1e83eeb3 --- /dev/null +++ b/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/stopwords.txt @@ -0,0 +1,14 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. diff --git a/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/stopwords_en.txt b/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/stopwords_en.txt new file mode 100755 index 0000000000..2c164c0b2a --- /dev/null +++ b/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/stopwords_en.txt @@ -0,0 +1,54 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. + +# a couple of test stopwords to test that the words are really being +# configured from this file: +stopworda +stopwordb + +# Standard english stop words taken from Lucene's StopAnalyzer +a +an +and +are +as +at +be +but +by +for +if +in +into +is +it +no +not +of +on +or +such +that +the +their +then +there +these +they +this +to +was +will +with diff --git a/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/synonyms.txt b/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/synonyms.txt new file mode 100755 index 0000000000..5c3b95fb6a --- /dev/null +++ b/KeywordSearch/solr4/solr/configsets/AutopsyConfig/conf/synonyms.txt @@ -0,0 +1,31 @@ +# The ASF licenses this file to You 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. + +#----------------------------------------------------------------------- +#some test synonym mappings unlikely to appear in real input text +aaafoo => aaabar +bbbfoo => bbbfoo bbbbar +cccfoo => cccbar cccbaz +fooaaa,baraaa,bazaaa + +# Some synonym groups specific to this example +GB,gib,gigabyte,gigabytes +MB,mib,megabyte,megabytes +Television, Televisions, TV, TVs +#notice we use "gib" instead of "GiB" so any WordDelimiterFilter coming +#after us won't split it into two words. + +# Synonym mappings can be used for spelling correction too +pixima => pixma + +a\,a => b\,b + diff --git a/KeywordSearch/solr/solr/solr.xml b/KeywordSearch/solr4/solr/solr.xml old mode 100644 new mode 100755 similarity index 100% rename from KeywordSearch/solr/solr/solr.xml rename to KeywordSearch/solr4/solr/solr.xml diff --git a/KeywordSearch/solr/solr/zoo.cfg b/KeywordSearch/solr4/solr/zoo.cfg old mode 100644 new mode 100755 similarity index 100% rename from KeywordSearch/solr/solr/zoo.cfg rename to KeywordSearch/solr4/solr/zoo.cfg diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties index 84c71a9bf5..b4e2850586 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties @@ -193,7 +193,12 @@ Server.openCore.exception.noIndexDir.msg=Index directory could not be created or Server.request.exception.exception.msg=Could not issue Solr request Server.commit.exception.msg=Could not commit index Server.addDoc.exception.msg=Could not add document to index via update handler: {0} +Server.addDoc.exception.msg2=Could not add document to index via update handler: {0} +Server.addDocBatch.exception.msg=Could not add batched documents to index Server.close.exception.msg=Cannot close Core +Server.connect.exception.msg=Unable to connect to Solr server {0} +Server.serverList.exception.msg=Unable to get the list of Solr servers from server {0} +Server.connectionInfoMissing.exception.msg=Solr version {0} multi-user connection info is not configured Server.solrServerNoPortException.msg=Indexing server could not bind to port {0}, port is not available, consider change the default {1} port. KeywordSearchJobSettingsPanel.keywordSearchEncodings.text=- KeywordSearchJobSettingsPanel.languagesValLabel.toolTipText= @@ -205,7 +210,6 @@ KeywordSearchJobSettingsPanel.languagesLabel.text=Scripts enabled for string ext KeywordSearchGlobalLanguageSettingsPanel.enableUTF8Checkbox.text=Enable UTF8 text extraction KeywordSearchGlobalLanguageSettingsPanel.ingestSettingsLabel.text=Ingest settings for string extraction from unknown file types (changes effective on next ingest): KeywordSearchGlobalLanguageSettingsPanel.enableUTF16Checkbox.text=Enable UTF16LE and UTF16BE string extraction -KeywordSearchGlobalLanguageSettingsPanel.enableOcrCheckbox.text=Enable Optical Character Recognition (OCR) KeywordSearchGlobalLanguageSettingsPanel.languagesLabel.text=Enabled scripts (languages): KeywordSearchGlobalSearchSettingsPanel.timeRadioButton1.toolTipText=20 mins. (fastest ingest time) KeywordSearchGlobalSearchSettingsPanel.timeRadioButton1.text=20 minutes (slowest feedback, fastest ingest) @@ -286,8 +290,6 @@ GlobalListsManagementPanel.copyListButton.text=Copy List GlobalListsManagementPanel.renameListButton.text=Edit List Name GlobalEditListPanel.editWordButton.text=Edit Keyword SolrSearchService.ServiceName=Solr Keyword Search Service -SolrSearchService.IndexReadOnlyDialog.title=Text Index Is Read-Only -SolrSearchService.IndexReadOnlyDialog.msg=The text index for this case is read-only.
    You will be able to see existing keyword search results and perform exact match and substring match keyword searches,
    but you will not be able to add new text to the index or perform regex searches. You may instead open the case
    with your previous version of this application. SolrSearchService.DeleteDataSource.msg=Error Deleting Solr data for data source id {0} DropdownSingleTermSearchPanel.dataSourceCheckBox.text=Restrict search to the selected data sources: DropdownListSearchPanel.dataSourceCheckBox.text=Restrict search to the selected data sources: @@ -317,3 +319,4 @@ ExtractedContentPanel.pageTotalLabel.text=- ExtractedContentPanel.pageOfLabel.text=of ExtractedContentPanel.pageCurLabel.text=- ExtractedContentPanel.pagesLabel.text=Page: +KeywordSearchGlobalSearchSettingsPanel.ocrCheckBox.text=Enable Optical Character Recognition (OCR) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED index 95066b41d1..2f39159d97 100755 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle.properties-MERGED @@ -18,6 +18,8 @@ GlobalEditListPanel.warning.title=Warning IndexedText.errorMessage.errorGettingText=Error retrieving indexed text. IndexedText.warningMessage.knownFile=This file is a known file (based on MD5 hash) and does not have indexed text. IndexedText.warningMessage.noTextAvailable=No indexed text for this file. +KeywordSearchGlobalLanguageSettingsPanel.enableOcrCheckbox.text=Enable Optical Character Recognition (OCR) +KeywordSearchGlobalSearchSettingsPanel.customizeComponents.windowsOCR=Enable Optical Character Recognition (OCR) (Requires Windows 64-bit) KeywordSearchGlobalSettingsPanel.Title=Global Keyword Search Settings KeywordSearchIngestModule.init.badInitMsg=Keyword search server was not properly initialized, cannot run keyword search ingest. # {0} - Reason for not connecting to Solr @@ -212,8 +214,11 @@ KeywordSearchSettings.propertiesNSRL.text={0}_NSRL KeywordSearchSettings.propertiesScripts.text={0}_Scripts NoOpenCoreException.err.noOpenSorlCore.msg=No currently open Solr core. SearchRunner.query.exception.msg=Error performing query: -# {0} - core name -Server.deleteCore.exception.msg=Failed to delete Solr core {0} +# {0} - colelction name +Server.deleteCore.exception.msg=Failed to delete Solr colelction {0} +Server.exceptionMessage.unableToBackupCollection=Unable to backup Solr collection +Server.exceptionMessage.unableToCreateCollection=Unable to create Solr collection +Server.exceptionMessage.unableToRestoreCollection=Unable to restore Solr collection Server.start.exception.cantStartSolr.msg=Could not start Solr server process Server.start.exception.cantStartSolr.msg2=Could not start Solr server process Server.isRunning.exception.errCheckSolrRunning.msg=Error checking if Solr server is running @@ -234,7 +239,12 @@ Server.openCore.exception.noIndexDir.msg=Index directory could not be created or Server.request.exception.exception.msg=Could not issue Solr request Server.commit.exception.msg=Could not commit index Server.addDoc.exception.msg=Could not add document to index via update handler: {0} +Server.addDoc.exception.msg2=Could not add document to index via update handler: {0} +Server.addDocBatch.exception.msg=Could not add batched documents to index Server.close.exception.msg=Cannot close Core +Server.connect.exception.msg=Unable to connect to Solr server {0} +Server.serverList.exception.msg=Unable to get the list of Solr servers from server {0} +Server.connectionInfoMissing.exception.msg=Solr version {0} multi-user connection info is not configured Server.solrServerNoPortException.msg=Indexing server could not bind to port {0}, port is not available, consider change the default {1} port. KeywordSearchJobSettingsPanel.keywordSearchEncodings.text=- KeywordSearchJobSettingsPanel.languagesValLabel.toolTipText= @@ -246,7 +256,6 @@ KeywordSearchJobSettingsPanel.languagesLabel.text=Scripts enabled for string ext KeywordSearchGlobalLanguageSettingsPanel.enableUTF8Checkbox.text=Enable UTF8 text extraction KeywordSearchGlobalLanguageSettingsPanel.ingestSettingsLabel.text=Ingest settings for string extraction from unknown file types (changes effective on next ingest): KeywordSearchGlobalLanguageSettingsPanel.enableUTF16Checkbox.text=Enable UTF16LE and UTF16BE string extraction -KeywordSearchGlobalLanguageSettingsPanel.enableOcrCheckbox.text=Enable Optical Character Recognition (OCR) KeywordSearchGlobalLanguageSettingsPanel.languagesLabel.text=Enabled scripts (languages): KeywordSearchGlobalSearchSettingsPanel.timeRadioButton1.toolTipText=20 mins. (fastest ingest time) KeywordSearchGlobalSearchSettingsPanel.timeRadioButton1.text=20 minutes (slowest feedback, fastest ingest) @@ -334,19 +343,17 @@ SolrSearch.creatingNewIndex.msg=Creating new text index SolrSearch.findingIndexes.msg=Looking for existing text index directories SolrSearch.indentifyingIndex.msg=Identifying text index to use SolrSearch.lookingForMetadata.msg=Looking for text index metadata file -SolrSearch.openCore.msg=Opening text index -SolrSearch.openGiantCore.msg=Opening text index. Text index for this case is very large and may take long time to load. -SolrSearch.openLargeCore.msg=Opening text index. This may take several minutes. +SolrSearch.openCore.msg=Opening text index. For large cases this may take several minutes. SolrSearch.readingIndexes.msg=Reading text index metadata file # {0} - index folder path SolrSearchService.exceptionMessage.failedToDeleteIndexFiles=Failed to delete text index files at {0} SolrSearchService.exceptionMessage.noCurrentSolrCore=IndexMetadata did not contain a current Solr core so could not delete the case # {0} - case directory SolrSearchService.exceptionMessage.noIndexMetadata=Unable to create IndexMetaData from case directory: {0} +# {0} - collection name +SolrSearchService.exceptionMessage.unableToDeleteCollection=Unable to delete collection {0} SolrSearchService.indexingError=Unable to index blackboard artifact. SolrSearchService.ServiceName=Solr Keyword Search Service -SolrSearchService.IndexReadOnlyDialog.title=Text Index Is Read-Only -SolrSearchService.IndexReadOnlyDialog.msg=The text index for this case is read-only.
    You will be able to see existing keyword search results and perform exact match and substring match keyword searches,
    but you will not be able to add new text to the index or perform regex searches. You may instead open the case
    with your previous version of this application. SolrSearchService.DeleteDataSource.msg=Error Deleting Solr data for data source id {0} DropdownSingleTermSearchPanel.dataSourceCheckBox.text=Restrict search to the selected data sources: DropdownListSearchPanel.dataSourceCheckBox.text=Restrict search to the selected data sources: @@ -376,6 +383,7 @@ ExtractedContentPanel.pageTotalLabel.text=- ExtractedContentPanel.pageOfLabel.text=of ExtractedContentPanel.pageCurLabel.text=- ExtractedContentPanel.pagesLabel.text=Page: +KeywordSearchGlobalSearchSettingsPanel.ocrCheckBox.text=Enable Optical Character Recognition (OCR) TextZoomPanel.zoomInButton.text= TextZoomPanel.zoomOutButton.text= TextZoomPanel.zoomResetButton.text=Reset diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle_ja.properties b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle_ja.properties index 6f7def55a8..2f50e62095 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle_ja.properties +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Bundle_ja.properties @@ -186,7 +186,6 @@ KeywordSearchEditListPanel.selectAllMenuItem.text=\u3059\u3079\u3066\u9078\u629e KeywordSearchFilterNode.getFileActions.openExternViewActLbl=\u5916\u90e8\u30d3\u30e5\u30fc\u30ef\u30fc\u3067\u958b\u304f Ctrl+E KeywordSearchFilterNode.getFileActions.searchSameMd5=\u540c\u3058MD5\u30cf\u30c3\u30b7\u30e5\u3067\u30d5\u30a1\u30a4\u30eb\u3092\u691c\u7d22 KeywordSearchFilterNode.getFileActions.viewInNewWinActionLbl=\u65b0\u3057\u3044\u30a6\u30a3\u30f3\u30c9\u30a6\u3067\u8868\u793a -KeywordSearchGlobalLanguageSettingsPanel.enableOcrCheckbox.text=\u5149\u5b66\u6587\u5b57\u8a8d\u8b58(OCR)\u3092\u6709\u52b9\u5316 KeywordSearchGlobalLanguageSettingsPanel.enableUTF16Checkbox.text=UTF16LE\u304a\u3088\u3073UTF16BE\u6587\u5b57\u5217\u62bd\u51fa\u3092\u6709\u52b9\u5316 KeywordSearchGlobalLanguageSettingsPanel.enableUTF8Checkbox.text=UTF8\u30c6\u30ad\u30b9\u30c8\u62bd\u51fa\u3092\u6709\u52b9\u5316\u3059\u308b KeywordSearchGlobalLanguageSettingsPanel.ingestSettingsLabel.text=\u672a\u77e5\u306e\u30d5\u30a1\u30a4\u30eb\u30bf\u30a4\u30d7\u304b\u3089\u306e\u6587\u5b57\u5217\u62bd\u51fa\u306e\u305f\u3081\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u8a2d\u5b9a(\u5909\u66f4\u306f\u6b21\u306e\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u3067\u6709\u52b9\u306b\u306a\u308a\u307e\u3059)\: @@ -368,5 +367,35 @@ SolrSearchService.IndexReadOnlyDialog.title=\u30c6\u30ad\u30b9\u30c8\u7d22\u5f15 SolrSearchService.ServiceName=Solr\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30b5\u30fc\u30d3\u30b9 SolrSearchService.exceptionMessage.failedToDeleteIndexFiles={0} \u306e\u30c6\u30ad\u30b9\u30c8\u7d22\u5f15\u30d5\u30a1\u30a4\u30eb\u3092\u524a\u9664\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f SolrSearchService.exceptionMessage.noCurrentSolrCore=IndexMetadata\u306b\u306f\u73fe\u5728\u306eSolr\u306e\u30b3\u30a2\u304c\u542b\u307e\u308c\u3066\u3044\u306a\u304b\u3063\u305f\u305f\u3081\u3001\u30b1\u30fc\u30b9\u3092\u524a\u9664\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f -SolrSearchService.exceptionMessage.noIndexMetadata=\u30b1\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u304b\u3089\u6b21\u306eIndexMetaData\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\: {0} +# {0} - \u30b1\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc +SolrSearchService.exceptionMessage.noIndexMetadata=\u30b1\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u304b\u3089\u6b21\u306eIndexMetaData\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f: {0} +SolrSearchService.ServiceName=Solr\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u30b5\u30fc\u30d3\u30b9 +DropdownSingleTermSearchPanel.dataSourceCheckBox.text=\u9078\u629e\u3057\u305f\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306b\u691c\u7d22\u3092\u5236\u9650: +DropdownListSearchPanel.dataSourceCheckBox.text=\u9078\u629e\u3057\u305f\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306b\u691c\u7d22\u3092\u5236\u9650: +DropdownSingleTermSearchPanel.ingestIndexLabel.text=\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb: +DropdownSingleTermSearchPanel.jSaveSearchResults.toolTipText=\u30ad\u30fc\u30ef\u30fc\u30c9\u30d2\u30c3\u30c8\u306b\u3088\u308b\u904e\u53bb\u306e\u691c\u7d22\u7d50\u679c\u306e\u5f62\u3067\u7d50\u679c\u3092\u4fdd\u5b58\u305b\u305a\u306b\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u3092\u5b9f\u884c +DropdownSingleTermSearchPanel.jSaveSearchResults.text=\u691c\u7d22\u7d50\u679c\u3092\u4fdd\u5b58 +DropdownListSearchPanel.jSaveSearchResults.toolTipText=\u30ad\u30fc\u30ef\u30fc\u30c9\u30d2\u30c3\u30c8\u306b\u3088\u308b\u904e\u53bb\u306e\u691c\u7d22\u7d50\u679c\u306e\u5f62\u3067\u7d50\u679c\u3092\u4fdd\u5b58\u305b\u305a\u306b\u30ad\u30fc\u30ef\u30fc\u30c9\u691c\u7d22\u3092\u5b9f\u884c +DropdownListSearchPanel.jSaveSearchResults.text=\u691c\u7d22\u7d50\u679c\u3092\u4fdd\u5b58 +GlobalEditListPanel.ingestWarningLabel.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u304c\u9032\u884c\u4e2d\u3067\u3059\u3002\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u304c\u5b8c\u4e86\u3059\u308b\u307e\u3067\u4e00\u90e8\u306e\u8a2d\u5b9a\u3092\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002 +KeywordSearchGlobalLanguageSettingsPanel.ingestWarningLabel.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u304c\u9032\u884c\u4e2d\u3067\u3059\u3002\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u304c\u5b8c\u4e86\u3059\u308b\u307e\u3067\u4e00\u90e8\u306e\u8a2d\u5b9a\u3092\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002 +KeywordSearchGlobalSearchSettingsPanel.ingestWarningLabel.text=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u304c\u9032\u884c\u4e2d\u3067\u3059\u3002\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u304c\u5b8c\u4e86\u3059\u308b\u307e\u3067\u4e00\u90e8\u306e\u8a2d\u5b9a\u3092\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002 +ExtractedContentPanel.jLabel1.text=\u30c6\u30ad\u30b9\u30c8\u30bd\u30fc\u30b9: +ExtractedContentPanel.hitNextButton.text= +ExtractedContentPanel.hitPreviousButton.text= +ExtractedContentPanel.hitButtonsLabel.text=\u4e00\u81f4\u3059\u308b\u7d50\u679c +ExtractedContentPanel.hitTotalLabel.text=- +ExtractedContentPanel.hitOfLabel.text=/ +ExtractedContentPanel.hitCountLabel.text=- +ExtractedContentPanel.hitLabel.toolTipText= +ExtractedContentPanel.hitLabel.text=\u30da\u30fc\u30b8\u4e0a\u306e\u4e00\u81f4\u3059\u308b\u7d50\u679c: +ExtractedContentPanel.pageNextButton.text= +ExtractedContentPanel.pagePreviousButton.actionCommand=pagePreviousButton +ExtractedContentPanel.pagePreviousButton.text= +ExtractedContentPanel.pageButtonsLabel.text=\u30da\u30fc\u30b8 +ExtractedContentPanel.pageTotalLabel.text=- +ExtractedContentPanel.pageOfLabel.text=/ +ExtractedContentPanel.pageCurLabel.text=- +ExtractedContentPanel.pagesLabel.text=\u30da\u30fc\u30b8: TextZoomPanel.zoomResetButton.text=\u30ea\u30bb\u30c3\u30c8 + diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Chunker.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Chunker.java index 08ca0ab511..2deb82d2f5 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Chunker.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Chunker.java @@ -184,10 +184,16 @@ class Chunker implements Iterator, Iterable { return new StringBuilder(UTF_16.decode(UTF_16.encode(s))); } - private static StringBuilder sanitize(String s) { + /** + * Wrapper method that performs UTF-8 string sanitization. + * + * @param s String to be sanitized. + * + * @return Sanitized string. + */ + static StringBuilder sanitize(String s) { String normStr = Normalizer.normalize(s, Normalizer.Form.NFKC); return sanitizeToUTF8(replaceInvalidUTF16(normStr)); - } @Override @@ -336,8 +342,9 @@ class Chunker implements Iterator, Iterable { String chunkSegment; if (Character.isHighSurrogate(ch)) { //read another char into the buffer. - charsRead = reader.read(tempChunkBuf, 1, 1); - if (charsRead == -1) { + int surrogateCharsRead = reader.read(tempChunkBuf, 1, 1); + charsRead += surrogateCharsRead; + if (surrogateCharsRead == -1) { //this is the last chunk, so just drop the unpaired surrogate endOfReaderReached = true; return; @@ -352,17 +359,32 @@ class Chunker implements Iterator, Iterable { //cleanup any invalid utf-16 sequences StringBuilder sanitizedChunkSegment = sanitize(chunkSegment); - //check for whitespace. - whitespaceFound = Character.isWhitespace(sanitizedChunkSegment.codePointAt(0)); - //add read chars to the chunk and update the length. - currentChunk.append(sanitizedChunkSegment); - chunkSizeBytes += sanitizedChunkSegment.toString().getBytes(UTF_8).length; - + //get the length in utf8 bytes of the read chars + int segmentSize = chunkSegment.getBytes(UTF_8).length; + // lower case the string and get it's size. NOTE: lower casing can // change the size of the string. String lowerCasedSegment = sanitizedChunkSegment.toString().toLowerCase(); - lowerCasedChunk.append(lowerCasedSegment); - lowerCasedChunkSizeBytes += lowerCasedSegment.getBytes(UTF_8).length; + int lowerCasedSegmentSize = lowerCasedSegment.getBytes(UTF_8).length; + + //if it will not put us past maxBytes + if ((chunkSizeBytes + segmentSize < maxBytes - MAX_CHAR_SIZE_INCREASE_IN_BYTES) + && (lowerCasedChunkSizeBytes + lowerCasedSegmentSize < maxBytes - MAX_CHAR_SIZE_INCREASE_IN_BYTES)) { + + //add read chars to the chunk and update the length. + currentChunk.append(sanitizedChunkSegment); + chunkSizeBytes += segmentSize; + + lowerCasedChunk.append(lowerCasedSegment); + lowerCasedChunkSizeBytes += lowerCasedSegmentSize; + + //check for whitespace. + whitespaceFound = Character.isWhitespace(sanitizedChunkSegment.codePointAt(0)); + } else { + //unread it, and break out of read loop. + reader.unread(tempChunkBuf, 0, charsRead); + return; + } } } } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownToolbar.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownToolbar.java index 7c456039b3..3f5669dde6 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownToolbar.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/DropdownToolbar.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2018 Basis Technology Corp. + * Copyright 2011-2020 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -183,23 +183,17 @@ class DropdownToolbar extends javax.swing.JPanel { } else { Index indexInfo = server.getIndexInfo(); - if (IndexFinder.getCurrentSolrVersion().equals(indexInfo.getSolrVersion())) { - /* - * Solr version is current, so check the Solr - * schema version and selectively enable the ad - * hoc search UI components. - */ - boolean schemaIsCompatible = indexInfo.isCompatible(IndexFinder.getCurrentSchemaVersion()); - listsButton.setEnabled(schemaIsCompatible); - searchDropButton.setEnabled(true); - dropPanel.setRegexSearchEnabled(schemaIsCompatible); - active = true; - } else { - /* - * Unsupported Solr version, disable the ad hoc - * search UI components. - */ + + // Check the Solr schema version and enable ad-hoc search UI components. + boolean schemaIsCompatible = indexInfo.isCompatible(IndexFinder.getCurrentSchemaVersion()); + if (!schemaIsCompatible) { + logger.log(Level.WARNING, "Text index schema version {0} is not compatible with current schema", indexInfo.getSchemaVersion()); //NON-NLS disableSearch = true; + } else { + listsButton.setEnabled(true); + searchDropButton.setEnabled(true); + dropPanel.setRegexSearchEnabled(true); + active = true; } } } catch (NoOpenCoreException ex) { diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Index.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Index.java index 20c62ccab2..f884654bb8 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Index.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Index.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2020 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,7 +21,6 @@ package org.sleuthkit.autopsy.keywordsearch; import java.text.SimpleDateFormat; import java.util.Date; import org.apache.commons.lang.math.NumberUtils; -import org.sleuthkit.autopsy.coreutils.UNCPathUtilities; /** * This class encapsulates KWS index data. @@ -33,7 +32,6 @@ final class Index { private final String solrVersion; private final String indexName; private static final String DEFAULT_CORE_NAME = "text_index"; //NON-NLS - private final UNCPathUtilities uncPathUtilities = new UNCPathUtilities(); /** * Constructs a representation of a text index. @@ -47,9 +45,9 @@ final class Index { * need to be generated. */ Index(String indexPath, String solrVersion, String schemaVersion, String coreName, String caseName) { - this.indexPath = uncPathUtilities.convertPathToUNC(indexPath); + this.indexPath = indexPath; this.solrVersion = solrVersion; - this.schemaVersion = schemaVersion; + this.schemaVersion = schemaVersion; if (coreName == null || coreName.isEmpty()) { // come up with a new core name coreName = createCoreName(caseName); diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/IndexFinder.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/IndexFinder.java index e2abde6eb0..9219a80b74 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/IndexFinder.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/IndexFinder.java @@ -20,10 +20,8 @@ package org.sleuthkit.autopsy.keywordsearch; import java.io.File; import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; import java.util.List; +import java.util.logging.Level; import org.apache.commons.lang.math.NumberUtils; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.Logger; @@ -38,8 +36,8 @@ class IndexFinder { private static final String KWS_OUTPUT_FOLDER_NAME = "keywordsearch"; private static final String KWS_DATA_FOLDER_NAME = "data"; private static final String INDEX_FOLDER_NAME = "index"; - private static final String CURRENT_SOLR_VERSION = "4"; - private static final String CURRENT_SOLR_SCHEMA_VERSION = "2.2"; + private static final String CURRENT_SOLR_VERSION = "8"; + private static final String CURRENT_SOLR_SCHEMA_VERSION = "2.3"; static String getCurrentSolrVersion() { return CURRENT_SOLR_VERSION; @@ -63,6 +61,7 @@ class IndexFinder { // new index should be stored in "\ModuleOutput\keywordsearch\data\solrX_schemaY\index" File targetDirPath = Paths.get(theCase.getModuleDirectory(), KWS_OUTPUT_FOLDER_NAME, KWS_DATA_FOLDER_NAME, indexFolderName, INDEX_FOLDER_NAME).toFile(); //NON-NLS if (!targetDirPath.mkdirs()) { + logger.log(Level.SEVERE, "Unable to create index directory: {0}", targetDirPath.toString()); throw new AutopsyService.AutopsyServiceException("Unable to create text index directory " + targetDirPath.getAbsolutePath()); } return new Index(targetDirPath.getAbsolutePath(), CURRENT_SOLR_VERSION, CURRENT_SOLR_SCHEMA_VERSION, "", theCase.getName()); @@ -93,84 +92,4 @@ class IndexFinder { } return bestCandidateIndex; } - - /** - * Find existing Solr 4 Schema 1.8 index directory location for the case. - * This is done via subdirectory search of all existing - * "ModuleOutput/node_name/keywordsearch/data/" folders. - * - * @param theCase the case to get index dir for - * - * @return List of Index objects for each found index directory - */ - static Index findOldIndexDir(Case theCase) { - // first find all existing "/ModuleOutput/keywordsearch/data/" folders - if (theCase.getCaseType() == Case.CaseType.MULTI_USER_CASE) { - // multi user cases contain a subfolder for each node that participated in case ingest or review. - // Any one (but only one!) of those subfolders may contain the actual index. - /* - * NOTE: the following path is an example of valid Solr 4 Schema 1.8 - * multi-user index path: - * X:\Case\ingest1\ModuleOutput\keywordsearch\data\index - */ - - // get a list of all folder's contents - List contents = getAllContentsInFolder(theCase.getCaseDirectory()); - if (!contents.isEmpty()) { - // decipher "ModuleOutput" directory name from module output path - // (e.g. X:\Case\ingest4\ModuleOutput\) because there is no other way to get it... - String moduleOutDirName = new File(theCase.getModuleDirectory()).getName(); - - // scan all topLevelOutputDir subfolders for presence of non-empty "/ModuleOutput/keywordsearch/data/" folder - for (File item : contents) { - File path = Paths.get(item.getAbsolutePath(), moduleOutDirName, KWS_OUTPUT_FOLDER_NAME, KWS_DATA_FOLDER_NAME, INDEX_FOLDER_NAME).toFile(); //NON-NLS - // must be a non-empty index directory - if (isNonEmptyIndexFolder(path)) { - return new Index(path.toString(), "4", "1.8", theCase.getTextIndexName(), theCase.getName()); - } - } - } - } else { - // single user case - /* - * NOTE: the following path is valid single user Solr 4 Schema 1.8 - * index path: X:\Case\ModuleOutput\keywordsearch\data\index - */ - File path = Paths.get(theCase.getModuleDirectory(), KWS_OUTPUT_FOLDER_NAME, KWS_DATA_FOLDER_NAME, INDEX_FOLDER_NAME).toFile(); //NON-NLS - // must be a non-empty index directory - if (isNonEmptyIndexFolder(path)) { - return new Index(path.toString(), "4", "1.8", theCase.getTextIndexName(), theCase.getName()); - } - } - return null; - } - - /** - * Returns a list of all contents in the folder of interest. - * - * @param path Absolute targetDirPath of the folder of interest - * - * @return List of all contents in the folder of interest - */ - private static List getAllContentsInFolder(String path) { - File directory = new File(path); - File[] contents = directory.listFiles(); - if (contents == null) { - // the directory file is not really a directory.. - return Collections.emptyList(); - } else if (contents.length == 0) { - // Folder is empty - return Collections.emptyList(); - } else { - // Folder has contents - return new ArrayList<>(Arrays.asList(contents)); - } - } - - private static boolean isNonEmptyIndexFolder(File path) { - if (path.exists() && path.isDirectory() && path.getName().equals(INDEX_FOLDER_NAME) && path.listFiles().length > 0) { - return true; - } - return false; - } } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/IndexMetadata.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/IndexMetadata.java index 4594bb36b3..b43ed40798 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/IndexMetadata.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/IndexMetadata.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2020 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -39,7 +39,6 @@ import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; -import org.sleuthkit.autopsy.coreutils.UNCPathUtilities; import org.sleuthkit.autopsy.coreutils.XMLUtil; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -61,11 +60,10 @@ class IndexMetadata { private final static String SOLR_VERSION_ELEMENT_NAME = "SolrVersion"; //NON-NLS private final static String TEXT_INDEX_PATH_ELEMENT_NAME = "TextIndexPath"; //NON-NLS private List indexes = new ArrayList<>(); - private final UNCPathUtilities uncPathUtilities = new UNCPathUtilities(); IndexMetadata(String caseDirectory, Index index) throws TextIndexMetadataException { this.metadataFilePath = Paths.get(caseDirectory, METADATA_FILE_NAME); - this.caseDirectoryPath = Paths.get(uncPathUtilities.convertPathToUNC(caseDirectory)); + this.caseDirectoryPath = Paths.get(caseDirectory); this.indexes.add(index); writeToFile(); } @@ -73,7 +71,7 @@ class IndexMetadata { IndexMetadata(String caseDirectory, List indexes) throws TextIndexMetadataException { this.metadataFilePath = Paths.get(caseDirectory, METADATA_FILE_NAME); - this.caseDirectoryPath = Paths.get(uncPathUtilities.convertPathToUNC(caseDirectory)); + this.caseDirectoryPath = Paths.get(caseDirectory); this.indexes = indexes; writeToFile(); } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Ingester.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Ingester.java index 576b65d581..feeea64957 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Ingester.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Ingester.java @@ -125,7 +125,55 @@ class Ingester { private Map getContentFields(SleuthkitVisitableItem item) { return item.accept(SOLR_FIELDS_VISITOR); } - + + /** + * Read and chunk the source text for indexing in Solr. Also performs + * language detection on the input text. + * + * @param The type of the Appendix provider that provides additional + * text to append to the final chunk. + * @param A subclass of SleuthkitVisibleItem. + * @param Reader The reader containing extracted text. + * @param source The source from which text will be extracted, chunked, and + * indexed. + * @param context The ingest job context that can be used to cancel this + * process. + * + * @return True if indexing was completed, false otherwise. + * + * @throws org.sleuthkit.autopsy.keywordsearch.Ingester.IngesterException + */ + // TODO (JIRA-3118): Cancelled text indexing does not propagate cancellation to clients + < T extends SleuthkitVisitableItem> boolean indexText(Reader sourceReader, long sourceID, String sourceName, T source, IngestJobContext context) throws Ingester.IngesterException { + boolean doLanguageDetection = true; + return indexText(sourceReader, sourceID, sourceName, source, context, doLanguageDetection); + } + + /** + * Read and chunk the source text for indexing in Solr. Does NOT perform + * language detection on the input strings. Per JIRA-7100, it was determined + * that language detection on extracted strings can take a really long time. + * + * @param The type of the Appendix provider that provides additional + * text to append to the final chunk. + * @param A subclass of SleuthkitVisibleItem. + * @param Reader The reader containing extracted text. + * @param source The source from which text will be extracted, chunked, and + * indexed. + * @param context The ingest job context that can be used to cancel this + * process. + * + * @return True if indexing was completed, false otherwise. + * + * @throws org.sleuthkit.autopsy.keywordsearch.Ingester.IngesterException + */ + // TODO (JIRA-3118): Cancelled text indexing does not propagate cancellation to clients + < T extends SleuthkitVisitableItem> boolean indexStrings(Reader sourceReader, long sourceID, String sourceName, T source, IngestJobContext context) throws Ingester.IngesterException { + // Per JIRA-7100, it was determined that language detection on extracted strings can take a really long time. + boolean doLanguageDetection = false; + return indexText(sourceReader, sourceID, sourceName, source, context, doLanguageDetection); + } + /** * Read and chunk the source text for indexing in Solr. * @@ -138,13 +186,14 @@ class Ingester { * and indexed. * @param context The ingest job context that can be used to cancel this * process. + * @param doLanguageDetection A flag whether to perform language detection on the input text/strings. * * @return True if indexing was completed, false otherwise. * * @throws org.sleuthkit.autopsy.keywordsearch.Ingester.IngesterException */ // TODO (JIRA-3118): Cancelled text indexing does not propagate cancellation to clients - < T extends SleuthkitVisitableItem> boolean indexText(Reader sourceReader, long sourceID, String sourceName, T source, IngestJobContext context) throws Ingester.IngesterException { + private < T extends SleuthkitVisitableItem> boolean indexText(Reader sourceReader, long sourceID, String sourceName, T source, IngestJobContext context, boolean doLanguageDetection) throws Ingester.IngesterException { int numChunks = 0; //unknown until chunking is done Map contentFields = Collections.unmodifiableMap(getContentFields(source)); @@ -162,8 +211,11 @@ class Ingester { String chunkId = Server.getChunkIdString(sourceID, numChunks + 1); fields.put(Server.Schema.ID.toString(), chunkId); fields.put(Server.Schema.CHUNK_SIZE.toString(), String.valueOf(chunk.getBaseChunkLength())); - Optional language = languageSpecificContentIndexingHelper.detectLanguageIfNeeded(chunk); - language.ifPresent(lang -> languageSpecificContentIndexingHelper.updateLanguageSpecificFields(fields, chunk, lang)); + Optional language = Optional.empty(); + if (doLanguageDetection) { + language = languageSpecificContentIndexingHelper.detectLanguageIfNeeded(chunk); + language.ifPresent(lang -> languageSpecificContentIndexingHelper.updateLanguageSpecificFields(fields, chunk, lang)); + } try { //add the chunk text to Solr index indexChunk(chunk.toString(), chunk.geLowerCasedChunk(), sourceName, fields); @@ -232,7 +284,7 @@ class Ingester { //Make a SolrInputDocument out of the field map SolrInputDocument updateDoc = new SolrInputDocument(); for (String key : fields.keySet()) { - updateDoc.addField(key, fields.get(key)); + updateDoc.addField(key, Chunker.sanitize((String)fields.get(key)).toString()); } try { diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Installer.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Installer.java index 8119015320..04bcbf6940 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Installer.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Installer.java @@ -19,6 +19,7 @@ package org.sleuthkit.autopsy.keywordsearch; import java.util.logging.Level; +import org.apache.solr.client.solrj.SolrServerException; import org.openide.modules.ModuleInstall; import org.openide.util.NbBundle; import org.openide.windows.WindowManager; @@ -49,12 +50,12 @@ class Installer extends ModuleInstall { server.start(); } catch (SolrServerNoPortException ex) { logger.log(Level.SEVERE, "Failed to start Keyword Search server: ", ex); //NON-NLS - if (ex.getPortNumber() == server.getCurrentSolrServerPort()) { + if (ex.getPortNumber() == server.getLocalSolrServerPort()) { reportPortError(ex.getPortNumber()); } else { reportStopPortError(ex.getPortNumber()); } - } catch (KeywordSearchModuleException ex) { + } catch (KeywordSearchModuleException | SolrServerException ex) { logger.log(Level.SEVERE, "Failed to start Keyword Search server: ", ex); //NON-NLS reportInitError(ex.getMessage()); } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchGlobalLanguageSettingsPanel.form b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchGlobalLanguageSettingsPanel.form index a4c3e517f9..6793f68e27 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchGlobalLanguageSettingsPanel.form +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchGlobalLanguageSettingsPanel.form @@ -16,32 +16,30 @@ - + + - - - - - - - - - - - - + + + - - - - - - + + + + + + + + + + + + - + @@ -50,18 +48,16 @@ - + - - - - + + @@ -105,7 +101,7 @@ - + @@ -139,16 +135,6 @@ - - - - - - - - - - diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchGlobalLanguageSettingsPanel.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchGlobalLanguageSettingsPanel.java index 95ee4faf7f..97a3d7207a 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchGlobalLanguageSettingsPanel.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchGlobalLanguageSettingsPanel.java @@ -70,12 +70,6 @@ class KeywordSearchGlobalLanguageSettingsPanel extends javax.swing.JPanel implem initScriptsCheckBoxes(); reloadScriptsCheckBoxes(); - - if (!PlatformUtil.isWindowsOS() || !PlatformUtil.is64BitOS()) { - enableOcrCheckbox.setText("Enable Optical Character Recognition (OCR) (Requires Windows 64-bit)"); - enableOcrCheckbox.setSelected(false); - enableOcrCheckbox.setEnabled(false); - } //allow panel to toggle its enabled status while it is open based on ingest events IngestManager.getInstance().addIngestJobEventListener(new PropertyChangeListener() { @@ -136,9 +130,6 @@ class KeywordSearchGlobalLanguageSettingsPanel extends javax.swing.JPanel implem = Boolean.parseBoolean(KeywordSearchSettings.getStringExtractOption(StringsExtractOptions.EXTRACT_UTF8.toString())); enableUTF8Checkbox.setSelected(utf8); - boolean ocr = KeywordSearchSettings.getOcrOption(); - enableOcrCheckbox.setSelected(ocr); - final List + + + + + + + + + + + ZIP target and then opens it up and adds in any files that we want. This is where we customize the + version number. --> @@ -124,19 +156,19 @@ - + - - - - - - - - - + + + + + + + + + @@ -146,9 +178,9 @@ + shadow the files in the autopsy/modules/lib/ARCHITECTURE folder in the JAR. + These files are legacy from when we used to copy the dlls to this location. + This check should do away in the future. Added Sept '13--> @@ -188,9 +220,9 @@ + message="Enter the desired build type:" + validargs="DEVELOPMENT,RELEASE" + defaultvalue="DEVELOPMENT"/> @@ -228,21 +260,21 @@ ${app.name} branding + file="${branding.dir}/core/core.jar/org/netbeans/core/startup/Bundle.properties" + comment="Updated by build script"> + file="${branding.dir}/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties" + comment="Updated by build script"> + file="${basedir}/Core/src/org/sleuthkit/autopsy/coreutils/Version.properties" + comment="Updated by build script"> @@ -329,12 +361,15 @@ - + + + + diff --git a/docs/doxygen-user/multi-user/installPostgres.dox b/docs/doxygen-user/multi-user/installPostgres.dox index fd7753281b..d56afe931b 100644 --- a/docs/doxygen-user/multi-user/installPostgres.dox +++ b/docs/doxygen-user/multi-user/installPostgres.dox @@ -13,7 +13,7 @@ You should ensure that the database folder is backed up. To install PostgreSQL, perform the following steps: -1. Download a 64-bit PostgreSQL installer from http://www.enterprisedb.com/products-services-training/pgdownload#windows Choose the one that says _Win X86-64_. Autopsy has been tested with PostgreSQL version 9.5. +1. Download a 64-bit PostgreSQL installer from https://www.enterprisedb.com/downloads/postgres-postgresql-downloads Choose one under Windows x86-64. Autopsy has been tested with PostgreSQL version 9.5. 2. Run the installer. The name will be similar to _postgresql-9.5.3-1-windows-x64.exe_. diff --git a/test/script/regression.py b/test/script/regression.py index 9999a5c563..e1616cc550 100644 --- a/test/script/regression.py +++ b/test/script/regression.py @@ -471,6 +471,12 @@ class TestRunner(object): test_data.ant.append("-DsolrPort=" + str(test_config.solrPort)) test_data.ant.append("-DmessageServiceHost=" + test_config.messageServiceHost) test_data.ant.append("-DmessageServicePort=" + str(test_config.messageServicePort)) + test_data.ant.append("-DcrHost=" + str(test_config.crHost)) + test_data.ant.append("-DcrPort=" + str(test_config.crPort)) + test_data.ant.append("-DcrUserName=" + str(test_config.crUserName)) + test_data.ant.append("-DcrPassword=" + str(test_config.crPassword)) + test_data.ant.append("-DzooKeeperHost=" + str(test_config.zooKeeperHost)) + test_data.ant.append("-DzooKeeperPort=" + str(test_config.zooKeeperPort)) if test_data.isMultiUser: test_data.ant.append("-DisMultiUser=true") # Note: test_data has autopys_version attribute, but we couldn't see it from here. It's set after run ingest. @@ -854,6 +860,18 @@ class TestConfiguration(object): self.messageServicePort = parsed_config.getElementsByTagName("messageServicePort")[0].getAttribute("value").encode().decode("utf_8") if parsed_config.getElementsByTagName("multiUser_outdir"): self.multiUser_outdir = parsed_config.getElementsByTagName("multiUser_outdir")[0].getAttribute("value").encode().decode("utf_8") + if parsed_config.getElementsByTagName("crHost"): + self.crHost = parsed_config.getElementsByTagName("crHost")[0].getAttribute("value").encode().decode("utf_8") + if parsed_config.getElementsByTagName("crPort"): + self.crPort = parsed_config.getElementsByTagName("crPort")[0].getAttribute("value").encode().decode("utf_8") + if parsed_config.getElementsByTagName("crUserName"): + self.crUserName = parsed_config.getElementsByTagName("crUserName")[0].getAttribute("value").encode().decode("utf_8") + if parsed_config.getElementsByTagName("crPassword"): + self.crPassword = parsed_config.getElementsByTagName("crPassword")[0].getAttribute("value").encode().decode("utf_8") + if parsed_config.getElementsByTagName("zooKeeperHost"): + self.zooKeeperHost = parsed_config.getElementsByTagName("zooKeeperHost")[0].getAttribute("value").encode().decode("utf_8") + if parsed_config.getElementsByTagName("zooKeeperPort"): + self.zooKeeperPort = parsed_config.getElementsByTagName("zooKeeperPort")[0].getAttribute("value").encode().decode("utf_8") self._init_imgs(parsed_config) self._init_build_info(parsed_config) diff --git a/test/script/regression_utils.py b/test/script/regression_utils.py index 51fa3eb1c4..0c0229beb2 100644 --- a/test/script/regression_utils.py +++ b/test/script/regression_utils.py @@ -27,7 +27,7 @@ def make_os_path(platform, *dirs): path += str(dir).replace('\\', '/') + '/' return path_fix(path) elif platform == "win32": - return make_path(dirs) + return make_path(*dirs) else: print("Couldn't make path, because we only support Windows and Cygwin at this time.") sys.exit(1) diff --git a/test/script/tskdbdiff.py b/test/script/tskdbdiff.py index f152cd923a..e202c3e111 100644 --- a/test/script/tskdbdiff.py +++ b/test/script/tskdbdiff.py @@ -464,6 +464,16 @@ def normalize_db_entry(line, files_table, vs_parts_table, vs_info_table, fs_info # remove object ID if files_index: + + # Ignore TIFF size and hash if extracted from PDFs. + # See JIRA-6951 for more details. + # index -1 = last element in the list, which is extension + # index -3 = 3rd from the end, which is the parent path. + if fields_list[-1] == "'tif'" and fields_list[-3].endswith(".pdf/'"): + fields_list[15] = "'SIZE_IGNORED'" + fields_list[23] = "'MD5_IGNORED'" + fields_list[24] = "'SHA256_IGNORED'" + newLine = ('INSERT INTO "tsk_files" VALUES(' + ', '.join(fields_list[1:]) + ');') # Remove object ID from Unalloc file name newLine = re.sub('Unalloc_[0-9]+_', 'Unalloc_', newLine) diff --git a/thirdparty/InterestingFileSetRules/Cloud Storage.xml b/thirdparty/InterestingFileSetRules/Cloud Storage.xml index 8e900f8c9a..17f735bfc2 100644 --- a/thirdparty/InterestingFileSetRules/Cloud Storage.xml +++ b/thirdparty/InterestingFileSetRules/Cloud Storage.xml @@ -18,7 +18,6 @@ MegaApp.exe MEGAsync.exe nextcloud.exe - onedrive.exe microsoft.microsoftskydrive.exe owncloud.exe pcloud.exe diff --git a/thirdparty/InterestingFileSetRules/Encryption Programs.xml b/thirdparty/InterestingFileSetRules/Encryption Programs.xml index 5ad59eee3b..51109ca707 100644 --- a/thirdparty/InterestingFileSetRules/Encryption Programs.xml +++ b/thirdparty/InterestingFileSetRules/Encryption Programs.xml @@ -3,7 +3,6 @@ aescrypt.exe AxCrypt.exe - BitLockerDeviceEncryption.exe certainsafe.exe cexpertcmd.exe cexpert_gui.exe diff --git a/thirdparty/aLeapp/LICENSE b/thirdparty/aLeapp/LICENSE new file mode 100644 index 0000000000..ae8fc549fc --- /dev/null +++ b/thirdparty/aLeapp/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Brigs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/thirdparty/aLeapp/aleapp.exe b/thirdparty/aLeapp/aleapp.exe new file mode 100644 index 0000000000..7ed46c4689 Binary files /dev/null and b/thirdparty/aLeapp/aleapp.exe differ diff --git a/thirdparty/iLeapp/ileapp.exe b/thirdparty/iLeapp/ileapp.exe index 8176b4f679..d17ab28f4d 100644 Binary files a/thirdparty/iLeapp/ileapp.exe and b/thirdparty/iLeapp/ileapp.exe differ diff --git a/thirdparty/rr-full/plugins/shellactivities.pl b/thirdparty/rr-full/plugins/shellactivities.pl index 8b16917536..5df9f5615f 100644 --- a/thirdparty/rr-full/plugins/shellactivities.pl +++ b/thirdparty/rr-full/plugins/shellactivities.pl @@ -70,6 +70,7 @@ sub processShellActivities { ::rptMsg(""); while ($offset < ($sz - 10)) { + # Code to locate the appropriate identifier $tag = 1; while ($tag) { @@ -78,9 +79,15 @@ sub processShellActivities { } else { $offset++; + # Check if at end of file and exit loop if it is + last if ($offset >= $sz ); } } - + + # Check if at end of file and exit loop if it is + last if ($offset >= $sz ); + + $offset += 2; $l = unpack("C",substr($data,$offset,1)); # ::rptMsg("String Length: ".sprintf "0x%x",$l); diff --git a/thirdparty/rr-full/shellitems.pl b/thirdparty/rr-full/shellitems.pl index 34b9174c9d..5ec51cd6a2 100644 --- a/thirdparty/rr-full/shellitems.pl +++ b/thirdparty/rr-full/shellitems.pl @@ -537,6 +537,7 @@ sub parseControlPanelEntry { #----------------------------------------------------------- sub parseFolderEntry { my $data = shift; + my $data_length = length($data); my %item = (); $item{type} = unpack("C",substr($data,2,1)); @@ -594,6 +595,9 @@ sub parseFolderEntry { } else { $cnt++; + if (($ofs + $cnt) > $data_length) { + return %item; + } } } $item{extver} = unpack("v",substr($data,$ofs + $cnt - 4,2)); diff --git a/thirdparty/yara/ReadMe.txt b/thirdparty/yara/ReadMe.txt index 31f38633b4..98c356f1b6 100755 --- a/thirdparty/yara/ReadMe.txt +++ b/thirdparty/yara/ReadMe.txt @@ -1,7 +1,7 @@ This folder contains the projects you need for building and testing the yarabridge.dll and YaraJNIWrapper.jar. bin: -Contains the built dll and jar. +Contains the built jar and jarac64.exe. jarac64.exe is used to by the ingest module to compile the rule files. yarabridge: VS project to create the dll that wraps the the libyara library. @@ -18,7 +18,8 @@ Steps for building yarabridge, YaraJNIWrapper and YaraWrapperTest. - Build Release x64. 3. Open the yarabridge project and build Release x64. -If you have link issues, make sure you build release x64 in the previous step. - -This project will automatically copy the built dll to the bin folder. + -This project will automatically copy the built dll into the YaraJNIWrapper src\org\sleuthkit\autopsy\yara folder. + - This is where is needs to be so that its included into the jar file. 4. Build YaraJNIWrapper - Open in netbeans and select Build. - Manually move the newly build jar file to the bin folder. After building the jar file can be found in diff --git a/thirdparty/yara/YaraJNIWrapper/nbproject/build-impl.xml b/thirdparty/yara/YaraJNIWrapper/nbproject/build-impl.xml index 38dd8d0c87..d5569a48c3 100755 --- a/thirdparty/yara/YaraJNIWrapper/nbproject/build-impl.xml +++ b/thirdparty/yara/YaraJNIWrapper/nbproject/build-impl.xml @@ -179,9 +179,7 @@ is divided into following sections: - - - + @@ -289,7 +287,6 @@ is divided into following sections: Must set src.dir - Must set test.src.dir Must set build.dir Must set dist.dir Must set build.classes.dir @@ -588,9 +585,6 @@ is divided into following sections: - - - @@ -613,11 +607,7 @@ is divided into following sections: - - - - - + @@ -1544,14 +1534,14 @@ is divided into following sections: - - + + - + @@ -1592,17 +1582,15 @@ is divided into following sections: - + - + - - - + @@ -1616,14 +1604,12 @@ is divided into following sections: Must select some files in the IDE or set javac.includes - + - - - + diff --git a/thirdparty/yara/YaraJNIWrapper/nbproject/private/private.xml b/thirdparty/yara/YaraJNIWrapper/nbproject/private/private.xml index 475096252c..f6db5d6149 100755 --- a/thirdparty/yara/YaraJNIWrapper/nbproject/private/private.xml +++ b/thirdparty/yara/YaraJNIWrapper/nbproject/private/private.xml @@ -1,4 +1,9 @@ + + + file:/C:/Users/kelly/Workspace/autopsy/thirdparty/yara/YaraJNIWrapper/src/org/sleuthkit/autopsy/yara/YaraJNIWrapper.java + + diff --git a/thirdparty/yara/YaraJNIWrapper/nbproject/project.properties b/thirdparty/yara/YaraJNIWrapper/nbproject/project.properties index a0ef4dac37..0af470a2bf 100755 --- a/thirdparty/yara/YaraJNIWrapper/nbproject/project.properties +++ b/thirdparty/yara/YaraJNIWrapper/nbproject/project.properties @@ -1,9 +1,10 @@ annotation.processing.enabled=true annotation.processing.enabled.in.editor=false -annotation.processing.processor.options= annotation.processing.processors.list= annotation.processing.run.all.processors=true annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output +application.title=YaraJNIWrapper +application.vendor=kelly build.classes.dir=${build.dir}/classes build.classes.excludes=**/*.java,**/*.form # This directory is removed when the project is cleaned: @@ -32,10 +33,13 @@ dist.jar=${dist.dir}/YaraJNIWrapper.jar dist.javadoc.dir=${dist.dir}/javadoc dist.jlink.dir=${dist.dir}/jlink dist.jlink.output=${dist.jlink.dir}/YaraJNIWrapper +endorsed.classpath= excludes= +file.reference.yara-lib=src/org/sleuthkit/autopsy/yara/lib includes=** jar.compress=false -javac.classpath= +javac.classpath=\ + ${file.reference.yara-lib} # Space-separated list of extra javac options javac.compilerargs= javac.deprecation=false @@ -90,4 +94,3 @@ run.test.modulepath=\ ${javac.test.modulepath} source.encoding=UTF-8 src.dir=src -test.src.dir=test diff --git a/thirdparty/yara/YaraJNIWrapper/nbproject/project.xml b/thirdparty/yara/YaraJNIWrapper/nbproject/project.xml index df43138d7e..89ae97a48b 100755 --- a/thirdparty/yara/YaraJNIWrapper/nbproject/project.xml +++ b/thirdparty/yara/YaraJNIWrapper/nbproject/project.xml @@ -7,9 +7,7 @@ - - - + diff --git a/thirdparty/yara/YaraJNIWrapper/src/org/sleuthkit/autopsy/yara/YaraJNIWrapper.java b/thirdparty/yara/YaraJNIWrapper/src/org/sleuthkit/autopsy/yara/YaraJNIWrapper.java index 0fc5e8f0f4..b14cea7fd3 100755 --- a/thirdparty/yara/YaraJNIWrapper/src/org/sleuthkit/autopsy/yara/YaraJNIWrapper.java +++ b/thirdparty/yara/YaraJNIWrapper/src/org/sleuthkit/autopsy/yara/YaraJNIWrapper.java @@ -18,9 +18,11 @@ */ package org.sleuthkit.autopsy.yara; -import java.net.URISyntaxException; -import java.nio.file.Path; -import java.nio.file.Paths; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; @@ -31,18 +33,12 @@ import java.util.logging.Logger; */ public class YaraJNIWrapper { - // Load the yarabridge.dll which should be located in the same directory as - // the jar file. If we need to use this code for debugging the dll this - // code will need to be modified to add that support. static { - Path directoryPath = null; try { - directoryPath = Paths.get(YaraJNIWrapper.class.getProtectionDomain().getCodeSource().getLocation().toURI()).getParent().toAbsolutePath(); - } catch (URISyntaxException ex) { + extractAndLoadDll(); + } catch (IOException | YaraWrapperException ex) { Logger.getLogger(YaraJNIWrapper.class.getName()).log(Level.SEVERE, null, ex); } - String libraryPath = Paths.get(directoryPath != null ? directoryPath.toString() : "", "yarabridge.dll").toAbsolutePath().toString(); - System.load(libraryPath); } /** @@ -50,19 +46,65 @@ public class YaraJNIWrapper { * * The rule path must be to a yara compile rule file. * - * @param compiledRulesPath - * @param byteBuffer + * @param compiledRulesPath Absolute path to a compiled YARA rule file. + * @param byteBuffer File buffer. + * @param bufferSize Size of the byte to read in the given buffer + * @param timeoutSec Scan timeout value in seconds. * * @return List of rules found rules. Null maybe returned if error occurred. * * @throws YaraWrapperException */ - static public native List findRuleMatch(String compiledRulesPath, byte[] byteBuffer) throws YaraWrapperException; + static public native List findRuleMatch(String compiledRulesPath, byte[] byteBuffer, int bufferSize, int timeoutSec) throws YaraWrapperException; + + /** + * Returns a list of matching YARA rules found in the given file. + * + * @param compiledRulePath Absolute path to a compiled YARA rule file. + * @param filePath Absolute path to the file to search. + * @param timeoutSec Scan timeout value in seconds. + * + * @return List of rules found rules. Null maybe returned if error occurred. + * + * + * @throws YaraWrapperException + */ + static public native List findRuleMatchFile(String compiledRulePath, String filePath, int timeoutSec) throws YaraWrapperException; + + /** + * Copy yarabridge.dll from inside the jar to a temp file that can be loaded + * with System.load. + * + * To make this work, the dll needs to be in the same folder as this source + * file. The dll needs to be located somewhere in the jar class path. + * + * @throws IOException + * @throws YaraWrapperException + */ + static private void extractAndLoadDll() throws IOException, YaraWrapperException { + File tempFile = File.createTempFile("lib", null); + tempFile.deleteOnExit(); + try (InputStream in = YaraJNIWrapper.class.getResourceAsStream("yarabridge.dll")) { + if (in == null) { + throw new YaraWrapperException("native library was not found in jar file."); + } + try (OutputStream out = new FileOutputStream(tempFile)) { + byte[] buffer = new byte[1024]; + int lengthRead; + while ((lengthRead = in.read(buffer)) > 0) { + out.write(buffer, 0, lengthRead); + out.flush(); + } + } + } + + System.load(tempFile.getAbsolutePath()); + } /** * private constructor. */ private YaraJNIWrapper() { } - + } diff --git a/thirdparty/yara/YaraJNIWrapper/src/org/sleuthkit/autopsy/yara/yarabridge.dll b/thirdparty/yara/YaraJNIWrapper/src/org/sleuthkit/autopsy/yara/yarabridge.dll new file mode 100755 index 0000000000..4be3a859e1 Binary files /dev/null and b/thirdparty/yara/YaraJNIWrapper/src/org/sleuthkit/autopsy/yara/yarabridge.dll differ diff --git a/thirdparty/yara/YaraWrapperTest/nbproject/project.properties b/thirdparty/yara/YaraWrapperTest/nbproject/project.properties index c0126ab42a..b7874aae82 100755 --- a/thirdparty/yara/YaraWrapperTest/nbproject/project.properties +++ b/thirdparty/yara/YaraWrapperTest/nbproject/project.properties @@ -35,7 +35,7 @@ dist.jlink.dir=${dist.dir}/jlink dist.jlink.output=${dist.jlink.dir}/YaraWrapperTest endorsed.classpath= excludes= -file.reference.YaraJNIWrapper.jar=../bin/YaraJNIWrapper.jar +file.reference.YaraJNIWrapper.jar=../YaraJNIWrapper/dist/YaraJNIWrapper.jar includes=** jar.compress=false javac.classpath=\ diff --git a/thirdparty/yara/YaraWrapperTest/src/org/sleuthkit/autopsy/yara/YaraWrapperTest.java b/thirdparty/yara/YaraWrapperTest/src/org/sleuthkit/autopsy/yara/YaraWrapperTest.java index 4a57abfef2..ee25676896 100755 --- a/thirdparty/yara/YaraWrapperTest/src/org/sleuthkit/autopsy/yara/YaraWrapperTest.java +++ b/thirdparty/yara/YaraWrapperTest/src/org/sleuthkit/autopsy/yara/YaraWrapperTest.java @@ -26,8 +26,6 @@ import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; -import org.sleuthkit.autopsy.yara.YaraJNIWrapper; -import org.sleuthkit.autopsy.yara.YaraWrapperException; /** * Tests the YaraJNIWrapper code. @@ -43,6 +41,7 @@ public class YaraWrapperTest { } testFileRuleMatch(args[0], args[1]); + testFileRuleMatchFile(args[0], args[1]); } /** @@ -58,7 +57,7 @@ public class YaraWrapperTest { try { byte[] data = Files.readAllBytes(path); - List list = YaraJNIWrapper.findRuleMatch(compiledRulePath, data); + List list = YaraJNIWrapper.findRuleMatch(compiledRulePath, data, data.length, 100); if (list != null) { if (list.isEmpty()) { @@ -77,5 +76,34 @@ public class YaraWrapperTest { logger.log(Level.SEVERE, "Error thrown from yarabridge", ex); } } + + /** + * Test the call to findRuleMatchFile which takes a compiled rule file + * path and a path to a file. + * + * @param compiledRulePath + * @param filePath + */ + private static void testFileRuleMatchFile(String compiledRulePath, String filePath) { + try { + List list = YaraJNIWrapper.findRuleMatchFile(compiledRulePath, filePath, 100); + + if (list != null) { + if (list.isEmpty()) { + System.out.println("FindRuleMatch return an empty list"); + } else { + System.out.println("Matching Rules:"); + for (String s : list) { + System.out.println(s); + } + } + } else { + logger.log(Level.SEVERE, "FindRuleMatch return a null list"); + } + + } catch (YaraWrapperException ex) { + logger.log(Level.SEVERE, "Error thrown from yarabridge", ex); + } + } } diff --git a/thirdparty/yara/bin/YaraJNIWrapper.jar b/thirdparty/yara/bin/YaraJNIWrapper.jar index 749d7a6ae7..21fe881a82 100755 Binary files a/thirdparty/yara/bin/YaraJNIWrapper.jar and b/thirdparty/yara/bin/YaraJNIWrapper.jar differ diff --git a/thirdparty/yara/bin/yarabridge.dll b/thirdparty/yara/bin/yarabridge.dll deleted file mode 100755 index c74062a626..0000000000 Binary files a/thirdparty/yara/bin/yarabridge.dll and /dev/null differ diff --git a/thirdparty/yara/bin/yarac64.exe b/thirdparty/yara/bin/yarac64.exe new file mode 100755 index 0000000000..bf94cc4462 Binary files /dev/null and b/thirdparty/yara/bin/yarac64.exe differ diff --git a/thirdparty/yara/yarabridge/yarabridge/YaraJNIWrapper.cpp b/thirdparty/yara/yarabridge/yarabridge/YaraJNIWrapper.cpp index 0d36d2a039..0fc7f63806 100755 --- a/thirdparty/yara/yarabridge/yarabridge/YaraJNIWrapper.cpp +++ b/thirdparty/yara/yarabridge/yarabridge/YaraJNIWrapper.cpp @@ -20,7 +20,6 @@ using std::string; using std::vector; - /* Callback method to be passed to yr_rules_scan_mem method. user_data is expected to be a pointer to a string vector. @@ -79,49 +78,85 @@ jobject createArrayList(JNIEnv *env, std::vector vector) { return list; } +/* + Loads the compiled rules file returning a YARA error code. + Throws a java exeception if there are any issues. +*/ +int loadRuleFile(JNIEnv * env, jstring compiledRulePath, YR_RULES **rules) { + char errorMessage[256]; + const char *nativeString = env->GetStringUTFChars(compiledRulePath, 0); + int result = yr_rules_load(nativeString, rules); + + if (result != ERROR_SUCCESS) { + sprintf_s(errorMessage, "Failed to load compiled yara rule %s (error code = %d)\n", nativeString, result); + throwException(env, errorMessage); + } + + env->ReleaseStringUTFChars(compiledRulePath, nativeString); + + return result; +} + +/* + Initalize the YARA library, if needed. yr_initialize only needs to be called once. +*/ +int initalizeYaraLibrary(JNIEnv * env) { + static int library_initalized = 0; + char errorMessage[256]; + int result = ERROR_SUCCESS; + if (library_initalized == 0) { + if ((result = yr_initialize()) != ERROR_SUCCESS) { + sprintf_s(errorMessage, "libyara initialization error (%d)\n", result); + throwException(env, errorMessage); + } + library_initalized = 1; + } + + return result; +} + /* * Class: org_sleuthkit_autopsy_yara_YaraJNIWrapper * Method: FindRuleMatch * Signature: (Ljava/lang/String;[B)Ljava/util/List; */ JNIEXPORT jobject JNICALL Java_org_sleuthkit_autopsy_yara_YaraJNIWrapper_findRuleMatch -(JNIEnv * env, jclass cls, jstring compiledRulePath, jbyteArray fileByteArray) { +(JNIEnv * env, jclass cls, jstring compiledRulePath, jbyteArray fileByteArray, jint byteArrayLength, jint timeoutSec) { char errorMessage[256]; - const char *nativeString = env->GetStringUTFChars(compiledRulePath, 0); jobject resultList = NULL; - int result; - if ((result = yr_initialize()) != ERROR_SUCCESS) { - sprintf_s(errorMessage, "libyara initialization error (%d)\n", result); - throwException(env, errorMessage); + YR_RULES *rules = NULL; + + if ((result = initalizeYaraLibrary(env)) != ERROR_SUCCESS) { return resultList; } + while (1) { - YR_RULES *rules = NULL; - if ((result = yr_rules_load(nativeString, &rules)) != ERROR_SUCCESS) { - sprintf_s(errorMessage, "Failed to load compiled yara rules (%d)\n", result); - throwException(env, errorMessage); + if((result = loadRuleFile(env, compiledRulePath, &rules)) != ERROR_SUCCESS) { break; } - boolean isCopy; - int byteArrayLength = env->GetArrayLength(fileByteArray); if (byteArrayLength == 0) { throwException(env, "Unable to scan for matches. File byte array size was 0."); break; } + boolean isCopy; jbyte* nativeByteArray = env->GetByteArrayElements(fileByteArray, &isCopy); - int flags = 0; std::vector scanResults; - result = yr_rules_scan_mem(rules, (unsigned char*)nativeByteArray, byteArrayLength, flags, callback, &scanResults, 1000000); + result = yr_rules_scan_mem(rules, (unsigned char*)nativeByteArray, byteArrayLength, 0, callback, &scanResults, timeoutSec); env->ReleaseByteArrayElements(fileByteArray, nativeByteArray, 0); if (result != ERROR_SUCCESS) { - sprintf_s(errorMessage, "Yara file scan failed (%d)\n", result); + if (result == ERROR_SCAN_TIMEOUT) { + sprintf_s(errorMessage, "Yara file scan timed out"); + } + else { + sprintf_s(errorMessage, "Yara file scan failed (%d)\n", result); + } throwException(env, errorMessage); break; } @@ -130,9 +165,60 @@ JNIEXPORT jobject JNICALL Java_org_sleuthkit_autopsy_yara_YaraJNIWrapper_findRul break; } - env->ReleaseStringUTFChars(compiledRulePath, nativeString); - yr_finalize(); + if (rules != NULL) { + yr_rules_destroy(rules); + } return resultList; +} + +/* +* Class: org_sleuthkit_autopsy_yara_YaraJNIWrapper +* Method: findRuleMatchFile +* Signature: (Ljava/lang/String;Ljava/lang/String;)Ljava/util/List; +*/ +JNIEXPORT jobject JNICALL Java_org_sleuthkit_autopsy_yara_YaraJNIWrapper_findRuleMatchFile +(JNIEnv * env, jclass cls, jstring compiledRulePath, jstring filePath, jint timeoutSec) { + + char errorMessage[256]; + jobject resultList = NULL; + int result; + YR_RULES *rules = NULL; + + if ((result = initalizeYaraLibrary(env)) != ERROR_SUCCESS) { + return resultList; + } + + + while (1) { + if ((result = loadRuleFile(env, compiledRulePath, &rules)) != ERROR_SUCCESS) { + break; + } + + std::vector scanResults; + const char *nativeString = env->GetStringUTFChars(filePath, 0); + + result = yr_rules_scan_file(rules, nativeString, 0, callback, &scanResults, timeoutSec); + + if (result != ERROR_SUCCESS) { + if (result == ERROR_SCAN_TIMEOUT) { + sprintf_s(errorMessage, "Yara file scan timed out on file %s", nativeString); + } + else { + sprintf_s(errorMessage, "Yara file scan failed (%d)\n", result); + } + throwException(env, errorMessage); + break; + } + + resultList = createArrayList(env, scanResults); + break; + } + + if (rules != NULL) { + yr_rules_destroy(rules); + } + + return resultList; } \ No newline at end of file diff --git a/thirdparty/yara/yarabridge/yarabridge/YaraJNIWrapper.h b/thirdparty/yara/yarabridge/yarabridge/YaraJNIWrapper.h index 6c5f5f5d75..d93db2ac7a 100755 --- a/thirdparty/yara/yarabridge/yarabridge/YaraJNIWrapper.h +++ b/thirdparty/yara/yarabridge/yarabridge/YaraJNIWrapper.h @@ -13,7 +13,15 @@ extern "C" { * Signature: (Ljava/lang/String;[B)Ljava/util/List; */ JNIEXPORT jobject JNICALL Java_org_sleuthkit_autopsy_yara_YaraJNIWrapper_findRuleMatch - (JNIEnv *, jclass, jstring, jbyteArray); + (JNIEnv *, jclass, jstring, jbyteArray, jint, jint); + + /* + * Class: org_sleuthkit_autopsy_yara_YaraJNIWrapper + * Method: findRuleMatchFile + * Signature: (Ljava/lang/String;Ljava/lang/String;)Ljava/util/List; + */ + JNIEXPORT jobject JNICALL Java_org_sleuthkit_autopsy_yara_YaraJNIWrapper_findRuleMatchFile + (JNIEnv *, jclass, jstring, jstring, jint); #ifdef __cplusplus } diff --git a/thirdparty/yara/yarabridge/yarabridge/yarabridge.vcxproj b/thirdparty/yara/yarabridge/yarabridge/yarabridge.vcxproj index ce5dd10c80..c049e81dc9 100755 --- a/thirdparty/yara/yarabridge/yarabridge/yarabridge.vcxproj +++ b/thirdparty/yara/yarabridge/yarabridge/yarabridge.vcxproj @@ -113,7 +113,7 @@ ws2_32.lib;crypt32.lib;libyara64.lib;%(AdditionalDependencies) - copy "$(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName).dll" "$(SolutionDir)..\bin\$(ProjectName).dll" + copy "$(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName).dll" "$(SolutionDir)..\YaraJNIWrapper\src\org\sleuthkit\autopsy\yara\$(ProjectName).dll" @@ -153,7 +153,7 @@ ws2_32.lib;crypt32.lib;libyara64.lib;%(AdditionalDependencies) - copy "$(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName).dll" "$(SolutionDir)..\bin\$(ProjectName).dll" + copy "$(SolutionDir)$(Platform)\$(Configuration)\$(ProjectName).dll" "$(SolutionDir)..\YaraJNIWrapper\src\org\sleuthkit\autopsy\yara\$(ProjectName).dll" diff --git a/thunderbirdparser/nbproject/project.properties b/thunderbirdparser/nbproject/project.properties index ea9d0786eb..d85c634cf8 100644 --- a/thunderbirdparser/nbproject/project.properties +++ b/thunderbirdparser/nbproject/project.properties @@ -5,6 +5,7 @@ file.reference.commons-lang3-3.8.1.jar=release/modules/ext/commons-lang3-3.8.1.j file.reference.apache-mime4j-core-0.8.0.jar=release/modules/ext/apache-mime4j-core-0.8.0-SNAPSHOT.jar file.reference.apache-mime4j-dom-0.8.0.jar=release/modules/ext/apache-mime4j-dom-0.8.0-SNAPSHOT.jar file.reference.apache-mime4j-mbox-iterator-0.8.0.jar=release/modules/ext/apache-mime4j-mbox-iterator-0.8.0-SNAPSHOT.jar +file.reference.commons-validator-1.6.jar=release/modules/ext/commons-validator-1.6.jar file.reference.guava-19.0.jar=release/modules/ext/guava-19.0.jar file.reference.java-libpst-1.0-SNAPSHOT.jar=release/modules/ext/java-libpst-1.0-SNAPSHOT.jar file.reference.ez-vcard-0.10.5.jar=release/modules/ext/ez-vcard-0.10.5.jar diff --git a/thunderbirdparser/nbproject/project.xml b/thunderbirdparser/nbproject/project.xml index b4d9ca8cbf..94e45a2cc8 100644 --- a/thunderbirdparser/nbproject/project.xml +++ b/thunderbirdparser/nbproject/project.xml @@ -100,6 +100,10 @@ ext/vinnie-2.0.2.jar release/modules/ext/vinnie-2.0.2.jar + + ext/commons-validator-1.6.jar + release/modules/ext/commons-validator-1.6.jar + diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/MboxParser.java b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/MboxParser.java index a17294b15b..1b2a3008eb 100644 --- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/MboxParser.java +++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/MboxParser.java @@ -35,12 +35,16 @@ import java.util.Iterator; import java.util.List; import java.util.logging.Level; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector; import org.apache.james.mime4j.dom.Message; import org.apache.james.mime4j.mboxiterator.CharBufferWrapper; import org.apache.james.mime4j.mboxiterator.MboxIterator; import org.apache.tika.parser.txt.CharsetDetector; import org.apache.tika.parser.txt.CharsetMatch; +import org.apache.commons.validator.routines.EmailValidator; +import org.apache.james.mime4j.mboxiterator.MboxIterator.Builder; import org.openide.util.NbBundle; +import org.sleuthkit.datamodel.AbstractFile; /** * An Iterator for parsing mbox files. Wraps an instance of MBoxEmailIterator. @@ -50,13 +54,34 @@ class MboxParser extends MimeJ4MessageParser implements Iterator { private static final Logger logger = Logger.getLogger(MboxParser.class.getName()); private Iterator emailIterator = null; + + private MboxIterator mboxIterable; private MboxParser(String localPath) { setLocalPath(localPath); } - static boolean isValidMimeTypeMbox(byte[] buffer) { - return (new String(buffer)).startsWith("From "); //NON-NLS + static boolean isValidMimeTypeMbox(byte[] buffer, AbstractFile abstractFile) { + String mboxHeaderLine = new String(buffer); + if (mboxHeaderLine.startsWith("From ")) { + String mimeType = abstractFile.getMIMEType(); + + // if it is not present, attempt to use the FileTypeDetector to determine + if (mimeType == null || mimeType.isEmpty()) { + FileTypeDetector fileTypeDetector = null; + try { + fileTypeDetector = new FileTypeDetector(); + } catch (FileTypeDetector.FileTypeDetectorInitException ex) { + logger.log(Level.WARNING, String.format("Unable to create file type detector for determining MIME type for file %s with id of %d", abstractFile.getName(), abstractFile.getId())); + return false; + } + mimeType = fileTypeDetector.getMIMEType(abstractFile); + } + if (mimeType.equalsIgnoreCase("application/mbox")) { + return true; + } + } + return false; //NON-NLS } /** @@ -108,7 +133,7 @@ class MboxParser extends MimeJ4MessageParser implements Iterator { // That will usually be one of the first ones. for (CharsetEncoder encoder : encoders) { try { - Iterable mboxIterable = MboxIterator.fromFile(mboxFile).charset(encoder.charset()).build(); + mboxIterable = MboxIterator.fromFile(mboxFile).charset(encoder.charset()).build(); if (mboxIterable != null) { emailIterator = new MBoxEmailIterator(mboxIterable.iterator(), encoder, fileID, wholeMsg); } @@ -133,6 +158,13 @@ class MboxParser extends MimeJ4MessageParser implements Iterator { public EmailMessage next() { return emailIterator != null ? emailIterator.next() : null; } + + @Override + public void close() throws Exception { + if(mboxIterable != null) { + mboxIterable.close(); + } + } /** * Get a list of the possible encoders for the given mboxFile using Tika's diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/MimeJ4MessageParser.java b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/MimeJ4MessageParser.java index 828968d750..7b2f6fbf85 100755 --- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/MimeJ4MessageParser.java +++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/MimeJ4MessageParser.java @@ -50,7 +50,7 @@ import org.sleuthkit.datamodel.TskData; /** * Super class for email parsers that can use the james.mime4J.Message objects. */ -class MimeJ4MessageParser { +class MimeJ4MessageParser implements AutoCloseable{ private static final Logger logger = Logger.getLogger(MimeJ4MessageParser.class.getName()); @@ -370,4 +370,9 @@ class MimeJ4MessageParser { private static String getAddresses(AddressList addressList) { return (addressList == null) ? "" : getAddresses(addressList.flatten()); } + + @Override + public void close() throws Exception { + + } } diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/PstParser.java b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/PstParser.java index f747ea2d9f..e3292de3a8 100644 --- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/PstParser.java +++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/PstParser.java @@ -28,6 +28,7 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Iterator; @@ -51,7 +52,7 @@ import org.sleuthkit.datamodel.TskData; * * @author jwallace */ -class PstParser { +class PstParser implements AutoCloseable{ private static final Logger logger = Logger.getLogger(PstParser.class.getName()); /** @@ -120,6 +121,16 @@ class PstParser { return ParseResult.OK; } + + @Override + public void close() throws Exception{ + if(pstFile != null) { + RandomAccessFile file = pstFile.getFileHandle(); + if(file != null) { + file.close(); + } + } + } /** * Creates an EmailMessage iterator for pstFile. These Email objects will be diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java index 12f3342dac..f97bde95fb 100644 --- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java +++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java @@ -130,7 +130,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule { if (abstractFile.getSize() > 64) { int byteRead = abstractFile.read(t, 0, 64); if (byteRead > 0) { - isMbox = MboxParser.isValidMimeTypeMbox(t); + isMbox = MboxParser.isValidMimeTypeMbox(t, abstractFile); isEMLFile = EMLParser.isEMLFile(abstractFile, t); } } @@ -203,79 +203,70 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule { services.postMessage(msg); return ProcessResult.OK; } - - try { - ContentUtils.writeToFile(abstractFile, file, context::fileIngestIsCancelled); - } catch (IOException ex) { - logger.log(Level.WARNING, "Failed writing pst file to disk.", ex); //NON-NLS - return ProcessResult.OK; - } - - PstParser parser = new PstParser(services); - PstParser.ParseResult result = parser.open(file, abstractFile.getId()); - - switch( result) { - case OK: - Iterator pstMsgIterator = parser.getEmailMessageIterator(); - if (pstMsgIterator != null) { - processEmails(parser.getPartialEmailMessages(), pstMsgIterator, abstractFile); - if (context.fileIngestIsCancelled()) { - return ProcessResult.OK; + try (PstParser parser = new PstParser(services)){ + try { + ContentUtils.writeToFile(abstractFile, file, context::fileIngestIsCancelled); + } catch (IOException ex) { + logger.log(Level.WARNING, "Failed writing pst file to disk.", ex); //NON-NLS + return ProcessResult.OK; + } + + PstParser.ParseResult result = parser.open(file, abstractFile.getId()); + + switch( result) { + case OK: + Iterator pstMsgIterator = parser.getEmailMessageIterator(); + if (pstMsgIterator != null) { + processEmails(parser.getPartialEmailMessages(), pstMsgIterator, abstractFile); + if (context.fileIngestIsCancelled()) { + return ProcessResult.OK; + } + } else { + // sometimes parser returns ParseResult=OK but there are no messages + postErrorMessage( + NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.processPst.errProcFile.msg", + abstractFile.getName()), + NbBundle.getMessage(this.getClass(), + "ThunderbirdMboxFileIngestModule.processPst.errProcFile.details")); + logger.log(Level.INFO, "PSTParser failed to parse {0}", abstractFile.getName()); //NON-NLS + return ProcessResult.ERROR; } - } else { - // sometimes parser returns ParseResult=OK but there are no messages + break; + + case ENCRYPT: + // encrypted pst: Add encrypted file artifact + try { + + BlackboardArtifact artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED); + artifact.addAttribute(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME, EmailParserModuleFactory.getModuleName(), NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.encryptionFileLevel"))); + + try { + // index the artifact for keyword search + blackboard.postArtifact(artifact, EmailParserModuleFactory.getModuleName()); + } catch (Blackboard.BlackboardException ex) { + MessageNotifyUtil.Notify.error(Bundle.ThunderbirdMboxFileIngestModule_processPst_indexError_message(), artifact.getDisplayName()); + logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex); //NON-NLS + } + } catch (TskCoreException ex) { + logger.log(Level.INFO, "Failed to add encryption attribute to file: {0}", abstractFile.getName()); //NON-NLS + } + break; + default: + // parsing error: log message postErrorMessage( NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.processPst.errProcFile.msg", abstractFile.getName()), NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.processPst.errProcFile.details")); logger.log(Level.INFO, "PSTParser failed to parse {0}", abstractFile.getName()); //NON-NLS - // delete the temp file - if (file.delete() == false) { - logger.log(Level.INFO, "Failed to delete temp file: {0}", file.getName()); //NON-NLS - } return ProcessResult.ERROR; - } - break; - - case ENCRYPT: - // encrypted pst: Add encrypted file artifact - try { - - BlackboardArtifact artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED); - artifact.addAttribute(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME, EmailParserModuleFactory.getModuleName(), NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.encryptionFileLevel"))); - - try { - // index the artifact for keyword search - blackboard.postArtifact(artifact, EmailParserModuleFactory.getModuleName()); - } catch (Blackboard.BlackboardException ex) { - MessageNotifyUtil.Notify.error(Bundle.ThunderbirdMboxFileIngestModule_processPst_indexError_message(), artifact.getDisplayName()); - logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex); //NON-NLS - } - } catch (TskCoreException ex) { - logger.log(Level.INFO, "Failed to add encryption attribute to file: {0}", abstractFile.getName()); //NON-NLS - } - break; - default: - // parsing error: log message - postErrorMessage( - NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.processPst.errProcFile.msg", - abstractFile.getName()), - NbBundle.getMessage(this.getClass(), - "ThunderbirdMboxFileIngestModule.processPst.errProcFile.details")); - logger.log(Level.INFO, "PSTParser failed to parse {0}", abstractFile.getName()); //NON-NLS - // delete the temp file - if (file.delete() == false) { - logger.log(Level.INFO, "Failed to delete temp file: {0}", file.getName()); //NON-NLS - } - return ProcessResult.ERROR; + } + } catch(Exception ex) { + logger.log(Level.WARNING, String.format("Failed to close temp pst file %s", file.getAbsolutePath())); + } finally { + file.delete(); } - - if (file.delete() == false) { - logger.log(Level.INFO, "Failed to delete temp file: {0}", file.getName()); //NON-NLS - } - return ProcessResult.OK; } @@ -330,13 +321,13 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule { return ProcessResult.OK; } - processMboxFile(file, abstractFile, emailFolder); - if (context.fileIngestIsCancelled()) { - return ProcessResult.OK; - } - - if (file.delete() == false) { - logger.log(Level.INFO, "Failed to delete temp file: {0}", file.getName()); //NON-NLS + try{ + processMboxFile(file, abstractFile, emailFolder); + if (context.fileIngestIsCancelled()) { + return ProcessResult.OK; + } + }finally { + file.delete(); } } else { @@ -357,11 +348,13 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule { logger.log(Level.WARNING, "Failed writing split mbox file to disk.", ex); //NON-NLS return ProcessResult.OK; } - processMboxFile(splitFile, abstractFile, emailFolder); - startingOffset = mboxSplitOffset; - if (splitFile.delete() == false) { - logger.log(Level.INFO, "Failed to delete temp file: {0}", splitFile); //NON-NLS + try{ + processMboxFile(splitFile, abstractFile, emailFolder); + startingOffset = mboxSplitOffset; + } finally { + splitFile.delete(); } + if (context.fileIngestIsCancelled()) { return ProcessResult.OK; } @@ -395,28 +388,30 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule { private void processMboxFile(File file, AbstractFile abstractFile, String emailFolder) { - - MboxParser emailIterator = MboxParser.getEmailIterator( emailFolder, file, abstractFile.getId()); - List emails = new ArrayList<>(); - if(emailIterator != null) { - while(emailIterator.hasNext()) { - if (context.fileIngestIsCancelled()) { - return; + try(MboxParser emailIterator = MboxParser.getEmailIterator( emailFolder, file, abstractFile.getId())) { + List emails = new ArrayList<>(); + if(emailIterator != null) { + while(emailIterator.hasNext()) { + if (context.fileIngestIsCancelled()) { + return; + } + EmailMessage emailMessage = emailIterator.next(); + if(emailMessage != null) { + emails.add(emailMessage); + } } - EmailMessage emailMessage = emailIterator.next(); - if(emailMessage != null) { - emails.add(emailMessage); + + String errors = emailIterator.getErrors(); + if (!errors.isEmpty()) { + postErrorMessage( + NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.processMBox.errProcFile.msg2", + abstractFile.getName()), errors); } } - - String errors = emailIterator.getErrors(); - if (!errors.isEmpty()) { - postErrorMessage( - NbBundle.getMessage(this.getClass(), "ThunderbirdMboxFileIngestModule.processMBox.errProcFile.msg2", - abstractFile.getName()), errors); - } + processEmails(emails, MboxParser.getEmailIterator( emailFolder, file, abstractFile.getId()), abstractFile); + } catch(Exception ex) { + logger.log(Level.WARNING, String.format("Failed to close mbox temp file %s", file.getAbsolutePath())); } - processEmails(emails, MboxParser.getEmailIterator( emailFolder, file, abstractFile.getId()), abstractFile); } @@ -681,6 +676,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule { * * @param email The e-mail message. * @param abstractFile The associated file. + * @param accountFileInstanceCache The current cache of account instances. * * @return The generated e-mail message artifact. */ diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/VcardParser.java b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/VcardParser.java index d662c5f5e6..886a3bc41f 100755 --- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/VcardParser.java +++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/VcardParser.java @@ -53,6 +53,7 @@ import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.AccountFileInstance; import org.sleuthkit.datamodel.Blackboard; +import org.sleuthkit.datamodel.Blackboard.BlackboardException; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.Content; @@ -421,14 +422,16 @@ final class VcardParser { if (attributeType == null) { try{ // Add this attribute type to the case database. - attributeType = tskCase.addArtifactAttributeType(attributeTypeName, + attributeType = tskCase.getBlackboard().getOrAddAttributeType(attributeTypeName, BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING, String.format("Phone Number (%s)", StringUtils.capitalize(splitType.toLowerCase()))); - }catch (TskDataException ex) { - attributeType = tskCase.getAttributeType(attributeTypeName); + + ThunderbirdMboxFileIngestModule.addArtifactAttribute(telephoneText, attributeType, attributes); + }catch (BlackboardException ex) { + logger.log(Level.WARNING, String.format("Unable to retrieve attribute type '%s' for file '%s' (id=%d).", attributeTypeName, abstractFile.getName(), abstractFile.getId()), ex); } } - ThunderbirdMboxFileIngestModule.addArtifactAttribute(telephoneText, attributeType, attributes); + } catch (TskCoreException ex) { logger.log(Level.WARNING, String.format("Unable to retrieve attribute type '%s' for file '%s' (id=%d).", attributeTypeName, abstractFile.getName(), abstractFile.getId()), ex); } @@ -474,14 +477,14 @@ final class VcardParser { BlackboardAttribute.Type attributeType = tskCase.getAttributeType(attributeTypeName); if (attributeType == null) { // Add this attribute type to the case database. - attributeType = tskCase.addArtifactAttributeType(attributeTypeName, + attributeType = tskCase.getBlackboard().getOrAddAttributeType(attributeTypeName, BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING, String.format("Email (%s)", StringUtils.capitalize(splitType.toLowerCase()))); } ThunderbirdMboxFileIngestModule.addArtifactAttribute(email.getValue(), attributeType, attributes); } catch (TskCoreException ex) { logger.log(Level.SEVERE, String.format("Unable to retrieve attribute type '%s' for file '%s' (id=%d).", attributeTypeName, abstractFile.getName(), abstractFile.getId()), ex); - } catch (TskDataException ex) { + } catch (BlackboardException ex) { logger.log(Level.SEVERE, String.format("Unable to add custom attribute type '%s' for file '%s' (id=%d).", attributeTypeName, abstractFile.getName(), abstractFile.getId()), ex); } } diff --git a/travis_build.sh b/travis_build.sh index 5f574d5726..a561ccd21e 100755 --- a/travis_build.sh +++ b/travis_build.sh @@ -8,5 +8,16 @@ pushd bindings/java && ant -q dist && popd echo "Building Autopsy..." && echo -en 'travis_fold:start:script.build\\r' cd $TRAVIS_BUILD_DIR/ -ant build +ant -q build echo -en 'travis_fold:end:script.build\\r' + +echo "Testing Autopsy..." && echo -en 'travis_fold:start:script.tests\\r' +echo "Free Space:" +echo `df -h .` + +if [ "${TRAVIS_OS_NAME}" = "linux" ]; then + # if linux use xvfb + xvfb-run ant -q test-no-regression +fi + +echo -en 'travis_fold:end:script.tests\\r'