From 3b4d2206988dc7887e295d06c59e1e5d38d279ee Mon Sep 17 00:00:00 2001 From: Mark Kidder <83427558+mark-sil@users.noreply.github.com> Date: Tue, 14 Jan 2025 08:10:13 -0500 Subject: [PATCH 001/123] Fix problems with using local palaso and chorus (#246) - Use .net framework 462 for palaso and chorus - For Chorus, modify the versions for the specific package names instead of everything that begins with SIL.Chorus. This prevents modifying the version of SIL.Chorus.l10ns A remaining task is to either remove the line containing "SIL.Core" version="8.1.0-beta0035" from packages.config, or modify the sed command so that it does not change this version number. --- Build/buildLocalLibraries.sh | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/Build/buildLocalLibraries.sh b/Build/buildLocalLibraries.sh index 61b8219796..dd77514556 100755 --- a/Build/buildLocalLibraries.sh +++ b/Build/buildLocalLibraries.sh @@ -5,6 +5,9 @@ libpalaso_dir="" liblcm_dir="" chorus_dir="" +libpalaso_net_ver="net462" +liblcm_net_ver="net461" +chorus_net_ver="net462" mkall_targets_file="mkall.targets" packages_dir="../packages" @@ -43,7 +46,7 @@ function delete_and_pack_liblcm { (cd "$liblcm_dir/artifacts" && rm *nupkg) echo "Running 'dotnet pack' in the liblcm directory: $liblcm_dir" - pack_output=$(cd "$liblcm_dir" && dotnet pack -c Debug -p:TargetFrameworks=net461) + pack_output=$(cd "$liblcm_dir" && dotnet pack -c Debug -p:TargetFrameworks=$liblcm_net_ver) # Extract version number using regex if [[ $pack_output =~ $version_regex ]]; then @@ -53,7 +56,7 @@ function delete_and_pack_liblcm { echo "Error: Unable to extract version number from dotnet pack output. (Maybe build failure or nothing needed building?)" exit 1 fi - copy_pdb_files "$liblcm_dir/artifacts/Debug/net461" + copy_pdb_files "$liblcm_dir/artifacts/Debug/$liblcm_net_ver" fi # Update LcmNugetVersion in mkall.targets @@ -74,20 +77,21 @@ function delete_and_pack_chorus { echo "Error: The specified packages directory does not exist: $packages_dir" exit 1 fi - + prefixes=("SIL.Chorus.App" "SIL.Chorus.LibChorus") if [ "$use_manual_version" == true ]; then version_number="$manual_version" else - prefix="SIL.Chorus" echo "Deleting files starting with specified prefix in $packages_dir" - find "$packages_dir" -name "${prefix}*" -exec rm -f -r {} \; + for prefix in "${prefixes[@]}"; do + find "$packages_dir" -name "${prefix}*" -exec rm -f -r {} \; + done echo "Removing chorus output packages so that dotnet pack will run and output the version" (cd "$chorus_dir/output" && rm *nupkg) echo "Running 'dotnet pack' in the chorus directory: $chorus_dir" - pack_output=$(cd "$chorus_dir" && dotnet pack -c Debug -p:TargetFrameworks=net461) + pack_output=$(cd "$chorus_dir" && dotnet pack -c Debug -p:TargetFrameworks=$chorus_net_ver) # Extract version number using regex if [[ $pack_output =~ $version_regex ]]; then @@ -97,13 +101,15 @@ function delete_and_pack_chorus { echo "Error: Unable to extract version number from dotnet pack output." exit 1 fi - copy_pdb_files "$chorus_dir/Output/Debug/net461" + copy_pdb_files "$chorus_dir/Output/Debug/$chorus_net_ver" fi # Update ChorusNugetVersion in mkall.targets update_mkall_targets "ChorusNugetVersion" "$version_number" # Update packages.config with extracted version - update_packages_config "SIL.Chorus" "$version_number" + for prefix in "${prefixes[@]}"; do + update_packages_config "$prefix" "$version_number" + done fi } @@ -130,7 +136,7 @@ function delete_and_pack_libpalaso { (cd "$libpalaso_dir/output" && rm *nupkg) echo "Running 'dotnet pack' in the libpalaso directory: $libpalaso_dir" - pack_output=$(cd "$libpalaso_dir" && dotnet pack -c Debug -p:TargetFrameworks=net461) + pack_output=$(cd "$libpalaso_dir" && dotnet pack -c Debug -p:TargetFrameworks=$libpalaso_net_ver) # Extract version number using regex if [[ $pack_output =~ $version_regex ]]; then @@ -140,7 +146,7 @@ function delete_and_pack_libpalaso { echo "Error: Unable to extract version number from dotnet pack output." exit 1 fi - copy_pdb_files "$libpalaso_dir/output/Debug/net461" + copy_pdb_files "$libpalaso_dir/output/Debug/$libpalaso_net_ver" fi # Update PalasoNugetVersion in mkall.targets From 9cb20de15dd86879aba70f63b0b085a35dce060c Mon Sep 17 00:00:00 2001 From: Mark Kidder <83427558+mark-sil@users.noreply.github.com> Date: Thu, 16 Jan 2025 12:00:46 -0500 Subject: [PATCH 002/123] Word Export: prevent cross-thread exception (#184) * Word Export: prevent cross-thread exception Use InvokeRequired and Invoke to re-execute the TextForReal call. Notes: - XHTML export is also calling this from a different thread but we were not seeing this error because the text string was the same as the current value so no change was made. - The exception was only happening when there were reversals and it seemed to only/mostly happen in debug builds. --- Src/Common/Controls/FwControls/StatusBarTextBox.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Src/Common/Controls/FwControls/StatusBarTextBox.cs b/Src/Common/Controls/FwControls/StatusBarTextBox.cs index b6f690e5ac..b4071a64bb 100644 --- a/Src/Common/Controls/FwControls/StatusBarTextBox.cs +++ b/Src/Common/Controls/FwControls/StatusBarTextBox.cs @@ -135,6 +135,12 @@ public string TextForReal { CheckDisposed(); + if (m_bar.InvokeRequired) + { + m_bar.Invoke((Action)(s => this.TextForReal = s), value); + return; + } + m_text = value; // But we still need to set the Text property to get autosizing to work. this.Text = m_text; From e94c063da1387afbdc2827517060512942998980 Mon Sep 17 00:00:00 2001 From: John T Maxwell III Date: Wed, 22 Jan 2025 12:02:15 -0800 Subject: [PATCH 003/123] Fix LT-22001 (#247) --- .../XMLViews/ColumnConfigureDialog.cs | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Src/Common/Controls/XMLViews/ColumnConfigureDialog.cs b/Src/Common/Controls/XMLViews/ColumnConfigureDialog.cs index 67cafd74ca..9f68e85151 100644 --- a/Src/Common/Controls/XMLViews/ColumnConfigureDialog.cs +++ b/Src/Common/Controls/XMLViews/ColumnConfigureDialog.cs @@ -383,9 +383,9 @@ public static void AddWritingSystemsToCombo(LcmCache cache, } } AddWritingSystemsToCombo(cache, items, - cache.ServiceLocator.WritingSystems.CurrentVernacularWritingSystems); + cache.ServiceLocator.WritingSystems.VernacularWritingSystems); AddWritingSystemsToCombo(cache, items, - cache.ServiceLocator.WritingSystems.CurrentAnalysisWritingSystems); + cache.ServiceLocator.WritingSystems.AnalysisWritingSystems); break; case WsComboContent.kwccAnalAndVern: if (!skipDefaults) @@ -403,9 +403,9 @@ public static void AddWritingSystemsToCombo(LcmCache cache, } } AddWritingSystemsToCombo(cache, items, - cache.ServiceLocator.WritingSystems.CurrentAnalysisWritingSystems); + cache.ServiceLocator.WritingSystems.AnalysisWritingSystems); AddWritingSystemsToCombo(cache, items, - cache.ServiceLocator.WritingSystems.CurrentVernacularWritingSystems); + cache.ServiceLocator.WritingSystems.VernacularWritingSystems); break; case WsComboContent.kwccBestAnalOrVern: if (!skipDefaults) @@ -424,9 +424,9 @@ public static void AddWritingSystemsToCombo(LcmCache cache, } AddWritingSystemsToCombo(cache, items, - cache.ServiceLocator.WritingSystems.CurrentAnalysisWritingSystems); + cache.ServiceLocator.WritingSystems.AnalysisWritingSystems); AddWritingSystemsToCombo(cache, items, - cache.ServiceLocator.WritingSystems.CurrentVernacularWritingSystems); + cache.ServiceLocator.WritingSystems.VernacularWritingSystems); break; case WsComboContent.kwccBestAnalysis: if (!skipDefaults) @@ -440,7 +440,7 @@ public static void AddWritingSystemsToCombo(LcmCache cache, } AddWritingSystemsToCombo(cache, items, - cache.ServiceLocator.WritingSystems.CurrentAnalysisWritingSystems); + cache.ServiceLocator.WritingSystems.AnalysisWritingSystems); break; case WsComboContent.kwccBestVernacular: if (!skipDefaults) @@ -453,7 +453,7 @@ public static void AddWritingSystemsToCombo(LcmCache cache, } } AddWritingSystemsToCombo(cache, items, - cache.ServiceLocator.WritingSystems.CurrentVernacularWritingSystems); + cache.ServiceLocator.WritingSystems.VernacularWritingSystems); break; case WsComboContent.kwccBestVernOrAnal: if (!skipDefaults) @@ -472,9 +472,9 @@ public static void AddWritingSystemsToCombo(LcmCache cache, } AddWritingSystemsToCombo(cache, items, - cache.ServiceLocator.WritingSystems.CurrentVernacularWritingSystems); + cache.ServiceLocator.WritingSystems.VernacularWritingSystems); AddWritingSystemsToCombo(cache, items, - cache.ServiceLocator.WritingSystems.CurrentAnalysisWritingSystems); + cache.ServiceLocator.WritingSystems.AnalysisWritingSystems); break; case WsComboContent.kwccAnalysis: if (!skipDefaults) @@ -488,12 +488,12 @@ public static void AddWritingSystemsToCombo(LcmCache cache, } AddWritingSystemsToCombo(cache, items, - cache.ServiceLocator.WritingSystems.CurrentAnalysisWritingSystems); + cache.ServiceLocator.WritingSystems.AnalysisWritingSystems); break; case WsComboContent.kwccVernacularInParagraph: items.Add(new WsComboItem(sVernacularInPara, "vern in para")); AddWritingSystemsToCombo(cache, items, - cache.ServiceLocator.WritingSystems.CurrentVernacularWritingSystems); + cache.ServiceLocator.WritingSystems.VernacularWritingSystems); break; case WsComboContent.kwccVernacular: if (!skipDefaults) @@ -507,7 +507,7 @@ public static void AddWritingSystemsToCombo(LcmCache cache, } AddWritingSystemsToCombo(cache, items, - cache.ServiceLocator.WritingSystems.CurrentVernacularWritingSystems); + cache.ServiceLocator.WritingSystems.VernacularWritingSystems); break; case WsComboContent.kwccPronunciation: if (!skipDefaults) From 985bbc7ad56e539c17f460441d584e4e91bb2b19 Mon Sep 17 00:00:00 2001 From: "D. Ror." Date: Thu, 23 Jan 2025 14:57:44 -0500 Subject: [PATCH 004/123] Preserve sense examples, subsenses importing from The Combine (#253) --- Src/LexText/LexTextControls/LiftMerger.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Src/LexText/LexTextControls/LiftMerger.cs b/Src/LexText/LexTextControls/LiftMerger.cs index 0a3d479f64..4d7abc3afe 100644 --- a/Src/LexText/LexTextControls/LiftMerger.cs +++ b/Src/LexText/LexTextControls/LiftMerger.cs @@ -4003,7 +4003,7 @@ private void MergeIntoExistingSense(ILexSense ls, CmLiftSense sense) setUsed.Add(lsSub.Hvo); } // If we're keeping only the imported data, delete any unused subsense. - if (m_msImport == MergeStyle.MsKeepOnlyNew || m_msImport == MergeStyle.MsTheCombine) + if (m_msImport == MergeStyle.MsKeepOnlyNew) { foreach (int hvo in ls.SensesOS.ToHvoArray()) { @@ -4063,7 +4063,7 @@ private void MergeSenseExamples(ILexSense ls, CmLiftSense sense) setUsed.Add(les.Hvo); } // If we're keeping only the imported data, delete any unused example. - if (m_msImport == MergeStyle.MsKeepOnlyNew || m_msImport == MergeStyle.MsTheCombine) + if (m_msImport == MergeStyle.MsKeepOnlyNew) { foreach (int hvo in ls.ExamplesOS.ToHvoArray()) { @@ -4297,7 +4297,7 @@ private void MergeExampleTranslations(ILexExampleSentence les, CmLiftExample exp setUsed.Add(ct.Hvo); } // If we're keeping only the imported data, erase any unused existing data first. - if (m_msImport == MergeStyle.MsKeepOnlyNew || m_msImport == MergeStyle.MsTheCombine) + if (m_msImport == MergeStyle.MsKeepOnlyNew) { foreach (int hvo in les.TranslationsOC.ToHvoArray()) { From 4fb0ef9aa3ec319b1528dcfa0442a67395f5843c Mon Sep 17 00:00:00 2001 From: John T Maxwell III Date: Thu, 23 Jan 2025 13:23:20 -0800 Subject: [PATCH 005/123] LT-22021: Add Word export doc to Help > Resources menu (#252) * LT-22021 Add documentation for export to Word Added access to Publishing Flex dictionaries via Word Change-Id: I1c2beaaa92f18b07ef8d6ab7764edaad48a4b2c2 * Replaced OnHelpPublishToWord --------- Co-authored-by: Ken Zook Co-authored-by: Mark Kidder <83427558+mark-sil@users.noreply.github.com> --- DistFiles/Language Explorer/Configuration/ContextHelp.xml | 1 + DistFiles/Language Explorer/Configuration/Main.xml | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/DistFiles/Language Explorer/Configuration/ContextHelp.xml b/DistFiles/Language Explorer/Configuration/ContextHelp.xml index 63102f131a..aa45f1fdcd 100644 --- a/DistFiles/Language Explorer/Configuration/ContextHelp.xml +++ b/DistFiles/Language Explorer/Configuration/ContextHelp.xml @@ -229,6 +229,7 @@ Open the Demo Movies online. Display Introduction to Lexicography (only available in English). Display technical notes on SFM database import (only available in English). + Display publishing FLEx dictionaries using Microsoft Word (only available in English). Display technical notes on FieldWorks Send/Receive (only available in English). Display technical notes on LinguaLinks database import (only available in English). Display technical notes on interlinear import (only available in English). diff --git a/DistFiles/Language Explorer/Configuration/Main.xml b/DistFiles/Language Explorer/Configuration/Main.xml index f356e34e46..a29bea9509 100644 --- a/DistFiles/Language Explorer/Configuration/Main.xml +++ b/DistFiles/Language Explorer/Configuration/Main.xml @@ -158,6 +158,9 @@ + + + @@ -607,6 +610,7 @@ + From 0ea8e06b13dc756abb9c76f083ff90369fb383bf Mon Sep 17 00:00:00 2001 From: John T Maxwell III Date: Fri, 24 Jan 2025 11:37:57 -0800 Subject: [PATCH 006/123] Fix LT-21962 (#254) --- Src/xWorks/RecordClerk.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Src/xWorks/RecordClerk.cs b/Src/xWorks/RecordClerk.cs index 8500c747b4..a17b133313 100644 --- a/Src/xWorks/RecordClerk.cs +++ b/Src/xWorks/RecordClerk.cs @@ -522,7 +522,8 @@ protected virtual bool TryRestoreFilter(XmlNode clerkConfiguration, LcmCache cac } if (m_list.Filter == filter) return false; - m_list.Filter = filter; + // Use OnChangeFilter so that column headers get updated (LT-21962). + OnChangeFilter(new FilterChangeEventArgs(filter, m_list.Filter)); m_list.TransferOwnership(filter as IDisposable); return true; } From 430b73c68d0872f865fb2311ab6d84ef38923cf2 Mon Sep 17 00:00:00 2001 From: "Ariel Ror." Date: Tue, 4 Feb 2025 11:12:27 -0500 Subject: [PATCH 007/123] LT-22025: Fix font issues in image captions (#255) * LT-22025: Fix font issues in image captions Root problem is that some image caption component styles were not based on writing system styles in the Word export. * Create function "GenerateContentForSimpleString" that gets a writing system and passes it to AddProperty to generate the string property content. * Use first analysis language as the default writing system for string properties. * In AddProperty, use AddRun with the writing system to get/create a style that is based on the writing system. * Delete unused WriteElementContents method. --- Src/xWorks/ConfiguredLcmGenerator.cs | 68 +++++++++++++++++++++------- Src/xWorks/ILcmContentGenerator.cs | 2 +- Src/xWorks/LcmJsonGenerator.cs | 2 +- Src/xWorks/LcmWordGenerator.cs | 38 +++++++--------- Src/xWorks/LcmXhtmlGenerator.cs | 2 +- Src/xWorks/WordStylesGenerator.cs | 7 +++ 6 files changed, 79 insertions(+), 40 deletions(-) diff --git a/Src/xWorks/ConfiguredLcmGenerator.cs b/Src/xWorks/ConfiguredLcmGenerator.cs index ddcaacd052..de35ad4aa2 100644 --- a/Src/xWorks/ConfiguredLcmGenerator.cs +++ b/Src/xWorks/ConfiguredLcmGenerator.cs @@ -2512,18 +2512,15 @@ private static IFragment GenerateContentForValue(object field, object propertyVa if (propertyValue is int) { - var cssClassName = settings.StylesGenerator.AddStyles(config).Trim('.'); ; - return settings.ContentGenerator.AddProperty(config, cssClassName, false, propertyValue.ToString()); + return GenerateContentForSimpleString(config, settings, false, propertyValue.ToString()); } if (propertyValue is DateTime) { - var cssClassName = settings.StylesGenerator.AddStyles(config).Trim('.'); ; - return settings.ContentGenerator.AddProperty(config, cssClassName, false, ((DateTime)propertyValue).ToLongDateString()); + return GenerateContentForSimpleString(config, settings, false, ((DateTime)propertyValue).ToLongDateString()); } else if (propertyValue is GenDate) { - var cssClassName = settings.StylesGenerator.AddStyles(config).Trim('.'); ; - return settings.ContentGenerator.AddProperty(config, cssClassName, false, ((GenDate)propertyValue).ToLongString()); + return GenerateContentForSimpleString(config, settings, false, ((GenDate)propertyValue).ToLongString()); } else if (propertyValue is IMultiAccessorBase) { @@ -2533,8 +2530,7 @@ private static IFragment GenerateContentForValue(object field, object propertyVa } else if (propertyValue is string) { - var cssClassName = settings.StylesGenerator.AddStyles(config).Trim('.'); - return settings.ContentGenerator.AddProperty(config, cssClassName, false, propertyValue.ToString()); + return GenerateContentForSimpleString(config, settings, false, propertyValue.ToString()); } else if (propertyValue is IStText) { @@ -2570,16 +2566,18 @@ private static IFragment GenerateContentForValue(object field, object propertyVa } } - private static IFragment WriteElementContents(object propertyValue, - ConfigurableDictionaryNode config, GeneratorSettings settings) + /// + /// This method will add a property containing the string, using the first selected writing system, + /// or the first analysis writing system if no writing system is selected. + /// + private static IFragment GenerateContentForSimpleString(ConfigurableDictionaryNode config, + GeneratorSettings settings, bool isBlockProperty, string simpleString) { - var content = propertyValue.ToString(); - if (!String.IsNullOrEmpty(content)) - { - return settings.ContentGenerator.AddProperty(config, GetClassNameAttributeForConfig(config), IsBlockProperty(config), content); - } + var writingSystem = GetLanguageFromFirstOptionOrAnalysis(config.DictionaryNodeOptions as DictionaryNodeWritingSystemOptions, + settings.Cache); + var cssClassName = settings.StylesGenerator.AddStyles(config).Trim('.'); + return settings.ContentGenerator.AddProperty(config, settings.PropertyTable, cssClassName, false, simpleString, writingSystem); - return settings.ContentGenerator.CreateFragment(); } private static IFragment GenerateContentForStrings(IMultiStringAccessor multiStringAccessor, ConfigurableDictionaryNode config, @@ -3084,6 +3082,29 @@ internal static bool IsBlockProperty(ConfigurableDictionaryNode config) /// /// This method returns the lang attribute value from the first selected writing system in the given options. + /// It defaults to the first analysis writing system if no options are given, and English if no analysis writing system is specified. + /// + /// + /// + /// + private static string GetLanguageFromFirstOptionOrAnalysis(DictionaryNodeWritingSystemOptions wsOptions, LcmCache cache) + { + if (wsOptions == null) + { + const string defaultLang = "en"; + var analWs = cache.WritingSystemFactory.GetStrFromWs(cache.DefaultAnalWs); + if (analWs == null) + return defaultLang; + + return analWs; + } + + return GetLanguageFromFirstWs(wsOptions, cache); + } + + /// + /// This method returns the lang attribute value from the first selected writing system in the given options. + /// It defaults to English if no options are given. /// /// /// @@ -3093,6 +3114,21 @@ private static string GetLanguageFromFirstOption(DictionaryNodeWritingSystemOpti const string defaultLang = "en"; if (wsOptions == null) return defaultLang; + return GetLanguageFromFirstWs(wsOptions, cache); + } + + /// + /// This method returns the lang attribute value from the first selected writing system in the given options. + /// Returns null if no options are given. + /// + /// + /// + /// + private static string GetLanguageFromFirstWs(DictionaryNodeWritingSystemOptions wsOptions, LcmCache cache) + { + if (wsOptions == null) + return null; + foreach (var option in wsOptions.Options) { if (option.IsEnabled) diff --git a/Src/xWorks/ILcmContentGenerator.cs b/Src/xWorks/ILcmContentGenerator.cs index 753a07dc08..32cf1fc4f5 100644 --- a/Src/xWorks/ILcmContentGenerator.cs +++ b/Src/xWorks/ILcmContentGenerator.cs @@ -24,7 +24,7 @@ IFragment GenerateGroupingNode(ConfigurableDictionaryNode config, object field, Func childContentGenerator); IFragment AddSenseData(ConfigurableDictionaryNode config, IFragment senseNumberSpan, Guid ownerGuid, IFragment senseContent, bool first); IFragment AddCollectionItem(ConfigurableDictionaryNode config, bool isBlock, string collectionItemClass, IFragment content, bool first); - IFragment AddProperty(ConfigurableDictionaryNode config, string className, bool isBlockProperty, string content); + IFragment AddProperty(ConfigurableDictionaryNode config, ReadOnlyPropertyTable propTable, string className, bool isBlockProperty, string content, string writingSystem); IFragment CreateFragment(); IFragment CreateFragment(string str); IFragmentWriter CreateWriter(IFragment fragment); diff --git a/Src/xWorks/LcmJsonGenerator.cs b/Src/xWorks/LcmJsonGenerator.cs index 6c4b4bb036..483ad7a64d 100644 --- a/Src/xWorks/LcmJsonGenerator.cs +++ b/Src/xWorks/LcmJsonGenerator.cs @@ -124,7 +124,7 @@ public IFragment AddCollectionItem(ConfigurableDictionaryNode config, bool isBlo return fragment; } - public IFragment AddProperty(ConfigurableDictionaryNode config, string className, bool isBlockProperty, string content) + public IFragment AddProperty(ConfigurableDictionaryNode config, ReadOnlyPropertyTable propTable, string className, bool isBlockProperty, string content, string writingSystem) { var fragment = new StringFragment($"\"{className}\": \"{content}\","); return fragment; diff --git a/Src/xWorks/LcmWordGenerator.cs b/Src/xWorks/LcmWordGenerator.cs index 388879ee1c..c47ba49dc8 100644 --- a/Src/xWorks/LcmWordGenerator.cs +++ b/Src/xWorks/LcmWordGenerator.cs @@ -1121,39 +1121,35 @@ config.DictionaryNodeOptions is IParaOption && return collData; } - public IFragment AddProperty(ConfigurableDictionaryNode config, string className, bool isBlockProperty, string content) + public IFragment AddProperty(ConfigurableDictionaryNode config, ReadOnlyPropertyTable propTable, string className, bool isBlockProperty, string content, string writingSystem) { var propFrag = new DocFragment(); Run contentRun = null; string styleDisplayName = null; - // Add the content with the style. - if (!string.IsNullOrEmpty(content)) + if (content == null) { - if (!string.IsNullOrEmpty(config.Style)) - { - string displayNameBase = !string.IsNullOrEmpty(config.DisplayLabel) ? config.DisplayLabel : config.Style; - - Style style = GetOrCreateCharacterStyle(config.Style, displayNameBase, _propertyTable); - if (style != null) - { - styleDisplayName = style.StyleId; - } - } - contentRun = CreateRun(content, styleDisplayName); + // In this case, we should not generate the run or any before/after text for it. + return propFrag; } + // Create a run with the correct style. + var writer = CreateWriter(propFrag); + ((WordFragmentWriter)writer).AddRun(Cache, config, propTable, writingSystem, true); + + // Add the content to the run. + AddToRunContent(writer, content); + var currentRun = ((WordFragmentWriter)writer).WordFragment.GetLastRun(); + + // Get the run's styleDisplayName for use in before/after text runs. + if (currentRun.RunProperties != null) + styleDisplayName = currentRun.RunProperties.RunStyle?.Val; + // Add Before text. if (!string.IsNullOrEmpty(config.Before)) { var beforeRun = CreateBeforeAfterBetweenRun(config.Before, styleDisplayName); - propFrag.DocBody.Append(beforeRun); - } - - // Add the content. - if (contentRun != null) - { - propFrag.DocBody.Append(contentRun); + propFrag.DocBody.PrependChild(beforeRun); } // Add After text. diff --git a/Src/xWorks/LcmXhtmlGenerator.cs b/Src/xWorks/LcmXhtmlGenerator.cs index 841f51a381..fa15493e7e 100644 --- a/Src/xWorks/LcmXhtmlGenerator.cs +++ b/Src/xWorks/LcmXhtmlGenerator.cs @@ -1076,7 +1076,7 @@ public IFragment AddCollectionItem(ConfigurableDictionaryNode config, bool isBlo } } - public IFragment AddProperty(ConfigurableDictionaryNode config, string className, bool isBlockProperty, string content) + public IFragment AddProperty(ConfigurableDictionaryNode config, ReadOnlyPropertyTable propTable, string className, bool isBlockProperty, string content, string writingSystem) { var bldr = new StringBuilder(); var fragment = new StringFragment(bldr); diff --git a/Src/xWorks/WordStylesGenerator.cs b/Src/xWorks/WordStylesGenerator.cs index cf3579ad1a..5e7bd1f548 100644 --- a/Src/xWorks/WordStylesGenerator.cs +++ b/Src/xWorks/WordStylesGenerator.cs @@ -515,6 +515,13 @@ private static StyleRunProperties AddFontInfoWordStyles(BaseStyleInfo projectSty var fontName = wsFontInfo.m_fontName.ValueIsSet ? wsFontInfo.m_fontName.Value : defaultFontInfo.FontName.ValueIsSet ? defaultFontInfo.FontName.Value : null; + // If font is explicitly set in FLEx to "", this gets picked up as the fontname. + // In that case, we want to set fontNome to null in the word style so that it can be inherited from the WS. + if (fontName == "") + { + fontName = null; + } + // fontName still null means not set in Normal Style, then get default fonts from WritingSystems configuration. // Comparison, projectStyle.Name == "Normal", required to limit the font-family definition to the // empty span (ie span[lang="en"]{}. If not included, font-family will be added to many more spans. From 37e09bd50443081cc0538eb6ec83bce416766d19 Mon Sep 17 00:00:00 2001 From: "Ariel Ror." Date: Wed, 5 Feb 2025 10:24:32 -0500 Subject: [PATCH 008/123] Fix string null check in Word Generator (#258) --- Src/xWorks/LcmWordGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/xWorks/LcmWordGenerator.cs b/Src/xWorks/LcmWordGenerator.cs index c47ba49dc8..e8ccdb704a 100644 --- a/Src/xWorks/LcmWordGenerator.cs +++ b/Src/xWorks/LcmWordGenerator.cs @@ -1127,7 +1127,7 @@ public IFragment AddProperty(ConfigurableDictionaryNode config, ReadOnlyProperty Run contentRun = null; string styleDisplayName = null; - if (content == null) + if (string.IsNullOrEmpty(content)) { // In this case, we should not generate the run or any before/after text for it. return propFrag; From c6688e9d46d928adf9b13bd87cf26a15d10feca4 Mon Sep 17 00:00:00 2001 From: Jason Naylor Date: Mon, 3 Feb 2025 09:33:44 -0800 Subject: [PATCH 009/123] Bump version to 9.2.6 --- Src/MasterVersionInfo.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/MasterVersionInfo.txt b/Src/MasterVersionInfo.txt index 54a33adb9d..068145baa2 100644 --- a/Src/MasterVersionInfo.txt +++ b/Src/MasterVersionInfo.txt @@ -1,4 +1,4 @@ FWMAJOR=9 FWMINOR=2 -FWREVISION=5 +FWREVISION=6 FWBETAVERSION=Beta From 558bb68a742fe864f33f4af19e9a9215943ef84b Mon Sep 17 00:00:00 2001 From: Jason Naylor Date: Mon, 3 Feb 2025 09:34:52 -0800 Subject: [PATCH 010/123] Update fw-nunitreport-action to honor the github annotation limit --- .github/workflows/CI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 908be04e65..d1205d78a0 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -74,7 +74,7 @@ jobs: run: .\NUnitReport /a ^| tee-object -FilePath test-results.log - name: Report Test Results - uses: sillsdev/fw-nunitreport-action@v1.0.0 + uses: sillsdev/fw-nunitreport-action@v2.0.0 with: log-path: Build/test-results.log token: ${{ secrets.GITHUB_TOKEN }} From 175667038b2482c0658d553c897dbafb6baed790 Mon Sep 17 00:00:00 2001 From: Jason Naylor Date: Wed, 5 Feb 2025 08:57:23 -0800 Subject: [PATCH 011/123] Fix LT-22041: Handle null license in case of bad image metadata (#257) * This work also exposed incorrect logic for missing licenses on Webonary upload --- Src/xWorks/DictConfigModelExt.cs | 11 +++++++++-- Src/xWorks/UploadToWebonaryController.cs | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Src/xWorks/DictConfigModelExt.cs b/Src/xWorks/DictConfigModelExt.cs index e3aa2b8cf5..0dd45cc295 100644 --- a/Src/xWorks/DictConfigModelExt.cs +++ b/Src/xWorks/DictConfigModelExt.cs @@ -3,6 +3,7 @@ // (http://www.gnu.org/licenses/lgpl-2.1.html) using System; +using System.Linq; using SIL.Reporting; using SIL.Windows.Forms.ClearShare; @@ -30,8 +31,14 @@ public static string CopyrightAndLicense(this LCModel.ICmPicture picture) } // As of 2023.07, the only implementation that actually uses the language list is CustomLicense w/o custom text, // which our UI seems to prevent users from creating. - var license = metadata.License.GetMinimalFormForCredits(new[] { "en" }, out _); - return string.IsNullOrEmpty(license) ? metadata.ShortCopyrightNotice : string.Join(", ", metadata.ShortCopyrightNotice, license); + var license = metadata.License?.GetMinimalFormForCredits(new[] { "en" }, out _); + if (string.IsNullOrEmpty(metadata.CopyrightNotice) && string.IsNullOrEmpty(license)) + return null; + // We want the short copyright notice, but it isn't safe to ask for if CopyrightNotice is null + var copyright = string.IsNullOrEmpty(metadata.CopyrightNotice) + ? string.Empty + : metadata.ShortCopyrightNotice; + return string.Join(", ", new[] { copyright, license }.Where(txt => !string.IsNullOrEmpty(txt))); } private static Metadata MetadataFromFile(this LCModel.ICmPicture picture) diff --git a/Src/xWorks/UploadToWebonaryController.cs b/Src/xWorks/UploadToWebonaryController.cs index 597d60b0e1..580ef79052 100644 --- a/Src/xWorks/UploadToWebonaryController.cs +++ b/Src/xWorks/UploadToWebonaryController.cs @@ -565,7 +565,7 @@ private bool IsFileLicenseValidForUpload(string path) if (imageExtensions.Any(path.ToLowerInvariant().EndsWith)) { var metaData = Metadata.FromFile(path); - if (metaData == null || !metaData.IsMinimallyComplete || !metaData.IsLicenseNotSet) + if (metaData == null || !metaData.IsMinimallyComplete || metaData.IsLicenseNotSet) { return false; } From c267a0fb3f30c13e573107c3dea485b5c5ca43f6 Mon Sep 17 00:00:00 2001 From: "Ariel Ror." Date: Wed, 5 Feb 2025 19:57:29 -0500 Subject: [PATCH 012/123] LT-21818: Fix cutoff diacritics in Word Export (#259) Fix logic for determining at least and exact line spacing in the Word Export. --- Src/xWorks/WordStylesGenerator.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Src/xWorks/WordStylesGenerator.cs b/Src/xWorks/WordStylesGenerator.cs index 5e7bd1f548..97e6c298b6 100644 --- a/Src/xWorks/WordStylesGenerator.cs +++ b/Src/xWorks/WordStylesGenerator.cs @@ -280,16 +280,16 @@ internal static Style GenerateWordStyleFromLcmStyleSheet(string styleName, int w else { // Note: In Flex a user can set 'at least' or 'exactly' for line heights. These are differentiated using negative and positive - // values in LineSpacing.m_lineHeight -- negative value means at least line height, otherwise it's exactly line height + // values in LineSpacing.m_lineHeight -- positive value means at least line height, otherwise it's exact line height var lineHeight = exportStyleInfo.LineSpacing.m_lineHeight; - if (lineHeight < 0) + if (lineHeight >= 0) { - lineHeight = MilliPtToTwentiPt(Math.Abs(exportStyleInfo.LineSpacing.m_lineHeight)); + lineHeight = MilliPtToTwentiPt(lineHeight); parProps.Append(new SpacingBetweenLines() { Line = lineHeight.ToString(), LineRule = LineSpacingRuleValues.AtLeast }); } else { - lineHeight = MilliPtToTwentiPt(exportStyleInfo.LineSpacing.m_lineHeight); + lineHeight = MilliPtToTwentiPt(Math.Abs(lineHeight)); parProps.Append(new SpacingBetweenLines() { Line = lineHeight.ToString(), LineRule = LineSpacingRuleValues.Exact }); } } @@ -516,7 +516,7 @@ private static StyleRunProperties AddFontInfoWordStyles(BaseStyleInfo projectSty : defaultFontInfo.FontName.ValueIsSet ? defaultFontInfo.FontName.Value : null; // If font is explicitly set in FLEx to "", this gets picked up as the fontname. - // In that case, we want to set fontNome to null in the word style so that it can be inherited from the WS. + // In that case, we want to set fontName to null in the word style so that it can be inherited from the WS. if (fontName == "") { fontName = null; From 7e93595255038224404f5c05d42ae0601f4507ed Mon Sep 17 00:00:00 2001 From: Jake Oliver Date: Thu, 13 Feb 2025 21:38:18 -0500 Subject: [PATCH 013/123] LT-21053: Update homographs to match publication (#261) --- Src/xWorks/ConfiguredLcmGenerator.cs | 210 +++++++++++-------- Src/xWorks/DictionaryPublicationDecorator.cs | 87 ++++++++ 2 files changed, 206 insertions(+), 91 deletions(-) diff --git a/Src/xWorks/ConfiguredLcmGenerator.cs b/Src/xWorks/ConfiguredLcmGenerator.cs index de35ad4aa2..5d4ab62faf 100644 --- a/Src/xWorks/ConfiguredLcmGenerator.cs +++ b/Src/xWorks/ConfiguredLcmGenerator.cs @@ -29,6 +29,7 @@ using System.IO; using System.Linq; using System.Reflection; +using System.Runtime.Remoting.Messaging; using System.Text; using System.Text.RegularExpressions; using System.Threading; @@ -382,6 +383,34 @@ public static string GetClassNameAttributeForConfig(ConfigurableDictionaryNode c return classAtt; } + private static string PlainFieldName(string fieldname) + { + if (fieldname.EndsWith("OA") || fieldname.EndsWith("OS") || fieldname.EndsWith("OC") + || fieldname.EndsWith("RA") || fieldname.EndsWith("RS") || fieldname.EndsWith("RC")) + { + return fieldname.Substring(0, fieldname.Length - 2); + } + return fieldname; + } + + private static object GetValueFromMember(MemberInfo property, object instance) + { + switch (property.MemberType) + { + case MemberTypes.Property: + { + return ((PropertyInfo)property).GetValue(instance, new object[] { }); + } + case MemberTypes.Method: + { + // Execute the presumed extension method (passing the instance as the 'this' parameter) + return ((MethodInfo)property).Invoke(instance, new object[] { instance }); + } + default: + return null; + } + } + /// /// This method will use reflection to pull data out of the given object based on the given configuration and /// write out appropriate content using the settings parameter. @@ -429,7 +458,7 @@ internal static IFragment GenerateContentForFieldByReflection(object field, Conf { // REVIEW: We have overloaded terms here, this is a C# class not a css class, consider a different name var customFieldOwnerClassName = GetClassNameForCustomFieldParent(config, settings.Cache); - if (!GetPropValueForCustomField(field, config, cache, customFieldOwnerClassName, config.FieldDescription, ref propertyValue)) + if (!GetPropValueForCustomField(field, config, cache, publicationDecorator, customFieldOwnerClassName, config.FieldDescription, ref propertyValue)) return settings.ContentGenerator.CreateFragment(); } else @@ -454,7 +483,18 @@ internal static IFragment GenerateContentForFieldByReflection(object field, Conf #endif return settings.ContentGenerator.CreateFragment(); } - propertyValue = GetValueFromMember(property, field); + // This code demonstrates using the cache metadata, + // an alternative form of reflection to get values that respect the decorator + bool success = false; + if (field is ICmObject) + { + success = GetPropValueForCustomField(field, config, cache, publicationDecorator, + ((ICmObject)field).ClassName, PlainFieldName(property.Name), ref propertyValue); + } + + if (!success) + propertyValue = GetValueFromMember(property, field); + GetSortedReferencePropertyValue(config, ref propertyValue, field); } // If the property value is null there is nothing to generate @@ -467,7 +507,7 @@ internal static IFragment GenerateContentForFieldByReflection(object field, Conf if (config.IsCustomField) { // Get the custom field value (in SubField) using the property which came from the field object - if (!GetPropValueForCustomField(propertyValue, config, cache, ((ICmObject)propertyValue).ClassName, + if (!GetPropValueForCustomField(propertyValue, config, cache, publicationDecorator, ((ICmObject)propertyValue).ClassName, config.SubField, ref propertyValue)) { return settings.ContentGenerator.CreateFragment(); @@ -572,86 +612,92 @@ private static IFragment GenerateContentForGroupingNode(object field, Configurab /// true if the custom field was valid and false otherwise /// propertyValue can be null if the custom field is valid but no value is stored for the owning object private static bool GetPropValueForCustomField(object fieldOwner, ConfigurableDictionaryNode config, - LcmCache cache, string customFieldOwnerClassName, string customFieldName, ref object propertyValue) + LcmCache cache, ISilDataAccess decorator, string customFieldOwnerClassName, string customFieldName, ref object propertyValue) { + if (decorator == null) + decorator = cache.DomainDataByFlid; int customFieldFlid = GetCustomFieldFlid(config, cache, customFieldOwnerClassName, customFieldName); - if (customFieldFlid != 0) + if (customFieldFlid == 0) + return false; + + var customFieldType = cache.MetaDataCacheAccessor.GetFieldType(customFieldFlid); + ICmObject specificObject; + if (fieldOwner is ISenseOrEntry) { - var customFieldType = cache.MetaDataCacheAccessor.GetFieldType(customFieldFlid); - ICmObject specificObject; - if (fieldOwner is ISenseOrEntry) + specificObject = ((ISenseOrEntry)fieldOwner).Item; + if (!((IFwMetaDataCacheManaged)cache.MetaDataCacheAccessor).GetFields(specificObject.ClassID, + true, (int)CellarPropertyTypeFilter.All).Contains(customFieldFlid)) { - specificObject = ((ISenseOrEntry)fieldOwner).Item; - if (!((IFwMetaDataCacheManaged)cache.MetaDataCacheAccessor).GetFields(specificObject.ClassID, - true, (int)CellarPropertyTypeFilter.All).Contains(customFieldFlid)) - { - return false; - } - } - else - { - specificObject = (ICmObject)fieldOwner; + return false; } + } + else + { + specificObject = (ICmObject)fieldOwner; + } - switch (customFieldType) - { - case (int)CellarPropertyType.ReferenceCollection: - case (int)CellarPropertyType.OwningCollection: - // Collections are stored essentially the same as sequences. - case (int)CellarPropertyType.ReferenceSequence: - case (int)CellarPropertyType.OwningSequence: - { - var sda = cache.MainCacheAccessor; - // This method returns the hvo of the object pointed to - var chvo = sda.get_VecSize(specificObject.Hvo, customFieldFlid); - int[] contents; - using (var arrayPtr = MarshalEx.ArrayToNative(chvo)) - { - sda.VecProp(specificObject.Hvo, customFieldFlid, chvo, out chvo, arrayPtr); - contents = MarshalEx.NativeToArray(arrayPtr, chvo); - } - // if the hvo is invalid set propertyValue to null otherwise get the object - propertyValue = contents.Select(id => cache.LangProject.Services.GetObject(id)); - break; - } - case (int)CellarPropertyType.ReferenceAtomic: - case (int)CellarPropertyType.OwningAtomic: + switch (customFieldType) + { + case (int)CellarPropertyType.ReferenceCollection: + case (int)CellarPropertyType.OwningCollection: + // Collections are stored essentially the same as sequences. + case (int)CellarPropertyType.ReferenceSequence: + case (int)CellarPropertyType.OwningSequence: + { + var sda = cache.MainCacheAccessor; + // This method returns the hvo of the object pointed to + var chvo = sda.get_VecSize(specificObject.Hvo, customFieldFlid); + int[] contents; + using (var arrayPtr = MarshalEx.ArrayToNative(chvo)) { - // This method returns the hvo of the object pointed to - propertyValue = cache.MainCacheAccessor.get_ObjectProp(specificObject.Hvo, customFieldFlid); - // if the hvo is invalid set propertyValue to null otherwise get the object - propertyValue = (int)propertyValue > 0 ? cache.LangProject.Services.GetObject((int)propertyValue) : null; - break; - } - case (int)CellarPropertyType.GenDate: - { - propertyValue = new GenDate(cache.MainCacheAccessor.get_IntProp(specificObject.Hvo, customFieldFlid)); - break; + sda.VecProp(specificObject.Hvo, customFieldFlid, chvo, out chvo, arrayPtr); + contents = MarshalEx.NativeToArray(arrayPtr, chvo); } + // Convert the contents to IEnumerable + var objects = contents.Select(id => cache.LangProject.Services.GetObject(id)); + var type = objects.FirstOrDefault()?.GetType() ?? typeof(object); + var castMethod = typeof(Enumerable).GetMethod("Cast").MakeGenericMethod(type); + propertyValue = castMethod.Invoke(null, new object[] { objects }); + break; + } + case (int)CellarPropertyType.ReferenceAtomic: + case (int)CellarPropertyType.OwningAtomic: + { + // This method returns the hvo of the object pointed to + propertyValue = decorator.get_ObjectProp(specificObject.Hvo, customFieldFlid); + // if the hvo is invalid set propertyValue to null otherwise get the object + propertyValue = (int)propertyValue > 0 ? cache.LangProject.Services.GetObject((int)propertyValue) : null; + break; + } + case (int)CellarPropertyType.GenDate: + { + propertyValue = new GenDate(decorator.get_IntProp(specificObject.Hvo, customFieldFlid)); + break; + } - case (int)CellarPropertyType.Time: - { - propertyValue = SilTime.ConvertFromSilTime(cache.MainCacheAccessor.get_TimeProp(specificObject.Hvo, customFieldFlid)); - break; - } - case (int)CellarPropertyType.MultiUnicode: - case (int)CellarPropertyType.MultiString: - { - propertyValue = cache.MainCacheAccessor.get_MultiStringProp(specificObject.Hvo, customFieldFlid); - break; - } - case (int)CellarPropertyType.String: - { - propertyValue = cache.MainCacheAccessor.get_StringProp(specificObject.Hvo, customFieldFlid); - break; - } - case (int)CellarPropertyType.Integer: - { - propertyValue = cache.MainCacheAccessor.get_IntProp(specificObject.Hvo, customFieldFlid); - break; - } - } + case (int)CellarPropertyType.Time: + { + propertyValue = SilTime.ConvertFromSilTime(decorator.get_TimeProp(specificObject.Hvo, customFieldFlid)); + break; + } + case (int)CellarPropertyType.MultiUnicode: + case (int)CellarPropertyType.MultiString: + { + propertyValue = decorator.get_MultiStringProp(specificObject.Hvo, customFieldFlid); + break; + } + case (int)CellarPropertyType.String: + { + propertyValue = decorator.get_StringProp(specificObject.Hvo, customFieldFlid); + break; + } + case (int)CellarPropertyType.Integer: + { + propertyValue = decorator.get_IntProp(specificObject.Hvo, customFieldFlid); + break; + } } + return true; } @@ -1219,24 +1265,6 @@ private static Type GetTypeFromMember(MemberInfo property) } } - private static object GetValueFromMember(MemberInfo property, object instance) - { - switch (property.MemberType) - { - case MemberTypes.Property: - { - return ((PropertyInfo)property).GetValue(instance, new object[] {}); - } - case MemberTypes.Method: - { - // Execute the presumed extension method (passing the instance as the 'this' parameter) - return ((MethodInfo)property).Invoke(instance, new object[] {instance}); - } - default: - return null; - } - } - private static Type GetCustomFieldType(Type lookupType, ConfigurableDictionaryNode config, LcmCache cache) { // FDO doesn't work with interfaces, just concrete classes so chop the I off any interface types diff --git a/Src/xWorks/DictionaryPublicationDecorator.cs b/Src/xWorks/DictionaryPublicationDecorator.cs index 69988b8821..06b843b311 100644 --- a/Src/xWorks/DictionaryPublicationDecorator.cs +++ b/Src/xWorks/DictionaryPublicationDecorator.cs @@ -244,6 +244,15 @@ private string GetSenseNumber(ILexSense sense) return Cache.GetOutlineNumber(sense, LexSenseTags.kflidSenses, false, true, this); } + public override ITsMultiString get_MultiStringProp(int hvo, int tag) + { + if (tag == m_mlHeadwordFlid) + { + return new PublicationAwareMultiStringAccessor(hvo, tag, this); + } + return base.get_MultiStringProp(hvo, tag); + } + public override ITsString get_MultiStringAlt(int hvo, int tag, int ws) { if (tag == m_mlHeadwordFlid) @@ -615,5 +624,83 @@ private bool IsPublishableReference(ILexEntryRef entryRef) // A reference is also not publishable if all of its PrimarySensesOrEntries are excluded return entryRef.PrimarySensesOrEntries.Any(senseOrEntry => !m_excludedItems.Contains(senseOrEntry.Item.Hvo)); } + + private class PublicationAwareMultiStringAccessor : IMultiAccessorBase + { + private readonly int m_hvo; + private readonly int m_tag; + private readonly DictionaryPublicationDecorator m_decorator; + + public PublicationAwareMultiStringAccessor(int hvo, int tag, DictionaryPublicationDecorator decorator) + { + m_hvo = hvo; + m_tag = tag; + m_decorator = decorator; + } + + public ITsString GetStringFromIndex(int iws, out int _ws) + { + throw new NotImplementedException(); + } + + public ITsString get_String(int ws) + { + return m_decorator.get_MultiStringAlt(m_hvo, m_tag, ws); + } + + public void set_String(int ws, ITsString _tss) + { + throw new NotImplementedException(); + } + + public int StringCount { get; } + public void SetAnalysisDefaultWritingSystem(string val) + { + throw new NotImplementedException(); + } + + public void SetVernacularDefaultWritingSystem(string val) + { + throw new NotImplementedException(); + } + + public void SetUserWritingSystem(string val) + { + throw new NotImplementedException(); + } + + public void set_String(int ws, string val) + { + throw new NotImplementedException(); + } + + public bool TryWs(int ws, out int actualWs) + { + throw new NotImplementedException(); + } + + public bool TryWs(int ws, out int actualWs, out ITsString tssActual) + { + throw new NotImplementedException(); + } + + public ITsString StringOrNull(int ws) + { + throw new NotImplementedException(); + } + + public int Flid { get; } + public ITsString NotFoundTss { get; } + public ITsString AnalysisDefaultWritingSystem { get; set; } + public ITsString VernacularDefaultWritingSystem { get; set; } + public string UiString { get; } + public ITsString UserDefaultWritingSystem { get; set; } + public ITsString RawUserDefaultWritingSystem { get; } + public ITsString BestAnalysisVernacularAlternative { get; } + public ITsString BestAnalysisAlternative { get; } + public ITsString BestVernacularAlternative { get; } + public ITsString BestVernacularAnalysisAlternative { get; } + public int[] AvailableWritingSystemIds { get; } + } } } From fb47694fcfff8b414cac6807455ede42c7c634ab Mon Sep 17 00:00:00 2001 From: Mark Kidder <83427558+mark-sil@users.noreply.github.com> Date: Tue, 18 Feb 2025 08:15:54 -0500 Subject: [PATCH 014/123] LT-22042: Fix incorrect styles being used in Flex (#263) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Keep the node path back to the root in the css file. This is needed to ensure that styles are applied from the most specific (leaf node) to the lease specific (root node). - All node names now have a unique identifier. This is to prevent the wrong style from being used when nodes have the same display name (ie. Headword) but are located at different places in the tree. Without the unique identifier a node without a style could end up using a style from a different node with the same display name. With unique names, a node without a style will look to its parent node for style information, and then it’s grandparent node, … up to the root. - The change in RemoveBeforeAndAfterForNoteInParaRules(): Changed the method so that we keep non-before/after rules. This fixes a problem with notes that should be in their own paragraph, not displaying correctly in Flex. Additional cleanup still needed for the .css files (these issues existed before this PR): - We are currently creating multiple css entries with the same fields but different values. In this case we always use the values from the last entry in the file. We need to figure out why we are doing this and either support the intended behavior in a different way or remove the duplicates. Example: ‘.subentries .subentry{}’ is written multiple times with the same 12 fields. The only field that ever changes value is ‘margin-left’. - Sometimes we are removing the last letter from entry names in the css file for ‘between’ content. Need to investigate if this currently works to provide ‘between’ content. Example: .headword> .headwor+ .headwor:before{ content:', ';} --- Src/xWorks/CssGenerator.cs | 184 +++--- Src/xWorks/DictionaryConfigurationDlg.cs | 2 +- .../ConfiguredLcmUsfmGeneratorTests.cs | 2 +- .../ConfiguredXHTMLGeneratorReversalTests.cs | 52 +- .../ConfiguredXHTMLGeneratorTests.cs | 575 +++++++++--------- Src/xWorks/xWorksTests/CssGeneratorTests.cs | 52 +- .../xWorksTests/LcmJsonGeneratorTests.cs | 118 ++-- 7 files changed, 511 insertions(+), 474 deletions(-) diff --git a/Src/xWorks/CssGenerator.cs b/Src/xWorks/CssGenerator.cs index 647e308dd5..77d7db96a1 100644 --- a/Src/xWorks/CssGenerator.cs +++ b/Src/xWorks/CssGenerator.cs @@ -58,6 +58,7 @@ public class CssGenerator : ILcmStylesGenerator private LcmCache _cache; private ReadOnlyPropertyTable _propertyTable; private Dictionary> _styleDictionary = new Dictionary>(); + private Dictionary _uniqueNodeNames = new Dictionary(); private StyleSheet _styleSheet = new StyleSheet(); public void Init(ReadOnlyPropertyTable propertyTable) @@ -83,61 +84,125 @@ public string AddStyles(ConfigurableDictionaryNode node) var className = $".{GetClassAttributeForConfig(node)}"; lock (_styleDictionary) { - var styleContent = GenerateCssFromConfigurationNode(node, className, _propertyTable).NonEmpty(); - if (!styleContent.Any()) + // Create a list of nodes for the path, from root to 'node'. + List nodes = new List(); + var pathNode = node; + while (pathNode != null) { - return className; + nodes.Add(pathNode); + pathNode = pathNode.Parent; } - if (!_styleDictionary.ContainsKey(className)) - { - _styleDictionary[className] = styleContent; - return className; - } - // If the content is the same, then do nothing - if (AreStyleRulesListsEquivalent(_styleDictionary[className], styleContent)) + nodes.Reverse(); + + // Generate the unique name and css for each node (starting from the root node). + string uniqueNodeName = null; + string uniqueNodePath = null; + for (int ii=0; ii < nodes.Count; ii++) { - return className; + var workingNode = nodes[ii]; + string nodePath = GetNodePath(workingNode); + var workingClassName = $".{GetClassAttributeForConfig(workingNode)}"; + + // If this node is ".subentries" and the next node is ".mainentrysubentries", + // then set the uniqueNodeName to ".mainentrysubentry" and skip the next node. + if (workingClassName == ".subentries" && (ii + 1 < nodes.Count) && + $".{GetClassAttributeForConfig(nodes[ii + 1])}" == ".mainentrysubentries") + { + // We need a specificity higher than ".senses-? .subentries-? .subentry" and + // because of the way the path to ".mainentrysubentries" is generated we can + // get the wrong value for ".sense-?. So add ".sensecontent .sense" to the path. + uniqueNodePath = uniqueNodePath + ".sensecontent .sense "; + uniqueNodeName = ".mainentrysubentry"; + ii++; + } + else + { + uniqueNodeName = GetUniqueNodeName(nodePath, workingClassName); + } + + if (!_styleDictionary.ContainsKey(uniqueNodeName)) + { + var styleRules = GenerateCssFromConfigurationNode(workingNode, uniqueNodeName, _propertyTable).NonEmpty(); + styleRules = styleRules.Distinct().ToList(); // Remove duplicate rules. + AddUniquePathToStyleRules(styleRules, uniqueNodePath); + _styleDictionary[uniqueNodeName] = styleRules; + } + uniqueNodePath = uniqueNodePath + uniqueNodeName + " "; } - // Otherwise get a unique but useful class name and re-generate the style with the new name - className = GetBestUniqueNameForNode(node); - return className; + return uniqueNodeName; } } - public static bool AreStyleRulesListsEquivalent(List first, - List second) + /// + /// Get a path containing Non-Unique names for all the nodes from the root up to and + /// including the 'node' passed in. + /// + private string GetNodePath(ConfigurableDictionaryNode node) { - return first.Count == second.Count && first.TrueForAll(rule => second.Any(otherrule => otherrule.ToString().Equals(rule.ToString()))); + // Generate the node path info from the root to this node. + string pathToNode = null; + var workingNode = node; + while (workingNode != null) + { + string workingClassName = $".{GetClassAttributeForConfig(workingNode)} "; + pathToNode = workingClassName + pathToNode; + workingNode = workingNode.Parent; + } + return pathToNode; } /// - /// Finds an unused class name for the configuration node. This should be called when there are two nodes in the DictionaryConfigurationModel - /// have the same class name, but different style content. We want this name to be usefully recognizable. + /// To avoid problems with one node using the style assigned to a different node with the same + /// name, assign a unique name to every node. /// + /// A path containing Non-Unique names for all the nodes. + /// The name without an appended unique number. /// - public string GetBestUniqueNameForNode(ConfigurableDictionaryNode node) + private string GetUniqueNodeName(string nodePath, string className) { - Guard.AgainstNull(node.Parent, "There should not be duplicate class names at the top of tree."); - // First try appending the parent node classname. Pathway has code that cares about what - // the className starts with, so keep the 'node' name first. - var className = $".{GetClassAttributeForConfig(node)}-{GetClassAttributeForConfig(node.Parent)}"; + if (_uniqueNodeNames.ContainsKey(nodePath)) + { + return _uniqueNodeNames[nodePath]; + } - string classNameBase = className; int counter = 0; - lock (_styleDictionary) + string uniqueNodeName; + do + { + uniqueNodeName = $"{className}-{++counter}"; + } while (_styleDictionary.ContainsKey(uniqueNodeName)); + + _uniqueNodeNames[nodePath] = uniqueNodeName; + return uniqueNodeName; + } + + /// + /// To avoid problems with the wrong style being used, add the unique path to the style + /// rules. This increases specificity. + /// + /// The rules to be pre-pended with the uniquePath. + /// A path containing Unique names for all the nodes. + private void AddUniquePathToStyleRules(List styleRules, string uniquePath) + { + if (!string.IsNullOrEmpty(uniquePath)) { - while (_styleDictionary.ContainsKey(className)) + foreach (var styleRule in styleRules) { - var styleContent = GenerateCssFromConfigurationNode(node, className, _propertyTable).NonEmpty(); - if (AreStyleRulesListsEquivalent(_styleDictionary[className], styleContent)) + string existingRule = styleRule.Value; + // If the styleRule already contains the last node on the uniquePath, then don't add the node again. + int indexSpace = styleRule.Value.IndexOf(' '); + if (indexSpace != -1) { - return className; + string ruleFirstNode = styleRule.Value.Substring(0, indexSpace + 1 /*intentionally include the space*/); + if (uniquePath.EndsWith(ruleFirstNode)) + { + existingRule = styleRule.Value.Substring(indexSpace + 1 /*intentionally exclude the space*/); + } } - className = $"{classNameBase}-{++counter}"; + + styleRule.Value = uniquePath + existingRule; } - _styleDictionary[className] = GenerateCssFromConfigurationNode(node, className, _propertyTable).NonEmpty(); } - return className; } public string GetStylesString() @@ -200,17 +265,11 @@ private static List GenerateCssForDefaultStyles(ReadOnlyPropertyTable if (propStyleSheet.Styles.Contains("Normal")) styles.AddRange(GenerateCssForWsSpanWithNormalStyle(propertyTable)); - var entryBaseStyle = ConfiguredLcmGenerator.GetEntryStyle(model); - if (propStyleSheet.Styles.Contains(entryBaseStyle)) - styles.AddRange(GenerateDictionaryNormalParagraphCss(propertyTable, entryBaseStyle)); - if (propStyleSheet.Styles.Contains(LetterHeadingStyleName)) { styles.AddRange(GenerateCssForWritingSystems(".letter", LetterHeadingStyleName, propertyTable)); } - styles.AddRange(GenerateDictionaryMinorParagraphCss(propertyTable, model)); - return styles; } @@ -257,42 +316,6 @@ private static List GenerateCssForWsSpanWithNormalStyle(ReadOnlyPrope return styles; } - private static List GenerateDictionaryNormalParagraphCss(ReadOnlyPropertyTable propertyTable, string entryBaseStyle) - { - var styles = new List(); - var dictNormalRule = new StyleRule { Value = "div.entry" }; - var dictNormalStyle = GenerateCssStyleFromLcmStyleSheet(entryBaseStyle, 0, propertyTable); - dictNormalRule.Declarations.Properties.AddRange(GetOnlyParagraphStyle(dictNormalStyle)); - styles.Add(dictNormalRule); - // Then generate the rules for all the writing system overrides - styles.AddRange(GenerateCssForWritingSystems("div.entry span", entryBaseStyle, propertyTable)); - return styles; - } - - private static List GenerateDictionaryMinorParagraphCss(ReadOnlyPropertyTable propertyTable, DictionaryConfigurationModel model) - { - var styles = new List(); - // Use the style set in all the parts following main entry, if no style is specified assume Dictionary-Minor - for (var i = 1; i < model.Parts.Count; ++i) - { - var minorEntryNode = model.Parts[i]; - if (minorEntryNode.IsEnabled) - { - var styleName = minorEntryNode.Style; - if (string.IsNullOrEmpty(styleName)) - styleName = DictionaryMinor; - var dictionaryMinorStyle = GenerateCssStyleFromLcmStyleSheet(styleName, 0, propertyTable); - var minorRule = new StyleRule { Value = string.Format("div.{0}", GetClassAttributeForConfig(minorEntryNode)) }; - minorRule.Declarations.Properties.AddRange(GetOnlyParagraphStyle(dictionaryMinorStyle)); - styles.Add(minorRule); - // Then generate the rules for all the writing system overrides - styles.AddRange(GenerateCssForWritingSystems(string.Format("div.{0} span", GetClassAttributeForConfig(minorEntryNode)), styleName, propertyTable)); - } - } - - return styles; - } - private static List GenerateCssForWritingSystems(string selector, string styleName, ReadOnlyPropertyTable propertyTable) { var cache = propertyTable.GetValue("cache"); @@ -421,6 +444,7 @@ private static IEnumerable RemoveBeforeAfterSelectorRules(IEnumerable private static List GenerateCssForSenses(ConfigurableDictionaryNode configNode, DictionaryNodeSenseOptions senseOptions, ref string baseSelection, ReadOnlyPropertyTable propertyTable) { + string baseSelectionOrig = baseSelection; var styleRules = new List(); var selectors = GenerateSelectorsFromNode(configNode, ref baseSelection, propertyTable.GetValue("cache"), propertyTable); // Insert '> .sensecontent' between '.*senses' and '.*sense' (where * could be 'referring', 'sub', or similar) @@ -501,6 +525,13 @@ private static List GenerateCssForSenses(ConfigurableDictionaryNode c if (!IsEmptyRule(senseContentRule)) styleRules.Add(senseContentRule); } + + // Add the ws specific styles. + if (!string.IsNullOrEmpty(configNode.Style)) + { + styleRules.AddRange(GenerateCssForWritingSystems(baseSelectionOrig + " span", configNode.Style, propertyTable)); + } + return styleRules; } @@ -607,7 +638,8 @@ private static List GenerateCssFromListAndParaOptions(ConfigurableDic private static IEnumerable RemoveBeforeAndAfterForNoteInParaRules(IEnumerable rules) { - return rules.Where(rule => rule.Value.Contains("~")); + // Return non-before/after rules and before/after rules that contains a '~'. + return rules.Where(rule => (!IsBeforeOrAfter(rule) || rule.Value.Contains("~"))); } /// diff --git a/Src/xWorks/DictionaryConfigurationDlg.cs b/Src/xWorks/DictionaryConfigurationDlg.cs index b0a8ce2603..3d70b48b70 100644 --- a/Src/xWorks/DictionaryConfigurationDlg.cs +++ b/Src/xWorks/DictionaryConfigurationDlg.cs @@ -214,7 +214,7 @@ private static List FindConfiguredItem(ConfigurableDictionaryNode var topLevelClass = CssGenerator.GetClassAttributeForConfig(topLevelConfigNode); foreach (var div in body.GetElementsByTagName("div")) { - if (Equals(div.ParentElement, body) && div.GetAttribute("class") == topLevelClass) + if (Equals(div.ParentElement, body) && div.GetAttribute("class").StartsWith(topLevelClass)) elements.AddRange(FindMatchingSpans(selectedConfigNode, div, topLevelConfigNode, cache)); } return elements; diff --git a/Src/xWorks/xWorksTests/ConfiguredLcmUsfmGeneratorTests.cs b/Src/xWorks/xWorksTests/ConfiguredLcmUsfmGeneratorTests.cs index ab07598513..d8fa38954a 100644 --- a/Src/xWorks/xWorksTests/ConfiguredLcmUsfmGeneratorTests.cs +++ b/Src/xWorks/xWorksTests/ConfiguredLcmUsfmGeneratorTests.cs @@ -25,7 +25,7 @@ namespace SIL.FieldWorks.XWorks [TestFixture] public class ConfiguredLcmUsfmGeneratorTests : MemoryOnlyBackendProviderRestoredForEachTestTestBase, IDisposable { - private const string XPathToUSFMField = "/div[@class='lexentry']/span[@class='usfm-field']"; + private const string XPathToUSFMField = "/div[@class='lexentry-1']/span[@class='usfm-field-1']"; private const string XPathToTitle = XPathToUSFMField + "/table/caption/span"; private const string XPathToRow = XPathToUSFMField + "/table/tbody/tr"; private const string XPathToCell = XPathToRow + "/td/span"; diff --git a/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorReversalTests.cs b/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorReversalTests.cs index e43d64b0c4..f4ed244516 100644 --- a/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorReversalTests.cs +++ b/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorReversalTests.cs @@ -120,21 +120,21 @@ public void GenerateXHTMLForEntry_LexemeFormConfigurationGeneratesCorrectResult( var entry = CreateInterestingEnglishReversalEntry(); //SUT string result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings).ToString(); - const string frenchLexForm = "/div[@class='reversalindexentry']/span[@class='reversalform']/span[@lang='en' and text()='ReversalForm']"; + const string frenchLexForm = "/div[@class='reversalindexentry-1']/span[@class='reversalform-1']/span[@lang='en' and text()='ReversalForm']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(frenchLexForm, 1); } #region PrimareyEntryReferenceTests // Xpath used by PrimaryEntryReference tests - private const string senseXpath = "/div[@class='reversalindexentry']/span[@class='sensesrs']/span[@class='sensecontent']/span[@class='sensesr']"; - private const string entryRefsXpath = senseXpath + "/span[@class='mainentryrefs']"; + private const string senseXpath = "/div[@class='reversalindexentry-1']/span[@class='sensesrs-1']/span[@class='sensecontent']/span[@class='sensesr']"; + private const string entryRefsXpath = senseXpath + "/span[@class='mainentryrefs-1']"; private const string entryRefXpath = entryRefsXpath + "/span[@class='mainentryref']"; - private const string entryRefTypeBit = "span[@class='entrytypes']/span[@class='entrytype']"; + private const string entryRefTypeBit = "span[@class='entrytypes-1']/span[@class='entrytype']"; private const string entryRefTypeXpath = entryRefsXpath + "/" + entryRefTypeBit; - private const string primaryLexemeBit = "/span[@class='primarylexemes']/span[@class='primarylexeme']"; + private const string primaryLexemeBit = "/span[@class='primarylexemes-1']/span[@class='primarylexeme']"; private const string primaryEntryXpath = entryRefXpath + primaryLexemeBit; //private const string primaryEntryXpath = entryRefXpath + "/span[@class='primarylexemes']/span[@class='primarylexeme']"; - private const string refHeadwordXpath = primaryEntryXpath + "/span[@class='headword']/span[@lang='fr']/a[text()='parole']"; + private const string refHeadwordXpath = primaryEntryXpath + "/span[@class='headword-2']/span[@lang='fr']/a[text()='parole']"; [Test] public void GenerateXHTMLForEntry_PrimaryEntryReferencesWork_ComplexFormOfEntry() @@ -147,12 +147,12 @@ public void GenerateXHTMLForEntry_PrimaryEntryReferencesWork_ComplexFormOfEntry( CXGTests.CreateComplexForm(Cache, paroleEntry, sense.Owner as ILexEntry, true); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(reversalEntry, mainRevEntryNode, null, DefaultSettings).ToString(); - const string headwordXpath = senseXpath + "/span[@class='headword']/span[@lang='fr']//a[text()='porte-parole']"; + const string headwordXpath = senseXpath + "/span[@class='headword-1']/span[@lang='fr']//a[text()='porte-parole']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(headwordXpath, 1); - const string refTypeXpath = entryRefTypeXpath + "/span[@class='abbreviation']/span[@lang='en' and text()='comp. of']"; + const string refTypeXpath = entryRefTypeXpath + "/span[@class='abbreviation-1']/span[@lang='en' and text()='comp. of']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(refTypeXpath, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(refHeadwordXpath, 1); - const string glossOrSummDefXpath = primaryEntryXpath + "/span[@class='glossorsummary']/span[@lang='en' and text()='summDefn']"; + const string glossOrSummDefXpath = primaryEntryXpath + "/span[@class='glossorsummary-1']/span[@lang='en' and text()='summDefn']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(glossOrSummDefXpath, 1); } @@ -166,10 +166,10 @@ public void GenerateXHTMLForEntry_PrimaryEntryReferencesWork_ComplexFormOfSense( CXGTests.CreateComplexForm(Cache, paroleEntry.SensesOS[0], sense.Owner as ILexEntry, true); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(reversalEntry, mainRevEntryNode, null, DefaultSettings).ToString(); - const string refTypeXpath = entryRefTypeXpath + "/span[@class='abbreviation']/span[@lang='en' and text()='comp. of']"; + const string refTypeXpath = entryRefTypeXpath + "/span[@class='abbreviation-1']/span[@lang='en' and text()='comp. of']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(refTypeXpath, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(refHeadwordXpath, 1); - const string glossOrSummDefXpath = primaryEntryXpath + "/span[@class='glossorsummary']/span[@lang='en' and text()='speech']"; + const string glossOrSummDefXpath = primaryEntryXpath + "/span[@class='glossorsummary-1']/span[@lang='en' and text()='speech']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(glossOrSummDefXpath, 1); } @@ -183,10 +183,10 @@ public void GenerateXHTMLForEntry_PrimaryEntryReferencesWork_VariantFormOfSense( CXGTests.CreateVariantForm(Cache, paroleEntry.SensesOS[0], variantEntry, "Spelling Variant"); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(reversalEntry, mainRevEntryNode, null, DefaultSettings).ToString(); - const string refTypeXpath = entryRefTypeXpath + "/span[@class='abbreviation']/span[@lang='en' and text()='sp. var. of']"; + const string refTypeXpath = entryRefTypeXpath + "/span[@class='abbreviation-1']/span[@lang='en' and text()='sp. var. of']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(refTypeXpath, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(refHeadwordXpath, 1); - const string glossOrSummDefXpath = primaryEntryXpath + "/span[@class='glossorsummary']/span[@lang='en' and text()='speech']"; + const string glossOrSummDefXpath = primaryEntryXpath + "/span[@class='glossorsummary-1']/span[@lang='en' and text()='speech']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(glossOrSummDefXpath, 1); } @@ -201,10 +201,10 @@ public void GenerateXHTMLForEntry_PrimaryEntryReferencesWork_VariantFormOfEntry( CXGTests.CreateVariantForm(Cache, paroleEntry, variantEntry, "Spelling Variant"); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(reversalEntry, mainRevEntryNode, null, DefaultSettings).ToString(); - const string refTypeXpath = entryRefTypeXpath + "/span[@class='abbreviation']/span[@lang='en' and text()='sp. var. of']"; + const string refTypeXpath = entryRefTypeXpath + "/span[@class='abbreviation-1']/span[@lang='en' and text()='sp. var. of']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(refTypeXpath, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(refHeadwordXpath, 1); - const string glossOrSummDefXpath = primaryEntryXpath + "/span[@class='glossorsummary']/span[@lang='en' and text()='summDefn']"; + const string glossOrSummDefXpath = primaryEntryXpath + "/span[@class='glossorsummary-1']/span[@lang='en' and text()='summDefn']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(glossOrSummDefXpath, 1); } @@ -229,10 +229,10 @@ public void GenerateXHTMLForEntry_PrimaryEntryReferences_Ordered() var result = ConfiguredLcmGenerator.GenerateContentForEntry(reversalEntry, mainRevEntryNode, null, DefaultSettings).ToString(); // SUT var assertIt = AssertThatXmlIn.String(result); assertIt.HasSpecifiedNumberOfMatchesForXpath(entryRefTypeXpath, 3); // should be one Complex Form Type and two Variant Types. - const string headwordBit = "/span[@class='headword']/span[@lang='fr']/a[text()='{1}']"; + const string headwordBit = "/span[@class='headword-2']/span[@lang='fr']/a[text()='{1}']"; const string entryRefWithSiblingXpath = entryRefsXpath + "/span[@class='mainentryref' and preceding-sibling::"; const string typeAndHeadwordXpath = entryRefWithSiblingXpath - + entryRefTypeBit + "/span[@class='abbreviation']/span[@lang='en' and text()='{0}']]" + primaryLexemeBit + headwordBit; + + entryRefTypeBit + "/span[@class='abbreviation-1']/span[@lang='en' and text()='{0}']]" + primaryLexemeBit + headwordBit; var adjacentHeadwordXpath = entryRefWithSiblingXpath + "span[@class='mainentryref']" + primaryLexemeBit + headwordBit.Replace("{1}", "{0}") + "]" + primaryLexemeBit + headwordBit; // check for proper headings on each referenced headword @@ -402,7 +402,7 @@ public void GenerateXHTMLForEntry_ReversalStringGeneratesContent() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(rie, reversalNode, null, DefaultSettings).ToString(); - var reversalFormDataPath = string.Format("/div[@class='reversalindexentry']/span[@class='reversalform']/span[text()='{0}']", + var reversalFormDataPath = string.Format("/div[@class='reversalindexentry-1']/span[@class='reversalform-1']/span[text()='{0}']", TsStringUtils.Compose(rie.LongName)); var entryDataPath = string.Format("//span[text()='{0}']", entryHeadWord.get_NormalizedForm(FwNormalizationMode.knmNFC).Text); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(reversalFormDataPath, 1); @@ -462,14 +462,14 @@ public void GenerateXHTMLForEntry_SenseNumbersGeneratedForMultipleReferencedSens AddSenseToReversaEntry(testEntry, "second gloss", m_wsEn, Cache); //SUT var xhtml = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, DefaultSettings).ToString(); - const string senseNumberOne = "/div[@class='reversalindexentry']/span[@class='sensesrs']/span[@class='sensecontent']/span[@class='sensesr' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='gloss']"; - const string senseNumberTwo = "/div[@class='reversalindexentry']/span[@class='sensesrs']/span[@class='sensecontent']/span[@class='sensesr' and preceding-sibling::span[@class='sensenumber' and text()='2']]//span[@lang='en' and text()='second gloss']"; + const string senseNumberOne = "/div[@class='reversalindexentry-1']/span[@class='sensesrs-1']/span[@class='sensecontent']/span[@class='sensesr' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='gloss']"; + const string senseNumberTwo = "/div[@class='reversalindexentry-1']/span[@class='sensesrs-1']/span[@class='sensecontent']/span[@class='sensesr' and preceding-sibling::span[@class='sensenumber' and text()='2']]//span[@lang='en' and text()='second gloss']"; //This assert is dependent on the specific entry data created in CreateInterestingEnglishReversalEntry AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(senseNumberOne, 1); AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(senseNumberTwo, 1); - const string headwordOne = "/div[@class='reversalindexentry']/span[@class='sensesrs']/span[@class='sensecontent']/span[@class='sensesr']/span[@class='headword']/span[@lang='fr' and child::span[@lang='fr']/a[text()='1']]/span[@lang='fr' and a[text()='Citation']]"; - const string headwordTwo = "/div[@class='reversalindexentry']/span[@class='sensesrs']/span[@class='sensecontent']/span[@class='sensesr']/span[@class='headword']/span[@lang='fr' and child::span[@lang='fr']/a[text()='2']]/span[@lang='fr' and a[text()='Citation']]"; + const string headwordOne = "/div[@class='reversalindexentry-1']/span[@class='sensesrs-1']/span[@class='sensecontent']/span[@class='sensesr']/span[@class='headword-1']/span[@lang='fr' and child::span[@lang='fr']/a[text()='1']]/span[@lang='fr' and a[text()='Citation']]"; + const string headwordTwo = "/div[@class='reversalindexentry-1']/span[@class='sensesrs-1']/span[@class='sensecontent']/span[@class='sensesr']/span[@class='headword-1']/span[@lang='fr' and child::span[@lang='fr']/a[text()='2']]/span[@lang='fr' and a[text()='Citation']]"; AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(headwordOne, 1); AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(headwordTwo, 1); } @@ -525,7 +525,7 @@ public void GenerateXHTMLForEntry_VernacularFormWithSubSenses() //SUT var xhtml = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, DefaultSettings).ToString(); // REVIEW (Hasso) 2016.03: we should probably do something about the leading space in the Sense Number Run, as it is currently in addition to the "between" space. - const string subSenseOneOne = "/div[@class='reversalindexentry']/span[@class='sensesrs']/span[@class='sensecontent']/span[@class='sensesr']/span[@class='headword']/span/span/a[text()='1.1']"; + const string subSenseOneOne = "/div[@class='reversalindexentry-1']/span[@class='sensesrs-1']/span[@class='sensecontent']/span[@class='sensesr']/span[@class='headword-1']/span/span/a[text()='1.1']"; AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(subSenseOneOne, 1); } @@ -586,7 +586,7 @@ public void GenerateXHTMLForEntry_VernacularFormWithSubSensesinReversalSubEntry( var testEntry = CreateInterestingEnglishSubReversalEntryWithSubSense(); //SUT var xhtml = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, DefaultSettings).ToString(); - const string subSenseOneOne = "/div[@class='reversalindexentry']/span[@class='subentries']/span[@class='subentry']/span[@class='sensesrs']/span[@class='sensecontent']/span[@class='sensesr']/span[@class='headword']/span/span/a[text()='1.1']"; + const string subSenseOneOne = "/div[@class='reversalindexentry-1']/span[@class='subentries-1']/span[@class='subentry']/span[@class='sensesrs-1']/span[@class='sensecontent']/span[@class='sensesr']/span[@class='headword-1']/span/span/a[text()='1.1']"; AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(subSenseOneOne, 1); } @@ -706,8 +706,8 @@ public void GenerateXHTMLForEntry_SameGramInfoCollapsesOnDemand() var xhtml = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); // check that the sense gram info appears once before the rest of the sense information. Assert.That(xhtml, Is.Not.Null.Or.Empty); - const string sharedGramInfo = "/div[@class='reversalindexentry']/span[@class='sensesrs']/span[@class='sharedgrammaticalinfo']/span[@class='morphosyntaxanalysis']/span[@class='partofspeech']/span[@lang='en' and text()='n']"; - const string separateGramInfo = "/div[@class='reversalindexentry']/span[@class='sensesrs']/span[@class='sensecontent']/span[@class='sensesr']/span[@class='morphosyntaxanalysis']/span[@class='partofspeech']/span[@lang='en']"; + const string sharedGramInfo = "/div[@class='reversalindexentry-1']/span[@class='sensesrs-1']/span[@class='sharedgrammaticalinfo']/span[@class='morphosyntaxanalysis-1']/span[@class='partofspeech-1']/span[@lang='en' and text()='n']"; + const string separateGramInfo = "/div[@class='reversalindexentry-1']/span[@class='sensesrs-1']/span[@class='sensecontent']/span[@class='sensesr']/span[@class='morphosyntaxanalysis-1']/span[@class='partofspeech-1']/span[@lang='en']"; AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(sharedGramInfo, 1); AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(separateGramInfo, 0); diff --git a/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs b/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs index 1d60ade2f5..bcdb41df9e 100644 --- a/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs +++ b/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs @@ -167,7 +167,7 @@ public void ResetModelAssembly() ConfiguredLcmGenerator.Init(); } - const string xpathThruSense = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']"; + const string xpathThruSense = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']"; private const string TestVariantName = "Crazy Variant"; [Test] @@ -190,7 +190,7 @@ public void GenerateContentForEntry_HeadwordConfigurationGeneratesCorrectResult( var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, new ReadOnlyPropertyTable(m_propertyTable), false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); - const string frenchHeadwordOfHeadwordTest = "/div[@class='lexentry']/span[@class='headword']/span[@lang='fr']/a[text()='HeadWordTest']"; + const string frenchHeadwordOfHeadwordTest = "/div[@class='lexentry-1']/span[@class='headword-1']/span[@lang='fr']/a[text()='HeadWordTest']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(frenchHeadwordOfHeadwordTest, 1); } @@ -213,7 +213,7 @@ public void GenerateContentForEntry_InvalidUnicodeHeadword_GeneratesErrorResult( var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, new ReadOnlyPropertyTable(m_propertyTable), false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); - const string invalidCharsHeadwordTest = "/div[@class='lexentry']/span[@class='headword']/span[text()='\u0fff\u0fff\u0fff']"; + const string invalidCharsHeadwordTest = "/div[@class='lexentry-1']/span[@class='headword-1']/span[text()='\u0fff\u0fff\u0fff']"; // change Headword back to something legal so that we don't crash trying to save bad data into the cache. AddHeadwordToEntry(entry, "notbadanymore", Cache.DefaultVernWs); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(invalidCharsHeadwordTest, 1); @@ -280,7 +280,7 @@ public void GenerateContentForEntry_LexemeFormConfigurationGeneratesCorrectResul var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, new ReadOnlyPropertyTable(m_propertyTable), false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); - const string frenchLexForm = "/div[@class='lexentry']/span[@class='lexemeformoa']/span[@lang='fr']/a[text()='LexemeFormTest']"; + const string frenchLexForm = "/div[@class='lexentry-1']/span[@class='lexemeformoa-1']/span[@lang='fr']/a[text()='LexemeFormTest']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(frenchLexForm, 1); } @@ -325,7 +325,7 @@ public void GenerateContentForEntry_PronunciationLocationGeneratesCorrectResult( var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); - const string hereLocation = "/div[@class='lexentry']/span[@class='pronunciations']/span[@class='pronunciation']/span[@class='location']/span[@class='name']/span[@lang='fr' and text()='Here!']"; + const string hereLocation = "/div[@class='lexentry-1']/span[@class='pronunciations-1']/span[@class='pronunciation']/span[@class='location-1']/span[@class='name-1']/span[@lang='fr' and text()='Here!']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(hereLocation, 1); } @@ -393,13 +393,14 @@ public void GenerateContentForEntry_PronunciationVideoFileGeneratesAnchorTag() const string movieCameraChar = "\U0001f3a5"; const string movieCamSearch = "/a/text()['" + movieCameraChar + "']"; - const string entryPart = "/div[@class='lexentry']"; - const string pronunciationsPart = "/span[@class='pronunciations']/span[@class='pronunciation']"; - const string mediaFilePart = "/span[@class='mediafiles']/span[@class='mediafile']"; - const string mediaFileAnchor1 = entryPart + pronunciationsPart + mediaFilePart + movieCamSearch; - const string variantsPart = "/span[@class='variantformentrybackrefs']/span[@class='variantformentrybackref']"; - const string varPronPart = "/span[@class='variantpronunciations']/span[@class='variantpronunciation']"; - const string mediaFileAnchor2 = entryPart + variantsPart + varPronPart + mediaFilePart + movieCamSearch; + const string entryPart = "/div[@class='lexentry-1']"; + const string pronunciationsPart = "/span[@class='pronunciations-1']/span[@class='pronunciation']"; + const string mediaFilePart1 = "/span[@class='mediafiles-1']/span[@class='mediafile']"; + const string mediaFileAnchor1 = entryPart + pronunciationsPart + mediaFilePart1 + movieCamSearch; + const string variantsPart = "/span[@class='variantformentrybackrefs-1']/span[@class='variantformentrybackref']"; + const string varPronPart = "/span[@class='variantpronunciations-1']/span[@class='variantpronunciation']"; + const string mediaFilePart2 = "/span[@class='mediafiles-2']/span[@class='mediafile']"; + const string mediaFileAnchor2 = entryPart + variantsPart + varPronPart + mediaFilePart2 + movieCamSearch; //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); @@ -482,9 +483,9 @@ public void GenerateContentForEntry_HomographNumbersGeneratesCorrectResult() XHTMLStringBuilder.Append(result); XHTMLStringBuilder.AppendLine(""); - var entryWithHomograph = "/TESTWRAPPER/div[@class='lexentry']/span[@class='homographnumber' and text()='1']"; + var entryWithHomograph = "/TESTWRAPPER/div[@class='lexentry-1']/span[@class='homographnumber-1' and text()='1']"; AssertThatXmlIn.String(XHTMLStringBuilder.ToString()).HasSpecifiedNumberOfMatchesForXpath(entryWithHomograph, 1); - entryWithHomograph = entryWithHomograph.Replace('1', '2'); + entryWithHomograph = "/TESTWRAPPER/div[@class='lexentry-1']/span[@class='homographnumber-1' and text()='2']"; AssertThatXmlIn.String(XHTMLStringBuilder.ToString()).HasSpecifiedNumberOfMatchesForXpath(entryWithHomograph, 1); } @@ -585,9 +586,9 @@ public void GenerateContentForEntry_OneEntryWithSenseAndOneWithoutWorks() XHTMLStringBuilder.Append(result); XHTMLStringBuilder.AppendLine(""); result = XHTMLStringBuilder.ToString(); - var entryOneHasSensesSpan = "/TESTWRAPPER/div[@class='lexentry' and @id='g" + entryOneId + "']/span[@class='senses']"; - var entryTwoExists = "/TESTWRAPPER/div[@class='lexentry' and @id='g" + entryTwoId + "']"; - var entryTwoHasNoSensesSpan = "/TESTWRAPPER/div[@class='lexentry' and @id='g" + entryTwoId + "']/span[@class='senses']"; + var entryOneHasSensesSpan = "/TESTWRAPPER/div[@class='lexentry-1' and @id='g" + entryOneId + "']/span[@class='senses-1']"; + var entryTwoExists = "/TESTWRAPPER/div[@class='lexentry-1' and @id='g" + entryTwoId + "']"; + var entryTwoHasNoSensesSpan = "/TESTWRAPPER/div[@class='lexentry-1' and @id='g" + entryTwoId + "']/span[@class='senses-1']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(entryOneHasSensesSpan, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(entryTwoExists, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(entryTwoHasNoSensesSpan, 0); @@ -603,7 +604,7 @@ public void GenerateContentForEntry_DefaultRootGeneratesResult() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, dictionaryModel.Parts[0], DefaultDecorator, settings).ToString(); - var entryExists = "/div[@class='entry' and @id='g" + entry.Guid + "']"; + var entryExists = "/div[@class='entry-1' and @id='g" + entry.Guid + "']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(entryExists, 1); } @@ -640,8 +641,8 @@ public void GenerateContentForEntry_DoesNotDescendThroughDisabledNode() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); - const string sensesThatShouldNotBe = "/div[@class='entry']/span[@class='senses']"; - const string headwordThatShouldNotBe = "//span[@class='gloss']"; + const string sensesThatShouldNotBe = "/div[@class='entry-1']/span[@class='senses-1']"; + const string headwordThatShouldNotBe = "//span[@class='gloss-1']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(sensesThatShouldNotBe, 0); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(headwordThatShouldNotBe, 0); } @@ -729,8 +730,8 @@ public void GenerateContentForEntry_TwoSensesWithSameInfoShowGramInfoFirst() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT var xhtmlString = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); - const string sharedGramInfoPath = "//div[@class='lexentry']/span[@class='senses']/span[@class='sharedgrammaticalinfo']"; - const string gramInfoPath = "//div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='msas']/span[@class='mlpartofspeech']"; + const string sharedGramInfoPath = "//div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sharedgrammaticalinfo']"; + const string gramInfoPath = "//div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='msas-1']/span[@class='mlpartofspeech-1']"; AssertThatXmlIn.String(xhtmlString).HasNoMatchForXpath(gramInfoPath); AssertThatXmlIn.String(xhtmlString).HasSpecifiedNumberOfMatchesForXpath(sharedGramInfoPath, 1); } @@ -814,8 +815,8 @@ public void GenerateContentForEntry_TwoSensesWithSameInfo_ThirdSenseNotPublished var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT var xhtmlString = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, mainDictionaryDecorator, settings).ToString(); - const string sharedGramInfoPath = "//div[@class='lexentry']/span[@class='senses']/span[@class='sharedgrammaticalinfo']"; - const string gramInfoPath = "//div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='msa']/span[@class='mlpartofspeech']"; + const string sharedGramInfoPath = "//div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sharedgrammaticalinfo']"; + const string gramInfoPath = "//div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='msa']/span[@class='mlpartofspeech-1']"; AssertThatXmlIn.String(xhtmlString).HasNoMatchForXpath(gramInfoPath); AssertThatXmlIn.String(xhtmlString).HasSpecifiedNumberOfMatchesForXpath(sharedGramInfoPath, 1); } @@ -879,8 +880,8 @@ public void GenerateContentForEntry_TwoSensesWithDifferentGramInfoShowInfoInSens var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT var xhtmlString = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); - const string sharedGramInfoPath = "//div[@class='lexentry']/span[@class='sensesos']/span[@class='sharedgrammaticalinfo']"; - const string gramInfoPath = "//div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='msas']/span[@class='mlpartofspeech']"; + const string sharedGramInfoPath = "//div[@class='lexentry-1']/span[@class='sensesos']/span[@class='sharedgrammaticalinfo']"; + const string gramInfoPath = "//div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='msas-1']/span[@class='mlpartofspeech-1']"; AssertThatXmlIn.String(xhtmlString).HasSpecifiedNumberOfMatchesForXpath(gramInfoPath, 2); AssertThatXmlIn.String(xhtmlString).HasNoMatchForXpath(sharedGramInfoPath); } @@ -928,8 +929,8 @@ public void GenerateContentForEntry_TwoSensesWithNoGramInfoDisplaysNothingForSha var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT var xhtmlString = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); - const string sharedGramInfoPath = "//div[@class='lexentry']/span[@class='sensesos']/span[@class='sharedgrammaticalinfo']"; - const string gramInfoPath = "//div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='msas']/span[@class='mlpartofspeech']"; + const string sharedGramInfoPath = "//div[@class='lexentry-1']/span[@class='sensesos']/span[@class='sharedgrammaticalinfo']"; + const string gramInfoPath = "//div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='msas-1']/span[@class='mlpartofspeech-1']"; AssertThatXmlIn.String(xhtmlString).HasNoMatchForXpath(gramInfoPath); AssertThatXmlIn.String(xhtmlString).HasNoMatchForXpath(sharedGramInfoPath); } @@ -988,7 +989,7 @@ public void GenerateContentForEntry_MorphemeType() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); - const string morphTypePath = "//span[@class='morphosyntaxanalysis']/span[@class='morphtypes']/span[@class='morphtype']/span[@class='abbreviation']/span[@lang='en' and text()='sfx']"; + const string morphTypePath = "//span[@class='morphosyntaxanalysis-1']/span[@class='morphtypes-1']/span[@class='morphtype']/span[@class='abbreviation-1']/span[@lang='en' and text()='sfx']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(morphTypePath, 1); } @@ -1028,7 +1029,7 @@ public void GenerateContentForEntry_MakesSpanForRA() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); - const string gramInfoPath = xpathThruSense + "/span[@class='morphosyntaxanalysis']"; + const string gramInfoPath = xpathThruSense + "/span[@class='morphosyntaxanalysis-1']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(gramInfoPath, 1); } @@ -1079,7 +1080,7 @@ public void GenerateContentForEntry_CmObjectWithNoEnabledChildrenSkipsSpan() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); - const string gramInfoPath = xpathThruSense + "/span[@class='morphosyntaxanalysis']"; + const string gramInfoPath = xpathThruSense + "/span[@class='morphosyntaxanalysis-1']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(gramInfoPath, 0); } @@ -1113,7 +1114,7 @@ public void GenerateContentForEntry_DoesNotMakeSpanForRAIfNoData() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); - const string gramInfoPath = xpathThruSense + "/span[@class='morphosyntaxanalysis']"; + const string gramInfoPath = xpathThruSense + "/span[@class='morphosyntaxanalysis-1']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(gramInfoPath, 0); } @@ -1204,10 +1205,10 @@ public void GenerateContentForEntry_SupportsGramAbbrChildOfMSARA() // SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); - const string gramAbbr1 = xpathThruSense + "/span[@class='morphosyntaxanalysis']/span[@class='interlinearabbrtss']/span[@lang='fr']/span[@lang='fr' and text()='Blah']"; - const string gramAbbr2 = xpathThruSense + "/span[@class='morphosyntaxanalysis']/span[@class='interlinearabbrtss']/span[@lang='fr']/span[@lang='en' and text()=':Any']"; - const string gramName1 = xpathThruSense + "/span[@class='morphosyntaxanalysis']/span[@class='interlinearnametss']/span[@lang='fr']/span[@lang='fr' and text()='Blah']"; - const string gramName2 = xpathThruSense + "/span[@class='morphosyntaxanalysis']/span[@class='interlinearnametss']/span[@lang='fr']/span[@lang='en' and text()=':Any']"; + const string gramAbbr1 = xpathThruSense + "/span[@class='morphosyntaxanalysis-1']/span[@class='interlinearabbrtss-1']/span[@lang='fr']/span[@lang='fr' and text()='Blah']"; + const string gramAbbr2 = xpathThruSense + "/span[@class='morphosyntaxanalysis-1']/span[@class='interlinearabbrtss-1']/span[@lang='fr']/span[@lang='en' and text()=':Any']"; + const string gramName1 = xpathThruSense + "/span[@class='morphosyntaxanalysis-1']/span[@class='interlinearnametss-1']/span[@lang='fr']/span[@lang='fr' and text()='Blah']"; + const string gramName2 = xpathThruSense + "/span[@class='morphosyntaxanalysis-1']/span[@class='interlinearnametss-1']/span[@lang='fr']/span[@lang='en' and text()=':Any']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(gramAbbr1, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(gramAbbr2, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(gramName1, 1); @@ -1267,10 +1268,10 @@ public void GenerateContentForEntry_DontDisplayNotSure() // SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); - const string gramAbbr1 = xpathThruSense + "/span[@class='morphosyntaxanalysis']/span[@class='interlinearabbrtss']/span[@lang='fr']/span[@lang='fr' and text()='']"; - const string gramAbbr2 = xpathThruSense + "/span[@class='morphosyntaxanalysis']/span[@class='interlinearabbrtss']/span[@lang='fr']/span[@lang='en' and text()=':Any']"; - const string gramName1 = xpathThruSense + "/span[@class='morphosyntaxanalysis']/span[@class='interlinearnametss']/span[@lang='fr']/span[@lang='fr' and text()='']"; - const string gramName2 = xpathThruSense + "/span[@class='morphosyntaxanalysis']/span[@class='interlinearnametss']/span[@lang='fr']/span[@lang='en' and text()=':Any']"; + const string gramAbbr1 = xpathThruSense + "/span[@class='morphosyntaxanalysis-1']/span[@class='interlinearabbrtss-1']/span[@lang='fr']/span[@lang='fr' and text()='']"; + const string gramAbbr2 = xpathThruSense + "/span[@class='morphosyntaxanalysis-1']/span[@class='interlinearabbrtss-1']/span[@lang='fr']/span[@lang='en' and text()=':Any']"; + const string gramName1 = xpathThruSense + "/span[@class='morphosyntaxanalysis-1']/span[@class='interlinearnametss-1']/span[@lang='fr']/span[@lang='fr' and text()='']"; + const string gramName2 = xpathThruSense + "/span[@class='morphosyntaxanalysis-1']/span[@class='interlinearnametss-1']/span[@lang='fr']/span[@lang='en' and text()=':Any']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(gramAbbr1, 0); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(gramAbbr2, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(gramName1, 0); @@ -1315,7 +1316,7 @@ public void GenerateContentForEntry_CaptionOrHeadwordGetsCaption() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); - const string captionOrHeadwordContainsCaption = "/div[@class='lexentry']/span[@class='pictures']/div[@class='picture']/div[@class='captionContent']/span[@class='captionorheadword']//span[text()='captionEn']"; + const string captionOrHeadwordContainsCaption = "/div[@class='lexentry-1']/span[@class='pictures-1']/div[@class='picture']/div[@class='captionContent']/span[@class='captionorheadword']//span[text()='captionEn']"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(captionOrHeadwordContainsCaption, 1); } @@ -1358,7 +1359,7 @@ public void GenerateContentForEntry_CaptionOrHeadwordGetsHeadword() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); - const string captionOrHeadwordContainsHeadword = "/div[@class='lexentry']/span[@class='pictures']/div[@class='picture']/div[@class='captionContent']/span[@class='captionorheadword']//span[text()='HeadwordEn']"; + const string captionOrHeadwordContainsHeadword = "/div[@class='lexentry-1']/span[@class='pictures-1']/div[@class='picture']/div[@class='captionContent']/span[@class='captionorheadword']//span[text()='HeadwordEn']"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(captionOrHeadwordContainsHeadword, 1); } @@ -1402,8 +1403,8 @@ public void GenerateContentForEntry_CaptionOrHeadword_HandlePerWs() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); - const string captionOrHeadwordContainsCaptionEn = "/div[@class='lexentry']/span[@class='pictures']/div[@class='picture']/div[@class='captionContent']/span[@class='captionorheadword']//span[@lang='en' and text()='captionEn']"; - const string captionOrHeadwordContainsHeadwordFr = "/div[@class='lexentry']/span[@class='pictures']/div[@class='picture']/div[@class='captionContent']/span[@class='captionorheadword']//span[@lang='fr' and text()='HeadwordFr']"; + const string captionOrHeadwordContainsCaptionEn = "/div[@class='lexentry-1']/span[@class='pictures-1']/div[@class='picture']/div[@class='captionContent']/span[@class='captionorheadword']//span[@lang='en' and text()='captionEn']"; + const string captionOrHeadwordContainsHeadwordFr = "/div[@class='lexentry-1']/span[@class='pictures-1']/div[@class='picture']/div[@class='captionContent']/span[@class='captionorheadword']//span[@lang='fr' and text()='HeadwordFr']"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(captionOrHeadwordContainsCaptionEn, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(captionOrHeadwordContainsHeadwordFr, 1); @@ -1433,7 +1434,7 @@ public void GenerateContentForEntry_DefinitionOrGlossWorks() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); - const string senseWithdefinitionOrGloss = "//span[@class='sense']/span[@class='definitionorgloss']/span[text()='gloss']"; + const string senseWithdefinitionOrGloss = "//span[@class='sense']/span[@class='definitionorgloss-1']/span[text()='gloss']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseWithdefinitionOrGloss, 1); } @@ -1464,7 +1465,7 @@ public void GenerateContentForEntry_DefinitionOrGlossWorks_WithAbbrev() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); const string senseWithdefinitionOrGloss = - "//span[@class='sense']/span[@class='definitionorgloss']/span[@class='writingsystemprefix' and normalize-space(text())='Eng']"; + "//span[@class='sense']/span[@class='definitionorgloss-1']/span[@class='writingsystemprefix' and normalize-space(text())='Eng']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseWithdefinitionOrGloss, 1); } @@ -1492,7 +1493,7 @@ public void GenerateContentForEntry_DefinitionOrGloss_HandlePerWS() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); - const string senseWithdefinitionOrGlossTwoWs = "//span[@class='sense']/span[@class='definitionorgloss' and span[1]='gloss' and span[2]='definition']"; + const string senseWithdefinitionOrGlossTwoWs = "//span[@class='sense']/span[@class='definitionorgloss-1' and span[1]='gloss' and span[2]='definition']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseWithdefinitionOrGlossTwoWs, 1); } @@ -1555,17 +1556,17 @@ public void GenerateContentForEntry_ReferencedComplexFormDefinitionOrGloss_Handl // set of xpaths and required number of matches. var checkthis = new Dictionary() { - { "/div/span[@class='visiblecomplexformbackrefs']/span[@class='visiblecomplexformbackref']/span[@class='definitionorgloss']/span[.='definitionA1']", 1 }, - { "/div/span[@class='visiblecomplexformbackrefs']/span[@class='visiblecomplexformbackref']/span[@class='definitionorgloss']/span[.='definitionA2']", 1 }, + { "/div/span[@class='visiblecomplexformbackrefs-1']/span[@class='visiblecomplexformbackref']/span[@class='definitionorgloss-1']/span[.='definitionA1']", 1 }, + { "/div/span[@class='visiblecomplexformbackrefs-1']/span[@class='visiblecomplexformbackref']/span[@class='definitionorgloss-1']/span[.='definitionA2']", 1 }, - { "/div/span[@class='visiblecomplexformbackrefs']/span[@class='visiblecomplexformbackref']/span[@class='definitionorgloss']/span[.='glossB1']", 1 }, - { "/div/span[@class='visiblecomplexformbackrefs']/span[@class='visiblecomplexformbackref']/span[@class='definitionorgloss']/span[.='glossB2']", 1 }, + { "/div/span[@class='visiblecomplexformbackrefs-1']/span[@class='visiblecomplexformbackref']/span[@class='definitionorgloss-1']/span[.='glossB1']", 1 }, + { "/div/span[@class='visiblecomplexformbackrefs-1']/span[@class='visiblecomplexformbackref']/span[@class='definitionorgloss-1']/span[.='glossB2']", 1 }, - { "/div/span[@class='visiblecomplexformbackrefs']/span[@class='visiblecomplexformbackref']/span[@class='definitionorgloss']/span[.='definitionC1']", 1 }, - { "/div/span[@class='visiblecomplexformbackrefs']/span[@class='visiblecomplexformbackref']/span[@class='definitionorgloss']/span[.='glossC2']", 1 }, + { "/div/span[@class='visiblecomplexformbackrefs-1']/span[@class='visiblecomplexformbackref']/span[@class='definitionorgloss-1']/span[.='definitionC1']", 1 }, + { "/div/span[@class='visiblecomplexformbackrefs-1']/span[@class='visiblecomplexformbackref']/span[@class='definitionorgloss-1']/span[.='glossC2']", 1 }, - { "/div/span[@class='visiblecomplexformbackrefs']/span[@class='visiblecomplexformbackref']/span[@class='definitionorgloss']/span[.='glossD1']", 1 }, - { "/div/span[@class='visiblecomplexformbackrefs']/span[@class='visiblecomplexformbackref']/span[@class='definitionorgloss']/span[.='definitionD2']", 1 }, + { "/div/span[@class='visiblecomplexformbackrefs-1']/span[@class='visiblecomplexformbackref']/span[@class='definitionorgloss-1']/span[.='glossD1']", 1 }, + { "/div/span[@class='visiblecomplexformbackrefs-1']/span[@class='visiblecomplexformbackref']/span[@class='definitionorgloss-1']/span[.='definitionD2']", 1 }, }; foreach (var thing in checkthis) { @@ -1625,10 +1626,10 @@ public void GenerateContentForEntry_OtherReferencedComplexForms() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); var fwdNameXpath = string.Format( - "//span[@class='complexformsnotsubentries']/span[@class='complexformtypes']/span[@class='complexformtype']/span/span[@lang='en' and text()='{0}']", + "//span[@class='complexformsnotsubentries-1']/span[@class='complexformtypes-1']/span[@class='complexformtype']/span/span[@lang='en' and text()='{0}']", complexRefAbbr); var revNameXpath = string.Format( - "//span[@class='complexformsnotsubentries']/span[@class='complexformtypes']/span[@class='complexformtype']/span[@class='reverseabbr']/span[@lang='en' and text()='{0}']", + "//span[@class='complexformsnotsubentries-1']/span[@class='complexformtypes-1']/span[@class='complexformtype']/span[@class='reverseabbr-1']/span[@lang='en' and text()='{0}']", complexRefRevAbbr); AssertThatXmlIn.String(result).HasNoMatchForXpath(fwdNameXpath); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(revNameXpath, 1); @@ -1661,7 +1662,7 @@ public void GenerateContentForEntry_DuplicateConfigNodeWithSpaceWorks() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); - const string senseWithHyphenSuffix = "//span[@class='senses_test-one']/span[@class='sense_test-one']"; + const string senseWithHyphenSuffix = "//span[@class='senses_test-one-1']/span[@class='sense_test-one']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseWithHyphenSuffix, 1); } @@ -1692,7 +1693,7 @@ public void GenerateContentForEntry_DuplicateConfigNodeWithPuncWorks() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); - const string senseWithHyphenSuffix = "//span[@class='senses_-test']/span[@class='sense_-test']"; + const string senseWithHyphenSuffix = "//span[@class='senses_-test-1']/span[@class='sense_-test']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseWithHyphenSuffix, 1); } @@ -1723,7 +1724,7 @@ public void GenerateContentForEntry_DuplicateConfigNodeWithMultiPuncWorks() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); - const string senseWithHyphenSuffix = "//span[@class='senses_-test-']/span[@class='sense_-test-']"; + const string senseWithHyphenSuffix = "//span[@class='senses_-test--1']/span[@class='sense_-test-']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseWithHyphenSuffix, 1); } @@ -1782,7 +1783,7 @@ public void GenerateContentForEntry_HeadWordRefVirtualPropWorks() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); - var headwordMatch = string.Format("//span[@class='{0}']//span[@class='{1}']/span[text()='{2}']", + var headwordMatch = string.Format("//span[@class='{0}-1']//span[@class='{1}-1']/span[text()='{2}']", nters, headWord, entryThreeForm); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(headwordMatch, 1); } @@ -1836,9 +1837,9 @@ public void GenerateContentForEntry_EtymologyLanguageWorks() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); - const string etymologyWithArabicSrcLanguage = "//span[@class='etymologies']/span[@class='etymology']/span[@class='languages']/span[@class='language']/span[@class='abbreviation']/span[@lang='en' and text()='ar']"; + const string etymologyWithArabicSrcLanguage = "//span[@class='etymologies-1']/span[@class='etymology']/span[@class='languages-1']/span[@class='language']/span[@class='abbreviation-1']/span[@lang='en' and text()='ar']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(etymologyWithArabicSrcLanguage, 1); - const string etymologyWithGeorgianNotes = "//span[@class='etymologies']/span[@class='etymology']/span[@class='languagenotes']/span[@lang='en' and text()='Georgian']"; + const string etymologyWithGeorgianNotes = "//span[@class='etymologies-1']/span[@class='etymology']/span[@class='languagenotes-1']/span[@lang='en' and text()='Georgian']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(etymologyWithGeorgianNotes, 1); } @@ -1859,7 +1860,7 @@ public void GenerateEntryHtmlWithStyles_SelectsDirectionUsingDictionaryNormal() //SUT var xhtml = LcmXhtmlGenerator.GenerateEntryHtmlWithStyles(mainEntry, configModel, DefaultDecorator, m_propertyTable); // this test relies on specific test data from CreateInterestingConfigurationModel - const string xpath = "/html[@dir='rtl']/body[@dir='rtl']/div[@class='lexentry']/span[@class='entry']"; + const string xpath = "/html[@dir='rtl']/body[@dir='rtl']/div[@class='lexentry-1']/span[@class='entry-1']"; AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(xpath, 1); } finally @@ -1883,7 +1884,7 @@ public void GenerateEntryHtmlWithStyles_MinorEntryUsesMinorEntryFormatting( //SUT var xhtml = LcmXhtmlGenerator.GenerateEntryHtmlWithStyles(minorEntry, configModel, DefaultDecorator, m_propertyTable); // this test relies on specific test data from CreateInterestingConfigurationModel - const string xpath = "/html/body/div[@class='minorentry']/span[@class='entry']"; + const string xpath = "/html/body/div[@class='minorentry-1']/span[@class='entry-1']"; AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(xpath, 1); } @@ -1900,7 +1901,7 @@ public void GenerateEntryHtmlWithStyles_MinorEntryUnCheckedItemsGenerateNothing( //SUT var xhtml = LcmXhtmlGenerator.GenerateEntryHtmlWithStyles(minorEntry, configModel, DefaultDecorator, m_propertyTable); // this test relies on specific test data from CreateInterestingConfigurationModel - const string xpath = "/html/body/div[@class='minorentry']/span[@class='entry']"; + const string xpath = "/html/body/div[@class='minorentry-1']/span[@class='entry-1']"; // only the variant is selected, so the other minor entry should not have been generated AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(xpath, 0); } @@ -1917,7 +1918,7 @@ public void GenerateEntryHtmlWithStyles_DoesNotShowHiddenMinorEntries() //SUT var xhtml = LcmXhtmlGenerator.GenerateEntryHtmlWithStyles(minorEntry, configModel, DefaultDecorator, m_propertyTable); // this test relies on specific test data from CreateInterestingConfigurationModel - const string xpath = "/div[@class='minorentry']/span[@class='entry']"; + const string xpath = "/div[@class='minorentry-1']/span[@class='entry-1']"; AssertThatXmlIn.String(xhtml).HasNoMatchForXpath(xpath); } @@ -1939,7 +1940,7 @@ public void GenerateEntryHtmlWithStyles_DoesNotShowMinorEntriesTwice([Values(tru //SUT var xhtml = LcmXhtmlGenerator.GenerateEntryHtmlWithStyles(minorEntry, configModel, DefaultDecorator, m_propertyTable); // this test relies on specific test data from CreateInterestingConfigurationModel - const string xpath = "/html/body/div[@class='minorentry']/span[@class='entry']"; + const string xpath = "/html/body/div[@class='minorentry-1']/span[@class='entry-1']"; AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(xpath, 1); } @@ -1958,7 +1959,7 @@ public void GenerateContentForEntry_LexemeBasedConsidersComplexFormsMainEntries( //SUT var xhtml = LcmXhtmlGenerator.GenerateEntryHtmlWithStyles(complexEntry, configModel, DefaultDecorator, m_propertyTable); // this test relies on specific test data from CreateInterestingConfigurationModel - const string xpath = "/html/body/div[@class='minorentry']/span[@class='entry']"; + const string xpath = "/html/body/div[@class='minorentry-1']/span[@class='entry-1']"; // only the variant is selected, so the other minor entry should not have been generated AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(xpath, 0); } @@ -2009,8 +2010,8 @@ public void GenerateContentForEntry_SenseNumbersGeneratedForMultipleSenses(strin var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); - string senseNumberOne = $"/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and @lang='{homographWs}' and text()='1']]//span[@lang='en' and text()='gloss']"; - string senseNumberTwo = $"/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and @lang='{homographWs}' and text()='2']]//span[@lang='en' and text()='second gloss']"; + string senseNumberOne = $"/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and @lang='{homographWs}' and text()='1']]//span[@lang='en' and text()='gloss']"; + string senseNumberTwo = $"/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and @lang='{homographWs}' and text()='2']]//span[@lang='en' and text()='second gloss']"; string senseNumberTen = $"//span[@class='sensecontent']/spansenses/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and @lang='{homographWs}' and text()='{tenthSenseNumber}']]//span[@lang='en' and text()='10']"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseNumberOne, 1); @@ -2171,7 +2172,7 @@ public void GenerateContentForEntry_SingleSenseGetsNoSenseNumber() Assert.That(testEntry.AllSenses.First().AllSenses.Count, Is.EqualTo(1), "Test not set up correctly. There should be no subsenses."); // SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); - const string senseNumberOne = "/div[@class='lexentry']/span[@class='senses']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]/span[@lang='en' and text()='gloss']"; + const string senseNumberOne = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]/span[@lang='en' and text()='gloss']"; // This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasNoMatchForXpath(senseNumberOne); @@ -2239,7 +2240,7 @@ public void GenerateContentForEntry_TurnedOffSubsensesCausesSenseToBehaveLikeSin Assert.That(testEntry.AllSenses.First().AllSenses.Count, Is.EqualTo(3), "Test not set up correctly."); // SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); - const string senseNumberXpath = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sensenumber']"; + const string senseNumberXpath = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sensenumber']"; AssertThatXmlIn.String(result).HasNoMatchForXpath(senseNumberXpath); // Should not have a sense number on top sense. // Piggy-back a test for ShouldThisSenseBeNumbered @@ -2306,7 +2307,7 @@ public void GenerateContentForEntry_EmptyStyleSubsensesCausesSenseToBehaveLikeSi Assert.That(testEntry.AllSenses.First().AllSenses.Count, Is.EqualTo(3), "Test not set up correctly."); // SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); - const string senseNumberXpath = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sensenumber']"; + const string senseNumberXpath = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sensenumber']"; AssertThatXmlIn.String(result).HasNoMatchForXpath(senseNumberXpath); // Should not have a sense number on top sense. // Piggy-back a test for ShouldThisSenseBeNumbered @@ -2373,7 +2374,7 @@ public void GenerateContentForEntry_SubsenseStyleInfluencesSenseNumberShown() Assert.That(testEntry.AllSenses.First().AllSenses.Count, Is.EqualTo(3), "Test not set up correctly."); // SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); - const string senseNumberXpath = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sensenumber']"; + const string senseNumberXpath = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sensenumber']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseNumberXpath, 1); // Should have sense number on top sense. // Piggy-back a test for ShouldThisSenseBeNumbered @@ -2423,7 +2424,7 @@ public void GenerateContentForEntry_NumberingSingleSenseAlsoCountsSubSense() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); - const string SenseOneSubSense = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]/span[@class='senses']/span[@class='sensecontent']//span[@lang='en' and text()='gloss1.1']"; + const string SenseOneSubSense = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]/span[@class='senses-2']/span[@class='sensecontent']//span[@lang='en' and text()='gloss1.1']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(SenseOneSubSense, 1); } @@ -2480,10 +2481,10 @@ public void GenerateContentForEntry_SensesAndSubSensesWithDifferentNumberingStyl var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); - const string senseNumberOne = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='A']]//span[@lang='en' and text()='gloss']"; - const string senseNumberTwo = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='B']]//span[@lang='en' and text()='second gloss']"; - const string subSensesNumberTwoOne = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='I']]//span[@lang='en' and text()='second gloss2.1']"; - const string subSenseNumberTwoTwo = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='II']]//span[@lang='en' and text()='second gloss2.2']"; + const string senseNumberOne = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='A']]//span[@lang='en' and text()='gloss']"; + const string senseNumberTwo = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='B']]//span[@lang='en' and text()='second gloss']"; + const string subSensesNumberTwoOne = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='I']]//span[@lang='en' and text()='second gloss2.1']"; + const string subSenseNumberTwoTwo = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='II']]//span[@lang='en' and text()='second gloss2.2']"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseNumberOne, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseNumberTwo, 1); @@ -2545,10 +2546,10 @@ public void GenerateContentForEntry_SensesAndSubSensesWithNumberingStyle() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); - const string senseNumberOne = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='A']]//span[@lang='en' and text()='gloss']"; - const string senseNumberTwo = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='B']]//span[@lang='en' and text()='second gloss']"; - const string subSensesNumberTwoOne = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='second gloss2.1']"; - const string subSenseNumberTwoTwo = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]//span[@lang='en' and text()='second gloss2.2']"; + const string senseNumberOne = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='A']]//span[@lang='en' and text()='gloss']"; + const string senseNumberTwo = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='B']]//span[@lang='en' and text()='second gloss']"; + const string subSensesNumberTwoOne = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='second gloss2.1']"; + const string subSenseNumberTwoTwo = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]//span[@lang='en' and text()='second gloss2.2']"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseNumberOne, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseNumberTwo, 1); @@ -2597,7 +2598,7 @@ public void GenerateContentForEntry_NoSenseNumberFIfStyleSaysNoNumbering() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); - const string senseNumberXpath = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sensenumber']"; + const string senseNumberXpath = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sensenumber']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseNumberXpath, 0); // Should not have produced sense number if style said not to number it. // Piggy-back a test for ShouldThisSenseBeNumbered @@ -2657,8 +2658,8 @@ public void GenerateContentForEntry_SensesNoneAndSubSensesWithNumberingStyle() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); - const string subSensesNumberOne = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='second gloss2.1']"; - const string subSenseNumberTwo = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]//span[@lang='en' and text()='second gloss2.2']"; + const string subSensesNumberOne = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='second gloss2.1']"; + const string subSenseNumberTwo = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]//span[@lang='en' and text()='second gloss2.2']"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(subSensesNumberOne, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(subSenseNumberTwo, 1); @@ -2699,10 +2700,10 @@ public void GenerateContentForEntry_SensesGeneratedForMultipleSubSenses() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); - const string senseNumberOne = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='gloss']"; - const string senseNumberTwo = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]//span[@lang='en' and text()='second gloss']"; - const string subSensesNumberTwoOne = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='second gloss2.1']"; - const string subSenseNumberTwoTwo = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]//span[@lang='en' and text()='second gloss2.2']"; + const string senseNumberOne = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='gloss']"; + const string senseNumberTwo = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]//span[@lang='en' and text()='second gloss']"; + const string subSensesNumberTwoOne = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='second gloss2.1']"; + const string subSenseNumberTwoTwo = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]//span[@lang='en' and text()='second gloss2.2']"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseNumberOne, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseNumberTwo, 1); @@ -2754,9 +2755,9 @@ public void GenerateContentForEntry_SubSenseParentSenseNumberingStyleJoined() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); - const string senseNumber = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]"; - const string subSenseNumber = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2a']]"; - const string subSubSenseNumber = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2aA']]"; + const string senseNumber = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]"; + const string subSenseNumber = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2a']]"; + const string subSubSenseNumber = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-3']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2aA']]"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseNumber, 1); @@ -2808,9 +2809,9 @@ public void GenerateContentForEntry_SubSenseParentSenseNumberingStyleSeparatedBy var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); - const string senseNumber = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]"; - const string subSenseNumber = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2.a']]"; - const string subSubSenseNumber = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2.a.A']]"; + const string senseNumber = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]"; + const string subSenseNumber = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2.a']]"; + const string subSubSenseNumber = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-3']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2.a.A']]"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseNumber, 1); @@ -2862,9 +2863,9 @@ public void GenerateContentForEntry_SubSenseParentSenseNumberingStyleNone() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); - const string senseNumber = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]"; - const string subSenseNumber = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='a']]"; - const string subSubSenseNumber = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='A']]"; + const string senseNumber = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]"; + const string subSenseNumber = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='a']]"; + const string subSubSenseNumber = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-3']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='A']]"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseNumber, 1); @@ -2986,13 +2987,13 @@ public void GenerateContentForEntry_GeneratesGramInfoFirstEvenSingleSense() xhtmlPath = LcmXhtmlGenerator.SavePreviewHtmlWithStyles(new[] { firstEntry.Hvo }, pubEverything, model, m_propertyTable); var xhtml = File.ReadAllText(xhtmlPath); // SUT - const string gramInfoPath = "/html/body/div[@class='lexentry']/span[@class='senses']/span[@class='sharedgrammaticalinfo']/span[@class='morphosyntaxanalysis']/span[@class='mlpartofspeech']/span[@lang='en' and text()='n']"; + const string gramInfoPath = "/html/body/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sharedgrammaticalinfo']/span[@class='morphosyntaxanalysis-1']/span[@class='mlpartofspeech-1']/span[@lang='en' and text()='n']"; AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(gramInfoPath, 1); - const string senseNumberPath = "/html/body/div[@class='lexentry']/span[@class='senses']/span[2][@class='sensecontent']/span[@class='sensenumber' and text()='1']"; + const string senseNumberPath = "/html/body/div[@class='lexentry-1']/span[@class='senses-1']/span[2][@class='sensecontent']/span[@class='sensenumber' and text()='1']"; AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(senseNumberPath, 1); - const string senseTextPath = "/html/body/div[@class='lexentry']/span[@class='senses']/span[2][@class='sensecontent']/span[@class='sense']/span[@class='gloss']/span[@lang='en' and text()='man']"; + const string senseTextPath = "/html/body/div[@class='lexentry-1']/span[@class='senses-1']/span[2][@class='sensecontent']/span[@class='sense']/span[@class='gloss-1']/span[@lang='en' and text()='man']"; AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(senseTextPath, 1); } finally @@ -3098,9 +3099,9 @@ public void GenerateContentForEntry_SubentriesSensesDontGetMainEntrySensesNumber var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); - const string senseContent = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']"; + const string senseContent = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']"; const string senseNumberOne = senseContent + "/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='gloss']"; - const string subentrySenseContent = senseContent + "/span[@class='sense']/span[@class='subentries']/span[@class='subentry']/span[@class='senses']/span[@class='sensecontent']"; + const string subentrySenseContent = senseContent + "/span[@class='sense']/span[@class='subentries-1']/span[@class='subentry']/span[@class='senses-2']/span[@class='sensecontent']"; const string subentrySenseNumberOne = subentrySenseContent + "/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='subgloss']"; const string subentrySenseNumberOneOne = subentrySenseContent + "/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1.1']]//span[@lang='en']"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry @@ -3138,7 +3139,7 @@ public void GenerateContentForEntry_SingleSenseGetsNumberWithNumberEvenOneSenseO // SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, DefaultSettings).ToString(); - const string senseNumberOne = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='gloss']"; + const string senseNumberOne = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='gloss']"; // This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseNumberOne, 1); @@ -3168,10 +3169,10 @@ public void GenerateContentForEntry_SenseContentWithGuid() // SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, DefaultSettings).ToString(); - const string senseEntryGuid = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and @entryguid]"; + const string senseEntryGuid = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense' and @entryguid]"; // This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseEntryGuid, 1); - string senseEntryGuidstatsWithG = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and @entryguid='g" + testEntry.Guid + "']"; + string senseEntryGuidstatsWithG = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense' and @entryguid='g" + testEntry.Guid + "']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseEntryGuidstatsWithG, 1); } @@ -3221,11 +3222,11 @@ public void GenerateContentForEntry_ExampleAndTranslationAreGenerated() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, DefaultSettings).ToString(); - const string xpathThruExample = xpathThruSense + "/span[@class='examplescontents']/span[@class='examplescontent']"; - var oneSenseWithExample = string.Format(xpathThruExample + "/span[@class='example']/span[@lang='fr' and text()='{0}']", example); + const string xpathThruExample = xpathThruSense + "/span[@class='examplescontents-1']/span[@class='examplescontent']"; + var oneSenseWithExample = string.Format(xpathThruExample + "/span[@class='example-1']/span[@lang='fr' and text()='{0}']", example); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(oneSenseWithExample, 1); var oneExampleSentenceTranslation = string.Format(xpathThruExample + - "/span[@class='translationcontents']/span[@class='translationcontent']/span[@class='translation']/span[@lang='en' and text()='{0}']", translation); + "/span[@class='translationcontents-1']/span[@class='translationcontent']/span[@class='translation-1']/span[@lang='en' and text()='{0}']", translation); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(oneExampleSentenceTranslation, 1); } @@ -3275,11 +3276,11 @@ public void GenerateContentForEntry_ExampleSentenceAndTranslationAreGenerated() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); - const string xpathThruExampleSentence = "/div[@class='lexentry']/span[@class='complexformsnotsubentries']/span[@class='complexformsnotsubentry']/span[@class='examplesentences']/span[@class='examplesentence']"; + const string xpathThruExampleSentence = "/div[@class='lexentry-1']/span[@class='complexformsnotsubentries-1']/span[@class='complexformsnotsubentry']/span[@class='examplesentences-1']/span[@class='examplesentence']"; var oneSenseWithExample = string.Format(xpathThruExampleSentence + "//span[@lang='fr' and text()='{0}']", example); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(oneSenseWithExample, 1); var oneExampleSentenceTranslation = string.Format( - xpathThruExampleSentence + "/span[@class='translations']/span[@class='translation']//span[@lang='en' and text()='{0}']", translation); + xpathThruExampleSentence + "/span[@class='translations-1']/span[@class='translation']//span[@lang='en' and text()='{0}']", translation); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(oneExampleSentenceTranslation, 1); } @@ -3329,11 +3330,11 @@ public void GenerateContentForEntry_LineSeperatorUnicodeCharBecomesBrElement() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); - const string xpathThruExampleSentence = "/div[@class='lexentry']/span[@class='complexformsnotsubentries']/span[@class='complexformsnotsubentry']/span[@class='examplesentences']/span[@class='examplesentence']"; + const string xpathThruExampleSentence = "/div[@class='lexentry-1']/span[@class='complexformsnotsubentries-1']/span[@class='complexformsnotsubentry']/span[@class='examplesentences-1']/span[@class='examplesentence']"; var oneSenseWithExample = string.Format(xpathThruExampleSentence + "//span[@lang='fr']//br"); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(oneSenseWithExample, 1); var oneExampleSentenceTranslation = string.Format( - xpathThruExampleSentence + "/span[@class='translations']/span[@class='translation']//span[@lang='en']//br"); + xpathThruExampleSentence + "/span[@class='translations-1']/span[@class='translation']//span[@lang='en']//br"); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(oneExampleSentenceTranslation, 1); } @@ -3405,18 +3406,18 @@ public void GenerateContentForEntry_ExtendedNoteChildrenAreGenerated() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); - const string extendedNote = xpathThruSense + "/span[@class='extendednotecontents']/span[@class='extendednotecontent']"; - var xpathThruNoteType = string.Format(extendedNote + "/span[@class='extendednotetypera_name']/span[@lang='en' and text()='{0}']", noteType); + const string extendedNote = xpathThruSense + "/span[@class='extendednotecontents-1']/span[@class='extendednotecontent']"; + var xpathThruNoteType = string.Format(extendedNote + "/span[@class='extendednotetypera_name-1']/span[@lang='en' and text()='{0}']", noteType); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(xpathThruNoteType, 1); - var xpathThruDiscussion = string.Format(extendedNote + "/span[@class='discussion']/span[@lang='fr' and text()='{0}']", discussion); + var xpathThruDiscussion = string.Format(extendedNote + "/span[@class='discussion-1']/span[@lang='fr' and text()='{0}']", discussion); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(xpathThruDiscussion, 1); - const string xpathThruExample = extendedNote + "/span[@class='examples']/span[@class='example']"; - var oneSenseWithExample = string.Format(xpathThruExample + "/span[@class='example']/span[@lang='fr' and text()='{0}']", example); + const string xpathThruExample = extendedNote + "/span[@class='examples-1']/span[@class='example']"; + var oneSenseWithExample = string.Format(xpathThruExample + "/span[@class='example-1']/span[@lang='fr' and text()='{0}']", example); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(oneSenseWithExample, 1); var oneExampleSentenceTranslation = string.Format( - xpathThruExample + "/span[@class='translations']/span[@class='translation']/span[@class='translation']/span[@lang='en' and text()='{0}']", translation); + xpathThruExample + "/span[@class='translations-1']/span[@class='translation']/span[@class='translation-1']/span[@lang='en' and text()='{0}']", translation); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(oneExampleSentenceTranslation, 1); } @@ -3487,8 +3488,8 @@ public void GenerateContentForEntry_ExtendedNoteNoteTypeEmptyAreGenerated() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); - const string extendedNote = xpathThruSense + "/span[@class='extendednotecontents']/span[@class='extendednotecontent']"; - var xpathThruNoteType = string.Format(extendedNote + "/span[@class='extendednotetypera_name']/span[@lang='en' and text()='{0}']", noteType); + const string extendedNote = xpathThruSense + "/span[@class='extendednotecontents-1']/span[@class='extendednotecontent']"; + var xpathThruNoteType = string.Format(extendedNote + "/span[@class='extendednotetypera_name-1']/span[@lang='en' and text()='{0}']", noteType); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(xpathThruNoteType, 0); } @@ -3562,11 +3563,11 @@ public void GenerateContentForEntry_EnvironmentsAndAllomorphsAreGenerated() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); - const string xPathThruAllomorph = "/div[@class='lexentry']/span[@class='alternateformsos']/span[@class='alternateformso']"; + const string xPathThruAllomorph = "/div[@class='lexentry-1']/span[@class='alternateformsos-1']/span[@class='alternateformso']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( - xPathThruAllomorph + "/span[@class='form']/span[@lang='fr' and text()='Allomorph']", 1); + xPathThruAllomorph + "/span[@class='form-1']/span[@lang='fr' and text()='Allomorph']", 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(xPathThruAllomorph + - "/span[@class='allomorphenvironments']/span[@class='allomorphenvironment']/span[@class='stringrepresentation']/span[@lang='en' and text()='phoneyEnv']", 1); + "/span[@class='allomorphenvironments-1']/span[@class='allomorphenvironment']/span[@class='stringrepresentation-1']/span[@lang='en' and text()='phoneyEnv']", 1); } [Test] @@ -3600,7 +3601,7 @@ public void GenerateContentForEntry_ReferencedComplexFormsIncludesSubentriesAndO //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( - "/div[@class='lexentry']/span[@class='visiblecomplexformbackrefs']/span[@class='visiblecomplexformbackref']//span[@lang='fr']/span[@lang='fr']", 4); + "/div[@class='lexentry-1']/span[@class='visiblecomplexformbackrefs-1']/span[@class='visiblecomplexformbackref']//span[@lang='fr']/span[@lang='fr']", 4); } [Test] @@ -3652,9 +3653,9 @@ public void GenerateContentForEntry_GeneratesLinksForReferencedForms() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( - "//span[@class='visiblevariantentryrefs']/span[@class='visiblevariantentryref']/span[@class='referencedentries']/span[@class='referencedentry']/span[@class='headword']/span[@lang='fr']/span[@lang='fr']/a[@href]", 2); + "//span[@class='visiblevariantentryrefs-1']/span[@class='visiblevariantentryref']/span[@class='referencedentries-1']/span[@class='referencedentry']/span[@class='headword-1']/span[@lang='fr']/span[@lang='fr']/a[@href]", 2); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( - "/div[@class='lexentry']/span[@class='visiblecomplexformbackrefs']/span[@class='visiblecomplexformbackref']/span[@class='headword']/span[@lang='fr']/span[@lang='fr']/a[@href]", 2); + "/div[@class='lexentry-1']/span[@class='visiblecomplexformbackrefs-1']/span[@class='visiblecomplexformbackref']/span[@class='headword-2']/span[@lang='fr']/span[@lang='fr']/a[@href]", 2); } [Test] @@ -3751,9 +3752,9 @@ public void GenerateContentForEntry_GeneratesLinksForPrimaryEntryReferences() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(otherMainEntry, mainEntryNode, null, settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( - "//span[@class='minimallexreferences']/span[@class='minimallexreference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='primaryentryrefs']/span[@class='primaryentryref']/span[@class='referencedentries']/span[@class='referencedentry']/span[@class='headword']/span[@lang='fr']/a[@href]", 1); + "//span[@class='minimallexreferences-1']/span[@class='minimallexreference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='primaryentryrefs-1']/span[@class='primaryentryref']/span[@class='referencedentries-1']/span[@class='referencedentry']/span[@class='headword-1']/span[@lang='fr']/a[@href]", 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( - "//span[@class='minimallexreferences']/span[@class='minimallexreference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='primaryentryrefs']/span[@class='primaryentryref']/span[@class='referencedentries']/span[@class='referencedentry']/span[@class='headword']/span[@lang='fr']/a[@href][contains(text(), 'Test')]", 1); + "//span[@class='minimallexreferences-1']/span[@class='minimallexreference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='primaryentryrefs-1']/span[@class='primaryentryref']/span[@class='referencedentries-1']/span[@class='referencedentry']/span[@class='headword-1']/span[@lang='fr']/a[@href][contains(text(), 'Test')]", 1); } [Test] @@ -3798,7 +3799,7 @@ public void GenerateContentForEntry_GeneratesLinksForCrossReferences() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( - "//span[@class='minimallexreferences']/span[@class='minimallexreference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='headword']/span[@lang='fr']/span[@lang='fr']/a[@href]", 4); + "//span[@class='minimallexreferences-1']/span[@class='minimallexreference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='headword-1']/span[@lang='fr']/span[@lang='fr']/a[@href]", 4); } [Test] @@ -3849,7 +3850,7 @@ public void GenerateContentForEntry_GeneratesCssForConfigTargetsInLexReferences( var result = ((CssGenerator)settings.StylesGenerator).GetStylesString(); - var pattern = @".configtargets>\s*\.configtarget\s*\+\s*\.configtarget:before\s*\{\s*content:\s*';';\s*\}\s*\.configtargets:before\s*\{\s*content:\s*' ';\s*\}\s*\.configtargets:after\s*\{\s*content:\s*'!';\s*\}"; + var pattern = @".configtargets-1>\s*\.configtarget\s*\+\s*\.configtarget:before\s*\{\s*content:\s*';';\s*\}\s*\.lexentry-1\s\.minimallexreferences-1\s\.configtargets-1:before\s*\{\s*content:\s*' ';\s*\}\s*\.lexentry-1\s\.minimallexreferences-1\s\.configtargets-1:after\s*\{\s*content:\s*'!';\s*\}"; CssGeneratorTests.VerifyRegex(result, pattern, "CSS verification failed."); } @@ -3904,7 +3905,7 @@ public void GenerateContentForEntry_GeneratesLinksForCrossReferencesWithReferenc //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( - "//span[@class='configtargets']/span[@class='configtarget']/span[@class='headword']/span[@lang='fr']//a[@href]", 4); + "//span[@class='configtargets']/span[@class='configtarget']/span[@class='headword-1']/span[@lang='fr']//a[@href]", 4); } [Test] @@ -3960,7 +3961,7 @@ public void GenerateContentForEntry_GeneratesCrossReferencesOnUnCheckConfigTarge //SUT- var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); AssertThatXmlIn.String(result).HasNoMatchForXpath( - "//span[@class='minimallexreferences']/span[@class='minimallexreference']/span[@class='configtargets']"); + "//span[@class='minimallexreferences-1']/span[@class='minimallexreference']/span[@class='configtargets']"); } [Test] @@ -4000,9 +4001,9 @@ public void GenerateContentForEntry_GeneratesForwardNameForSymmetricCrossReferen //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(referencedEntry, mainEntryNode, null, settings).ToString(); var fwdNameXpath = string.Format( - "//span[@class='minimallexreferences']/span[@class='minimallexreference']/span[@class='ownertype_name']/span[@lang='en' and text()='{0}']", refTypeName); + "//span[@class='minimallexreferences-1']/span[@class='minimallexreference']/span[@class='ownertype_name-1']/span[@lang='en' and text()='{0}']", refTypeName); const string anyNameXpath = - "//span[@class='minimallexreferences']/span[@class='minimallexreference']/span[@class='ownertype_name']/span[@lang='en']"; + "//span[@class='minimallexreferences-1']/span[@class='minimallexreference']/span[@class='ownertype_name-1']/span[@lang='en']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(fwdNameXpath, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(anyNameXpath, 1); // ensure there are no spurious names } @@ -4045,9 +4046,9 @@ public void GenerateContentForEntry_GeneratesForwardNameForForwardCrossReference //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); var fwdNameXpath = string.Format( - "//span[@class='minimallexreferences']/span[@class='minimallexreference']/span[@class='ownertype_name']/span[@lang='en' and text()='{0}']", refTypeName); + "//span[@class='minimallexreferences-1']/span[@class='minimallexreference']/span[@class='ownertype_name-1']/span[@lang='en' and text()='{0}']", refTypeName); var revNameXpath = string.Format( - "//span[@class='minimallexreferences']/span[@class='minimallexreference']/span[@class='ownertype_name']/span[@lang='en' and text()='{0}']", refTypeRevName); + "//span[@class='minimallexreferences-1']/span[@class='minimallexreference']/span[@class='ownertype_name-1']/span[@lang='en' and text()='{0}']", refTypeRevName); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(fwdNameXpath, 1); AssertThatXmlIn.String(result).HasNoMatchForXpath(revNameXpath); } @@ -4090,9 +4091,9 @@ public void GenerateContentForEntry_GeneratesReverseNameForReverseCrossReference //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(referencedEntry, mainEntryNode, null, settings).ToString(); var fwdNameXpath = string.Format( - "//span[@class='minimallexreferences']/span[@class='minimallexreference']/span[@class='ownertype_name']/span[@lang='en' and text()='{0}']", refTypeName); + "//span[@class='minimallexreferences-1']/span[@class='minimallexreference']/span[@class='ownertype_name-1']/span[@lang='en' and text()='{0}']", refTypeName); var revNameXpath = string.Format( - "//span[@class='minimallexreferences']/span[@class='minimallexreference']/span[@class='ownertype_name']/span[@lang='en' and text()='{0}']", refTypeRevName); + "//span[@class='minimallexreferences-1']/span[@class='minimallexreference']/span[@class='ownertype_name-1']/span[@lang='en' and text()='{0}']", refTypeRevName); AssertThatXmlIn.String(result).HasNoMatchForXpath(fwdNameXpath); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(revNameXpath, 1); } @@ -4140,9 +4141,9 @@ public void GenerateContentForEntry_GeneratesForwardNameForForwardLexicalRelatio //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); var fwdNameXpath = string.Format( - "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='ownertype_name']/span[@lang='en' and text()='{0}']", refTypeName); + "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='ownertype_name-1']/span[@lang='en' and text()='{0}']", refTypeName); var revNameXpath = string.Format( - "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='ownertype_name']/span[@lang='en' and text()='{0}']", refTypeRevName); + "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='ownertype_name-1']/span[@lang='en' and text()='{0}']", refTypeRevName); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(fwdNameXpath, 1); AssertThatXmlIn.String(result).HasNoMatchForXpath(revNameXpath); } @@ -4191,7 +4192,7 @@ public void GenerateContentForEntry_GeneratesLexicalRelationsLabelWithNoRepetiti //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); var fwdNameXpath = string.Format( - "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='ownertype_name']/span[@lang='en' and text()='{0}']", refTypeName); + "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='ownertype_name-1']/span[@lang='en' and text()='{0}']", refTypeName); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(fwdNameXpath, 1); } @@ -4238,9 +4239,9 @@ public void GenerateContentForEntry_GeneratesReverseNameForReverseLexicalRelatio //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(referencedEntry, mainEntryNode, null, settings).ToString(); var fwdNameXpath = string.Format( - "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='ownertype_name']/span[@lang='en' and text()='{0}']", refTypeName); + "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='ownertype_name-1']/span[@lang='en' and text()='{0}']", refTypeName); var revNameXpath = string.Format( - "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='ownertype_name']/span[@lang='en' and text()='{0}']", refTypeRevName); + "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='ownertype_name-1']/span[@lang='en' and text()='{0}']", refTypeRevName); AssertThatXmlIn.String(result).HasNoMatchForXpath(fwdNameXpath); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(revNameXpath, 1); } @@ -4292,7 +4293,7 @@ public void GenerateContentForEntry_LexicalRelationsSortbyNodeOptionsOrder() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); - const string NameXpath = "//span[@class='minimallexreferences']/span[@class='minimallexreference' and position()='{0}']/span[@class='ownertype_name']/span[@lang='en' and text()='{1}']"; + const string NameXpath = "//span[@class='minimallexreferences-1']/span[@class='minimallexreference' and position()='{0}']/span[@class='ownertype_name-1']/span[@lang='en' and text()='{1}']"; var fwdNameFirstXpath = string.Format(NameXpath, "1", etyRefTypeName); var fwdNameSecondXpath = string.Format(NameXpath, "2", comRefTypeName); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(fwdNameFirstXpath, 1); @@ -4372,21 +4373,21 @@ public void GenerateContentForEntry_GeneratesAsymmetricRelationsProperly() //SUT var output = ConfiguredLcmGenerator.GenerateContentForEntry(armEntry, mainEntryNode, null, settings).ToString(); var fwdNameXpath = string.Format( - "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='ownertype_name']/span[@lang='en' and text()='{0}']", refTypeName); + "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='ownertype_name-1']/span[@lang='en' and text()='{0}']", refTypeName); AssertThatXmlIn.String(output).HasNoMatchForXpath(fwdNameXpath); var revNameXpath = string.Format( - "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='ownertype_name']/span[@lang='en' and text()='{0}']", refTypeRevName); + "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='ownertype_name-1']/span[@lang='en' and text()='{0}']", refTypeRevName); AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(revNameXpath, 1); - var badTarget1 = "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='gloss']"; + var badTarget1 = "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='gloss-1']"; AssertThatXmlIn.String(output).HasNoMatchForXpath(badTarget1); var badTarget2 = string.Format( - "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='headword']/span[@lang='fr' and text()='{0}']", secondWord); + "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='headword-1']/span[@lang='fr' and text()='{0}']", secondWord); AssertThatXmlIn.String(output).HasNoMatchForXpath(badTarget2); var badTarget3 = string.Format( - "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='headword']/span[@lang='fr' and text()='{0}']", thirdWord); + "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='headword-1']/span[@lang='fr' and text()='{0}']", thirdWord); AssertThatXmlIn.String(output).HasNoMatchForXpath(badTarget3); var goodTarget = string.Format( - "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='headword']/span[@lang='fr' and text()='{0}']", firstWord); + "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='headword-1']/span[@lang='fr' and text()='{0}']", firstWord); AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(goodTarget, 1); } @@ -4450,9 +4451,9 @@ public void GenerateContentForEntry_GeneratesConfigTargetsForSubSenseProperly() //SUT var output = ConfiguredLcmGenerator.GenerateContentForEntry(firstEntry, mainEntryNode, null, settings).ToString(); var goodTarget = string.Format( - "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='headword']/span[@lang='fr' and text()='{0}']", firstHeadword); + "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='headword-1']/span[@lang='fr' and text()='{0}']", firstHeadword); AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(goodTarget, 1); - var badTarget = "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='gloss']"; + var badTarget = "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='gloss-1']"; AssertThatXmlIn.String(output).HasNoMatchForXpath(badTarget); } @@ -4519,13 +4520,13 @@ public void GenerateContentForEntry_GeneratesConfigTargetsForTreeBetweenSenses() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(firstEntry, mainEntryNode, DefaultDecorator, settings).ToString(); - var goodTarget1 = "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='ownertype_name']/span[text()='Part']/ancestor::span[1]/following-sibling::node()//span[@class='configtarget']/span[@class='gloss']/span[@lang='en' and text()='b2']"; + var goodTarget1 = "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='ownertype_name-1']/span[text()='Part']/ancestor::span[1]/following-sibling::node()//span[@class='configtarget']/span[@class='gloss-1']/span[@lang='en' and text()='b2']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(goodTarget1, 1); - var badTarget1 = "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='ownertype_name']/span[text()='Part']/ancestor::span[1]/following-sibling::node()//span[@class='configtarget']/span[@class='gloss']/span[@lang='en' and text()='b1']"; + var badTarget1 = "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='ownertype_name-1']/span[text()='Part']/ancestor::span[1]/following-sibling::node()//span[@class='configtarget']/span[@class='gloss-1']/span[@lang='en' and text()='b1']"; AssertThatXmlIn.String(result).HasNoMatchForXpath(badTarget1); - var goodTarget2 = "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='ownertype_name']/span[text()='Whole']/ancestor::span[1]/following-sibling::node()//span[@class='configtarget']/span[@class='gloss']/span[@lang='en' and text()='b1']"; + var goodTarget2 = "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='ownertype_name-1']/span[text()='Whole']/ancestor::span[1]/following-sibling::node()//span[@class='configtarget']/span[@class='gloss-1']/span[@lang='en' and text()='b1']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(goodTarget2, 1); - var badTarget2 = "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='ownertype_name']/span[text()='Whole']/ancestor::span[1]/following-sibling::node()//span[@class='configtarget']/span[@class='gloss']/span[@lang='en' and text()='b2']"; + var badTarget2 = "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='ownertype_name-1']/span[text()='Whole']/ancestor::span[1]/following-sibling::node()//span[@class='configtarget']/span[@class='gloss-1']/span[@lang='en' and text()='b2']"; AssertThatXmlIn.String(result).HasNoMatchForXpath(badTarget2); } @@ -4952,7 +4953,7 @@ public void GenerateContentForEntry_NoncheckedListItemsAreNotGenerated() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( - "/div[@class='lexentry']/span[@class='variantformentrybackrefs']/span[@class='variantformentrybackref']/span[@lang='fr']", 0); + "/div[@class='lexentry-1']/span[@class='variantformentrybackrefs-1']/span[@class='variantformentrybackref']/span[@lang='fr']", 0); } [Test] @@ -5003,7 +5004,7 @@ public void GenerateContentForEntry_CheckedListItemsAreGenerated() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( - "/div[@class='lexentry']/span[@class='variantformentrybackrefs']/span[@class='variantformentrybackref']//span[@lang='fr']/span[@lang='fr']", 2); + "/div[@class='lexentry-1']/span[@class='variantformentrybackrefs-1']/span[@class='variantformentrybackref']//span[@lang='fr']/span[@lang='fr']", 2); } [Test] @@ -5060,8 +5061,8 @@ public void GenerateContentForEntry_VariantTypeIsUncheckedAndHeadwordIsChecked() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( - "/div[@class='lexentry']/span[@class='variantformentrybackrefs']/span[@class='variantformentrybackref']/span[@class='referencedentries']" + - "/span[@class='referencedentry']/span[@class='headword']/span[@lang='fr']/span[@lang='fr' and text()='Citation']", 1); + "/div[@class='lexentry-1']/span[@class='variantformentrybackrefs-1']/span[@class='variantformentrybackref']/span[@class='referencedentries-1']" + + "/span[@class='referencedentry']/span[@class='headword-1']/span[@lang='fr']/span[@lang='fr' and text()='Citation']", 1); } [Test] @@ -5102,7 +5103,7 @@ public void GenerateContentForEntry_ReferencedComplexFormsUnderSensesIncludesSub //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( - xpathThruSense + "/span[@class='visiblecomplexformbackrefs']/span[@class='visiblecomplexformbackref']//span[@lang='fr']/span[@lang='fr']", 4); + xpathThruSense + "/span[@class='visiblecomplexformbackrefs-1']/span[@class='visiblecomplexformbackref']//span[@lang='fr']/span[@lang='fr']", 4); } [Test] @@ -5304,8 +5305,8 @@ public void GenerateContentForEntry_OneSenseWithSinglePicture() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); - const string oneSenseWithPicture = "/div[@class='lexentry']/span[@class='pictures']/div[@class='picture']/img[@class='photo' and @id]"; - const string oneSenseWithPictureCaption = "/div[@class='lexentry']/span[@class='pictures']/div[@class='picture']/div[@class='captionContent']/span[@class='caption']//span[text()='caption']"; + const string oneSenseWithPicture = "/div[@class='lexentry-1']/span[@class='pictures-1']/div[@class='picture']/img[@class='photo-1' and @id]"; + const string oneSenseWithPictureCaption = "/div[@class='lexentry-1']/span[@class='pictures-1']/div[@class='picture']/div[@class='captionContent']/span[@class='caption-1']//span[text()='caption']"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(oneSenseWithPicture, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(oneSenseWithPictureCaption, 1); @@ -5411,8 +5412,8 @@ public void GenerateContentForEntry_PictureWithCreator() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); - const string oneSenseWithPicture = "/div[@class='lexentry']/span[@class='pictures']/div[@class='picture']/img[@class='photo' and @id]"; - const string oneSenseWithPictureCaption = "/div[@class='lexentry']/span[@class='pictures']/div[@class='picture']/div[@class='captionContent']/span[@class='creator' and text()='Jason Naylor']"; + const string oneSenseWithPicture = "/div[@class='lexentry-1']/span[@class='pictures-1']/div[@class='picture']/img[@class='photo-1' and @id]"; + const string oneSenseWithPictureCaption = "/div[@class='lexentry-1']/span[@class='pictures-1']/div[@class='picture']/div[@class='captionContent']/span[@class='creator-1' and text()='Jason Naylor']"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(oneSenseWithPicture, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(oneSenseWithPictureCaption, 1); @@ -5443,7 +5444,7 @@ public void GenerateContentForEntry_PictureWithNonUnicodePathLinksCorrectly() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); - var pictureWithComposedPath = "/div[@class='lexentry']/span[@class='pictures']/span[@class='picture']/img[contains(@src, '" + composedPath + "')]"; + var pictureWithComposedPath = "/div[@class='lexentry-1']/span[@class='pictures-1']/span[@class='picture']/img[contains(@src, '" + composedPath + "')]"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(pictureWithComposedPath, 1); } @@ -5472,7 +5473,7 @@ public void GenerateContentForEntry_PictureCopiedAndRelativePathUsed() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); var pictureRelativePath = Path.Combine("pictures", Path.GetFileName(filePath)); - var pictureWithComposedPath = "/div[@class='lexentry']/span[@class='pictures']/span[@class='picture']/img[starts-with(@src, '" + pictureRelativePath + "')]"; + var pictureWithComposedPath = "/div[@class='lexentry-1']/span[@class='pictures-1']/span[@class='picture']/img[starts-with(@src, '" + pictureRelativePath + "')]"; if (!Platform.IsUnix) AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(pictureWithComposedPath, 1); // that src starts with a string, and escaping any Windows path separators @@ -5509,7 +5510,7 @@ public void GenerateContentForEntry_MissingPictureFileDoesNotCrashOnCopy() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); var pictureRelativePath = Path.Combine("pictures", Path.GetFileName(filePath)); - var pictureWithComposedPath = "/div[@class='lexentry']/span[@class='pictures']/span[@class='picture']/img[starts-with(@src, '" + pictureRelativePath + "')]"; + var pictureWithComposedPath = "/div[@class='lexentry-1']/span[@class='pictures-1']/span[@class='picture']/img[starts-with(@src, '" + pictureRelativePath + "')]"; if (!Platform.IsUnix) AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(pictureWithComposedPath, 1); // that src starts with a string, and escaping any Windows path separators @@ -5564,14 +5565,14 @@ public void GenerateContentForEntry_TwoDifferentFilesGetTwoDifferentResults() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); var pictureRelativePath = Path.Combine("pictures", Path.GetFileName(fileName)); - var pictureWithComposedPath = "/div[@class='lexentry']/span[@class='pictures']/span[@class='picture']/img[contains(@src, '" + pictureRelativePath + "')]"; + var pictureWithComposedPath = "/div[@class='lexentry-1']/span[@class='pictures-1']/span[@class='picture']/img[contains(@src, '" + pictureRelativePath + "')]"; if (!Platform.IsUnix) AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(pictureWithComposedPath, 1); // that src contains a string, and escaping any Windows path separators AssertRegex(result, string.Format("src=\"[^\"]*{0}[^\"]*\"", pictureRelativePath.Replace(@"\", @"\\")), 1); // The second file with the same name should have had something appended to the end of the filename but the initial filename should match both entries var filenameWithoutExtension = Path.GetFileNameWithoutExtension(pictureRelativePath); - var pictureStartsWith = "/div[@class='lexentry']/span[@class='pictures']/span[@class='picture']/img[contains(@src, '" + filenameWithoutExtension + "')]"; + var pictureStartsWith = "/div[@class='lexentry-1']/span[@class='pictures-1']/span[@class='picture']/img[contains(@src, '" + filenameWithoutExtension + "')]"; if (!Platform.IsUnix) AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(pictureStartsWith, 2); // that src contains a string @@ -5619,7 +5620,7 @@ public void GenerateContentForEntry_UniqueIdsForSameFile() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); var pictureRelativePath = Path.Combine("pictures", Path.GetFileName(fileName)); - const string pictureXPath = "/div[@class='lexentry']/span[@class='pictures']/span[@class='picture']/img"; + const string pictureXPath = "/div[@class='lexentry-1']/span[@class='pictures-1']/span[@class='picture']/img"; var pictureWithComposedPath = pictureXPath + "[contains(@src, '" + pictureRelativePath + "')]"; if (!Platform.IsUnix) AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(pictureWithComposedPath, 2); @@ -5761,7 +5762,7 @@ public void GenerateContentForEntry_TwoDifferentLinksToTheSamefileWorks() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); var pictureRelativePath = Path.Combine("pictures", Path.GetFileName(fileName)); - var pictureWithComposedPath = "/div[@class='lexentry']/span[@class='pictures']/span[@class='picture']/img[contains(@src, '" + pictureRelativePath + "')]"; + var pictureWithComposedPath = "/div[@class='lexentry-1']/span[@class='pictures-1']/span[@class='picture']/img[contains(@src, '" + pictureRelativePath + "')]"; if (!Platform.IsUnix) AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(pictureWithComposedPath, 2); // that src starts with string, and escaping Windows directory separators @@ -5802,7 +5803,7 @@ public void GenerateContentForEntry_StringCustomFieldGeneratesContent() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); - var customDataPath = string.Format("/div[@class='lexentry']/span[@class='customstring']/span[text()='{0}']", customData); + var customDataPath = string.Format("/div[@class='lexentry-1']/span[@class='customstring-1']/span[text()='{0}']", customData); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); } } @@ -5839,7 +5840,7 @@ public void GenerateContentForEntry_CustomFieldInGroupingNodeGeneratesContent() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); - var customDataPath = $"/div[@class='lexentry']/span[@class='grouping_customgroup']/span[@class='customstring']/span[text()='" + customData + "']"; + var customDataPath = $"/div[@class='lexentry-1']/span[@class='grouping_customgroup-1']/span[@class='customstring-1']/span[text()='" + customData + "']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); } } @@ -5888,8 +5889,8 @@ public void GenerateContentForEntry_CustomFieldInNestedGroupingNodeGeneratesCont var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); - const string grpXPath = "/span[@class='grouping_customgroup']"; - var customDataPath = $"/div[@class='lexentry']{grpXPath}{grpXPath}{grpXPath}/span[@class='customstring']/span[text()='{customData}']"; + const string grpXPath = "/span[@class='grouping_customgroup-1']/span[@class='grouping_customgroup-2']/span[@class='grouping_customgroup-3']"; + var customDataPath = $"/div[@class='lexentry-1']{grpXPath}/span[@class='customstring-1']/span[text()='{customData}']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); } } @@ -5956,7 +5957,7 @@ public void GenerateContentForEntry_StringCustomFieldOnSenseGeneratesContent() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); - var customDataPath = string.Format("/div[@class='l']/span[@class='es']/span[@class='e']/span[@class='customstring']/span[text()='{0}']", customData); + var customDataPath = string.Format("/div[@class='l-1']/span[@class='es-1']/span[@class='e']/span[@class='customstring-1']/span[text()='{0}']", customData); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); } } @@ -6003,7 +6004,7 @@ public void GenerateContentForEntry_StringCustomFieldOnExampleGeneratesContent() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); var customDataPath = string.Format( - "/div[@class='l']/span[@class='es']//span[@class='xs']/span[@class='x']/span[@class='customstring']/span[text()='{0}']", customData); + "/div[@class='l-1']/span[@class='es-1']//span[@class='xs-1']/span[@class='x']/span[@class='customstring-1']/span[text()='{0}']", customData); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); } } @@ -6042,7 +6043,7 @@ public void GenerateContentForEntry_StringCustomFieldOnAllomorphGeneratesContent //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); var customDataPath = string.Format( - "/div[@class='l']/span[@class='as']/span[@class='a']/span[@class='customstring']/span[text()='{0}']", customData); + "/div[@class='l-1']/span[@class='as-1']/span[@class='a']/span[@class='customstring-1']/span[text()='{0}']", customData); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); } } @@ -6073,7 +6074,7 @@ public void GenerateContentForEntry_MultiStringCustomFieldGeneratesContent() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); - var customDataPath = string.Format("/div[@class='lexentry']/span[@class='customstring']/span[text()='{0}']", customData); + var customDataPath = string.Format("/div[@class='lexentry-1']/span[@class='customstring-1']/span[text()='{0}']", customData); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); } } @@ -6135,9 +6136,9 @@ public void GenerateContentForEntry_CustomFieldOnISenseOrEntryGeneratesContentFo var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); - var entryDataPath = string.Format("/div[@class='lexentry']/span[@class='mlrs']/span[@class='mlr']/span[@class='configtargets']/span[@class='configtarget']/span[@class='entrycstring']/span[text()='{0}']", entryCustomData); + var entryDataPath = string.Format("/div[@class='lexentry-1']/span[@class='mlrs-1']/span[@class='mlr']/span[@class='configtargets']/span[@class='configtarget']/span[@class='entrycstring-1']/span[text()='{0}']", entryCustomData); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(entryDataPath, 1); - var senseDataPath = string.Format("/div[@class='lexentry']/span[@class='mlrs']/span[@class='mlr']/span[@class='configtargets']/span[@class='configtarget']/span[@class='sensecstring']/span[text()='{0}']", senseCustomData); + var senseDataPath = string.Format("/div[@class='lexentry-1']/span[@class='mlrs-1']/span[@class='mlr']/span[@class='configtargets']/span[@class='configtarget']/span[@class='sensecstring']/span[text()='{0}']", senseCustomData); AssertThatXmlIn.String(result).HasNoMatchForXpath(senseDataPath, message: "Ref is to Entry; should be no Sense Custom Data"); } } @@ -6199,9 +6200,9 @@ public void GenerateContentForEntry_CustomFieldOnISenseOrEntryGeneratesContentFo var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); - var entryDataPath = string.Format("/div[@class='lexentry']/span[@class='mlrs']/span[@class='mlr']/span[@class='configtargets']/span[@class='configtarget']/span[@class='entrycstring']/span[text()='{0}']", entryCustomData); + var entryDataPath = string.Format("/div[@class='lexentry-1']/span[@class='mlrs-1']/span[@class='mlr']/span[@class='configtargets']/span[@class='configtarget']/span[@class='entrycstring-1']/span[text()='{0}']", entryCustomData); AssertThatXmlIn.String(result).HasNoMatchForXpath(entryDataPath, message: "Ref is to Sense; should be no Entry Custom Data"); - var senseDataPath = string.Format("/div[@class='lexentry']/span[@class='mlrs']/span[@class='mlr']/span[@class='configtargets']/span[@class='configtarget']/span[@class='sensecstring']/span[text()='{0}']", senseCustomData); + var senseDataPath = string.Format("/div[@class='lexentry-1']/span[@class='mlrs-1']/span[@class='mlr']/span[@class='configtargets']/span[@class='configtarget']/span[@class='sensecstring-1']/span[text()='{0}']", senseCustomData); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseDataPath, 1); } } @@ -6239,7 +6240,7 @@ public void GenerateContentForEntry_CustomFieldOnRefdLexEntryGeneratesContent() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); - var customDataPath = string.Format("/div[@class='lexentry']/span[@class='vars']/span[@class='var']/span[@class='owningentry_customstring']/span[text()='{0}']", customData); + var customDataPath = string.Format("/div[@class='lexentry-1']/span[@class='vars-1']/span[@class='var']/span[@class='owningentry_customstring-1']/span[text()='{0}']", customData); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); } } @@ -6275,7 +6276,7 @@ public void GenerateContentForEntry_MultiStringDefinition_GeneratesMultilingualS var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); - var definitionXpath = "//div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='definition']/span[@lang='en']"; + var definitionXpath = "//div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='definition-1']/span[@lang='en']"; var str1Xpath = string.Format(definitionXpath + "/span[@lang='en' and text()='{0}']", multirunContent[0]); var str2Xpath = string.Format(definitionXpath + "/span[@lang='fr' and text()='{0}']", multirunContent[1]); var str3Xpath = string.Format(definitionXpath + "/span[@lang='en' and text()='{0}']", multirunContent[2]); @@ -6323,7 +6324,7 @@ public void GenerateContentForEntry_ListItemCustomFieldGeneratesContent() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); - const string customDataPath = "/div[@class='lexentry']/span[@class='customlistitem']/span[@class='name']/span[text()='Djbuti']"; + const string customDataPath = "/div[@class='lexentry-1']/span[@class='customlistitem-1']/span[@class='name-1']/span[text()='Djbuti']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); } } @@ -6364,8 +6365,8 @@ public void GenerateContentForEntry_MultiListItemCustomFieldGeneratesContent() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); - const string customDataPath1 = "/div[@class='lexentry']/span[@class='customlistitems']/span[@class='customlistitem']/span[@class='name']/span[text()='Dallas']"; - const string customDataPath2 = "/div[@class='lexentry']/span[@class='customlistitems']/span[@class='customlistitem']/span[@class='name']/span[text()='Barcelona']"; + const string customDataPath1 = "/div[@class='lexentry-1']/span[@class='customlistitems-1']/span[@class='customlistitem']/span[@class='name-1']/span[text()='Dallas']"; + const string customDataPath2 = "/div[@class='lexentry-1']/span[@class='customlistitems-1']/span[@class='customlistitem']/span[@class='name-1']/span[text()='Barcelona']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath1, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath2, 1); } @@ -6396,7 +6397,7 @@ public void GenerateContentForEntry_DateCustomFieldGeneratesContent() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); - var customDataPath = string.Format("/div[@class='lexentry']/span[@class='customdate' and text()='{0}']", customData.ToLongDateString()); + var customDataPath = string.Format("/div[@class='lexentry-1']/span[@class='customdate-1' and text()='{0}']", customData.ToLongDateString()); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); } } @@ -6426,7 +6427,7 @@ public void GenerateContentForEntry_IntegerCustomFieldGeneratesContent() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); - var customDataPath = string.Format("/div[@class='lexentry']/span[@class='custominteger' and text()='{0}']", customData); + var customDataPath = string.Format("/div[@class='lexentry-1']/span[@class='custominteger-1' and text()='{0}']", customData); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); } } @@ -6458,7 +6459,7 @@ public void GenerateContentForEntry_MultiLineCustomFieldGeneratesContent() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, rootNode, null, settings).ToString(); const string customDataPath = - "/div[@class='lexentry']/div/span[text()='First para Custom string'] | /div[@class='lexentry']/div/span[text()='Second para Custom string']"; + "/div[@class='lexentry-1']/div/span[text()='First para Custom string'] | /div[@class='lexentry-1']/div/span[text()='Second para Custom string']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 2); } } @@ -6496,7 +6497,7 @@ public void GenerateContentForEntry_VariantOfReferencedHeadWord() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); const string referencedEntries = - "//span[@class='visiblevariantentryrefs']/span[@class='visiblevariantentryref']/span[@class='referencedentries']/span[@class='referencedentry']/span[@class='headword']/span[@lang='fr']/span[@lang='fr']"; + "//span[@class='visiblevariantentryrefs-1']/span[@class='visiblevariantentryref']/span[@class='referencedentries-1']/span[@class='referencedentry']/span[@class='headword-1']/span[@lang='fr']/span[@lang='fr']"; AssertThatXmlIn.String(result) .HasSpecifiedNumberOfMatchesForXpath(referencedEntries, 2); } @@ -6534,7 +6535,7 @@ public void GenerateContentForEntry_WsAudiowithHyperlink() @"Src/xWorks/xWorksTests/TestData/LinkedFiles/AudioVisual/" + audioFileName; Assert.That(result, Contains.Substring(audioFileUrl)); const string linkTagwithOnClick = - "//span[@class='lexemeformoa']/span/a[@class='en-Zxxx-x-audio' and contains(@onclick,'play()')]"; + "//span[@class='lexemeformoa-1']/span/a[@class='en-Zxxx-x-audio' and contains(@onclick,'play()')]"; AssertThatXmlIn.String(result) .HasSpecifiedNumberOfMatchesForXpath(linkTagwithOnClick, 1); } @@ -6843,7 +6844,7 @@ public void GenerateContentForEntry_WsAudiowithRelativePaths() AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(audioTagwithSource, 1); var audioFileUrl = Path.Combine("AudioVisual", audioFileName); Assert.That(result, Contains.Substring(audioFileUrl)); - var linkTagwithOnClick = "//span[@class='lexemeformoa']/span/a[@class='en-Zxxx-x-audio'"; + var linkTagwithOnClick = "//span[@class='lexemeformoa-1']/span/a[@class='en-Zxxx-x-audio'"; linkTagwithOnClick += " and @href='#" + safeAudioId + "'"; linkTagwithOnClick += " and contains(@onclick,'" + safeAudioId + "') and contains(@onclick,'.play()')]"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(linkTagwithOnClick, 1); @@ -6973,10 +6974,10 @@ public void GenerateContentForEntry_GeneratesComplexFormTypeForSubentryUnderSens //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings).ToString(); var fwdNameXpath = string.Format( - "//span[@class='sense']/span[@class='subentries']/span[@class='subentry']/span[@class='complexformtypes']/span[@class='complexformtype']/span/span[@lang='en' and text()='{0}']", + "//span[@class='sense']/span[@class='subentries-1']/span[@class='subentry']/span[@class='complexformtypes-1']/span[@class='complexformtype']/span/span[@lang='en' and text()='{0}']", complexRefAbbr); var revNameXpath = string.Format( - "//span[@class='sense']/span[@class='subentries']/span[@class='subentry']/span[@class='complexformtypes']/span[@class='complexformtype']/span[@class='reverseabbr']/span[@lang='en' and text()='{0}']", + "//span[@class='sense']/span[@class='subentries-1']/span[@class='subentry']/span[@class='complexformtypes-1']/span[@class='complexformtype']/span[@class='reverseabbr-1']/span[@lang='en' and text()='{0}']", complexRefRevAbbr); AssertThatXmlIn.String(result).HasNoMatchForXpath(fwdNameXpath); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(revNameXpath, 1); @@ -7021,10 +7022,10 @@ public void GenerateContentForEntry_GeneratesComplexFormTypeForSubentry() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings).ToString(); var fwdNameXpath = string.Format( - "//span[@class='subentries']/span[@class='subentry']/span[@class='complexformtypes']/span[@class='complexformtype']/span/span[@lang='en' and text()='{0}']", + "//span[@class='subentries-1']/span[@class='subentry']/span[@class='complexformtypes-1']/span[@class='complexformtype']/span/span[@lang='en' and text()='{0}']", complexRefAbbr); var revNameXpath = string.Format( - "//span[@class='subentries']/span[@class='subentry']/span[@class='complexformtypes']/span[@class='complexformtype']/span[@class='reverseabbr']/span[@lang='en' and text()='{0}']", + "//span[@class='subentries-1']/span[@class='subentry']/span[@class='complexformtypes-1']/span[@class='complexformtype']/span[@class='reverseabbr-1']/span[@lang='en' and text()='{0}']", complexRefRevAbbr); AssertThatXmlIn.String(result).HasNoMatchForXpath(fwdNameXpath); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(revNameXpath, 1); @@ -7092,11 +7093,11 @@ public void GenerateContentForEntry_GeneratesComplexFormTypeForSubsubentry([Valu //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings).ToString(); const string fwdNameXpath = - "//span[@class='subentries']/span[@class='subentry subentry']/span[@class='subentries']/span[@class='subentry subentry']" - + "/span[@class='complexformtypes']/span[@class='complexformtype']/span/span[@lang='en' and text()='{0}']"; + "//span[@class='subentries-1']/span[@class='subentry subentry']/span[@class='subentries-3']/span[@class='subentry subentry']" + + "/span[@class='complexformtypes-1']/span[@class='complexformtype']/span/span[@lang='en' and text()='{0}']"; const string revNameXpath = - "//span[@class='subentries']/span[@class='subentry subentry']/span[@class='subentries']/span[@class='subentry subentry']" - + "/span[@class='complexformtypes']/span[@class='complexformtype']/span[@class='reverseabbr']/span[@lang='en' and text()='{0}']"; + "//span[@class='subentries-1']/span[@class='subentry subentry']/span[@class='subentries-3']/span[@class='subentry subentry']" + + "/span[@class='complexformtypes-1']/span[@class='complexformtype']/span[@class='reverseabbr-1']/span[@lang='en' and text()='{0}']"; AssertThatXmlIn.String(result).HasNoMatchForXpath(string.Format(fwdNameXpath, complexRefAbbr)); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(string.Format(revNameXpath, complexRefRevAbbr), 1); AssertThatXmlIn.String(result).HasNoMatchForXpath(string.Format(revNameXpath, otherComplexRefRevAbbr), @@ -7152,7 +7153,7 @@ public void GenerateContentForEntry_DoesntGeneratesComplexFormType_WhenDisabled( var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings).ToString(); - const string refTypeXpath = "//span[@class='subentries']/span[@class='subentry']/span[@class='complexformtypes']"; + const string refTypeXpath = "//span[@class='subentries-1']/span[@class='subentry']/span[@class='complexformtypes-1']"; AssertThatXmlIn.String(result).HasNoMatchForXpath(refTypeXpath); StringAssert.DoesNotContain(complexRefAbbr, result); } @@ -7239,9 +7240,9 @@ public void GenerateContentForEntry_GeneratesComplexForm_NoTypeSpecified() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings).ToString(); - const string refTypeXpath = "//span[@class='visiblecomplexformbackrefs']/span[@class='visiblecomplexformbackref']/span[@class='complexformtypes']/span[@class='complexformtype']"; + const string refTypeXpath = "//span[@class='visiblecomplexformbackrefs-1']/span[@class='visiblecomplexformbackref']/span[@class='complexformtypes-1']/span[@class='complexformtype']"; AssertThatXmlIn.String(result).HasNoMatchForXpath(refTypeXpath); - const string headwordXpath = "//span[@class='visiblecomplexformbackrefs']/span[@class='visiblecomplexformbackref']/span[@class='headword']"; + const string headwordXpath = "//span[@class='visiblecomplexformbackrefs-1']/span[@class='visiblecomplexformbackref']/span[@class='headword-1']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(headwordXpath, 1); } @@ -7281,9 +7282,9 @@ public void GenerateContentForEntry_GeneratesSubentry_NoTypeSpecified() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings).ToString(); - const string refTypeXpath = "//span[@class='subentries']/span[@class='subentry']/span[@class='complexformtypes']/span[@class='complexformtype']"; + const string refTypeXpath = "//span[@class='subentries-1']/span[@class='subentry']/span[@class='complexformtypes-1']/span[@class='complexformtype']"; AssertThatXmlIn.String(result).HasNoMatchForXpath(refTypeXpath); - const string headwordXpath = "//span[@class='subentries']/span[@class='subentry']/span[@class='headword']"; + const string headwordXpath = "//span[@class='subentries-1']/span[@class='subentry']/span[@class='headword-1']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(headwordXpath, 1); } @@ -7321,8 +7322,8 @@ public void GenerateContentForEntry_ComplexFormDontGenerateReference() CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); subentryRef.HideMinorEntry = 1; - const string withReference = "/div[@class='lexentry']/span[@class='subentries']/span[@class='subentry']/span[@class='headword']/span[@lang='fr']/span[@lang='fr']/a[@href]"; - const string withoutReference = "/div[@class='lexentry']/span[@class='subentries']/span[@class='subentry']/span[@class='headword']/span[@lang='fr']/span[@lang='fr']"; + const string withReference = "/div[@class='lexentry-1']/span[@class='subentries-1']/span[@class='subentry']/span[@class='headword-1']/span[@lang='fr']/span[@lang='fr']/a[@href]"; + const string withoutReference = "/div[@class='lexentry-1']/span[@class='subentries-1']/span[@class='subentry']/span[@class='headword-1']/span[@lang='fr']/span[@lang='fr']"; // When hiding minor entries this should still generate the reference (if not publishing to Webonary). var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null, false, false); @@ -7426,9 +7427,9 @@ public void GenerateContentForEntry_GeneratesVariant_NoTypeSpecified() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings).ToString(); - const string refTypeXpath = "//span[@class='variantformentrybackrefs']/span[@class='variantformentrybackref']/span[@class='variantentrytypesrs']/span[@class='variantentrytypesr']"; + const string refTypeXpath = "//span[@class='variantformentrybackrefs-1']/span[@class='variantformentrybackref']/span[@class='variantentrytypesrs']/span[@class='variantentrytypesr']"; AssertThatXmlIn.String(result).HasNoMatchForXpath(refTypeXpath); - const string headwordXpath = "//span[@class='variantformentrybackrefs']/span[@class='variantformentrybackref']/span[@class='headword']"; + const string headwordXpath = "//span[@class='variantformentrybackrefs-1']/span[@class='variantformentrybackref']/span[@class='headword-1']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(headwordXpath, 1); } @@ -7468,11 +7469,11 @@ public void GenerateContentForEntry_VariantShowsIfNotHideMinorEntry_ViewDoesntMa // try with HideMinorEntry off variantEntryRef.HideMinorEntry = 0; result = ConfiguredLcmGenerator.GenerateContentForEntry(variantEntry, model, null, settings).ToString(); - AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath("/div[@class='lexentry']/span[@class='headword']", 1); + AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath("/div[@class='lexentry-1']/span[@class='headword-1']", 1); // Should get the same results if in Root based view model.IsRootBased = true; result = ConfiguredLcmGenerator.GenerateContentForEntry(variantEntry, model, null, settings).ToString(); - AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath("/div[@class='lexentry']/span[@class='headword']", 1); + AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath("/div[@class='lexentry-1']/span[@class='headword-1']", 1); variantEntryRef.HideMinorEntry = 1; result = ConfiguredLcmGenerator.GenerateContentForEntry(variantEntry, model, null, settings).ToString(); Assert.IsEmpty(result); @@ -7527,7 +7528,7 @@ public void GenerateContentForEntry_VariantGenerateReferenceForHiddenEntry() CssGeneratorTests.PopulateFieldsForTesting(model); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null, false, false); - const string withReference = "/div[@class='lexentry']/span[@class='variantformentrybackrefs']/span[@class='variantformentrybackref']/span[@class='headword']/span[@lang='fr']/span[@lang='fr']/a[@href]"; + const string withReference = "/div[@class='lexentry-1']/span[@class='variantformentrybackrefs-1']/span[@class='variantformentrybackref']/span[@class='headword-1']/span[@lang='fr']/span[@lang='fr']/a[@href]"; // When not hiding minor entries this should generate the reference. variantEntryRef.HideMinorEntry = 0; @@ -7590,8 +7591,8 @@ public void GenerateContentForEntry_VariantDontGenerateReference() CssGeneratorTests.PopulateFieldsForTesting(model); variantEntryRef.HideMinorEntry = 1; - const string withReference = "/div[@class='lexentry']/span[@class='variantformentrybackrefs']/span[@class='variantformentrybackref']/span[@class='headword']/span[@lang='fr']/span[@lang='fr']/a[@href]"; - const string withoutReference = "/div[@class='lexentry']/span[@class='variantformentrybackrefs']/span[@class='variantformentrybackref']/span[@class='headword']/span[@lang='fr']/span[@lang='fr']"; + const string withReference = "/div[@class='lexentry-1']/span[@class='variantformentrybackrefs-1']/span[@class='variantformentrybackref']/span[@class='headword-1']/span[@lang='fr']/span[@lang='fr']/a[@href]"; + const string withoutReference = "/div[@class='lexentry-1']/span[@class='variantformentrybackrefs-1']/span[@class='variantformentrybackref']/span[@class='headword-1']/span[@lang='fr']/span[@lang='fr']"; // When hiding minor entries this should still generate the reference (if not publishing to Webonary). var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null, false, false); @@ -7645,7 +7646,7 @@ public void GenerateContentForEntry_ReferencedNode_GeneratesBothClasses() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings).ToString(); - const string headwordXpath = "//span[@class='reffingsubs']/span[@class='reffingsub sharedsubentry']/span[@class='headword']"; + const string headwordXpath = "//span[@class='reffingsubs-1']/span[@class='reffingsub sharedsubentry']/span[@class='headword-1']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(headwordXpath, 1); } @@ -7679,7 +7680,7 @@ public void GenerateContentForEntry_GeneratesCorrectMainAndMinorEntries() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForMainEntry(idiom, mainEntryNode, null, settings, 0).ToString(); - AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath("/div[@class='lexentry']/span[@class='headword']", 1); + AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath("/div[@class='lexentry-1']/span[@class='headword-1']", 1); var css = ((CssGenerator)settings.StylesGenerator).GetStylesString(); // verify that the flow reset css is generated Assert.That(css, Contains.Substring("white-space:pre-wrap")); @@ -7757,7 +7758,7 @@ public void GenerateContentForEntry_GeneratesCorrectMinorEntries( var isMinorEntryShowing = isComplexFormShowing || isVariantFormShowing; if (isMinorEntryShowing) - AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath("/div[@class='lexentry']/span[@class='headword']", 1); + AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath("/div[@class='lexentry-1']/span[@class='headword-1']", 1); else Assert.IsEmpty(result); } @@ -7984,15 +7985,15 @@ public void GenerateContentForEntry_FilterByPublication() }; CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); - const string matchFrenchEntry = "//span[@class='entry']/span[@lang='fr']"; - const string matchFrenchPronunciation = "//span[@class='pronunciations']/span[@class='pronunciation']/span[@class='form']/span[@lang='fr']"; + const string matchFrenchEntry = "//span[@class='entry-1']/span[@lang='fr']"; + const string matchFrenchPronunciation = "//span[@class='pronunciations-1']/span[@class='pronunciation']/span[@class='form-1']/span[@lang='fr']"; const string matchEnglishDefOrGloss = - "//span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='definitionorgloss']/span[@lang='en']"; + "//span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='definitionorgloss-1']/span[@lang='en']"; const string matchFrenchExample = - "//span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='examples']/span[@class='example']/span[@class='examplesentence']/span[@lang='fr']"; + "//span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='examples-1']/span[@class='example']/span[@class='examplesentence-1']/span[@lang='fr']"; const string matchEnglishTranslation = - "//span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='examples']/span[@class='example']/span[@class='translations']/span[@class='translation']/span[@class='translatedsentence']/span[@lang='en']"; - const string matchFrenchPictureCaption = "//span[@class='pictures']/div[@class='picture']/div[@class='captionContent']/span[@class='caption']/span[@lang='fr']"; + "//span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='examples-1']/span[@class='example']/span[@class='translations-1']/span[@class='translation']/span[@class='translatedsentence-1']/span[@lang='en']"; + const string matchFrenchPictureCaption = "//span[@class='pictures-1']/div[@class='picture']/div[@class='captionContent']/span[@class='caption-1']/span[@lang='fr']"; var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT @@ -8018,7 +8019,7 @@ public void GenerateContentForEntry_FilterByPublication() AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchEnglishTranslation, 1); AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchFrenchPictureCaption, 1); const string matchBodyIsBig = - "//span[@class='examples']/span[@class='example']/span[@class='translations']/span[@class='translation']/span[@class='translatedsentence']/span[@lang='en' and text()='The body is big.']"; + "//span[@class='examples-1']/span[@class='example']/span[@class='translations-1']/span[@class='translation']/span[@class='translatedsentence-1']/span[@lang='en' and text()='The body is big.']"; AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchBodyIsBig, 1); //SUT @@ -8032,7 +8033,7 @@ public void GenerateContentForEntry_FilterByPublication() AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchEnglishTranslation, 1); AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchFrenchPictureCaption, 0); const string matchCorpseIsDead = - "//span[@class='examples']/span[@class='example']/span[@class='translations']/span[@class='translation']/span[@class='translatedsentence']/span[@lang='en' and text()='The corpse is dead.']"; + "//span[@class='examples-1']/span[@class='example']/span[@class='translations-1']/span[@class='translation']/span[@class='translatedsentence-1']/span[@lang='en' and text()='The corpse is dead.']"; AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchCorpseIsDead, 1); //SUT @@ -8093,10 +8094,10 @@ public void GenerateContentForEntry_FilterByPublication() AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchFrenchExample, 1); AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchEnglishTranslation, 1); - const string matchFrenchSubentry = "//span[@class='subentries']/span[@class='subentry']/span[@class='subentry']/span[@lang='fr']"; - const string matchMainsubentry = "//span[@class='subentries']/span[@class='subentry']/span[@class='subentry']/span[@lang='fr'and text()='mainsubentry']"; - const string matchTestsubentry = "//span[@class='subentries']/span[@class='subentry']/span[@class='subentry']/span[@lang='fr'and text()='testsubentry']"; - const string matchVariantRef = "//span[@class='variantentrytypes']/span[@class='variantentrytype']/span[@class='name']/span[@lang='en']"; + const string matchFrenchSubentry = "//span[@class='subentries-1']/span[@class='subentry']/span[@class='subentry-1']/span[@lang='fr']"; + const string matchMainsubentry = "//span[@class='subentries-1']/span[@class='subentry']/span[@class='subentry-1']/span[@lang='fr'and text()='mainsubentry']"; + const string matchTestsubentry = "//span[@class='subentries-1']/span[@class='subentry']/span[@class='subentry-1']/span[@lang='fr'and text()='testsubentry']"; + const string matchVariantRef = "//span[@class='variantentrytypes-1']/span[@class='variantentrytype']/span[@class='name-1']/span[@lang='en']"; //SUT output = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNode, pubMain, settings).ToString(); @@ -8175,7 +8176,7 @@ public void GenerateContentForEntry_GeneratesVariantEntryTypesLabelWithNoRepetit { var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); var output = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNode, null, settings).ToString(); - const string matchVariantRef = "//span[@class='variantentrytypes']/span[@class='variantentrytype']/span[@class='name']/span[@lang='en']"; + const string matchVariantRef = "//span[@class='variantentrytypes-1']/span[@class='variantentrytype']/span[@class='name-1']/span[@lang='en']"; AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchVariantRef, 2); } } @@ -8238,9 +8239,9 @@ public void GenerateContentForEntry_GeneratesVariantEntryTypesShowOnlySelectedLi CreateVariantForm(Cache, entryEntry, ve2, "Spelling Variant"); // unique Type; UnChecked var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); var output = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNode, null, settings).ToString(); - const string matchFreeVariantRef = "//span[@class='variantentrytypes']/span[@class='variantentrytype']/span[@class='name']/span[@lang='en' and text()='Free Variant']"; + const string matchFreeVariantRef = "//span[@class='variantentrytypes-1']/span[@class='variantentrytype']/span[@class='name-1']/span[@lang='en' and text()='Free Variant']"; AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchFreeVariantRef, 1); - const string matchSpellingVariantRef = "//span[@class='variantentrytypes']/span[@class='variantentrytype']/span[@class='name']/span[@lang='en' and text()='Spelling Variant']"; + const string matchSpellingVariantRef = "//span[@class='variantentrytypes-1']/span[@class='variantentrytype']/span[@class='name-1']/span[@lang='en' and text()='Spelling Variant']"; AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchSpellingVariantRef, 0); } @@ -8301,7 +8302,7 @@ public void GenerateContentForEntry_GeneratesComplexFormEntryTypesLabelWithNoRep CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); var output = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNode, pubMain, DefaultSettings).ToString(); - const string matchComplexFormRef = "//span[@class='complexformtypes']/span[@class='complexformtype']/span[@class='name']/span[@lang='en']"; + const string matchComplexFormRef = "//span[@class='complexformtypes-1']/span[@class='complexformtype']/span[@class='name-1']/span[@lang='en']"; AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchComplexFormRef, 1); } @@ -8364,11 +8365,11 @@ public void GenerateContentForEntry_GeneratesComplexFormEntryTypesAndNamesGroup( CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); var output = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNode, pubMain, DefaultSettings).ToString(); - const string matchComplexFormTypeCompound = "//span[@class='complexformtypes']/span[@class='complexformtype']/span[@class='name']/span[@lang='en' and text()='Compound']"; + const string matchComplexFormTypeCompound = "//span[@class='complexformtypes-1']/span[@class='complexformtype']/span[@class='name-1']/span[@lang='en' and text()='Compound']"; AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchComplexFormTypeCompound, 1); - const string matchComplexFormTypeIdiom = "//span[@class='complexformtypes']/span[@class='complexformtype']/span[@class='name']/span[@lang='en' and text()='Idiom']"; + const string matchComplexFormTypeIdiom = "//span[@class='complexformtypes-1']/span[@class='complexformtype']/span[@class='name-1']/span[@lang='en' and text()='Idiom']"; AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchComplexFormTypeIdiom, 1); - const string matchComplexFormName = "//span[@class='visiblecomplexformbackref']/span[@class='headword']/span[@lang='fr']/a"; + const string matchComplexFormName = "//span[@class='visiblecomplexformbackref']/span[@class='headword-1']/span[@lang='fr']/a"; AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchComplexFormName, 4); } @@ -8417,9 +8418,9 @@ public void GenerateContentForEntry_ComplexFormAndSenseInPara() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, DefaultSettings).ToString(); - const string senseXpath = "div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='gloss']/span[@lang='en' and text()='gloss']"; + const string senseXpath = "div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='gloss-1']/span[@lang='en' and text()='gloss']"; var paracontinuationxpath = string.Format( - "div[@class='lexentry']//span[@class='subentries']/span[@class='subentry']/span[@class='complexformtypes']/span[@class='complexformtype']/span[@class='reverseabbr']/span[@lang='en' and text()='{0}']", + "div[@class='lexentry-1']//span[@class='subentries-1']/span[@class='subentry']/span[@class='complexformtypes-1']/span[@class='complexformtype']/span[@class='reverseabbr-1']/span[@lang='en' and text()='{0}']", complexRefRevAbbr); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseXpath, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(paracontinuationxpath, 1); @@ -8476,19 +8477,19 @@ public void GenerateContentForEntry_MinorComplexForm_GeneratesGlossOrSummaryDefi var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(subentry1, minorEntryNode, null, settings).ToString(); - const string complexFormEntryRefXpath = "div[@class='minorentrycomplex']/span[@class='complexformentryrefs']/span[@class='complexformentryref']"; - const string referencedEntriesXpath = "/span[@class='referencedentries']/span[@class='referencedentry']"; - const string glossOrSummXpath1 = complexFormEntryRefXpath + referencedEntriesXpath + "/span[@class='glossorsummary']/span[@lang='en' and text()='MainEntrySummaryDefn']"; + const string complexFormEntryRefXpath = "div[@class='minorentrycomplex-1']/span[@class='complexformentryrefs-1']/span[@class='complexformentryref']"; + const string referencedEntriesXpath = "/span[@class='referencedentries-1']/span[@class='referencedentry']"; + const string glossOrSummXpath1 = complexFormEntryRefXpath + referencedEntriesXpath + "/span[@class='glossorsummary-1']/span[@lang='en' and text()='MainEntrySummaryDefn']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(glossOrSummXpath1, 1); //SUT var result2 = ConfiguredLcmGenerator.GenerateContentForEntry(subentry2, minorEntryNode, null, settings).ToString(); - const string glossOrSummXpath2 = complexFormEntryRefXpath + referencedEntriesXpath + "/span[@class='glossorsummary']/span[@lang='en' and text()='gloss2']"; + const string glossOrSummXpath2 = complexFormEntryRefXpath + referencedEntriesXpath + "/span[@class='glossorsummary-1']/span[@lang='en' and text()='gloss2']"; AssertThatXmlIn.String(result2).HasSpecifiedNumberOfMatchesForXpath(glossOrSummXpath2, 1); //SUT var result3 = ConfiguredLcmGenerator.GenerateContentForEntry(subentry3, minorEntryNode, null, settings).ToString(); - const string glossOrSummXpath3 = complexFormEntryRefXpath + referencedEntriesXpath + "/span[@class='glossorsummary']/span[@lang='en' and text()='MainEntryS3Defn']"; + const string glossOrSummXpath3 = complexFormEntryRefXpath + referencedEntriesXpath + "/span[@class='glossorsummary-1']/span[@lang='en' and text()='MainEntryS3Defn']"; AssertThatXmlIn.String(result3).HasSpecifiedNumberOfMatchesForXpath(glossOrSummXpath3, 1); } @@ -8519,7 +8520,7 @@ public void GenerateContentForEntry_ContinuationParagraphWithEmtpyContentDoesNot var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings).ToString(); - const string senseXpath = "div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='gloss']/span[@lang='en' and text()='gloss']"; + const string senseXpath = "div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='gloss-1']/span[@lang='en' and text()='gloss']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseXpath, 1); Assert.That(result, Does.Not.Match(@"
"), "Empty Self closing
element should not generated after senses in paragraph"); @@ -8773,7 +8774,7 @@ public void SavePublishedHtmlWithStyles_ExtraEntriesIncludedInLastPage() const string pagesDivXPath = "//div[@class='pages']"; const string pageButtonXPath = "//div[@class='pages']/span[@class='pagebutton']"; const string pageButtonLastIndexPath = "//div[@class='pages']/span[@class='pagebutton' and @endIndex='20']"; - const string entryDivXPath = "//div[@class='entry']"; + const string entryDivXPath = "//div[@class='entry-1']"; try { xhtmlPath = LcmXhtmlGenerator.SavePreviewHtmlWithStyles(hvos, pubEverything, model, m_propertyTable, entriesPerPage: 10); @@ -8822,7 +8823,7 @@ public void SavePublishedHtmlWithStyles_ExtraEntriesMoreThanTenPercentGetOwnPage const string pageButtonXPath = "//div[@class='pages']/span[@class='pagebutton']"; const string firstPageButtonXPath = "//div[@class='pages']/span[@class='pagebutton' and @id='currentPageButton' and @startIndex='0' and @endIndex='7']"; const string lastPageButtonXPath = "//div[@class='pages']/span[@class='pagebutton' and @startIndex='16' and @endIndex='20']"; - const string entryXPath = "//div[@class='entry']"; + const string entryXPath = "//div[@class='entry-1']"; try { xhtmlPath = LcmXhtmlGenerator.SavePreviewHtmlWithStyles(hvos, pubEverything, model, m_propertyTable, entriesPerPage: 8); @@ -9017,14 +9018,16 @@ public void CheckSubsenseOutput() var xhtml = File.ReadAllText(xhtmlPath); //System.Diagnostics.Debug.WriteLine(String.Format("GENERATED XHTML = \r\n{0}\r\n=====================", xhtml)); // SUT - const string allCategsPath = "//span[@class='morphosyntaxanalysis']/span[@class='mlpartofspeech']/span[@lang='en']"; - const string firstCategPath = "/html/body/div[@class='lexentry']/span[@class='senses']//span[@class='sensecontent']/span[@class='sense']/span[@class='morphosyntaxanalysis']/span[@class='mlpartofspeech']/span[@lang='en' and text()='n']"; - const string secondCategPath = "/html/body/div[@class='lexentry']/span[@class='senses']/span[@class='sharedgrammaticalinfo']/span[@class='morphosyntaxanalysis']/span[@class='mlpartofspeech']/span[@lang='en' and text()='n']"; - const string thirdCategPath = "/html/body/div[@class='lexentry']/span[@class='senses']/span[@class='sharedgrammaticalinfo']/span[@class='morphosyntaxanalysis']/span[@class='mlpartofspeech']/span[@lang='en' and text()='adj']"; - const string fourthCategPath = "/html/body/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='morphosyntaxanalysis']/span[@class='mlpartofspeech']/span[@lang='en' and text()='adj']"; - const string fifthCategPath = "/html/body/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='morphosyntaxanalysis']/span[@class='mlpartofspeech']/span[@lang='en' and text()='n']"; - - AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(allCategsPath, 5); + const string allCategsPath1 = "//span[@class='morphosyntaxanalysis-1']/span[@class='mlpartofspeech-1']/span[@lang='en']"; + const string allCategsPath2 = "//span[@class='morphosyntaxanalysis-2']/span[@class='mlpartofspeech-2']/span[@lang='en']"; + const string firstCategPath = "/html/body/div[@class='lexentry-1']/span[@class='senses-1']//span[@class='sensecontent']/span[@class='sense']/span[@class='morphosyntaxanalysis-2']/span[@class='mlpartofspeech-2']/span[@lang='en' and text()='n']"; + const string secondCategPath = "/html/body/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sharedgrammaticalinfo']/span[@class='morphosyntaxanalysis-1']/span[@class='mlpartofspeech-1']/span[@lang='en' and text()='n']"; + const string thirdCategPath = "/html/body/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sharedgrammaticalinfo']/span[@class='morphosyntaxanalysis-1']/span[@class='mlpartofspeech-1']/span[@lang='en' and text()='adj']"; + const string fourthCategPath = "/html/body/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense']/span[@class='morphosyntaxanalysis-2']/span[@class='mlpartofspeech-2']/span[@lang='en' and text()='adj']"; + const string fifthCategPath = "/html/body/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense']/span[@class='morphosyntaxanalysis-2']/span[@class='mlpartofspeech-2']/span[@lang='en' and text()='n']"; + + AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(allCategsPath1, 3); + AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(allCategsPath2, 2); AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(firstCategPath, 1); AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(secondCategPath, 2); AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(thirdCategPath, 1); @@ -9105,8 +9108,8 @@ public void GenerateContentForEntry_EmbeddedWritingSystemGeneratesCorrectResult( entry.Bibliography.set_String(m_wsFr, multiRunString); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings).ToString(); - const string nestedEn = "/div[@class='lexentry']/span[@class='bib']/span[@lang='fr']/span[@lang='en']"; - const string nestedFr = "/div[@class='lexentry']/span[@class='bib']/span[@lang='fr']/span[@lang='fr']"; + const string nestedEn = "/div[@class='lexentry-1']/span[@class='bib-1']/span[@lang='fr']/span[@lang='en']"; + const string nestedFr = "/div[@class='lexentry-1']/span[@class='bib-1']/span[@lang='fr']/span[@lang='fr']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(nestedEn, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(nestedFr, 2); } @@ -9132,9 +9135,9 @@ public void GenerateContentForEntry_EmbeddedWritingSystemOfOppositeDirectionGene entry.Bibliography.set_String(wsHe, multiRunString); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings).ToString(); - const string nestedEn = "/div[@class='lexentry']/span[@class='bib']/span[@lang='he']/span[@dir='rtl']/span[@lang='en']/span[@dir='ltr']"; - const string nestedHe = "/div[@class='lexentry']/span[@class='bib']/span[@lang='he']/span[@dir='rtl']/span[@lang='he']"; - const string extraDirection = "/div[@class='lexentry']/span[@class='bib']/span[@lang='he']/span[@dir='rtl']/span[@lang='he']/span[@dir='rtl']"; + const string nestedEn = "/div[@class='lexentry-1']/span[@class='bib-1']/span[@lang='he']/span[@dir='rtl']/span[@lang='en']/span[@dir='ltr']"; + const string nestedHe = "/div[@class='lexentry-1']/span[@class='bib-1']/span[@lang='he']/span[@dir='rtl']/span[@lang='he']"; + const string extraDirection = "/div[@class='lexentry-1']/span[@class='bib-1']/span[@lang='he']/span[@dir='rtl']/span[@lang='he']/span[@dir='rtl']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(nestedEn, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(nestedHe, 2); AssertThatXmlIn.String(result).HasNoMatchForXpath(extraDirection); @@ -9162,10 +9165,10 @@ public void GenerateContentForEntry_WritingSystemOfSameDirectionGeneratesNoExtra var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null, true); // Right-to-Left //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); - const string nestedEn = "/div[@class='lexentry']/span[@class='bib']/span[@lang='he']/span[@lang='en']/span[@dir='ltr']"; - const string nestedHe = "/div[@class='lexentry']/span[@class='bib']/span[@lang='he']/span[@lang='he']"; - const string extraDirection0 = "/div[@class='lexentry']/span[@class='bib']/span[@lang='he']/span[@dir='rtl']"; - const string extraDirection1 = "/div[@class='lexentry']/span[@class='bib']/span[@lang='he']/span[@lang='he']/span[@dir='rtl']"; + const string nestedEn = "/div[@class='lexentry-1']/span[@class='bib-1']/span[@lang='he']/span[@lang='en']/span[@dir='ltr']"; + const string nestedHe = "/div[@class='lexentry-1']/span[@class='bib-1']/span[@lang='he']/span[@lang='he']"; + const string extraDirection0 = "/div[@class='lexentry-1']/span[@class='bib-1']/span[@lang='he']/span[@dir='rtl']"; + const string extraDirection1 = "/div[@class='lexentry-1']/span[@class='bib-1']/span[@lang='he']/span[@lang='he']/span[@dir='rtl']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(nestedEn, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(nestedHe, 1); AssertThatXmlIn.String(result).HasNoMatchForXpath(extraDirection0); @@ -9200,28 +9203,28 @@ public void GenerateContentForEntry_EmbeddedHyperlinkGeneratesAnchor() entry.Bibliography.set_String(m_wsFr, stringBldr.GetString()); // SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings).ToString(); - string nestedLink = $"/div[@class='lexentry']/span[@class='bib']/span/span/a[@href='{testUrl}']"; + string nestedLink = $"/div[@class='lexentry-1']/span[@class='bib-1']/span/span/a[@href='{testUrl}']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(nestedLink, 1); } private const string crossRefOwnerTypeXpath = - "//span[@class='minimallexreferences']/span[@class='minimallexreference']/span[@class='ownertype_name']"; + "//span[@class='minimallexreferences-1']/span[@class='minimallexreference']/span[@class='ownertype_name-1']"; private static string CrossRefOwnerTypeXpath(string type) { - return string.Format("//span[@class='minimallexreferences']/span[@class='minimallexreference']/span[@class='ownertype_name']" + + return string.Format("//span[@class='minimallexreferences-1']/span[@class='minimallexreference']/span[@class='ownertype_name-1']" + "/span[@lang='en' and text()='{0}']", type); } private static string HeadwordOrderInCrossRefsXpath(int position, string headword) { - return string.Format("//span[@class='minimallexreferences']/span[@class='minimallexreference']/span[@class='configtargets']" + + return string.Format("//span[@class='minimallexreferences-1']/span[@class='minimallexreference']/span[@class='configtargets']" + "/span[@class='configtarget' and position()='{0}']/span/span/a[text()='{1}']", position, headword); } private static string HeadwordWsInCrossRefsXpath(string ws, string headword) // REVIEW (Hasso) 2017.04: move these helpers to Helpers? { - return string.Format("//span[@class='minimallexreferences']/span[@class='minimallexreference']/span[@class='configtargets']" + + return string.Format("//span[@class='minimallexreferences-1']/span[@class='minimallexreference']/span[@class='configtargets']" + "/span[@class='configtarget']/span/span[@lang='{0}']/a[text()='{1}']", ws, headword); } @@ -9456,7 +9459,7 @@ public void GenerateContentForEntry_LexicalReferencesOrderedCorrectly([Values(tr FieldDescription = "LexEntry", Children = new List { relationsNode } }; - var xpathLexRef = "//div/span[@class='lexrefs']/span[@class='lexref']"; + var xpathLexRef = "//div/span[@class='lexrefs-1']/span[@class='lexref']"; if (usingSubfield) { // If we are testing subfields, insert 'SensesOS->Entry', which returns the same data, but allows us to make LexRefs a subfield. @@ -9475,18 +9478,18 @@ public void GenerateContentForEntry_LexicalReferencesOrderedCorrectly([Values(tr Children = mainEntryNode.Children }; mainEntryNode.Children = new List { senseNode }; - xpathLexRef = "//div/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='lexrefs']/span[@class='lexref']"; + xpathLexRef = "//div/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='lexrefs-1']/span[@class='lexref']"; } CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); var settings = DefaultSettings; - string antAbbrSpan = $"ant"; - string whSpan = $"wh"; - string ptSpan = $"pt"; - string antNameSpan = $"Antonym"; - string femmeSpan = $"femme"; - var garçonSpan = TsStringUtils.Compose($"garçon"); - var bêteSpan = TsStringUtils.Compose($"bête"); - string trucSpan = $"truc"; + string antAbbrSpan = $"ant"; + string whSpan = $"wh"; + string ptSpan = $"pt"; + string antNameSpan = $"Antonym"; + string femmeSpan = $"femme"; + var garçonSpan = TsStringUtils.Compose($"garçon"); + var bêteSpan = TsStringUtils.Compose($"bête"); + string trucSpan = $"truc"; //SUT //Console.WriteLine(LcmXhtmlGenerator.SavePreviewHtmlWithStyles(new[] { manEntry.Hvo, familyEntry.Hvo, girlEntry.Hvo, individualEntry.Hvo }, null, // new DictionaryConfigurationModel { Parts = new List { mainEntryNode } }, m_mediator)); // full output for diagnostics @@ -10198,8 +10201,8 @@ public void GenerateContentForEntry_GroupingNodeGeneratesSpanAndInnerContentWork //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, DefaultSettings).ToString(); - const string oneSenseWithGlossOfGloss = "/div[@class='lexentry']/span[@class='grouping_sensegroup']" - + "/span[@class='senses']/span[@class='sense']//span[@lang='en' and text()='gloss']"; + const string oneSenseWithGlossOfGloss = "/div[@class='lexentry-1']/span[@class='grouping_sensegroup-1']" + + "/span[@class='senses-1']/span[@class='sense']//span[@lang='en' and text()='gloss']"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(oneSenseWithGlossOfGloss, 1); } diff --git a/Src/xWorks/xWorksTests/CssGeneratorTests.cs b/Src/xWorks/xWorksTests/CssGeneratorTests.cs index f8fe0c3dc1..abb5ecb5ef 100644 --- a/Src/xWorks/xWorksTests/CssGeneratorTests.cs +++ b/Src/xWorks/xWorksTests/CssGeneratorTests.cs @@ -185,7 +185,7 @@ public void GenerateCssForConfiguration_SharedConfigurationGeneratesValidCss() cssGenerator.AddStyles(subEntryHeadwordNode); var cssResult = cssGenerator.GetStylesString(); // verify that the css result contains a line similar to: .sharedsubentries .sharedsubentry .headword span{ - VerifyRegex(cssResult, @"^\s*\.mainheadword-sharedsubentries>\s*span\s*{.*", + VerifyRegex(cssResult, @"\.sharedsubentries-.\s\.mainheadword-.>\s*span\s*{.*", "Css for child node(headword) did not generate a match"); } @@ -214,7 +214,7 @@ public void GenerateCssForConfiguration_AddStyleDoesNotGenerateEmptyStyle() cssGenerator.Init(m_propertyTable); //SUT cssGenerator.AddStyles(emptyNode); - Assert.That(cssGenerator.GetStylesString(), Is.EqualTo(string.Empty)); + Assert.That(!cssGenerator.GetStylesString().Contains(".nothing")); } [Test] @@ -333,14 +333,14 @@ public void GenerateCssForConfiguration_BeforeAfterGroupingSpanWorks() cssGenerator.AddStyles(headwordNode); var cssResult = cssGenerator.GetStylesString(); // Check the result for before and after rules for the group - Assert.IsTrue(Regex.Match(cssResult, @"\.grouping_hwg\s*:before\s*{\s*content\s*:\s*'{';\s*}").Success, + Assert.IsTrue(Regex.Match(cssResult, @"\.grouping_hwg-.\s*:before\s*{\s*content\s*:\s*'{';\s*}").Success, "css before rule for the grouping node was not generated"); - Assert.IsTrue(Regex.Match(cssResult, @"\.grouping_hwg\s*:after\s*{\s*content\s*:\s*'}';\s*}").Success, + Assert.IsTrue(Regex.Match(cssResult, @"\.grouping_hwg-.\s*:after\s*{\s*content\s*:\s*'}';\s*}").Success, "css after rule for the grouping node was not generated"); // Check result for before and after rules equivalent to .headword span:first-child{content:'Z';} and .headword span:last-child{content:'A'} - Assert.IsTrue(Regex.Match(cssResult, @"\.mh-grouping_hwg>\s*span\s*:\s*first-child:before\s*{\s*content\s*:\s*'Z';\s*}").Success, + Assert.IsTrue(Regex.Match(cssResult, @"\.grouping_hwg-.\s\.mh-.>\s*span\s*:\s*first-child:before\s*{\s*content\s*:\s*'Z';\s*}").Success, "css before rule with Z content not found on headword"); - Assert.IsTrue(Regex.Match(cssResult, @"\.mh-grouping_hwg>\s*span\s*:\s*last-child:after\s*{\s*content\s*:\s*'A';\s*}").Success, + Assert.IsTrue(Regex.Match(cssResult, @"\.grouping_hwg-.\s\.mh-.>\s*span\s*:\s*last-child:after\s*{\s*content\s*:\s*'A';\s*}").Success, "css after rule with A content not found on headword"); } @@ -439,9 +439,9 @@ public void GenerateCssForConfiguration_BeforeAfterConfigGeneratesBeforeAfterCss cssGenerator.AddStyles(headwordNode); var cssResult = cssGenerator.GetStylesString(); // Check result for before and after rules equivalent to .headword-subentries span:first-child{content:'Z';} and .headword span:last-child{content:'A'} - VerifyRegex(cssResult, @"\.headword-subentries>\s*span\s*:\s*first-child:before\s*{\s*content\s*:\s*'Z';\s*}", + VerifyRegex(cssResult, @"\.subentries-.\s\.headword-.>\s*span\s*:\s*first-child:before\s*{\s*content\s*:\s*'Z';\s*}", "css before rule with Z content not found on headword"); - VerifyRegex(cssResult, @"\.headword-subentries>\s*span\s*:\s*last-child:after\s*{\s*content\s*:\s*'A';\s*}", + VerifyRegex(cssResult, @"\.subentries-.\s\.headword-.>\s*span\s*:\s*last-child:after\s*{\s*content\s*:\s*'A';\s*}", "css after rule with A content not found on headword"); } @@ -1294,8 +1294,8 @@ public void ClassMappingOverrides_ApplyAtRoot() XHTMLWriter.WriteEndElement(); XHTMLWriter.Flush(); var result = xhtmResult.ToString(); - const string positiveTest = "//*[@class='bolo']"; - const string negativeTest = "//*[@class='lexentry']"; + const string positiveTest = "//*[@class='bolo-1']"; + const string negativeTest = "//*[@class='lexentry-1']"; AssertThatXmlIn.String(result).HasNoMatchForXpath(negativeTest); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(positiveTest, 1); } @@ -1332,8 +1332,8 @@ public void ClassMappingOverrides_ApplyToChildren() Assert.That(cssResult, Contains.Substring(".tailwind")); var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, testParentNode, null, DefaultSettings).ToString(); - const string positiveTest = "//*[@class='tailwind']"; - const string negativeTest = "//*[@class='headword']"; + const string positiveTest = "//*[@class='tailwind-1']"; + const string negativeTest = "//*[@class='headword-1']"; AssertThatXmlIn.String(result).HasNoMatchForXpath(negativeTest); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(positiveTest, 1); } @@ -1373,7 +1373,7 @@ public void CssAndXhtmlMatchOnSenseCollectionItems() Assert.That(cssResult, Contains.Substring(".gloss")); var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, testEntryNode, null, DefaultSettings).ToString(); - const string positiveTest = "/*[@class='lexentry']/span[@class='senses']/span[@class='sense']/span[@class='gloss']"; + const string positiveTest = "/*[@class='lexentry-1']/span[@class='senses-1']/span[@class='sense']/span[@class='gloss-1']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(positiveTest, 1); } @@ -1936,7 +1936,7 @@ public void GenerateCssForConfiguration_SenseComplexFormsNotSubEntriesHeadWord() cssGenerator.AddStyles(headwordMain); cssGenerator.AddStyles(form); var cssResult = cssGenerator.GetStylesString(); - VerifyRegex(cssResult, @"^\s*\.headword-otherreferencedcomplexforms", "Headword node not generated for non subentry headword"); + VerifyRegex(cssResult, @"\.otherreferencedcomplexforms-.\s.headword-.", "Headword node not generated for non subentry headword"); } [Test] @@ -2009,8 +2009,8 @@ public void GenerateCssForConfiguration_SenseShowGramInfoFirstWorks() cssGenerator.AddStyles(senses); cssGenerator.AddStyles(gramInfo); var cssResult = cssGenerator.GetStylesString(); - VerifyRegex(cssResult, @"^\s*\.morphosyntaxanalysisra", "Style for morphosyntaxanalysisra not generated"); - VerifyRegex(cssResult, @"^\s*\.morphosyntaxanalysisra\s*{.*font-family\s*:\s*'foofoo'\,serif.*}", + VerifyRegex(cssResult, @"\.morphosyntaxanalysisra-.", "Style for morphosyntaxanalysisra not generated"); + VerifyRegex(cssResult, @"\.morphosyntaxanalysisra-.\s*{.*font-family\s*:\s*'foofoo'\,serif.*}", "Style for morphosyntaxanalysisra not placed correctly"); } @@ -3088,6 +3088,7 @@ public void GenerateCssForConfiguration_GenerateMainEntryParagraphStyle() var testEntryNode = new ConfigurableDictionaryNode { FieldDescription = "LexEntry", + Style = "Dictionary-Normal", Children = new List { testSensesNode }, CSSClassNameOverride = "entry" }; @@ -3100,13 +3101,13 @@ public void GenerateCssForConfiguration_GenerateMainEntryParagraphStyle() var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); Assert.IsTrue( Regex.Match(cssResult, - @"div.entry{\s*margin-left:24pt;\s*padding-right:48pt;\s*}", + @".entry{\s*margin-left:24pt;\s*padding-right:48pt;\s*", RegexOptions.Singleline).Success, "Dictionary-Normal Paragraph Style not generated when main entry has no style selected."); model.Parts[0].Style = "Dictionary-RTL"; cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); Assert.IsTrue( - Regex.Match(cssResult, @"div.entry{\s*direction:rtl;\s*}", + Regex.Match(cssResult, @".entry{\s*direction:rtl;\s*}", RegexOptions.Singleline).Success, "Main Entry style was not used as the main page style"); } @@ -3127,6 +3128,7 @@ public void GenerateCssForConfiguration_GenerateDictionaryMinorParagraphStyle() { FieldDescription = "LexEntry", CSSClassNameOverride = "minorentry", + Style = "Dictionary-Minor", Children = new List { testSensesNode } }; var extraEntryNode = new ConfigurableDictionaryNode @@ -3155,11 +3157,11 @@ public void GenerateCssForConfiguration_GenerateDictionaryMinorParagraphStyle() model.Parts.ForEach(PopulateFieldsForTesting); //SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(model, m_propertyTable); - VerifyRegex(cssResult, @"div.minorentry{\s*margin-left:24pt;\s*padding-right:48pt;\s*}", + VerifyRegex(cssResult, @".minorentry{\s*margin-left:24pt;\s*padding-right:48pt;\s*", "Dictionary-Minor Paragraph Style not generated."); - VerifyRegex(cssResult, @"div.specialminorentry{\s*padding-right:32pt;\s*}", + VerifyRegex(cssResult, @".specialminorentry{\s*padding-right:32pt;\s*}", "Dictionary-Minor Paragraph Style for node with style attribute not generated."); - VerifyRegex(cssResult, @"div.optionsminorentry{\s*padding-right:16pt;\s*}", + VerifyRegex(cssResult, @".optionsminorentry{\s*padding-right:16pt;\s*}", "Dictionary-Minor Paragraph Style for node with paragraph options not generated."); } @@ -3198,7 +3200,7 @@ public void GenerateCssForConfiguration_DictionaryMinorUnusedDoesNotOverride() // "Then generate the rules for all the writing system overrides" // So I chose to check specifically for one of the default writing systems; DefaultAnalWs would have worked too. var vernStyle = "span[lang='" + vernWs + "']{color:#008000;}"; - Assert.That(Regex.Replace(cssResult, @"\t|\n|\r", ""), Contains.Substring(@"div.minorentryvariant " + vernStyle), + Assert.That(Regex.Replace(cssResult, @"\t|\n|\r", ""), Contains.Substring(@"minorentryvariant " + vernStyle), "Dictionary-Secondary Paragraph Style should be generated."); } @@ -3468,7 +3470,7 @@ public void GenerateCssForBulletStyle_OneStyleWhenSubSenseMatchesSense() cssGenerator.AddStyles(senses); cssGenerator.AddStyles(subsenses); var cssResult = cssGenerator.GetStylesString(); - const string regExPected = @".*senses\s>\s.sensecontent:before.*{.*content:'\\25A0';.*font-size:14pt;.*color:Green;.*font-family:Arial;.*font-weight:bold;.*font-style:italic;.*background-color:Brown;.*}"; + const string regExPected = @".*senses-.\s>\s.sensecontent:before.*{.*content:'\\25A0';.*font-size:14pt;.*color:Green;.*font-family:Arial;.*font-weight:bold;.*font-style:italic;.*background-color:Brown;.*}"; Assert.That(Regex.Match(cssResult, regExPected, RegexOptions.Singleline).Success, "Bulleted style for SubSenses not generated."); Assert.That(!Regex.Match(cssResult, regExPected, RegexOptions.Singleline).NextMatch().Success, "Bulleted style for SubSenses not generated."); } @@ -3514,9 +3516,9 @@ public void GenerateCssForBulletStyle_TwoStylesWhenSubSensesAreDifferent() cssGenerator.AddStyles(senses); cssGenerator.AddStyles(subsenses); var cssResult = cssGenerator.GetStylesString(); - const string regExPectedForSub = @"\.senses-senses\s>\s.sensecontent:before.*{.*content:'\\25A0';.*font-size:14pt;.*color:Green;.*font-family:Arial;.*font-weight:bold;.*font-style:italic;.*background-color:Brown;.*}"; + const string regExPectedForSub = @"\.senses-.\s\.senses-.\s>\s\.sensecontent:before.*{.*content:'\\25A0';.*font-size:14pt;.*color:Green;.*font-family:Arial;.*font-weight:bold;.*font-style:italic;.*background-color:Brown;.*}"; VerifyRegex(cssResult, regExPectedForSub, "Bulleted style for SubSenses not generated."); - const string regExPectedForSense = @"\.senses\s>\s\.sensecontent"; // Make sure there is a .sense > .sensecontent rule as well as the bulletted sub-sense + const string regExPectedForSense = @"\.senses-.\s>\s\.sensecontent"; // Make sure there is a .sense > .sensecontent rule as well as the bulletted sub-sense VerifyRegex(cssResult, regExPectedForSense, "Non-bulleted style for Senses not generated."); } diff --git a/Src/xWorks/xWorksTests/LcmJsonGeneratorTests.cs b/Src/xWorks/xWorksTests/LcmJsonGeneratorTests.cs index 48875bfcac..c276f3bc5c 100644 --- a/Src/xWorks/xWorksTests/LcmJsonGeneratorTests.cs +++ b/Src/xWorks/xWorksTests/LcmJsonGeneratorTests.cs @@ -158,8 +158,8 @@ public void GenerateJsonForEntry_OneSenseWithGlossGeneratesCorrectResult() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings, 0).ToString(); Console.WriteLine(result); - var expectedResult = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + entry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, - ""senses"": [{""guid"":""g" + entry.Guid + @""",""gloss"": [{""lang"":""en"",""value"":""gloss""}]},]}"; + var expectedResult = @"{""xhtmlTemplate"": ""lexentry-1"",""guid"":""g" + entry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, + ""senses-1"": [{""guid"":""g" + entry.Guid + @""",""gloss-1"": [{""lang"":""en"",""value"":""gloss""}]},]}"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry var expected = (JObject)JsonConvert.DeserializeObject(expectedResult); VerifyJson(result, expected); @@ -191,8 +191,8 @@ public void GenerateJsonForEntry_DefinitionOrGloss_HandlePerWS() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings, 0).ToString(); - var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + entry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, - ""senses"": [{""guid"":""g" + entry.Guid + @""", ""definitionorgloss"": [{""lang"":""en"",""value"":""gloss""}, + var expectedResults = @"{""xhtmlTemplate"": ""lexentry-1"",""guid"":""g" + entry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, + ""senses-1"": [{""guid"":""g" + entry.Guid + @""", ""definitionorgloss-1"": [{""lang"":""en"",""value"":""gloss""}, {""lang"":""es"",""value"":""definition""}]}]}"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry var expected = (JObject)JsonConvert.DeserializeObject(expectedResults); @@ -257,9 +257,9 @@ public void GenerateJsonForEntry_TwoSensesWithSameInfoShowGramInfoFirst_Json() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings, 0).ToString(); Console.WriteLine(result); - var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + entry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, - ""msas"": {""mlpartofspeech"": [{""lang"":""en"",""value"":""Blah""}]}, ""senses"": [{""guid"":""g" + entry.Guid + @""",""gloss"": [{""lang"":""en"",""value"":""gloss""}]}, - {""guid"":""g" + entry.Guid + @""",""gloss"": [{""lang"":""en"",""value"":""second sense""}]}]}"; + var expectedResults = @"{""xhtmlTemplate"": ""lexentry-1"",""guid"":""g" + entry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, + ""msas-1"": {""mlpartofspeech-1"": [{""lang"":""en"",""value"":""Blah""}]}, ""senses-1"": [{""guid"":""g" + entry.Guid + @""",""gloss-1"": [{""lang"":""en"",""value"":""gloss""}]}, + {""guid"":""g" + entry.Guid + @""",""gloss-1"": [{""lang"":""en"",""value"":""second sense""}]}]}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(result, expected); } @@ -297,9 +297,9 @@ public void GenerateJsonForEntry_OneSenseWithSinglePicture() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings, 0).ToString(); - var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + entry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, - ""pictures"": [{""guid"":""g" + sensePic.Guid + @""",""src"":""pictures/test_auth_copy_license.jpg"", - ""sensenumber"": [{""lang"":""en"",""value"":""1""}],""caption"": [{""lang"":""en"",""value"":""caption""}]}]}"; + var expectedResults = @"{""xhtmlTemplate"": ""lexentry-1"",""guid"":""g" + entry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, + ""pictures-1"": [{""guid"":""g" + sensePic.Guid + @""",""src"":""pictures/test_auth_copy_license.jpg"", + ""sensenumber-1"": [{""lang"":""en"",""value"":""1""}],""caption-1"": [{""lang"":""en"",""value"":""caption""}]}]}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(result, expected); } @@ -512,12 +512,12 @@ public void GenerateJsonForEntry_FilterByPublication() //SUT var output = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNode, pubMain, DefaultSettings, 0).ToString(); Assert.That(output, Is.Not.Null.Or.Empty); - var expectedResults = "{\"xhtmlTemplate\": \"lexentry\",\"guid\":\"g" + entryEntry.Guid + "\",\"letterHead\": \"e\",\"sortIndex\": 0," + - "\"entry\": [{\"lang\":\"fr\",\"value\":\"entry\"}],\"senses\": [{\"guid\":\"g" + - entryEntry.Guid + "\",\"definitionorgloss\": [{\"lang\":\"en\",\"value\":\"entry\"}]}]," + - "\"subentries\": [{\"subentry\": [{\"lang\":\"fr\",\"value\":\"mainsubentry\"}]}]," + - "\"variantformentrybackrefs\": [{\"referenceType\":\"{\\\"name\\\": [{\\\"lang\\\":\\\"en\\\"," + - "\\\"value\\\":\\\"Spelling Variant\\\"}],},\",\"references\":[{\"headword\": [{\"lang\":\"fr\",\"guid\": \"g" + + var expectedResults = "{\"xhtmlTemplate\": \"lexentry-1\",\"guid\":\"g" + entryEntry.Guid + "\",\"letterHead\": \"e\",\"sortIndex\": 0," + + "\"entry-1\": [{\"lang\":\"fr\",\"value\":\"entry\"}],\"senses-1\": [{\"guid\":\"g" + + entryEntry.Guid + "\",\"definitionorgloss-1\": [{\"lang\":\"en\",\"value\":\"entry\"}]}]," + + "\"subentries-1\": [{\"subentry-1\": [{\"lang\":\"fr\",\"value\":\"mainsubentry\"}]}]," + + "\"variantformentrybackrefs-1\": [{\"referenceType\":\"{\\\"name-1\\\": [{\\\"lang\\\":\\\"en\\\"," + + "\\\"value\\\":\\\"Spelling Variant\\\"}],},\",\"references\":[{\"headword-1\": [{\"lang\":\"fr\",\"guid\": \"g" + bizarroVariant.Guid + "\", \"value\":\"bizarre\"}]}]}]}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(output, expected); @@ -582,11 +582,11 @@ public void GenerateJsonForEntry_TypeAfterForm() //SUT var outputTypeAfter = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNodeTypeAfter, pubMain, DefaultSettings, 0).ToString(); Assert.That(outputTypeAfter, Is.Not.Null.Or.Empty); - var expectedResultsTypeAfter = "{\"xhtmlTemplate\": \"lexentry\",\"guid\":\"g" + entryEntry.Guid + "\",\"letterHead\": \"e\",\"sortIndex\": 0," + - "\"entry\": [{\"lang\":\"fr\",\"value\":\"entry\"}]," + - "\"variantformentrybackrefs\": [{\"references\":[{\"headword\": [{\"lang\":\"fr\",\"guid\": \"g" + + var expectedResultsTypeAfter = "{\"xhtmlTemplate\": \"lexentry-1\",\"guid\":\"g" + entryEntry.Guid + "\",\"letterHead\": \"e\",\"sortIndex\": 0," + + "\"entry-1\": [{\"lang\":\"fr\",\"value\":\"entry\"}]," + + "\"variantformentrybackrefs-1\": [{\"references\":[{\"headword-1\": [{\"lang\":\"fr\",\"guid\": \"g" + bizarroVariant.Guid + "\", \"value\":\"bizarre\"}]}]," + - "\"referenceType\":\"{\\\"name\\\": [{\\\"lang\\\":\\\"en\\\",\\\"value\\\":\\\"Spelling Variant\\\"}],},\"}]}"; + "\"referenceType\":\"{\\\"name-1\\\": [{\\\"lang\\\":\\\"en\\\",\\\"value\\\":\\\"Spelling Variant\\\"}],},\"}]}"; var expectedTypeAfter = (JObject)JsonConvert.DeserializeObject(expectedResultsTypeAfter, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(outputTypeAfter, expectedTypeAfter); } @@ -617,8 +617,8 @@ public void GenerateJsonForEntry_WsAudiowithHyperlink() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, DefaultSettings, 0).ToString(); - var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"": ""g" + entryOne.Guid + - @""",""letterHead"": ""c"",""sortIndex"": 0, ""headword"": [{""guid"": ""g" + entryOne.Guid + @""", ""lang"":""en-Zxxx-x-audio"", ""value"": {""id"": ""gTest_Audi_o"", ""src"": ""AudioVisual/Test Audi'o.wav""}}]}"; + var expectedResults = @"{""xhtmlTemplate"": ""lexentry-1"",""guid"": ""g" + entryOne.Guid + + @""",""letterHead"": ""c"",""sortIndex"": 0, ""headword-1"": [{""guid"": ""g" + entryOne.Guid + @""", ""lang"":""en-Zxxx-x-audio"", ""value"": {""id"": ""gTest_Audi_o"", ""src"": ""AudioVisual/Test Audi'o.wav""}}]}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(result, expected); } @@ -674,9 +674,9 @@ public void GenerateJsonForEntry_SensibleJsonForVideoFiles() //SUT var output = ConfiguredLcmGenerator.GenerateContentForEntry(entryCorps, mainEntryNode, DefaultDecorator, DefaultSettings, 0).ToString(); Assert.That(output, Is.Not.Null.Or.Empty); - var expectedResults = "{\"xhtmlTemplate\":\"lexentry\",\"guid\":\"g" + entryCorps.Guid + "\",\"letterHead\":\"c\",\"sortIndex\":0," + - "\"entry\": [{\"lang\":\"fr\",\"value\":\"corps\"}]," + - "\"pronunciations\": [{\"mediafiles\": [{\"value\": {\"id\":\"g" + mainMediaFile.Guid + "\",\"src\": \"AudioVisual/fileName.mp4\"}}]}]}"; + var expectedResults = "{\"xhtmlTemplate\":\"lexentry-1\",\"guid\":\"g" + entryCorps.Guid + "\",\"letterHead\":\"c\",\"sortIndex\":0," + + "\"entry-1\": [{\"lang\":\"fr\",\"value\":\"corps\"}]," + + "\"pronunciations-1\": [{\"mediafiles-1\": [{\"value\": {\"id\":\"g" + mainMediaFile.Guid + "\",\"src\": \"AudioVisual/fileName.mp4\"}}]}]}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(output, expected); } @@ -703,9 +703,9 @@ public void GenerateJsonForEntry_SenseNumbersGeneratedForMultipleSenses() ConfiguredXHTMLGeneratorTests.AddSenseToEntry(testEntry, "second gloss", m_wsEn, Cache); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, DefaultSettings, 0).ToString(); - var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + testEntry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0,""senses"":[{""senseNumber"":""1"", - ""guid"":""g" + testEntry.Guid + @""",""gloss"":[{""lang"":""en"",""value"":""gloss""}]}, - {""senseNumber"":""2"",""guid"":""g" + testEntry.Guid + @""",""gloss"":[{""lang"":""en"",""value"":""second gloss""}]}]}"; + var expectedResults = @"{""xhtmlTemplate"": ""lexentry-1"",""guid"":""g" + testEntry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0,""senses-1"":[{""senseNumber"":""1"", + ""guid"":""g" + testEntry.Guid + @""",""gloss-1"":[{""lang"":""en"",""value"":""gloss""}]}, + {""senseNumber"":""2"",""guid"":""g" + testEntry.Guid + @""",""gloss-1"":[{""lang"":""en"",""value"":""second gloss""}]}]}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(result, expected); } @@ -732,7 +732,7 @@ public void GenerateJsonForEntry_EmbeddedWritingSystemGeneratesCorrectResult() entry.Bibliography.set_String(m_wsFr, multiRunString); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings, 0).ToString(); - var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + entry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0,""bib"": [{""lang"":""fr"",""value"":""French with ""}, + var expectedResults = @"{""xhtmlTemplate"": ""lexentry-1"",""guid"":""g" + entry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0,""bib-1"": [{""lang"":""fr"",""value"":""French with ""}, {""lang"":""en"",""value"":""English""},{""lang"":""fr"",""value"":"" embedded""}]}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(result, expected); @@ -758,8 +758,8 @@ public void GenerateJsonForEntry_UnicodeLineBreak_GeneratesValidJson() entry.Bibliography.set_String(m_wsFr, englishStr); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings, 0).ToString(); - var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + entry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, - ""bib"": [{""lang"":""en"",""value"":""English\nwith line break""}]}"; + var expectedResults = @"{""xhtmlTemplate"": ""lexentry-1"",""guid"":""g" + entry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, + ""bib-1"": [{""lang"":""en"",""value"":""English\nwith line break""}]}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(result, expected); } @@ -805,8 +805,8 @@ public void GenerateJsonForEntry_GeneratesForwardNameForForwardLexicalRelations( //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, DefaultSettings, 0).ToString(); - var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + mainEntry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, - ""sensesos"": [{""lexsensereferences"": [{""ownertype_name"": [{""lang"":""en"",""value"":""TestRefType""}]}]}]}"; + var expectedResults = @"{""xhtmlTemplate"": ""lexentry-1"",""guid"":""g" + mainEntry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, + ""sensesos-1"": [{""lexsensereferences-1"": [{""ownertype_name-1"": [{""lang"":""en"",""value"":""TestRefType""}]}]}]}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(result, expected); } @@ -848,8 +848,8 @@ public void GenerateJsonForEntry_EmptyNameOnLexicalRelation_GeneratesEmptyButVal //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, DefaultSettings, 0).ToString(); - var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + mainEntry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, - ""sensesos"": [{""lexsensereferences"": [{}]}]}"; + var expectedResults = @"{""xhtmlTemplate"": ""lexentry-1"",""guid"":""g" + mainEntry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, + ""sensesos-1"": [{""lexsensereferences-1"": [{}]}]}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(result, expected); } @@ -875,13 +875,13 @@ public void GenerateJsonForEntry_HomographNumbersGeneratesCorrectResult() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, DefaultSettings, 0).ToString(); - var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + entryOne.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0,""homographnumber"": ""1"", - ""citationform"": [{""lang"":""fr"",""value"":""Citation""}]}"; + var expectedResults = @"{""xhtmlTemplate"": ""lexentry-1"",""guid"":""g" + entryOne.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0,""homographnumber-1"": ""1"", + ""citationform-1"": [{""lang"":""fr"",""value"":""Citation""}]}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(result, expected); result = ConfiguredLcmGenerator.GenerateContentForEntry(entryTwo, mainEntryNode, null, DefaultSettings, 0).ToString(); - expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + entryTwo.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0,""homographnumber"": ""2"", - ""citationform"": [{""lang"":""fr"",""value"":""Citation""}]}"; + expectedResults = @"{""xhtmlTemplate"": ""lexentry-1"",""guid"":""g" + entryTwo.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0,""homographnumber-1"": ""2"", + ""citationform-1"": [{""lang"":""fr"",""value"":""Citation""}]}"; expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(result, expected); @@ -906,14 +906,14 @@ public void GenerateJsonForEntry_GeneratesSpecifiedSortIndex() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, DefaultSettings, 36).ToString(); - var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + entryOne.Guid + @""",""letterHead"": ""c"",""sortIndex"": 36, - ""citationform"": [{""lang"":""fr"",""value"":""Citation""}]}"; + var expectedResults = @"{""xhtmlTemplate"": ""lexentry-1"",""guid"":""g" + entryOne.Guid + @""",""letterHead"": ""c"",""sortIndex"": 36, + ""citationform-1"": [{""lang"":""fr"",""value"":""Citation""}]}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(result, expected); // default value of -1 result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, DefaultSettings).ToString(); - expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + entryOne.Guid + @""",""letterHead"": ""c"",""sortIndex"": -1, - ""citationform"": [{""lang"":""fr"",""value"":""Citation""}]}"; + expectedResults = @"{""xhtmlTemplate"": ""lexentry-1"",""guid"":""g" + entryOne.Guid + @""",""letterHead"": ""c"",""sortIndex"": -1, + ""citationform-1"": [{""lang"":""fr"",""value"":""Citation""}]}"; expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(result, expected); @@ -961,8 +961,8 @@ public void GenerateJsonForEntry_TwoDifferentPicturesGetUniqueWebFriendlyPaths() var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, DefaultSettings, 0).ToString(); // Bug: The second filename should be different after the export with relative path settings (fix later) - var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + testEntry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, - ""pictures"": [{""guid"":""g" + sensePic.Guid + @""",""src"":""pictures/" + fileName + @"""}, + var expectedResults = @"{""xhtmlTemplate"": ""lexentry-1"",""guid"":""g" + testEntry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, + ""pictures-1"": [{""guid"":""g" + sensePic.Guid + @""",""src"":""pictures/" + fileName + @"""}, {""guid"":""g" + sensePic2.Guid + @""",""src"":""pictures/" + fileName + @"""}],}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(result, expected); @@ -1025,22 +1025,22 @@ public void GenerateJsonForEntry_MinorComplexForm_TemplateTypeCorrect_GeneratesG ; //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(subentry1, minorEntryNode, null, DefaultSettings, 0).ToString(); - var expectedResults = @"{""xhtmlTemplate"":""minorentrycomplex"",""guid"":""g" + subentry1.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, - ""complexformentryrefs"": [{""referencedentries"": [{""glossorsummary"": [{""lang"":""en"",""value"":""MainEntrySummaryDefn""}]}]}]}"; + var expectedResults = @"{""xhtmlTemplate"":""minorentrycomplex-1"",""guid"":""g" + subentry1.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, + ""complexformentryrefs-1"": [{""referencedentries-1"": [{""glossorsummary-1"": [{""lang"":""en"",""value"":""MainEntrySummaryDefn""}]}]}]}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(result, expected); //SUT var result2 = ConfiguredLcmGenerator.GenerateContentForEntry(subentry2, minorEntryNode, null, DefaultSettings, 0).ToString(); - expectedResults = @"{""xhtmlTemplate"":""minorentrycomplex"",""guid"":""g" + subentry2.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, - ""complexformentryrefs"": [{""referencedentries"": [{""glossorsummary"": [{""lang"":""en"",""value"":""gloss2""}]}]}]}"; + expectedResults = @"{""xhtmlTemplate"":""minorentrycomplex-1"",""guid"":""g" + subentry2.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, + ""complexformentryrefs-1"": [{""referencedentries-1"": [{""glossorsummary-1"": [{""lang"":""en"",""value"":""gloss2""}]}]}]}"; expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(result2, expected); //SUT var result3 = ConfiguredLcmGenerator.GenerateContentForEntry(subentry3, minorEntryNode, null, DefaultSettings, 0).ToString(); - expectedResults = @"{""xhtmlTemplate"": ""minorentrycomplex"",""guid"":""g" + subentry3.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, - ""complexformentryrefs"": [{""referencedentries"": [{""glossorsummary"": [{""lang"":""en"",""value"":""MainEntryS3Defn""}]}]}]}"; + expectedResults = @"{""xhtmlTemplate"": ""minorentrycomplex-1"",""guid"":""g" + subentry3.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, + ""complexformentryrefs-1"": [{""referencedentries-1"": [{""glossorsummary-1"": [{""lang"":""en"",""value"":""MainEntryS3Defn""}]}]}]}"; expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(result3, expected); } @@ -1103,11 +1103,11 @@ public void SavePublishedJsonWithStyles_DisplayXhtmlPopulated() DefaultDecorator, 1, new DictionaryConfigurationModel { Parts = new List { mainEntryNode } }, m_propertyTable, "test.json", null, out int[] _); - var expectedResults = @"{""xhtmlTemplate"":""lexentry"",""guid"":""g" + testEntry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, - ""homographnumber"":""0"",""citationform"":[{""lang"":""fr"",""value"":""Citation""}], - ""displayXhtml"":""
0Citation
""}"; + var expectedResults = @"{""xhtmlTemplate"":""lexentry-1"",""guid"":""g" + testEntry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, + ""homographnumber-1"":""0"",""citationform-1"":[{""lang"":""fr"",""value"":""Citation""}], + ""displayXhtml"":""
0Citation
""}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(results[0][0].ToString(Formatting.None), expected); } @@ -1203,8 +1203,8 @@ public void GenerateXHTMLForEntry_EmbeddedWritingSystemOfOppositeDirectionGenera //SUT var json = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings).ToString(); - var expectedResults = @"{""xhtmlTemplate"":""lexentry"",""guid"":""g" + entry.Guid + @""",""letterHead"":""c"",""sortIndex"":-1, - ""bib"": [{""lang"":""he"",""value"":""דוד""},{""lang"":""en"",""value"":"" et ""},{""lang"":""he"",""value"":""דניאל""}],}"; + var expectedResults = @"{""xhtmlTemplate"":""lexentry-1"",""guid"":""g" + entry.Guid + @""",""letterHead"":""c"",""sortIndex"":-1, + ""bib-1"": [{""lang"":""he"",""value"":""דוד""},{""lang"":""en"",""value"":"" et ""},{""lang"":""he"",""value"":""דניאל""}],}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(json, expected); } @@ -1233,7 +1233,7 @@ public void GenerateXHTMLForEntry_MultiLineCustomFieldGeneratesContent() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, rootNode, null, DefaultSettings, 0).ToString(); - var expectedResults = @"{""xhtmlTemplate"":""lexentry"", + var expectedResults = @"{""xhtmlTemplate"":""lexentry-1"", ""guid"":""g" + testEntry.Guid + @""", ""letterHead"":""c"", ""sortIndex"":0, From f22223360eff2e6674f059316577eb42373bae91 Mon Sep 17 00:00:00 2001 From: Mark Kidder <83427558+mark-sil@users.noreply.github.com> Date: Tue, 18 Feb 2025 10:18:19 -0500 Subject: [PATCH 015/123] Add comments (#264) --- Src/xWorks/CssGenerator.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Src/xWorks/CssGenerator.cs b/Src/xWorks/CssGenerator.cs index 77d7db96a1..da48f0d6f4 100644 --- a/Src/xWorks/CssGenerator.cs +++ b/Src/xWorks/CssGenerator.cs @@ -110,8 +110,8 @@ public string AddStyles(ConfigurableDictionaryNode node) { // We need a specificity higher than ".senses-? .subentries-? .subentry" and // because of the way the path to ".mainentrysubentries" is generated we can - // get the wrong value for ".sense-?. So add ".sensecontent .sense" to the path. - uniqueNodePath = uniqueNodePath + ".sensecontent .sense "; + // get the wrong value for ".sense-?. So add ".sensecontent .sense " to the path. + uniqueNodePath = uniqueNodePath + ".sensecontent .sense "; // Intentionally include the space at the end. uniqueNodeName = ".mainentrysubentry"; ii++; } @@ -127,7 +127,7 @@ public string AddStyles(ConfigurableDictionaryNode node) AddUniquePathToStyleRules(styleRules, uniqueNodePath); _styleDictionary[uniqueNodeName] = styleRules; } - uniqueNodePath = uniqueNodePath + uniqueNodeName + " "; + uniqueNodePath = uniqueNodePath + uniqueNodeName + " "; // Intentionally include the space at the end. } return uniqueNodeName; } From f7461021db5dc08fb14d05d43553c669aa5fd7e7 Mon Sep 17 00:00:00 2001 From: John T Maxwell III Date: Mon, 24 Feb 2025 08:11:25 -0800 Subject: [PATCH 016/123] Fix LT-21953: Sort Reversal Subentries crash when subentry missing form (#268) --- Src/ManagedLgIcuCollator/LgIcuCollator.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Src/ManagedLgIcuCollator/LgIcuCollator.cs b/Src/ManagedLgIcuCollator/LgIcuCollator.cs index d36bb3942e..704b82d899 100644 --- a/Src/ManagedLgIcuCollator/LgIcuCollator.cs +++ b/Src/ManagedLgIcuCollator/LgIcuCollator.cs @@ -94,6 +94,10 @@ public void SortKeyRgch(string _ch, int cchIn, LgCollatingOptions colopt, int cc public int Compare(string bstrValue1, string bstrValue2, LgCollatingOptions colopt) { EnsureCollator(); + if (bstrValue1 == null) + bstrValue1 = ""; + if (bstrValue2 == null) + bstrValue2 = ""; var key1 = m_collator.GetSortKey(bstrValue1).KeyData; var key2 = m_collator.GetSortKey(bstrValue2).KeyData; From 3cb1d5c18966f6a7b1cd71c4b3ae7d4348b224d5 Mon Sep 17 00:00:00 2001 From: John T Maxwell III Date: Mon, 24 Feb 2025 09:21:16 -0800 Subject: [PATCH 017/123] Fix LT-21958 (Required Features for Lexeme Form should be visible) (#267) --- .../Configuration/Lexicon/browseDialogColumns.xml | 1 + .../Language Explorer/Configuration/Parts/LexEntryParts.xml | 3 +++ .../Language Explorer/Configuration/Parts/MorphologyParts.xml | 4 +++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/DistFiles/Language Explorer/Configuration/Lexicon/browseDialogColumns.xml b/DistFiles/Language Explorer/Configuration/Lexicon/browseDialogColumns.xml index 85441b0687..4b350c0836 100644 --- a/DistFiles/Language Explorer/Configuration/Lexicon/browseDialogColumns.xml +++ b/DistFiles/Language Explorer/Configuration/Lexicon/browseDialogColumns.xml @@ -175,6 +175,7 @@ + diff --git a/DistFiles/Language Explorer/Configuration/Parts/LexEntryParts.xml b/DistFiles/Language Explorer/Configuration/Parts/LexEntryParts.xml index 628e4a4386..04d9b4f0c2 100644 --- a/DistFiles/Language Explorer/Configuration/Parts/LexEntryParts.xml +++ b/DistFiles/Language Explorer/Configuration/Parts/LexEntryParts.xml @@ -367,6 +367,9 @@ + + + diff --git a/DistFiles/Language Explorer/Configuration/Parts/MorphologyParts.xml b/DistFiles/Language Explorer/Configuration/Parts/MorphologyParts.xml index 762342c1f3..f73bf68716 100644 --- a/DistFiles/Language Explorer/Configuration/Parts/MorphologyParts.xml +++ b/DistFiles/Language Explorer/Configuration/Parts/MorphologyParts.xml @@ -370,7 +370,9 @@ - + + + From eb20655e75065f12aade7c0224572e30bde3493c Mon Sep 17 00:00:00 2001 From: Mark Kidder <83427558+mark-sil@users.noreply.github.com> Date: Tue, 25 Feb 2025 13:03:13 -0500 Subject: [PATCH 018/123] LT-22042: Fix incorrect styles being used for subentries (#265) * LT-22042: Fix incorrect styles being used for subentries The style information used for the nodes under MainEntry->Subentries, is also used for MainEntry->Senses->Subentries. Fix the problem so that we now use the style information for both node paths. --- Src/xWorks/CssGenerator.cs | 53 +++++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/Src/xWorks/CssGenerator.cs b/Src/xWorks/CssGenerator.cs index da48f0d6f4..4f8d619ecf 100644 --- a/Src/xWorks/CssGenerator.cs +++ b/Src/xWorks/CssGenerator.cs @@ -97,37 +97,66 @@ public string AddStyles(ConfigurableDictionaryNode node) // Generate the unique name and css for each node (starting from the root node). string uniqueNodeName = null; string uniqueNodePath = null; + string sensesSubentryNodePath = null; + bool buildSenseSubentryRules = false; for (int ii=0; ii < nodes.Count; ii++) { var workingNode = nodes[ii]; - string nodePath = GetNodePath(workingNode); var workingClassName = $".{GetClassAttributeForConfig(workingNode)}"; // If this node is ".subentries" and the next node is ".mainentrysubentries", - // then set the uniqueNodeName to ".mainentrysubentry" and skip the next node. + // then we need to build the same set of rules twice, once for ".entry-1 .subentries-?" + // and once for ".entry-1 .senses-? .subentries-?", so that the xhtml can choose + // the rules associated with the correct path. We only need to build the two + // set for the children. The node ".entry-1 .senses-? .subentries-?" does NOT + // need a second set of rules. It's rules will get created through the normal + // execution of this method (and could use a different style). if (workingClassName == ".subentries" && (ii + 1 < nodes.Count) && $".{GetClassAttributeForConfig(nodes[ii + 1])}" == ".mainentrysubentries") { - // We need a specificity higher than ".senses-? .subentries-? .subentry" and - // because of the way the path to ".mainentrysubentries" is generated we can - // get the wrong value for ".sense-?. So add ".sensecontent .sense " to the path. - uniqueNodePath = uniqueNodePath + ".sensecontent .sense "; // Intentionally include the space at the end. - uniqueNodeName = ".mainentrysubentry"; - ii++; + // We need a specificity higher than ".entry-1 .senses-? .subentries-? .subentry" + // for the sensesSubentryNodePath so make it more specific by adding + // ".sensecontent .sense .subentry ". + sensesSubentryNodePath = uniqueNodePath + ".sensecontent .sense .subentry "; // Intentionally include the space at the end. } - else + + if (workingClassName == ".mainentrysubentries") { - uniqueNodeName = GetUniqueNodeName(nodePath, workingClassName); + buildSenseSubentryRules = true; + continue; } + uniqueNodeName = GetUniqueNodeName(workingNode, workingClassName); if (!_styleDictionary.ContainsKey(uniqueNodeName)) { + List senseSubentryRules = null; var styleRules = GenerateCssFromConfigurationNode(workingNode, uniqueNodeName, _propertyTable).NonEmpty(); styleRules = styleRules.Distinct().ToList(); // Remove duplicate rules. + // Make a copy of each rule and prepend the unique path for senses subentries. + if (buildSenseSubentryRules) + { + senseSubentryRules = styleRules.Select(x => new StyleRule(x.Declarations) + { + Selector = x.Selector, + Value = x.Value + }).ToList(); + AddUniquePathToStyleRules(senseSubentryRules, sensesSubentryNodePath); + } AddUniquePathToStyleRules(styleRules, uniqueNodePath); + + // Add the senses subentries rules to the standard subentries rules. + if (buildSenseSubentryRules) + { + styleRules.AddRange(senseSubentryRules); + } _styleDictionary[uniqueNodeName] = styleRules; } + uniqueNodePath = uniqueNodePath + uniqueNodeName + " "; // Intentionally include the space at the end. + if (buildSenseSubentryRules) + { + sensesSubentryNodePath = sensesSubentryNodePath + uniqueNodeName + " "; // Intentionally include the space at the end. + } } return uniqueNodeName; } @@ -155,11 +184,11 @@ private string GetNodePath(ConfigurableDictionaryNode node) /// To avoid problems with one node using the style assigned to a different node with the same /// name, assign a unique name to every node. ///
- /// A path containing Non-Unique names for all the nodes. /// The name without an appended unique number. /// - private string GetUniqueNodeName(string nodePath, string className) + private string GetUniqueNodeName(ConfigurableDictionaryNode node, string className) { + string nodePath = GetNodePath(node); if (_uniqueNodeNames.ContainsKey(nodePath)) { return _uniqueNodeNames[nodePath]; From 96156f68657880ef740cdeb70176f0f46b9de49d Mon Sep 17 00:00:00 2001 From: John Maxwell Date: Wed, 19 Feb 2025 10:55:51 -0800 Subject: [PATCH 019/123] Fix LT-22047 without breaking LT-21810 --- Src/LexText/ParserCore/ParserWorker.cs | 36 ++++++++++++-------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/Src/LexText/ParserCore/ParserWorker.cs b/Src/LexText/ParserCore/ParserWorker.cs index 5cb68ef4bd..48e16776eb 100644 --- a/Src/LexText/ParserCore/ParserWorker.cs +++ b/Src/LexText/ParserCore/ParserWorker.cs @@ -158,26 +158,24 @@ public bool UpdateWordform(IWfiWordform wordform, ParserPriority priority, bool if (sLower != form.Text) { var text = TsStringUtils.MakeString(sLower, form.get_WritingSystem(0)); - IWfiWordform lcWordform; - // We cannot use WfiWordformServices.FindOrCreateWordform because of props change (LT-21810). - // Only parse the lowercase version if it exists. - if (m_cache.ServiceLocator.GetInstance().TryGetObject(text, out lcWordform)) + var lcWord = normalizer.Normalize(sLower.Replace(' ', '.')); + ParseResult lcResult = null; + stopWatch.Start(); + using (var task = new TaskReport(String.Format(ParserCoreStrings.ksParsingX, word), m_taskUpdateHandler)) { - var lcWord = normalizer.Normalize(sLower.Replace(' ', '.')); - ParseResult lcResult = null; - stopWatch.Start(); - using (var task = new TaskReport(String.Format(ParserCoreStrings.ksParsingX, word), m_taskUpdateHandler)) - { - lcResult = m_parser.ParseWord(lcWord); - } - stopWatch.Stop(); - lcResult.ParseTime = stopWatch.ElapsedMilliseconds; - if (lcResult.Analyses.Count > 0 && lcResult.ErrorMessage == null) - { - m_parseFiler.ProcessParse(lcWordform, 0, lcResult, checkParser); - m_parseFiler.ProcessParse(wordform, priority, result, checkParser); - return true; - } + lcResult = m_parser.ParseWord(lcWord); + } + stopWatch.Stop(); + lcResult.ParseTime = stopWatch.ElapsedMilliseconds; + if (lcResult.Analyses.Count > 0 && lcResult.ErrorMessage == null) + { + IWfiWordform lcWordform = null; + NonUndoableUnitOfWorkHelper.Do( + m_cache.ActionHandlerAccessor, + () => lcWordform = WfiWordformServices.FindOrCreateWordform(m_cache, text)); + m_parseFiler.ProcessParse(lcWordform, 0, lcResult, checkParser); + m_parseFiler.ProcessParse(wordform, priority, result, checkParser); + return true; } } From 20c4f15dccb8a8d4aa92e7b0ea38452258b01d89 Mon Sep 17 00:00:00 2001 From: John Maxwell Date: Mon, 24 Feb 2025 08:08:06 -0800 Subject: [PATCH 020/123] Switch to DoUsingNewOrCurrentUOW --- Src/LexText/ParserCore/ParserWorker.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/LexText/ParserCore/ParserWorker.cs b/Src/LexText/ParserCore/ParserWorker.cs index 48e16776eb..524474ca86 100644 --- a/Src/LexText/ParserCore/ParserWorker.cs +++ b/Src/LexText/ParserCore/ParserWorker.cs @@ -170,7 +170,7 @@ public bool UpdateWordform(IWfiWordform wordform, ParserPriority priority, bool if (lcResult.Analyses.Count > 0 && lcResult.ErrorMessage == null) { IWfiWordform lcWordform = null; - NonUndoableUnitOfWorkHelper.Do( + NonUndoableUnitOfWorkHelper.DoUsingNewOrCurrentUOW( m_cache.ActionHandlerAccessor, () => lcWordform = WfiWordformServices.FindOrCreateWordform(m_cache, text)); m_parseFiler.ProcessParse(lcWordform, 0, lcResult, checkParser); From 0369b8644e05c873ce83f3c004e656a0ac231639 Mon Sep 17 00:00:00 2001 From: Jason Naylor Date: Wed, 26 Feb 2025 09:30:13 -0800 Subject: [PATCH 021/123] Use wildcard for release branch filters (#272) --- .github/workflows/CI.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index d1205d78a0..a05fb273c3 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -1,9 +1,9 @@ name: Flex CI on: push: - branches: ["release/9.1", "develop", "master", "feature/PubSub"] + branches: ["release/**", "develop", "master", "feature/PubSub"] pull_request: - branches: ["release/9.1", "develop", "master", "feature/PubSub"] + branches: ["release/**", "develop", "master", "feature/PubSub"] workflow_dispatch: concurrency: From f7496760e23dd2491122d066ed388a2fc0565f91 Mon Sep 17 00:00:00 2001 From: Jake Oliver Date: Thu, 27 Feb 2025 10:16:21 -0500 Subject: [PATCH 022/123] LT-7810: Clear Inflection Class and fix Preview in Bulk Edit (#274) --- Src/FdoUi/InflectionClassEditor.cs | 28 ++++++------------- .../InflectionClassPopupTreeManager.cs | 2 ++ 2 files changed, 11 insertions(+), 19 deletions(-) diff --git a/Src/FdoUi/InflectionClassEditor.cs b/Src/FdoUi/InflectionClassEditor.cs index 07176d211d..9050eb3ba6 100644 --- a/Src/FdoUi/InflectionClassEditor.cs +++ b/Src/FdoUi/InflectionClassEditor.cs @@ -249,7 +249,8 @@ private void m_pOSPopupTreeManager_AfterSelect(object sender, TreeViewEventArgs // Todo: user selected a part of speech. // Arrange to turn all relevant items blue. // Remember which item was selected so we can later 'doit'. - if (e.Node is HvoTreeNode) + var hvoNode = e.Node as HvoTreeNode; + if (hvoNode != null && hvoNode.Hvo != 0) { var hvo = (e.Node as HvoTreeNode).Hvo; var clid = m_cache.ServiceLocator.GetInstance().GetClsid(hvo); @@ -276,7 +277,7 @@ private void m_pOSPopupTreeManager_AfterSelect(object sender, TreeViewEventArgs // Tell the parent control that we may have changed the selected item so it can // enable or disable the Apply and Preview buttons based on the selection. if (ValueChanged != null) - ValueChanged(this, new FwObjectSelectionEventArgs(m_selectedHvo)); + ValueChanged(this, new FwObjectSelectionEventArgs(m_selectedHvo, hvoNode != null ? 0 : -1)); } /// @@ -330,7 +331,7 @@ public void DoIt(IEnumerable itemsToChange, ProgressState state) state.PercentDone = i * 20 / itemsToChange.Count(); state.Breath(); } - if (!IsItemEligible(m_cache.DomainDataByFlid, hvoSense, possiblePOS)) + if (!IsItemEligible(hvoSense)) continue; var ls = m_cache.ServiceLocator.GetInstance().GetObject(hvoSense); var msa = (IMoStemMsa)ls.MorphoSyntaxAnalysisRA; @@ -395,7 +396,7 @@ public void DoIt(IEnumerable itemsToChange, ProgressState state) // Adjust its POS as well as its inflection class, just to be sure. msmTarget = msm; msmTarget.PartOfSpeechRA = kvp.Key.Item2; - msmTarget.InflectionClassRA = m_cache.ServiceLocator.GetInstance().GetObject(m_selectedHvo); + msmTarget.InflectionClassRA = m_selectedHvo == 0 ? null : m_cache.ServiceLocator.GetInstance().GetObject(m_selectedHvo); break; } } @@ -406,7 +407,7 @@ public void DoIt(IEnumerable itemsToChange, ProgressState state) msmTarget = m_cache.ServiceLocator.GetInstance().Create(); entry.MorphoSyntaxAnalysesOC.Add(msmTarget); msmTarget.PartOfSpeechRA = kvp.Key.Item2; - msmTarget.InflectionClassRA = m_cache.ServiceLocator.GetInstance().GetObject(m_selectedHvo); + msmTarget.InflectionClassRA = m_selectedHvo == 0 ? null : m_cache.ServiceLocator.GetInstance().GetObject(m_selectedHvo); } // Finally! Make the senses we want to change use it. foreach (var ls in sensesToChange) @@ -455,7 +456,7 @@ public void FakeDoit(IEnumerable itemsToChange, int tagFakeFlid, int tagEna state.PercentDone = i * 100 / itemsToChange.Count(); state.Breath(); } - bool fEnable = IsItemEligible(m_sda, hvo, possiblePOS); + bool fEnable = IsItemEligible(hvo); if (fEnable) m_sda.SetString(hvo, tagFakeFlid, tss); m_sda.SetInt(hvo, tagEnable, (fEnable ? 1 : 0)); @@ -502,21 +503,10 @@ public List FieldPath } } - private bool IsItemEligible(ISilDataAccess sda, int hvo, HashSet possiblePOS) + private bool IsItemEligible(int hvo) { - bool fEnable = false; var ls = m_cache.ServiceLocator.GetInstance().GetObject(hvo); - if (ls.MorphoSyntaxAnalysisRA != null && ls.MorphoSyntaxAnalysisRA is IMoStemMsa) - { - var msa = ls.MorphoSyntaxAnalysisRA as IMoStemMsa; - var pos = msa.PartOfSpeechRA; - if (pos != null && possiblePOS.Contains(pos.Hvo)) - { - // Only show it as a change if it is different - fEnable = msa.InflectionClassRA == null || msa.InflectionClassRA.Hvo != m_selectedHvo; - } - } - return fEnable; + return ls.MorphoSyntaxAnalysisRA is IMoStemMsa; } private IPartOfSpeech GetPOS() diff --git a/Src/LexText/LexTextControls/InflectionClassPopupTreeManager.cs b/Src/LexText/LexTextControls/InflectionClassPopupTreeManager.cs index 4caa8b248b..38137a63cf 100644 --- a/Src/LexText/LexTextControls/InflectionClassPopupTreeManager.cs +++ b/Src/LexText/LexTextControls/InflectionClassPopupTreeManager.cs @@ -93,6 +93,8 @@ protected override TreeNode MakeMenuItems(PopupTree popupTree, int hvoTarget) if (match1 != null) match = match1; } + AddTimberLine(popupTree); + AddNotSureItem(popupTree); return match; } } From acb738549f78e056a8b2e5270e50e5983414250c Mon Sep 17 00:00:00 2001 From: Jason Naylor Date: Wed, 5 Mar 2025 14:07:57 -0800 Subject: [PATCH 023/123] Add Pictures to DictionaryConfiguration options (LT-22053) (#277) --- .../Configuration/DictionaryConfiguration.xsd | 6 ++++ .../Dictionary/Hybrid.fwdictconfig | 2 +- .../Dictionary/Lexeme.fwdictconfig | 2 +- .../Dictionary/Root.fwdictconfig | 2 +- .../AllReversalIndexes.fwdictconfig | 2 +- .../DictionaryConfigurationController.cs | 2 +- Src/xWorks/DictionaryConfigurationMigrator.cs | 2 +- Src/xWorks/DictionaryConfigurationModel.cs | 35 ++++++++++++++++++- Src/xWorks/DictionaryNodeOptions.cs | 23 ++++++------ Src/xWorks/PictureConfiguration.cs | 28 +++++++++++++++ Src/xWorks/xWorks.csproj | 1 + .../DictionaryConfigurationMigratorTests.cs | 2 +- .../DictionaryConfigurationModelTests.cs | 20 +++++++++-- 13 files changed, 105 insertions(+), 22 deletions(-) create mode 100644 Src/xWorks/PictureConfiguration.cs diff --git a/DistFiles/Language Explorer/Configuration/DictionaryConfiguration.xsd b/DistFiles/Language Explorer/Configuration/DictionaryConfiguration.xsd index fcf4fcef06..05802f59e0 100644 --- a/DistFiles/Language Explorer/Configuration/DictionaryConfiguration.xsd +++ b/DistFiles/Language Explorer/Configuration/DictionaryConfiguration.xsd @@ -19,6 +19,7 @@ + @@ -38,6 +39,10 @@ + + + + @@ -177,6 +182,7 @@ + diff --git a/DistFiles/Language Explorer/DefaultConfigurations/Dictionary/Hybrid.fwdictconfig b/DistFiles/Language Explorer/DefaultConfigurations/Dictionary/Hybrid.fwdictconfig index e63e7e08f7..d32ca9fb43 100644 --- a/DistFiles/Language Explorer/DefaultConfigurations/Dictionary/Hybrid.fwdictconfig +++ b/DistFiles/Language Explorer/DefaultConfigurations/Dictionary/Hybrid.fwdictconfig @@ -3,7 +3,7 @@ xsi:noNamespaceSchemaLocation="../../Configuration/DictionaryConfiguration.xsd" xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="Hybrid (complex forms as main entries and mini subentries)" - version="25" lastModified="2023-07-13" + version="26" lastModified="2025-02-26" allPublications="true" isRootBased="false"> diff --git a/DistFiles/Language Explorer/DefaultConfigurations/Dictionary/Lexeme.fwdictconfig b/DistFiles/Language Explorer/DefaultConfigurations/Dictionary/Lexeme.fwdictconfig index ae660e95a9..f3fe45e96c 100644 --- a/DistFiles/Language Explorer/DefaultConfigurations/Dictionary/Lexeme.fwdictconfig +++ b/DistFiles/Language Explorer/DefaultConfigurations/Dictionary/Lexeme.fwdictconfig @@ -1,5 +1,5 @@ - + diff --git a/DistFiles/Language Explorer/DefaultConfigurations/Dictionary/Root.fwdictconfig b/DistFiles/Language Explorer/DefaultConfigurations/Dictionary/Root.fwdictconfig index dd7ac90966..162e02723e 100644 --- a/DistFiles/Language Explorer/DefaultConfigurations/Dictionary/Root.fwdictconfig +++ b/DistFiles/Language Explorer/DefaultConfigurations/Dictionary/Root.fwdictconfig @@ -1,5 +1,5 @@ - + diff --git a/Src/xWorks/DictionaryConfigurationController.cs b/Src/xWorks/DictionaryConfigurationController.cs index 382f1a4e78..e0b2c7467e 100644 --- a/Src/xWorks/DictionaryConfigurationController.cs +++ b/Src/xWorks/DictionaryConfigurationController.cs @@ -706,7 +706,7 @@ internal enum Direction { Up, Down - }; + } /// /// Move a node among its siblings in the model, and cause the view to update accordingly. diff --git a/Src/xWorks/DictionaryConfigurationMigrator.cs b/Src/xWorks/DictionaryConfigurationMigrator.cs index 2fe6a175a7..b58a554d62 100644 --- a/Src/xWorks/DictionaryConfigurationMigrator.cs +++ b/Src/xWorks/DictionaryConfigurationMigrator.cs @@ -19,7 +19,7 @@ namespace SIL.FieldWorks.XWorks /// public class DictionaryConfigurationMigrator { - public const int VersionCurrent = 25; + public const int VersionCurrent = 26; internal const string NodePathSeparator = " > "; public const string RootFileName = "Root"; public const string HybridFileName = "Hybrid"; diff --git a/Src/xWorks/DictionaryConfigurationModel.cs b/Src/xWorks/DictionaryConfigurationModel.cs index 1de0ef10c8..5b89f5432d 100644 --- a/Src/xWorks/DictionaryConfigurationModel.cs +++ b/Src/xWorks/DictionaryConfigurationModel.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016 SIL International +// Copyright (c) 2014-2016 SIL International // This software is licensed under the LGPL, version 2.1 or later // (http://www.gnu.org/licenses/lgpl-2.1.html) @@ -105,6 +105,9 @@ public enum ConfigType { Hybrid, Lexeme, Root, Reversal } [XmlElement("HomographConfiguration")] public DictionaryHomographConfiguration HomographConfiguration { get; set; } + [XmlElement("Pictures")] + public PictureConfiguration Pictures { get; set; } + /// /// Checks which folder this will be saved in to determine if it is a reversal /// @@ -200,6 +203,11 @@ public void Load(LcmCache cache) HomographConfiguration.CustomHomographNumbers = string.Empty; } } + + if (Pictures == null) + { + Pictures = new PictureConfiguration(); + } // Handle any changes to the custom field definitions. (See https://jira.sil.org/browse/LT-16430.) // The "Merge" method handles both additions and deletions. DictionaryConfigurationController.MergeCustomFieldsIntoDictionaryModel(this, cache); @@ -279,6 +287,16 @@ public DictionaryConfigurationModel DeepClone() clone.Publications = new List(Publications); } + if (HomographConfiguration != null) + { + clone.HomographConfiguration = new DictionaryHomographConfiguration(HomographConfiguration); + } + + if (Pictures != null) + { + clone.Pictures = new PictureConfiguration(Pictures); + } + return clone; } @@ -322,6 +340,21 @@ public class DictionaryHomographConfiguration { public DictionaryHomographConfiguration() {} + public DictionaryHomographConfiguration(DictionaryHomographConfiguration other) : this() + { + if (other == null) + return; + + ShowHwNumber = other.ShowHwNumber; + ShowHwNumInCrossRef = other.ShowHwNumInCrossRef; + ShowHwNumInReversalCrossRef = other.ShowHwNumInReversalCrossRef; + ShowSenseNumber = other.ShowSenseNumber; + ShowSenseNumberReversal = other.ShowSenseNumberReversal; + HomographNumberBefore = other.HomographNumberBefore; + HomographWritingSystem = other.HomographWritingSystem; + CustomHomographNumberList = other.CustomHomographNumberList != null ? new List(other.CustomHomographNumberList) : null; + } + public DictionaryHomographConfiguration(HomographConfiguration config) { HomographNumberBefore = config.HomographNumberBefore; diff --git a/Src/xWorks/DictionaryNodeOptions.cs b/Src/xWorks/DictionaryNodeOptions.cs index 0cc3e17b77..209d8768a6 100644 --- a/Src/xWorks/DictionaryNodeOptions.cs +++ b/Src/xWorks/DictionaryNodeOptions.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2014 SIL International +// Copyright (c) 2014 SIL International // This software is licensed under the LGPL, version 2.1 or later // (http://www.gnu.org/licenses/lgpl-2.1.html) @@ -254,16 +254,6 @@ public override DictionaryNodeOptions DeepClone() /// Options for formatting Pictures public class DictionaryNodePictureOptions : DictionaryNodeOptions { - public enum AlignmentType - { - // Since Right=0, it is the default selected if nothing is specified in the xml - [XmlEnum("right")] - Right = 0, - [XmlEnum("left")] - Left - //todo: add options for above and below entry - } - [XmlAttribute(AttributeName = "minimumHeight")] public float MinimumHeight { get; set; } @@ -288,6 +278,17 @@ public override DictionaryNodeOptions DeepClone() } } + public enum AlignmentType + { + // Since Right=0, it is the default selected if nothing is specified in the xml + [XmlEnum("right")] + Right = 0, + [XmlEnum("left")] + Left, + [XmlEnum("center")] + Center + } + /// Options for allowing the grouping of nodes which are not related in the model public class DictionaryNodeGroupingOptions : DictionaryNodeOptions, IParaOption { diff --git a/Src/xWorks/PictureConfiguration.cs b/Src/xWorks/PictureConfiguration.cs new file mode 100644 index 0000000000..b757a1b4dc --- /dev/null +++ b/Src/xWorks/PictureConfiguration.cs @@ -0,0 +1,28 @@ +using System; +using System.Xml.Serialization; + +namespace SIL.FieldWorks.XWorks +{ + /// + /// Configuration shared by all pictures in a dictionary configuration. + /// + public class PictureConfiguration + { + public PictureConfiguration(PictureConfiguration other) + { + Alignment = other.Alignment; + Width = other.Width; + } + + public PictureConfiguration() { } + + [XmlAttribute("alignment")] + public AlignmentType Alignment { get; set; } = AlignmentType.Center; + + /// + /// Width of the picture in inches. + /// + [XmlAttribute("width")] + public float Width { get; set; } = 1.0f; + } +} \ No newline at end of file diff --git a/Src/xWorks/xWorks.csproj b/Src/xWorks/xWorks.csproj index 6eecc3e003..407b3b4e2d 100644 --- a/Src/xWorks/xWorks.csproj +++ b/Src/xWorks/xWorks.csproj @@ -438,6 +438,7 @@ + diff --git a/Src/xWorks/xWorksTests/DictionaryConfigurationMigrators/DictionaryConfigurationMigratorTests.cs b/Src/xWorks/xWorksTests/DictionaryConfigurationMigrators/DictionaryConfigurationMigratorTests.cs index 1a38fe5601..d4abf79697 100644 --- a/Src/xWorks/xWorksTests/DictionaryConfigurationMigrators/DictionaryConfigurationMigratorTests.cs +++ b/Src/xWorks/xWorksTests/DictionaryConfigurationMigrators/DictionaryConfigurationMigratorTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 SIL International +// Copyright (c) 2014-2017 SIL International // This software is licensed under the LGPL, version 2.1 or later // (http://www.gnu.org/licenses/lgpl-2.1.html) diff --git a/Src/xWorks/xWorksTests/DictionaryConfigurationModelTests.cs b/Src/xWorks/xWorksTests/DictionaryConfigurationModelTests.cs index 9af88db7a3..f12f4d240f 100644 --- a/Src/xWorks/xWorksTests/DictionaryConfigurationModelTests.cs +++ b/Src/xWorks/xWorksTests/DictionaryConfigurationModelTests.cs @@ -734,7 +734,7 @@ public void Save_ConfigWithPictureOptionsValidatesAgainstSchema() DictionaryNodeOptions = new DictionaryNodePictureOptions { StackMultiplePictures = true, - PictureLocation = DictionaryNodePictureOptions.AlignmentType.Left, + PictureLocation = AlignmentType.Left, MaximumHeight = maxHeight, MinimumHeight = minHeight, MaximumWidth = maxWidth, @@ -1212,7 +1212,8 @@ public void CanDeepClone() Parts = new List { parentNode }, SharedItems = new List { parentNode.DeepCloneUnderSameParent() }, Publications = new List { "unabridged", "college", "urban colloquialisms" }, - HomographConfiguration = new DictionaryHomographConfiguration { HomographNumberBefore = true, ShowHwNumber = false } + HomographConfiguration = new DictionaryHomographConfiguration { HomographNumberBefore = true, ShowHwNumber = false }, + Pictures = new PictureConfiguration { Alignment = AlignmentType.Center, Width = .5f } }; // SUT @@ -1229,7 +1230,20 @@ public void CanDeepClone() { Assert.AreEqual(model.Publications[i], clone.Publications[i]); } - Assert.AreEqual(model.HomographConfiguration, clone.HomographConfiguration); + Assert.That(model.HomographConfiguration, Is.Not.SameAs(clone.HomographConfiguration)); + // If we were on NUnit 4 + // Assert.That(model.HomographConfiguration, Is.EqualTo(clone.HomographConfiguration).UsingPropertiesComparer()); + // But we're not, so we have to do it manually or implement otherwise unnecessary equality interfaces + Assert.That(model.HomographConfiguration.CustomHomographNumbers, Is.EqualTo(clone.HomographConfiguration.CustomHomographNumbers)); + Assert.That(model.HomographConfiguration.HomographNumberBefore, Is.EqualTo(clone.HomographConfiguration.HomographNumberBefore)); + Assert.That(model.HomographConfiguration.HomographWritingSystem, Is.EqualTo(clone.HomographConfiguration.HomographWritingSystem)); + Assert.That(model.HomographConfiguration.ShowHwNumber, Is.EqualTo(clone.HomographConfiguration.ShowHwNumber)); + Assert.That(model.HomographConfiguration.ShowHwNumInCrossRef, Is.EqualTo(clone.HomographConfiguration.ShowHwNumInCrossRef)); + Assert.That(model.HomographConfiguration.ShowHwNumInReversalCrossRef, Is.EqualTo(clone.HomographConfiguration.ShowHwNumInReversalCrossRef)); + // Same here + Assert.That(model.Pictures, Is.Not.SameAs(clone.Pictures)); + Assert.That(model.Pictures.Alignment, Is.EqualTo(clone.Pictures.Alignment)); + Assert.That(model.Pictures.Width, Is.EqualTo(clone.Pictures.Width)); } [Test] From 27f6f85029701143c38346b9856bac1ad086c2ed Mon Sep 17 00:00:00 2001 From: John T Maxwell III Date: Tue, 4 Mar 2025 09:21:11 -0800 Subject: [PATCH 024/123] LT-22063: Crash while navigating to Word Analyses view (#276) Fix LT-22063 by returning if the object no longer exists (cherry picked from commit 7a1d1414551b52ba63bf6496287d48117dc11828) --- Src/xWorks/RecordClerk.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Src/xWorks/RecordClerk.cs b/Src/xWorks/RecordClerk.cs index a17b133313..21fed93873 100644 --- a/Src/xWorks/RecordClerk.cs +++ b/Src/xWorks/RecordClerk.cs @@ -1075,7 +1075,11 @@ public bool OnJumpToRecord(object argument) // target before complaining to the user about a filter being on. var mdc = (IFwMetaDataCacheManaged)m_list.VirtualListPublisher.MetaDataCache; int clidList = mdc.FieldExists(m_list.Flid) ? mdc.GetDstClsId(m_list.Flid) : -1; - int clidObj = m_list.Cache.ServiceLocator.GetInstance().GetObject(hvoTarget).ClassID; + ICmObjectRepository objRepository = m_list.Cache.ServiceLocator.GetInstance(); + if (!objRepository.TryGetObject(hvoTarget, out ICmObject targetObject)) + // The object was deleted (LT-22063). + return true; + int clidObj = targetObject.ClassID; // If (int) clidList is -1, that means it was for a decorator property and the IsSameOrSubclassOf // test won't be valid. From 6ed8f266b87afe5c23c03cb9a4c0eb8cd92acd17 Mon Sep 17 00:00:00 2001 From: Jake Oliver Date: Wed, 5 Mar 2025 17:27:33 -0500 Subject: [PATCH 025/123] LT-21730: Update interlinear enabled lines after configuration (#278) --- Src/LexText/Interlinear/FocusBoxController.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Src/LexText/Interlinear/FocusBoxController.cs b/Src/LexText/Interlinear/FocusBoxController.cs index d8d743f813..f22be1409c 100644 --- a/Src/LexText/Interlinear/FocusBoxController.cs +++ b/Src/LexText/Interlinear/FocusBoxController.cs @@ -59,6 +59,7 @@ void HandleFocusWrongButton(object sender, EventArgs e) public void UpdateLineChoices(InterlinLineChoices choices) { + m_lineChoices = choices; // Under certain circumstances this can get called when sandbox is null (LT-11468) if (m_sandbox != null) m_sandbox.UpdateLineChoices(choices); From 71249545842235f9650c73e75beefa267ff196e3 Mon Sep 17 00:00:00 2001 From: Jake Oliver Date: Mon, 10 Mar 2025 12:10:45 -0400 Subject: [PATCH 026/123] Refactor DoIt method in InflectionClassEditor.cs (#280) --- Src/FdoUi/InflectionClassEditor.cs | 59 ++++++++++-------------------- 1 file changed, 20 insertions(+), 39 deletions(-) diff --git a/Src/FdoUi/InflectionClassEditor.cs b/Src/FdoUi/InflectionClassEditor.cs index 9050eb3ba6..bb99b5a24a 100644 --- a/Src/FdoUi/InflectionClassEditor.cs +++ b/Src/FdoUi/InflectionClassEditor.cs @@ -355,50 +355,31 @@ public void DoIt(IEnumerable itemsToChange, ProgressState state) var entry = kvp.Key.Item1; var sensesToChange = kvp.Value; IMoStemMsa msmTarget = null; - foreach (var msa in entry.MorphoSyntaxAnalysesOC) - { - var msm = msa as IMoStemMsa; - if (msm != null && msm.InflectionClassRA != null && msm.InflectionClassRA.Hvo == m_selectedHvo) - { - // Can reuse this one! - msmTarget = msm; - break; - } - } + msmTarget = entry.MorphoSyntaxAnalysesOC + .OfType() + .FirstOrDefault(msm => msm.InflectionClassRA?.Hvo == m_selectedHvo); + if (msmTarget == null) { // See if we can reuse an existing MoStemMsa by changing it. // This is possible if it is used only by senses in the list, or not used at all. - var otherSenses = new List(); - if (entry.SensesOS.Count != sensesToChange.Count) - { - foreach (var ls in entry.SensesOS) - if (!sensesToChange.Contains(ls)) - otherSenses.Add(ls); - } - foreach (var msa in entry.MorphoSyntaxAnalysesOC) + var otherSenses = entry.SensesOS.Count != sensesToChange.Count + ? entry.SensesOS.Where(ls => !sensesToChange.Contains(ls)).ToList() + : new List(); + + var msm = entry.MorphoSyntaxAnalysesOC + .OfType() + .FirstOrDefault(ms => !otherSenses.Any(ls => ls.MorphoSyntaxAnalysisRA == ms)); + + if (msm != null) { - var msm = msa as IMoStemMsa; - if (msm == null) - continue; - bool fOk = true; - foreach (var ls in otherSenses) - { - if (ls.MorphoSyntaxAnalysisRA == msm) - { - fOk = false; - break; - } - } - if (fOk) - { - // Can reuse this one! Nothing we don't want to change uses it. - // Adjust its POS as well as its inflection class, just to be sure. - msmTarget = msm; - msmTarget.PartOfSpeechRA = kvp.Key.Item2; - msmTarget.InflectionClassRA = m_selectedHvo == 0 ? null : m_cache.ServiceLocator.GetInstance().GetObject(m_selectedHvo); - break; - } + // Can reuse this one! Nothing we don't want to change uses it. + // Adjust its POS as well as its inflection class, just to be sure. + msmTarget = msm; + msmTarget.PartOfSpeechRA = kvp.Key.Item2; + msmTarget.InflectionClassRA = m_selectedHvo == 0 + ? null + : m_cache.ServiceLocator.GetInstance().GetObject(m_selectedHvo); } } if (msmTarget == null) From 2962c9056fdee29de0cc0c0f155fc7b55955d192 Mon Sep 17 00:00:00 2001 From: Jason Naylor Date: Mon, 10 Mar 2025 14:20:39 -0700 Subject: [PATCH 027/123] Implement LT-22054: Add UI for setting picture options (#281) * Add a new DictionaryDetailsView for pictures that modifies the Pictures part of the DictionaryConfigurationModel - I chose to use the existing picture node to add the interaction as that is most discoverable. I decided that with a lable that indicated that the setting was for *all* pictures in the document a separate dialog was not required. This could prove to be unfounded optimism --- Src/xWorks/DictionaryDetailsController.cs | 27 +++- .../PictureOptionsView.Designer.cs | 136 ++++++++++++++++++ .../PictureOptionsView.cs | 43 ++++++ .../PictureOptionsView.resx | 120 ++++++++++++++++ Src/xWorks/PictureConfiguration.cs | 4 +- Src/xWorks/xWorks.csproj | 9 ++ Src/xWorks/xWorksStrings.Designer.cs | 27 ++++ Src/xWorks/xWorksStrings.resx | 9 ++ 8 files changed, 371 insertions(+), 4 deletions(-) create mode 100644 Src/xWorks/DictionaryDetailsView/PictureOptionsView.Designer.cs create mode 100644 Src/xWorks/DictionaryDetailsView/PictureOptionsView.cs create mode 100644 Src/xWorks/DictionaryDetailsView/PictureOptionsView.resx diff --git a/Src/xWorks/DictionaryDetailsController.cs b/Src/xWorks/DictionaryDetailsController.cs index cda846b858..493a1fa90c 100644 --- a/Src/xWorks/DictionaryDetailsController.cs +++ b/Src/xWorks/DictionaryDetailsController.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2016 SIL International +// Copyright (c) 2014-2016 SIL International // This software is licensed under the LGPL, version 2.1 or later // (http://www.gnu.org/licenses/lgpl-2.1.html) @@ -111,7 +111,7 @@ public void LoadNode(DictionaryConfigurationModel model, ConfigurableDictionaryN } else if (Options is DictionaryNodePictureOptions) { - // todo: loading options here once UX has been worked out + optionsView = LoadPictureOptions(model.Pictures); } else { @@ -196,6 +196,29 @@ public void LoadNode(DictionaryConfigurationModel model, ConfigurableDictionaryN View.ResumeLayout(); } + private UserControl LoadPictureOptions(PictureConfiguration modelPictures) + { + var pictureOptionsView = new PictureOptionsView + { + Alignment = modelPictures.Alignment, + PictureWidth = modelPictures.Width + }; + + pictureOptionsView.AlignmentChanged += (sender, e) => + { + modelPictures.Alignment = pictureOptionsView.Alignment; + RefreshPreview(); + }; + + pictureOptionsView.WidthChanged += (sender, e) => + { + modelPictures.Width = pictureOptionsView.PictureWidth; + RefreshPreview(); + }; + + return pictureOptionsView; + } + internal static IEnumerable FindNodes( List nodes, Func match) { diff --git a/Src/xWorks/DictionaryDetailsView/PictureOptionsView.Designer.cs b/Src/xWorks/DictionaryDetailsView/PictureOptionsView.Designer.cs new file mode 100644 index 0000000000..c9a4e5598f --- /dev/null +++ b/Src/xWorks/DictionaryDetailsView/PictureOptionsView.Designer.cs @@ -0,0 +1,136 @@ +namespace SIL.FieldWorks.XWorks.DictionaryDetailsView +{ + partial class PictureOptionsView + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + System.Diagnostics.Debug.WriteLineIf(!disposing, "****** Missing Dispose() call for " + GetType().Name + ". ****** "); + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.tableLayoutPanel = new System.Windows.Forms.TableLayoutPanel(); + this.documentPictureSettingsLabel = new System.Windows.Forms.Label(); + this.alignmentLabel = new System.Windows.Forms.Label(); + this.alignmentComboBox = new System.Windows.Forms.ComboBox(); + this.widthLabel = new System.Windows.Forms.Label(); + this.widthTextBox = new System.Windows.Forms.TextBox(); + this.tableLayoutPanel.SuspendLayout(); + this.SuspendLayout(); + // + // tableLayoutPanel + // + this.tableLayoutPanel.ColumnCount = 2; + this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel.Controls.Add(this.documentPictureSettingsLabel, 0, 0); + this.tableLayoutPanel.Controls.Add(this.alignmentLabel, 0, 1); + this.tableLayoutPanel.Controls.Add(this.alignmentComboBox, 1, 1); + this.tableLayoutPanel.Controls.Add(this.widthLabel, 0, 2); + this.tableLayoutPanel.Controls.Add(this.widthTextBox, 1, 2); + this.tableLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel.Name = "tableLayoutPanel"; + this.tableLayoutPanel.RowCount = 3; + this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 30F)); + this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 30F)); + this.tableLayoutPanel.Size = new System.Drawing.Size(200, 80); + this.tableLayoutPanel.TabIndex = 0; + // + // documentImageSettingsLabel + // + this.documentPictureSettingsLabel.AutoSize = true; + this.tableLayoutPanel.SetColumnSpan(this.documentPictureSettingsLabel, 2); + this.documentPictureSettingsLabel.Location = new System.Drawing.Point(3, 0); + this.documentPictureSettingsLabel.Name = "documentPictureSettingsLabel"; + this.documentPictureSettingsLabel.Size = new System.Drawing.Size(123, 13); + this.documentPictureSettingsLabel.TabIndex = 0; + this.documentPictureSettingsLabel.Text = xWorksStrings.DocumentPictureSettingsLabelText; + this.documentPictureSettingsLabel.Margin = new System.Windows.Forms.Padding(3, 3, 3, 3); + // + // alignmentLabel + // + this.alignmentLabel.AutoSize = true; + this.alignmentLabel.Location = new System.Drawing.Point(3, 20); + this.alignmentLabel.Name = "alignmentLabel"; + this.alignmentLabel.Size = new System.Drawing.Size(53, 13); + this.alignmentLabel.TabIndex = 1; + this.alignmentLabel.Text = xWorksStrings.AlignmentLabelText; + this.alignmentLabel.Margin = new System.Windows.Forms.Padding(3, 6, 3, 3); + // + // alignmentComboBox + // + this.alignmentComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.alignmentComboBox.FormattingEnabled = true; + this.alignmentComboBox.Items.AddRange(new object[] { + SIL.FieldWorks.XWorks.AlignmentType.Left, + SIL.FieldWorks.XWorks.AlignmentType.Center, + SIL.FieldWorks.XWorks.AlignmentType.Right}); + this.alignmentComboBox.Location = new System.Drawing.Point(62, 23); + this.alignmentComboBox.Name = "alignmentComboBox"; + this.alignmentComboBox.Size = new System.Drawing.Size(121, 21); + this.alignmentComboBox.TabIndex = 2; + this.alignmentComboBox.Margin = new System.Windows.Forms.Padding(3, 3, 3, 3); + // + // widthLabel + // + this.widthLabel.AutoSize = true; + this.widthLabel.Location = new System.Drawing.Point(3, 50); + this.widthLabel.Name = "widthLabel"; + this.widthLabel.Size = new System.Drawing.Size(35, 13); + this.widthLabel.TabIndex = 3; + this.widthLabel.Text = xWorksStrings.WidthLabelText; + this.widthLabel.Margin = new System.Windows.Forms.Padding(3, 6, 3, 3); + // + // widthTextBox + // + this.widthTextBox.Location = new System.Drawing.Point(62, 53); + this.widthTextBox.Name = "widthTextBox"; + this.widthTextBox.Size = new System.Drawing.Size(121, 20); + this.widthTextBox.TabIndex = 4; + this.widthTextBox.Margin = new System.Windows.Forms.Padding(3, 3, 3, 3); + // + // PictureOptionsView + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.tableLayoutPanel); + this.Name = "PictureOptionsView"; + this.Size = new System.Drawing.Size(200, 80); + this.tableLayoutPanel.ResumeLayout(false); + this.tableLayoutPanel.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel; + private System.Windows.Forms.Label documentPictureSettingsLabel; + private System.Windows.Forms.Label alignmentLabel; + private System.Windows.Forms.ComboBox alignmentComboBox; + private System.Windows.Forms.Label widthLabel; + private System.Windows.Forms.TextBox widthTextBox; + } +} diff --git a/Src/xWorks/DictionaryDetailsView/PictureOptionsView.cs b/Src/xWorks/DictionaryDetailsView/PictureOptionsView.cs new file mode 100644 index 0000000000..332ffd30e6 --- /dev/null +++ b/Src/xWorks/DictionaryDetailsView/PictureOptionsView.cs @@ -0,0 +1,43 @@ +using System; +using System.Globalization; +using System.Windows.Forms; + +namespace SIL.FieldWorks.XWorks.DictionaryDetailsView +{ + /// + /// This view is responsible for the display of options for a PictureNode in the configuration dialog + /// + public partial class PictureOptionsView : UserControl + { + public PictureOptionsView() + { + InitializeComponent(); + } + + public AlignmentType Alignment + { + get => (AlignmentType)alignmentComboBox.SelectedItem; + set => alignmentComboBox.SelectedItem = value; + } + + public double PictureWidth + { + get => double.TryParse(widthTextBox.Text, NumberStyles.Float, null, out var width) + ? Math.Round(width, 2) + : 0.0f; + set => widthTextBox.Text = value.ToString("F2"); + } + + public event EventHandler AlignmentChanged + { + add => alignmentComboBox.SelectedIndexChanged += value; + remove => alignmentComboBox.SelectedIndexChanged -= value; + } + + public event EventHandler WidthChanged + { + add => widthTextBox.TextChanged += value; + remove => widthTextBox.TextChanged -= value; + } + } +} diff --git a/Src/xWorks/DictionaryDetailsView/PictureOptionsView.resx b/Src/xWorks/DictionaryDetailsView/PictureOptionsView.resx new file mode 100644 index 0000000000..d58980a38d --- /dev/null +++ b/Src/xWorks/DictionaryDetailsView/PictureOptionsView.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Src/xWorks/PictureConfiguration.cs b/Src/xWorks/PictureConfiguration.cs index b757a1b4dc..dd3d8b3647 100644 --- a/Src/xWorks/PictureConfiguration.cs +++ b/Src/xWorks/PictureConfiguration.cs @@ -17,12 +17,12 @@ public PictureConfiguration(PictureConfiguration other) public PictureConfiguration() { } [XmlAttribute("alignment")] - public AlignmentType Alignment { get; set; } = AlignmentType.Center; + public AlignmentType Alignment { get; set; } = AlignmentType.Right; /// /// Width of the picture in inches. /// [XmlAttribute("width")] - public float Width { get; set; } = 1.0f; + public double Width { get; set; } = 1.0f; } } \ No newline at end of file diff --git a/Src/xWorks/xWorks.csproj b/Src/xWorks/xWorks.csproj index 407b3b4e2d..453da6b82b 100644 --- a/Src/xWorks/xWorks.csproj +++ b/Src/xWorks/xWorks.csproj @@ -349,6 +349,12 @@ + + UserControl + + + PictureOptionsView.cs + Form @@ -610,6 +616,9 @@ AddCustomFieldDlg.cs Designer + + PictureOptionsView.cs + HeadWordNumbersDlg.cs diff --git a/Src/xWorks/xWorksStrings.Designer.cs b/Src/xWorks/xWorksStrings.Designer.cs index 9215844f61..fafcf7e845 100644 --- a/Src/xWorks/xWorksStrings.Designer.cs +++ b/Src/xWorks/xWorksStrings.Designer.cs @@ -69,6 +69,15 @@ internal static string ADeletedObject { } } + /// + /// Looks up a localized string similar to Alignment:. + /// + internal static string AlignmentLabelText { + get { + return ResourceManager.GetString("AlignmentLabelText", resourceCulture); + } + } + /// /// Looks up a localized string similar to All Analysis then all Vernacular Writing Systems. /// @@ -374,6 +383,15 @@ internal static string DidNotSelectValidWsForDb { } } + /// + /// Looks up a localized string similar to Document Picture Settings:. + /// + internal static string DocumentPictureSettingsLabelText { + get { + return ResourceManager.GetString("DocumentPictureSettingsLabelText", resourceCulture); + } + } + /// /// Looks up a localized string similar to Duplicate. /// @@ -3088,6 +3106,15 @@ internal static string WebonaryLogViewer_Save_a_copy { } } + /// + /// Looks up a localized string similar to Width (inches):. + /// + internal static string WidthLabelText { + get { + return ResourceManager.GetString("WidthLabelText", resourceCulture); + } + } + /// /// Looks up a localized string similar to You are resetting the following to factory defaults:. /// diff --git a/Src/xWorks/xWorksStrings.resx b/Src/xWorks/xWorksStrings.resx index 28699506ef..233ab11cad 100644 --- a/Src/xWorks/xWorksStrings.resx +++ b/Src/xWorks/xWorksStrings.resx @@ -158,6 +158,15 @@ Are you SURE you want to delete the field {0}? There are duplicate {0} fields named "{1}". Please choose a different name. + + Alignment: + + + Width (inches): + + + Document Picture Settings: + Duplicate Custom Field Names From bf83be4d5bd2a1e22a828af1b2e7e427d714990b Mon Sep 17 00:00:00 2001 From: John T Maxwell III Date: Thu, 6 Mar 2025 09:13:49 -0800 Subject: [PATCH 028/123] LT-22064: Issue with uppercase words in "Word Analyses" view (#279) Fix LT-22064: Issue with uppercase words in "Word Analyses" view (cherry picked from commit c117685ff6a8838f383c6aa2c39ab23c8c59db82) --- Src/LexText/Interlinear/FocusBoxController.ApproveAndMove.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Src/LexText/Interlinear/FocusBoxController.ApproveAndMove.cs b/Src/LexText/Interlinear/FocusBoxController.ApproveAndMove.cs index b6a3ac084f..d623797543 100644 --- a/Src/LexText/Interlinear/FocusBoxController.ApproveAndMove.cs +++ b/Src/LexText/Interlinear/FocusBoxController.ApproveAndMove.cs @@ -403,6 +403,9 @@ public virtual void ApproveAnalysis(AnalysisOccurrence occ, bool allOccurrences, FinishSettingAnalysis(newAnalysisTree, oldAnalysis); if (obsoleteAna != null) obsoleteAna.Delete(); + if (oldWf != null && oldWf != wf && oldWf.OccurrencesInTexts.Count() == 0) + // oldWf is probably the uppercase form of wf. + oldWf.Delete(); } // Caller must create UOW From 5d03a246611aa623d20e9ff9b8ebb9e90f3ed76e Mon Sep 17 00:00:00 2001 From: John T Maxwell III Date: Thu, 13 Mar 2025 13:37:28 -0700 Subject: [PATCH 029/123] Fix LT-22077: Crash in interlinear (#287) --- Src/LexText/Interlinear/FocusBoxController.ApproveAndMove.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/LexText/Interlinear/FocusBoxController.ApproveAndMove.cs b/Src/LexText/Interlinear/FocusBoxController.ApproveAndMove.cs index d623797543..527ac09d1b 100644 --- a/Src/LexText/Interlinear/FocusBoxController.ApproveAndMove.cs +++ b/Src/LexText/Interlinear/FocusBoxController.ApproveAndMove.cs @@ -403,7 +403,7 @@ public virtual void ApproveAnalysis(AnalysisOccurrence occ, bool allOccurrences, FinishSettingAnalysis(newAnalysisTree, oldAnalysis); if (obsoleteAna != null) obsoleteAna.Delete(); - if (oldWf != null && oldWf != wf && oldWf.OccurrencesInTexts.Count() == 0) + if (oldWf != null && oldWf.Cache != null && oldWf != wf && oldWf.OccurrencesInTexts.Count() == 0) // oldWf is probably the uppercase form of wf. oldWf.Delete(); } From 1562b0fc49472424c0949220ab5977a36a80033a Mon Sep 17 00:00:00 2001 From: Jason Naylor Date: Mon, 10 Mar 2025 13:33:28 -0700 Subject: [PATCH 030/123] Generate css to support image settings for width and alignment --- Src/xWorks/ConfigurableDictionaryNode.cs | 7 ++ Src/xWorks/CssGenerator.cs | 91 +++++++++++++++---- .../FirstAlphaMigrator.cs | 2 +- .../FirstBetaMigrator.cs | 2 +- Src/xWorks/DictionaryConfigurationModel.cs | 9 +- .../ConfiguredXHTMLGeneratorTests.cs | 27 +++++- Src/xWorks/xWorksTests/CssGeneratorTests.cs | 41 +++++---- .../DictionaryConfigurationControllerTests.cs | 6 +- .../DictionaryConfigurationModelTests.cs | 16 ++-- 9 files changed, 145 insertions(+), 56 deletions(-) diff --git a/Src/xWorks/ConfigurableDictionaryNode.cs b/Src/xWorks/ConfigurableDictionaryNode.cs index 22b1eed7db..f84eac835c 100644 --- a/Src/xWorks/ConfigurableDictionaryNode.cs +++ b/Src/xWorks/ConfigurableDictionaryNode.cs @@ -29,6 +29,13 @@ public override String ToString() [XmlIgnore] public StringTable StringTable { get; set; } + /// + /// This is the model that the configuration node is associated with, used for some + /// more global settings. + /// + [XmlIgnore] + public DictionaryConfigurationModel Model { get; set; } + /// /// The non-editable portion of the label to display for this node /// diff --git a/Src/xWorks/CssGenerator.cs b/Src/xWorks/CssGenerator.cs index 4f8d619ecf..b4869e2921 100644 --- a/Src/xWorks/CssGenerator.cs +++ b/Src/xWorks/CssGenerator.cs @@ -763,28 +763,83 @@ private static List GenerateCssForWritingSystemPrefix(ConfigurableDic private static List GenerateCssFromPictureOptions(ConfigurableDictionaryNode configNode, DictionaryNodePictureOptions pictureOptions, string baseSelection, LcmCache cache, ReadOnlyPropertyTable propertyTable) { + // Add the property to keep the caption centered under the image + var captionRule = new StyleRule(); + captionRule.Value = baseSelection + " .captionContent"; + // set the display as table-caption to allow the caption to be centered under the image + captionRule.Declarations.Properties.Add(new Property("display") + { + Term = new PrimitiveTerm(UnitType.Ident, "table-caption") + }); + // set the caption-side to get the caption below the image + captionRule.Declarations.Properties.Add(new Property("caption-side") + { + Term = new PrimitiveTerm(UnitType.Ident, "bottom") + }); + captionRule.Declarations.Properties.Add(new Property("text-align") + { + Term = new PrimitiveTerm(UnitType.Ident, "center") + }); + // clear the hanging indent + captionRule.Declarations.Properties.Add(new Property("text-indent") + { + Term = new PrimitiveTerm(UnitType.Point, 0) + }); var styles = GenerateSelectorsFromNode(configNode, ref baseSelection, cache, propertyTable); + styles.Add(captionRule); + // css to keep caption centered under the image will be using display: table to support old gecko + // display: table; + // flex-direction: column; /* Stack image and caption vertically */ + // text-align: left, right, center; /* moves the block img correctly */ + // margin: 0 auto; /* centers block under parent */ + // doing this and then adding a text-align:center for the caption content will center the caption under the image var pictureAndCaptionRule = new StyleRule(); pictureAndCaptionRule.Value = baseSelection; var pictureProps = pictureAndCaptionRule.Declarations.Properties; - pictureProps.Add(new Property("float") { Term = new PrimitiveTerm(UnitType.Ident, "right") }); - pictureProps.Add(new Property("text-align") { Term = new PrimitiveTerm(UnitType.Ident, "center") }); - var margin = new Property("margin"); - var marginValues = BuildTermList(TermList.TermSeparator.Space, new PrimitiveTerm(UnitType.Point, 0), - new PrimitiveTerm(UnitType.Point, 0), new PrimitiveTerm(UnitType.Point, 4), new PrimitiveTerm(UnitType.Point, 4)); - margin.Term = marginValues; + var alignment = configNode.Model.Pictures != null + ? configNode.Model.Pictures.Alignment.ToString().ToLowerInvariant() + : pictureOptions.PictureLocation.ToString().ToLowerInvariant(); + string marginPropName; + Term marginValue; + switch (alignment) + { + case "left": + // assign all available space to the right of the image as margin (push all content to the left) + marginPropName = "margin-right"; + marginValue = new PrimitiveTerm(UnitType.Ident, "auto"); + break; + case "center": + marginPropName = "margin"; + marginValue = BuildTermList(TermList.TermSeparator.Space, + new PrimitiveTerm(UnitType.Point, 0), + new PrimitiveTerm(UnitType.Ident, "auto")); + break; + default: + // assign all available space to the left of the image as margin (push all content to the right) + // right aligned images were the historical default + marginPropName = "margin-left"; + marginValue = new PrimitiveTerm(UnitType.Ident, "auto"); + break; + } + // add props for the flexbox layout of the pictures element + pictureProps.Add(new Property("display") { Term = new PrimitiveTerm(UnitType.Ident, "table") }); + var margin = new Property(marginPropName); + margin.Term = marginValue; pictureProps.Add(margin); - pictureProps.Add(new Property("padding") { Term = new PrimitiveTerm(UnitType.Point, 2) }); - pictureProps.Add(new Property("float") - { - Term = new PrimitiveTerm(UnitType.Ident, pictureOptions.PictureLocation.ToString().ToLowerInvariant()) - }); + // add the alignment property + pictureProps.Add(new Property("text-align") { Term = new PrimitiveTerm(UnitType.Ident, alignment) }); styles.Add(pictureAndCaptionRule); + // Add the properties to size the images var pictureRule = new StyleRule(); pictureRule.Value = pictureAndCaptionRule.Value + " img"; - if(pictureOptions.MinimumHeight > 0) + // display the img as a block so that it will take up the full width of the container + pictureRule.Declarations.Properties.Add(new Property("display") + { + Term = new PrimitiveTerm(UnitType.Ident, "block") + }); + if (pictureOptions.MinimumHeight > 0) { pictureRule.Declarations.Properties.Add(new Property("min-height") { @@ -805,13 +860,13 @@ private static List GenerateCssFromPictureOptions(ConfigurableDiction Term = new PrimitiveTerm(UnitType.Inch, pictureOptions.MinimumWidth) }); } - if(pictureOptions.MaximumWidth > 0) + + var width = pictureOptions.MaximumWidth; + pictureRule.Declarations.Properties.Add(new Property("max-width") { - pictureRule.Declarations.Properties.Add(new Property("max-width") - { - Term = new PrimitiveTerm(UnitType.Inch, pictureOptions.MaximumWidth) - }); - } + Term = new PrimitiveTerm(UnitType.Inch, configNode.Model.Pictures != null ? (float)configNode.Model.Pictures.Width : width) + }); + if (!IsEmptyRule(pictureRule)) styles.Add(pictureRule); return styles; diff --git a/Src/xWorks/DictionaryConfigurationMigrators/FirstAlphaMigrator.cs b/Src/xWorks/DictionaryConfigurationMigrators/FirstAlphaMigrator.cs index 281005d181..b8807204e2 100644 --- a/Src/xWorks/DictionaryConfigurationMigrators/FirstAlphaMigrator.cs +++ b/Src/xWorks/DictionaryConfigurationMigrators/FirstAlphaMigrator.cs @@ -59,7 +59,7 @@ internal void MigrateFrom83Alpha(DictionaryConfigurationModel alphaModel) if (alphaModel.Version == PreHistoricMigrator.VersionPre83 || alphaModel.Version == PreHistoricMigrator.VersionAlpha1) RemoveNonLoadableData(alphaModel.PartsAndSharedItems); // now that it's safe to specify them, it would be helpful to have parents in certain steps: - DictionaryConfigurationModel.SpecifyParentsAndReferences(alphaModel.Parts, alphaModel.SharedItems); + DictionaryConfigurationModel.SpecifyParentsAndReferences(alphaModel.Parts, sharedItems:alphaModel.SharedItems); switch (alphaModel.Version) { case -1: diff --git a/Src/xWorks/DictionaryConfigurationMigrators/FirstBetaMigrator.cs b/Src/xWorks/DictionaryConfigurationMigrators/FirstBetaMigrator.cs index 66963114ba..670e2b2710 100644 --- a/Src/xWorks/DictionaryConfigurationMigrators/FirstBetaMigrator.cs +++ b/Src/xWorks/DictionaryConfigurationMigrators/FirstBetaMigrator.cs @@ -93,7 +93,7 @@ internal DictionaryConfigurationModel LoadBetaDefaultForAlphaConfig(DictionaryCo internal void MigrateFrom83Alpha(ISimpleLogger logger, DictionaryConfigurationModel oldConfig, DictionaryConfigurationModel currentDefaultModel) { // it may be helpful to have parents and current custom fields in the oldConfig (currentDefaultModel already has them): - DictionaryConfigurationModel.SpecifyParentsAndReferences(oldConfig.Parts, oldConfig.SharedItems); + DictionaryConfigurationModel.SpecifyParentsAndReferences(oldConfig.Parts, sharedItems:oldConfig.SharedItems); DictionaryConfigurationController.MergeCustomFieldsIntoDictionaryModel(oldConfig, Cache); ChooseAppropriateComplexForms(oldConfig); // 13->14, but needed before rearranging and adding new nodes ConflateMainEntriesIfNecessary(logger, oldConfig); // 12->13, but needed before rearranging and adding new nodes diff --git a/Src/xWorks/DictionaryConfigurationModel.cs b/Src/xWorks/DictionaryConfigurationModel.cs index 5b89f5432d..9095bebbd2 100644 --- a/Src/xWorks/DictionaryConfigurationModel.cs +++ b/Src/xWorks/DictionaryConfigurationModel.cs @@ -185,7 +185,7 @@ public void Load(LcmCache cache) SharedItems = SharedItems ?? new List(); if (cache == null) return; - SpecifyParentsAndReferences(Parts, SharedItems); + SpecifyParentsAndReferences(Parts, this, SharedItems); if (AllPublications) Publications = DictionaryConfigurationController.GetAllPublications(cache); else @@ -278,7 +278,7 @@ public DictionaryConfigurationModel DeepClone() if (Parts != null) { clone.Parts = Parts.Select(node => node.DeepCloneUnderParent(null, true)).ToList(); - SpecifyParentsAndReferences(clone.Parts, clone.SharedItems); + SpecifyParentsAndReferences(clone.Parts, clone, clone.SharedItems); } // Clone Publications @@ -303,7 +303,7 @@ public DictionaryConfigurationModel DeepClone() /// /// Assign Parent and ReferencedNode properties to descendants of nodes. /// - internal static void SpecifyParentsAndReferences(List nodes, List sharedItems = null) + internal static void SpecifyParentsAndReferences(List nodes, DictionaryConfigurationModel model = null, List sharedItems = null) { if (nodes == null) throw new ArgumentNullException(); @@ -314,6 +314,7 @@ internal static void SpecifyParentsAndReferences(List { sensesNode, pictureNode, headwordNode }, FieldDescription = "LexEntry" }; - CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); + var model = new DictionaryConfigurationModel + { + Parts = new List(){ mainEntryNode }, + Pictures = new PictureConfiguration() + }; + CssGeneratorTests.PopulateFieldsForTesting(model); var entryOne = CreateInterestingLexEntry(Cache); AddHeadwordToEntry(entryOne, "HeadwordEn", m_wsEn); var sense = entryOne.SensesOS[0]; @@ -1350,7 +1355,12 @@ public void GenerateContentForEntry_CaptionOrHeadwordGetsHeadword() Children = new List { sensesNode, pictureNode, headwordNode }, FieldDescription = "LexEntry" }; - CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); + var model = new DictionaryConfigurationModel + { + Parts = new List(){ mainEntryNode }, + Pictures = new PictureConfiguration() + }; + CssGeneratorTests.PopulateFieldsForTesting(model); var entryOne = CreateInterestingLexEntry(Cache); AddHeadwordToEntry(entryOne, "HeadwordEn", m_wsEn); var sense = entryOne.SensesOS[0]; @@ -1393,7 +1403,12 @@ public void GenerateContentForEntry_CaptionOrHeadword_HandlePerWs() Children = new List { sensesNode, pictureNode, headwordNode }, FieldDescription = "LexEntry" }; - CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); + var model = new DictionaryConfigurationModel + { + Parts = new List () { mainEntryNode }, + Pictures = new PictureConfiguration() + }; + CssGeneratorTests.PopulateFieldsForTesting(model); var entryOne = CreateInterestingLexEntry(Cache); AddHeadwordToEntry(entryOne, "HeadwordEn", m_wsEn); AddHeadwordToEntry(entryOne, "HeadwordFr", m_wsFr); @@ -7983,7 +7998,11 @@ public void GenerateContentForEntry_FilterByPublication() Children = new List { mainHeadwordNode, mainPronunciationsNode, sensesNode, pictureNode, subentryNode, variantNode }, FieldDescription = "LexEntry" }; - CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); + var model = new DictionaryConfigurationModel + { + Parts = new List() { mainEntryNode } + }; + CssGeneratorTests.PopulateFieldsForTesting(model); const string matchFrenchEntry = "//span[@class='entry-1']/span[@lang='fr']"; const string matchFrenchPronunciation = "//span[@class='pronunciations-1']/span[@class='pronunciation']/span[@class='form-1']/span[@lang='fr']"; diff --git a/Src/xWorks/xWorksTests/CssGeneratorTests.cs b/Src/xWorks/xWorksTests/CssGeneratorTests.cs index abb5ecb5ef..8a1025e471 100644 --- a/Src/xWorks/xWorksTests/CssGeneratorTests.cs +++ b/Src/xWorks/xWorksTests/CssGeneratorTests.cs @@ -2711,9 +2711,12 @@ public void ClassAttributeForConfig_DuplicateNodeOverrideUsesLabelSuffix() /// /// The css for a picture is floated right and we want to clear the float at each entry. /// - [Test] - public void GenerateCssForConfiguration_PictureCssIsGenerated() + [TestCase("Left", 1.0f)] + [TestCase("Right", 1.5f)] + [TestCase("Center", 2.0f)] + public void GenerateCssForConfiguration_PictureCssIsGenerated(string alignString, double width) { + var align = (AlignmentType)Enum.Parse(typeof(AlignmentType), alignString); TestStyle style = GenerateStyle("Normal"); style.SetExplicitParaIntProp((int)FwTextPropType.ktptLeadingIndent, 0, LeadingIndent); ConfiguredLcmGenerator.AssemblyFile = "xWorksTests"; @@ -2722,7 +2725,7 @@ public void GenerateCssForConfiguration_PictureCssIsGenerated() var captionNode = new ConfigurableDictionaryNode { FieldDescription = "Caption", Style = "Normal" }; var memberNode = new ConfigurableDictionaryNode { - DictionaryNodeOptions = new DictionaryNodePictureOptions { MaximumWidth = 1 }, + DictionaryNodeOptions = new DictionaryNodePictureOptions { MaximumWidth = 1.0f }, CSSClassNameOverride = "pictures", FieldDescription = "Pictures", Children = new List { pictureFileNode, senseNumberNode, captionNode } @@ -2737,20 +2740,21 @@ public void GenerateCssForConfiguration_PictureCssIsGenerated() CSSClassNameOverride = "entry", Children = new List { sensesNode, memberNode } }; - PopulateFieldsForTesting(rootNode); - var config = new DictionaryConfigurationModel() + var config = new DictionaryConfigurationModel { - Parts = new List { rootNode } + Parts = new List { rootNode }, + Pictures = new PictureConfiguration { Alignment = align, Width = width} }; - + PopulateFieldsForTesting(config); + var alignment = Enum.GetName(typeof(AlignmentType), align)?.ToLower(); // SUT var cssWithPictureRules = CssGenerator.GenerateCssFromConfiguration(config, m_propertyTable); - VerifyRegex(cssWithPictureRules, @"^\s*\.picture.*{.*float:right.*}", "picture not floated right"); - VerifyRegex(cssWithPictureRules, @"^\s*\.picture.*img.*{.*max-width:1in;.*}", "css for image did not contain height contraint attribute"); - VerifyRegex(cssWithPictureRules, @"^\s*\.pictures.*picture.*{.*margin:\s*0pt\s*0pt\s*4pt\s*4pt.*;.*}", "css for image did not contain valid margin attribute"); + VerifyRegex(cssWithPictureRules, @"^\s*\.picture.*{.*text-align:" + alignment + ".*}", "picture not honoring alignment setting"); + VerifyRegex(cssWithPictureRules, @"^\s*\.picture.*img.*{.*max-width:" + width +"in;.*}", "css for image did not contain width constraint attribute"); VerifyRegex(cssWithPictureRules, @"^\s*\.entry\s*{.*clear:both.*}", "float not cleared at entry"); VerifyRegex(cssWithPictureRules, @"^\s*\s*.captionContent\s*.caption*\{.*margin-left:\s*24pt", "css for caption did not contain valid margin attribute"); + VerifyRegex(cssWithPictureRules, @"^\s*\.pictures\s*\.captionContent\s*\{.*text-indent:\s*0pt", "css for caption did not clear entry text indent"); } /// @@ -2795,12 +2799,13 @@ public void GenerateCssForConfiguration_PictureSubfieldsBeforeBetweenAfterIsAreG CSSClassNameOverride = "entry", Children = new List { sensesNode, memberNode } }; - PopulateFieldsForTesting(rootNode); var config = new DictionaryConfigurationModel() { - Parts = new List { rootNode } + Parts = new List { rootNode }, + Pictures = new PictureConfiguration() }; + PopulateFieldsForTesting(config); // SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(config, m_propertyTable); @@ -2853,12 +2858,13 @@ public void GenerateCssForConfiguration_PictureBeforeBetweenAfterIsAreGenerated( CSSClassNameOverride = "entry", Children = new List { sensesNode, memberNode } }; - PopulateFieldsForTesting(rootNode); var config = new DictionaryConfigurationModel() { - Parts = new List { rootNode } + Parts = new List { rootNode }, + Pictures = new PictureConfiguration() }; + PopulateFieldsForTesting(config); // SUT var cssResult = CssGenerator.GenerateCssFromConfiguration(config, m_propertyTable); @@ -2929,12 +2935,13 @@ public void GenerateCssForConfiguration_PictureWritesRulesForHeadwordAndGlossInC CSSClassNameOverride = "entry", Children = new List { memberNode } }; - PopulateFieldsForTesting(rootNode); var config = new DictionaryConfigurationModel() { - Parts = new List { rootNode } + Parts = new List { rootNode }, + Pictures = new PictureConfiguration() }; + PopulateFieldsForTesting(config); // SUT var cssWithPictureRules = CssGenerator.GenerateCssFromConfiguration(config, m_propertyTable); @@ -3946,7 +3953,7 @@ internal static void PopulateFieldsForTesting(DictionaryConfigurationModel model { Assert.NotNull(model); PopulateFieldsForTesting(model.Parts.Concat(model.SharedItems)); - DictionaryConfigurationModel.SpecifyParentsAndReferences(model.Parts, model.SharedItems); + DictionaryConfigurationModel.SpecifyParentsAndReferences(model.Parts, model, sharedItems:model.SharedItems); } private static void PopulateFieldsForTesting(IEnumerable nodes) diff --git a/Src/xWorks/xWorksTests/DictionaryConfigurationControllerTests.cs b/Src/xWorks/xWorksTests/DictionaryConfigurationControllerTests.cs index e9c6e96b4f..eae6fc88ab 100644 --- a/Src/xWorks/xWorksTests/DictionaryConfigurationControllerTests.cs +++ b/Src/xWorks/xWorksTests/DictionaryConfigurationControllerTests.cs @@ -2333,7 +2333,7 @@ public void ShareNodeAsReference_PreventsDuplicateSharedItemLabel() }; var preextantSharedNode = new ConfigurableDictionaryNode { Label = "Sharedparent" }; var model = DictionaryConfigurationModelTests.CreateSimpleSharingModel(configNode, preextantSharedNode); - DictionaryConfigurationModel.SpecifyParentsAndReferences(model.Parts, model.SharedItems); + DictionaryConfigurationModel.SpecifyParentsAndReferences(model.Parts, sharedItems: model.SharedItems); // SUT Assert.Throws(() => DictionaryConfigurationController.ShareNodeAsReference(model.SharedItems, configNode)); @@ -2351,7 +2351,7 @@ public void ShareNodeAsReference_PreventsDuplicateSharedItemCssClass() }; var preextantSharedNode = new ConfigurableDictionaryNode { CSSClassNameOverride = string.Format("shared{0}", m_field).ToLower() }; var model = DictionaryConfigurationModelTests.CreateSimpleSharingModel(configNode, preextantSharedNode); - DictionaryConfigurationModel.SpecifyParentsAndReferences(model.Parts, model.SharedItems); + DictionaryConfigurationModel.SpecifyParentsAndReferences(model.Parts, sharedItems: model.SharedItems); // SUT Assert.Throws(() => DictionaryConfigurationController.ShareNodeAsReference(model.SharedItems, configNode)); @@ -2369,7 +2369,7 @@ public void ShareNodeAsReference_DoesntShareNodeOfSameTypeAsPreextantSharedNode( }; var preextantSharedNode = new ConfigurableDictionaryNode { FieldDescription = m_field, Parent = new ConfigurableDictionaryNode() }; var model = DictionaryConfigurationModelTests.CreateSimpleSharingModel(configNode, preextantSharedNode); - DictionaryConfigurationModel.SpecifyParentsAndReferences(model.Parts, model.SharedItems); + DictionaryConfigurationModel.SpecifyParentsAndReferences(model.Parts, sharedItems: model.SharedItems); // SUT DictionaryConfigurationController.ShareNodeAsReference(model.SharedItems, configNode); diff --git a/Src/xWorks/xWorksTests/DictionaryConfigurationModelTests.cs b/Src/xWorks/xWorksTests/DictionaryConfigurationModelTests.cs index f12f4d240f..1b396d913e 100644 --- a/Src/xWorks/xWorksTests/DictionaryConfigurationModelTests.cs +++ b/Src/xWorks/xWorksTests/DictionaryConfigurationModelTests.cs @@ -1086,12 +1086,12 @@ public void SpecifyParentsAndReferences_ThrowsIfReferenceItemDNE() var model = new DictionaryConfigurationModel { Parts = new List { configNode }, SharedItems = null }; // SUT (DNE b/c no SharedItems) - Assert.Throws(() => DictionaryConfigurationModel.SpecifyParentsAndReferences(model.Parts, model.SharedItems), "No SharedItems!"); + Assert.Throws(() => DictionaryConfigurationModel.SpecifyParentsAndReferences(model.Parts, sharedItems: model.SharedItems), "No SharedItems!"); model.SharedItems = new List(); // SUT (DNE b/c SharedItems doesn't contain what was requested) - Assert.Throws(() => DictionaryConfigurationModel.SpecifyParentsAndReferences(model.Parts, model.SharedItems), "No matching item!"); + Assert.Throws(() => DictionaryConfigurationModel.SpecifyParentsAndReferences(model.Parts, sharedItems: model.SharedItems), "No matching item!"); } [Test] @@ -1102,14 +1102,14 @@ public void SpecifyParentsAndReferences_ProhibitsReferencesOfIncompatibleTypes() var model = CreateSimpleSharingModel(configNode, refConfigNode); // SUT (Field is different) - Assert.Throws(() => DictionaryConfigurationModel.SpecifyParentsAndReferences(model.Parts, model.SharedItems)); + Assert.Throws(() => DictionaryConfigurationModel.SpecifyParentsAndReferences(model.Parts, sharedItems: model.SharedItems)); Assert.That(configNode.ReferencedNode, Is.Null, "ReferencedNode should not have been set"); refConfigNode.FieldDescription = m_field; refConfigNode.SubField = "SensesOS"; // SUT (SubField is different) - Assert.Throws(() => DictionaryConfigurationModel.SpecifyParentsAndReferences(model.Parts, model.SharedItems)); + Assert.Throws(() => DictionaryConfigurationModel.SpecifyParentsAndReferences(model.Parts, sharedItems: model.SharedItems)); Assert.That(configNode.ReferencedNode, Is.Null, "ReferencedNode should not have been set"); } @@ -1121,7 +1121,7 @@ public void SpecifyParentsAndReferences_UpdatesReferencePropertyOfNodeWithRefere var model = CreateSimpleSharingModel(oneConfigNode, oneRefConfigNode); // SUT - DictionaryConfigurationModel.SpecifyParentsAndReferences(model.Parts, model.SharedItems); + DictionaryConfigurationModel.SpecifyParentsAndReferences(model.Parts, sharedItems: model.SharedItems); Assert.AreSame(oneRefConfigNode, oneConfigNode.ReferencedNode); } @@ -1139,7 +1139,7 @@ public void SpecifyParentsAndReferences_RefsPreferFirstParentIfSameLevel() }; // SUT - DictionaryConfigurationModel.SpecifyParentsAndReferences(model.Parts, model.SharedItems); + DictionaryConfigurationModel.SpecifyParentsAndReferences(model.Parts, sharedItems: model.SharedItems); Assert.AreSame(configNodeOne, refdConfigNode.Parent, "The Referenced node's 'Parent' should be the first to reference (breadth first)"); } @@ -1158,7 +1158,7 @@ public void SpecifyParentsAndReferences_RefsPreferShallowestParentEvenIfNotFirst }; // SUT - DictionaryConfigurationModel.SpecifyParentsAndReferences(model.Parts, model.SharedItems); + DictionaryConfigurationModel.SpecifyParentsAndReferences(model.Parts, sharedItems: model.SharedItems); Assert.AreSame(configNodeTwo, refdConfigNode.Parent, "The Referenced node's 'Parent' should be the first to reference (breadth first)"); } @@ -1176,7 +1176,7 @@ public void SpecifyParentsAndReferences_WorksForCircularReferences() var model = CreateSimpleSharingModel(configNode, refdConfigNode); // SUT - DictionaryConfigurationModel.SpecifyParentsAndReferences(model.Parts, model.SharedItems); + DictionaryConfigurationModel.SpecifyParentsAndReferences(model.Parts, sharedItems: model.SharedItems); Assert.AreSame(refdConfigNode, refdConfigNodeChild.Parent); Assert.AreSame(refdConfigNode, refdConfigNodeChild.ReferencedNode); From da25614ea54012963ee08040c7266e8ae1e003c9 Mon Sep 17 00:00:00 2001 From: Jason Naylor Date: Wed, 12 Mar 2025 15:15:52 -0700 Subject: [PATCH 031/123] Add maximum height setting and update and localize width label --- .../Configuration/DictionaryConfiguration.xsd | 1 + Src/xWorks/CssGenerator.cs | 13 ++-- Src/xWorks/DictionaryDetailsController.cs | 9 ++- .../PictureOptionsView.Designer.cs | 68 ++++++++++++------- .../PictureOptionsView.cs | 14 ++++ Src/xWorks/PictureConfiguration.cs | 8 ++- Src/xWorks/xWorksStrings.Designer.cs | 18 +++++ Src/xWorks/xWorksStrings.resx | 6 ++ 8 files changed, 105 insertions(+), 32 deletions(-) diff --git a/DistFiles/Language Explorer/Configuration/DictionaryConfiguration.xsd b/DistFiles/Language Explorer/Configuration/DictionaryConfiguration.xsd index 05802f59e0..be42241f69 100644 --- a/DistFiles/Language Explorer/Configuration/DictionaryConfiguration.xsd +++ b/DistFiles/Language Explorer/Configuration/DictionaryConfiguration.xsd @@ -42,6 +42,7 @@ + diff --git a/Src/xWorks/CssGenerator.cs b/Src/xWorks/CssGenerator.cs index b4869e2921..9c873ddf9e 100644 --- a/Src/xWorks/CssGenerator.cs +++ b/Src/xWorks/CssGenerator.cs @@ -846,13 +846,6 @@ private static List GenerateCssFromPictureOptions(ConfigurableDiction Term = new PrimitiveTerm(UnitType.Inch, pictureOptions.MinimumHeight) }); } - if(pictureOptions.MaximumHeight > 0) - { - pictureRule.Declarations.Properties.Add(new Property("max-height") - { - Term = new PrimitiveTerm(UnitType.Inch, pictureOptions.MaximumHeight) - }); - } if(pictureOptions.MinimumWidth > 0) { pictureRule.Declarations.Properties.Add(new Property("min-width") @@ -867,6 +860,12 @@ private static List GenerateCssFromPictureOptions(ConfigurableDiction Term = new PrimitiveTerm(UnitType.Inch, configNode.Model.Pictures != null ? (float)configNode.Model.Pictures.Width : width) }); + var height = pictureOptions.MaximumHeight; + pictureRule.Declarations.Properties.Add(new Property("max-height") + { + Term = new PrimitiveTerm(UnitType.Inch, configNode.Model.Pictures != null ? (float)configNode.Model.Pictures.Height : height) + }); + if (!IsEmptyRule(pictureRule)) styles.Add(pictureRule); return styles; diff --git a/Src/xWorks/DictionaryDetailsController.cs b/Src/xWorks/DictionaryDetailsController.cs index 493a1fa90c..151e812761 100644 --- a/Src/xWorks/DictionaryDetailsController.cs +++ b/Src/xWorks/DictionaryDetailsController.cs @@ -201,7 +201,8 @@ private UserControl LoadPictureOptions(PictureConfiguration modelPictures) var pictureOptionsView = new PictureOptionsView { Alignment = modelPictures.Alignment, - PictureWidth = modelPictures.Width + PictureWidth = modelPictures.Width, + PictureHeight = modelPictures.Height }; pictureOptionsView.AlignmentChanged += (sender, e) => @@ -216,6 +217,12 @@ private UserControl LoadPictureOptions(PictureConfiguration modelPictures) RefreshPreview(); }; + pictureOptionsView.HeightChanged += (sender, e) => + { + modelPictures.Height = pictureOptionsView.PictureHeight; + RefreshPreview(); + }; + return pictureOptionsView; } diff --git a/Src/xWorks/DictionaryDetailsView/PictureOptionsView.Designer.cs b/Src/xWorks/DictionaryDetailsView/PictureOptionsView.Designer.cs index c9a4e5598f..6bc2e76ec0 100644 --- a/Src/xWorks/DictionaryDetailsView/PictureOptionsView.Designer.cs +++ b/Src/xWorks/DictionaryDetailsView/PictureOptionsView.Designer.cs @@ -35,6 +35,8 @@ private void InitializeComponent() this.alignmentComboBox = new System.Windows.Forms.ComboBox(); this.widthLabel = new System.Windows.Forms.Label(); this.widthTextBox = new System.Windows.Forms.TextBox(); + this.heightLabel = new System.Windows.Forms.Label(); + this.heightTextBox = new System.Windows.Forms.TextBox(); this.tableLayoutPanel.SuspendLayout(); this.SuspendLayout(); // @@ -48,68 +50,86 @@ private void InitializeComponent() this.tableLayoutPanel.Controls.Add(this.alignmentComboBox, 1, 1); this.tableLayoutPanel.Controls.Add(this.widthLabel, 0, 2); this.tableLayoutPanel.Controls.Add(this.widthTextBox, 1, 2); + this.tableLayoutPanel.Controls.Add(this.heightLabel, 0, 3); + this.tableLayoutPanel.Controls.Add(this.heightTextBox, 1, 3); this.tableLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill; this.tableLayoutPanel.Location = new System.Drawing.Point(0, 0); this.tableLayoutPanel.Name = "tableLayoutPanel"; - this.tableLayoutPanel.RowCount = 3; + this.tableLayoutPanel.RowCount = 4; this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 30F)); this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 30F)); + this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); this.tableLayoutPanel.Size = new System.Drawing.Size(200, 80); this.tableLayoutPanel.TabIndex = 0; // - // documentImageSettingsLabel + // documentPictureSettingsLabel // this.documentPictureSettingsLabel.AutoSize = true; this.tableLayoutPanel.SetColumnSpan(this.documentPictureSettingsLabel, 2); - this.documentPictureSettingsLabel.Location = new System.Drawing.Point(3, 0); + this.documentPictureSettingsLabel.Location = new System.Drawing.Point(3, 3); + this.documentPictureSettingsLabel.Margin = new System.Windows.Forms.Padding(3); this.documentPictureSettingsLabel.Name = "documentPictureSettingsLabel"; - this.documentPictureSettingsLabel.Size = new System.Drawing.Size(123, 13); + this.documentPictureSettingsLabel.Size = new System.Drawing.Size(136, 13); this.documentPictureSettingsLabel.TabIndex = 0; - this.documentPictureSettingsLabel.Text = xWorksStrings.DocumentPictureSettingsLabelText; - this.documentPictureSettingsLabel.Margin = new System.Windows.Forms.Padding(3, 3, 3, 3); + this.documentPictureSettingsLabel.Text = "Document Picture Settings:"; // // alignmentLabel // this.alignmentLabel.AutoSize = true; - this.alignmentLabel.Location = new System.Drawing.Point(3, 20); + this.alignmentLabel.Location = new System.Drawing.Point(3, 26); + this.alignmentLabel.Margin = new System.Windows.Forms.Padding(3, 6, 3, 3); this.alignmentLabel.Name = "alignmentLabel"; - this.alignmentLabel.Size = new System.Drawing.Size(53, 13); + this.alignmentLabel.Size = new System.Drawing.Size(56, 13); this.alignmentLabel.TabIndex = 1; - this.alignmentLabel.Text = xWorksStrings.AlignmentLabelText; - this.alignmentLabel.Margin = new System.Windows.Forms.Padding(3, 6, 3, 3); + this.alignmentLabel.Text = "Alignment:"; // // alignmentComboBox // this.alignmentComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.alignmentComboBox.FormattingEnabled = true; this.alignmentComboBox.Items.AddRange(new object[] { - SIL.FieldWorks.XWorks.AlignmentType.Left, - SIL.FieldWorks.XWorks.AlignmentType.Center, - SIL.FieldWorks.XWorks.AlignmentType.Right}); - this.alignmentComboBox.Location = new System.Drawing.Point(62, 23); + SIL.FieldWorks.XWorks.AlignmentType.Left, + SIL.FieldWorks.XWorks.AlignmentType.Center, + SIL.FieldWorks.XWorks.AlignmentType.Right}); + this.alignmentComboBox.Location = new System.Drawing.Point(109, 23); this.alignmentComboBox.Name = "alignmentComboBox"; - this.alignmentComboBox.Size = new System.Drawing.Size(121, 21); + this.alignmentComboBox.Size = new System.Drawing.Size(88, 21); this.alignmentComboBox.TabIndex = 2; - this.alignmentComboBox.Margin = new System.Windows.Forms.Padding(3, 3, 3, 3); // // widthLabel // this.widthLabel.AutoSize = true; - this.widthLabel.Location = new System.Drawing.Point(3, 50); + this.widthLabel.Location = new System.Drawing.Point(3, 56); + this.widthLabel.Margin = new System.Windows.Forms.Padding(3, 6, 3, 3); this.widthLabel.Name = "widthLabel"; - this.widthLabel.Size = new System.Drawing.Size(35, 13); + this.widthLabel.Size = new System.Drawing.Size(78, 13); this.widthLabel.TabIndex = 3; - this.widthLabel.Text = xWorksStrings.WidthLabelText; - this.widthLabel.Margin = new System.Windows.Forms.Padding(3, 6, 3, 3); + this.widthLabel.Text = xWorksStrings.PictureOptionsView_MaxWidthLabel; // // widthTextBox // - this.widthTextBox.Location = new System.Drawing.Point(62, 53); + this.widthTextBox.Location = new System.Drawing.Point(109, 53); this.widthTextBox.Name = "widthTextBox"; - this.widthTextBox.Size = new System.Drawing.Size(121, 20); + this.widthTextBox.Size = new System.Drawing.Size(88, 20); this.widthTextBox.TabIndex = 4; - this.widthTextBox.Margin = new System.Windows.Forms.Padding(3, 3, 3, 3); + // + // heightLabel + // + this.heightLabel.AutoSize = true; + this.heightLabel.Location = new System.Drawing.Point(3, 80); + this.heightLabel.Margin = new System.Windows.Forms.Padding(3, 6, 3, 3); + this.heightLabel.Name = "heightLabel"; + this.heightLabel.Size = new System.Drawing.Size(78, 13); + this.heightLabel.TabIndex = 5; + this.heightLabel.Text = xWorksStrings.PictureOptionsView_MaxHeightLabel; + // + // heightTextBox + // + this.heightTextBox.Location = new System.Drawing.Point(109, 83); + this.heightTextBox.Name = "heightTextBox"; + this.heightTextBox.Size = new System.Drawing.Size(88, 20); + this.heightTextBox.TabIndex = 6; // // PictureOptionsView // @@ -132,5 +152,7 @@ private void InitializeComponent() private System.Windows.Forms.ComboBox alignmentComboBox; private System.Windows.Forms.Label widthLabel; private System.Windows.Forms.TextBox widthTextBox; + private System.Windows.Forms.Label heightLabel; + private System.Windows.Forms.TextBox heightTextBox; } } diff --git a/Src/xWorks/DictionaryDetailsView/PictureOptionsView.cs b/Src/xWorks/DictionaryDetailsView/PictureOptionsView.cs index 332ffd30e6..7d031d8527 100644 --- a/Src/xWorks/DictionaryDetailsView/PictureOptionsView.cs +++ b/Src/xWorks/DictionaryDetailsView/PictureOptionsView.cs @@ -28,6 +28,14 @@ public double PictureWidth set => widthTextBox.Text = value.ToString("F2"); } + public double PictureHeight + { + get => double.TryParse(heightTextBox.Text, NumberStyles.Float, null, out var width) + ? Math.Round(width, 2) + : 0.0f; + set => heightTextBox.Text = value.ToString("F2"); + } + public event EventHandler AlignmentChanged { add => alignmentComboBox.SelectedIndexChanged += value; @@ -39,5 +47,11 @@ public event EventHandler WidthChanged add => widthTextBox.TextChanged += value; remove => widthTextBox.TextChanged -= value; } + + public event EventHandler HeightChanged + { + add => heightTextBox.TextChanged += value; + remove => heightTextBox.TextChanged -= value; + } } } diff --git a/Src/xWorks/PictureConfiguration.cs b/Src/xWorks/PictureConfiguration.cs index dd3d8b3647..4332ed5311 100644 --- a/Src/xWorks/PictureConfiguration.cs +++ b/Src/xWorks/PictureConfiguration.cs @@ -20,9 +20,15 @@ public PictureConfiguration() { } public AlignmentType Alignment { get; set; } = AlignmentType.Right; /// - /// Width of the picture in inches. + /// Maximum width of the picture in inches. /// [XmlAttribute("width")] public double Width { get; set; } = 1.0f; + + /// + /// Maximum height of the picture in inches. + /// + [XmlAttribute("height")] + public double Height { get; set; } = 1.0f; } } \ No newline at end of file diff --git a/Src/xWorks/xWorksStrings.Designer.cs b/Src/xWorks/xWorksStrings.Designer.cs index fafcf7e845..f9f601320d 100644 --- a/Src/xWorks/xWorksStrings.Designer.cs +++ b/Src/xWorks/xWorksStrings.Designer.cs @@ -2618,6 +2618,24 @@ internal static string NotVisibleLabel { } } + /// + /// Looks up a localized string similar to Max Height (inches):. + /// + internal static string PictureOptionsView_MaxHeightLabel { + get { + return ResourceManager.GetString("PictureOptionsView_MaxHeightLabel", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Max Width (inches):. + /// + internal static string PictureOptionsView_MaxWidthLabel { + get { + return ResourceManager.GetString("PictureOptionsView_MaxWidthLabel", resourceCulture); + } + } + /// /// Looks up a localized string similar to Problem Starting {0}. /// diff --git a/Src/xWorks/xWorksStrings.resx b/Src/xWorks/xWorksStrings.resx index 233ab11cad..b6ac2a28e9 100644 --- a/Src/xWorks/xWorksStrings.resx +++ b/Src/xWorks/xWorksStrings.resx @@ -1292,4 +1292,10 @@ See USFM documentation for help. Save a copy + + Max Height (inches): + + + Max Width (inches): + \ No newline at end of file From 69b455dd55622d3a06652d5cf1235362f7d31954 Mon Sep 17 00:00:00 2001 From: John T Maxwell III Date: Fri, 14 Mar 2025 11:17:18 -0700 Subject: [PATCH 032/123] Fix LT-22079: Yellow Crash occurs while performing Reparse all words (#288) --- Src/LexText/ParserCore/ParseFiler.cs | 35 +++++++++++++++++-- .../ParserCoreTests/ParseWorkerTests.cs | 4 +-- Src/LexText/ParserCore/ParserScheduler.cs | 2 +- Src/LexText/ParserCore/ParserWorker.cs | 14 +++----- 4 files changed, 40 insertions(+), 15 deletions(-) diff --git a/Src/LexText/ParserCore/ParseFiler.cs b/Src/LexText/ParserCore/ParseFiler.cs index 8f9faa3ea0..5479f1e0e7 100644 --- a/Src/LexText/ParserCore/ParseFiler.cs +++ b/Src/LexText/ParserCore/ParseFiler.cs @@ -8,7 +8,9 @@ using System.Linq; using SIL.LCModel; using SIL.LCModel.Application; +using SIL.LCModel.Core.KernelInterfaces; using SIL.LCModel.Core.Text; +using SIL.LCModel.DomainServices; using SIL.LCModel.Infrastructure; using XCore; @@ -122,7 +124,21 @@ public ParseFiler(LcmCache cache, Action taskUpdateHandler, IdleQueu public bool ProcessParse(IWfiWordform wordform, ParserPriority priority, ParseResult parseResult, bool checkParser = false) { lock (m_syncRoot) - m_workQueue.Enqueue(new WordformUpdateWork(wordform, priority, parseResult, checkParser)); + m_workQueue.Enqueue(new WordformUpdateWork(wordform, null, priority, parseResult, checkParser)); + m_idleQueue.Add(IdleQueuePriority.Low, UpdateWordforms); + return true; + } + + /// + /// Process the parse result without a wordform. + /// + /// The text. + /// The priority. + /// The parse result. + public bool ProcessParse(ITsString text, ParserPriority priority, ParseResult parseResult, bool checkParser = false) + { + lock (m_syncRoot) + m_workQueue.Enqueue(new WordformUpdateWork(null, text, priority, parseResult, checkParser)); m_idleQueue.Add(IdleQueuePriority.Low, UpdateWordforms); return true; } @@ -158,6 +174,11 @@ private bool UpdateWordforms(object parameter) { foreach (WordformUpdateWork work in results) { + if (work.Wordform == null) + { + // We postponed creation of the lowercase wordform till we were inside a UnitOfWorkHelper. + work.Wordform = WfiWordformServices.FindOrCreateWordform(m_cache, work.Text); + } if (work.CheckParser) { // This was just a test. Don't update data. @@ -290,14 +311,16 @@ private void SetUnsuccessfulParseEvals(IWfiWordform wordform, Opinions opinion) private class WordformUpdateWork { - private readonly IWfiWordform m_wordform; + private IWfiWordform m_wordform; + private readonly ITsString m_text; private readonly ParserPriority m_priority; private readonly ParseResult m_parseResult; private readonly bool m_checkParser; - public WordformUpdateWork(IWfiWordform wordform, ParserPriority priority, ParseResult parseResult, bool checkParser) + public WordformUpdateWork(IWfiWordform wordform, ITsString text, ParserPriority priority, ParseResult parseResult, bool checkParser) { m_wordform = wordform; + m_text = text; m_priority = priority; m_parseResult = parseResult; m_checkParser = checkParser; @@ -306,6 +329,12 @@ public WordformUpdateWork(IWfiWordform wordform, ParserPriority priority, ParseR public IWfiWordform Wordform { get { return m_wordform; } + set { m_wordform = value; } + } + + public ITsString Text + { + get { return m_text; } } public ParserPriority Priority diff --git a/Src/LexText/ParserCore/ParserCoreTests/ParseWorkerTests.cs b/Src/LexText/ParserCore/ParserCoreTests/ParseWorkerTests.cs index 10a7ae3748..8acc402583 100644 --- a/Src/LexText/ParserCore/ParserCoreTests/ParseWorkerTests.cs +++ b/Src/LexText/ParserCore/ParserCoreTests/ParseWorkerTests.cs @@ -146,7 +146,7 @@ public void UpdateWordform() // SUT // Parsing an uppercase wordform should cause the lowercase wordform to be parsed. // The uppercase wordform doesn't get a parse. - var bVal = parserWorker.UpdateWordform(catsUpperTest, ParserPriority.Low); + var bVal = parserWorker.ParseAndUpdateWordform(catsUpperTest, ParserPriority.Low); ExecuteIdleQueue(); Assert.IsTrue(bVal); CheckAnalysisSize("Cats", 0, false); @@ -154,7 +154,7 @@ public void UpdateWordform() // SUT // The lowercase wordform has already been parsed. - bVal = parserWorker.UpdateWordform(catsLowerTest, ParserPriority.Low); + bVal = parserWorker.ParseAndUpdateWordform(catsLowerTest, ParserPriority.Low); ExecuteIdleQueue(); Assert.IsTrue(bVal); CheckAnalysisSize("Cats", 0, false); diff --git a/Src/LexText/ParserCore/ParserScheduler.cs b/Src/LexText/ParserCore/ParserScheduler.cs index 9527d8a885..bd480ee935 100644 --- a/Src/LexText/ParserCore/ParserScheduler.cs +++ b/Src/LexText/ParserCore/ParserScheduler.cs @@ -106,7 +106,7 @@ public UpdateWordformWork(ParserScheduler scheduler, ParserPriority priority, IW public override void DoWork() { - m_scheduler.m_parserWorker.UpdateWordform(m_wordform, m_priority, m_checkParser); + m_scheduler.m_parserWorker.ParseAndUpdateWordform(m_wordform, m_priority, m_checkParser); base.DoWork(); } } diff --git a/Src/LexText/ParserCore/ParserWorker.cs b/Src/LexText/ParserCore/ParserWorker.cs index 524474ca86..3280bc5f6b 100644 --- a/Src/LexText/ParserCore/ParserWorker.cs +++ b/Src/LexText/ParserCore/ParserWorker.cs @@ -115,7 +115,7 @@ public void TryAWord(string sForm, bool fDoTrace, int[] sSelectTraceMorphs) } } - public bool UpdateWordform(IWfiWordform wordform, ParserPriority priority, bool checkParser = false) + public bool ParseAndUpdateWordform(IWfiWordform wordform, ParserPriority priority, bool checkParser = false) { CheckDisposed(); @@ -157,7 +157,7 @@ public bool UpdateWordform(IWfiWordform wordform, ParserPriority priority, bool if (sLower != form.Text) { - var text = TsStringUtils.MakeString(sLower, form.get_WritingSystem(0)); + var lcText = TsStringUtils.MakeString(sLower, form.get_WritingSystem(0)); var lcWord = normalizer.Normalize(sLower.Replace(' ', '.')); ParseResult lcResult = null; stopWatch.Start(); @@ -169,13 +169,9 @@ public bool UpdateWordform(IWfiWordform wordform, ParserPriority priority, bool lcResult.ParseTime = stopWatch.ElapsedMilliseconds; if (lcResult.Analyses.Count > 0 && lcResult.ErrorMessage == null) { - IWfiWordform lcWordform = null; - NonUndoableUnitOfWorkHelper.DoUsingNewOrCurrentUOW( - m_cache.ActionHandlerAccessor, - () => lcWordform = WfiWordformServices.FindOrCreateWordform(m_cache, text)); - m_parseFiler.ProcessParse(lcWordform, 0, lcResult, checkParser); - m_parseFiler.ProcessParse(wordform, priority, result, checkParser); - return true; + // Don't turn lcText into a wordform here. + // This avoids a problem with broadcasting PropChanged (cf. LT-22079). + m_parseFiler.ProcessParse(lcText, 0, lcResult, checkParser); } } From d2ced43c67cbe04d587684173cd2b17573950d1a Mon Sep 17 00:00:00 2001 From: "Ariel Ror." Date: Fri, 14 Mar 2025 16:47:50 -0400 Subject: [PATCH 033/123] LT-22052: Use textboxes instead of textframes for pictures (#284) * Use global picture settings in word export * Use unique IDs for all pictures * IDs for each graphic and drawing object within a single picture are unique and consecutive. --------- Co-authored-by: Jason Naylor --- Src/xWorks/LcmWordGenerator.cs | 274 ++++++++++++++++++++++++++---- Src/xWorks/WordStyleCollection.cs | 16 ++ Src/xWorks/WordStylesGenerator.cs | 39 ++--- 3 files changed, 268 insertions(+), 61 deletions(-) diff --git a/Src/xWorks/LcmWordGenerator.cs b/Src/xWorks/LcmWordGenerator.cs index e8ccdb704a..ad6a116982 100644 --- a/Src/xWorks/LcmWordGenerator.cs +++ b/Src/xWorks/LcmWordGenerator.cs @@ -25,6 +25,7 @@ using XCore; using XmlDrawing = DocumentFormat.OpenXml.Drawing; using DrawingWP = DocumentFormat.OpenXml.Drawing.Wordprocessing; +using DrawingShape = DocumentFormat.OpenXml.Office2010.Word.DrawingShape; using Pictures = DocumentFormat.OpenXml.Drawing.Pictures; using System.Text.RegularExpressions; using SIL.LCModel.Core.KernelInterfaces; @@ -40,8 +41,6 @@ public class LcmWordGenerator : ILcmContentGenerator, ILcmStylesGenerator private LcmCache Cache { get; } private static WordStyleCollection s_styleCollection = new WordStyleCollection(); private ReadOnlyPropertyTable _propertyTable; - internal const int maxImageHeightInches = 1; - internal const int maxImageWidthInches = 1; public static bool IsBidi { get; private set; } public LcmWordGenerator(LcmCache cache) @@ -568,6 +567,31 @@ public void AppendToParagraph(IFragment fragToCopy, Run run, bool forceNewParagr lastPar.AppendChild(CloneElement(fragToCopy, run)); } + public void AppendImageToTextbox(IFragment fragToCopy, Run run, WP.ParagraphProperties paragraphProps) + { + WP.TextBoxContent lastTextBox = GetLastTextBox(); + + if (lastTextBox == null) + return; + + WP.Paragraph newImagePar = new WP.Paragraph(); + newImagePar.Append(paragraphProps); + + // Deep clone the run b/c of its tree of properties and to maintain styles. + newImagePar.AppendChild(CloneElement(fragToCopy, run)); + + lastTextBox.AppendChild(newImagePar); + } + + public void AppendCaptionParagraphToTextbox(IFragment fragToCopy, WP.Paragraph para) + { + WP.TextBoxContent lastTextBox = GetLastTextBox(); + + if (lastTextBox == null) + return; + lastTextBox.AppendChild(CloneElement(fragToCopy, para)); + } + /// /// Does a deep clone of the element. If there is picture data then that is cloned /// from the copyFromFrag into 'this' frag. @@ -682,6 +706,16 @@ public WP.Paragraph GetLastParagraph() return GetNewParagraph(); } + public WP.TextBoxContent GetLastTextBox() + { + // Returns the textbox content belonging to the last textbox added to the document + List textBoxContList = + DocBody.Descendants().ToList(); + if (textBoxContList.Any()) + return textBoxContList.Last(); + return null; + } + /// /// Creates and returns a new paragraph. /// @@ -691,6 +725,175 @@ public WP.Paragraph GetNewParagraph() return newPar; } + public void AppendNewTextboxParagraph(IFragment frag, Run run, WP.ParagraphProperties paragraphProps, ConfigurableDictionaryNode config) + { + int uniqueGraphicId; + int uniqueInnerDrawingId; + int uniqueOuterDrawingId; + + // Lock style collection while getting the IDs to use for the Image & Textbox + lock (s_styleCollection) + { + // The xml textbox image structure consists of a graphic object that is nested inside a drawing object + // that is nested inside another drawing object. + // The unique ID for the outer drawing object should be incremented once from the inner drawing object, + // which should be incremented once from the unique ID of the innermost graphic object. + uniqueGraphicId = s_styleCollection.GetAndIncrementPictureUniqueIdCount; + uniqueInnerDrawingId = s_styleCollection.GetAndIncrementPictureUniqueIdCount; + uniqueOuterDrawingId = s_styleCollection.GetAndIncrementPictureUniqueIdCount; + } + + //Use the image alignment specified in FLEx for the textbox alignment, with right align as default + string alignment = "right"; + if (config.DictionaryNodeOptions is DictionaryNodePictureOptions) + alignment = config.Model.Pictures.Alignment.ToString().ToLower(); + + WP.Paragraph newImagePar = new WP.Paragraph(); + newImagePar.Append(paragraphProps); + // Deep clone the run b/c of its tree of properties and to maintain styles. + newImagePar.AppendChild(CloneElement(frag, run)); + + // Get the properties of the inner drawing object in order to set its unique ID. + DrawingWP.DocProperties innerDrawingObjectProps = + newImagePar.Descendants().FirstOrDefault(); + innerDrawingObjectProps.Id = Convert.ToUInt32(uniqueInnerDrawingId); + + // Get the properties of the innermost graphic object in order to set its unique ID. + // We will also use this to get the name of the image to use in the textbox. + Pictures.NonVisualDrawingProperties graphicObjectProps = + newImagePar.Descendants() + .FirstOrDefault(); + graphicObjectProps.Id = Convert.ToUInt32(uniqueGraphicId); + + // Calculate the height and width in emus to use for the drawing containing the textbox. + // Drawing extent is specified in English Metric Units or EMUs, 914400 EMUs corresponds to one inch, and there are 72 points per inch. + const double emusPerPoint = 914400.0/72.0; + DrawingWP.Extent textBoxExtent = newImagePar.Descendants().FirstOrDefault(); + + Int64Value extentX = (Int64Value)Math.Ceiling((double)textBoxExtent.Cx + (24.0 * emusPerPoint)); + Int64Value extentY = (Int64Value)Math.Ceiling((double)textBoxExtent.Cy + (48.0 * emusPerPoint)); + + WP.Paragraph textBoxPar = new WP.Paragraph(); + Run textBoxRun = new Run(); + Drawing textBoxDrawing = new Drawing(); + + DrawingWP.Anchor anchor = new DrawingWP.Anchor() + { + DistanceFromTop = (UInt32Value)0U, + DistanceFromBottom = (UInt32Value)0U, + DistanceFromLeft = (UInt32Value)0U, + DistanceFromRight = (UInt32Value)0U, + SimplePos = false, + RelativeHeight = (UInt32Value)0U, + BehindDoc = false, + Locked = false, + LayoutInCell = true, + AllowOverlap = false + }; + anchor.Append(new DrawingWP.SimplePosition() { X = 0L, Y = 0L}); + + // image textbox is anchored horizontally wrt the column + anchor.Append( + new DrawingWP.HorizontalPosition( + new DrawingWP.HorizontalAlignment(alignment) + ) + { + RelativeFrom = DrawingWP.HorizontalRelativePositionValues.Column + } + ); + + // image textbox is anchored vertically wrt the preceeding paragraph + anchor.Append( + new DrawingWP.VerticalPosition( + new DrawingWP.PositionOffset("0") + ) + { + RelativeFrom = DrawingWP.VerticalRelativePositionValues.Paragraph + } + ); + + // This extent must also be declared in the anchor's shapeproperties element. + anchor.Append( + new DrawingWP.Extent() + { + Cx = extentX, + Cy = extentY + } + ); + + // We don't apply an effect to the textbox, so effect extent is 0 + anchor.Append( + new DrawingWP.EffectExtent() + { + LeftEdge = 0L, + TopEdge = 0L, + RightEdge = 0L, + BottomEdge = 0L + } + ); + + // Text should wrap above and below the textbox + anchor.Append(new DrawingWP.WrapTopBottom()); + + // Need to add ID and name to the textbox drawing. Without them, the word document will be mis-formatted and unable to open. + // Use the name from the picture for the textbox. + anchor.Append(new DrawingWP.DocProperties() + { Id = Convert.ToUInt32(uniqueOuterDrawingId), Name = graphicObjectProps.Name }); + + // Graphic frame drawing properties must be specified or the word document will be mis-formatted and unable to open. + anchor.Append( + new DrawingWP.NonVisualGraphicFrameDrawingProperties( + new XmlDrawing.GraphicFrameLocks()//{ NoChangeAspect = true } + ) + ); + + anchor.Append( + new XmlDrawing.Graphic( + + new XmlDrawing.GraphicData( + + new DrawingShape.WordprocessingShape( + + new DrawingShape.NonVisualDrawingShapeProperties(){ TextBox = true }, + + new DrawingShape.ShapeProperties( + new XmlDrawing.TransformGroup( + new XmlDrawing.Offset(){X=0, Y=0}, + // Specify the extent here and in the anchor itself (see above) + new XmlDrawing.Extents(){Cx = extentX, Cy = extentY} + ), + new XmlDrawing.PresetGeometry() { Preset = XmlDrawing.ShapeTypeValues.Rectangle } + ) { BlackWhiteMode = XmlDrawing.BlackWhiteModeValues.Auto }, + + new DrawingShape.TextBoxInfo2( + new WP.TextBoxContent( + newImagePar + ) + ), + + new DrawingShape.TextBodyProperties( + // ShapeAutoFit allows the textbox to resize if its contents overflow + new XmlDrawing.ShapeAutoFit() + ) + { + Rotation = 0, + Vertical = XmlDrawing.TextVerticalValues.Horizontal, + Wrap = XmlDrawing.TextWrappingValues.Square, + Anchor = XmlDrawing.TextAnchoringTypeValues.Top, + AnchorCenter = false + } + ) + ) { Uri = "http://schemas.microsoft.com/office/word/2010/wordprocessingShape" } + ) + ); + + textBoxDrawing.Append(anchor); + textBoxRun.Append(textBoxDrawing); + textBoxPar.Append(textBoxRun); + DocBody.AppendChild(textBoxPar); + + } + /// /// Returns last run in the document if it contains any, /// else creates and returns a new run. @@ -1496,22 +1699,18 @@ public void AddEntryData(IFragmentWriter writer, List @@ -2090,7 +2299,7 @@ public static string AddImagePartToPackage(WordprocessingDocument doc, string im return mainPart.GetIdOfPart(imagePart); } - public static Drawing CreateImage(WordprocessingDocument doc, string filepath, string partId) + public static Drawing CreateImage(WordprocessingDocument doc, string filepath, string partId, double maxWidthInches, double maxHeightInches) { // Create a bitmap to store the image so we can track/preserve aspect ratio. var img = new BitmapImage(); @@ -2107,28 +2316,28 @@ public static Drawing CreateImage(WordprocessingDocument doc, string filepath, s var actHeightPx = img.PixelHeight; var horzRezDpi = img.DpiX; var vertRezDpi = img.DpiY; - var actWidthInches = (float)(actWidthPx / horzRezDpi); - var actHeightInches = (float)(actHeightPx / vertRezDpi); + var actWidthInches = actWidthPx / horzRezDpi; + var actHeightInches = actHeightPx / vertRezDpi; var ratioActualInches = actHeightInches / actWidthInches; - var ratioMaxInches = (float)(maxImageHeightInches) / (float)(maxImageWidthInches); + var ratioMaxInches = maxHeightInches / maxWidthInches; - // height/widthInches will store the actual height and width + // height/widthInches will store the final height and width // to use for the image in the Word doc. - float heightInches = maxImageHeightInches; - float widthInches = maxImageWidthInches; + double heightInches = maxHeightInches; + double widthInches = maxWidthInches; // If the ratio of the actual image is greater than the max ratio, // we leave height equal to the max height and scale width accordingly. if (ratioActualInches >= ratioMaxInches) { - widthInches = actWidthInches * (maxImageHeightInches / actHeightInches); + widthInches = actWidthInches * (maxHeightInches / actHeightInches); } // Otherwise, if the ratio of the actual image is less than the max ratio, // we leave width equal to the max width and scale height accordingly. else if (ratioActualInches < ratioMaxInches) { - heightInches = actHeightInches * (maxImageWidthInches / actWidthInches); + heightInches = actHeightInches * (maxWidthInches / actWidthInches); } // Calculate the actual height and width in emus to use for the image. @@ -2142,7 +2351,6 @@ public static Drawing CreateImage(WordprocessingDocument doc, string filepath, s // Create and add a floating image with image wrap set to top/bottom // Name for the image -- the name of the file after all containing folders and the file extension are removed. string name = (filepath.Split('\\').Last()).Split('.').First(); - var element = new Drawing( new DrawingWP.Inline( new DrawingWP.Extent() @@ -2159,7 +2367,7 @@ public static Drawing CreateImage(WordprocessingDocument doc, string filepath, s }, new DrawingWP.DocProperties() { - Id = (UInt32Value)1U, + // The drawing also needs an Id; we will add this when we add the image to the textbox. Name = name }, new DrawingWP.NonVisualGraphicFrameDrawingProperties( @@ -2170,12 +2378,12 @@ public static Drawing CreateImage(WordprocessingDocument doc, string filepath, s new Pictures.NonVisualPictureProperties( new Pictures.NonVisualDrawingProperties() { - Id = (UInt32Value)0U, + // The graphic also needs an Id; we will add this when we add the image to the textbox. Name = name }, new Pictures.NonVisualPictureDrawingProperties( new XmlDrawing.PictureLocks() - {NoChangeAspect = true, NoChangeArrowheads = true} + {NoChangeAspect = true} ) ), new Pictures.BlipFill( diff --git a/Src/xWorks/WordStyleCollection.cs b/Src/xWorks/WordStyleCollection.cs index c4ae80369d..0f2f4a399c 100644 --- a/Src/xWorks/WordStyleCollection.cs +++ b/Src/xWorks/WordStyleCollection.cs @@ -25,6 +25,7 @@ public class WordStyleCollection // private Dictionary> styleDictionary = new Dictionary>(); private int bulletAndNumberingUniqueIdCounter = 1; + private int pictureUniqueIdCounter = 1; /// /// Returns a single list containing all of the Styles. @@ -62,6 +63,7 @@ public void Clear() { styleDictionary.Clear(); bulletAndNumberingUniqueIdCounter = 1; + pictureUniqueIdCounter = 1; } } @@ -259,6 +261,20 @@ public int GetNewBulletAndNumberingUniqueId } } } + + /// + /// Returns a unique id that is used for picture IDs. + /// + public int GetAndIncrementPictureUniqueIdCount + { + get + { + lock (styleDictionary) + { + return pictureUniqueIdCounter++; + } + } + } } // WordStyleCollection dictionary values. diff --git a/Src/xWorks/WordStylesGenerator.cs b/Src/xWorks/WordStylesGenerator.cs index 97e6c298b6..ded55f469d 100644 --- a/Src/xWorks/WordStylesGenerator.cs +++ b/Src/xWorks/WordStylesGenerator.cs @@ -392,35 +392,18 @@ public static List GenerateWritingSystemsCharacterStyles(ReadOnlyP private static Style GenerateParagraphStyleFromPictureOptions(ConfigurableDictionaryNode configNode, DictionaryNodePictureOptions pictureOptions, LcmCache cache, ReadOnlyPropertyTable propertyTable) { - var frameStyle = new Style(); - - // A textframe for holding an image/caption has to be a paragraph - frameStyle.Type = StyleValues.Paragraph; - - // We use FLEX's max image width as the width for the textframe. - // Note: 1 inch is equivalent to 72 points, and width is specified in twentieths of a point. - // Thus, we calculate textframe width by multiplying max image width in inches by 72*30 = 1440 - var textFrameWidth = LcmWordGenerator.maxImageWidthInches * 1440; - - // We will leave a 4-pt border around the textframe--80 twentieths of a point. - var textFrameBorder = "80"; - - // A paragraph is turned into a textframe simply by adding a frameproperties object inside the paragraph properties. - // Note that the argument "Y = textFrameBorder" is necessary for the following reason: - // In Word 2019, in order for the image textframe to display below the entry it portrays, - // a positive y-value offset must be specified that matches or exceeds the border of the textframe. - // We also lock the image's anchor because this allows greater flexibility in positioning the image from within Word. - // Without a locked anchor, if a user drags a textframe, Word will arbitrarily change the anchor and snap the textframe into a new location, - // rather than allowing the user to drag the textframe to their desired location. - var textFrameProps = new FrameProperties() { Width = textFrameWidth.ToString(), HeightType = HeightRuleValues.Auto, HorizontalSpace = textFrameBorder, VerticalSpace = textFrameBorder, - Wrap = TextWrappingValues.NotBeside, VerticalPosition = VerticalAnchorValues.Text, HorizontalPosition = HorizontalAnchorValues.Text, XAlign = HorizontalAlignmentValues.Right, - Y=textFrameBorder, AnchorLock = new DocumentFormat.OpenXml.OnOffValue(true) }; + // Creating a style for the paragraph that will contain the image and caption + var textBoxStyle = new Style() { + Type = StyleValues.Paragraph, + StyleId = PictureAndCaptionTextframeStyle, + StyleName = new StyleName() { Val = PictureAndCaptionTextframeStyle } + }; + var parProps = new ParagraphProperties(); - frameStyle.StyleId = PictureAndCaptionTextframeStyle; - frameStyle.StyleName = new StyleName(){Val = PictureAndCaptionTextframeStyle}; - parProps.Append(textFrameProps); - frameStyle.Append(parProps); - return frameStyle; + // The image and caption should always be centered within the textbox. + parProps.Justification = new Justification() { Val = JustificationValues.Center }; ; + textBoxStyle.Append(parProps); + return textBoxStyle; } private static Styles GenerateWordStylesFromListAndParaOptions(ConfigurableDictionaryNode configNode, From a1fd5ab0b42a4b9ab17ee3b40e140d3c0725e31e Mon Sep 17 00:00:00 2001 From: "Ariel Ror." Date: Tue, 18 Mar 2025 13:27:48 -0400 Subject: [PATCH 034/123] LT-22049: Use default vernacular WS style for Letter Heading (#289) --- Src/xWorks/WordStylesGenerator.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Src/xWorks/WordStylesGenerator.cs b/Src/xWorks/WordStylesGenerator.cs index ded55f469d..97958a4f1c 100644 --- a/Src/xWorks/WordStylesGenerator.cs +++ b/Src/xWorks/WordStylesGenerator.cs @@ -51,7 +51,9 @@ public class WordStylesGenerator public static Style GenerateLetterHeaderParagraphStyle(ReadOnlyPropertyTable propertyTable, out BulletInfo? bulletInfo) { - var style = GenerateParagraphStyleFromLcmStyleSheet(LetterHeadingStyleName, DefaultStyle, propertyTable, out bulletInfo); + var cache = propertyTable.GetValue("cache"); + var wsId = cache.ServiceLocator.WritingSystems.DefaultVernacularWritingSystem.Handle; + var style = GenerateParagraphStyleFromLcmStyleSheet(LetterHeadingStyleName, wsId, propertyTable, out bulletInfo); style.StyleId = LetterHeadingDisplayName; style.StyleName.Val = style.StyleId; return style; From a2f4e78841a52ebe3f570d1f5de23e720da7aa02 Mon Sep 17 00:00:00 2001 From: Jason Naylor Date: Tue, 18 Mar 2025 14:11:51 -0700 Subject: [PATCH 035/123] Fix LT-22078: Correct lexical reference targets css classname --- Src/xWorks/ConfiguredLcmGenerator.cs | 4 +-- .../ConfiguredXHTMLGeneratorTests.cs | 34 +++++++++---------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Src/xWorks/ConfiguredLcmGenerator.cs b/Src/xWorks/ConfiguredLcmGenerator.cs index 5d4ab62faf..b6f9059b54 100644 --- a/Src/xWorks/ConfiguredLcmGenerator.cs +++ b/Src/xWorks/ConfiguredLcmGenerator.cs @@ -2155,9 +2155,9 @@ private static IFragment GenerateCrossReferenceChildren(ConfigurableDictionaryNo if (!content.IsNullOrEmpty()) { // targets + var className = settings.StylesGenerator.AddStyles(child).Trim('.'); settings.ContentGenerator.AddCollection(xw, child, IsBlockProperty(child), - CssGenerator.GetClassAttributeForConfig(child), content); - settings.StylesGenerator.AddStyles(child); + className, content); } break; case "OwnerType": diff --git a/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs b/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs index 89b1e4efdb..1e340c093e 100644 --- a/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs +++ b/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs @@ -3767,9 +3767,9 @@ public void GenerateContentForEntry_GeneratesLinksForPrimaryEntryReferences() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(otherMainEntry, mainEntryNode, null, settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( - "//span[@class='minimallexreferences-1']/span[@class='minimallexreference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='primaryentryrefs-1']/span[@class='primaryentryref']/span[@class='referencedentries-1']/span[@class='referencedentry']/span[@class='headword-1']/span[@lang='fr']/a[@href]", 1); + "//span[@class='minimallexreferences-1']/span[@class='minimallexreference']/span[@class='configtargets-1']/span[@class='configtarget']/span[@class='primaryentryrefs-1']/span[@class='primaryentryref']/span[@class='referencedentries-1']/span[@class='referencedentry']/span[@class='headword-1']/span[@lang='fr']/a[@href]", 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( - "//span[@class='minimallexreferences-1']/span[@class='minimallexreference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='primaryentryrefs-1']/span[@class='primaryentryref']/span[@class='referencedentries-1']/span[@class='referencedentry']/span[@class='headword-1']/span[@lang='fr']/a[@href][contains(text(), 'Test')]", 1); + "//span[@class='minimallexreferences-1']/span[@class='minimallexreference']/span[@class='configtargets-1']/span[@class='configtarget']/span[@class='primaryentryrefs-1']/span[@class='primaryentryref']/span[@class='referencedentries-1']/span[@class='referencedentry']/span[@class='headword-1']/span[@lang='fr']/a[@href][contains(text(), 'Test')]", 1); } [Test] @@ -3814,7 +3814,7 @@ public void GenerateContentForEntry_GeneratesLinksForCrossReferences() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( - "//span[@class='minimallexreferences-1']/span[@class='minimallexreference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='headword-1']/span[@lang='fr']/span[@lang='fr']/a[@href]", 4); + "//span[@class='minimallexreferences-1']/span[@class='minimallexreference']/span[@class='configtargets-1']/span[@class='configtarget']/span[@class='headword-1']/span[@lang='fr']/span[@lang='fr']/a[@href]", 4); } [Test] @@ -3920,7 +3920,7 @@ public void GenerateContentForEntry_GeneratesLinksForCrossReferencesWithReferenc //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( - "//span[@class='configtargets']/span[@class='configtarget']/span[@class='headword-1']/span[@lang='fr']//a[@href]", 4); + "//span[@class='configtargets-1']/span[@class='configtarget']/span[@class='headword-1']/span[@lang='fr']//a[@href]", 4); } [Test] @@ -3976,7 +3976,7 @@ public void GenerateContentForEntry_GeneratesCrossReferencesOnUnCheckConfigTarge //SUT- var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); AssertThatXmlIn.String(result).HasNoMatchForXpath( - "//span[@class='minimallexreferences-1']/span[@class='minimallexreference']/span[@class='configtargets']"); + "//span[@class='minimallexreferences-1']/span[@class='minimallexreference']/span[@class='configtargets-1']"); } [Test] @@ -4393,16 +4393,16 @@ public void GenerateContentForEntry_GeneratesAsymmetricRelationsProperly() var revNameXpath = string.Format( "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='ownertype_name-1']/span[@lang='en' and text()='{0}']", refTypeRevName); AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(revNameXpath, 1); - var badTarget1 = "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='gloss-1']"; + var badTarget1 = "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='configtargets-1']/span[@class='configtarget']/span[@class='gloss-1']"; AssertThatXmlIn.String(output).HasNoMatchForXpath(badTarget1); var badTarget2 = string.Format( - "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='headword-1']/span[@lang='fr' and text()='{0}']", secondWord); + "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='configtargets-1']/span[@class='configtarget']/span[@class='headword-1']/span[@lang='fr' and text()='{0}']", secondWord); AssertThatXmlIn.String(output).HasNoMatchForXpath(badTarget2); var badTarget3 = string.Format( - "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='headword-1']/span[@lang='fr' and text()='{0}']", thirdWord); + "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='configtargets-1']/span[@class='configtarget']/span[@class='headword-1']/span[@lang='fr' and text()='{0}']", thirdWord); AssertThatXmlIn.String(output).HasNoMatchForXpath(badTarget3); var goodTarget = string.Format( - "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='headword-1']/span[@lang='fr' and text()='{0}']", firstWord); + "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='configtargets-1']/span[@class='configtarget']/span[@class='headword-1']/span[@lang='fr' and text()='{0}']", firstWord); AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(goodTarget, 1); } @@ -4466,9 +4466,9 @@ public void GenerateContentForEntry_GeneratesConfigTargetsForSubSenseProperly() //SUT var output = ConfiguredLcmGenerator.GenerateContentForEntry(firstEntry, mainEntryNode, null, settings).ToString(); var goodTarget = string.Format( - "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='headword-1']/span[@lang='fr' and text()='{0}']", firstHeadword); + "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='configtargets-1']/span[@class='configtarget']/span[@class='headword-1']/span[@lang='fr' and text()='{0}']", firstHeadword); AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(goodTarget, 1); - var badTarget = "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='gloss-1']"; + var badTarget = "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='configtargets-1']/span[@class='configtarget']/span[@class='gloss-1']"; AssertThatXmlIn.String(output).HasNoMatchForXpath(badTarget); } @@ -6151,9 +6151,9 @@ public void GenerateContentForEntry_CustomFieldOnISenseOrEntryGeneratesContentFo var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); - var entryDataPath = string.Format("/div[@class='lexentry-1']/span[@class='mlrs-1']/span[@class='mlr']/span[@class='configtargets']/span[@class='configtarget']/span[@class='entrycstring-1']/span[text()='{0}']", entryCustomData); + var entryDataPath = string.Format("/div[@class='lexentry-1']/span[@class='mlrs-1']/span[@class='mlr']/span[@class='configtargets-1']/span[@class='configtarget']/span[@class='entrycstring-1']/span[text()='{0}']", entryCustomData); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(entryDataPath, 1); - var senseDataPath = string.Format("/div[@class='lexentry-1']/span[@class='mlrs-1']/span[@class='mlr']/span[@class='configtargets']/span[@class='configtarget']/span[@class='sensecstring']/span[text()='{0}']", senseCustomData); + var senseDataPath = string.Format("/div[@class='lexentry-1']/span[@class='mlrs-1']/span[@class='mlr']/span[@class='configtargets-1']/span[@class='configtarget']/span[@class='sensecstring']/span[text()='{0}']", senseCustomData); AssertThatXmlIn.String(result).HasNoMatchForXpath(senseDataPath, message: "Ref is to Entry; should be no Sense Custom Data"); } } @@ -6215,9 +6215,9 @@ public void GenerateContentForEntry_CustomFieldOnISenseOrEntryGeneratesContentFo var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); - var entryDataPath = string.Format("/div[@class='lexentry-1']/span[@class='mlrs-1']/span[@class='mlr']/span[@class='configtargets']/span[@class='configtarget']/span[@class='entrycstring-1']/span[text()='{0}']", entryCustomData); + var entryDataPath = string.Format("/div[@class='lexentry-1']/span[@class='mlrs-1']/span[@class='mlr']/span[@class='configtargets-1']/span[@class='configtarget']/span[@class='entrycstring-1']/span[text()='{0}']", entryCustomData); AssertThatXmlIn.String(result).HasNoMatchForXpath(entryDataPath, message: "Ref is to Sense; should be no Entry Custom Data"); - var senseDataPath = string.Format("/div[@class='lexentry-1']/span[@class='mlrs-1']/span[@class='mlr']/span[@class='configtargets']/span[@class='configtarget']/span[@class='sensecstring-1']/span[text()='{0}']", senseCustomData); + var senseDataPath = string.Format("/div[@class='lexentry-1']/span[@class='mlrs-1']/span[@class='mlr']/span[@class='configtargets-1']/span[@class='configtarget']/span[@class='sensecstring-1']/span[text()='{0}']", senseCustomData); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseDataPath, 1); } } @@ -9237,13 +9237,13 @@ private static string CrossRefOwnerTypeXpath(string type) private static string HeadwordOrderInCrossRefsXpath(int position, string headword) { - return string.Format("//span[@class='minimallexreferences-1']/span[@class='minimallexreference']/span[@class='configtargets']" + + return string.Format("//span[@class='minimallexreferences-1']/span[@class='minimallexreference']/span[@class='configtargets-1']" + "/span[@class='configtarget' and position()='{0}']/span/span/a[text()='{1}']", position, headword); } private static string HeadwordWsInCrossRefsXpath(string ws, string headword) // REVIEW (Hasso) 2017.04: move these helpers to Helpers? { - return string.Format("//span[@class='minimallexreferences-1']/span[@class='minimallexreference']/span[@class='configtargets']" + + return string.Format("//span[@class='minimallexreferences-1']/span[@class='minimallexreference']/span[@class='configtargets-1']" + "/span[@class='configtarget']/span/span[@lang='{0}']/a[text()='{1}']", ws, headword); } From fed435ea1fb113b1d0ebd1935226586e81f3ca1c Mon Sep 17 00:00:00 2001 From: "Ariel Ror." Date: Wed, 19 Mar 2025 12:09:40 -0400 Subject: [PATCH 036/123] LT-22081: Fix unbalanced columns on last page of exported doc (#291) In order to balance columns in the last page of the Word doc, 2 column section properties need to be added into the paragraph properties of the last paragraph of the doc, in addition to section properties being added as the last child element of the doc. --- Src/xWorks/LcmWordGenerator.cs | 42 +++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/Src/xWorks/LcmWordGenerator.cs b/Src/xWorks/LcmWordGenerator.cs index ad6a116982..15570276c6 100644 --- a/Src/xWorks/LcmWordGenerator.cs +++ b/Src/xWorks/LcmWordGenerator.cs @@ -132,19 +132,49 @@ public static void SavePublishedDocx(int[] entryHvos, DictionaryPublicationDecor // Set the last section of the document to be two columns and add the page headers. (The last section // is all the entries after the last letter header.) For the last section this information is stored - // different than all the other sections. It is stored as the last child element of the body. - var sectProps = new SectionProperties( + // differently than for all the other sections: + // + // For the last section of the document, section properties objects must be added in two places, + // otherwise the columns on the final page will not be balanced. + // + // First, a section properties object using two columns and containing the page headers must be + // added to the paragraph properties for the last paragraph in the document. + // + // Second, a section properties object using two columns but omitting header information must be + // added as the last child element of the body. + + //Adding the final section properties to the paragraph properties for the final paragraph. + WP.Paragraph docLastParagraph = fragment.GetLastParagraph(); + var lastParSectProps = new SectionProperties( new HeaderReference() { Id = WordStylesGenerator.PageHeaderIdEven, Type = HeaderFooterValues.Even }, new HeaderReference() { Id = WordStylesGenerator.PageHeaderIdOdd, Type = HeaderFooterValues.Default }, new Columns() { EqualWidth = true, ColumnCount = 2 }, new SectionType() { Val = SectionMarkValues.Continuous } - ); - // Set the section to BiDi so the columns are displayed right to left. + ); + if (IsBidi) + { + // Set the section to BiDi so the columns are displayed right to left. + lastParSectProps.Append(new BiDi()); + } + if (docLastParagraph.ParagraphProperties != null) + docLastParagraph.ParagraphProperties.Append(lastParSectProps); + else + { + docLastParagraph.Append(new ParagraphProperties()); + docLastParagraph.ParagraphProperties.Append(lastParSectProps); + } + + // Adding 2 column section properties without headers as doc's last child. + var lastChildSectProps = new SectionProperties( + new Columns() { EqualWidth = true, ColumnCount = 2 }, + new SectionType() { Val = SectionMarkValues.Continuous } + ); if (IsBidi) { - sectProps.Append(new BiDi()); + // Set the section to BiDi so the columns are displayed right to left. + lastChildSectProps.Append(new BiDi()); } - fragment.DocBody.Append(sectProps); + fragment.DocBody.Append(lastChildSectProps); if (progress != null) progress.Message = xWorksStrings.ksGeneratingStyleInfo; From e6ef722548fafb625dafda93acfb62e9eeb891ba Mon Sep 17 00:00:00 2001 From: Jason Naylor Date: Wed, 19 Mar 2025 09:11:25 -0700 Subject: [PATCH 037/123] Bump version to 9.2.7 Beta --- Src/MasterVersionInfo.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/MasterVersionInfo.txt b/Src/MasterVersionInfo.txt index 068145baa2..cac6de2493 100644 --- a/Src/MasterVersionInfo.txt +++ b/Src/MasterVersionInfo.txt @@ -1,4 +1,4 @@ FWMAJOR=9 FWMINOR=2 -FWREVISION=6 +FWREVISION=7 FWBETAVERSION=Beta From ad2aa533edd685b5ad848d52fbffc2183d3e7a78 Mon Sep 17 00:00:00 2001 From: Jason Naylor Date: Wed, 19 Mar 2025 11:15:37 -0700 Subject: [PATCH 038/123] Rescue patching from additional help file deletion --- Build/Installer.targets | 1 + 1 file changed, 1 insertion(+) diff --git a/Build/Installer.targets b/Build/Installer.targets index 9a0d9b546f..a0a0169006 100644 --- a/Build/Installer.targets +++ b/Build/Installer.targets @@ -103,6 +103,7 @@ + From d97ce98d5393f25afd5d5de4f215ba83f79d6fdf Mon Sep 17 00:00:00 2001 From: Jake Oliver Date: Thu, 20 Mar 2025 11:51:36 -0400 Subject: [PATCH 039/123] LT-12587: Add Not Sure in Inflection Features options in Bulk Edit (#293) --- Src/FdoUi/InflectionFeatureEditor.cs | 16 ++++++++++++---- .../InflectionFeaturePopupTreeManager.cs | 4 ++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/Src/FdoUi/InflectionFeatureEditor.cs b/Src/FdoUi/InflectionFeatureEditor.cs index 207b9455a7..4a8b6a4c18 100644 --- a/Src/FdoUi/InflectionFeatureEditor.cs +++ b/Src/FdoUi/InflectionFeatureEditor.cs @@ -253,7 +253,8 @@ private void m_pOSPopupTreeManager_AfterSelect(object sender, System.Windows.For // Todo: user selected a part of speech. // Arrange to turn all relevant items blue. // Remember which item was selected so we can later 'doit'. - if (e.Node == null) + var hvoNode = e.Node as HvoTreeNode; + if (hvoNode == null || hvoNode.Hvo == 0) { m_selectedHvo = 0; m_selectedLabel = ""; @@ -280,7 +281,7 @@ private void m_pOSPopupTreeManager_AfterSelect(object sender, System.Windows.For // Tell the parent control that we may have changed the selected item so it can // enable or disable the Apply and Preview buttons based on the selection. if (ValueChanged != null) - ValueChanged(this, new FwObjectSelectionEventArgs(m_selectedHvo)); + ValueChanged(this, new FwObjectSelectionEventArgs(m_selectedHvo, hvoNode != null ? 0 : -1)); } /// @@ -450,8 +451,13 @@ public void SetClearField() private void InitMsa(IMoStemMsa msmTarget, int hvoPos) { - msmTarget.PartOfSpeechRA = m_cache.ServiceLocator.GetObject(hvoPos) as IPartOfSpeech; - var newFeatures = (IFsFeatStruc)m_cache.ServiceLocator.GetObject(m_selectedHvo); + msmTarget.PartOfSpeechRA = m_cache.ServiceLocator.GetObject(hvoPos) as IPartOfSpeech;//var newFeatures = (IFsFeatStruc)m_cache.ServiceLocator.GetObject(m_selectedHvo); + var newFeatures = m_selectedHvo == 0 ? null : (IFsFeatStruc)m_cache.ServiceLocator.GetObject(m_selectedHvo); + if (newFeatures == null) + { + msmTarget.MsFeaturesOA = null; + return; + } msmTarget.CopyMsFeatures(newFeatures); } @@ -538,6 +544,8 @@ private bool IsItemEligible(ISilDataAccess sda, int hvo, HashSet possiblePO { bool fEnable = false; int hvoMsa = sda.get_ObjectProp(hvo, LexSenseTags.kflidMorphoSyntaxAnalysis); + if (hvo == 0) + return true; if (hvoMsa != 0) { int clsid = m_cache.ServiceLocator.GetInstance().GetObject(hvoMsa).ClassID; diff --git a/Src/LexText/LexTextControls/InflectionFeaturePopupTreeManager.cs b/Src/LexText/LexTextControls/InflectionFeaturePopupTreeManager.cs index e2b565aea1..d73ed462f4 100644 --- a/Src/LexText/LexTextControls/InflectionFeaturePopupTreeManager.cs +++ b/Src/LexText/LexTextControls/InflectionFeaturePopupTreeManager.cs @@ -63,6 +63,10 @@ protected override TreeNode MakeMenuItems(PopupTree popupTree, int hvoTarget) TsStringUtils.MakeString(LexTextControls.ksChooseInflFeats, Cache.WritingSystemFactory.UserWs), kMore)); } + + AddTimberLine(popupTree); + AddNotSureItem(popupTree); + return match; } From 6e44f284a9b0f1451a5a09b580ebf1bfa533a1bd Mon Sep 17 00:00:00 2001 From: John T Maxwell III Date: Thu, 20 Mar 2025 14:19:49 -0700 Subject: [PATCH 040/123] Fix LT-22083: Crash occurs while importing interlinear into empty pane (#294) Co-authored-by: Jake Oliver --- Src/LexText/Interlinear/InterlinDocRootSiteBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/LexText/Interlinear/InterlinDocRootSiteBase.cs b/Src/LexText/Interlinear/InterlinDocRootSiteBase.cs index 4c6580956c..6c65d7da4e 100644 --- a/Src/LexText/Interlinear/InterlinDocRootSiteBase.cs +++ b/Src/LexText/Interlinear/InterlinDocRootSiteBase.cs @@ -1030,7 +1030,7 @@ public virtual void PropChanged(int hvo, int tag, int ivMin, int cvIns, int cvDe { //If the RootStText is null we are either in a place that doesn't care about parser related updates // or we are not yet completely displaying the text, so we should be fine, I hope? (LT-12493) - if (SuspendResettingAnalysisCache || RootStText == null) + if (SuspendResettingAnalysisCache || RootStText == null || RootStText.Cache == null) return; switch (tag) From 2e4455c0921a80d4e88da63bf0433a9d90f1cfe0 Mon Sep 17 00:00:00 2001 From: John T Maxwell III Date: Thu, 20 Mar 2025 14:37:46 -0700 Subject: [PATCH 041/123] LT-22082: Still having capitalization problems (#295) * LT-22082: Fix capitalization problem with Run Tests * Fix LT_22082: Still having capitalization problems --- Src/LexText/ParserCore/ParseFiler.cs | 9 +++++++++ Src/LexText/ParserUI/ParserListener.cs | 18 +++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/Src/LexText/ParserCore/ParseFiler.cs b/Src/LexText/ParserCore/ParseFiler.cs index 5479f1e0e7..f74653775b 100644 --- a/Src/LexText/ParserCore/ParseFiler.cs +++ b/Src/LexText/ParserCore/ParseFiler.cs @@ -170,6 +170,8 @@ private bool UpdateWordforms(object parameter) m_workQueue.Clear(); } + // Update work.Wordform with its own NonUndoableUnitOfWorkHelper + // so that PropChanged will be triggered when it is updated below. NonUndoableUnitOfWorkHelper.Do(m_cache.ActionHandlerAccessor, () => { foreach (WordformUpdateWork work in results) @@ -179,6 +181,13 @@ private bool UpdateWordforms(object parameter) // We postponed creation of the lowercase wordform till we were inside a UnitOfWorkHelper. work.Wordform = WfiWordformServices.FindOrCreateWordform(m_cache, work.Text); } + } + }); + + NonUndoableUnitOfWorkHelper.Do(m_cache.ActionHandlerAccessor, () => + { + foreach (WordformUpdateWork work in results) + { if (work.CheckParser) { // This was just a test. Don't update data. diff --git a/Src/LexText/ParserUI/ParserListener.cs b/Src/LexText/ParserUI/ParserListener.cs index f95c9f6fd3..2bd114dd92 100644 --- a/Src/LexText/ParserUI/ParserListener.cs +++ b/Src/LexText/ParserUI/ParserListener.cs @@ -653,7 +653,11 @@ private void InitCheckParserResults(IEnumerable wordforms, string private void WordformUpdatedEventHandler(object sender, WordformUpdatedEventArgs e) { - if (e.CheckParser && m_checkParserResults != null && m_checkParserResults.ContainsKey(e.Wordform)) + if (e.CheckParser && m_checkParserResults != null && + (m_checkParserResults.ContainsKey(e.Wordform) || + // Only include an unanticipated wordform if it is the lowercase + // version of an existing uppercase wordform. + ParserResultsHasUppercase(e.Wordform))) { // Record the parse result. m_checkParserResults[e.Wordform] = e.ParseResult; @@ -675,6 +679,18 @@ private void WordformUpdatedEventHandler(object sender, WordformUpdatedEventArgs } } + private bool ParserResultsHasUppercase(IWfiWordform wordform) + { + string form = wordform.Form.BestVernacularAlternative.Text; + foreach (IWfiWordform key in m_checkParserResults.Keys) + { + string keyForm = key.Form.BestVernacularAlternative.Text; + if (keyForm.ToLower() == form) + return true; + } + return false; + } + /// /// Create a parser report from the parse results. /// From 4b9c711221029d387cf9c9195d65eecc3cf87409 Mon Sep 17 00:00:00 2001 From: John T Maxwell III Date: Fri, 21 Mar 2025 10:19:41 -0700 Subject: [PATCH 042/123] Fix LT-22084: Crash while performing undo after deleting text (#296) Co-authored-by: Jake Oliver --- Src/xWorks/RecordList.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/xWorks/RecordList.cs b/Src/xWorks/RecordList.cs index 7a5ed518c5..7629a1bbc3 100644 --- a/Src/xWorks/RecordList.cs +++ b/Src/xWorks/RecordList.cs @@ -1872,7 +1872,7 @@ public IManyOnePathSortItem SortItemAt(int index) { CheckDisposed(); - if (SortedObjects.Count == 0) + if (SortedObjects.Count <= index) return null; else return SortedObjects[index] as IManyOnePathSortItem; From fc49dd74225e23729e3fd2ff845b1577a04b2b56 Mon Sep 17 00:00:00 2001 From: Jake Oliver Date: Mon, 24 Mar 2025 13:45:14 -0400 Subject: [PATCH 043/123] Refactor DoIt method for InflectionFeatureEditor.cs (#297) --- Src/FdoUi/InflectionFeatureEditor.cs | 53 ++++++++-------------------- 1 file changed, 15 insertions(+), 38 deletions(-) diff --git a/Src/FdoUi/InflectionFeatureEditor.cs b/Src/FdoUi/InflectionFeatureEditor.cs index 4a8b6a4c18..5b8a8c2229 100644 --- a/Src/FdoUi/InflectionFeatureEditor.cs +++ b/Src/FdoUi/InflectionFeatureEditor.cs @@ -361,17 +361,9 @@ public void DoIt(IEnumerable itemsToChange, ProgressState state) } var entry = m_cache.ServiceLocator.GetInstance().GetObject(kvp.Key); var sensesToChange = kvp.Value; - IMoStemMsa msmTarget = null; - foreach (var msa in entry.MorphoSyntaxAnalysesOC) - { - var msm = msa as IMoStemMsa; - if (msm != null && MsaMatchesTarget(msm, fsTarget)) - { - // Can reuse this one! - msmTarget = msm; - break; - } - } + IMoStemMsa msmTarget = entry.MorphoSyntaxAnalysesOC.OfType() + .FirstOrDefault(msm => MsaMatchesTarget(msm, fsTarget)); + if (msmTarget == null) { // See if we can reuse an existing MoStemMsa by changing it. @@ -380,35 +372,20 @@ public void DoIt(IEnumerable itemsToChange, ProgressState state) var senses = new HashSet(entry.AllSenses.ToArray()); if (senses.Count != sensesToChange.Count) { - foreach (var ls in senses) - { - if (!sensesToChange.Contains(ls)) - otherSenses.Add(ls); - } + otherSenses = new HashSet(senses.Where(ls => !sensesToChange.Contains(ls))); } - foreach (var msa in entry.MorphoSyntaxAnalysesOC) + + var msm = entry.MorphoSyntaxAnalysesOC + .OfType() // filter only IMoStemMsa + .FirstOrDefault(msa => !otherSenses.Any(ls => ls.MorphoSyntaxAnalysisRA == msa)); + + if (msm != null) { - var msm = msa as IMoStemMsa; - if (msm == null) - continue; - bool fOk = true; - foreach (var ls in otherSenses) - { - if (ls.MorphoSyntaxAnalysisRA == msm) - { - fOk = false; - break; - } - } - if (fOk) - { - // Can reuse this one! Nothing we don't want to change uses it. - // Adjust its POS as well as its inflection feature, just to be sure. - // Ensure that we don't change the POS! See LT-6835. - msmTarget = msm; - InitMsa(msmTarget, msm.PartOfSpeechRA.Hvo); - break; - } + // Can reuse this one! Nothing we don't want to change uses it. + // Adjust its POS as well as its inflection feature, just to be sure. + // Ensure that we don't change the POS! See LT-6835. + msmTarget = msm; + InitMsa(msmTarget, msm.PartOfSpeechRA.Hvo); } } if (msmTarget == null) From 0b8a7e9fa2986318798f6da3b9b1995baddb741b Mon Sep 17 00:00:00 2001 From: John T Maxwell III Date: Tue, 25 Mar 2025 09:07:28 -0700 Subject: [PATCH 044/123] Fix LT-22086: Reversal Indexes crashes without any reversals (#298) --- Src/xWorks/RecordList.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Src/xWorks/RecordList.cs b/Src/xWorks/RecordList.cs index 7629a1bbc3..c423c64f87 100644 --- a/Src/xWorks/RecordList.cs +++ b/Src/xWorks/RecordList.cs @@ -1872,10 +1872,10 @@ public IManyOnePathSortItem SortItemAt(int index) { CheckDisposed(); - if (SortedObjects.Count <= index) - return null; + if (0 <= index && index < SortedObjects.Count) + return SortedObjects[index] as IManyOnePathSortItem; else - return SortedObjects[index] as IManyOnePathSortItem; + return null; } /// From a28b5eaeadff6625e31b2f1252aec69eb28d3712 Mon Sep 17 00:00:00 2001 From: Mark Kidder <83427558+mark-sil@users.noreply.github.com> Date: Wed, 26 Mar 2025 10:39:33 -0400 Subject: [PATCH 045/123] =?UTF-8?q?Remove=20appended=20=E2=80=98-1?= =?UTF-8?q?=E2=80=99=20from=20XHTML=20node=20names=20(#299)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The node names need to be unique but the first one does not need to contain an appended ‘-1’. By removing the ‘-1 we fix problems with Webonary code that is not expecting the appended ‘-1’. Co-authored-by: Jake Oliver --- Src/xWorks/CssGenerator.cs | 4 +- .../ConfiguredLcmUsfmGeneratorTests.cs | 2 +- .../ConfiguredXHTMLGeneratorReversalTests.cs | 48 +- .../ConfiguredXHTMLGeneratorTests.cs | 564 +++++++++--------- Src/xWorks/xWorksTests/CssGeneratorTests.cs | 32 +- .../xWorksTests/LcmJsonGeneratorTests.cs | 118 ++-- 6 files changed, 385 insertions(+), 383 deletions(-) diff --git a/Src/xWorks/CssGenerator.cs b/Src/xWorks/CssGenerator.cs index 9c873ddf9e..88e331d75a 100644 --- a/Src/xWorks/CssGenerator.cs +++ b/Src/xWorks/CssGenerator.cs @@ -198,7 +198,9 @@ private string GetUniqueNodeName(ConfigurableDictionaryNode node, string classNa string uniqueNodeName; do { - uniqueNodeName = $"{className}-{++counter}"; + ++counter; + // For the first unique name, don't append the -1. Webonary does not expect it. + uniqueNodeName = counter == 1 ? $"{className}" : $"{className}-{counter}"; } while (_styleDictionary.ContainsKey(uniqueNodeName)); _uniqueNodeNames[nodePath] = uniqueNodeName; diff --git a/Src/xWorks/xWorksTests/ConfiguredLcmUsfmGeneratorTests.cs b/Src/xWorks/xWorksTests/ConfiguredLcmUsfmGeneratorTests.cs index d8fa38954a..ab07598513 100644 --- a/Src/xWorks/xWorksTests/ConfiguredLcmUsfmGeneratorTests.cs +++ b/Src/xWorks/xWorksTests/ConfiguredLcmUsfmGeneratorTests.cs @@ -25,7 +25,7 @@ namespace SIL.FieldWorks.XWorks [TestFixture] public class ConfiguredLcmUsfmGeneratorTests : MemoryOnlyBackendProviderRestoredForEachTestTestBase, IDisposable { - private const string XPathToUSFMField = "/div[@class='lexentry-1']/span[@class='usfm-field-1']"; + private const string XPathToUSFMField = "/div[@class='lexentry']/span[@class='usfm-field']"; private const string XPathToTitle = XPathToUSFMField + "/table/caption/span"; private const string XPathToRow = XPathToUSFMField + "/table/tbody/tr"; private const string XPathToCell = XPathToRow + "/td/span"; diff --git a/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorReversalTests.cs b/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorReversalTests.cs index f4ed244516..dea15bc5b4 100644 --- a/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorReversalTests.cs +++ b/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorReversalTests.cs @@ -120,18 +120,18 @@ public void GenerateXHTMLForEntry_LexemeFormConfigurationGeneratesCorrectResult( var entry = CreateInterestingEnglishReversalEntry(); //SUT string result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings).ToString(); - const string frenchLexForm = "/div[@class='reversalindexentry-1']/span[@class='reversalform-1']/span[@lang='en' and text()='ReversalForm']"; + const string frenchLexForm = "/div[@class='reversalindexentry']/span[@class='reversalform']/span[@lang='en' and text()='ReversalForm']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(frenchLexForm, 1); } #region PrimareyEntryReferenceTests // Xpath used by PrimaryEntryReference tests - private const string senseXpath = "/div[@class='reversalindexentry-1']/span[@class='sensesrs-1']/span[@class='sensecontent']/span[@class='sensesr']"; - private const string entryRefsXpath = senseXpath + "/span[@class='mainentryrefs-1']"; + private const string senseXpath = "/div[@class='reversalindexentry']/span[@class='sensesrs']/span[@class='sensecontent']/span[@class='sensesr']"; + private const string entryRefsXpath = senseXpath + "/span[@class='mainentryrefs']"; private const string entryRefXpath = entryRefsXpath + "/span[@class='mainentryref']"; - private const string entryRefTypeBit = "span[@class='entrytypes-1']/span[@class='entrytype']"; + private const string entryRefTypeBit = "span[@class='entrytypes']/span[@class='entrytype']"; private const string entryRefTypeXpath = entryRefsXpath + "/" + entryRefTypeBit; - private const string primaryLexemeBit = "/span[@class='primarylexemes-1']/span[@class='primarylexeme']"; + private const string primaryLexemeBit = "/span[@class='primarylexemes']/span[@class='primarylexeme']"; private const string primaryEntryXpath = entryRefXpath + primaryLexemeBit; //private const string primaryEntryXpath = entryRefXpath + "/span[@class='primarylexemes']/span[@class='primarylexeme']"; private const string refHeadwordXpath = primaryEntryXpath + "/span[@class='headword-2']/span[@lang='fr']/a[text()='parole']"; @@ -147,12 +147,12 @@ public void GenerateXHTMLForEntry_PrimaryEntryReferencesWork_ComplexFormOfEntry( CXGTests.CreateComplexForm(Cache, paroleEntry, sense.Owner as ILexEntry, true); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(reversalEntry, mainRevEntryNode, null, DefaultSettings).ToString(); - const string headwordXpath = senseXpath + "/span[@class='headword-1']/span[@lang='fr']//a[text()='porte-parole']"; + const string headwordXpath = senseXpath + "/span[@class='headword']/span[@lang='fr']//a[text()='porte-parole']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(headwordXpath, 1); - const string refTypeXpath = entryRefTypeXpath + "/span[@class='abbreviation-1']/span[@lang='en' and text()='comp. of']"; + const string refTypeXpath = entryRefTypeXpath + "/span[@class='abbreviation']/span[@lang='en' and text()='comp. of']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(refTypeXpath, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(refHeadwordXpath, 1); - const string glossOrSummDefXpath = primaryEntryXpath + "/span[@class='glossorsummary-1']/span[@lang='en' and text()='summDefn']"; + const string glossOrSummDefXpath = primaryEntryXpath + "/span[@class='glossorsummary']/span[@lang='en' and text()='summDefn']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(glossOrSummDefXpath, 1); } @@ -166,10 +166,10 @@ public void GenerateXHTMLForEntry_PrimaryEntryReferencesWork_ComplexFormOfSense( CXGTests.CreateComplexForm(Cache, paroleEntry.SensesOS[0], sense.Owner as ILexEntry, true); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(reversalEntry, mainRevEntryNode, null, DefaultSettings).ToString(); - const string refTypeXpath = entryRefTypeXpath + "/span[@class='abbreviation-1']/span[@lang='en' and text()='comp. of']"; + const string refTypeXpath = entryRefTypeXpath + "/span[@class='abbreviation']/span[@lang='en' and text()='comp. of']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(refTypeXpath, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(refHeadwordXpath, 1); - const string glossOrSummDefXpath = primaryEntryXpath + "/span[@class='glossorsummary-1']/span[@lang='en' and text()='speech']"; + const string glossOrSummDefXpath = primaryEntryXpath + "/span[@class='glossorsummary']/span[@lang='en' and text()='speech']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(glossOrSummDefXpath, 1); } @@ -183,10 +183,10 @@ public void GenerateXHTMLForEntry_PrimaryEntryReferencesWork_VariantFormOfSense( CXGTests.CreateVariantForm(Cache, paroleEntry.SensesOS[0], variantEntry, "Spelling Variant"); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(reversalEntry, mainRevEntryNode, null, DefaultSettings).ToString(); - const string refTypeXpath = entryRefTypeXpath + "/span[@class='abbreviation-1']/span[@lang='en' and text()='sp. var. of']"; + const string refTypeXpath = entryRefTypeXpath + "/span[@class='abbreviation']/span[@lang='en' and text()='sp. var. of']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(refTypeXpath, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(refHeadwordXpath, 1); - const string glossOrSummDefXpath = primaryEntryXpath + "/span[@class='glossorsummary-1']/span[@lang='en' and text()='speech']"; + const string glossOrSummDefXpath = primaryEntryXpath + "/span[@class='glossorsummary']/span[@lang='en' and text()='speech']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(glossOrSummDefXpath, 1); } @@ -201,10 +201,10 @@ public void GenerateXHTMLForEntry_PrimaryEntryReferencesWork_VariantFormOfEntry( CXGTests.CreateVariantForm(Cache, paroleEntry, variantEntry, "Spelling Variant"); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(reversalEntry, mainRevEntryNode, null, DefaultSettings).ToString(); - const string refTypeXpath = entryRefTypeXpath + "/span[@class='abbreviation-1']/span[@lang='en' and text()='sp. var. of']"; + const string refTypeXpath = entryRefTypeXpath + "/span[@class='abbreviation']/span[@lang='en' and text()='sp. var. of']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(refTypeXpath, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(refHeadwordXpath, 1); - const string glossOrSummDefXpath = primaryEntryXpath + "/span[@class='glossorsummary-1']/span[@lang='en' and text()='summDefn']"; + const string glossOrSummDefXpath = primaryEntryXpath + "/span[@class='glossorsummary']/span[@lang='en' and text()='summDefn']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(glossOrSummDefXpath, 1); } @@ -232,7 +232,7 @@ public void GenerateXHTMLForEntry_PrimaryEntryReferences_Ordered() const string headwordBit = "/span[@class='headword-2']/span[@lang='fr']/a[text()='{1}']"; const string entryRefWithSiblingXpath = entryRefsXpath + "/span[@class='mainentryref' and preceding-sibling::"; const string typeAndHeadwordXpath = entryRefWithSiblingXpath - + entryRefTypeBit + "/span[@class='abbreviation-1']/span[@lang='en' and text()='{0}']]" + primaryLexemeBit + headwordBit; + + entryRefTypeBit + "/span[@class='abbreviation']/span[@lang='en' and text()='{0}']]" + primaryLexemeBit + headwordBit; var adjacentHeadwordXpath = entryRefWithSiblingXpath + "span[@class='mainentryref']" + primaryLexemeBit + headwordBit.Replace("{1}", "{0}") + "]" + primaryLexemeBit + headwordBit; // check for proper headings on each referenced headword @@ -402,7 +402,7 @@ public void GenerateXHTMLForEntry_ReversalStringGeneratesContent() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(rie, reversalNode, null, DefaultSettings).ToString(); - var reversalFormDataPath = string.Format("/div[@class='reversalindexentry-1']/span[@class='reversalform-1']/span[text()='{0}']", + var reversalFormDataPath = string.Format("/div[@class='reversalindexentry']/span[@class='reversalform']/span[text()='{0}']", TsStringUtils.Compose(rie.LongName)); var entryDataPath = string.Format("//span[text()='{0}']", entryHeadWord.get_NormalizedForm(FwNormalizationMode.knmNFC).Text); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(reversalFormDataPath, 1); @@ -462,14 +462,14 @@ public void GenerateXHTMLForEntry_SenseNumbersGeneratedForMultipleReferencedSens AddSenseToReversaEntry(testEntry, "second gloss", m_wsEn, Cache); //SUT var xhtml = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, DefaultSettings).ToString(); - const string senseNumberOne = "/div[@class='reversalindexentry-1']/span[@class='sensesrs-1']/span[@class='sensecontent']/span[@class='sensesr' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='gloss']"; - const string senseNumberTwo = "/div[@class='reversalindexentry-1']/span[@class='sensesrs-1']/span[@class='sensecontent']/span[@class='sensesr' and preceding-sibling::span[@class='sensenumber' and text()='2']]//span[@lang='en' and text()='second gloss']"; + const string senseNumberOne = "/div[@class='reversalindexentry']/span[@class='sensesrs']/span[@class='sensecontent']/span[@class='sensesr' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='gloss']"; + const string senseNumberTwo = "/div[@class='reversalindexentry']/span[@class='sensesrs']/span[@class='sensecontent']/span[@class='sensesr' and preceding-sibling::span[@class='sensenumber' and text()='2']]//span[@lang='en' and text()='second gloss']"; //This assert is dependent on the specific entry data created in CreateInterestingEnglishReversalEntry AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(senseNumberOne, 1); AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(senseNumberTwo, 1); - const string headwordOne = "/div[@class='reversalindexentry-1']/span[@class='sensesrs-1']/span[@class='sensecontent']/span[@class='sensesr']/span[@class='headword-1']/span[@lang='fr' and child::span[@lang='fr']/a[text()='1']]/span[@lang='fr' and a[text()='Citation']]"; - const string headwordTwo = "/div[@class='reversalindexentry-1']/span[@class='sensesrs-1']/span[@class='sensecontent']/span[@class='sensesr']/span[@class='headword-1']/span[@lang='fr' and child::span[@lang='fr']/a[text()='2']]/span[@lang='fr' and a[text()='Citation']]"; + const string headwordOne = "/div[@class='reversalindexentry']/span[@class='sensesrs']/span[@class='sensecontent']/span[@class='sensesr']/span[@class='headword']/span[@lang='fr' and child::span[@lang='fr']/a[text()='1']]/span[@lang='fr' and a[text()='Citation']]"; + const string headwordTwo = "/div[@class='reversalindexentry']/span[@class='sensesrs']/span[@class='sensecontent']/span[@class='sensesr']/span[@class='headword']/span[@lang='fr' and child::span[@lang='fr']/a[text()='2']]/span[@lang='fr' and a[text()='Citation']]"; AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(headwordOne, 1); AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(headwordTwo, 1); } @@ -525,7 +525,7 @@ public void GenerateXHTMLForEntry_VernacularFormWithSubSenses() //SUT var xhtml = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, DefaultSettings).ToString(); // REVIEW (Hasso) 2016.03: we should probably do something about the leading space in the Sense Number Run, as it is currently in addition to the "between" space. - const string subSenseOneOne = "/div[@class='reversalindexentry-1']/span[@class='sensesrs-1']/span[@class='sensecontent']/span[@class='sensesr']/span[@class='headword-1']/span/span/a[text()='1.1']"; + const string subSenseOneOne = "/div[@class='reversalindexentry']/span[@class='sensesrs']/span[@class='sensecontent']/span[@class='sensesr']/span[@class='headword']/span/span/a[text()='1.1']"; AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(subSenseOneOne, 1); } @@ -586,7 +586,7 @@ public void GenerateXHTMLForEntry_VernacularFormWithSubSensesinReversalSubEntry( var testEntry = CreateInterestingEnglishSubReversalEntryWithSubSense(); //SUT var xhtml = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, DefaultSettings).ToString(); - const string subSenseOneOne = "/div[@class='reversalindexentry-1']/span[@class='subentries-1']/span[@class='subentry']/span[@class='sensesrs-1']/span[@class='sensecontent']/span[@class='sensesr']/span[@class='headword-1']/span/span/a[text()='1.1']"; + const string subSenseOneOne = "/div[@class='reversalindexentry']/span[@class='subentries']/span[@class='subentry']/span[@class='sensesrs']/span[@class='sensecontent']/span[@class='sensesr']/span[@class='headword']/span/span/a[text()='1.1']"; AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(subSenseOneOne, 1); } @@ -706,8 +706,8 @@ public void GenerateXHTMLForEntry_SameGramInfoCollapsesOnDemand() var xhtml = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); // check that the sense gram info appears once before the rest of the sense information. Assert.That(xhtml, Is.Not.Null.Or.Empty); - const string sharedGramInfo = "/div[@class='reversalindexentry-1']/span[@class='sensesrs-1']/span[@class='sharedgrammaticalinfo']/span[@class='morphosyntaxanalysis-1']/span[@class='partofspeech-1']/span[@lang='en' and text()='n']"; - const string separateGramInfo = "/div[@class='reversalindexentry-1']/span[@class='sensesrs-1']/span[@class='sensecontent']/span[@class='sensesr']/span[@class='morphosyntaxanalysis-1']/span[@class='partofspeech-1']/span[@lang='en']"; + const string sharedGramInfo = "/div[@class='reversalindexentry']/span[@class='sensesrs']/span[@class='sharedgrammaticalinfo']/span[@class='morphosyntaxanalysis']/span[@class='partofspeech']/span[@lang='en' and text()='n']"; + const string separateGramInfo = "/div[@class='reversalindexentry']/span[@class='sensesrs']/span[@class='sensecontent']/span[@class='sensesr']/span[@class='morphosyntaxanalysis']/span[@class='partofspeech']/span[@lang='en']"; AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(sharedGramInfo, 1); AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(separateGramInfo, 0); diff --git a/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs b/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs index 1e340c093e..e62206ea51 100644 --- a/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs +++ b/Src/xWorks/xWorksTests/ConfiguredXHTMLGeneratorTests.cs @@ -167,7 +167,7 @@ public void ResetModelAssembly() ConfiguredLcmGenerator.Init(); } - const string xpathThruSense = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']"; + const string xpathThruSense = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']"; private const string TestVariantName = "Crazy Variant"; [Test] @@ -190,7 +190,7 @@ public void GenerateContentForEntry_HeadwordConfigurationGeneratesCorrectResult( var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, new ReadOnlyPropertyTable(m_propertyTable), false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); - const string frenchHeadwordOfHeadwordTest = "/div[@class='lexentry-1']/span[@class='headword-1']/span[@lang='fr']/a[text()='HeadWordTest']"; + const string frenchHeadwordOfHeadwordTest = "/div[@class='lexentry']/span[@class='headword']/span[@lang='fr']/a[text()='HeadWordTest']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(frenchHeadwordOfHeadwordTest, 1); } @@ -213,7 +213,7 @@ public void GenerateContentForEntry_InvalidUnicodeHeadword_GeneratesErrorResult( var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, new ReadOnlyPropertyTable(m_propertyTable), false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); - const string invalidCharsHeadwordTest = "/div[@class='lexentry-1']/span[@class='headword-1']/span[text()='\u0fff\u0fff\u0fff']"; + const string invalidCharsHeadwordTest = "/div[@class='lexentry']/span[@class='headword']/span[text()='\u0fff\u0fff\u0fff']"; // change Headword back to something legal so that we don't crash trying to save bad data into the cache. AddHeadwordToEntry(entry, "notbadanymore", Cache.DefaultVernWs); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(invalidCharsHeadwordTest, 1); @@ -280,7 +280,7 @@ public void GenerateContentForEntry_LexemeFormConfigurationGeneratesCorrectResul var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, new ReadOnlyPropertyTable(m_propertyTable), false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); - const string frenchLexForm = "/div[@class='lexentry-1']/span[@class='lexemeformoa-1']/span[@lang='fr']/a[text()='LexemeFormTest']"; + const string frenchLexForm = "/div[@class='lexentry']/span[@class='lexemeformoa']/span[@lang='fr']/a[text()='LexemeFormTest']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(frenchLexForm, 1); } @@ -325,7 +325,7 @@ public void GenerateContentForEntry_PronunciationLocationGeneratesCorrectResult( var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); - const string hereLocation = "/div[@class='lexentry-1']/span[@class='pronunciations-1']/span[@class='pronunciation']/span[@class='location-1']/span[@class='name-1']/span[@lang='fr' and text()='Here!']"; + const string hereLocation = "/div[@class='lexentry']/span[@class='pronunciations']/span[@class='pronunciation']/span[@class='location']/span[@class='name']/span[@lang='fr' and text()='Here!']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(hereLocation, 1); } @@ -393,12 +393,12 @@ public void GenerateContentForEntry_PronunciationVideoFileGeneratesAnchorTag() const string movieCameraChar = "\U0001f3a5"; const string movieCamSearch = "/a/text()['" + movieCameraChar + "']"; - const string entryPart = "/div[@class='lexentry-1']"; - const string pronunciationsPart = "/span[@class='pronunciations-1']/span[@class='pronunciation']"; - const string mediaFilePart1 = "/span[@class='mediafiles-1']/span[@class='mediafile']"; + const string entryPart = "/div[@class='lexentry']"; + const string pronunciationsPart = "/span[@class='pronunciations']/span[@class='pronunciation']"; + const string mediaFilePart1 = "/span[@class='mediafiles']/span[@class='mediafile']"; const string mediaFileAnchor1 = entryPart + pronunciationsPart + mediaFilePart1 + movieCamSearch; - const string variantsPart = "/span[@class='variantformentrybackrefs-1']/span[@class='variantformentrybackref']"; - const string varPronPart = "/span[@class='variantpronunciations-1']/span[@class='variantpronunciation']"; + const string variantsPart = "/span[@class='variantformentrybackrefs']/span[@class='variantformentrybackref']"; + const string varPronPart = "/span[@class='variantpronunciations']/span[@class='variantpronunciation']"; const string mediaFilePart2 = "/span[@class='mediafiles-2']/span[@class='mediafile']"; const string mediaFileAnchor2 = entryPart + variantsPart + varPronPart + mediaFilePart2 + movieCamSearch; @@ -483,9 +483,9 @@ public void GenerateContentForEntry_HomographNumbersGeneratesCorrectResult() XHTMLStringBuilder.Append(result); XHTMLStringBuilder.AppendLine(""); - var entryWithHomograph = "/TESTWRAPPER/div[@class='lexentry-1']/span[@class='homographnumber-1' and text()='1']"; + var entryWithHomograph = "/TESTWRAPPER/div[@class='lexentry']/span[@class='homographnumber' and text()='1']"; AssertThatXmlIn.String(XHTMLStringBuilder.ToString()).HasSpecifiedNumberOfMatchesForXpath(entryWithHomograph, 1); - entryWithHomograph = "/TESTWRAPPER/div[@class='lexentry-1']/span[@class='homographnumber-1' and text()='2']"; + entryWithHomograph = "/TESTWRAPPER/div[@class='lexentry']/span[@class='homographnumber' and text()='2']"; AssertThatXmlIn.String(XHTMLStringBuilder.ToString()).HasSpecifiedNumberOfMatchesForXpath(entryWithHomograph, 1); } @@ -586,9 +586,9 @@ public void GenerateContentForEntry_OneEntryWithSenseAndOneWithoutWorks() XHTMLStringBuilder.Append(result); XHTMLStringBuilder.AppendLine(""); result = XHTMLStringBuilder.ToString(); - var entryOneHasSensesSpan = "/TESTWRAPPER/div[@class='lexentry-1' and @id='g" + entryOneId + "']/span[@class='senses-1']"; - var entryTwoExists = "/TESTWRAPPER/div[@class='lexentry-1' and @id='g" + entryTwoId + "']"; - var entryTwoHasNoSensesSpan = "/TESTWRAPPER/div[@class='lexentry-1' and @id='g" + entryTwoId + "']/span[@class='senses-1']"; + var entryOneHasSensesSpan = "/TESTWRAPPER/div[@class='lexentry' and @id='g" + entryOneId + "']/span[@class='senses']"; + var entryTwoExists = "/TESTWRAPPER/div[@class='lexentry' and @id='g" + entryTwoId + "']"; + var entryTwoHasNoSensesSpan = "/TESTWRAPPER/div[@class='lexentry' and @id='g" + entryTwoId + "']/span[@class='senses']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(entryOneHasSensesSpan, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(entryTwoExists, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(entryTwoHasNoSensesSpan, 0); @@ -604,7 +604,7 @@ public void GenerateContentForEntry_DefaultRootGeneratesResult() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, dictionaryModel.Parts[0], DefaultDecorator, settings).ToString(); - var entryExists = "/div[@class='entry-1' and @id='g" + entry.Guid + "']"; + var entryExists = "/div[@class='entry' and @id='g" + entry.Guid + "']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(entryExists, 1); } @@ -641,8 +641,8 @@ public void GenerateContentForEntry_DoesNotDescendThroughDisabledNode() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); - const string sensesThatShouldNotBe = "/div[@class='entry-1']/span[@class='senses-1']"; - const string headwordThatShouldNotBe = "//span[@class='gloss-1']"; + const string sensesThatShouldNotBe = "/div[@class='entry']/span[@class='senses']"; + const string headwordThatShouldNotBe = "//span[@class='gloss']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(sensesThatShouldNotBe, 0); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(headwordThatShouldNotBe, 0); } @@ -730,8 +730,8 @@ public void GenerateContentForEntry_TwoSensesWithSameInfoShowGramInfoFirst() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT var xhtmlString = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); - const string sharedGramInfoPath = "//div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sharedgrammaticalinfo']"; - const string gramInfoPath = "//div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='msas-1']/span[@class='mlpartofspeech-1']"; + const string sharedGramInfoPath = "//div[@class='lexentry']/span[@class='senses']/span[@class='sharedgrammaticalinfo']"; + const string gramInfoPath = "//div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='msas']/span[@class='mlpartofspeech']"; AssertThatXmlIn.String(xhtmlString).HasNoMatchForXpath(gramInfoPath); AssertThatXmlIn.String(xhtmlString).HasSpecifiedNumberOfMatchesForXpath(sharedGramInfoPath, 1); } @@ -815,8 +815,8 @@ public void GenerateContentForEntry_TwoSensesWithSameInfo_ThirdSenseNotPublished var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT var xhtmlString = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, mainDictionaryDecorator, settings).ToString(); - const string sharedGramInfoPath = "//div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sharedgrammaticalinfo']"; - const string gramInfoPath = "//div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='msa']/span[@class='mlpartofspeech-1']"; + const string sharedGramInfoPath = "//div[@class='lexentry']/span[@class='senses']/span[@class='sharedgrammaticalinfo']"; + const string gramInfoPath = "//div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='msa']/span[@class='mlpartofspeech']"; AssertThatXmlIn.String(xhtmlString).HasNoMatchForXpath(gramInfoPath); AssertThatXmlIn.String(xhtmlString).HasSpecifiedNumberOfMatchesForXpath(sharedGramInfoPath, 1); } @@ -880,8 +880,8 @@ public void GenerateContentForEntry_TwoSensesWithDifferentGramInfoShowInfoInSens var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT var xhtmlString = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); - const string sharedGramInfoPath = "//div[@class='lexentry-1']/span[@class='sensesos']/span[@class='sharedgrammaticalinfo']"; - const string gramInfoPath = "//div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='msas-1']/span[@class='mlpartofspeech-1']"; + const string sharedGramInfoPath = "//div[@class='lexentry']/span[@class='sensesos']/span[@class='sharedgrammaticalinfo']"; + const string gramInfoPath = "//div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='msas']/span[@class='mlpartofspeech']"; AssertThatXmlIn.String(xhtmlString).HasSpecifiedNumberOfMatchesForXpath(gramInfoPath, 2); AssertThatXmlIn.String(xhtmlString).HasNoMatchForXpath(sharedGramInfoPath); } @@ -929,8 +929,8 @@ public void GenerateContentForEntry_TwoSensesWithNoGramInfoDisplaysNothingForSha var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT var xhtmlString = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); - const string sharedGramInfoPath = "//div[@class='lexentry-1']/span[@class='sensesos']/span[@class='sharedgrammaticalinfo']"; - const string gramInfoPath = "//div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='msas-1']/span[@class='mlpartofspeech-1']"; + const string sharedGramInfoPath = "//div[@class='lexentry']/span[@class='sensesos']/span[@class='sharedgrammaticalinfo']"; + const string gramInfoPath = "//div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='msas']/span[@class='mlpartofspeech']"; AssertThatXmlIn.String(xhtmlString).HasNoMatchForXpath(gramInfoPath); AssertThatXmlIn.String(xhtmlString).HasNoMatchForXpath(sharedGramInfoPath); } @@ -989,7 +989,7 @@ public void GenerateContentForEntry_MorphemeType() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); - const string morphTypePath = "//span[@class='morphosyntaxanalysis-1']/span[@class='morphtypes-1']/span[@class='morphtype']/span[@class='abbreviation-1']/span[@lang='en' and text()='sfx']"; + const string morphTypePath = "//span[@class='morphosyntaxanalysis']/span[@class='morphtypes']/span[@class='morphtype']/span[@class='abbreviation']/span[@lang='en' and text()='sfx']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(morphTypePath, 1); } @@ -1029,7 +1029,7 @@ public void GenerateContentForEntry_MakesSpanForRA() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); - const string gramInfoPath = xpathThruSense + "/span[@class='morphosyntaxanalysis-1']"; + const string gramInfoPath = xpathThruSense + "/span[@class='morphosyntaxanalysis']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(gramInfoPath, 1); } @@ -1080,7 +1080,7 @@ public void GenerateContentForEntry_CmObjectWithNoEnabledChildrenSkipsSpan() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); - const string gramInfoPath = xpathThruSense + "/span[@class='morphosyntaxanalysis-1']"; + const string gramInfoPath = xpathThruSense + "/span[@class='morphosyntaxanalysis']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(gramInfoPath, 0); } @@ -1114,7 +1114,7 @@ public void GenerateContentForEntry_DoesNotMakeSpanForRAIfNoData() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); - const string gramInfoPath = xpathThruSense + "/span[@class='morphosyntaxanalysis-1']"; + const string gramInfoPath = xpathThruSense + "/span[@class='morphosyntaxanalysis']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(gramInfoPath, 0); } @@ -1205,10 +1205,10 @@ public void GenerateContentForEntry_SupportsGramAbbrChildOfMSARA() // SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); - const string gramAbbr1 = xpathThruSense + "/span[@class='morphosyntaxanalysis-1']/span[@class='interlinearabbrtss-1']/span[@lang='fr']/span[@lang='fr' and text()='Blah']"; - const string gramAbbr2 = xpathThruSense + "/span[@class='morphosyntaxanalysis-1']/span[@class='interlinearabbrtss-1']/span[@lang='fr']/span[@lang='en' and text()=':Any']"; - const string gramName1 = xpathThruSense + "/span[@class='morphosyntaxanalysis-1']/span[@class='interlinearnametss-1']/span[@lang='fr']/span[@lang='fr' and text()='Blah']"; - const string gramName2 = xpathThruSense + "/span[@class='morphosyntaxanalysis-1']/span[@class='interlinearnametss-1']/span[@lang='fr']/span[@lang='en' and text()=':Any']"; + const string gramAbbr1 = xpathThruSense + "/span[@class='morphosyntaxanalysis']/span[@class='interlinearabbrtss']/span[@lang='fr']/span[@lang='fr' and text()='Blah']"; + const string gramAbbr2 = xpathThruSense + "/span[@class='morphosyntaxanalysis']/span[@class='interlinearabbrtss']/span[@lang='fr']/span[@lang='en' and text()=':Any']"; + const string gramName1 = xpathThruSense + "/span[@class='morphosyntaxanalysis']/span[@class='interlinearnametss']/span[@lang='fr']/span[@lang='fr' and text()='Blah']"; + const string gramName2 = xpathThruSense + "/span[@class='morphosyntaxanalysis']/span[@class='interlinearnametss']/span[@lang='fr']/span[@lang='en' and text()=':Any']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(gramAbbr1, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(gramAbbr2, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(gramName1, 1); @@ -1268,10 +1268,10 @@ public void GenerateContentForEntry_DontDisplayNotSure() // SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); - const string gramAbbr1 = xpathThruSense + "/span[@class='morphosyntaxanalysis-1']/span[@class='interlinearabbrtss-1']/span[@lang='fr']/span[@lang='fr' and text()='']"; - const string gramAbbr2 = xpathThruSense + "/span[@class='morphosyntaxanalysis-1']/span[@class='interlinearabbrtss-1']/span[@lang='fr']/span[@lang='en' and text()=':Any']"; - const string gramName1 = xpathThruSense + "/span[@class='morphosyntaxanalysis-1']/span[@class='interlinearnametss-1']/span[@lang='fr']/span[@lang='fr' and text()='']"; - const string gramName2 = xpathThruSense + "/span[@class='morphosyntaxanalysis-1']/span[@class='interlinearnametss-1']/span[@lang='fr']/span[@lang='en' and text()=':Any']"; + const string gramAbbr1 = xpathThruSense + "/span[@class='morphosyntaxanalysis']/span[@class='interlinearabbrtss']/span[@lang='fr']/span[@lang='fr' and text()='']"; + const string gramAbbr2 = xpathThruSense + "/span[@class='morphosyntaxanalysis']/span[@class='interlinearabbrtss']/span[@lang='fr']/span[@lang='en' and text()=':Any']"; + const string gramName1 = xpathThruSense + "/span[@class='morphosyntaxanalysis']/span[@class='interlinearnametss']/span[@lang='fr']/span[@lang='fr' and text()='']"; + const string gramName2 = xpathThruSense + "/span[@class='morphosyntaxanalysis']/span[@class='interlinearnametss']/span[@lang='fr']/span[@lang='en' and text()=':Any']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(gramAbbr1, 0); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(gramAbbr2, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(gramName1, 0); @@ -1321,7 +1321,7 @@ public void GenerateContentForEntry_CaptionOrHeadwordGetsCaption() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); - const string captionOrHeadwordContainsCaption = "/div[@class='lexentry-1']/span[@class='pictures-1']/div[@class='picture']/div[@class='captionContent']/span[@class='captionorheadword']//span[text()='captionEn']"; + const string captionOrHeadwordContainsCaption = "/div[@class='lexentry']/span[@class='pictures']/div[@class='picture']/div[@class='captionContent']/span[@class='captionorheadword']//span[text()='captionEn']"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(captionOrHeadwordContainsCaption, 1); } @@ -1369,7 +1369,7 @@ public void GenerateContentForEntry_CaptionOrHeadwordGetsHeadword() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); - const string captionOrHeadwordContainsHeadword = "/div[@class='lexentry-1']/span[@class='pictures-1']/div[@class='picture']/div[@class='captionContent']/span[@class='captionorheadword']//span[text()='HeadwordEn']"; + const string captionOrHeadwordContainsHeadword = "/div[@class='lexentry']/span[@class='pictures']/div[@class='picture']/div[@class='captionContent']/span[@class='captionorheadword']//span[text()='HeadwordEn']"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(captionOrHeadwordContainsHeadword, 1); } @@ -1418,8 +1418,8 @@ public void GenerateContentForEntry_CaptionOrHeadword_HandlePerWs() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); - const string captionOrHeadwordContainsCaptionEn = "/div[@class='lexentry-1']/span[@class='pictures-1']/div[@class='picture']/div[@class='captionContent']/span[@class='captionorheadword']//span[@lang='en' and text()='captionEn']"; - const string captionOrHeadwordContainsHeadwordFr = "/div[@class='lexentry-1']/span[@class='pictures-1']/div[@class='picture']/div[@class='captionContent']/span[@class='captionorheadword']//span[@lang='fr' and text()='HeadwordFr']"; + const string captionOrHeadwordContainsCaptionEn = "/div[@class='lexentry']/span[@class='pictures']/div[@class='picture']/div[@class='captionContent']/span[@class='captionorheadword']//span[@lang='en' and text()='captionEn']"; + const string captionOrHeadwordContainsHeadwordFr = "/div[@class='lexentry']/span[@class='pictures']/div[@class='picture']/div[@class='captionContent']/span[@class='captionorheadword']//span[@lang='fr' and text()='HeadwordFr']"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(captionOrHeadwordContainsCaptionEn, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(captionOrHeadwordContainsHeadwordFr, 1); @@ -1449,7 +1449,7 @@ public void GenerateContentForEntry_DefinitionOrGlossWorks() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); - const string senseWithdefinitionOrGloss = "//span[@class='sense']/span[@class='definitionorgloss-1']/span[text()='gloss']"; + const string senseWithdefinitionOrGloss = "//span[@class='sense']/span[@class='definitionorgloss']/span[text()='gloss']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseWithdefinitionOrGloss, 1); } @@ -1480,7 +1480,7 @@ public void GenerateContentForEntry_DefinitionOrGlossWorks_WithAbbrev() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); const string senseWithdefinitionOrGloss = - "//span[@class='sense']/span[@class='definitionorgloss-1']/span[@class='writingsystemprefix' and normalize-space(text())='Eng']"; + "//span[@class='sense']/span[@class='definitionorgloss']/span[@class='writingsystemprefix' and normalize-space(text())='Eng']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseWithdefinitionOrGloss, 1); } @@ -1508,7 +1508,7 @@ public void GenerateContentForEntry_DefinitionOrGloss_HandlePerWS() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); - const string senseWithdefinitionOrGlossTwoWs = "//span[@class='sense']/span[@class='definitionorgloss-1' and span[1]='gloss' and span[2]='definition']"; + const string senseWithdefinitionOrGlossTwoWs = "//span[@class='sense']/span[@class='definitionorgloss' and span[1]='gloss' and span[2]='definition']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseWithdefinitionOrGlossTwoWs, 1); } @@ -1571,17 +1571,17 @@ public void GenerateContentForEntry_ReferencedComplexFormDefinitionOrGloss_Handl // set of xpaths and required number of matches. var checkthis = new Dictionary() { - { "/div/span[@class='visiblecomplexformbackrefs-1']/span[@class='visiblecomplexformbackref']/span[@class='definitionorgloss-1']/span[.='definitionA1']", 1 }, - { "/div/span[@class='visiblecomplexformbackrefs-1']/span[@class='visiblecomplexformbackref']/span[@class='definitionorgloss-1']/span[.='definitionA2']", 1 }, + { "/div/span[@class='visiblecomplexformbackrefs']/span[@class='visiblecomplexformbackref']/span[@class='definitionorgloss']/span[.='definitionA1']", 1 }, + { "/div/span[@class='visiblecomplexformbackrefs']/span[@class='visiblecomplexformbackref']/span[@class='definitionorgloss']/span[.='definitionA2']", 1 }, - { "/div/span[@class='visiblecomplexformbackrefs-1']/span[@class='visiblecomplexformbackref']/span[@class='definitionorgloss-1']/span[.='glossB1']", 1 }, - { "/div/span[@class='visiblecomplexformbackrefs-1']/span[@class='visiblecomplexformbackref']/span[@class='definitionorgloss-1']/span[.='glossB2']", 1 }, + { "/div/span[@class='visiblecomplexformbackrefs']/span[@class='visiblecomplexformbackref']/span[@class='definitionorgloss']/span[.='glossB1']", 1 }, + { "/div/span[@class='visiblecomplexformbackrefs']/span[@class='visiblecomplexformbackref']/span[@class='definitionorgloss']/span[.='glossB2']", 1 }, - { "/div/span[@class='visiblecomplexformbackrefs-1']/span[@class='visiblecomplexformbackref']/span[@class='definitionorgloss-1']/span[.='definitionC1']", 1 }, - { "/div/span[@class='visiblecomplexformbackrefs-1']/span[@class='visiblecomplexformbackref']/span[@class='definitionorgloss-1']/span[.='glossC2']", 1 }, + { "/div/span[@class='visiblecomplexformbackrefs']/span[@class='visiblecomplexformbackref']/span[@class='definitionorgloss']/span[.='definitionC1']", 1 }, + { "/div/span[@class='visiblecomplexformbackrefs']/span[@class='visiblecomplexformbackref']/span[@class='definitionorgloss']/span[.='glossC2']", 1 }, - { "/div/span[@class='visiblecomplexformbackrefs-1']/span[@class='visiblecomplexformbackref']/span[@class='definitionorgloss-1']/span[.='glossD1']", 1 }, - { "/div/span[@class='visiblecomplexformbackrefs-1']/span[@class='visiblecomplexformbackref']/span[@class='definitionorgloss-1']/span[.='definitionD2']", 1 }, + { "/div/span[@class='visiblecomplexformbackrefs']/span[@class='visiblecomplexformbackref']/span[@class='definitionorgloss']/span[.='glossD1']", 1 }, + { "/div/span[@class='visiblecomplexformbackrefs']/span[@class='visiblecomplexformbackref']/span[@class='definitionorgloss']/span[.='definitionD2']", 1 }, }; foreach (var thing in checkthis) { @@ -1641,10 +1641,10 @@ public void GenerateContentForEntry_OtherReferencedComplexForms() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); var fwdNameXpath = string.Format( - "//span[@class='complexformsnotsubentries-1']/span[@class='complexformtypes-1']/span[@class='complexformtype']/span/span[@lang='en' and text()='{0}']", + "//span[@class='complexformsnotsubentries']/span[@class='complexformtypes']/span[@class='complexformtype']/span/span[@lang='en' and text()='{0}']", complexRefAbbr); var revNameXpath = string.Format( - "//span[@class='complexformsnotsubentries-1']/span[@class='complexformtypes-1']/span[@class='complexformtype']/span[@class='reverseabbr-1']/span[@lang='en' and text()='{0}']", + "//span[@class='complexformsnotsubentries']/span[@class='complexformtypes']/span[@class='complexformtype']/span[@class='reverseabbr']/span[@lang='en' and text()='{0}']", complexRefRevAbbr); AssertThatXmlIn.String(result).HasNoMatchForXpath(fwdNameXpath); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(revNameXpath, 1); @@ -1677,7 +1677,7 @@ public void GenerateContentForEntry_DuplicateConfigNodeWithSpaceWorks() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); - const string senseWithHyphenSuffix = "//span[@class='senses_test-one-1']/span[@class='sense_test-one']"; + const string senseWithHyphenSuffix = "//span[@class='senses_test-one']/span[@class='sense_test-one']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseWithHyphenSuffix, 1); } @@ -1708,7 +1708,7 @@ public void GenerateContentForEntry_DuplicateConfigNodeWithPuncWorks() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); - const string senseWithHyphenSuffix = "//span[@class='senses_-test-1']/span[@class='sense_-test']"; + const string senseWithHyphenSuffix = "//span[@class='senses_-test']/span[@class='sense_-test']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseWithHyphenSuffix, 1); } @@ -1739,7 +1739,7 @@ public void GenerateContentForEntry_DuplicateConfigNodeWithMultiPuncWorks() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); - const string senseWithHyphenSuffix = "//span[@class='senses_-test--1']/span[@class='sense_-test-']"; + const string senseWithHyphenSuffix = "//span[@class='senses_-test-']/span[@class='sense_-test-']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseWithHyphenSuffix, 1); } @@ -1798,7 +1798,7 @@ public void GenerateContentForEntry_HeadWordRefVirtualPropWorks() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); // SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); - var headwordMatch = string.Format("//span[@class='{0}-1']//span[@class='{1}-1']/span[text()='{2}']", + var headwordMatch = string.Format("//span[@class='{0}']//span[@class='{1}']/span[text()='{2}']", nters, headWord, entryThreeForm); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(headwordMatch, 1); } @@ -1852,9 +1852,9 @@ public void GenerateContentForEntry_EtymologyLanguageWorks() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, settings).ToString(); - const string etymologyWithArabicSrcLanguage = "//span[@class='etymologies-1']/span[@class='etymology']/span[@class='languages-1']/span[@class='language']/span[@class='abbreviation-1']/span[@lang='en' and text()='ar']"; + const string etymologyWithArabicSrcLanguage = "//span[@class='etymologies']/span[@class='etymology']/span[@class='languages']/span[@class='language']/span[@class='abbreviation']/span[@lang='en' and text()='ar']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(etymologyWithArabicSrcLanguage, 1); - const string etymologyWithGeorgianNotes = "//span[@class='etymologies-1']/span[@class='etymology']/span[@class='languagenotes-1']/span[@lang='en' and text()='Georgian']"; + const string etymologyWithGeorgianNotes = "//span[@class='etymologies']/span[@class='etymology']/span[@class='languagenotes']/span[@lang='en' and text()='Georgian']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(etymologyWithGeorgianNotes, 1); } @@ -1875,7 +1875,7 @@ public void GenerateEntryHtmlWithStyles_SelectsDirectionUsingDictionaryNormal() //SUT var xhtml = LcmXhtmlGenerator.GenerateEntryHtmlWithStyles(mainEntry, configModel, DefaultDecorator, m_propertyTable); // this test relies on specific test data from CreateInterestingConfigurationModel - const string xpath = "/html[@dir='rtl']/body[@dir='rtl']/div[@class='lexentry-1']/span[@class='entry-1']"; + const string xpath = "/html[@dir='rtl']/body[@dir='rtl']/div[@class='lexentry']/span[@class='entry']"; AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(xpath, 1); } finally @@ -1899,7 +1899,7 @@ public void GenerateEntryHtmlWithStyles_MinorEntryUsesMinorEntryFormatting( //SUT var xhtml = LcmXhtmlGenerator.GenerateEntryHtmlWithStyles(minorEntry, configModel, DefaultDecorator, m_propertyTable); // this test relies on specific test data from CreateInterestingConfigurationModel - const string xpath = "/html/body/div[@class='minorentry-1']/span[@class='entry-1']"; + const string xpath = "/html/body/div[@class='minorentry']/span[@class='entry']"; AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(xpath, 1); } @@ -1916,7 +1916,7 @@ public void GenerateEntryHtmlWithStyles_MinorEntryUnCheckedItemsGenerateNothing( //SUT var xhtml = LcmXhtmlGenerator.GenerateEntryHtmlWithStyles(minorEntry, configModel, DefaultDecorator, m_propertyTable); // this test relies on specific test data from CreateInterestingConfigurationModel - const string xpath = "/html/body/div[@class='minorentry-1']/span[@class='entry-1']"; + const string xpath = "/html/body/div[@class='minorentry']/span[@class='entry']"; // only the variant is selected, so the other minor entry should not have been generated AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(xpath, 0); } @@ -1933,7 +1933,7 @@ public void GenerateEntryHtmlWithStyles_DoesNotShowHiddenMinorEntries() //SUT var xhtml = LcmXhtmlGenerator.GenerateEntryHtmlWithStyles(minorEntry, configModel, DefaultDecorator, m_propertyTable); // this test relies on specific test data from CreateInterestingConfigurationModel - const string xpath = "/div[@class='minorentry-1']/span[@class='entry-1']"; + const string xpath = "/div[@class='minorentry']/span[@class='entry']"; AssertThatXmlIn.String(xhtml).HasNoMatchForXpath(xpath); } @@ -1955,7 +1955,7 @@ public void GenerateEntryHtmlWithStyles_DoesNotShowMinorEntriesTwice([Values(tru //SUT var xhtml = LcmXhtmlGenerator.GenerateEntryHtmlWithStyles(minorEntry, configModel, DefaultDecorator, m_propertyTable); // this test relies on specific test data from CreateInterestingConfigurationModel - const string xpath = "/html/body/div[@class='minorentry-1']/span[@class='entry-1']"; + const string xpath = "/html/body/div[@class='minorentry']/span[@class='entry']"; AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(xpath, 1); } @@ -1974,7 +1974,7 @@ public void GenerateContentForEntry_LexemeBasedConsidersComplexFormsMainEntries( //SUT var xhtml = LcmXhtmlGenerator.GenerateEntryHtmlWithStyles(complexEntry, configModel, DefaultDecorator, m_propertyTable); // this test relies on specific test data from CreateInterestingConfigurationModel - const string xpath = "/html/body/div[@class='minorentry-1']/span[@class='entry-1']"; + const string xpath = "/html/body/div[@class='minorentry']/span[@class='entry']"; // only the variant is selected, so the other minor entry should not have been generated AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(xpath, 0); } @@ -2025,8 +2025,8 @@ public void GenerateContentForEntry_SenseNumbersGeneratedForMultipleSenses(strin var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); - string senseNumberOne = $"/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and @lang='{homographWs}' and text()='1']]//span[@lang='en' and text()='gloss']"; - string senseNumberTwo = $"/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and @lang='{homographWs}' and text()='2']]//span[@lang='en' and text()='second gloss']"; + string senseNumberOne = $"/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and @lang='{homographWs}' and text()='1']]//span[@lang='en' and text()='gloss']"; + string senseNumberTwo = $"/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and @lang='{homographWs}' and text()='2']]//span[@lang='en' and text()='second gloss']"; string senseNumberTen = $"//span[@class='sensecontent']/spansenses/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and @lang='{homographWs}' and text()='{tenthSenseNumber}']]//span[@lang='en' and text()='10']"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseNumberOne, 1); @@ -2187,7 +2187,7 @@ public void GenerateContentForEntry_SingleSenseGetsNoSenseNumber() Assert.That(testEntry.AllSenses.First().AllSenses.Count, Is.EqualTo(1), "Test not set up correctly. There should be no subsenses."); // SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); - const string senseNumberOne = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]/span[@lang='en' and text()='gloss']"; + const string senseNumberOne = "/div[@class='lexentry']/span[@class='senses']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]/span[@lang='en' and text()='gloss']"; // This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasNoMatchForXpath(senseNumberOne); @@ -2255,7 +2255,7 @@ public void GenerateContentForEntry_TurnedOffSubsensesCausesSenseToBehaveLikeSin Assert.That(testEntry.AllSenses.First().AllSenses.Count, Is.EqualTo(3), "Test not set up correctly."); // SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); - const string senseNumberXpath = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sensenumber']"; + const string senseNumberXpath = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sensenumber']"; AssertThatXmlIn.String(result).HasNoMatchForXpath(senseNumberXpath); // Should not have a sense number on top sense. // Piggy-back a test for ShouldThisSenseBeNumbered @@ -2322,7 +2322,7 @@ public void GenerateContentForEntry_EmptyStyleSubsensesCausesSenseToBehaveLikeSi Assert.That(testEntry.AllSenses.First().AllSenses.Count, Is.EqualTo(3), "Test not set up correctly."); // SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); - const string senseNumberXpath = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sensenumber']"; + const string senseNumberXpath = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sensenumber']"; AssertThatXmlIn.String(result).HasNoMatchForXpath(senseNumberXpath); // Should not have a sense number on top sense. // Piggy-back a test for ShouldThisSenseBeNumbered @@ -2389,7 +2389,7 @@ public void GenerateContentForEntry_SubsenseStyleInfluencesSenseNumberShown() Assert.That(testEntry.AllSenses.First().AllSenses.Count, Is.EqualTo(3), "Test not set up correctly."); // SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); - const string senseNumberXpath = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sensenumber']"; + const string senseNumberXpath = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sensenumber']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseNumberXpath, 1); // Should have sense number on top sense. // Piggy-back a test for ShouldThisSenseBeNumbered @@ -2439,7 +2439,7 @@ public void GenerateContentForEntry_NumberingSingleSenseAlsoCountsSubSense() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); - const string SenseOneSubSense = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]/span[@class='senses-2']/span[@class='sensecontent']//span[@lang='en' and text()='gloss1.1']"; + const string SenseOneSubSense = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]/span[@class='senses-2']/span[@class='sensecontent']//span[@lang='en' and text()='gloss1.1']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(SenseOneSubSense, 1); } @@ -2496,10 +2496,10 @@ public void GenerateContentForEntry_SensesAndSubSensesWithDifferentNumberingStyl var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); - const string senseNumberOne = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='A']]//span[@lang='en' and text()='gloss']"; - const string senseNumberTwo = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='B']]//span[@lang='en' and text()='second gloss']"; - const string subSensesNumberTwoOne = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='I']]//span[@lang='en' and text()='second gloss2.1']"; - const string subSenseNumberTwoTwo = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='II']]//span[@lang='en' and text()='second gloss2.2']"; + const string senseNumberOne = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='A']]//span[@lang='en' and text()='gloss']"; + const string senseNumberTwo = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='B']]//span[@lang='en' and text()='second gloss']"; + const string subSensesNumberTwoOne = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='I']]//span[@lang='en' and text()='second gloss2.1']"; + const string subSenseNumberTwoTwo = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='II']]//span[@lang='en' and text()='second gloss2.2']"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseNumberOne, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseNumberTwo, 1); @@ -2561,10 +2561,10 @@ public void GenerateContentForEntry_SensesAndSubSensesWithNumberingStyle() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); - const string senseNumberOne = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='A']]//span[@lang='en' and text()='gloss']"; - const string senseNumberTwo = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='B']]//span[@lang='en' and text()='second gloss']"; - const string subSensesNumberTwoOne = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='second gloss2.1']"; - const string subSenseNumberTwoTwo = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]//span[@lang='en' and text()='second gloss2.2']"; + const string senseNumberOne = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='A']]//span[@lang='en' and text()='gloss']"; + const string senseNumberTwo = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='B']]//span[@lang='en' and text()='second gloss']"; + const string subSensesNumberTwoOne = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='second gloss2.1']"; + const string subSenseNumberTwoTwo = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]//span[@lang='en' and text()='second gloss2.2']"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseNumberOne, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseNumberTwo, 1); @@ -2613,7 +2613,7 @@ public void GenerateContentForEntry_NoSenseNumberFIfStyleSaysNoNumbering() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); - const string senseNumberXpath = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sensenumber']"; + const string senseNumberXpath = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sensenumber']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseNumberXpath, 0); // Should not have produced sense number if style said not to number it. // Piggy-back a test for ShouldThisSenseBeNumbered @@ -2673,8 +2673,8 @@ public void GenerateContentForEntry_SensesNoneAndSubSensesWithNumberingStyle() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); - const string subSensesNumberOne = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='second gloss2.1']"; - const string subSenseNumberTwo = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]//span[@lang='en' and text()='second gloss2.2']"; + const string subSensesNumberOne = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='second gloss2.1']"; + const string subSenseNumberTwo = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]//span[@lang='en' and text()='second gloss2.2']"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(subSensesNumberOne, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(subSenseNumberTwo, 1); @@ -2715,10 +2715,10 @@ public void GenerateContentForEntry_SensesGeneratedForMultipleSubSenses() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); - const string senseNumberOne = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='gloss']"; - const string senseNumberTwo = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]//span[@lang='en' and text()='second gloss']"; - const string subSensesNumberTwoOne = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='second gloss2.1']"; - const string subSenseNumberTwoTwo = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]//span[@lang='en' and text()='second gloss2.2']"; + const string senseNumberOne = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='gloss']"; + const string senseNumberTwo = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]//span[@lang='en' and text()='second gloss']"; + const string subSensesNumberTwoOne = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='second gloss2.1']"; + const string subSenseNumberTwoTwo = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]//span[@lang='en' and text()='second gloss2.2']"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseNumberOne, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseNumberTwo, 1); @@ -2770,9 +2770,9 @@ public void GenerateContentForEntry_SubSenseParentSenseNumberingStyleJoined() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); - const string senseNumber = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]"; - const string subSenseNumber = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2a']]"; - const string subSubSenseNumber = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-3']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2aA']]"; + const string senseNumber = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]"; + const string subSenseNumber = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2a']]"; + const string subSubSenseNumber = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-3']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2aA']]"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseNumber, 1); @@ -2824,9 +2824,9 @@ public void GenerateContentForEntry_SubSenseParentSenseNumberingStyleSeparatedBy var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); - const string senseNumber = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]"; - const string subSenseNumber = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2.a']]"; - const string subSubSenseNumber = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-3']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2.a.A']]"; + const string senseNumber = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]"; + const string subSenseNumber = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2.a']]"; + const string subSubSenseNumber = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-3']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2.a.A']]"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseNumber, 1); @@ -2878,9 +2878,9 @@ public void GenerateContentForEntry_SubSenseParentSenseNumberingStyleNone() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); - const string senseNumber = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]"; - const string subSenseNumber = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='a']]"; - const string subSubSenseNumber = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-3']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='A']]"; + const string senseNumber = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='2']]"; + const string subSenseNumber = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='a']]"; + const string subSubSenseNumber = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-3']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='A']]"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseNumber, 1); @@ -3002,13 +3002,13 @@ public void GenerateContentForEntry_GeneratesGramInfoFirstEvenSingleSense() xhtmlPath = LcmXhtmlGenerator.SavePreviewHtmlWithStyles(new[] { firstEntry.Hvo }, pubEverything, model, m_propertyTable); var xhtml = File.ReadAllText(xhtmlPath); // SUT - const string gramInfoPath = "/html/body/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sharedgrammaticalinfo']/span[@class='morphosyntaxanalysis-1']/span[@class='mlpartofspeech-1']/span[@lang='en' and text()='n']"; + const string gramInfoPath = "/html/body/div[@class='lexentry']/span[@class='senses']/span[@class='sharedgrammaticalinfo']/span[@class='morphosyntaxanalysis']/span[@class='mlpartofspeech']/span[@lang='en' and text()='n']"; AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(gramInfoPath, 1); - const string senseNumberPath = "/html/body/div[@class='lexentry-1']/span[@class='senses-1']/span[2][@class='sensecontent']/span[@class='sensenumber' and text()='1']"; + const string senseNumberPath = "/html/body/div[@class='lexentry']/span[@class='senses']/span[2][@class='sensecontent']/span[@class='sensenumber' and text()='1']"; AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(senseNumberPath, 1); - const string senseTextPath = "/html/body/div[@class='lexentry-1']/span[@class='senses-1']/span[2][@class='sensecontent']/span[@class='sense']/span[@class='gloss-1']/span[@lang='en' and text()='man']"; + const string senseTextPath = "/html/body/div[@class='lexentry']/span[@class='senses']/span[2][@class='sensecontent']/span[@class='sense']/span[@class='gloss']/span[@lang='en' and text()='man']"; AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(senseTextPath, 1); } finally @@ -3114,9 +3114,9 @@ public void GenerateContentForEntry_SubentriesSensesDontGetMainEntrySensesNumber var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, settings).ToString(); - const string senseContent = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']"; + const string senseContent = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']"; const string senseNumberOne = senseContent + "/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='gloss']"; - const string subentrySenseContent = senseContent + "/span[@class='sense']/span[@class='subentries-1']/span[@class='subentry']/span[@class='senses-2']/span[@class='sensecontent']"; + const string subentrySenseContent = senseContent + "/span[@class='sense']/span[@class='subentries']/span[@class='subentry']/span[@class='senses-2']/span[@class='sensecontent']"; const string subentrySenseNumberOne = subentrySenseContent + "/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='subgloss']"; const string subentrySenseNumberOneOne = subentrySenseContent + "/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1.1']]//span[@lang='en']"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry @@ -3154,7 +3154,7 @@ public void GenerateContentForEntry_SingleSenseGetsNumberWithNumberEvenOneSenseO // SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, DefaultSettings).ToString(); - const string senseNumberOne = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='gloss']"; + const string senseNumberOne = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and preceding-sibling::span[@class='sensenumber' and text()='1']]//span[@lang='en' and text()='gloss']"; // This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseNumberOne, 1); @@ -3184,10 +3184,10 @@ public void GenerateContentForEntry_SenseContentWithGuid() // SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, DefaultSettings).ToString(); - const string senseEntryGuid = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense' and @entryguid]"; + const string senseEntryGuid = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and @entryguid]"; // This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseEntryGuid, 1); - string senseEntryGuidstatsWithG = "/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense' and @entryguid='g" + testEntry.Guid + "']"; + string senseEntryGuidstatsWithG = "/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense' and @entryguid='g" + testEntry.Guid + "']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseEntryGuidstatsWithG, 1); } @@ -3237,11 +3237,11 @@ public void GenerateContentForEntry_ExampleAndTranslationAreGenerated() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, DefaultSettings).ToString(); - const string xpathThruExample = xpathThruSense + "/span[@class='examplescontents-1']/span[@class='examplescontent']"; - var oneSenseWithExample = string.Format(xpathThruExample + "/span[@class='example-1']/span[@lang='fr' and text()='{0}']", example); + const string xpathThruExample = xpathThruSense + "/span[@class='examplescontents']/span[@class='examplescontent']"; + var oneSenseWithExample = string.Format(xpathThruExample + "/span[@class='example']/span[@lang='fr' and text()='{0}']", example); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(oneSenseWithExample, 1); var oneExampleSentenceTranslation = string.Format(xpathThruExample + - "/span[@class='translationcontents-1']/span[@class='translationcontent']/span[@class='translation-1']/span[@lang='en' and text()='{0}']", translation); + "/span[@class='translationcontents']/span[@class='translationcontent']/span[@class='translation']/span[@lang='en' and text()='{0}']", translation); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(oneExampleSentenceTranslation, 1); } @@ -3291,11 +3291,11 @@ public void GenerateContentForEntry_ExampleSentenceAndTranslationAreGenerated() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); - const string xpathThruExampleSentence = "/div[@class='lexentry-1']/span[@class='complexformsnotsubentries-1']/span[@class='complexformsnotsubentry']/span[@class='examplesentences-1']/span[@class='examplesentence']"; + const string xpathThruExampleSentence = "/div[@class='lexentry']/span[@class='complexformsnotsubentries']/span[@class='complexformsnotsubentry']/span[@class='examplesentences']/span[@class='examplesentence']"; var oneSenseWithExample = string.Format(xpathThruExampleSentence + "//span[@lang='fr' and text()='{0}']", example); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(oneSenseWithExample, 1); var oneExampleSentenceTranslation = string.Format( - xpathThruExampleSentence + "/span[@class='translations-1']/span[@class='translation']//span[@lang='en' and text()='{0}']", translation); + xpathThruExampleSentence + "/span[@class='translations']/span[@class='translation']//span[@lang='en' and text()='{0}']", translation); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(oneExampleSentenceTranslation, 1); } @@ -3345,11 +3345,11 @@ public void GenerateContentForEntry_LineSeperatorUnicodeCharBecomesBrElement() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); - const string xpathThruExampleSentence = "/div[@class='lexentry-1']/span[@class='complexformsnotsubentries-1']/span[@class='complexformsnotsubentry']/span[@class='examplesentences-1']/span[@class='examplesentence']"; + const string xpathThruExampleSentence = "/div[@class='lexentry']/span[@class='complexformsnotsubentries']/span[@class='complexformsnotsubentry']/span[@class='examplesentences']/span[@class='examplesentence']"; var oneSenseWithExample = string.Format(xpathThruExampleSentence + "//span[@lang='fr']//br"); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(oneSenseWithExample, 1); var oneExampleSentenceTranslation = string.Format( - xpathThruExampleSentence + "/span[@class='translations-1']/span[@class='translation']//span[@lang='en']//br"); + xpathThruExampleSentence + "/span[@class='translations']/span[@class='translation']//span[@lang='en']//br"); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(oneExampleSentenceTranslation, 1); } @@ -3421,18 +3421,18 @@ public void GenerateContentForEntry_ExtendedNoteChildrenAreGenerated() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); - const string extendedNote = xpathThruSense + "/span[@class='extendednotecontents-1']/span[@class='extendednotecontent']"; - var xpathThruNoteType = string.Format(extendedNote + "/span[@class='extendednotetypera_name-1']/span[@lang='en' and text()='{0}']", noteType); + const string extendedNote = xpathThruSense + "/span[@class='extendednotecontents']/span[@class='extendednotecontent']"; + var xpathThruNoteType = string.Format(extendedNote + "/span[@class='extendednotetypera_name']/span[@lang='en' and text()='{0}']", noteType); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(xpathThruNoteType, 1); - var xpathThruDiscussion = string.Format(extendedNote + "/span[@class='discussion-1']/span[@lang='fr' and text()='{0}']", discussion); + var xpathThruDiscussion = string.Format(extendedNote + "/span[@class='discussion']/span[@lang='fr' and text()='{0}']", discussion); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(xpathThruDiscussion, 1); - const string xpathThruExample = extendedNote + "/span[@class='examples-1']/span[@class='example']"; - var oneSenseWithExample = string.Format(xpathThruExample + "/span[@class='example-1']/span[@lang='fr' and text()='{0}']", example); + const string xpathThruExample = extendedNote + "/span[@class='examples']/span[@class='example']"; + var oneSenseWithExample = string.Format(xpathThruExample + "/span[@class='example']/span[@lang='fr' and text()='{0}']", example); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(oneSenseWithExample, 1); var oneExampleSentenceTranslation = string.Format( - xpathThruExample + "/span[@class='translations-1']/span[@class='translation']/span[@class='translation-1']/span[@lang='en' and text()='{0}']", translation); + xpathThruExample + "/span[@class='translations']/span[@class='translation']/span[@class='translation']/span[@lang='en' and text()='{0}']", translation); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(oneExampleSentenceTranslation, 1); } @@ -3503,8 +3503,8 @@ public void GenerateContentForEntry_ExtendedNoteNoteTypeEmptyAreGenerated() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); - const string extendedNote = xpathThruSense + "/span[@class='extendednotecontents-1']/span[@class='extendednotecontent']"; - var xpathThruNoteType = string.Format(extendedNote + "/span[@class='extendednotetypera_name-1']/span[@lang='en' and text()='{0}']", noteType); + const string extendedNote = xpathThruSense + "/span[@class='extendednotecontents']/span[@class='extendednotecontent']"; + var xpathThruNoteType = string.Format(extendedNote + "/span[@class='extendednotetypera_name']/span[@lang='en' and text()='{0}']", noteType); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(xpathThruNoteType, 0); } @@ -3578,11 +3578,11 @@ public void GenerateContentForEntry_EnvironmentsAndAllomorphsAreGenerated() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); - const string xPathThruAllomorph = "/div[@class='lexentry-1']/span[@class='alternateformsos-1']/span[@class='alternateformso']"; + const string xPathThruAllomorph = "/div[@class='lexentry']/span[@class='alternateformsos']/span[@class='alternateformso']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( - xPathThruAllomorph + "/span[@class='form-1']/span[@lang='fr' and text()='Allomorph']", 1); + xPathThruAllomorph + "/span[@class='form']/span[@lang='fr' and text()='Allomorph']", 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(xPathThruAllomorph + - "/span[@class='allomorphenvironments-1']/span[@class='allomorphenvironment']/span[@class='stringrepresentation-1']/span[@lang='en' and text()='phoneyEnv']", 1); + "/span[@class='allomorphenvironments']/span[@class='allomorphenvironment']/span[@class='stringrepresentation']/span[@lang='en' and text()='phoneyEnv']", 1); } [Test] @@ -3616,7 +3616,7 @@ public void GenerateContentForEntry_ReferencedComplexFormsIncludesSubentriesAndO //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( - "/div[@class='lexentry-1']/span[@class='visiblecomplexformbackrefs-1']/span[@class='visiblecomplexformbackref']//span[@lang='fr']/span[@lang='fr']", 4); + "/div[@class='lexentry']/span[@class='visiblecomplexformbackrefs']/span[@class='visiblecomplexformbackref']//span[@lang='fr']/span[@lang='fr']", 4); } [Test] @@ -3668,9 +3668,9 @@ public void GenerateContentForEntry_GeneratesLinksForReferencedForms() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( - "//span[@class='visiblevariantentryrefs-1']/span[@class='visiblevariantentryref']/span[@class='referencedentries-1']/span[@class='referencedentry']/span[@class='headword-1']/span[@lang='fr']/span[@lang='fr']/a[@href]", 2); + "//span[@class='visiblevariantentryrefs']/span[@class='visiblevariantentryref']/span[@class='referencedentries']/span[@class='referencedentry']/span[@class='headword']/span[@lang='fr']/span[@lang='fr']/a[@href]", 2); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( - "/div[@class='lexentry-1']/span[@class='visiblecomplexformbackrefs-1']/span[@class='visiblecomplexformbackref']/span[@class='headword-2']/span[@lang='fr']/span[@lang='fr']/a[@href]", 2); + "/div[@class='lexentry']/span[@class='visiblecomplexformbackrefs']/span[@class='visiblecomplexformbackref']/span[@class='headword-2']/span[@lang='fr']/span[@lang='fr']/a[@href]", 2); } [Test] @@ -3767,9 +3767,9 @@ public void GenerateContentForEntry_GeneratesLinksForPrimaryEntryReferences() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(otherMainEntry, mainEntryNode, null, settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( - "//span[@class='minimallexreferences-1']/span[@class='minimallexreference']/span[@class='configtargets-1']/span[@class='configtarget']/span[@class='primaryentryrefs-1']/span[@class='primaryentryref']/span[@class='referencedentries-1']/span[@class='referencedentry']/span[@class='headword-1']/span[@lang='fr']/a[@href]", 1); + "//span[@class='minimallexreferences']/span[@class='minimallexreference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='primaryentryrefs']/span[@class='primaryentryref']/span[@class='referencedentries']/span[@class='referencedentry']/span[@class='headword']/span[@lang='fr']/a[@href]", 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( - "//span[@class='minimallexreferences-1']/span[@class='minimallexreference']/span[@class='configtargets-1']/span[@class='configtarget']/span[@class='primaryentryrefs-1']/span[@class='primaryentryref']/span[@class='referencedentries-1']/span[@class='referencedentry']/span[@class='headword-1']/span[@lang='fr']/a[@href][contains(text(), 'Test')]", 1); + "//span[@class='minimallexreferences']/span[@class='minimallexreference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='primaryentryrefs']/span[@class='primaryentryref']/span[@class='referencedentries']/span[@class='referencedentry']/span[@class='headword']/span[@lang='fr']/a[@href][contains(text(), 'Test')]", 1); } [Test] @@ -3814,7 +3814,7 @@ public void GenerateContentForEntry_GeneratesLinksForCrossReferences() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( - "//span[@class='minimallexreferences-1']/span[@class='minimallexreference']/span[@class='configtargets-1']/span[@class='configtarget']/span[@class='headword-1']/span[@lang='fr']/span[@lang='fr']/a[@href]", 4); + "//span[@class='minimallexreferences']/span[@class='minimallexreference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='headword']/span[@lang='fr']/span[@lang='fr']/a[@href]", 4); } [Test] @@ -3865,7 +3865,7 @@ public void GenerateContentForEntry_GeneratesCssForConfigTargetsInLexReferences( var result = ((CssGenerator)settings.StylesGenerator).GetStylesString(); - var pattern = @".configtargets-1>\s*\.configtarget\s*\+\s*\.configtarget:before\s*\{\s*content:\s*';';\s*\}\s*\.lexentry-1\s\.minimallexreferences-1\s\.configtargets-1:before\s*\{\s*content:\s*' ';\s*\}\s*\.lexentry-1\s\.minimallexreferences-1\s\.configtargets-1:after\s*\{\s*content:\s*'!';\s*\}"; + var pattern = @".configtargets>\s*\.configtarget\s*\+\s*\.configtarget:before\s*\{\s*content:\s*';';\s*\}\s*\.lexentry\s\.minimallexreferences\s\.configtargets:before\s*\{\s*content:\s*' ';\s*\}\s*\.lexentry\s\.minimallexreferences\s\.configtargets:after\s*\{\s*content:\s*'!';\s*\}"; CssGeneratorTests.VerifyRegex(result, pattern, "CSS verification failed."); } @@ -3920,7 +3920,7 @@ public void GenerateContentForEntry_GeneratesLinksForCrossReferencesWithReferenc //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( - "//span[@class='configtargets-1']/span[@class='configtarget']/span[@class='headword-1']/span[@lang='fr']//a[@href]", 4); + "//span[@class='configtargets']/span[@class='configtarget']/span[@class='headword']/span[@lang='fr']//a[@href]", 4); } [Test] @@ -3976,7 +3976,7 @@ public void GenerateContentForEntry_GeneratesCrossReferencesOnUnCheckConfigTarge //SUT- var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); AssertThatXmlIn.String(result).HasNoMatchForXpath( - "//span[@class='minimallexreferences-1']/span[@class='minimallexreference']/span[@class='configtargets-1']"); + "//span[@class='minimallexreferences']/span[@class='minimallexreference']/span[@class='configtargets']"); } [Test] @@ -4016,9 +4016,9 @@ public void GenerateContentForEntry_GeneratesForwardNameForSymmetricCrossReferen //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(referencedEntry, mainEntryNode, null, settings).ToString(); var fwdNameXpath = string.Format( - "//span[@class='minimallexreferences-1']/span[@class='minimallexreference']/span[@class='ownertype_name-1']/span[@lang='en' and text()='{0}']", refTypeName); + "//span[@class='minimallexreferences']/span[@class='minimallexreference']/span[@class='ownertype_name']/span[@lang='en' and text()='{0}']", refTypeName); const string anyNameXpath = - "//span[@class='minimallexreferences-1']/span[@class='minimallexreference']/span[@class='ownertype_name-1']/span[@lang='en']"; + "//span[@class='minimallexreferences']/span[@class='minimallexreference']/span[@class='ownertype_name']/span[@lang='en']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(fwdNameXpath, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(anyNameXpath, 1); // ensure there are no spurious names } @@ -4061,9 +4061,9 @@ public void GenerateContentForEntry_GeneratesForwardNameForForwardCrossReference //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); var fwdNameXpath = string.Format( - "//span[@class='minimallexreferences-1']/span[@class='minimallexreference']/span[@class='ownertype_name-1']/span[@lang='en' and text()='{0}']", refTypeName); + "//span[@class='minimallexreferences']/span[@class='minimallexreference']/span[@class='ownertype_name']/span[@lang='en' and text()='{0}']", refTypeName); var revNameXpath = string.Format( - "//span[@class='minimallexreferences-1']/span[@class='minimallexreference']/span[@class='ownertype_name-1']/span[@lang='en' and text()='{0}']", refTypeRevName); + "//span[@class='minimallexreferences']/span[@class='minimallexreference']/span[@class='ownertype_name']/span[@lang='en' and text()='{0}']", refTypeRevName); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(fwdNameXpath, 1); AssertThatXmlIn.String(result).HasNoMatchForXpath(revNameXpath); } @@ -4106,9 +4106,9 @@ public void GenerateContentForEntry_GeneratesReverseNameForReverseCrossReference //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(referencedEntry, mainEntryNode, null, settings).ToString(); var fwdNameXpath = string.Format( - "//span[@class='minimallexreferences-1']/span[@class='minimallexreference']/span[@class='ownertype_name-1']/span[@lang='en' and text()='{0}']", refTypeName); + "//span[@class='minimallexreferences']/span[@class='minimallexreference']/span[@class='ownertype_name']/span[@lang='en' and text()='{0}']", refTypeName); var revNameXpath = string.Format( - "//span[@class='minimallexreferences-1']/span[@class='minimallexreference']/span[@class='ownertype_name-1']/span[@lang='en' and text()='{0}']", refTypeRevName); + "//span[@class='minimallexreferences']/span[@class='minimallexreference']/span[@class='ownertype_name']/span[@lang='en' and text()='{0}']", refTypeRevName); AssertThatXmlIn.String(result).HasNoMatchForXpath(fwdNameXpath); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(revNameXpath, 1); } @@ -4156,9 +4156,9 @@ public void GenerateContentForEntry_GeneratesForwardNameForForwardLexicalRelatio //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); var fwdNameXpath = string.Format( - "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='ownertype_name-1']/span[@lang='en' and text()='{0}']", refTypeName); + "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='ownertype_name']/span[@lang='en' and text()='{0}']", refTypeName); var revNameXpath = string.Format( - "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='ownertype_name-1']/span[@lang='en' and text()='{0}']", refTypeRevName); + "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='ownertype_name']/span[@lang='en' and text()='{0}']", refTypeRevName); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(fwdNameXpath, 1); AssertThatXmlIn.String(result).HasNoMatchForXpath(revNameXpath); } @@ -4207,7 +4207,7 @@ public void GenerateContentForEntry_GeneratesLexicalRelationsLabelWithNoRepetiti //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); var fwdNameXpath = string.Format( - "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='ownertype_name-1']/span[@lang='en' and text()='{0}']", refTypeName); + "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='ownertype_name']/span[@lang='en' and text()='{0}']", refTypeName); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(fwdNameXpath, 1); } @@ -4254,9 +4254,9 @@ public void GenerateContentForEntry_GeneratesReverseNameForReverseLexicalRelatio //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(referencedEntry, mainEntryNode, null, settings).ToString(); var fwdNameXpath = string.Format( - "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='ownertype_name-1']/span[@lang='en' and text()='{0}']", refTypeName); + "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='ownertype_name']/span[@lang='en' and text()='{0}']", refTypeName); var revNameXpath = string.Format( - "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='ownertype_name-1']/span[@lang='en' and text()='{0}']", refTypeRevName); + "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='ownertype_name']/span[@lang='en' and text()='{0}']", refTypeRevName); AssertThatXmlIn.String(result).HasNoMatchForXpath(fwdNameXpath); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(revNameXpath, 1); } @@ -4308,7 +4308,7 @@ public void GenerateContentForEntry_LexicalRelationsSortbyNodeOptionsOrder() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); - const string NameXpath = "//span[@class='minimallexreferences-1']/span[@class='minimallexreference' and position()='{0}']/span[@class='ownertype_name-1']/span[@lang='en' and text()='{1}']"; + const string NameXpath = "//span[@class='minimallexreferences']/span[@class='minimallexreference' and position()='{0}']/span[@class='ownertype_name']/span[@lang='en' and text()='{1}']"; var fwdNameFirstXpath = string.Format(NameXpath, "1", etyRefTypeName); var fwdNameSecondXpath = string.Format(NameXpath, "2", comRefTypeName); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(fwdNameFirstXpath, 1); @@ -4388,21 +4388,21 @@ public void GenerateContentForEntry_GeneratesAsymmetricRelationsProperly() //SUT var output = ConfiguredLcmGenerator.GenerateContentForEntry(armEntry, mainEntryNode, null, settings).ToString(); var fwdNameXpath = string.Format( - "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='ownertype_name-1']/span[@lang='en' and text()='{0}']", refTypeName); + "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='ownertype_name']/span[@lang='en' and text()='{0}']", refTypeName); AssertThatXmlIn.String(output).HasNoMatchForXpath(fwdNameXpath); var revNameXpath = string.Format( - "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='ownertype_name-1']/span[@lang='en' and text()='{0}']", refTypeRevName); + "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='ownertype_name']/span[@lang='en' and text()='{0}']", refTypeRevName); AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(revNameXpath, 1); - var badTarget1 = "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='configtargets-1']/span[@class='configtarget']/span[@class='gloss-1']"; + var badTarget1 = "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='gloss']"; AssertThatXmlIn.String(output).HasNoMatchForXpath(badTarget1); var badTarget2 = string.Format( - "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='configtargets-1']/span[@class='configtarget']/span[@class='headword-1']/span[@lang='fr' and text()='{0}']", secondWord); + "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='headword']/span[@lang='fr' and text()='{0}']", secondWord); AssertThatXmlIn.String(output).HasNoMatchForXpath(badTarget2); var badTarget3 = string.Format( - "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='configtargets-1']/span[@class='configtarget']/span[@class='headword-1']/span[@lang='fr' and text()='{0}']", thirdWord); + "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='headword']/span[@lang='fr' and text()='{0}']", thirdWord); AssertThatXmlIn.String(output).HasNoMatchForXpath(badTarget3); var goodTarget = string.Format( - "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='configtargets-1']/span[@class='configtarget']/span[@class='headword-1']/span[@lang='fr' and text()='{0}']", firstWord); + "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='headword']/span[@lang='fr' and text()='{0}']", firstWord); AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(goodTarget, 1); } @@ -4466,9 +4466,9 @@ public void GenerateContentForEntry_GeneratesConfigTargetsForSubSenseProperly() //SUT var output = ConfiguredLcmGenerator.GenerateContentForEntry(firstEntry, mainEntryNode, null, settings).ToString(); var goodTarget = string.Format( - "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='configtargets-1']/span[@class='configtarget']/span[@class='headword-1']/span[@lang='fr' and text()='{0}']", firstHeadword); + "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='headword']/span[@lang='fr' and text()='{0}']", firstHeadword); AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(goodTarget, 1); - var badTarget = "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='configtargets-1']/span[@class='configtarget']/span[@class='gloss-1']"; + var badTarget = "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='configtargets']/span[@class='configtarget']/span[@class='gloss']"; AssertThatXmlIn.String(output).HasNoMatchForXpath(badTarget); } @@ -4535,13 +4535,13 @@ public void GenerateContentForEntry_GeneratesConfigTargetsForTreeBetweenSenses() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(firstEntry, mainEntryNode, DefaultDecorator, settings).ToString(); - var goodTarget1 = "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='ownertype_name-1']/span[text()='Part']/ancestor::span[1]/following-sibling::node()//span[@class='configtarget']/span[@class='gloss-1']/span[@lang='en' and text()='b2']"; + var goodTarget1 = "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='ownertype_name']/span[text()='Part']/ancestor::span[1]/following-sibling::node()//span[@class='configtarget']/span[@class='gloss']/span[@lang='en' and text()='b2']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(goodTarget1, 1); - var badTarget1 = "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='ownertype_name-1']/span[text()='Part']/ancestor::span[1]/following-sibling::node()//span[@class='configtarget']/span[@class='gloss-1']/span[@lang='en' and text()='b1']"; + var badTarget1 = "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='ownertype_name']/span[text()='Part']/ancestor::span[1]/following-sibling::node()//span[@class='configtarget']/span[@class='gloss']/span[@lang='en' and text()='b1']"; AssertThatXmlIn.String(result).HasNoMatchForXpath(badTarget1); - var goodTarget2 = "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='ownertype_name-1']/span[text()='Whole']/ancestor::span[1]/following-sibling::node()//span[@class='configtarget']/span[@class='gloss-1']/span[@lang='en' and text()='b1']"; + var goodTarget2 = "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='ownertype_name']/span[text()='Whole']/ancestor::span[1]/following-sibling::node()//span[@class='configtarget']/span[@class='gloss']/span[@lang='en' and text()='b1']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(goodTarget2, 1); - var badTarget2 = "//span[@class='lexsensereferences-1']/span[@class='lexsensereference']/span[@class='ownertype_name-1']/span[text()='Whole']/ancestor::span[1]/following-sibling::node()//span[@class='configtarget']/span[@class='gloss-1']/span[@lang='en' and text()='b2']"; + var badTarget2 = "//span[@class='lexsensereferences']/span[@class='lexsensereference']/span[@class='ownertype_name']/span[text()='Whole']/ancestor::span[1]/following-sibling::node()//span[@class='configtarget']/span[@class='gloss']/span[@lang='en' and text()='b2']"; AssertThatXmlIn.String(result).HasNoMatchForXpath(badTarget2); } @@ -4968,7 +4968,7 @@ public void GenerateContentForEntry_NoncheckedListItemsAreNotGenerated() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( - "/div[@class='lexentry-1']/span[@class='variantformentrybackrefs-1']/span[@class='variantformentrybackref']/span[@lang='fr']", 0); + "/div[@class='lexentry']/span[@class='variantformentrybackrefs']/span[@class='variantformentrybackref']/span[@lang='fr']", 0); } [Test] @@ -5019,7 +5019,7 @@ public void GenerateContentForEntry_CheckedListItemsAreGenerated() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( - "/div[@class='lexentry-1']/span[@class='variantformentrybackrefs-1']/span[@class='variantformentrybackref']//span[@lang='fr']/span[@lang='fr']", 2); + "/div[@class='lexentry']/span[@class='variantformentrybackrefs']/span[@class='variantformentrybackref']//span[@lang='fr']/span[@lang='fr']", 2); } [Test] @@ -5076,8 +5076,8 @@ public void GenerateContentForEntry_VariantTypeIsUncheckedAndHeadwordIsChecked() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( - "/div[@class='lexentry-1']/span[@class='variantformentrybackrefs-1']/span[@class='variantformentrybackref']/span[@class='referencedentries-1']" + - "/span[@class='referencedentry']/span[@class='headword-1']/span[@lang='fr']/span[@lang='fr' and text()='Citation']", 1); + "/div[@class='lexentry']/span[@class='variantformentrybackrefs']/span[@class='variantformentrybackref']/span[@class='referencedentries']" + + "/span[@class='referencedentry']/span[@class='headword']/span[@lang='fr']/span[@lang='fr' and text()='Citation']", 1); } [Test] @@ -5118,7 +5118,7 @@ public void GenerateContentForEntry_ReferencedComplexFormsUnderSensesIncludesSub //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath( - xpathThruSense + "/span[@class='visiblecomplexformbackrefs-1']/span[@class='visiblecomplexformbackref']//span[@lang='fr']/span[@lang='fr']", 4); + xpathThruSense + "/span[@class='visiblecomplexformbackrefs']/span[@class='visiblecomplexformbackref']//span[@lang='fr']/span[@lang='fr']", 4); } [Test] @@ -5320,8 +5320,8 @@ public void GenerateContentForEntry_OneSenseWithSinglePicture() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); - const string oneSenseWithPicture = "/div[@class='lexentry-1']/span[@class='pictures-1']/div[@class='picture']/img[@class='photo-1' and @id]"; - const string oneSenseWithPictureCaption = "/div[@class='lexentry-1']/span[@class='pictures-1']/div[@class='picture']/div[@class='captionContent']/span[@class='caption-1']//span[text()='caption']"; + const string oneSenseWithPicture = "/div[@class='lexentry']/span[@class='pictures']/div[@class='picture']/img[@class='photo' and @id]"; + const string oneSenseWithPictureCaption = "/div[@class='lexentry']/span[@class='pictures']/div[@class='picture']/div[@class='captionContent']/span[@class='caption']//span[text()='caption']"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(oneSenseWithPicture, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(oneSenseWithPictureCaption, 1); @@ -5427,8 +5427,8 @@ public void GenerateContentForEntry_PictureWithCreator() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); - const string oneSenseWithPicture = "/div[@class='lexentry-1']/span[@class='pictures-1']/div[@class='picture']/img[@class='photo-1' and @id]"; - const string oneSenseWithPictureCaption = "/div[@class='lexentry-1']/span[@class='pictures-1']/div[@class='picture']/div[@class='captionContent']/span[@class='creator-1' and text()='Jason Naylor']"; + const string oneSenseWithPicture = "/div[@class='lexentry']/span[@class='pictures']/div[@class='picture']/img[@class='photo' and @id]"; + const string oneSenseWithPictureCaption = "/div[@class='lexentry']/span[@class='pictures']/div[@class='picture']/div[@class='captionContent']/span[@class='creator' and text()='Jason Naylor']"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(oneSenseWithPicture, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(oneSenseWithPictureCaption, 1); @@ -5459,7 +5459,7 @@ public void GenerateContentForEntry_PictureWithNonUnicodePathLinksCorrectly() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); - var pictureWithComposedPath = "/div[@class='lexentry-1']/span[@class='pictures-1']/span[@class='picture']/img[contains(@src, '" + composedPath + "')]"; + var pictureWithComposedPath = "/div[@class='lexentry']/span[@class='pictures']/span[@class='picture']/img[contains(@src, '" + composedPath + "')]"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(pictureWithComposedPath, 1); } @@ -5488,7 +5488,7 @@ public void GenerateContentForEntry_PictureCopiedAndRelativePathUsed() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); var pictureRelativePath = Path.Combine("pictures", Path.GetFileName(filePath)); - var pictureWithComposedPath = "/div[@class='lexentry-1']/span[@class='pictures-1']/span[@class='picture']/img[starts-with(@src, '" + pictureRelativePath + "')]"; + var pictureWithComposedPath = "/div[@class='lexentry']/span[@class='pictures']/span[@class='picture']/img[starts-with(@src, '" + pictureRelativePath + "')]"; if (!Platform.IsUnix) AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(pictureWithComposedPath, 1); // that src starts with a string, and escaping any Windows path separators @@ -5525,7 +5525,7 @@ public void GenerateContentForEntry_MissingPictureFileDoesNotCrashOnCopy() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); var pictureRelativePath = Path.Combine("pictures", Path.GetFileName(filePath)); - var pictureWithComposedPath = "/div[@class='lexentry-1']/span[@class='pictures-1']/span[@class='picture']/img[starts-with(@src, '" + pictureRelativePath + "')]"; + var pictureWithComposedPath = "/div[@class='lexentry']/span[@class='pictures']/span[@class='picture']/img[starts-with(@src, '" + pictureRelativePath + "')]"; if (!Platform.IsUnix) AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(pictureWithComposedPath, 1); // that src starts with a string, and escaping any Windows path separators @@ -5580,14 +5580,14 @@ public void GenerateContentForEntry_TwoDifferentFilesGetTwoDifferentResults() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); var pictureRelativePath = Path.Combine("pictures", Path.GetFileName(fileName)); - var pictureWithComposedPath = "/div[@class='lexentry-1']/span[@class='pictures-1']/span[@class='picture']/img[contains(@src, '" + pictureRelativePath + "')]"; + var pictureWithComposedPath = "/div[@class='lexentry']/span[@class='pictures']/span[@class='picture']/img[contains(@src, '" + pictureRelativePath + "')]"; if (!Platform.IsUnix) AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(pictureWithComposedPath, 1); // that src contains a string, and escaping any Windows path separators AssertRegex(result, string.Format("src=\"[^\"]*{0}[^\"]*\"", pictureRelativePath.Replace(@"\", @"\\")), 1); // The second file with the same name should have had something appended to the end of the filename but the initial filename should match both entries var filenameWithoutExtension = Path.GetFileNameWithoutExtension(pictureRelativePath); - var pictureStartsWith = "/div[@class='lexentry-1']/span[@class='pictures-1']/span[@class='picture']/img[contains(@src, '" + filenameWithoutExtension + "')]"; + var pictureStartsWith = "/div[@class='lexentry']/span[@class='pictures']/span[@class='picture']/img[contains(@src, '" + filenameWithoutExtension + "')]"; if (!Platform.IsUnix) AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(pictureStartsWith, 2); // that src contains a string @@ -5635,7 +5635,7 @@ public void GenerateContentForEntry_UniqueIdsForSameFile() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); var pictureRelativePath = Path.Combine("pictures", Path.GetFileName(fileName)); - const string pictureXPath = "/div[@class='lexentry-1']/span[@class='pictures-1']/span[@class='picture']/img"; + const string pictureXPath = "/div[@class='lexentry']/span[@class='pictures']/span[@class='picture']/img"; var pictureWithComposedPath = pictureXPath + "[contains(@src, '" + pictureRelativePath + "')]"; if (!Platform.IsUnix) AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(pictureWithComposedPath, 2); @@ -5777,7 +5777,7 @@ public void GenerateContentForEntry_TwoDifferentLinksToTheSamefileWorks() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); var pictureRelativePath = Path.Combine("pictures", Path.GetFileName(fileName)); - var pictureWithComposedPath = "/div[@class='lexentry-1']/span[@class='pictures-1']/span[@class='picture']/img[contains(@src, '" + pictureRelativePath + "')]"; + var pictureWithComposedPath = "/div[@class='lexentry']/span[@class='pictures']/span[@class='picture']/img[contains(@src, '" + pictureRelativePath + "')]"; if (!Platform.IsUnix) AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(pictureWithComposedPath, 2); // that src starts with string, and escaping Windows directory separators @@ -5818,7 +5818,7 @@ public void GenerateContentForEntry_StringCustomFieldGeneratesContent() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); - var customDataPath = string.Format("/div[@class='lexentry-1']/span[@class='customstring-1']/span[text()='{0}']", customData); + var customDataPath = string.Format("/div[@class='lexentry']/span[@class='customstring']/span[text()='{0}']", customData); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); } } @@ -5855,7 +5855,7 @@ public void GenerateContentForEntry_CustomFieldInGroupingNodeGeneratesContent() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); - var customDataPath = $"/div[@class='lexentry-1']/span[@class='grouping_customgroup-1']/span[@class='customstring-1']/span[text()='" + customData + "']"; + var customDataPath = $"/div[@class='lexentry']/span[@class='grouping_customgroup']/span[@class='customstring']/span[text()='" + customData + "']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); } } @@ -5904,8 +5904,8 @@ public void GenerateContentForEntry_CustomFieldInNestedGroupingNodeGeneratesCont var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); - const string grpXPath = "/span[@class='grouping_customgroup-1']/span[@class='grouping_customgroup-2']/span[@class='grouping_customgroup-3']"; - var customDataPath = $"/div[@class='lexentry-1']{grpXPath}/span[@class='customstring-1']/span[text()='{customData}']"; + const string grpXPath = "/span[@class='grouping_customgroup']/span[@class='grouping_customgroup-2']/span[@class='grouping_customgroup-3']"; + var customDataPath = $"/div[@class='lexentry']{grpXPath}/span[@class='customstring']/span[text()='{customData}']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); } } @@ -5972,7 +5972,7 @@ public void GenerateContentForEntry_StringCustomFieldOnSenseGeneratesContent() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); - var customDataPath = string.Format("/div[@class='l-1']/span[@class='es-1']/span[@class='e']/span[@class='customstring-1']/span[text()='{0}']", customData); + var customDataPath = string.Format("/div[@class='l']/span[@class='es']/span[@class='e']/span[@class='customstring']/span[text()='{0}']", customData); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); } } @@ -6019,7 +6019,7 @@ public void GenerateContentForEntry_StringCustomFieldOnExampleGeneratesContent() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); var customDataPath = string.Format( - "/div[@class='l-1']/span[@class='es-1']//span[@class='xs-1']/span[@class='x']/span[@class='customstring-1']/span[text()='{0}']", customData); + "/div[@class='l']/span[@class='es']//span[@class='xs']/span[@class='x']/span[@class='customstring']/span[text()='{0}']", customData); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); } } @@ -6058,7 +6058,7 @@ public void GenerateContentForEntry_StringCustomFieldOnAllomorphGeneratesContent //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); var customDataPath = string.Format( - "/div[@class='l-1']/span[@class='as-1']/span[@class='a']/span[@class='customstring-1']/span[text()='{0}']", customData); + "/div[@class='l']/span[@class='as']/span[@class='a']/span[@class='customstring']/span[text()='{0}']", customData); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); } } @@ -6089,7 +6089,7 @@ public void GenerateContentForEntry_MultiStringCustomFieldGeneratesContent() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); - var customDataPath = string.Format("/div[@class='lexentry-1']/span[@class='customstring-1']/span[text()='{0}']", customData); + var customDataPath = string.Format("/div[@class='lexentry']/span[@class='customstring']/span[text()='{0}']", customData); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); } } @@ -6151,9 +6151,9 @@ public void GenerateContentForEntry_CustomFieldOnISenseOrEntryGeneratesContentFo var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); - var entryDataPath = string.Format("/div[@class='lexentry-1']/span[@class='mlrs-1']/span[@class='mlr']/span[@class='configtargets-1']/span[@class='configtarget']/span[@class='entrycstring-1']/span[text()='{0}']", entryCustomData); + var entryDataPath = string.Format("/div[@class='lexentry']/span[@class='mlrs']/span[@class='mlr']/span[@class='configtargets']/span[@class='configtarget']/span[@class='entrycstring']/span[text()='{0}']", entryCustomData); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(entryDataPath, 1); - var senseDataPath = string.Format("/div[@class='lexentry-1']/span[@class='mlrs-1']/span[@class='mlr']/span[@class='configtargets-1']/span[@class='configtarget']/span[@class='sensecstring']/span[text()='{0}']", senseCustomData); + var senseDataPath = string.Format("/div[@class='lexentry']/span[@class='mlrs']/span[@class='mlr']/span[@class='configtargets']/span[@class='configtarget']/span[@class='sensecstring']/span[text()='{0}']", senseCustomData); AssertThatXmlIn.String(result).HasNoMatchForXpath(senseDataPath, message: "Ref is to Entry; should be no Sense Custom Data"); } } @@ -6215,9 +6215,9 @@ public void GenerateContentForEntry_CustomFieldOnISenseOrEntryGeneratesContentFo var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); - var entryDataPath = string.Format("/div[@class='lexentry-1']/span[@class='mlrs-1']/span[@class='mlr']/span[@class='configtargets-1']/span[@class='configtarget']/span[@class='entrycstring-1']/span[text()='{0}']", entryCustomData); + var entryDataPath = string.Format("/div[@class='lexentry']/span[@class='mlrs']/span[@class='mlr']/span[@class='configtargets']/span[@class='configtarget']/span[@class='entrycstring']/span[text()='{0}']", entryCustomData); AssertThatXmlIn.String(result).HasNoMatchForXpath(entryDataPath, message: "Ref is to Sense; should be no Entry Custom Data"); - var senseDataPath = string.Format("/div[@class='lexentry-1']/span[@class='mlrs-1']/span[@class='mlr']/span[@class='configtargets-1']/span[@class='configtarget']/span[@class='sensecstring-1']/span[text()='{0}']", senseCustomData); + var senseDataPath = string.Format("/div[@class='lexentry']/span[@class='mlrs']/span[@class='mlr']/span[@class='configtargets']/span[@class='configtarget']/span[@class='sensecstring']/span[text()='{0}']", senseCustomData); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseDataPath, 1); } } @@ -6255,7 +6255,7 @@ public void GenerateContentForEntry_CustomFieldOnRefdLexEntryGeneratesContent() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); - var customDataPath = string.Format("/div[@class='lexentry-1']/span[@class='vars-1']/span[@class='var']/span[@class='owningentry_customstring-1']/span[text()='{0}']", customData); + var customDataPath = string.Format("/div[@class='lexentry']/span[@class='vars']/span[@class='var']/span[@class='owningentry_customstring']/span[text()='{0}']", customData); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); } } @@ -6291,7 +6291,7 @@ public void GenerateContentForEntry_MultiStringDefinition_GeneratesMultilingualS var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); - var definitionXpath = "//div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='definition-1']/span[@lang='en']"; + var definitionXpath = "//div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='definition']/span[@lang='en']"; var str1Xpath = string.Format(definitionXpath + "/span[@lang='en' and text()='{0}']", multirunContent[0]); var str2Xpath = string.Format(definitionXpath + "/span[@lang='fr' and text()='{0}']", multirunContent[1]); var str3Xpath = string.Format(definitionXpath + "/span[@lang='en' and text()='{0}']", multirunContent[2]); @@ -6339,7 +6339,7 @@ public void GenerateContentForEntry_ListItemCustomFieldGeneratesContent() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); - const string customDataPath = "/div[@class='lexentry-1']/span[@class='customlistitem-1']/span[@class='name-1']/span[text()='Djbuti']"; + const string customDataPath = "/div[@class='lexentry']/span[@class='customlistitem']/span[@class='name']/span[text()='Djbuti']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); } } @@ -6380,8 +6380,8 @@ public void GenerateContentForEntry_MultiListItemCustomFieldGeneratesContent() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); - const string customDataPath1 = "/div[@class='lexentry-1']/span[@class='customlistitems-1']/span[@class='customlistitem']/span[@class='name-1']/span[text()='Dallas']"; - const string customDataPath2 = "/div[@class='lexentry-1']/span[@class='customlistitems-1']/span[@class='customlistitem']/span[@class='name-1']/span[text()='Barcelona']"; + const string customDataPath1 = "/div[@class='lexentry']/span[@class='customlistitems']/span[@class='customlistitem']/span[@class='name']/span[text()='Dallas']"; + const string customDataPath2 = "/div[@class='lexentry']/span[@class='customlistitems']/span[@class='customlistitem']/span[@class='name']/span[text()='Barcelona']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath1, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath2, 1); } @@ -6412,7 +6412,7 @@ public void GenerateContentForEntry_DateCustomFieldGeneratesContent() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); - var customDataPath = string.Format("/div[@class='lexentry-1']/span[@class='customdate-1' and text()='{0}']", customData.ToLongDateString()); + var customDataPath = string.Format("/div[@class='lexentry']/span[@class='customdate' and text()='{0}']", customData.ToLongDateString()); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); } } @@ -6442,7 +6442,7 @@ public void GenerateContentForEntry_IntegerCustomFieldGeneratesContent() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, settings).ToString(); - var customDataPath = string.Format("/div[@class='lexentry-1']/span[@class='custominteger-1' and text()='{0}']", customData); + var customDataPath = string.Format("/div[@class='lexentry']/span[@class='custominteger' and text()='{0}']", customData); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 1); } } @@ -6474,7 +6474,7 @@ public void GenerateContentForEntry_MultiLineCustomFieldGeneratesContent() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, rootNode, null, settings).ToString(); const string customDataPath = - "/div[@class='lexentry-1']/div/span[text()='First para Custom string'] | /div[@class='lexentry-1']/div/span[text()='Second para Custom string']"; + "/div[@class='lexentry']/div/span[text()='First para Custom string'] | /div[@class='lexentry']/div/span[text()='Second para Custom string']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(customDataPath, 2); } } @@ -6512,7 +6512,7 @@ public void GenerateContentForEntry_VariantOfReferencedHeadWord() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, settings).ToString(); const string referencedEntries = - "//span[@class='visiblevariantentryrefs-1']/span[@class='visiblevariantentryref']/span[@class='referencedentries-1']/span[@class='referencedentry']/span[@class='headword-1']/span[@lang='fr']/span[@lang='fr']"; + "//span[@class='visiblevariantentryrefs']/span[@class='visiblevariantentryref']/span[@class='referencedentries']/span[@class='referencedentry']/span[@class='headword']/span[@lang='fr']/span[@lang='fr']"; AssertThatXmlIn.String(result) .HasSpecifiedNumberOfMatchesForXpath(referencedEntries, 2); } @@ -6550,7 +6550,7 @@ public void GenerateContentForEntry_WsAudiowithHyperlink() @"Src/xWorks/xWorksTests/TestData/LinkedFiles/AudioVisual/" + audioFileName; Assert.That(result, Contains.Substring(audioFileUrl)); const string linkTagwithOnClick = - "//span[@class='lexemeformoa-1']/span/a[@class='en-Zxxx-x-audio' and contains(@onclick,'play()')]"; + "//span[@class='lexemeformoa']/span/a[@class='en-Zxxx-x-audio' and contains(@onclick,'play()')]"; AssertThatXmlIn.String(result) .HasSpecifiedNumberOfMatchesForXpath(linkTagwithOnClick, 1); } @@ -6859,7 +6859,7 @@ public void GenerateContentForEntry_WsAudiowithRelativePaths() AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(audioTagwithSource, 1); var audioFileUrl = Path.Combine("AudioVisual", audioFileName); Assert.That(result, Contains.Substring(audioFileUrl)); - var linkTagwithOnClick = "//span[@class='lexemeformoa-1']/span/a[@class='en-Zxxx-x-audio'"; + var linkTagwithOnClick = "//span[@class='lexemeformoa']/span/a[@class='en-Zxxx-x-audio'"; linkTagwithOnClick += " and @href='#" + safeAudioId + "'"; linkTagwithOnClick += " and contains(@onclick,'" + safeAudioId + "') and contains(@onclick,'.play()')]"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(linkTagwithOnClick, 1); @@ -6989,10 +6989,10 @@ public void GenerateContentForEntry_GeneratesComplexFormTypeForSubentryUnderSens //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings).ToString(); var fwdNameXpath = string.Format( - "//span[@class='sense']/span[@class='subentries-1']/span[@class='subentry']/span[@class='complexformtypes-1']/span[@class='complexformtype']/span/span[@lang='en' and text()='{0}']", + "//span[@class='sense']/span[@class='subentries']/span[@class='subentry']/span[@class='complexformtypes']/span[@class='complexformtype']/span/span[@lang='en' and text()='{0}']", complexRefAbbr); var revNameXpath = string.Format( - "//span[@class='sense']/span[@class='subentries-1']/span[@class='subentry']/span[@class='complexformtypes-1']/span[@class='complexformtype']/span[@class='reverseabbr-1']/span[@lang='en' and text()='{0}']", + "//span[@class='sense']/span[@class='subentries']/span[@class='subentry']/span[@class='complexformtypes']/span[@class='complexformtype']/span[@class='reverseabbr']/span[@lang='en' and text()='{0}']", complexRefRevAbbr); AssertThatXmlIn.String(result).HasNoMatchForXpath(fwdNameXpath); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(revNameXpath, 1); @@ -7037,10 +7037,10 @@ public void GenerateContentForEntry_GeneratesComplexFormTypeForSubentry() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings).ToString(); var fwdNameXpath = string.Format( - "//span[@class='subentries-1']/span[@class='subentry']/span[@class='complexformtypes-1']/span[@class='complexformtype']/span/span[@lang='en' and text()='{0}']", + "//span[@class='subentries']/span[@class='subentry']/span[@class='complexformtypes']/span[@class='complexformtype']/span/span[@lang='en' and text()='{0}']", complexRefAbbr); var revNameXpath = string.Format( - "//span[@class='subentries-1']/span[@class='subentry']/span[@class='complexformtypes-1']/span[@class='complexformtype']/span[@class='reverseabbr-1']/span[@lang='en' and text()='{0}']", + "//span[@class='subentries']/span[@class='subentry']/span[@class='complexformtypes']/span[@class='complexformtype']/span[@class='reverseabbr']/span[@lang='en' and text()='{0}']", complexRefRevAbbr); AssertThatXmlIn.String(result).HasNoMatchForXpath(fwdNameXpath); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(revNameXpath, 1); @@ -7108,11 +7108,11 @@ public void GenerateContentForEntry_GeneratesComplexFormTypeForSubsubentry([Valu //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings).ToString(); const string fwdNameXpath = - "//span[@class='subentries-1']/span[@class='subentry subentry']/span[@class='subentries-3']/span[@class='subentry subentry']" - + "/span[@class='complexformtypes-1']/span[@class='complexformtype']/span/span[@lang='en' and text()='{0}']"; + "//span[@class='subentries']/span[@class='subentry subentry']/span[@class='subentries-3']/span[@class='subentry subentry']" + + "/span[@class='complexformtypes']/span[@class='complexformtype']/span/span[@lang='en' and text()='{0}']"; const string revNameXpath = - "//span[@class='subentries-1']/span[@class='subentry subentry']/span[@class='subentries-3']/span[@class='subentry subentry']" - + "/span[@class='complexformtypes-1']/span[@class='complexformtype']/span[@class='reverseabbr-1']/span[@lang='en' and text()='{0}']"; + "//span[@class='subentries']/span[@class='subentry subentry']/span[@class='subentries-3']/span[@class='subentry subentry']" + + "/span[@class='complexformtypes']/span[@class='complexformtype']/span[@class='reverseabbr']/span[@lang='en' and text()='{0}']"; AssertThatXmlIn.String(result).HasNoMatchForXpath(string.Format(fwdNameXpath, complexRefAbbr)); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(string.Format(revNameXpath, complexRefRevAbbr), 1); AssertThatXmlIn.String(result).HasNoMatchForXpath(string.Format(revNameXpath, otherComplexRefRevAbbr), @@ -7168,7 +7168,7 @@ public void GenerateContentForEntry_DoesntGeneratesComplexFormType_WhenDisabled( var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings).ToString(); - const string refTypeXpath = "//span[@class='subentries-1']/span[@class='subentry']/span[@class='complexformtypes-1']"; + const string refTypeXpath = "//span[@class='subentries']/span[@class='subentry']/span[@class='complexformtypes']"; AssertThatXmlIn.String(result).HasNoMatchForXpath(refTypeXpath); StringAssert.DoesNotContain(complexRefAbbr, result); } @@ -7255,9 +7255,9 @@ public void GenerateContentForEntry_GeneratesComplexForm_NoTypeSpecified() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings).ToString(); - const string refTypeXpath = "//span[@class='visiblecomplexformbackrefs-1']/span[@class='visiblecomplexformbackref']/span[@class='complexformtypes-1']/span[@class='complexformtype']"; + const string refTypeXpath = "//span[@class='visiblecomplexformbackrefs']/span[@class='visiblecomplexformbackref']/span[@class='complexformtypes']/span[@class='complexformtype']"; AssertThatXmlIn.String(result).HasNoMatchForXpath(refTypeXpath); - const string headwordXpath = "//span[@class='visiblecomplexformbackrefs-1']/span[@class='visiblecomplexformbackref']/span[@class='headword-1']"; + const string headwordXpath = "//span[@class='visiblecomplexformbackrefs']/span[@class='visiblecomplexformbackref']/span[@class='headword']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(headwordXpath, 1); } @@ -7297,9 +7297,9 @@ public void GenerateContentForEntry_GeneratesSubentry_NoTypeSpecified() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings).ToString(); - const string refTypeXpath = "//span[@class='subentries-1']/span[@class='subentry']/span[@class='complexformtypes-1']/span[@class='complexformtype']"; + const string refTypeXpath = "//span[@class='subentries']/span[@class='subentry']/span[@class='complexformtypes']/span[@class='complexformtype']"; AssertThatXmlIn.String(result).HasNoMatchForXpath(refTypeXpath); - const string headwordXpath = "//span[@class='subentries-1']/span[@class='subentry']/span[@class='headword-1']"; + const string headwordXpath = "//span[@class='subentries']/span[@class='subentry']/span[@class='headword']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(headwordXpath, 1); } @@ -7337,8 +7337,8 @@ public void GenerateContentForEntry_ComplexFormDontGenerateReference() CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); subentryRef.HideMinorEntry = 1; - const string withReference = "/div[@class='lexentry-1']/span[@class='subentries-1']/span[@class='subentry']/span[@class='headword-1']/span[@lang='fr']/span[@lang='fr']/a[@href]"; - const string withoutReference = "/div[@class='lexentry-1']/span[@class='subentries-1']/span[@class='subentry']/span[@class='headword-1']/span[@lang='fr']/span[@lang='fr']"; + const string withReference = "/div[@class='lexentry']/span[@class='subentries']/span[@class='subentry']/span[@class='headword']/span[@lang='fr']/span[@lang='fr']/a[@href]"; + const string withoutReference = "/div[@class='lexentry']/span[@class='subentries']/span[@class='subentry']/span[@class='headword']/span[@lang='fr']/span[@lang='fr']"; // When hiding minor entries this should still generate the reference (if not publishing to Webonary). var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null, false, false); @@ -7442,9 +7442,9 @@ public void GenerateContentForEntry_GeneratesVariant_NoTypeSpecified() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings).ToString(); - const string refTypeXpath = "//span[@class='variantformentrybackrefs-1']/span[@class='variantformentrybackref']/span[@class='variantentrytypesrs']/span[@class='variantentrytypesr']"; + const string refTypeXpath = "//span[@class='variantformentrybackrefs']/span[@class='variantformentrybackref']/span[@class='variantentrytypesrs']/span[@class='variantentrytypesr']"; AssertThatXmlIn.String(result).HasNoMatchForXpath(refTypeXpath); - const string headwordXpath = "//span[@class='variantformentrybackrefs-1']/span[@class='variantformentrybackref']/span[@class='headword-1']"; + const string headwordXpath = "//span[@class='variantformentrybackrefs']/span[@class='variantformentrybackref']/span[@class='headword']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(headwordXpath, 1); } @@ -7484,11 +7484,11 @@ public void GenerateContentForEntry_VariantShowsIfNotHideMinorEntry_ViewDoesntMa // try with HideMinorEntry off variantEntryRef.HideMinorEntry = 0; result = ConfiguredLcmGenerator.GenerateContentForEntry(variantEntry, model, null, settings).ToString(); - AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath("/div[@class='lexentry-1']/span[@class='headword-1']", 1); + AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath("/div[@class='lexentry']/span[@class='headword']", 1); // Should get the same results if in Root based view model.IsRootBased = true; result = ConfiguredLcmGenerator.GenerateContentForEntry(variantEntry, model, null, settings).ToString(); - AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath("/div[@class='lexentry-1']/span[@class='headword-1']", 1); + AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath("/div[@class='lexentry']/span[@class='headword']", 1); variantEntryRef.HideMinorEntry = 1; result = ConfiguredLcmGenerator.GenerateContentForEntry(variantEntry, model, null, settings).ToString(); Assert.IsEmpty(result); @@ -7543,7 +7543,7 @@ public void GenerateContentForEntry_VariantGenerateReferenceForHiddenEntry() CssGeneratorTests.PopulateFieldsForTesting(model); var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null, false, false); - const string withReference = "/div[@class='lexentry-1']/span[@class='variantformentrybackrefs-1']/span[@class='variantformentrybackref']/span[@class='headword-1']/span[@lang='fr']/span[@lang='fr']/a[@href]"; + const string withReference = "/div[@class='lexentry']/span[@class='variantformentrybackrefs']/span[@class='variantformentrybackref']/span[@class='headword']/span[@lang='fr']/span[@lang='fr']/a[@href]"; // When not hiding minor entries this should generate the reference. variantEntryRef.HideMinorEntry = 0; @@ -7606,8 +7606,8 @@ public void GenerateContentForEntry_VariantDontGenerateReference() CssGeneratorTests.PopulateFieldsForTesting(model); variantEntryRef.HideMinorEntry = 1; - const string withReference = "/div[@class='lexentry-1']/span[@class='variantformentrybackrefs-1']/span[@class='variantformentrybackref']/span[@class='headword-1']/span[@lang='fr']/span[@lang='fr']/a[@href]"; - const string withoutReference = "/div[@class='lexentry-1']/span[@class='variantformentrybackrefs-1']/span[@class='variantformentrybackref']/span[@class='headword-1']/span[@lang='fr']/span[@lang='fr']"; + const string withReference = "/div[@class='lexentry']/span[@class='variantformentrybackrefs']/span[@class='variantformentrybackref']/span[@class='headword']/span[@lang='fr']/span[@lang='fr']/a[@href]"; + const string withoutReference = "/div[@class='lexentry']/span[@class='variantformentrybackrefs']/span[@class='variantformentrybackref']/span[@class='headword']/span[@lang='fr']/span[@lang='fr']"; // When hiding minor entries this should still generate the reference (if not publishing to Webonary). var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null, false, false); @@ -7661,7 +7661,7 @@ public void GenerateContentForEntry_ReferencedNode_GeneratesBothClasses() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings).ToString(); - const string headwordXpath = "//span[@class='reffingsubs-1']/span[@class='reffingsub sharedsubentry']/span[@class='headword-1']"; + const string headwordXpath = "//span[@class='reffingsubs']/span[@class='reffingsub sharedsubentry']/span[@class='headword']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(headwordXpath, 1); } @@ -7695,7 +7695,7 @@ public void GenerateContentForEntry_GeneratesCorrectMainAndMinorEntries() var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForMainEntry(idiom, mainEntryNode, null, settings, 0).ToString(); - AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath("/div[@class='lexentry-1']/span[@class='headword-1']", 1); + AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath("/div[@class='lexentry']/span[@class='headword']", 1); var css = ((CssGenerator)settings.StylesGenerator).GetStylesString(); // verify that the flow reset css is generated Assert.That(css, Contains.Substring("white-space:pre-wrap")); @@ -7773,7 +7773,7 @@ public void GenerateContentForEntry_GeneratesCorrectMinorEntries( var isMinorEntryShowing = isComplexFormShowing || isVariantFormShowing; if (isMinorEntryShowing) - AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath("/div[@class='lexentry-1']/span[@class='headword-1']", 1); + AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath("/div[@class='lexentry']/span[@class='headword']", 1); else Assert.IsEmpty(result); } @@ -8004,15 +8004,15 @@ public void GenerateContentForEntry_FilterByPublication() }; CssGeneratorTests.PopulateFieldsForTesting(model); - const string matchFrenchEntry = "//span[@class='entry-1']/span[@lang='fr']"; - const string matchFrenchPronunciation = "//span[@class='pronunciations-1']/span[@class='pronunciation']/span[@class='form-1']/span[@lang='fr']"; + const string matchFrenchEntry = "//span[@class='entry']/span[@lang='fr']"; + const string matchFrenchPronunciation = "//span[@class='pronunciations']/span[@class='pronunciation']/span[@class='form']/span[@lang='fr']"; const string matchEnglishDefOrGloss = - "//span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='definitionorgloss-1']/span[@lang='en']"; + "//span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='definitionorgloss']/span[@lang='en']"; const string matchFrenchExample = - "//span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='examples-1']/span[@class='example']/span[@class='examplesentence-1']/span[@lang='fr']"; + "//span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='examples']/span[@class='example']/span[@class='examplesentence']/span[@lang='fr']"; const string matchEnglishTranslation = - "//span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='examples-1']/span[@class='example']/span[@class='translations-1']/span[@class='translation']/span[@class='translatedsentence-1']/span[@lang='en']"; - const string matchFrenchPictureCaption = "//span[@class='pictures-1']/div[@class='picture']/div[@class='captionContent']/span[@class='caption-1']/span[@lang='fr']"; + "//span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='examples']/span[@class='example']/span[@class='translations']/span[@class='translation']/span[@class='translatedsentence']/span[@lang='en']"; + const string matchFrenchPictureCaption = "//span[@class='pictures']/div[@class='picture']/div[@class='captionContent']/span[@class='caption']/span[@lang='fr']"; var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT @@ -8038,7 +8038,7 @@ public void GenerateContentForEntry_FilterByPublication() AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchEnglishTranslation, 1); AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchFrenchPictureCaption, 1); const string matchBodyIsBig = - "//span[@class='examples-1']/span[@class='example']/span[@class='translations-1']/span[@class='translation']/span[@class='translatedsentence-1']/span[@lang='en' and text()='The body is big.']"; + "//span[@class='examples']/span[@class='example']/span[@class='translations']/span[@class='translation']/span[@class='translatedsentence']/span[@lang='en' and text()='The body is big.']"; AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchBodyIsBig, 1); //SUT @@ -8052,7 +8052,7 @@ public void GenerateContentForEntry_FilterByPublication() AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchEnglishTranslation, 1); AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchFrenchPictureCaption, 0); const string matchCorpseIsDead = - "//span[@class='examples-1']/span[@class='example']/span[@class='translations-1']/span[@class='translation']/span[@class='translatedsentence-1']/span[@lang='en' and text()='The corpse is dead.']"; + "//span[@class='examples']/span[@class='example']/span[@class='translations']/span[@class='translation']/span[@class='translatedsentence']/span[@lang='en' and text()='The corpse is dead.']"; AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchCorpseIsDead, 1); //SUT @@ -8113,10 +8113,10 @@ public void GenerateContentForEntry_FilterByPublication() AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchFrenchExample, 1); AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchEnglishTranslation, 1); - const string matchFrenchSubentry = "//span[@class='subentries-1']/span[@class='subentry']/span[@class='subentry-1']/span[@lang='fr']"; - const string matchMainsubentry = "//span[@class='subentries-1']/span[@class='subentry']/span[@class='subentry-1']/span[@lang='fr'and text()='mainsubentry']"; - const string matchTestsubentry = "//span[@class='subentries-1']/span[@class='subentry']/span[@class='subentry-1']/span[@lang='fr'and text()='testsubentry']"; - const string matchVariantRef = "//span[@class='variantentrytypes-1']/span[@class='variantentrytype']/span[@class='name-1']/span[@lang='en']"; + const string matchFrenchSubentry = "//span[@class='subentries']/span[@class='subentry']/span[@class='subentry']/span[@lang='fr']"; + const string matchMainsubentry = "//span[@class='subentries']/span[@class='subentry']/span[@class='subentry']/span[@lang='fr'and text()='mainsubentry']"; + const string matchTestsubentry = "//span[@class='subentries']/span[@class='subentry']/span[@class='subentry']/span[@lang='fr'and text()='testsubentry']"; + const string matchVariantRef = "//span[@class='variantentrytypes']/span[@class='variantentrytype']/span[@class='name']/span[@lang='en']"; //SUT output = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNode, pubMain, settings).ToString(); @@ -8195,7 +8195,7 @@ public void GenerateContentForEntry_GeneratesVariantEntryTypesLabelWithNoRepetit { var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); var output = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNode, null, settings).ToString(); - const string matchVariantRef = "//span[@class='variantentrytypes-1']/span[@class='variantentrytype']/span[@class='name-1']/span[@lang='en']"; + const string matchVariantRef = "//span[@class='variantentrytypes']/span[@class='variantentrytype']/span[@class='name']/span[@lang='en']"; AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchVariantRef, 2); } } @@ -8258,9 +8258,9 @@ public void GenerateContentForEntry_GeneratesVariantEntryTypesShowOnlySelectedLi CreateVariantForm(Cache, entryEntry, ve2, "Spelling Variant"); // unique Type; UnChecked var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); var output = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNode, null, settings).ToString(); - const string matchFreeVariantRef = "//span[@class='variantentrytypes-1']/span[@class='variantentrytype']/span[@class='name-1']/span[@lang='en' and text()='Free Variant']"; + const string matchFreeVariantRef = "//span[@class='variantentrytypes']/span[@class='variantentrytype']/span[@class='name']/span[@lang='en' and text()='Free Variant']"; AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchFreeVariantRef, 1); - const string matchSpellingVariantRef = "//span[@class='variantentrytypes-1']/span[@class='variantentrytype']/span[@class='name-1']/span[@lang='en' and text()='Spelling Variant']"; + const string matchSpellingVariantRef = "//span[@class='variantentrytypes']/span[@class='variantentrytype']/span[@class='name']/span[@lang='en' and text()='Spelling Variant']"; AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchSpellingVariantRef, 0); } @@ -8321,7 +8321,7 @@ public void GenerateContentForEntry_GeneratesComplexFormEntryTypesLabelWithNoRep CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); var output = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNode, pubMain, DefaultSettings).ToString(); - const string matchComplexFormRef = "//span[@class='complexformtypes-1']/span[@class='complexformtype']/span[@class='name-1']/span[@lang='en']"; + const string matchComplexFormRef = "//span[@class='complexformtypes']/span[@class='complexformtype']/span[@class='name']/span[@lang='en']"; AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchComplexFormRef, 1); } @@ -8384,11 +8384,11 @@ public void GenerateContentForEntry_GeneratesComplexFormEntryTypesAndNamesGroup( CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); var output = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNode, pubMain, DefaultSettings).ToString(); - const string matchComplexFormTypeCompound = "//span[@class='complexformtypes-1']/span[@class='complexformtype']/span[@class='name-1']/span[@lang='en' and text()='Compound']"; + const string matchComplexFormTypeCompound = "//span[@class='complexformtypes']/span[@class='complexformtype']/span[@class='name']/span[@lang='en' and text()='Compound']"; AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchComplexFormTypeCompound, 1); - const string matchComplexFormTypeIdiom = "//span[@class='complexformtypes-1']/span[@class='complexformtype']/span[@class='name-1']/span[@lang='en' and text()='Idiom']"; + const string matchComplexFormTypeIdiom = "//span[@class='complexformtypes']/span[@class='complexformtype']/span[@class='name']/span[@lang='en' and text()='Idiom']"; AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchComplexFormTypeIdiom, 1); - const string matchComplexFormName = "//span[@class='visiblecomplexformbackref']/span[@class='headword-1']/span[@lang='fr']/a"; + const string matchComplexFormName = "//span[@class='visiblecomplexformbackref']/span[@class='headword']/span[@lang='fr']/a"; AssertThatXmlIn.String(output).HasSpecifiedNumberOfMatchesForXpath(matchComplexFormName, 4); } @@ -8437,9 +8437,9 @@ public void GenerateContentForEntry_ComplexFormAndSenseInPara() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, DefaultSettings).ToString(); - const string senseXpath = "div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='gloss-1']/span[@lang='en' and text()='gloss']"; + const string senseXpath = "div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='gloss']/span[@lang='en' and text()='gloss']"; var paracontinuationxpath = string.Format( - "div[@class='lexentry-1']//span[@class='subentries-1']/span[@class='subentry']/span[@class='complexformtypes-1']/span[@class='complexformtype']/span[@class='reverseabbr-1']/span[@lang='en' and text()='{0}']", + "div[@class='lexentry']//span[@class='subentries']/span[@class='subentry']/span[@class='complexformtypes']/span[@class='complexformtype']/span[@class='reverseabbr']/span[@lang='en' and text()='{0}']", complexRefRevAbbr); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseXpath, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(paracontinuationxpath, 1); @@ -8496,19 +8496,19 @@ public void GenerateContentForEntry_MinorComplexForm_GeneratesGlossOrSummaryDefi var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(subentry1, minorEntryNode, null, settings).ToString(); - const string complexFormEntryRefXpath = "div[@class='minorentrycomplex-1']/span[@class='complexformentryrefs-1']/span[@class='complexformentryref']"; - const string referencedEntriesXpath = "/span[@class='referencedentries-1']/span[@class='referencedentry']"; - const string glossOrSummXpath1 = complexFormEntryRefXpath + referencedEntriesXpath + "/span[@class='glossorsummary-1']/span[@lang='en' and text()='MainEntrySummaryDefn']"; + const string complexFormEntryRefXpath = "div[@class='minorentrycomplex']/span[@class='complexformentryrefs']/span[@class='complexformentryref']"; + const string referencedEntriesXpath = "/span[@class='referencedentries']/span[@class='referencedentry']"; + const string glossOrSummXpath1 = complexFormEntryRefXpath + referencedEntriesXpath + "/span[@class='glossorsummary']/span[@lang='en' and text()='MainEntrySummaryDefn']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(glossOrSummXpath1, 1); //SUT var result2 = ConfiguredLcmGenerator.GenerateContentForEntry(subentry2, minorEntryNode, null, settings).ToString(); - const string glossOrSummXpath2 = complexFormEntryRefXpath + referencedEntriesXpath + "/span[@class='glossorsummary-1']/span[@lang='en' and text()='gloss2']"; + const string glossOrSummXpath2 = complexFormEntryRefXpath + referencedEntriesXpath + "/span[@class='glossorsummary']/span[@lang='en' and text()='gloss2']"; AssertThatXmlIn.String(result2).HasSpecifiedNumberOfMatchesForXpath(glossOrSummXpath2, 1); //SUT var result3 = ConfiguredLcmGenerator.GenerateContentForEntry(subentry3, minorEntryNode, null, settings).ToString(); - const string glossOrSummXpath3 = complexFormEntryRefXpath + referencedEntriesXpath + "/span[@class='glossorsummary-1']/span[@lang='en' and text()='MainEntryS3Defn']"; + const string glossOrSummXpath3 = complexFormEntryRefXpath + referencedEntriesXpath + "/span[@class='glossorsummary']/span[@lang='en' and text()='MainEntryS3Defn']"; AssertThatXmlIn.String(result3).HasSpecifiedNumberOfMatchesForXpath(glossOrSummXpath3, 1); } @@ -8539,7 +8539,7 @@ public void GenerateContentForEntry_ContinuationParagraphWithEmtpyContentDoesNot var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(lexentry, mainEntryNode, null, settings).ToString(); - const string senseXpath = "div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='gloss-1']/span[@lang='en' and text()='gloss']"; + const string senseXpath = "div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='gloss']/span[@lang='en' and text()='gloss']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(senseXpath, 1); Assert.That(result, Does.Not.Match(@"
"), "Empty Self closing
element should not generated after senses in paragraph"); @@ -8793,7 +8793,7 @@ public void SavePublishedHtmlWithStyles_ExtraEntriesIncludedInLastPage() const string pagesDivXPath = "//div[@class='pages']"; const string pageButtonXPath = "//div[@class='pages']/span[@class='pagebutton']"; const string pageButtonLastIndexPath = "//div[@class='pages']/span[@class='pagebutton' and @endIndex='20']"; - const string entryDivXPath = "//div[@class='entry-1']"; + const string entryDivXPath = "//div[@class='entry']"; try { xhtmlPath = LcmXhtmlGenerator.SavePreviewHtmlWithStyles(hvos, pubEverything, model, m_propertyTable, entriesPerPage: 10); @@ -8842,7 +8842,7 @@ public void SavePublishedHtmlWithStyles_ExtraEntriesMoreThanTenPercentGetOwnPage const string pageButtonXPath = "//div[@class='pages']/span[@class='pagebutton']"; const string firstPageButtonXPath = "//div[@class='pages']/span[@class='pagebutton' and @id='currentPageButton' and @startIndex='0' and @endIndex='7']"; const string lastPageButtonXPath = "//div[@class='pages']/span[@class='pagebutton' and @startIndex='16' and @endIndex='20']"; - const string entryXPath = "//div[@class='entry-1']"; + const string entryXPath = "//div[@class='entry']"; try { xhtmlPath = LcmXhtmlGenerator.SavePreviewHtmlWithStyles(hvos, pubEverything, model, m_propertyTable, entriesPerPage: 8); @@ -9037,13 +9037,13 @@ public void CheckSubsenseOutput() var xhtml = File.ReadAllText(xhtmlPath); //System.Diagnostics.Debug.WriteLine(String.Format("GENERATED XHTML = \r\n{0}\r\n=====================", xhtml)); // SUT - const string allCategsPath1 = "//span[@class='morphosyntaxanalysis-1']/span[@class='mlpartofspeech-1']/span[@lang='en']"; + const string allCategsPath1 = "//span[@class='morphosyntaxanalysis']/span[@class='mlpartofspeech']/span[@lang='en']"; const string allCategsPath2 = "//span[@class='morphosyntaxanalysis-2']/span[@class='mlpartofspeech-2']/span[@lang='en']"; - const string firstCategPath = "/html/body/div[@class='lexentry-1']/span[@class='senses-1']//span[@class='sensecontent']/span[@class='sense']/span[@class='morphosyntaxanalysis-2']/span[@class='mlpartofspeech-2']/span[@lang='en' and text()='n']"; - const string secondCategPath = "/html/body/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sharedgrammaticalinfo']/span[@class='morphosyntaxanalysis-1']/span[@class='mlpartofspeech-1']/span[@lang='en' and text()='n']"; - const string thirdCategPath = "/html/body/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sharedgrammaticalinfo']/span[@class='morphosyntaxanalysis-1']/span[@class='mlpartofspeech-1']/span[@lang='en' and text()='adj']"; - const string fourthCategPath = "/html/body/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense']/span[@class='morphosyntaxanalysis-2']/span[@class='mlpartofspeech-2']/span[@lang='en' and text()='adj']"; - const string fifthCategPath = "/html/body/div[@class='lexentry-1']/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense']/span[@class='morphosyntaxanalysis-2']/span[@class='mlpartofspeech-2']/span[@lang='en' and text()='n']"; + const string firstCategPath = "/html/body/div[@class='lexentry']/span[@class='senses']//span[@class='sensecontent']/span[@class='sense']/span[@class='morphosyntaxanalysis-2']/span[@class='mlpartofspeech-2']/span[@lang='en' and text()='n']"; + const string secondCategPath = "/html/body/div[@class='lexentry']/span[@class='senses']/span[@class='sharedgrammaticalinfo']/span[@class='morphosyntaxanalysis']/span[@class='mlpartofspeech']/span[@lang='en' and text()='n']"; + const string thirdCategPath = "/html/body/div[@class='lexentry']/span[@class='senses']/span[@class='sharedgrammaticalinfo']/span[@class='morphosyntaxanalysis']/span[@class='mlpartofspeech']/span[@lang='en' and text()='adj']"; + const string fourthCategPath = "/html/body/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense']/span[@class='morphosyntaxanalysis-2']/span[@class='mlpartofspeech-2']/span[@lang='en' and text()='adj']"; + const string fifthCategPath = "/html/body/div[@class='lexentry']/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='senses-2']/span[@class='sensecontent']/span[@class='sense']/span[@class='morphosyntaxanalysis-2']/span[@class='mlpartofspeech-2']/span[@lang='en' and text()='n']"; AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(allCategsPath1, 3); AssertThatXmlIn.String(xhtml).HasSpecifiedNumberOfMatchesForXpath(allCategsPath2, 2); @@ -9127,8 +9127,8 @@ public void GenerateContentForEntry_EmbeddedWritingSystemGeneratesCorrectResult( entry.Bibliography.set_String(m_wsFr, multiRunString); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings).ToString(); - const string nestedEn = "/div[@class='lexentry-1']/span[@class='bib-1']/span[@lang='fr']/span[@lang='en']"; - const string nestedFr = "/div[@class='lexentry-1']/span[@class='bib-1']/span[@lang='fr']/span[@lang='fr']"; + const string nestedEn = "/div[@class='lexentry']/span[@class='bib']/span[@lang='fr']/span[@lang='en']"; + const string nestedFr = "/div[@class='lexentry']/span[@class='bib']/span[@lang='fr']/span[@lang='fr']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(nestedEn, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(nestedFr, 2); } @@ -9154,9 +9154,9 @@ public void GenerateContentForEntry_EmbeddedWritingSystemOfOppositeDirectionGene entry.Bibliography.set_String(wsHe, multiRunString); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings).ToString(); - const string nestedEn = "/div[@class='lexentry-1']/span[@class='bib-1']/span[@lang='he']/span[@dir='rtl']/span[@lang='en']/span[@dir='ltr']"; - const string nestedHe = "/div[@class='lexentry-1']/span[@class='bib-1']/span[@lang='he']/span[@dir='rtl']/span[@lang='he']"; - const string extraDirection = "/div[@class='lexentry-1']/span[@class='bib-1']/span[@lang='he']/span[@dir='rtl']/span[@lang='he']/span[@dir='rtl']"; + const string nestedEn = "/div[@class='lexentry']/span[@class='bib']/span[@lang='he']/span[@dir='rtl']/span[@lang='en']/span[@dir='ltr']"; + const string nestedHe = "/div[@class='lexentry']/span[@class='bib']/span[@lang='he']/span[@dir='rtl']/span[@lang='he']"; + const string extraDirection = "/div[@class='lexentry']/span[@class='bib']/span[@lang='he']/span[@dir='rtl']/span[@lang='he']/span[@dir='rtl']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(nestedEn, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(nestedHe, 2); AssertThatXmlIn.String(result).HasNoMatchForXpath(extraDirection); @@ -9184,10 +9184,10 @@ public void GenerateContentForEntry_WritingSystemOfSameDirectionGeneratesNoExtra var settings = new ConfiguredLcmGenerator.GeneratorSettings(Cache, m_propertyTable, false, false, null, true); // Right-to-Left //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings).ToString(); - const string nestedEn = "/div[@class='lexentry-1']/span[@class='bib-1']/span[@lang='he']/span[@lang='en']/span[@dir='ltr']"; - const string nestedHe = "/div[@class='lexentry-1']/span[@class='bib-1']/span[@lang='he']/span[@lang='he']"; - const string extraDirection0 = "/div[@class='lexentry-1']/span[@class='bib-1']/span[@lang='he']/span[@dir='rtl']"; - const string extraDirection1 = "/div[@class='lexentry-1']/span[@class='bib-1']/span[@lang='he']/span[@lang='he']/span[@dir='rtl']"; + const string nestedEn = "/div[@class='lexentry']/span[@class='bib']/span[@lang='he']/span[@lang='en']/span[@dir='ltr']"; + const string nestedHe = "/div[@class='lexentry']/span[@class='bib']/span[@lang='he']/span[@lang='he']"; + const string extraDirection0 = "/div[@class='lexentry']/span[@class='bib']/span[@lang='he']/span[@dir='rtl']"; + const string extraDirection1 = "/div[@class='lexentry']/span[@class='bib']/span[@lang='he']/span[@lang='he']/span[@dir='rtl']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(nestedEn, 1); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(nestedHe, 1); AssertThatXmlIn.String(result).HasNoMatchForXpath(extraDirection0); @@ -9222,28 +9222,28 @@ public void GenerateContentForEntry_EmbeddedHyperlinkGeneratesAnchor() entry.Bibliography.set_String(m_wsFr, stringBldr.GetString()); // SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings).ToString(); - string nestedLink = $"/div[@class='lexentry-1']/span[@class='bib-1']/span/span/a[@href='{testUrl}']"; + string nestedLink = $"/div[@class='lexentry']/span[@class='bib']/span/span/a[@href='{testUrl}']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(nestedLink, 1); } private const string crossRefOwnerTypeXpath = - "//span[@class='minimallexreferences-1']/span[@class='minimallexreference']/span[@class='ownertype_name-1']"; + "//span[@class='minimallexreferences']/span[@class='minimallexreference']/span[@class='ownertype_name']"; private static string CrossRefOwnerTypeXpath(string type) { - return string.Format("//span[@class='minimallexreferences-1']/span[@class='minimallexreference']/span[@class='ownertype_name-1']" + + return string.Format("//span[@class='minimallexreferences']/span[@class='minimallexreference']/span[@class='ownertype_name']" + "/span[@lang='en' and text()='{0}']", type); } private static string HeadwordOrderInCrossRefsXpath(int position, string headword) { - return string.Format("//span[@class='minimallexreferences-1']/span[@class='minimallexreference']/span[@class='configtargets-1']" + + return string.Format("//span[@class='minimallexreferences']/span[@class='minimallexreference']/span[@class='configtargets']" + "/span[@class='configtarget' and position()='{0}']/span/span/a[text()='{1}']", position, headword); } private static string HeadwordWsInCrossRefsXpath(string ws, string headword) // REVIEW (Hasso) 2017.04: move these helpers to Helpers? { - return string.Format("//span[@class='minimallexreferences-1']/span[@class='minimallexreference']/span[@class='configtargets-1']" + + return string.Format("//span[@class='minimallexreferences']/span[@class='minimallexreference']/span[@class='configtargets']" + "/span[@class='configtarget']/span/span[@lang='{0}']/a[text()='{1}']", ws, headword); } @@ -9478,7 +9478,7 @@ public void GenerateContentForEntry_LexicalReferencesOrderedCorrectly([Values(tr FieldDescription = "LexEntry", Children = new List { relationsNode } }; - var xpathLexRef = "//div/span[@class='lexrefs-1']/span[@class='lexref']"; + var xpathLexRef = "//div/span[@class='lexrefs']/span[@class='lexref']"; if (usingSubfield) { // If we are testing subfields, insert 'SensesOS->Entry', which returns the same data, but allows us to make LexRefs a subfield. @@ -9497,18 +9497,18 @@ public void GenerateContentForEntry_LexicalReferencesOrderedCorrectly([Values(tr Children = mainEntryNode.Children }; mainEntryNode.Children = new List { senseNode }; - xpathLexRef = "//div/span[@class='senses-1']/span[@class='sensecontent']/span[@class='sense']/span[@class='lexrefs-1']/span[@class='lexref']"; + xpathLexRef = "//div/span[@class='senses']/span[@class='sensecontent']/span[@class='sense']/span[@class='lexrefs']/span[@class='lexref']"; } CssGeneratorTests.PopulateFieldsForTesting(mainEntryNode); var settings = DefaultSettings; - string antAbbrSpan = $"ant"; - string whSpan = $"wh"; - string ptSpan = $"pt"; - string antNameSpan = $"Antonym"; - string femmeSpan = $"femme"; - var garçonSpan = TsStringUtils.Compose($"garçon"); - var bêteSpan = TsStringUtils.Compose($"bête"); - string trucSpan = $"truc"; + string antAbbrSpan = $"ant"; + string whSpan = $"wh"; + string ptSpan = $"pt"; + string antNameSpan = $"Antonym"; + string femmeSpan = $"femme"; + var garçonSpan = TsStringUtils.Compose($"garçon"); + var bêteSpan = TsStringUtils.Compose($"bête"); + string trucSpan = $"truc"; //SUT //Console.WriteLine(LcmXhtmlGenerator.SavePreviewHtmlWithStyles(new[] { manEntry.Hvo, familyEntry.Hvo, girlEntry.Hvo, individualEntry.Hvo }, null, // new DictionaryConfigurationModel { Parts = new List { mainEntryNode } }, m_mediator)); // full output for diagnostics @@ -10220,8 +10220,8 @@ public void GenerateContentForEntry_GroupingNodeGeneratesSpanAndInnerContentWork //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, DefaultSettings).ToString(); - const string oneSenseWithGlossOfGloss = "/div[@class='lexentry-1']/span[@class='grouping_sensegroup-1']" - + "/span[@class='senses-1']/span[@class='sense']//span[@lang='en' and text()='gloss']"; + const string oneSenseWithGlossOfGloss = "/div[@class='lexentry']/span[@class='grouping_sensegroup']" + + "/span[@class='senses']/span[@class='sense']//span[@lang='en' and text()='gloss']"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(oneSenseWithGlossOfGloss, 1); } diff --git a/Src/xWorks/xWorksTests/CssGeneratorTests.cs b/Src/xWorks/xWorksTests/CssGeneratorTests.cs index 8a1025e471..ed09476fbb 100644 --- a/Src/xWorks/xWorksTests/CssGeneratorTests.cs +++ b/Src/xWorks/xWorksTests/CssGeneratorTests.cs @@ -185,7 +185,7 @@ public void GenerateCssForConfiguration_SharedConfigurationGeneratesValidCss() cssGenerator.AddStyles(subEntryHeadwordNode); var cssResult = cssGenerator.GetStylesString(); // verify that the css result contains a line similar to: .sharedsubentries .sharedsubentry .headword span{ - VerifyRegex(cssResult, @"\.sharedsubentries-.\s\.mainheadword-.>\s*span\s*{.*", + VerifyRegex(cssResult, @"\.sharedsubentries\s\.mainheadword-.>\s*span\s*{.*", "Css for child node(headword) did not generate a match"); } @@ -333,14 +333,14 @@ public void GenerateCssForConfiguration_BeforeAfterGroupingSpanWorks() cssGenerator.AddStyles(headwordNode); var cssResult = cssGenerator.GetStylesString(); // Check the result for before and after rules for the group - Assert.IsTrue(Regex.Match(cssResult, @"\.grouping_hwg-.\s*:before\s*{\s*content\s*:\s*'{';\s*}").Success, + Assert.IsTrue(Regex.Match(cssResult, @"\.grouping_hwg\s*:before\s*{\s*content\s*:\s*'{';\s*}").Success, "css before rule for the grouping node was not generated"); - Assert.IsTrue(Regex.Match(cssResult, @"\.grouping_hwg-.\s*:after\s*{\s*content\s*:\s*'}';\s*}").Success, + Assert.IsTrue(Regex.Match(cssResult, @"\.grouping_hwg\s*:after\s*{\s*content\s*:\s*'}';\s*}").Success, "css after rule for the grouping node was not generated"); // Check result for before and after rules equivalent to .headword span:first-child{content:'Z';} and .headword span:last-child{content:'A'} - Assert.IsTrue(Regex.Match(cssResult, @"\.grouping_hwg-.\s\.mh-.>\s*span\s*:\s*first-child:before\s*{\s*content\s*:\s*'Z';\s*}").Success, + Assert.IsTrue(Regex.Match(cssResult, @"\.grouping_hwg\s\.mh-.>\s*span\s*:\s*first-child:before\s*{\s*content\s*:\s*'Z';\s*}").Success, "css before rule with Z content not found on headword"); - Assert.IsTrue(Regex.Match(cssResult, @"\.grouping_hwg-.\s\.mh-.>\s*span\s*:\s*last-child:after\s*{\s*content\s*:\s*'A';\s*}").Success, + Assert.IsTrue(Regex.Match(cssResult, @"\.grouping_hwg\s\.mh-.>\s*span\s*:\s*last-child:after\s*{\s*content\s*:\s*'A';\s*}").Success, "css after rule with A content not found on headword"); } @@ -439,9 +439,9 @@ public void GenerateCssForConfiguration_BeforeAfterConfigGeneratesBeforeAfterCss cssGenerator.AddStyles(headwordNode); var cssResult = cssGenerator.GetStylesString(); // Check result for before and after rules equivalent to .headword-subentries span:first-child{content:'Z';} and .headword span:last-child{content:'A'} - VerifyRegex(cssResult, @"\.subentries-.\s\.headword-.>\s*span\s*:\s*first-child:before\s*{\s*content\s*:\s*'Z';\s*}", + VerifyRegex(cssResult, @"\.subentries\s\.headword-.>\s*span\s*:\s*first-child:before\s*{\s*content\s*:\s*'Z';\s*}", "css before rule with Z content not found on headword"); - VerifyRegex(cssResult, @"\.subentries-.\s\.headword-.>\s*span\s*:\s*last-child:after\s*{\s*content\s*:\s*'A';\s*}", + VerifyRegex(cssResult, @"\.subentries\s\.headword-.>\s*span\s*:\s*last-child:after\s*{\s*content\s*:\s*'A';\s*}", "css after rule with A content not found on headword"); } @@ -1294,8 +1294,8 @@ public void ClassMappingOverrides_ApplyAtRoot() XHTMLWriter.WriteEndElement(); XHTMLWriter.Flush(); var result = xhtmResult.ToString(); - const string positiveTest = "//*[@class='bolo-1']"; - const string negativeTest = "//*[@class='lexentry-1']"; + const string positiveTest = "//*[@class='bolo']"; + const string negativeTest = "//*[@class='lexentry']"; AssertThatXmlIn.String(result).HasNoMatchForXpath(negativeTest); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(positiveTest, 1); } @@ -1332,8 +1332,8 @@ public void ClassMappingOverrides_ApplyToChildren() Assert.That(cssResult, Contains.Substring(".tailwind")); var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, testParentNode, null, DefaultSettings).ToString(); - const string positiveTest = "//*[@class='tailwind-1']"; - const string negativeTest = "//*[@class='headword-1']"; + const string positiveTest = "//*[@class='tailwind']"; + const string negativeTest = "//*[@class='headword']"; AssertThatXmlIn.String(result).HasNoMatchForXpath(negativeTest); AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(positiveTest, 1); } @@ -1373,7 +1373,7 @@ public void CssAndXhtmlMatchOnSenseCollectionItems() Assert.That(cssResult, Contains.Substring(".gloss")); var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, testEntryNode, null, DefaultSettings).ToString(); - const string positiveTest = "/*[@class='lexentry-1']/span[@class='senses-1']/span[@class='sense']/span[@class='gloss-1']"; + const string positiveTest = "/*[@class='lexentry']/span[@class='senses']/span[@class='sense']/span[@class='gloss']"; AssertThatXmlIn.String(result).HasSpecifiedNumberOfMatchesForXpath(positiveTest, 1); } @@ -1936,7 +1936,7 @@ public void GenerateCssForConfiguration_SenseComplexFormsNotSubEntriesHeadWord() cssGenerator.AddStyles(headwordMain); cssGenerator.AddStyles(form); var cssResult = cssGenerator.GetStylesString(); - VerifyRegex(cssResult, @"\.otherreferencedcomplexforms-.\s.headword-.", "Headword node not generated for non subentry headword"); + VerifyRegex(cssResult, @"\.otherreferencedcomplexforms\s.headword", "Headword node not generated for non subentry headword"); } [Test] @@ -2009,8 +2009,8 @@ public void GenerateCssForConfiguration_SenseShowGramInfoFirstWorks() cssGenerator.AddStyles(senses); cssGenerator.AddStyles(gramInfo); var cssResult = cssGenerator.GetStylesString(); - VerifyRegex(cssResult, @"\.morphosyntaxanalysisra-.", "Style for morphosyntaxanalysisra not generated"); - VerifyRegex(cssResult, @"\.morphosyntaxanalysisra-.\s*{.*font-family\s*:\s*'foofoo'\,serif.*}", + VerifyRegex(cssResult, @"\.morphosyntaxanalysisra", "Style for morphosyntaxanalysisra not generated"); + VerifyRegex(cssResult, @"\.morphosyntaxanalysisra\s*{.*font-family\s*:\s*'foofoo'\,serif.*}", "Style for morphosyntaxanalysisra not placed correctly"); } @@ -3523,7 +3523,7 @@ public void GenerateCssForBulletStyle_TwoStylesWhenSubSensesAreDifferent() cssGenerator.AddStyles(senses); cssGenerator.AddStyles(subsenses); var cssResult = cssGenerator.GetStylesString(); - const string regExPectedForSub = @"\.senses-.\s\.senses-.\s>\s\.sensecontent:before.*{.*content:'\\25A0';.*font-size:14pt;.*color:Green;.*font-family:Arial;.*font-weight:bold;.*font-style:italic;.*background-color:Brown;.*}"; + const string regExPectedForSub = @"\.senses\s\.senses-.\s>\s\.sensecontent:before.*{.*content:'\\25A0';.*font-size:14pt;.*color:Green;.*font-family:Arial;.*font-weight:bold;.*font-style:italic;.*background-color:Brown;.*}"; VerifyRegex(cssResult, regExPectedForSub, "Bulleted style for SubSenses not generated."); const string regExPectedForSense = @"\.senses-.\s>\s\.sensecontent"; // Make sure there is a .sense > .sensecontent rule as well as the bulletted sub-sense VerifyRegex(cssResult, regExPectedForSense, "Non-bulleted style for Senses not generated."); diff --git a/Src/xWorks/xWorksTests/LcmJsonGeneratorTests.cs b/Src/xWorks/xWorksTests/LcmJsonGeneratorTests.cs index c276f3bc5c..48875bfcac 100644 --- a/Src/xWorks/xWorksTests/LcmJsonGeneratorTests.cs +++ b/Src/xWorks/xWorksTests/LcmJsonGeneratorTests.cs @@ -158,8 +158,8 @@ public void GenerateJsonForEntry_OneSenseWithGlossGeneratesCorrectResult() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings, 0).ToString(); Console.WriteLine(result); - var expectedResult = @"{""xhtmlTemplate"": ""lexentry-1"",""guid"":""g" + entry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, - ""senses-1"": [{""guid"":""g" + entry.Guid + @""",""gloss-1"": [{""lang"":""en"",""value"":""gloss""}]},]}"; + var expectedResult = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + entry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, + ""senses"": [{""guid"":""g" + entry.Guid + @""",""gloss"": [{""lang"":""en"",""value"":""gloss""}]},]}"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry var expected = (JObject)JsonConvert.DeserializeObject(expectedResult); VerifyJson(result, expected); @@ -191,8 +191,8 @@ public void GenerateJsonForEntry_DefinitionOrGloss_HandlePerWS() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings, 0).ToString(); - var expectedResults = @"{""xhtmlTemplate"": ""lexentry-1"",""guid"":""g" + entry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, - ""senses-1"": [{""guid"":""g" + entry.Guid + @""", ""definitionorgloss-1"": [{""lang"":""en"",""value"":""gloss""}, + var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + entry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, + ""senses"": [{""guid"":""g" + entry.Guid + @""", ""definitionorgloss"": [{""lang"":""en"",""value"":""gloss""}, {""lang"":""es"",""value"":""definition""}]}]}"; //This assert is dependent on the specific entry data created in CreateInterestingLexEntry var expected = (JObject)JsonConvert.DeserializeObject(expectedResults); @@ -257,9 +257,9 @@ public void GenerateJsonForEntry_TwoSensesWithSameInfoShowGramInfoFirst_Json() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings, 0).ToString(); Console.WriteLine(result); - var expectedResults = @"{""xhtmlTemplate"": ""lexentry-1"",""guid"":""g" + entry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, - ""msas-1"": {""mlpartofspeech-1"": [{""lang"":""en"",""value"":""Blah""}]}, ""senses-1"": [{""guid"":""g" + entry.Guid + @""",""gloss-1"": [{""lang"":""en"",""value"":""gloss""}]}, - {""guid"":""g" + entry.Guid + @""",""gloss-1"": [{""lang"":""en"",""value"":""second sense""}]}]}"; + var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + entry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, + ""msas"": {""mlpartofspeech"": [{""lang"":""en"",""value"":""Blah""}]}, ""senses"": [{""guid"":""g" + entry.Guid + @""",""gloss"": [{""lang"":""en"",""value"":""gloss""}]}, + {""guid"":""g" + entry.Guid + @""",""gloss"": [{""lang"":""en"",""value"":""second sense""}]}]}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(result, expected); } @@ -297,9 +297,9 @@ public void GenerateJsonForEntry_OneSenseWithSinglePicture() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, settings, 0).ToString(); - var expectedResults = @"{""xhtmlTemplate"": ""lexentry-1"",""guid"":""g" + entry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, - ""pictures-1"": [{""guid"":""g" + sensePic.Guid + @""",""src"":""pictures/test_auth_copy_license.jpg"", - ""sensenumber-1"": [{""lang"":""en"",""value"":""1""}],""caption-1"": [{""lang"":""en"",""value"":""caption""}]}]}"; + var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + entry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, + ""pictures"": [{""guid"":""g" + sensePic.Guid + @""",""src"":""pictures/test_auth_copy_license.jpg"", + ""sensenumber"": [{""lang"":""en"",""value"":""1""}],""caption"": [{""lang"":""en"",""value"":""caption""}]}]}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(result, expected); } @@ -512,12 +512,12 @@ public void GenerateJsonForEntry_FilterByPublication() //SUT var output = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNode, pubMain, DefaultSettings, 0).ToString(); Assert.That(output, Is.Not.Null.Or.Empty); - var expectedResults = "{\"xhtmlTemplate\": \"lexentry-1\",\"guid\":\"g" + entryEntry.Guid + "\",\"letterHead\": \"e\",\"sortIndex\": 0," + - "\"entry-1\": [{\"lang\":\"fr\",\"value\":\"entry\"}],\"senses-1\": [{\"guid\":\"g" + - entryEntry.Guid + "\",\"definitionorgloss-1\": [{\"lang\":\"en\",\"value\":\"entry\"}]}]," + - "\"subentries-1\": [{\"subentry-1\": [{\"lang\":\"fr\",\"value\":\"mainsubentry\"}]}]," + - "\"variantformentrybackrefs-1\": [{\"referenceType\":\"{\\\"name-1\\\": [{\\\"lang\\\":\\\"en\\\"," + - "\\\"value\\\":\\\"Spelling Variant\\\"}],},\",\"references\":[{\"headword-1\": [{\"lang\":\"fr\",\"guid\": \"g" + + var expectedResults = "{\"xhtmlTemplate\": \"lexentry\",\"guid\":\"g" + entryEntry.Guid + "\",\"letterHead\": \"e\",\"sortIndex\": 0," + + "\"entry\": [{\"lang\":\"fr\",\"value\":\"entry\"}],\"senses\": [{\"guid\":\"g" + + entryEntry.Guid + "\",\"definitionorgloss\": [{\"lang\":\"en\",\"value\":\"entry\"}]}]," + + "\"subentries\": [{\"subentry\": [{\"lang\":\"fr\",\"value\":\"mainsubentry\"}]}]," + + "\"variantformentrybackrefs\": [{\"referenceType\":\"{\\\"name\\\": [{\\\"lang\\\":\\\"en\\\"," + + "\\\"value\\\":\\\"Spelling Variant\\\"}],},\",\"references\":[{\"headword\": [{\"lang\":\"fr\",\"guid\": \"g" + bizarroVariant.Guid + "\", \"value\":\"bizarre\"}]}]}]}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(output, expected); @@ -582,11 +582,11 @@ public void GenerateJsonForEntry_TypeAfterForm() //SUT var outputTypeAfter = ConfiguredLcmGenerator.GenerateContentForEntry(entryEntry, mainEntryNodeTypeAfter, pubMain, DefaultSettings, 0).ToString(); Assert.That(outputTypeAfter, Is.Not.Null.Or.Empty); - var expectedResultsTypeAfter = "{\"xhtmlTemplate\": \"lexentry-1\",\"guid\":\"g" + entryEntry.Guid + "\",\"letterHead\": \"e\",\"sortIndex\": 0," + - "\"entry-1\": [{\"lang\":\"fr\",\"value\":\"entry\"}]," + - "\"variantformentrybackrefs-1\": [{\"references\":[{\"headword-1\": [{\"lang\":\"fr\",\"guid\": \"g" + + var expectedResultsTypeAfter = "{\"xhtmlTemplate\": \"lexentry\",\"guid\":\"g" + entryEntry.Guid + "\",\"letterHead\": \"e\",\"sortIndex\": 0," + + "\"entry\": [{\"lang\":\"fr\",\"value\":\"entry\"}]," + + "\"variantformentrybackrefs\": [{\"references\":[{\"headword\": [{\"lang\":\"fr\",\"guid\": \"g" + bizarroVariant.Guid + "\", \"value\":\"bizarre\"}]}]," + - "\"referenceType\":\"{\\\"name-1\\\": [{\\\"lang\\\":\\\"en\\\",\\\"value\\\":\\\"Spelling Variant\\\"}],},\"}]}"; + "\"referenceType\":\"{\\\"name\\\": [{\\\"lang\\\":\\\"en\\\",\\\"value\\\":\\\"Spelling Variant\\\"}],},\"}]}"; var expectedTypeAfter = (JObject)JsonConvert.DeserializeObject(expectedResultsTypeAfter, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(outputTypeAfter, expectedTypeAfter); } @@ -617,8 +617,8 @@ public void GenerateJsonForEntry_WsAudiowithHyperlink() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, DefaultSettings, 0).ToString(); - var expectedResults = @"{""xhtmlTemplate"": ""lexentry-1"",""guid"": ""g" + entryOne.Guid + - @""",""letterHead"": ""c"",""sortIndex"": 0, ""headword-1"": [{""guid"": ""g" + entryOne.Guid + @""", ""lang"":""en-Zxxx-x-audio"", ""value"": {""id"": ""gTest_Audi_o"", ""src"": ""AudioVisual/Test Audi'o.wav""}}]}"; + var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"": ""g" + entryOne.Guid + + @""",""letterHead"": ""c"",""sortIndex"": 0, ""headword"": [{""guid"": ""g" + entryOne.Guid + @""", ""lang"":""en-Zxxx-x-audio"", ""value"": {""id"": ""gTest_Audi_o"", ""src"": ""AudioVisual/Test Audi'o.wav""}}]}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(result, expected); } @@ -674,9 +674,9 @@ public void GenerateJsonForEntry_SensibleJsonForVideoFiles() //SUT var output = ConfiguredLcmGenerator.GenerateContentForEntry(entryCorps, mainEntryNode, DefaultDecorator, DefaultSettings, 0).ToString(); Assert.That(output, Is.Not.Null.Or.Empty); - var expectedResults = "{\"xhtmlTemplate\":\"lexentry-1\",\"guid\":\"g" + entryCorps.Guid + "\",\"letterHead\":\"c\",\"sortIndex\":0," + - "\"entry-1\": [{\"lang\":\"fr\",\"value\":\"corps\"}]," + - "\"pronunciations-1\": [{\"mediafiles-1\": [{\"value\": {\"id\":\"g" + mainMediaFile.Guid + "\",\"src\": \"AudioVisual/fileName.mp4\"}}]}]}"; + var expectedResults = "{\"xhtmlTemplate\":\"lexentry\",\"guid\":\"g" + entryCorps.Guid + "\",\"letterHead\":\"c\",\"sortIndex\":0," + + "\"entry\": [{\"lang\":\"fr\",\"value\":\"corps\"}]," + + "\"pronunciations\": [{\"mediafiles\": [{\"value\": {\"id\":\"g" + mainMediaFile.Guid + "\",\"src\": \"AudioVisual/fileName.mp4\"}}]}]}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(output, expected); } @@ -703,9 +703,9 @@ public void GenerateJsonForEntry_SenseNumbersGeneratedForMultipleSenses() ConfiguredXHTMLGeneratorTests.AddSenseToEntry(testEntry, "second gloss", m_wsEn, Cache); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, DefaultDecorator, DefaultSettings, 0).ToString(); - var expectedResults = @"{""xhtmlTemplate"": ""lexentry-1"",""guid"":""g" + testEntry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0,""senses-1"":[{""senseNumber"":""1"", - ""guid"":""g" + testEntry.Guid + @""",""gloss-1"":[{""lang"":""en"",""value"":""gloss""}]}, - {""senseNumber"":""2"",""guid"":""g" + testEntry.Guid + @""",""gloss-1"":[{""lang"":""en"",""value"":""second gloss""}]}]}"; + var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + testEntry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0,""senses"":[{""senseNumber"":""1"", + ""guid"":""g" + testEntry.Guid + @""",""gloss"":[{""lang"":""en"",""value"":""gloss""}]}, + {""senseNumber"":""2"",""guid"":""g" + testEntry.Guid + @""",""gloss"":[{""lang"":""en"",""value"":""second gloss""}]}]}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(result, expected); } @@ -732,7 +732,7 @@ public void GenerateJsonForEntry_EmbeddedWritingSystemGeneratesCorrectResult() entry.Bibliography.set_String(m_wsFr, multiRunString); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings, 0).ToString(); - var expectedResults = @"{""xhtmlTemplate"": ""lexentry-1"",""guid"":""g" + entry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0,""bib-1"": [{""lang"":""fr"",""value"":""French with ""}, + var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + entry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0,""bib"": [{""lang"":""fr"",""value"":""French with ""}, {""lang"":""en"",""value"":""English""},{""lang"":""fr"",""value"":"" embedded""}]}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(result, expected); @@ -758,8 +758,8 @@ public void GenerateJsonForEntry_UnicodeLineBreak_GeneratesValidJson() entry.Bibliography.set_String(m_wsFr, englishStr); //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings, 0).ToString(); - var expectedResults = @"{""xhtmlTemplate"": ""lexentry-1"",""guid"":""g" + entry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, - ""bib-1"": [{""lang"":""en"",""value"":""English\nwith line break""}]}"; + var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + entry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, + ""bib"": [{""lang"":""en"",""value"":""English\nwith line break""}]}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(result, expected); } @@ -805,8 +805,8 @@ public void GenerateJsonForEntry_GeneratesForwardNameForForwardLexicalRelations( //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, DefaultSettings, 0).ToString(); - var expectedResults = @"{""xhtmlTemplate"": ""lexentry-1"",""guid"":""g" + mainEntry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, - ""sensesos-1"": [{""lexsensereferences-1"": [{""ownertype_name-1"": [{""lang"":""en"",""value"":""TestRefType""}]}]}]}"; + var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + mainEntry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, + ""sensesos"": [{""lexsensereferences"": [{""ownertype_name"": [{""lang"":""en"",""value"":""TestRefType""}]}]}]}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(result, expected); } @@ -848,8 +848,8 @@ public void GenerateJsonForEntry_EmptyNameOnLexicalRelation_GeneratesEmptyButVal //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(mainEntry, mainEntryNode, null, DefaultSettings, 0).ToString(); - var expectedResults = @"{""xhtmlTemplate"": ""lexentry-1"",""guid"":""g" + mainEntry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, - ""sensesos-1"": [{""lexsensereferences-1"": [{}]}]}"; + var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + mainEntry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, + ""sensesos"": [{""lexsensereferences"": [{}]}]}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(result, expected); } @@ -875,13 +875,13 @@ public void GenerateJsonForEntry_HomographNumbersGeneratesCorrectResult() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, DefaultSettings, 0).ToString(); - var expectedResults = @"{""xhtmlTemplate"": ""lexentry-1"",""guid"":""g" + entryOne.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0,""homographnumber-1"": ""1"", - ""citationform-1"": [{""lang"":""fr"",""value"":""Citation""}]}"; + var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + entryOne.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0,""homographnumber"": ""1"", + ""citationform"": [{""lang"":""fr"",""value"":""Citation""}]}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(result, expected); result = ConfiguredLcmGenerator.GenerateContentForEntry(entryTwo, mainEntryNode, null, DefaultSettings, 0).ToString(); - expectedResults = @"{""xhtmlTemplate"": ""lexentry-1"",""guid"":""g" + entryTwo.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0,""homographnumber-1"": ""2"", - ""citationform-1"": [{""lang"":""fr"",""value"":""Citation""}]}"; + expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + entryTwo.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0,""homographnumber"": ""2"", + ""citationform"": [{""lang"":""fr"",""value"":""Citation""}]}"; expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(result, expected); @@ -906,14 +906,14 @@ public void GenerateJsonForEntry_GeneratesSpecifiedSortIndex() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, DefaultSettings, 36).ToString(); - var expectedResults = @"{""xhtmlTemplate"": ""lexentry-1"",""guid"":""g" + entryOne.Guid + @""",""letterHead"": ""c"",""sortIndex"": 36, - ""citationform-1"": [{""lang"":""fr"",""value"":""Citation""}]}"; + var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + entryOne.Guid + @""",""letterHead"": ""c"",""sortIndex"": 36, + ""citationform"": [{""lang"":""fr"",""value"":""Citation""}]}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(result, expected); // default value of -1 result = ConfiguredLcmGenerator.GenerateContentForEntry(entryOne, mainEntryNode, null, DefaultSettings).ToString(); - expectedResults = @"{""xhtmlTemplate"": ""lexentry-1"",""guid"":""g" + entryOne.Guid + @""",""letterHead"": ""c"",""sortIndex"": -1, - ""citationform-1"": [{""lang"":""fr"",""value"":""Citation""}]}"; + expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + entryOne.Guid + @""",""letterHead"": ""c"",""sortIndex"": -1, + ""citationform"": [{""lang"":""fr"",""value"":""Citation""}]}"; expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(result, expected); @@ -961,8 +961,8 @@ public void GenerateJsonForEntry_TwoDifferentPicturesGetUniqueWebFriendlyPaths() var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, mainEntryNode, null, DefaultSettings, 0).ToString(); // Bug: The second filename should be different after the export with relative path settings (fix later) - var expectedResults = @"{""xhtmlTemplate"": ""lexentry-1"",""guid"":""g" + testEntry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, - ""pictures-1"": [{""guid"":""g" + sensePic.Guid + @""",""src"":""pictures/" + fileName + @"""}, + var expectedResults = @"{""xhtmlTemplate"": ""lexentry"",""guid"":""g" + testEntry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, + ""pictures"": [{""guid"":""g" + sensePic.Guid + @""",""src"":""pictures/" + fileName + @"""}, {""guid"":""g" + sensePic2.Guid + @""",""src"":""pictures/" + fileName + @"""}],}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(result, expected); @@ -1025,22 +1025,22 @@ public void GenerateJsonForEntry_MinorComplexForm_TemplateTypeCorrect_GeneratesG ; //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(subentry1, minorEntryNode, null, DefaultSettings, 0).ToString(); - var expectedResults = @"{""xhtmlTemplate"":""minorentrycomplex-1"",""guid"":""g" + subentry1.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, - ""complexformentryrefs-1"": [{""referencedentries-1"": [{""glossorsummary-1"": [{""lang"":""en"",""value"":""MainEntrySummaryDefn""}]}]}]}"; + var expectedResults = @"{""xhtmlTemplate"":""minorentrycomplex"",""guid"":""g" + subentry1.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, + ""complexformentryrefs"": [{""referencedentries"": [{""glossorsummary"": [{""lang"":""en"",""value"":""MainEntrySummaryDefn""}]}]}]}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(result, expected); //SUT var result2 = ConfiguredLcmGenerator.GenerateContentForEntry(subentry2, minorEntryNode, null, DefaultSettings, 0).ToString(); - expectedResults = @"{""xhtmlTemplate"":""minorentrycomplex-1"",""guid"":""g" + subentry2.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, - ""complexformentryrefs-1"": [{""referencedentries-1"": [{""glossorsummary-1"": [{""lang"":""en"",""value"":""gloss2""}]}]}]}"; + expectedResults = @"{""xhtmlTemplate"":""minorentrycomplex"",""guid"":""g" + subentry2.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, + ""complexformentryrefs"": [{""referencedentries"": [{""glossorsummary"": [{""lang"":""en"",""value"":""gloss2""}]}]}]}"; expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(result2, expected); //SUT var result3 = ConfiguredLcmGenerator.GenerateContentForEntry(subentry3, minorEntryNode, null, DefaultSettings, 0).ToString(); - expectedResults = @"{""xhtmlTemplate"": ""minorentrycomplex-1"",""guid"":""g" + subentry3.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, - ""complexformentryrefs-1"": [{""referencedentries-1"": [{""glossorsummary-1"": [{""lang"":""en"",""value"":""MainEntryS3Defn""}]}]}]}"; + expectedResults = @"{""xhtmlTemplate"": ""minorentrycomplex"",""guid"":""g" + subentry3.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, + ""complexformentryrefs"": [{""referencedentries"": [{""glossorsummary"": [{""lang"":""en"",""value"":""MainEntryS3Defn""}]}]}]}"; expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(result3, expected); } @@ -1103,11 +1103,11 @@ public void SavePublishedJsonWithStyles_DisplayXhtmlPopulated() DefaultDecorator, 1, new DictionaryConfigurationModel { Parts = new List { mainEntryNode } }, m_propertyTable, "test.json", null, out int[] _); - var expectedResults = @"{""xhtmlTemplate"":""lexentry-1"",""guid"":""g" + testEntry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, - ""homographnumber-1"":""0"",""citationform-1"":[{""lang"":""fr"",""value"":""Citation""}], - ""displayXhtml"":""
0Citation
""}"; + var expectedResults = @"{""xhtmlTemplate"":""lexentry"",""guid"":""g" + testEntry.Guid + @""",""letterHead"": ""c"",""sortIndex"": 0, + ""homographnumber"":""0"",""citationform"":[{""lang"":""fr"",""value"":""Citation""}], + ""displayXhtml"":""
0Citation
""}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(results[0][0].ToString(Formatting.None), expected); } @@ -1203,8 +1203,8 @@ public void GenerateXHTMLForEntry_EmbeddedWritingSystemOfOppositeDirectionGenera //SUT var json = ConfiguredLcmGenerator.GenerateContentForEntry(entry, mainEntryNode, null, DefaultSettings).ToString(); - var expectedResults = @"{""xhtmlTemplate"":""lexentry-1"",""guid"":""g" + entry.Guid + @""",""letterHead"":""c"",""sortIndex"":-1, - ""bib-1"": [{""lang"":""he"",""value"":""דוד""},{""lang"":""en"",""value"":"" et ""},{""lang"":""he"",""value"":""דניאל""}],}"; + var expectedResults = @"{""xhtmlTemplate"":""lexentry"",""guid"":""g" + entry.Guid + @""",""letterHead"":""c"",""sortIndex"":-1, + ""bib"": [{""lang"":""he"",""value"":""דוד""},{""lang"":""en"",""value"":"" et ""},{""lang"":""he"",""value"":""דניאל""}],}"; var expected = (JObject)JsonConvert.DeserializeObject(expectedResults, new JsonSerializerSettings { Formatting = Formatting.None }); VerifyJson(json, expected); } @@ -1233,7 +1233,7 @@ public void GenerateXHTMLForEntry_MultiLineCustomFieldGeneratesContent() //SUT var result = ConfiguredLcmGenerator.GenerateContentForEntry(testEntry, rootNode, null, DefaultSettings, 0).ToString(); - var expectedResults = @"{""xhtmlTemplate"":""lexentry-1"", + var expectedResults = @"{""xhtmlTemplate"":""lexentry"", ""guid"":""g" + testEntry.Guid + @""", ""letterHead"":""c"", ""sortIndex"":0, From 940f84cf3d9888d7abdc401860b05b9d59b7b3b6 Mon Sep 17 00:00:00 2001 From: John T Maxwell III Date: Thu, 27 Mar 2025 13:11:22 -0700 Subject: [PATCH 046/123] Fix LT-22089: Creating and undoing a text leads to confusion (#301) --- Src/xWorks/RecordClerk.cs | 2 +- Src/xWorks/RecordList.cs | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Src/xWorks/RecordClerk.cs b/Src/xWorks/RecordClerk.cs index 21fed93873..b3588d4774 100644 --- a/Src/xWorks/RecordClerk.cs +++ b/Src/xWorks/RecordClerk.cs @@ -950,7 +950,7 @@ public void ViewChangedSelectedRecord(FwObjectSelectionEventArgs e) // jumps to the first instance of that object (LT-4691). // Through deletion of Reversal Index entry it was possible to arrive here with // no sorted objects. (LT-13391) - if (e.Index >= 0 && m_list.SortedObjects.Count > 0) + if (0 <= e.Index && e.Index < m_list.SortedObjects.Count) { int ourHvo = m_list.SortItemAt(e.Index).RootObjectHvo; // if for some reason the index doesn't match the hvo, we'll jump to the Hvo. diff --git a/Src/xWorks/RecordList.cs b/Src/xWorks/RecordList.cs index c423c64f87..0df0622be8 100644 --- a/Src/xWorks/RecordList.cs +++ b/Src/xWorks/RecordList.cs @@ -1704,6 +1704,11 @@ private bool TryReloadForInvalidPathObjectsOnCurrentObject(int tag, int cvDel) // we want to wait until after everything is finished reloading, however. m_requestedLoadWhileSuppressed = true; } + if (Clerk.Id == "interlinearTexts") + { + // Wait for InterestingTextsList to be updated (cf. LT-22089). + m_requestedLoadWhileSuppressed = true; + } // Try to catch things that don't obviously affect us, but will cause problems. if (cvDel > 0 && CurrentIndex >= 0 && IsPropOwning(tag)) From b34051e392d699c2d690b066cd5fd8ed00dc7c42 Mon Sep 17 00:00:00 2001 From: John T Maxwell III Date: Mon, 31 Mar 2025 16:51:14 -0700 Subject: [PATCH 047/123] Fix LT-22090: Green box crash in IsLexicalPattern (#303) --- Src/LexText/Interlinear/SandboxBase.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Src/LexText/Interlinear/SandboxBase.cs b/Src/LexText/Interlinear/SandboxBase.cs index ee518eb94d..fdb99a3a9e 100644 --- a/Src/LexText/Interlinear/SandboxBase.cs +++ b/Src/LexText/Interlinear/SandboxBase.cs @@ -1481,9 +1481,12 @@ public static bool IsLexicalPattern(IMultiUnicode multiString) for (var i = 0; i < multiString.StringCount; i++) { int ws; - string text = multiString.GetStringFromIndex(i, out ws).Text; - if (text.Contains("[") && text.Contains("]")) - return true; + ITsString tssString = multiString.GetStringFromIndex(i, out ws); + if (tssString != null && tssString.Text != null) + { + if (tssString.Text.Contains("[") && tssString.Text.Contains("]")) + return true; + } } return false; } From a5804be64a266213558d576271f95e2328d487bd Mon Sep 17 00:00:00 2001 From: Jake Oliver Date: Tue, 1 Apr 2025 10:13:10 -0400 Subject: [PATCH 048/123] LT-21461: Prevent crash on double-clicking abstract entry (#304) --- Src/LexText/LexTextControls/BaseGoDlg.cs | 2 +- Src/LexText/LexTextControls/LinkAllomorphDlg.cs | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/Src/LexText/LexTextControls/BaseGoDlg.cs b/Src/LexText/LexTextControls/BaseGoDlg.cs index ef97519454..70fb7ebc66 100644 --- a/Src/LexText/LexTextControls/BaseGoDlg.cs +++ b/Src/LexText/LexTextControls/BaseGoDlg.cs @@ -853,7 +853,7 @@ private void m_matchingObjects_SelectionChanged(object sender, FwObjectSelection HandleMatchingSelectionChanged(e); } - private void m_matchingObjectsBrowser_SelectionMade(object sender, FwObjectSelectionEventArgs e) + protected virtual void m_matchingObjectsBrowser_SelectionMade(object sender, FwObjectSelectionEventArgs e) { DialogResult = DialogResult.OK; Close(); diff --git a/Src/LexText/LexTextControls/LinkAllomorphDlg.cs b/Src/LexText/LexTextControls/LinkAllomorphDlg.cs index cf9f87eade..18ac0210c8 100644 --- a/Src/LexText/LexTextControls/LinkAllomorphDlg.cs +++ b/Src/LexText/LexTextControls/LinkAllomorphDlg.cs @@ -7,6 +7,7 @@ using SIL.FieldWorks.Common.Widgets; using SIL.LCModel; using XCore; +using SIL.FieldWorks.Common.FwUtils; namespace SIL.FieldWorks.LexText.Controls { @@ -151,11 +152,12 @@ public override void SetDlgInfo(LcmCache cache, WindowParams wp, Mediator mediat protected override void HandleMatchingSelectionChanged() { - m_fwcbAllomorphs.Items.Clear(); - m_fwcbAllomorphs.Text = String.Empty; if (m_selObject == null) return; + m_fwcbAllomorphs.SuspendLayout(); + m_fwcbAllomorphs.Items.Clear(); + m_fwcbAllomorphs.Text = String.Empty; /* NB: We remove abstract MoForms, because the adhoc allo coprohibiton object wants them removed. * If any other client of this dlg comes along that wants them, * we will need to feed in a parameter that tells us whether to exclude them or not. @@ -172,6 +174,8 @@ protected override void HandleMatchingSelectionChanged() } if (m_fwcbAllomorphs.Items.Count > 0) m_fwcbAllomorphs.SelectedItem = m_fwcbAllomorphs.Items[0]; + else + m_selObject = null; m_btnOK.Enabled = m_fwcbAllomorphs.Items.Count > 0; m_fwcbAllomorphs.ResumeLayout(); // For a resizeable dialog, we don't want AdjustForStylesheet to really change its size, @@ -181,6 +185,14 @@ protected override void HandleMatchingSelectionChanged() Height = oldHeight; } + protected override void m_matchingObjectsBrowser_SelectionMade(object sender, FwObjectSelectionEventArgs e) + { + if (m_fwcbAllomorphs.Items.Count == 0) + return; + + base.m_matchingObjectsBrowser_SelectionMade(sender, e); + } + #endregion Other methods #region Designer generated code From d92780c6ff393eecbb701e6d6684d5d6bf092a16 Mon Sep 17 00:00:00 2001 From: Jake Oliver Date: Tue, 1 Apr 2025 10:29:53 -0400 Subject: [PATCH 049/123] Refactor get_MultiStringAlt in DictionaryPublicationDecorator.cs (#305) --- Src/xWorks/DictionaryPublicationDecorator.cs | 81 ++++++++------------ 1 file changed, 31 insertions(+), 50 deletions(-) diff --git a/Src/xWorks/DictionaryPublicationDecorator.cs b/Src/xWorks/DictionaryPublicationDecorator.cs index 06b843b311..bb70f0c80a 100644 --- a/Src/xWorks/DictionaryPublicationDecorator.cs +++ b/Src/xWorks/DictionaryPublicationDecorator.cs @@ -255,51 +255,32 @@ public override ITsMultiString get_MultiStringProp(int hvo, int tag) public override ITsString get_MultiStringAlt(int hvo, int tag, int ws) { - if (tag == m_mlHeadwordFlid) - { - int hn; - if (m_homographNumbers.TryGetValue(hvo, out hn)) - { - var entry = m_entryRepo.GetObject(hvo); - return StringServices.HeadWordForWsAndHn(entry, ws, hn, "", - HomographConfiguration.HeadwordVariant.Main); - } - // In case it's one we somehow don't know about, we'll let the base method try to get the real HN. - } - else if (tag == m_headwordRefFlid) - { - int hn; - if (m_homographNumbers.TryGetValue(hvo, out hn)) - { - var entry = m_entryRepo.GetObject(hvo); - return StringServices.HeadWordForWsAndHn(entry, ws, hn, "", - HomographConfiguration.HeadwordVariant.DictionaryCrossRef); - } - // In case it's one we somehow don't know about, we'll let the base method try to get the real HN. - } - else if (tag == m_headwordReversalFlid) + bool handleHeadwordTags = tag == m_mlHeadwordFlid || tag == m_headwordRefFlid || tag == m_headwordReversalFlid; + bool handleSenseTags = tag == m_mlOwnerOutlineFlid || tag == m_reversalNameFlid; + + if (handleHeadwordTags) { int hn; + var headwordVariant = tag == m_mlHeadwordFlid ? HomographConfiguration.HeadwordVariant.Main : + tag == m_headwordRefFlid ? HomographConfiguration.HeadwordVariant.DictionaryCrossRef : + HomographConfiguration.HeadwordVariant.ReversalCrossRef; if (m_homographNumbers.TryGetValue(hvo, out hn)) { var entry = m_entryRepo.GetObject(hvo); - return StringServices.HeadWordForWsAndHn(entry, ws, hn, "", - HomographConfiguration.HeadwordVariant.ReversalCrossRef); + return StringServices.HeadWordForWsAndHn(entry, ws, hn, "", headwordVariant); } - // In case it's one we somehow don't know about, we'll let the base method try to get the real HN. - } - else if (tag == m_mlOwnerOutlineFlid) - { - // This adapts the logic of LexSense.OwnerOutlineNameForWs - var sense = m_senseRepo.GetObject(hvo); - return OwnerOutlineNameForWs(sense, ws, HomographConfiguration.HeadwordVariant.DictionaryCrossRef); } - else if (tag == m_reversalNameFlid) + else if (handleSenseTags) { + var headwordVariant = tag == m_mlOwnerOutlineFlid ? HomographConfiguration.HeadwordVariant.DictionaryCrossRef : + HomographConfiguration.HeadwordVariant.ReversalCrossRef; + // This adapts the logic of LexSense.OwnerOutlineNameForWs var sense = m_senseRepo.GetObject(hvo); - return OwnerOutlineNameForWs(sense, ws, HomographConfiguration.HeadwordVariant.ReversalCrossRef); + return OwnerOutlineNameForWs(sense, ws, headwordVariant); } + + // In case it's one we somehow don't know about, we'll let the base method try to get the real HN. return base.get_MultiStringAlt(hvo, tag, ws); } @@ -388,26 +369,26 @@ private void BuildExcludedObjects() if (Publication != null) { foreach (var obj in Publication.ReferringObjects) - { - var entry = obj as ILexEntry; + { + var entry = obj as ILexEntry; if (entry == null || entry.DoNotPublishInRC.Contains(Publication)) - { - m_excludedItems.Add(obj.Hvo); - if (obj is ILexEntry) - foreach (var sense in ((ILexEntry)obj).SensesOS) - ExcludeSense(sense); - if (obj is ILexSense) - ExcludeSense((ILexSense)obj); - } - else - { - // It's an entry, and the only other option is that it refers in DoNotShowAsMainEntry - Debug.Assert(entry.DoNotShowMainEntryInRC.Contains(Publication)); - m_excludeAsMainEntry.Add(entry.Hvo); - } + { + m_excludedItems.Add(obj.Hvo); + if (obj is ILexEntry) + foreach (var sense in ((ILexEntry)obj).SensesOS) + ExcludeSense(sense); + if (obj is ILexSense) + ExcludeSense((ILexSense)obj); + } + else + { + // It's an entry, and the only other option is that it refers in DoNotShowAsMainEntry + Debug.Assert(entry.DoNotShowMainEntryInRC.Contains(Publication)); + m_excludeAsMainEntry.Add(entry.Hvo); } } } + } private void ExcludeSense(ILexSense sense) { From 95a60609db814a9dc859f624b4dc411dfedbae6c Mon Sep 17 00:00:00 2001 From: John T Maxwell III Date: Tue, 1 Apr 2025 10:29:37 -0700 Subject: [PATCH 050/123] Fix LT-22092: Text & Words area is not functioning properly (#306) --- Src/xWorks/RecordList.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/xWorks/RecordList.cs b/Src/xWorks/RecordList.cs index 0df0622be8..91e5626c00 100644 --- a/Src/xWorks/RecordList.cs +++ b/Src/xWorks/RecordList.cs @@ -1704,7 +1704,7 @@ private bool TryReloadForInvalidPathObjectsOnCurrentObject(int tag, int cvDel) // we want to wait until after everything is finished reloading, however. m_requestedLoadWhileSuppressed = true; } - if (Clerk.Id == "interlinearTexts") + if (Clerk.Id == "interlinearTexts" && cvDel > 0) { // Wait for InterestingTextsList to be updated (cf. LT-22089). m_requestedLoadWhileSuppressed = true; From 9d3e93e02819984aa25e5e22edb0ff349cc3d50d Mon Sep 17 00:00:00 2001 From: Jake Oliver Date: Wed, 26 Mar 2025 16:09:53 -0400 Subject: [PATCH 051/123] LT-21817: Properly add to complex feature tree in complex concordance --- Src/LexText/Interlinear/InflFeatureTreeModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/LexText/Interlinear/InflFeatureTreeModel.cs b/Src/LexText/Interlinear/InflFeatureTreeModel.cs index e8da411ec8..ff4b4e3327 100644 --- a/Src/LexText/Interlinear/InflFeatureTreeModel.cs +++ b/Src/LexText/Interlinear/InflFeatureTreeModel.cs @@ -39,7 +39,7 @@ private void AddFeatures(Node parent, IEnumerable features, IDictio object value; if (values == null || !values.TryGetValue(complexFeat, out value)) value = null; - AddFeatures(node, complexFeat.TypeRA.FeaturesRS.Where(f => !features.Contains(f)), (IDictionary) value); + AddFeatures(node, complexFeat.TypeRA.FeaturesRS.Where(f => f != feature), (IDictionary)value); parent.Nodes.Add(node); } else From 5394787aabad45fbc5ab3cee05617e748fb65bb6 Mon Sep 17 00:00:00 2001 From: Jason Naylor Date: Tue, 1 Apr 2025 13:53:36 -0700 Subject: [PATCH 052/123] Bump version to 9.2.8 --- Src/MasterVersionInfo.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/MasterVersionInfo.txt b/Src/MasterVersionInfo.txt index cac6de2493..b6f45a980f 100644 --- a/Src/MasterVersionInfo.txt +++ b/Src/MasterVersionInfo.txt @@ -1,4 +1,4 @@ FWMAJOR=9 FWMINOR=2 -FWREVISION=7 +FWREVISION=8 FWBETAVERSION=Beta From a1ea06ab0b9ecbf386d90aa1940e4244be845b63 Mon Sep 17 00:00:00 2001 From: Jason Naylor Date: Wed, 25 Mar 2020 21:11:13 -0500 Subject: [PATCH 053/123] LT-22039: xhtml has new nodeId attribute (#283) Co-authored-by: Jake Oliver # Conflicts: # Src/xWorks/ConfiguredLcmGenerator.cs --- Src/xWorks/ConfiguredLcmGenerator.cs | 28 ++++++----- Src/xWorks/DictionaryExportService.cs | 2 +- Src/xWorks/ILcmContentGenerator.cs | 22 ++++---- Src/xWorks/LcmJsonGenerator.cs | 22 ++++---- Src/xWorks/LcmWordGenerator.cs | 26 +++++----- Src/xWorks/LcmXhtmlGenerator.cs | 72 ++++++++++++++------------- 6 files changed, 88 insertions(+), 84 deletions(-) diff --git a/Src/xWorks/ConfiguredLcmGenerator.cs b/Src/xWorks/ConfiguredLcmGenerator.cs index b6f9059b54..60ddb2e37c 100644 --- a/Src/xWorks/ConfiguredLcmGenerator.cs +++ b/Src/xWorks/ConfiguredLcmGenerator.cs @@ -344,7 +344,7 @@ internal static IFragment GenerateContentForEntry(ICmObject entry, ConfigurableD { var clerk = settings.PropertyTable.GetValue("ActiveClerk", null); var entryClassName = settings.StylesGenerator.AddStyles(configuration).Trim('.'); - settings.ContentGenerator.StartEntry(xw, configuration, + settings.ContentGenerator.StartEntry(xw, configuration, settings, entryClassName, entry.Guid, index, clerk); settings.ContentGenerator.AddEntryData(xw, pieces); settings.ContentGenerator.EndEntry(xw); @@ -883,7 +883,7 @@ private static IFragment GenerateContentForPicture(ICmFile pictureFile, Configur // An XHTML id attribute must be unique but the ICmfile is used for all references to the same file within the project. // The ICmPicture that owns the file does have unique guid so we use that. var ownerGuid = owner.Guid.ToString(); - return settings.ContentGenerator.AddImage(config, className, srcAttribute, ownerGuid); + return settings.ContentGenerator.AddImage(config, settings, className, srcAttribute, ownerGuid); } return settings.ContentGenerator.CreateFragment(); } @@ -1936,7 +1936,7 @@ private static IFragment GenerateSenseContent(ConfigurableDictionaryNode config, if (bldr.Length() == 0) return bldr; - return settings.ContentGenerator.AddSenseData(config, senseNumberSpan, ((ICmObject)item).Owner.Guid, bldr, first); + return settings.ContentGenerator.AddSenseData(config, settings, senseNumberSpan, ((ICmObject)item).Owner.Guid, bldr, first); } private static IFragment GeneratePictureContent(ConfigurableDictionaryNode config, DictionaryPublicationDecorator publicationDecorator, @@ -2021,7 +2021,7 @@ private static IFragment GenerateCollectionItemContent(ConfigurableDictionaryNod if (bldr.Length() == 0) return bldr; var collectionContent = bldr; - return settings.ContentGenerator.AddCollectionItem(config, IsBlockProperty(config), GetCollectionItemClassAttribute(config), collectionContent, first); + return settings.ContentGenerator.AddCollectionItem(config, settings, IsBlockProperty(config), GetCollectionItemClassAttribute(config), collectionContent, first); } private static void GenerateContentForLexRefCollection(ConfigurableDictionaryNode config, @@ -2156,7 +2156,7 @@ private static IFragment GenerateCrossReferenceChildren(ConfigurableDictionaryNo { // targets var className = settings.StylesGenerator.AddStyles(child).Trim('.'); - settings.ContentGenerator.AddCollection(xw, child, IsBlockProperty(child), + settings.ContentGenerator.AddCollection(xw, child, settings, IsBlockProperty(child), className, content); } break; @@ -2222,7 +2222,7 @@ private static IFragment GenerateSenseNumberSpanIfNeeded(ConfigurableDictionaryN var senseNumberWs = string.IsNullOrEmpty(info.HomographConfig.WritingSystem) ? "en" : info.HomographConfig.WritingSystem; if (string.IsNullOrEmpty(formattedSenseNumber)) return settings.ContentGenerator.CreateFragment(); - return settings.ContentGenerator.GenerateSenseNumber(senseConfigNode, formattedSenseNumber, senseNumberWs); + return settings.ContentGenerator.GenerateSenseNumber(senseConfigNode, settings, formattedSenseNumber, senseNumberWs); } private static string GetSenseNumber(string numberingStyle, ref SenseInfo info) @@ -2604,7 +2604,7 @@ private static IFragment GenerateContentForSimpleString(ConfigurableDictionaryNo var writingSystem = GetLanguageFromFirstOptionOrAnalysis(config.DictionaryNodeOptions as DictionaryNodeWritingSystemOptions, settings.Cache); var cssClassName = settings.StylesGenerator.AddStyles(config).Trim('.'); - return settings.ContentGenerator.AddProperty(config, settings.PropertyTable, cssClassName, false, simpleString, writingSystem); + return settings.ContentGenerator.AddProperty(config, settings, cssClassName, false, simpleString, writingSystem); } @@ -2777,12 +2777,12 @@ private static IFragment GenerateContentForString(ITsString fieldValue, Configur { writingSystem = writingSystem ?? GetLanguageFromFirstOption(config.DictionaryNodeOptions as DictionaryNodeWritingSystemOptions, settings.Cache); - settings.ContentGenerator.StartMultiRunString(writer, config, writingSystem); + settings.ContentGenerator.StartMultiRunString(writer, config, settings, writingSystem); var wsRtl = settings.Cache.WritingSystemFactory.get_Engine(writingSystem).RightToLeftScript; if (rightToLeft != wsRtl) { rightToLeft = wsRtl; // the outer WS direction will be used to identify embedded runs of the opposite direction. - settings.ContentGenerator.StartBiDiWrapper(writer, config, rightToLeft); + settings.ContentGenerator.StartBiDiWrapper(writer, config, settings, rightToLeft); } } @@ -2858,11 +2858,11 @@ private static IFragment GenerateAudioWsContent(string wsId, private static void GenerateRunWithPossibleLink(GeneratorSettings settings, string writingSystem, IFragmentWriter writer, string style, string text, Guid linkDestination, bool rightToLeft, ConfigurableDictionaryNode config, bool first, string externalLink = null) { - settings.ContentGenerator.StartRun(writer, config, settings.PropertyTable, writingSystem, first); + settings.ContentGenerator.StartRun(writer, config, settings, writingSystem, first); var wsRtl = settings.Cache.WritingSystemFactory.get_Engine(writingSystem).RightToLeftScript; if (rightToLeft != wsRtl) { - settings.ContentGenerator.StartBiDiWrapper(writer, config, wsRtl); + settings.ContentGenerator.StartBiDiWrapper(writer, config, settings, wsRtl); } if (!String.IsNullOrEmpty(style)) { @@ -2913,7 +2913,7 @@ private static IFragment GenerateContentForAudioFile(ConfigurableDictionaryNode if (string.IsNullOrEmpty(audioId) && string.IsNullOrEmpty(srcAttribute) && string.IsNullOrEmpty(audioIcon)) return settings.ContentGenerator.CreateFragment(); var safeAudioId = GetSafeXHTMLId(audioId); - return settings.ContentGenerator.GenerateAudioLinkContent(config, classname, srcAttribute, audioIcon, safeAudioId); + return settings.ContentGenerator.GenerateAudioLinkContent(config, settings, classname, srcAttribute, audioIcon, safeAudioId); } private static string GetSafeXHTMLId(string audioId) @@ -3082,7 +3082,7 @@ private static void GenerateTableRow(ITsString rowUSFM, IFragmentWriter writer, private static void GenerateError(IFragmentWriter writer, GeneratorSettings settings, ConfigurableDictionaryNode config, string text) { var writingSystem = settings.Cache.WritingSystemFactory.GetStrFromWs(settings.Cache.WritingSystemFactory.UserWs); - settings.ContentGenerator.StartRun(writer, null, settings.PropertyTable, writingSystem, true); + settings.ContentGenerator.StartRun(writer, null, settings, writingSystem, true); settings.ContentGenerator.SetRunStyle(writer, null, settings.PropertyTable, writingSystem, null, true); if (text.Contains(TxtLineSplit)) { @@ -3258,6 +3258,7 @@ public class GeneratorSettings public string ExportPath { get; } public bool RightToLeft { get; } public bool IsWebExport { get; } + public bool IsXhtmlExport { get; set; } public bool IsTemplate { get; } public GeneratorSettings(LcmCache cache, PropertyTable propertyTable, bool relativePaths,bool copyFiles, string exportPath, bool rightToLeft = false, bool isWebExport = false) @@ -3285,6 +3286,7 @@ public GeneratorSettings(LcmCache cache, ReadOnlyPropertyTable propertyTable, bo RightToLeft = rightToLeft; IsWebExport = isWebExport; IsTemplate = isTemplate; + IsXhtmlExport = false; StylesGenerator.Init(propertyTable); } } diff --git a/Src/xWorks/DictionaryExportService.cs b/Src/xWorks/DictionaryExportService.cs index 4fded0980d..3e7e37d7df 100644 --- a/Src/xWorks/DictionaryExportService.cs +++ b/Src/xWorks/DictionaryExportService.cs @@ -140,7 +140,7 @@ private void ExportConfiguredXhtml(string xhtmlPath, DictionaryConfigurationMode var publicationDecorator = ConfiguredLcmGenerator.GetPublicationDecoratorAndEntries(m_propertyTable, out var entriesToSave, exportType); if (progress != null) progress.Maximum = entriesToSave.Length; - LcmXhtmlGenerator.SavePublishedHtmlWithStyles(entriesToSave, publicationDecorator, int.MaxValue, configuration, m_propertyTable, xhtmlPath, progress); + LcmXhtmlGenerator.SavePublishedHtmlWithStyles(entriesToSave, publicationDecorator, int.MaxValue, configuration, m_propertyTable, xhtmlPath, progress, true); } public List ExportConfiguredJson(string folderPath, DictionaryConfigurationModel configuration, out int[] entryIds) diff --git a/Src/xWorks/ILcmContentGenerator.cs b/Src/xWorks/ILcmContentGenerator.cs index 32cf1fc4f5..e33046c053 100644 --- a/Src/xWorks/ILcmContentGenerator.cs +++ b/Src/xWorks/ILcmContentGenerator.cs @@ -16,23 +16,23 @@ namespace SIL.FieldWorks.XWorks public interface ILcmContentGenerator { IFragment GenerateWsPrefixWithString(ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, bool displayAbbreviation, int wsId, IFragment content); - IFragment GenerateAudioLinkContent(ConfigurableDictionaryNode config, string classname, string srcAttribute, string caption, string safeAudioId); + IFragment GenerateAudioLinkContent(ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, string classname, string srcAttribute, string caption, string safeAudioId); IFragment WriteProcessedObject(ConfigurableDictionaryNode config, bool isBlock, IFragment elementContent, string className); IFragment WriteProcessedCollection(ConfigurableDictionaryNode config, bool isBlock, IFragment elementContent, string className); IFragment GenerateGramInfoBeforeSensesContent(IFragment content, ConfigurableDictionaryNode config); IFragment GenerateGroupingNode(ConfigurableDictionaryNode config, object field, string className, DictionaryPublicationDecorator publicationDecorator, ConfiguredLcmGenerator.GeneratorSettings settings, Func childContentGenerator); - IFragment AddSenseData(ConfigurableDictionaryNode config, IFragment senseNumberSpan, Guid ownerGuid, IFragment senseContent, bool first); - IFragment AddCollectionItem(ConfigurableDictionaryNode config, bool isBlock, string collectionItemClass, IFragment content, bool first); - IFragment AddProperty(ConfigurableDictionaryNode config, ReadOnlyPropertyTable propTable, string className, bool isBlockProperty, string content, string writingSystem); + IFragment AddSenseData(ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, IFragment senseNumberSpan, Guid ownerGuid, IFragment senseContent, bool first); + IFragment AddCollectionItem(ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, bool isBlock, string collectionItemClass, IFragment content, bool first); + IFragment AddProperty(ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, string className, bool isBlockProperty, string content, string writingSystem); IFragment CreateFragment(); IFragment CreateFragment(string str); IFragmentWriter CreateWriter(IFragment fragment); - void StartMultiRunString(IFragmentWriter writer, ConfigurableDictionaryNode config, string writingSystem); + void StartMultiRunString(IFragmentWriter writer, ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, string writingSystem); void EndMultiRunString(IFragmentWriter writer); - void StartBiDiWrapper(IFragmentWriter writer, ConfigurableDictionaryNode config, bool rightToLeft); + void StartBiDiWrapper(IFragmentWriter writer, ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, bool rightToLeft); void EndBiDiWrapper(IFragmentWriter writer); - void StartRun(IFragmentWriter writer, ConfigurableDictionaryNode config, ReadOnlyPropertyTable propTable, string writingSystem, bool first); + void StartRun(IFragmentWriter writer, ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, string writingSystem, bool first); void EndRun(IFragmentWriter writer); void SetRunStyle(IFragmentWriter writer, ConfigurableDictionaryNode config, ReadOnlyPropertyTable propertyTable, string writingSystem, string runStyle, bool error); void StartLink(IFragmentWriter writer, ConfigurableDictionaryNode config, Guid destination); @@ -48,16 +48,16 @@ IFragment GenerateGroupingNode(ConfigurableDictionaryNode config, object field, void EndTableRow(IFragmentWriter writer); void EndTableBody(IFragmentWriter writer); void EndTable(IFragmentWriter writer, ConfigurableDictionaryNode config); - void StartEntry(IFragmentWriter writer, ConfigurableDictionaryNode config, string className, Guid entryGuid, int index, RecordClerk clerk); + void StartEntry(IFragmentWriter writer, ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, string className, Guid entryGuid, int index, RecordClerk clerk); void AddEntryData(IFragmentWriter writer, List pieces); void EndEntry(IFragmentWriter writer); - void AddCollection(IFragmentWriter writer, ConfigurableDictionaryNode config, bool isBlockProperty, string className, IFragment content); + void AddCollection(IFragmentWriter writer, ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, bool isBlockProperty, string className, IFragment content); void BeginObjectProperty(IFragmentWriter writer, ConfigurableDictionaryNode config, bool isBlockProperty, string getCollectionItemClassAttribute); void EndObject(IFragmentWriter writer); void WriteProcessedContents(IFragmentWriter writer, ConfigurableDictionaryNode config, IFragment contents); - IFragment AddImage(ConfigurableDictionaryNode config, string classAttribute, string srcAttribute, string pictureGuid); + IFragment AddImage(ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, string classAttribute, string srcAttribute, string pictureGuid); IFragment AddImageCaption(ConfigurableDictionaryNode config, IFragment captionContent); - IFragment GenerateSenseNumber(ConfigurableDictionaryNode config, string formattedSenseNumber, string senseNumberWs); + IFragment GenerateSenseNumber(ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, string formattedSenseNumber, string senseNumberWs); IFragment AddLexReferences(ConfigurableDictionaryNode config, bool generateLexType, IFragment lexTypeContent, string className, IFragment referencesContent, bool typeBefore); void BeginCrossReference(IFragmentWriter writer, ConfigurableDictionaryNode config, bool isBlockProperty, string className); void EndCrossReference(IFragmentWriter writer); diff --git a/Src/xWorks/LcmJsonGenerator.cs b/Src/xWorks/LcmJsonGenerator.cs index 483ad7a64d..b5c8461725 100644 --- a/Src/xWorks/LcmJsonGenerator.cs +++ b/Src/xWorks/LcmJsonGenerator.cs @@ -44,7 +44,7 @@ public IFragment GenerateWsPrefixWithString(ConfigurableDictionaryNode config, C return content; } - public IFragment GenerateAudioLinkContent(ConfigurableDictionaryNode config, string classname, string srcAttribute, string caption, + public IFragment GenerateAudioLinkContent(ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, string classname, string srcAttribute, string caption, string safeAudioId) { /*"audio": { @@ -117,14 +117,14 @@ public IFragment GenerateGroupingNode(ConfigurableDictionaryNode config, object return new StringFragment(); } - public IFragment AddCollectionItem(ConfigurableDictionaryNode config, bool isBlock, string className, IFragment content, bool first) + public IFragment AddCollectionItem(ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, bool isBlock, string className, IFragment content, bool first) { var fragment = new StringFragment(); fragment.StrBuilder.Append(content.IsNullOrEmpty() ? string.Empty : $"{{{content}}},"); return fragment; } - public IFragment AddProperty(ConfigurableDictionaryNode config, ReadOnlyPropertyTable propTable, string className, bool isBlockProperty, string content, string writingSystem) + public IFragment AddProperty(ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, string className, bool isBlockProperty, string content, string writingSystem) { var fragment = new StringFragment($"\"{className}\": \"{content}\","); return fragment; @@ -145,7 +145,7 @@ public IFragmentWriter CreateWriter(IFragment bldr) return new JsonFragmentWriter(((StringFragment)bldr).StrBuilder); } - public void StartMultiRunString(IFragmentWriter writer, ConfigurableDictionaryNode config, string writingSystem) + public void StartMultiRunString(IFragmentWriter writer, ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, string writingSystem) { } @@ -153,7 +153,7 @@ public void EndMultiRunString(IFragmentWriter writer) { } - public void StartBiDiWrapper(IFragmentWriter writer, ConfigurableDictionaryNode config, bool rightToLeft) + public void StartBiDiWrapper(IFragmentWriter writer, ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, bool rightToLeft) { } @@ -161,7 +161,7 @@ public void EndBiDiWrapper(IFragmentWriter writer) { } - public void StartRun(IFragmentWriter writer, ConfigurableDictionaryNode config, ReadOnlyPropertyTable propTable, string writingSystem, bool first) + public void StartRun(IFragmentWriter writer, ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, string writingSystem, bool first) { var jsonWriter = (JsonFragmentWriter)writer; jsonWriter.StartObject(); @@ -253,7 +253,7 @@ public void EndTable(IFragmentWriter writer, ConfigurableDictionaryNode config) // TODO: decide on a useful json representation for tables } - public void StartEntry(IFragmentWriter xw, ConfigurableDictionaryNode config, string className, Guid entryGuid, int index, RecordClerk clerk) + public void StartEntry(IFragmentWriter xw, ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, string className, Guid entryGuid, int index, RecordClerk clerk) { var jsonWriter = (JsonFragmentWriter)xw; jsonWriter.StartObject(); @@ -292,7 +292,7 @@ public void EndEntry(IFragmentWriter xw) ((JsonFragmentWriter)xw).EndObject(); } - public void AddCollection(IFragmentWriter writer, ConfigurableDictionaryNode config, bool isBlockProperty, string className, IFragment content) + public void AddCollection(IFragmentWriter writer, ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, bool isBlockProperty, string className, IFragment content) { ((JsonFragmentWriter)writer).InsertPropertyName(className); BeginArray(writer); @@ -335,7 +335,7 @@ public void WriteProcessedContents(IFragmentWriter writer, ConfigurableDictionar } } - public IFragment AddImage(ConfigurableDictionaryNode config, string classAttribute, string srcAttribute, string pictureGuid) + public IFragment AddImage(ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, string classAttribute, string srcAttribute, string pictureGuid) { var bldr = new StringBuilder(); var fragment = new StringFragment(); @@ -358,7 +358,7 @@ public IFragment AddImageCaption(ConfigurableDictionaryNode config, IFragment ca return new StringFragment(captionContent.ToString()); } - public IFragment GenerateSenseNumber(ConfigurableDictionaryNode config, string formattedSenseNumber, string wsId) + public IFragment GenerateSenseNumber(ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, string formattedSenseNumber, string wsId) { return new StringFragment(formattedSenseNumber); } @@ -435,7 +435,7 @@ public IFragment GenerateErrorContent(StringBuilder badStrBuilder) return new StringFragment($"\\u+0FFF\\u+0FFF\\u+0FFF{badStrBuilder}"); } - public IFragment AddSenseData(ConfigurableDictionaryNode config, IFragment senseNumberSpan, Guid ownerGuid, IFragment senseContent, bool first) + public IFragment AddSenseData(ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, IFragment senseNumberSpan, Guid ownerGuid, IFragment senseContent, bool first) { var bldr = new StringBuilder(); var fragment = new StringFragment(bldr); diff --git a/Src/xWorks/LcmWordGenerator.cs b/Src/xWorks/LcmWordGenerator.cs index 15570276c6..76d312a3ba 100644 --- a/Src/xWorks/LcmWordGenerator.cs +++ b/Src/xWorks/LcmWordGenerator.cs @@ -1133,7 +1133,7 @@ public IFragment GenerateWsPrefixWithString(ConfigurableDictionaryNode config, C return content; } - public IFragment GenerateAudioLinkContent(ConfigurableDictionaryNode config, string classname, string srcAttribute, string caption, string safeAudioId) + public IFragment GenerateAudioLinkContent(ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, string classname, string srcAttribute, string caption, string safeAudioId) { // We are not planning to support audio and video content for Word Export. return new DocFragment(); @@ -1252,7 +1252,7 @@ config.DictionaryNodeOptions is DictionaryNodeGroupingOptions && return groupData; } - public IFragment AddSenseData(ConfigurableDictionaryNode config, IFragment senseNumberSpan, Guid ownerGuid, IFragment senseContent, bool first) + public IFragment AddSenseData(ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, IFragment senseNumberSpan, Guid ownerGuid, IFragment senseContent, bool first) { var senseData = new DocFragment(); WP.Paragraph newPara = null; @@ -1311,7 +1311,7 @@ public IFragment AddSenseData(ConfigurableDictionaryNode config, IFragment sense return senseData; } - public IFragment AddCollectionItem(ConfigurableDictionaryNode config, bool isBlock, string collectionItemClass, IFragment content, bool first) + public IFragment AddCollectionItem(ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, bool isBlock, string collectionItemClass, IFragment content, bool first) { // Add the style to all the runs in the content fragment. if (!string.IsNullOrEmpty(config.Style) && @@ -1354,7 +1354,7 @@ config.DictionaryNodeOptions is IParaOption && return collData; } - public IFragment AddProperty(ConfigurableDictionaryNode config, ReadOnlyPropertyTable propTable, string className, bool isBlockProperty, string content, string writingSystem) + public IFragment AddProperty(ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, string className, bool isBlockProperty, string content, string writingSystem) { var propFrag = new DocFragment(); Run contentRun = null; @@ -1368,7 +1368,7 @@ public IFragment AddProperty(ConfigurableDictionaryNode config, ReadOnlyProperty // Create a run with the correct style. var writer = CreateWriter(propFrag); - ((WordFragmentWriter)writer).AddRun(Cache, config, propTable, writingSystem, true); + ((WordFragmentWriter)writer).AddRun(Cache, config, settings.PropertyTable, writingSystem, true); // Add the content to the run. AddToRunContent(writer, content); @@ -1410,7 +1410,7 @@ public IFragmentWriter CreateWriter(IFragment frag) return new WordFragmentWriter((DocFragment)frag); } - public void StartMultiRunString(IFragmentWriter writer, ConfigurableDictionaryNode config, string writingSystem) + public void StartMultiRunString(IFragmentWriter writer, ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, string writingSystem) { return; } @@ -1418,7 +1418,7 @@ public void EndMultiRunString(IFragmentWriter writer) { return; } - public void StartBiDiWrapper(IFragmentWriter writer, ConfigurableDictionaryNode config, bool rightToLeft) + public void StartBiDiWrapper(IFragmentWriter writer, ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, bool rightToLeft) { return; } @@ -1431,9 +1431,9 @@ public void EndBiDiWrapper(IFragmentWriter writer) ///
/// /// - public void StartRun(IFragmentWriter writer, ConfigurableDictionaryNode config, ReadOnlyPropertyTable propTable, string writingSystem, bool first) + public void StartRun(IFragmentWriter writer, ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, string writingSystem, bool first) { - ((WordFragmentWriter)writer).AddRun(Cache, config, propTable, writingSystem, first); + ((WordFragmentWriter)writer).AddRun(Cache, config, settings.PropertyTable, writingSystem, first); } public void EndRun(IFragmentWriter writer) { @@ -1646,7 +1646,7 @@ public void EndTable(IFragmentWriter writer, ConfigurableDictionaryNode config) wordWriter.CurrentTable = null; } - public void StartEntry(IFragmentWriter writer, ConfigurableDictionaryNode node, string className, Guid entryGuid, int index, RecordClerk clerk) + public void StartEntry(IFragmentWriter writer, ConfigurableDictionaryNode node, ConfiguredLcmGenerator.GeneratorSettings settings, string className, Guid entryGuid, int index, RecordClerk clerk) { // Each entry starts a new paragraph. The paragraph will end whenever a child needs its own paragraph or // when a data type exists that cannot be in a paragraph (Tables or nested paragraphs). @@ -1782,7 +1782,7 @@ public void EndEntry(IFragmentWriter writer) { return; } - public void AddCollection(IFragmentWriter writer, ConfigurableDictionaryNode config, bool isBlockProperty, string className, IFragment content) + public void AddCollection(IFragmentWriter writer, ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, bool isBlockProperty, string className, IFragment content) { string styleDisplayName = GetUniqueDisplayName(config, content); // Add Before text. @@ -1819,7 +1819,7 @@ public void WriteProcessedContents(IFragmentWriter writer, ConfigurableDictionar ((WordFragmentWriter)writer).Insert(contents); } } - public IFragment AddImage(ConfigurableDictionaryNode config, string classAttribute, string srcAttribute, string pictureGuid) + public IFragment AddImage(ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, string classAttribute, string srcAttribute, string pictureGuid) { DocFragment imageFrag = new DocFragment(); WordprocessingDocument wordDoc = imageFrag.DocFrag; @@ -1865,7 +1865,7 @@ public IFragment AddImageCaption(ConfigurableDictionaryNode config, IFragment ca } return docFrag; } - public IFragment GenerateSenseNumber(ConfigurableDictionaryNode senseConfigNode, string formattedSenseNumber, string senseNumberWs) + public IFragment GenerateSenseNumber(ConfigurableDictionaryNode senseConfigNode, ConfiguredLcmGenerator.GeneratorSettings settings, string formattedSenseNumber, string senseNumberWs) { var senseOptions = (DictionaryNodeSenseOptions)senseConfigNode?.DictionaryNodeOptions; string afterNumber = null; diff --git a/Src/xWorks/LcmXhtmlGenerator.cs b/Src/xWorks/LcmXhtmlGenerator.cs index fa15493e7e..8b86e131b7 100644 --- a/Src/xWorks/LcmXhtmlGenerator.cs +++ b/Src/xWorks/LcmXhtmlGenerator.cs @@ -82,7 +82,8 @@ public static string SavePreviewHtmlWithStyles(int[] entryHvos, DictionaryPublic /// the given collection. ///
public static void SavePublishedHtmlWithStyles(int[] entryHvos, DictionaryPublicationDecorator publicationDecorator, int entriesPerPage, - DictionaryConfigurationModel configuration, XCore.PropertyTable propertyTable, string xhtmlPath, IThreadedProgress progress = null) + DictionaryConfigurationModel configuration, XCore.PropertyTable propertyTable, string xhtmlPath, IThreadedProgress progress = null, + bool isXhtmlExport = false) { var entryCount = entryHvos.Length; var cssPath = Path.ChangeExtension(xhtmlPath, "css"); @@ -97,6 +98,7 @@ public static void SavePublishedHtmlWithStyles(int[] entryHvos, DictionaryPublic var custCssPath = CssGenerator.CopyCustomCssAndGetPath(Path.GetDirectoryName(xhtmlPath), cache, false); var settings = new ConfiguredLcmGenerator.GeneratorSettings(cache, readOnlyPropertyTable, true, true, Path.GetDirectoryName(xhtmlPath), ConfiguredLcmGenerator.IsEntryStyleRtl(readOnlyPropertyTable, configuration), Path.GetFileName(cssPath) == "configured.css"); + settings.IsXhtmlExport = isXhtmlExport; settings.StylesGenerator.AddGlobalStyles(configuration, readOnlyPropertyTable); GenerateOpeningHtml(cssPath, custCssPath, settings, xhtmlWriter); Tuple currentPageBounds = GetPageForCurrentEntry(settings, entryHvos, entriesPerPage); @@ -550,10 +552,7 @@ public IFragment GenerateWsPrefixWithString(ConfigurableDictionaryNode config, C { xw.WriteStartElement("span"); xw.WriteAttributeString("class", CssGenerator.WritingSystemPrefix); - if (!settings.IsWebExport) - { - xw.WriteAttributeString("nodeId", $"{config.GetHashCode()}"); - } + WriteNodeId(xw, config, settings); var prefix = ((CoreWritingSystemDefinition)settings.Cache.WritingSystemFactory.get_EngineOrNull(wsId)).Abbreviation; xw.WriteString(prefix); xw.WriteEndElement(); @@ -564,7 +563,17 @@ public IFragment GenerateWsPrefixWithString(ConfigurableDictionaryNode config, C } } - public IFragment GenerateAudioLinkContent(ConfigurableDictionaryNode config, string classname, + private void WriteNodeId(XmlWriter xw, ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings) + { + if (config == null) + // When generating an error node config is null + return; + if (settings != null && (settings.IsWebExport || settings.IsXhtmlExport)) + return; + xw.WriteAttributeString("nodeId", $"{config.GetHashCode()}"); + } + + public IFragment GenerateAudioLinkContent(ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, string classname, string srcAttribute, string caption, string safeAudioId) { var bldr = new StringBuilder(); @@ -573,7 +582,7 @@ public IFragment GenerateAudioLinkContent(ConfigurableDictionaryNode config, str { xw.WriteStartElement("audio"); xw.WriteAttributeString("id", safeAudioId); - xw.WriteAttributeString("nodeId", $"{config.GetHashCode()}"); + WriteNodeId(xw, config, settings); xw.WriteStartElement("source"); xw.WriteAttributeString("src", srcAttribute); xw.WriteRaw(""); @@ -649,10 +658,7 @@ public IFragment GenerateGroupingNode(ConfigurableDictionaryNode config, object { xw.WriteStartElement("span"); xw.WriteAttributeString("class", className); - if (!settings.IsWebExport) - { - xw.WriteAttributeString("nodeId", $"{config.GetHashCode()}"); - } + WriteNodeId(xw, config, settings); var innerBuilder = new StringBuilder(); foreach (var child in config.ReferencedOrDirectChildren) @@ -705,11 +711,11 @@ public void Flush() } } - public void StartMultiRunString(IFragmentWriter writer, ConfigurableDictionaryNode config, string writingSystem) + public void StartMultiRunString(IFragmentWriter writer, ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, string writingSystem) { var xw = ((XmlFragmentWriter)writer).Writer; xw.WriteStartElement("span"); - xw.WriteAttributeString("nodeId", $"{config.GetHashCode()}"); + WriteNodeId(xw, config, settings); xw.WriteAttributeString("lang", writingSystem); } @@ -719,11 +725,11 @@ public void EndMultiRunString(IFragmentWriter writer) ((XmlFragmentWriter)writer).Writer.WriteEndElement(); // (lang) } - public void StartBiDiWrapper(IFragmentWriter writer, ConfigurableDictionaryNode config, bool rightToLeft) + public void StartBiDiWrapper(IFragmentWriter writer, ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, bool rightToLeft) { var xw = ((XmlFragmentWriter)writer).Writer; xw.WriteStartElement("span"); // set direction on a nested span to preserve Context's position and direction. - xw.WriteAttributeString("nodeId", $"{config.GetHashCode()}"); + WriteNodeId(xw, config, settings); xw.WriteAttributeString("dir", rightToLeft ? "rtl" : "ltr"); } @@ -733,15 +739,11 @@ public void EndBiDiWrapper(IFragmentWriter writer) ((XmlFragmentWriter)writer).Writer.WriteEndElement(); // (dir) } - public void StartRun(IFragmentWriter writer, ConfigurableDictionaryNode config, ReadOnlyPropertyTable propTable, string writingSystem, bool first) + public void StartRun(IFragmentWriter writer, ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, string writingSystem, bool first) { var xw = ((XmlFragmentWriter)writer).Writer; xw.WriteStartElement("span"); - // When generating an error node config is null - if (config != null) - { - xw.WriteAttributeString("nodeId", $"{config.GetHashCode()}"); - } + WriteNodeId(xw, config, settings); xw.WriteAttributeString("lang", writingSystem); } @@ -880,12 +882,12 @@ public void EndTable(IFragmentWriter writer, ConfigurableDictionaryNode config) ((XmlFragmentWriter)writer).Writer.WriteEndElement(); // should be } - public void StartEntry(IFragmentWriter writer, ConfigurableDictionaryNode config, string className, Guid entryGuid, int index, RecordClerk clerk) + public void StartEntry(IFragmentWriter writer, ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, string className, Guid entryGuid, int index, RecordClerk clerk) { var xw = ((XmlFragmentWriter)writer).Writer; xw.WriteStartElement("div"); xw.WriteAttributeString("class", className); - xw.WriteAttributeString("nodeId", $"{config.GetHashCode()}"); + WriteNodeId(xw, config, settings); xw.WriteAttributeString("id", "g" + entryGuid); } @@ -902,13 +904,13 @@ public void EndEntry(IFragmentWriter writer) EndObject(writer); } - public void AddCollection(IFragmentWriter writer, ConfigurableDictionaryNode config, + public void AddCollection(IFragmentWriter writer, ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, bool isBlockProperty, string className, IFragment content) { var xw = ((XmlFragmentWriter)writer).Writer; xw.WriteStartElement(isBlockProperty ? "div" : "span"); xw.WriteAttributeString("class", className); - xw.WriteAttributeString("nodeId", $"{config.GetHashCode()}"); + WriteNodeId(xw, config, settings); xw.WriteRaw(content.ToString()); xw.WriteEndElement(); } @@ -933,7 +935,7 @@ public void WriteProcessedContents(IFragmentWriter writer, ConfigurableDictionar /// /// This is used as an id in the xhtml and must be unique. - public IFragment AddImage(ConfigurableDictionaryNode config, string classAttribute, string srcAttribute, string pictureGuid) + public IFragment AddImage(ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, string classAttribute, string srcAttribute, string pictureGuid) { var bldr = new StringBuilder(); var fragment = new StringFragment(bldr); @@ -943,7 +945,7 @@ public IFragment AddImage(ConfigurableDictionaryNode config, string classAttribu xw.WriteAttributeString("class", classAttribute); xw.WriteAttributeString("src", srcAttribute); xw.WriteAttributeString("id", "g" + pictureGuid); - xw.WriteAttributeString("nodeId", $"{config.GetHashCode()}"); + WriteNodeId(xw, config, settings); xw.WriteEndElement(); xw.Flush(); return fragment; @@ -965,7 +967,7 @@ public IFragment AddImageCaption(ConfigurableDictionaryNode config, IFragment ca } } - public IFragment GenerateSenseNumber(ConfigurableDictionaryNode config, string formattedSenseNumber, string senseNumberWs) + public IFragment GenerateSenseNumber(ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, string formattedSenseNumber, string senseNumberWs) { var bldr = new StringBuilder(); var fragment = new StringFragment(bldr); @@ -974,7 +976,7 @@ public IFragment GenerateSenseNumber(ConfigurableDictionaryNode config, string f xw.WriteStartElement("span"); xw.WriteAttributeString("class", "sensenumber"); xw.WriteAttributeString("lang", senseNumberWs); - xw.WriteAttributeString("nodeId", $"{config.GetHashCode()}"); + WriteNodeId(xw, config, settings); xw.WriteString(formattedSenseNumber); xw.WriteEndElement(); xw.Flush(); @@ -1060,7 +1062,7 @@ public IFragment GenerateVideoLinkContent(ConfigurableDictionaryNode config, str } } - public IFragment AddCollectionItem(ConfigurableDictionaryNode config, bool isBlock, string collectionItemClass, IFragment content, bool first) + public IFragment AddCollectionItem(ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, bool isBlock, string collectionItemClass, IFragment content, bool first) { var bldr = new StringBuilder(); var builder = new StringFragment(bldr); @@ -1068,7 +1070,7 @@ public IFragment AddCollectionItem(ConfigurableDictionaryNode config, bool isBlo { xw.WriteStartElement(isBlock ? "div" : "span"); xw.WriteAttributeString("class", collectionItemClass); - xw.WriteAttributeString("nodeId", $"{config.GetHashCode()}"); + WriteNodeId(xw, config, settings); xw.WriteRaw(content.ToString()); xw.WriteEndElement(); xw.Flush(); @@ -1076,7 +1078,7 @@ public IFragment AddCollectionItem(ConfigurableDictionaryNode config, bool isBlo } } - public IFragment AddProperty(ConfigurableDictionaryNode config, ReadOnlyPropertyTable propTable, string className, bool isBlockProperty, string content, string writingSystem) + public IFragment AddProperty(ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, string className, bool isBlockProperty, string content, string writingSystem) { var bldr = new StringBuilder(); var fragment = new StringFragment(bldr); @@ -1085,7 +1087,7 @@ public IFragment AddProperty(ConfigurableDictionaryNode config, ReadOnlyProperty { xw.WriteStartElement(isBlockProperty ? "div" : "span"); xw.WriteAttributeString("class", className); - xw.WriteAttributeString("nodeId", $"{config.GetHashCode()}"); + WriteNodeId(xw, config, settings); xw.WriteString(content); xw.WriteEndElement(); xw.Flush(); @@ -1093,7 +1095,7 @@ public IFragment AddProperty(ConfigurableDictionaryNode config, ReadOnlyProperty } } - public IFragment AddSenseData(ConfigurableDictionaryNode config, IFragment senseNumberSpan, Guid ownerGuid, + public IFragment AddSenseData(ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, IFragment senseNumberSpan, Guid ownerGuid, IFragment senseContent, bool first) { bool isBlock = ConfiguredLcmGenerator.IsBlockProperty(config); @@ -1109,7 +1111,7 @@ public IFragment AddSenseData(ConfigurableDictionaryNode config, IFragment sense xw.WriteStartElement(isBlock ? "div" : "span"); xw.WriteAttributeString("class", className); xw.WriteAttributeString("entryguid", "g" + ownerGuid); - xw.WriteAttributeString("nodeId", $"{config.GetHashCode()}"); + WriteNodeId(xw, config, settings); xw.WriteRaw(senseContent.ToString()); xw.WriteEndElement(); // element name for property xw.WriteEndElement(); // From 33b96b187c521874a750fc4909ed921c30a23851 Mon Sep 17 00:00:00 2001 From: John T Maxwell III Date: Thu, 13 Mar 2025 08:58:35 -0700 Subject: [PATCH 054/123] Fix LT-22057: Duplicate Analysis ws created for English (#285) --- Src/FwCoreDlgs/FwNewLangProjectModel.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Src/FwCoreDlgs/FwNewLangProjectModel.cs b/Src/FwCoreDlgs/FwNewLangProjectModel.cs index d24acc6cca..c7a06241b6 100644 --- a/Src/FwCoreDlgs/FwNewLangProjectModel.cs +++ b/Src/FwCoreDlgs/FwNewLangProjectModel.cs @@ -263,12 +263,15 @@ public string CreateNewLangProj(IThreadedProgress progressDialog, ISynchronizeIn { var defaultAnalysis = WritingSystemContainer.CurrentAnalysisWritingSystems.First(); var defaultVernacular = WritingSystemContainer.CurrentVernacularWritingSystems.First(); + var remainingAnalysisWss = WritingSystemContainer.AnalysisWritingSystems.Skip(1).ToList(); + // Avoid duplicate "en" because of "en" below (see LT-22057). + remainingAnalysisWss.RemoveAll(ws => ws.LanguageTag == "en"); return LcmCache.CreateNewLangProj(progressDialog, ProjectName, FwDirectoryFinder.LcmDirectories, threadHelper, defaultAnalysis, defaultVernacular, "en",// TODO: replicate original - new HashSet(WritingSystemContainer.AnalysisWritingSystems.Skip(1)), + new HashSet(remainingAnalysisWss), new HashSet(WritingSystemContainer.VernacularWritingSystems.Skip(1)), AnthroModel.AnthroFileName); } From da7cd68b733a0657a21ae06191aedc68d61c7015 Mon Sep 17 00:00:00 2001 From: John T Maxwell III Date: Tue, 11 Mar 2025 10:34:03 -0700 Subject: [PATCH 055/123] Fix LT-21826: Usage figures for Publications show inverted values (#282) * Fix LT-21826: Usage figures for Publications show inverted values * Fix LT-21826: Add PublicationLabelNode with proper CountUsages --- .../XMLViews/ReallySimpleListChooser.cs | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/Src/Common/Controls/XMLViews/ReallySimpleListChooser.cs b/Src/Common/Controls/XMLViews/ReallySimpleListChooser.cs index 207ab156bf..ad499e83e5 100644 --- a/Src/Common/Controls/XMLViews/ReallySimpleListChooser.cs +++ b/Src/Common/Controls/XMLViews/ReallySimpleListChooser.cs @@ -1843,6 +1843,8 @@ public virtual bool WantNodeForLabel(ObjectLabel label) /// protected virtual LabelNode CreateLabelNode(ObjectLabel nol, bool displayUsage) { + if (nol.Object.Owner.ToString() == "Publications") + return new PublicationLabelNode(nol, m_stylesheet, displayUsage); return new LabelNode(nol, m_stylesheet, displayUsage); } @@ -2863,4 +2865,38 @@ private bool HasLeaves(ObjectLabel label) } } + /// ------------------------------------------------------------------------------------ + /// + /// PublicationLabelNode + /// + /// ------------------------------------------------------------------------------------ + public class PublicationLabelNode : LabelNode + { + + /// + /// Initializes a new instance of the class. + /// + public PublicationLabelNode(ObjectLabel label, IVwStylesheet stylesheet, bool displayUsage) + : base(label, stylesheet, displayUsage) + { + } + /// + /// Publication is the value of DoNotPublishIn, + /// so we need to invert the count. + /// + /// + protected override int CountUsages() + { + int count = 0; + // I think only label.Object is likely to be null, but let's prevent crashes thoroughly. + if (Label?.Object?.ReferringObjects != null) + { + ILexEntryRepository repository = Label.Object.Cache.ServiceLocator.GetInstance(); + count = repository.Count - Label.Object.ReferringObjects.Count; + } + return count; + } + + } + } From e310c91aa0281f82e5bf775316cec67aa0455549 Mon Sep 17 00:00:00 2001 From: Ken Zook Date: Tue, 1 Apr 2025 15:47:45 -0500 Subject: [PATCH 056/123] LT-17491 Finish Exemplar processing Implemented Exemplar in SFM export, and LIFT import/export Change-Id: I7c35bdb649043e2380f433b96a63b81fa5d1636d --- .../Export Templates/RootBasedMDF.xml | 1 + DistFiles/Language Explorer/Export Templates/mdf.xml | 1 + .../LexTextControlsTests/LiftMergerTests.cs | 9 +++++++++ Src/LexText/LexTextControls/LiftExporter.cs | 8 ++++++++ Src/LexText/LexTextControls/LiftMerger.cs | 11 +++++++++++ 5 files changed, 30 insertions(+) diff --git a/DistFiles/Language Explorer/Export Templates/RootBasedMDF.xml b/DistFiles/Language Explorer/Export Templates/RootBasedMDF.xml index 612d9815dd..3789d2648c 100644 --- a/DistFiles/Language Explorer/Export Templates/RootBasedMDF.xml +++ b/DistFiles/Language Explorer/Export Templates/RootBasedMDF.xml @@ -96,6 +96,7 @@ Export using the Multi-Dictionary Formatter root-based standard. This includes s + diff --git a/DistFiles/Language Explorer/Export Templates/mdf.xml b/DistFiles/Language Explorer/Export Templates/mdf.xml index e621732db0..710307e057 100644 --- a/DistFiles/Language Explorer/Export Templates/mdf.xml +++ b/DistFiles/Language Explorer/Export Templates/mdf.xml @@ -63,6 +63,7 @@ Export using the Multi-Dictionary Formatter lexeme-based standard. This exports + diff --git a/Src/LexText/LexTextControls/LexTextControlsTests/LiftMergerTests.cs b/Src/LexText/LexTextControls/LexTextControlsTests/LiftMergerTests.cs index 006899e51b..02de238379 100644 --- a/Src/LexText/LexTextControls/LexTextControlsTests/LiftMergerTests.cs +++ b/Src/LexText/LexTextControls/LexTextControlsTests/LiftMergerTests.cs @@ -1135,6 +1135,9 @@ public void TestLiftImport4() "", "
A summary definition (located at the entry level in the Entry pane) is a general definition summarizing all the senses of a primary entry. It has no theoretical value; its use is solely pragmatic.
", "
", + "", + "
This field stores the exemplar form for the current sense.
", + "
", "", "
This field stores the scientific name pertinent to the current sense.
", "
", @@ -1386,6 +1389,9 @@ public void TestLiftImport5_CustomFieldsStringsAndMultiUnicode() "", "
A summary definition (located at the entry level in the Entry pane) is a general definition summarizing all the senses of a primary entry. It has no theoretical value; its use is solely pragmatic.
", "
", + "", + "
This field stores the exemplar form for the current sense.
", + "
", "", "
This field stores the scientific name pertinent to the current sense.
", "
", @@ -2463,6 +2469,9 @@ private static string BestAlternative(IMultiAccessorBase multi, int wsDefault) "", "
A summary definition (located at the entry level in the Entry pane) is a general definition summarizing all the senses of a primary entry. It has no theoretical value; its use is solely pragmatic.
", "
", + "", + "
This field stores the exemplar form for the current sense.
", + "
", "", "
This field stores the scientific name pertinent to the current sense.
", "
", diff --git a/Src/LexText/LexTextControls/LiftExporter.cs b/Src/LexText/LexTextControls/LiftExporter.cs index e9edbe5624..99e5d9b6f4 100644 --- a/Src/LexText/LexTextControls/LiftExporter.cs +++ b/Src/LexText/LexTextControls/LiftExporter.cs @@ -773,6 +773,7 @@ private void WriteLexSense(TextWriter w, ILexSense sense, bool fOrder) break; } } + WriteAllForms(w, "field", "type=\"exemplar\"", "form", sense.Exemplar); WriteAllForms(w, null, null, "gloss", sense.Gloss); WriteAllForms(w, "definition", null, "form", sense.Definition); foreach (var publication in sense.DoNotPublishInRC) @@ -1235,6 +1236,9 @@ private void WriteInternalFieldInformation(TextWriter w) w.WriteLine(""); w.WriteLine("
A summary definition (located at the entry level in the Entry pane) is a general definition summarizing all the senses of a primary entry. It has no theoretical value; its use is solely pragmatic.
"); w.WriteLine("
"); + w.WriteLine(""); + w.WriteLine("
This field stores the exemplar form for the current sense.
"); + w.WriteLine("
"); w.WriteLine(""); w.WriteLine("
This field stores the scientific name pertinent to the current sense.
"); w.WriteLine("
"); @@ -1811,6 +1815,10 @@ static void WriteNoteTypeRange(TextWriter w) w.WriteLine(""); w.WriteLine("
This note gives encyclopedic information.
"); w.WriteLine(""); + w.WriteLine(""); + w.WriteLine(""); + w.WriteLine("
Gives the exemplar form of a sense
"); + w.WriteLine("
"); w.WriteLine(""); w.WriteLine(""); w.WriteLine("
General notes that do not fall in another clear category
"); diff --git a/Src/LexText/LexTextControls/LiftMerger.cs b/Src/LexText/LexTextControls/LiftMerger.cs index 4d7abc3afe..2ea98c8896 100644 --- a/Src/LexText/LexTextControls/LiftMerger.cs +++ b/Src/LexText/LexTextControls/LiftMerger.cs @@ -6351,6 +6351,9 @@ private void ProcessSenseFields(ILexSense ls, CmLiftSense sense) case "scientific-name": ls.ScientificName = StoreTsStringValue(m_fCreatingNewSense, ls.ScientificName, field.Content); break; + case "exemplar": + MergeInMultiString(ls.Exemplar, LexSenseTags.kflidExemplar, field.Content, ls.Guid); + break; default: ProcessUnknownField(ls, sense, field, "LexSense", "custom-sense-", LexSenseTags.kClassId); @@ -6470,6 +6473,14 @@ private bool SenseFieldsConflict(ILexSense ls, List list) return true; } break; + case "exemplar": + AddNewWsToVernacular(); + if (MultiTsStringsConflict(ls.Exemplar, field.Content)) + { + m_cdConflict = new ConflictingSense("Exemplar", ls, this); + return true; + } + break; default: int flid; if (m_dictCustomFlid.TryGetValue("LexSense-" + sType, out flid)) From 6bfc30a35e89f8e3414d6d74a9cd45f44406e6d7 Mon Sep 17 00:00:00 2001 From: Mark Kidder <83427558+mark-sil@users.noreply.github.com> Date: Wed, 2 Apr 2025 13:24:02 -0400 Subject: [PATCH 057/123] Word Export: LT-22051: Fix problems with incorrect styles (#302) Change the way that we generate character styles so that it includes styles from all the parent nodes. Also changed the way paragraph styles are generates so they no longer directly include run properties. Now they are linked to a character style that will be used to build the character style properties. --- Src/xWorks/CssGenerator.cs | 2 +- Src/xWorks/LcmWordGenerator.cs | 1002 ++++++----------- Src/xWorks/WordStyleCollection.cs | 751 +++++++++--- Src/xWorks/WordStylesGenerator.cs | 430 +++---- .../xWorksTests/LcmWordGeneratorTests.cs | 88 +- 5 files changed, 1076 insertions(+), 1197 deletions(-) diff --git a/Src/xWorks/CssGenerator.cs b/Src/xWorks/CssGenerator.cs index 88e331d75a..acf288475a 100644 --- a/Src/xWorks/CssGenerator.cs +++ b/Src/xWorks/CssGenerator.cs @@ -166,7 +166,7 @@ public string AddStyles(ConfigurableDictionaryNode node) /// Get a path containing Non-Unique names for all the nodes from the root up to and /// including the 'node' passed in. ///
- private string GetNodePath(ConfigurableDictionaryNode node) + public static string GetNodePath(ConfigurableDictionaryNode node) { // Generate the node path info from the root to this node. string pathToNode = null; diff --git a/Src/xWorks/LcmWordGenerator.cs b/Src/xWorks/LcmWordGenerator.cs index 76d312a3ba..edc9b48313 100644 --- a/Src/xWorks/LcmWordGenerator.cs +++ b/Src/xWorks/LcmWordGenerator.cs @@ -39,7 +39,9 @@ namespace SIL.FieldWorks.XWorks public class LcmWordGenerator : ILcmContentGenerator, ILcmStylesGenerator { private LcmCache Cache { get; } - private static WordStyleCollection s_styleCollection = new WordStyleCollection(); + private static WordStyleCollection s_styleCollection = null; + private static readonly object _collectionLock = new object(); + private ReadOnlyPropertyTable _propertyTable; public static bool IsBidi { get; private set; } @@ -48,6 +50,12 @@ public LcmWordGenerator(LcmCache cache) Cache = cache; } + public void Init(ReadOnlyPropertyTable propertyTable) + { + _propertyTable = propertyTable; + s_styleCollection = new WordStyleCollection(_propertyTable, _collectionLock); + } + public static void SavePublishedDocx(int[] entryHvos, DictionaryPublicationDecorator publicationDecorator, int batchSize, DictionaryConfigurationModel configuration, XCore.PropertyTable propertyTable, string filePath, IThreadedProgress progress = null) { @@ -107,10 +115,33 @@ public static void SavePublishedDocx(int[] entryHvos, DictionaryPublicationDecor var propStyleSheet = FontHeightAdjuster.StyleSheetFromPropertyTable(propertyTable); + s_styleCollection.RedirectCharacterElements(); + foreach (var entry in entryContents) { if (!entry.Item2.IsNullOrEmpty()) { + foreach (WP.Run run in ((DocFragment)entry.Item2).DocBody.Descendants()) + { + // Reset the run styles to any redirected styles and mark styles as being used. + var charElem = GetElementFromRun(run); + if (charElem != null) // Runs containing a 'Drawing' will not have RunProperties. + { + if (charElem.Redirect != null) + { + SetUniqueDisplayName(run, charElem.Redirect.UniqueDisplayName()); + charElem.Redirect.Used = true; + } + else + { + charElem.Used = true; + } + } + + // Remove temporary data + run.ClearAllAttributes(); + } + IFragment letterHeader = GenerateLetterHeaderIfNeeded(entry.Item1, ref lastHeader, col, settings, readOnlyPropertyTable, propStyleSheet, firstHeader, clerk ); firstHeader = false; @@ -188,12 +219,12 @@ public static void SavePublishedDocx(int[] entryHvos, DictionaryPublicationDecor stylePart = AddStylesPartToPackage(fragment.DocFrag); Styles styleSheet = new Styles(); - // Add generated styles into the stylesheet from the collection. - var styleElements = s_styleCollection.GetStyleElements(); - foreach (var styleElement in styleElements) + // Add generated styles into the stylesheet from the collections. + var paragraphElements = s_styleCollection.GetParagraphElements(); + foreach (var element in paragraphElements) { // Generate bullet and numbering data. - if (styleElement.BulletInfo.HasValue) + if (element.BulletInfo.HasValue) { // Initialize word doc's numbering part one time. if (numberingPart == null) @@ -201,10 +232,12 @@ public static void SavePublishedDocx(int[] entryHvos, DictionaryPublicationDecor numberingPart = AddNumberingPartToPackage(fragment.DocFrag); } - GenerateBulletAndNumberingData(styleElement, numberingPart); + GenerateBulletAndNumberingData(element, numberingPart); } - styleSheet.AppendChild(styleElement.Style.CloneNode(true)); + styleSheet.AppendChild(element.Style.CloneNode(true)); } + var characterElements = s_styleCollection.GetUsedCharacterElements(); + characterElements.ForEach(element => styleSheet.AppendChild(element.Style.CloneNode(true))); // Clear the collection. s_styleCollection.Clear(); @@ -276,7 +309,10 @@ internal static IFragment GenerateLetterHeaderIfNeeded(ICmObject entry, ref stri headwordWsCollator, settings, clerk); // Create LetterHeader doc fragment and link it with the letter heading style. - return DocFragment.GenerateLetterHeaderDocFragment(headerTextBuilder.ToString(), WordStylesGenerator.LetterHeadingDisplayName, firstHeader); + var cache = propertyTable.GetValue("cache"); + var wsId = cache.ServiceLocator.WritingSystems.DefaultVernacularWritingSystem.Handle; + var wsString = cache.WritingSystemFactory.GetStrFromWs(wsId); + return DocFragment.GenerateLetterHeaderDocFragment(headerTextBuilder.ToString(), WordStylesGenerator.LetterHeadingDisplayName, firstHeader, wsString); } /* @@ -289,7 +325,6 @@ public class DocFragment : IFragment internal WordprocessingDocument DocFrag { get; } internal MainDocumentPart mainDocPart { get; } internal WP.Body DocBody { get; } - internal string ParagraphStyle { get; private set; } /// /// Constructs a new memory stream and creates an empty doc fragment @@ -345,7 +380,7 @@ public DocFragment(string str) : this() /// Letter header string. /// Letter header style name to display in Word. /// True if this is the first header being written. - internal static DocFragment GenerateLetterHeaderDocFragment(string str, string styleDisplayName, bool firstHeader) + internal static DocFragment GenerateLetterHeaderDocFragment(string str, string styleDisplayName, bool firstHeader, string wsString) { var docFrag = new DocFragment(); // Only create paragraph, run, and text objects if string is nonempty @@ -375,6 +410,8 @@ internal static DocFragment GenerateLetterHeaderDocFragment(string str, string s WP.ParagraphProperties paragraphProps = new WP.ParagraphProperties(new ParagraphStyleId() { Val = styleDisplayName }); WP.Paragraph para = docFrag.DocBody.AppendChild(new WP.Paragraph(paragraphProps)); WP.Run run = para.AppendChild(new WP.Run()); + string uniqueDisplayName = WordStylesGenerator.LetterHeadingDisplayName + WordStylesGenerator.GetWsString(wsString); + run.Append(GenerateRunProperties(uniqueDisplayName)); // For spaces to show correctly, set preserve spaces on the text element WP.Text txt = new WP.Text(str); txt.Space = SpaceProcessingModeValues.Preserve; @@ -398,37 +435,6 @@ internal static DocFragment GenerateLetterHeaderDocFragment(string str, string s return docFrag; } - public static string GetWsStyleName(LcmCache cache, ConfigurableDictionaryNode config, string writingSystem) - { - string styleDisplayName = config.DisplayLabel; - - // If the config does not contain writing system options, then just return the style name.(An example is custom fields.) - if (!(config.DictionaryNodeOptions is DictionaryNodeWritingSystemOptions)) - { - return styleDisplayName; - } - - return GenerateWsStyleName(cache, styleDisplayName, writingSystem); - } - - public static string GenerateWsStyleName(LcmCache cache, string styleDisplayName, string writingSystem) - { - var wsStr = writingSystem; - var possiblyMagic = WritingSystemServices.GetMagicWsIdFromName(writingSystem); - // If it is magic, then get the associated ws. - if (possiblyMagic != 0) - { - // Get a list of the writing systems for the magic name, and use the first one. - wsStr = WritingSystemServices.GetWritingSystemList(cache, possiblyMagic, false).First().Id; - } - - // If there is no base style, return just the ws style. - if (string.IsNullOrEmpty(styleDisplayName)) - return WordStylesGenerator.GetWsString(wsStr); - // If there is a base style, return the ws-specific version of that style. - return styleDisplayName + WordStylesGenerator.GetWsString(wsStr); - } - /// /// Returns content of the doc fragment as a string. /// Be careful using this as document styles won't be preserved in a string. @@ -568,13 +574,14 @@ public void AppendToParagraph(IFragment fragToCopy, Run run, bool forceNewParagr ParagraphStyleId styleId = paraProps.OfType().FirstOrDefault(); if (styleId != null && styleId.Val != null && styleId.Val.Value != null) { - if (styleId.Val.Value.EndsWith(WordStylesGenerator.EntryStyleContinue)) + var paraElem = s_styleCollection.GetParagraphElement(styleId.Val.Value); + if (paraElem.DisplayNameBase.EndsWith(WordStylesGenerator.EntryStyleContinue)) { - style = styleId.Val.Value; + style = paraElem.UniqueDisplayName(); } else { - style = styleId.Val.Value + WordStylesGenerator.EntryStyleContinue; + style = paraElem.ContinuationElement.UniqueDisplayName(); } } } @@ -762,7 +769,7 @@ public void AppendNewTextboxParagraph(IFragment frag, Run run, WP.ParagraphPrope int uniqueOuterDrawingId; // Lock style collection while getting the IDs to use for the Image & Textbox - lock (s_styleCollection) + lock (_collectionLock) { // The xml textbox image structure consists of a graphic object that is nested inside a drawing object // that is nested inside another drawing object. @@ -989,98 +996,6 @@ public void Insert(IFragment frag) internal IFragment TableTitleContent { get; set; } internal int TableColumns { get; set; } internal int RowColumns { get; set; } - - /// - /// Add a new run to the WordFragment DocBody. - /// - public void AddRun(LcmCache cache, ConfigurableDictionaryNode config, ReadOnlyPropertyTable propTable, string writingSystem, bool first) - { - var run = new WP.Run(); - string uniqueDisplayName = null; - string displayNameBase = (config == null || writingSystem == null) ? - null : DocFragment.GetWsStyleName(cache, config, writingSystem); - - if (!string.IsNullOrEmpty(displayNameBase)) - { - // The calls to TryGetStyle() and AddStyle() need to be in the same lock. - lock (s_styleCollection) - { - if (s_styleCollection.TryGetStyle(config.Style, displayNameBase, out StyleElement existingStyle)) - { - uniqueDisplayName = existingStyle.Style.StyleId; - } - // If the style is not in the collection, then add it. - else - { - var wsString = WordStylesGenerator.GetWsString(writingSystem); - - // Get the style from the LcmStyleSheet, using the style name defined in the config. - if (!string.IsNullOrEmpty(config.Style)) - { - var wsId = cache.LanguageWritingSystemFactoryAccessor.GetWsFromStr(writingSystem); - Style style = WordStylesGenerator.GenerateCharacterStyleFromLcmStyleSheet(config.Style, wsId, propTable); - if (style == null) - { - // If we hit this assert, then we might end up referencing a style that - // does not get created. - Debug.Assert(false); - } - else - { - style.Append(new BasedOn() { Val = wsString }); - style.StyleId = displayNameBase; - style.StyleName.Val = style.StyleId; - bool wsIsRtl = IsWritingSystemRightToLeft(cache, wsId); - uniqueDisplayName = s_styleCollection.AddCharacterStyle(style, config.Style, style.StyleId, wsId, wsIsRtl); - } - } - // There is no style name defined in the config so generate a style that is identical to the writing system style - // except that it contains a display name that the user wants to see in the Word Styles. - // (example: "Reverse Abbreviation[lang='en']") - else - { - StyleElement rootElem = s_styleCollection.GetStyleElement(wsString); - // rootElem can be null, see LT-21981. - Style rootStyle = rootElem?.Style; - if (rootStyle != null) - { - Style basedOnStyle = WordStylesGenerator.GenerateBasedOnCharacterStyle(new Style(), wsString, displayNameBase); - if (basedOnStyle != null) - { - uniqueDisplayName = s_styleCollection.AddCharacterStyle(basedOnStyle, config.Style, basedOnStyle.StyleId, - rootElem.WritingSystemId, rootElem.WritingSystemIsRtl); - } - else - { - // If we hit this assert, then we might end up referencing a style that - // does not get created. - Debug.Assert(false, "Could not generate BasedOn character style " + displayNameBase); - } - } - else - { - // If we hit this assert, then we might end up referencing a style that - // does not get created. - Debug.Assert(false, "Could not create style for " + wsString); - } - } - } - run.Append(GenerateRunProperties(uniqueDisplayName)); - } - } - - // Add Between text, if it is not the first item. - if (!first && - config != null && - !string.IsNullOrEmpty(config.Between)) - { - var betweenRun = CreateBeforeAfterBetweenRun(config.Between, uniqueDisplayName); - WordFragment.DocBody.Append(betweenRun); - } - - // Add the run. - WordFragment.DocBody.AppendChild(run); - } } #endregion WordFragmentWriter class @@ -1088,16 +1003,39 @@ public void AddRun(LcmCache cache, ConfigurableDictionaryNode config, ReadOnlyPr * Content Generator Region */ #region ILcmContentGenerator functions to implement - public IFragment GenerateWsPrefixWithString(ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, + public IFragment GenerateWsPrefixWithString(ConfigurableDictionaryNode node, ConfiguredLcmGenerator.GeneratorSettings settings, bool displayAbbreviation, int wsId, IFragment content) { if (displayAbbreviation) { + // Get the unique display name to use in the run. + string uniqueDisplayName = null; + lock (_collectionLock) + { + string nodePath = CssGenerator.GetNodePath(node); + string wsAbbrevStyleName = WordStylesGenerator.WritingSystemStyleName; + string wsAbbrevNodePath = nodePath + wsAbbrevStyleName; + + if (s_styleCollection.TryGetCharacterStyle(wsAbbrevNodePath, wsId, out CharacterElement existingWsAbbrevStyle)) + { + uniqueDisplayName = existingWsAbbrevStyle.Style.StyleId; + } + // If the style is not in the collection, then add it. + else + { + s_styleCollection.TryGetCharacterStyle(nodePath, wsId, out CharacterElement existingStyle); + Debug.Assert(existingStyle != null); + var lastNodeUniqueName = existingStyle.UniqueDisplayName(); + uniqueDisplayName = s_styleCollection.AddSpecialCharacterStyle(wsAbbrevStyleName, + WordStylesGenerator.WritingSystemDisplayName, lastNodeUniqueName, node, wsAbbrevStyleName, wsId); + } + } + // Create the abbreviation run that uses the abbreviation style. // Note: Appending a space is similar to the code in CssGenerator.cs GenerateCssForWritingSystemPrefix() that adds // a space after the abbreviation. string abbrev = ((CoreWritingSystemDefinition)settings.Cache.WritingSystemFactory.get_EngineOrNull(wsId)).Abbreviation + " "; - var abbrevRun = CreateRun(abbrev, WordStylesGenerator.WritingSystemDisplayName); + var abbrevRun = CreateRun(abbrev, uniqueDisplayName); // We can't just prepend the abbreviation run because the content might already contain a before or between run. // The abbreviation run should go after the before or between run, but before the string run. @@ -1112,7 +1050,7 @@ public IFragment GenerateWsPrefixWithString(ConfigurableDictionaryNode config, C if (runProps != null) { RunStyle runStyle = runProps.OfType().FirstOrDefault(); - if (runStyle != null && runStyle.Val.ToString().StartsWith(WordStylesGenerator.BeforeAfterBetweenDisplayName)) + if (runStyle != null && runStyle.Val.ToString().Contains(WordStylesGenerator.BeforeAfterBetween)) { ((DocFragment)content).DocBody.InsertAfter(abbrevRun, firstRun); abbrevAdded = true; @@ -1125,9 +1063,6 @@ public IFragment GenerateWsPrefixWithString(ConfigurableDictionaryNode config, C { ((DocFragment)content).DocBody.PrependChild(abbrevRun); } - - // Add the abbreviation style to the collection (if not already added). - GetOrCreateCharacterStyle(WordStylesGenerator.WritingSystemStyleName, WordStylesGenerator.WritingSystemDisplayName, _propertyTable); } return content; @@ -1140,39 +1075,42 @@ public IFragment GenerateAudioLinkContent(ConfigurableDictionaryNode config, Con } public IFragment WriteProcessedObject(ConfigurableDictionaryNode config, bool isBlock, IFragment elementContent, string className) { - return WriteProcessedElementContent(elementContent, config); + var runs = ((DocFragment)elementContent)?.DocBody.OfType(); + // If there is only one run we can use it to get a writing system (needed for Before/After text). + int? wsId = null; + if (runs.Count() == 1) + { + var run = runs.First(); + var runElem = GetElementFromRun(run); + wsId = runElem.WritingSystemId; + } + + return WriteProcessedElementContent(elementContent, config, wsId); } public IFragment WriteProcessedCollection(ConfigurableDictionaryNode config, bool isBlock, IFragment elementContent, string className) { - return WriteProcessedElementContent(elementContent, config); + return WriteProcessedElementContent(elementContent, config, null); } - private IFragment WriteProcessedElementContent(IFragment elementContent, ConfigurableDictionaryNode config) + private IFragment WriteProcessedElementContent(IFragment elementContent, ConfigurableDictionaryNode config, int? wsId) { - // Check if the character style for the last run should be modified. - if (string.IsNullOrEmpty(config.Style) && !string.IsNullOrEmpty(config.Parent.Style) && - (config.Parent.StyleType != ConfigurableDictionaryNode.StyleTypes.Paragraph)) - { - AddRunStyle(elementContent, config.Parent.Style, config.Parent.DisplayLabel, false); - } - bool eachInAParagraph = config != null && config.DictionaryNodeOptions is IParaOption && ((IParaOption)(config.DictionaryNodeOptions)).DisplayEachInAParagraph; - string styleDisplayName = GetUniqueDisplayName(config, elementContent); - // Add Before text, if it is not going to be displayed in a paragraph. if (!eachInAParagraph && !string.IsNullOrEmpty(config.Before)) { - var beforeRun = CreateBeforeAfterBetweenRun(config.Before, styleDisplayName); + var beforeRun = wsId.HasValue ? CreateBeforeAfterBetweenRun(config, config.Before, wsId.Value) : + CreateDefaultBeforeAfterBetweenRun(config, config.Before); ((DocFragment)elementContent).DocBody.PrependChild(beforeRun); } // Add After text, if it is not going to be displayed in a paragraph. if (!eachInAParagraph && !string.IsNullOrEmpty(config.After)) { - var afterRun = CreateBeforeAfterBetweenRun(config.After, styleDisplayName); + var afterRun = wsId.HasValue ? CreateBeforeAfterBetweenRun(config, config.After, wsId.Value) : + CreateDefaultBeforeAfterBetweenRun(config, config.After); ((DocFragment)elementContent).DocBody.Append(afterRun); } @@ -1220,19 +1158,17 @@ config.DictionaryNodeOptions is DictionaryNodeGroupingOptions && } } - string styleDisplayName = GetUniqueDisplayName(config, childContent); - // Add Before text, if it is not going to be displayed in a paragraph. if (!eachInAParagraph && !string.IsNullOrEmpty(config.Before)) { - var beforeRun = CreateBeforeAfterBetweenRun(config.Before, styleDisplayName); + var beforeRun = CreateDefaultBeforeAfterBetweenRun(config, config.Before); groupData.DocBody.PrependChild(beforeRun); } // Add After text, if it is not going to be displayed in a paragraph. if (!eachInAParagraph && !string.IsNullOrEmpty(config.After)) { - var afterRun = CreateBeforeAfterBetweenRun(config.After, styleDisplayName); + var afterRun = CreateDefaultBeforeAfterBetweenRun(config, config.After); groupData.DocBody.Append(afterRun); } @@ -1252,8 +1188,53 @@ config.DictionaryNodeOptions is DictionaryNodeGroupingOptions && return groupData; } + /// + /// Iterate through the runs in the fragment when AddSenseData() is called. If any of the runs + /// are using a style that has the SensesSubentriesUniqueDisplayName property set, then we want + /// to change the run so that it uses the style built from the '.entry .senses .subentries' path + /// instead of the style that was built from the '.entry subentries' path. + /// + /// The root problem that this method is solving, is that '.entry .senses .subentries' uses the child + /// nodes from '.entry .subentries'. Since the properties of the child nodes are a combination of the + /// child node properties and all of its parent node properties, the cumulative properties can be + /// different for the two different node paths. While building the individual runs we do not know + /// where they are going to be used. It is not until we get in AddSenseData() that + /// we have enough information to figure out which style should be used for the runs. + /// + /// An additional complication is when we get in here for a style built on '.entry .subentries .senses'. + /// We only want to change to the style build from the '.entry .senses .subentries .senses' path + /// when we get in AddSenseDate() two times for the run. For this special case we set an attribute + /// on the run the first time we get in this method. The second time we get in here for the run, + /// change the unique name to what is stored in SensesSubentriesUniqueDisplayName. + /// + private void FixStylesForSensesSubentries(IFragment content) + { + foreach (WP.Run run in ((DocFragment)content).DocBody.Descendants()) + { + CharacterElement charElem = GetElementFromRun(run); + if (!string.IsNullOrEmpty(charElem.SensesSubentriesUniqueDisplayName)) + { + // If this node is '.entry .subentries .senses' or one of it's children, then we need + // to get in here twice for a run before we want to change the name. The first time in here + // we cannot distinguish if this should remain '.entry .subentries .senses' or be changed + // to '.entry .senses .subentries .senses'. If it needs to be changed we will get here + // a second time for the larger fragment that includes all children of '.entry .senses .subentries.'. + if (charElem.NodePath.StartsWith(".entry .subentries .senses") && !run.HasAttributes) + { + run.SetAttribute(new DocumentFormat.OpenXml.OpenXmlAttribute("EntrySubentriesSenses", null, "1")); + continue; + } + + SetUniqueDisplayName(run, charElem.SensesSubentriesUniqueDisplayName); + } + } + } + public IFragment AddSenseData(ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, IFragment senseNumberSpan, Guid ownerGuid, IFragment senseContent, bool first) { + FixStylesForSensesSubentries(senseContent); + FixStylesForSensesSubentries(senseNumberSpan); + var senseData = new DocFragment(); WP.Paragraph newPara = null; var senseNode = (DictionaryNodeSenseOptions)config?.DictionaryNodeOptions; @@ -1278,8 +1259,7 @@ public IFragment AddSenseData(ConfigurableDictionaryNode config, ConfiguredLcmGe !eachInAParagraph && !string.IsNullOrEmpty(config.Between)) { - string styleDisplayName = GetUniqueDisplayName(config, senseContent); - var betweenRun = CreateBeforeAfterBetweenRun(config.Between, styleDisplayName); + var betweenRun = CreateDefaultBeforeAfterBetweenRun(config, config.Between); senseData.DocBody.Append(betweenRun); } @@ -1313,13 +1293,6 @@ public IFragment AddSenseData(ConfigurableDictionaryNode config, ConfiguredLcmGe public IFragment AddCollectionItem(ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, bool isBlock, string collectionItemClass, IFragment content, bool first) { - // Add the style to all the runs in the content fragment. - if (!string.IsNullOrEmpty(config.Style) && - (config.StyleType != ConfigurableDictionaryNode.StyleTypes.Paragraph)) - { - AddRunStyle(content, config.Style, config.DisplayLabel, true); - } - var collData = new DocFragment(); WP.Paragraph newPara = null; bool eachInAParagraph = false; @@ -1338,8 +1311,7 @@ config.DictionaryNodeOptions is IParaOption && !eachInAParagraph && !string.IsNullOrEmpty(config.Between)) { - string styleDisplayName = GetUniqueDisplayName(config, content); - var betweenRun = CreateBeforeAfterBetweenRun(config.Between, styleDisplayName); + var betweenRun = CreateDefaultBeforeAfterBetweenRun(config, config.Between); ((DocFragment)collData).DocBody.Append(betweenRun); } @@ -1357,9 +1329,6 @@ config.DictionaryNodeOptions is IParaOption && public IFragment AddProperty(ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, string className, bool isBlockProperty, string content, string writingSystem) { var propFrag = new DocFragment(); - Run contentRun = null; - string styleDisplayName = null; - if (string.IsNullOrEmpty(content)) { // In this case, we should not generate the run or any before/after text for it. @@ -1368,27 +1337,23 @@ public IFragment AddProperty(ConfigurableDictionaryNode config, ConfiguredLcmGen // Create a run with the correct style. var writer = CreateWriter(propFrag); - ((WordFragmentWriter)writer).AddRun(Cache, config, settings.PropertyTable, writingSystem, true); + StartRun(writer, config, settings, writingSystem, true); // Add the content to the run. AddToRunContent(writer, content); - var currentRun = ((WordFragmentWriter)writer).WordFragment.GetLastRun(); - - // Get the run's styleDisplayName for use in before/after text runs. - if (currentRun.RunProperties != null) - styleDisplayName = currentRun.RunProperties.RunStyle?.Val; + var wsId = Cache.LanguageWritingSystemFactoryAccessor.GetWsFromStr(writingSystem); // Add Before text. if (!string.IsNullOrEmpty(config.Before)) { - var beforeRun = CreateBeforeAfterBetweenRun(config.Before, styleDisplayName); + var beforeRun = CreateBeforeAfterBetweenRun(config, config.Before, wsId); propFrag.DocBody.PrependChild(beforeRun); } // Add After text. if (!string.IsNullOrEmpty(config.After)) { - var afterRun = CreateBeforeAfterBetweenRun(config.After, styleDisplayName); + var afterRun = CreateBeforeAfterBetweenRun(config, config.After, wsId); propFrag.DocBody.Append(afterRun); } @@ -1429,12 +1394,40 @@ public void EndBiDiWrapper(IFragmentWriter writer) /// /// Add a new run to the writers WordFragment DocBody. /// - /// - /// public void StartRun(IFragmentWriter writer, ConfigurableDictionaryNode config, ConfiguredLcmGenerator.GeneratorSettings settings, string writingSystem, bool first) { - ((WordFragmentWriter)writer).AddRun(Cache, config, settings.PropertyTable, writingSystem, first); + var run = new WP.Run(); + var wsId = Cache.LanguageWritingSystemFactoryAccessor.GetWsFromStr(writingSystem); + string nodePath = CssGenerator.GetNodePath(config); + // The calls to 'TryGet' and 'Add' need to be in the same lock. + lock (_collectionLock) + { + string uniqueDisplayName = null; + if (s_styleCollection.TryGetCharacterStyle(nodePath, wsId, out CharacterElement existingStyle)) + { + uniqueDisplayName = existingStyle.Style.StyleId; + } + // If the style is not in the collection, then add it. + else + { + uniqueDisplayName = s_styleCollection.AddStyles(config, wsId); + } + run.Append(GenerateRunProperties(uniqueDisplayName)); + } + + // Add Between text, if it is not the first item. + if (!first && + config != null && + !string.IsNullOrEmpty(config.Between)) + { + var betweenRun = CreateBeforeAfterBetweenRun(config, config.Between, wsId); + ((WordFragmentWriter)writer).WordFragment.DocBody.Append(betweenRun); + } + + // Add the run. + ((WordFragmentWriter)writer).WordFragment.DocBody.Append(run); } + public void EndRun(IFragmentWriter writer) { // Ending the run should be a null op for word writer @@ -1451,7 +1444,7 @@ public void SetRunStyle(IFragmentWriter writer, ConfigurableDictionaryNode confi { if (!string.IsNullOrEmpty(runStyle)) { - AddRunStyle(((WordFragmentWriter)writer).WordFragment, runStyle, runStyle, false); + OverrideStyle(((WordFragmentWriter)writer).WordFragment, config, runStyle, writingSystem); } } public void StartLink(IFragmentWriter writer, ConfigurableDictionaryNode config, Guid destination) @@ -1654,11 +1647,8 @@ public void StartEntry(IFragmentWriter writer, ConfigurableDictionaryNode node, // needs to be added to the entry after the interruption. // Create the style for the entry. - var style = WordStylesGenerator.GenerateParagraphStyleFromLcmStyleSheet(node.Style, WordStylesGenerator.DefaultStyle, _propertyTable, out BulletInfo? bulletInfo); - style.StyleId = node.DisplayLabel; - style.StyleName.Val = style.StyleId; - AddParagraphBasedOnStyle(style, node, _propertyTable); - string uniqueDisplayName = s_styleCollection.AddParagraphStyle(style, node.Style, style.StyleId, bulletInfo); + ParagraphElement paraElem = s_styleCollection.AddParagraphStyle(node); + string uniqueDisplayName = paraElem.UniqueDisplayName(); // Create a new paragraph for the entry. DocFragment wordDoc = ((WordFragmentWriter)writer).WordFragment; @@ -1668,8 +1658,7 @@ public void StartEntry(IFragmentWriter writer, ConfigurableDictionaryNode node, // Create the 'continuation' style for the entry. This style will be the same as the style for the entry with the only // differences being that it does not contain the first line indenting or bullet info (since it is a continuation of the same entry). - var contStyle = WordStylesGenerator.GenerateContinuationStyle(style); - s_styleCollection.AddParagraphStyle(contStyle, node.Style, contStyle.StyleId, null); + s_styleCollection.AddParagraphContinuationStyle(paraElem); } public void AddEntryData(IFragmentWriter writer, List pieces) @@ -1731,14 +1720,14 @@ public void AddEntryData(IFragmentWriter writer, List("cache"); - var wsId = cache.LanguageWritingSystemFactoryAccessor.GetWsFromStr(senseNumberWs); - Style style = WordStylesGenerator.GenerateCharacterStyleFromLcmStyleSheet(numberStyleName, wsId, _propertyTable); - - style.Append(new BasedOn() { Val = wsString }); - style.StyleId = displayNameBase; - style.StyleName.Val = style.StyleId; - bool wsIsRtl = IsWritingSystemRightToLeft(cache, wsId); - uniqueDisplayName = s_styleCollection.AddCharacterStyle(style, numberStyleName, style.StyleId, wsId, wsIsRtl); + var lastNodeUniqueName = s_styleCollection.AddStyles(senseConfigNode, wsId); + uniqueDisplayName = s_styleCollection.AddSpecialCharacterStyle(numberStyleName, + WordStylesGenerator.SenseNumberDisplayName, lastNodeUniqueName, senseConfigNode, numberStyleName, wsId); } } @@ -1914,7 +1896,7 @@ public IFragment GenerateSenseNumber(ConfigurableDictionaryNode senseConfigNode, // Add characters before the number. if (!string.IsNullOrEmpty(beforeNumber)) { - var beforeRun = CreateBeforeAfterBetweenRun(beforeNumber, uniqueDisplayName); + var beforeRun = CreateBeforeAfterBetweenRun(senseConfigNode, beforeNumber, wsId, numberStyleName); senseNum.DocBody.AppendChild(beforeRun); } @@ -1928,12 +1910,13 @@ public IFragment GenerateSenseNumber(ConfigurableDictionaryNode senseConfigNode, // Add characters after the number. if (!string.IsNullOrEmpty(afterNumber)) { - var afterRun = CreateBeforeAfterBetweenRun(afterNumber, uniqueDisplayName); + var afterRun = CreateBeforeAfterBetweenRun(senseConfigNode, afterNumber, wsId, numberStyleName); senseNum.DocBody.AppendChild(afterRun); } return senseNum; } + public IFragment AddLexReferences(ConfigurableDictionaryNode config, bool generateLexType, IFragment lexTypeContent, string className, IFragment referencesContent, bool typeBefore) { var fragment = new DocFragment(); @@ -1966,8 +1949,7 @@ public void BetweenCrossReferenceType(IFragment content, ConfigurableDictionaryN // Add Between text if it is not the first item in the collection. if (!firstItem && !string.IsNullOrEmpty(node.Between)) { - string styleDisplayName = GetUniqueDisplayName(node, content); - var betweenRun = CreateBeforeAfterBetweenRun(node.Between, styleDisplayName); + var betweenRun = CreateDefaultBeforeAfterBetweenRun(node, node.Between); ((DocFragment)content).DocBody.PrependChild(betweenRun); } } @@ -1976,12 +1958,11 @@ public IFragment WriteProcessedSenses(ConfigurableDictionaryNode config, bool is { var senseOptions = config?.DictionaryNodeOptions as DictionaryNodeSenseOptions; bool eachInAParagraph = senseOptions?.DisplayEachSenseInAParagraph ?? false; - string styleDisplayName = GetUniqueDisplayName(config, sharedGramInfo); // Add Before text for the senses if they were not displayed in separate paragraphs. if (!eachInAParagraph && !string.IsNullOrEmpty(config.Before)) { - var beforeRun = CreateBeforeAfterBetweenRun(config.Before, styleDisplayName); + var beforeRun = CreateDefaultBeforeAfterBetweenRun(config, config.Before); ((DocFragment)sharedGramInfo).DocBody.PrependChild(beforeRun); } @@ -1991,7 +1972,7 @@ public IFragment WriteProcessedSenses(ConfigurableDictionaryNode config, bool is // Add After text for the senses if they were not displayed in separate paragraphs. if (!eachInAParagraph && !string.IsNullOrEmpty(config.After)) { - var afterRun = CreateBeforeAfterBetweenRun(config.After, styleDisplayName); + var afterRun = CreateDefaultBeforeAfterBetweenRun(config, config.After); ((DocFragment)sharedGramInfo).DocBody.Append(afterRun); } @@ -2020,183 +2001,8 @@ public IFragment GenerateVideoLinkContent(ConfigurableDictionaryNode config, str #region ILcmStylesGenerator functions to implement public void AddGlobalStyles(DictionaryConfigurationModel model, ReadOnlyPropertyTable propertyTable) { - var cache = propertyTable.GetValue("cache"); - var propStyleSheet = FontHeightAdjuster.StyleSheetFromPropertyTable(propertyTable); - - // Generate Character Styles - // - - var beforeAfterBetweenStyle = WordStylesGenerator.GenerateBeforeAfterBetweenCharacterStyle(propertyTable, out int wsId); - if (beforeAfterBetweenStyle != null) - { - bool wsIsRtl = IsWritingSystemRightToLeft(cache, wsId); - s_styleCollection.AddCharacterStyle(beforeAfterBetweenStyle, - WordStylesGenerator.BeforeAfterBetweenStyleName, beforeAfterBetweenStyle.StyleId, wsId, wsIsRtl); - } - - List writingSystemStyles = WordStylesGenerator.GenerateWritingSystemsCharacterStyles(propertyTable); - if (writingSystemStyles != null) - { - foreach (StyleElement elem in writingSystemStyles) - { - s_styleCollection.AddCharacterStyle(elem.Style, elem.Style.StyleId, elem.Style.StyleId, - elem.WritingSystemId, elem.WritingSystemIsRtl); - } - } - - // Generate Paragraph styles. - // Note: the order of generation is important since we want based on names to use the display names, not the style names. - // - BulletInfo? bulletInfo = null; - var normStyle = WordStylesGenerator.GenerateNormalParagraphStyle(propertyTable, out bulletInfo); - if (normStyle != null) - { - s_styleCollection.AddParagraphStyle(normStyle, WordStylesGenerator.NormalParagraphStyleName, normStyle.StyleId, bulletInfo); - } - - var pageHeaderStyle = WordStylesGenerator.GeneratePageHeaderStyle(normStyle); - // Intentionally re-using the bulletInfo from Normal. - s_styleCollection.AddParagraphStyle(pageHeaderStyle, WordStylesGenerator.PageHeaderStyleName, pageHeaderStyle.StyleId, bulletInfo); - - var mainStyle = WordStylesGenerator.GenerateMainEntryParagraphStyle(propertyTable, model, out ConfigurableDictionaryNode node, out bulletInfo); - if (mainStyle != null) - { - AddParagraphBasedOnStyle(mainStyle, node, propertyTable); - s_styleCollection.AddParagraphStyle(mainStyle, node.Style, mainStyle.StyleId, bulletInfo); - } - - var headStyle = WordStylesGenerator.GenerateLetterHeaderParagraphStyle(propertyTable, out bulletInfo); - if (headStyle != null) - { - AddParagraphBasedOnStyle(headStyle, null, _propertyTable); - s_styleCollection.AddParagraphStyle(headStyle, WordStylesGenerator.LetterHeadingStyleName, headStyle.StyleId, bulletInfo); - } - - // TODO: in openxml, will links be plaintext by default? - //WordStylesGenerator.MakeLinksLookLikePlainText(_styleSheet); - } - - /// - /// Intended to add the basedOn styles for paragraph styles, not character styles. - /// This method is recursive. It walks up the basedOn styles and adds them - /// until we get to a style that is already in the collection. - /// If the basedOn style is already in the collection then the style.BasedOn value will - /// get updated to the unique display name. - /// - /// The style to add it's basedOn style. (It's BasedOn value might get modified.) - /// Can be null, but if it is then the only option for getting a basedOnStyle is from - /// the style, not the parent node. - private void AddParagraphBasedOnStyle(Style style, ConfigurableDictionaryNode node, ReadOnlyPropertyTable propertyTable) - { - Debug.Assert(style.Type == StyleValues.Paragraph); - - // No based on styles for pictures. - if (style.StyleId == WordStylesGenerator.PictureAndCaptionTextframeStyle) - return; - - string basedOnStyleName = null; - string basedOnDisplayName = null; - ConfigurableDictionaryNode parentNode = null; - if (style.BasedOn != null && !string.IsNullOrEmpty(style.BasedOn.Val)) - { - basedOnStyleName = style.BasedOn.Val; - } - - // If there is no basedOn style, or the basedOn style is "Normal" then use the - // parent node's style for the basedOn style. - if (string.IsNullOrEmpty(basedOnStyleName) || - basedOnStyleName == WordStylesGenerator.NormalParagraphStyleName) - { - if (node?.Parent != null && !string.IsNullOrEmpty(node.Parent.Style) && - (node.Parent.StyleType == ConfigurableDictionaryNode.StyleTypes.Paragraph)) - { - parentNode = node.Parent; - basedOnStyleName = node.Parent.Style; - basedOnDisplayName = node.Parent.DisplayLabel; - } - } - - if (!string.IsNullOrEmpty(basedOnStyleName)) - { - bool continuationStyle = style.StyleId.Value.EndsWith(WordStylesGenerator.EntryStyleContinue); - // Currently this method does not work (and should not be used) for continuation styles. The problem is - // that the basedOn name of the regular style has already been changed to the display name. We would - // need a way to get the FLEX name from the display name. - if (continuationStyle) - { - Debug.Assert(!continuationStyle, "Currently this method does not support continuation styles."); - return; - } - - lock (s_styleCollection) - { - // If the basedOn style already exists, then update the reference to the basedOn styles unique name. - if (s_styleCollection.TryGetParagraphStyle(basedOnStyleName, out Style basedOnStyle)) - { - style.BasedOn.Val = basedOnStyle.StyleId; - } - // Else if the basedOn style does NOT already exist, then create the basedOn style, if needed add - // it's basedOn style, then add this basedOn style to the collection. - else - { - basedOnStyle = WordStylesGenerator.GenerateParagraphStyleFromLcmStyleSheet(basedOnStyleName, - WordStylesGenerator.DefaultStyle, propertyTable,out BulletInfo? bulletInfo); - // Check if the style is based on itself. This happens with the 'Normal' style and could possibly happen with others. - bool basedOnIsDifferent = basedOnStyle.BasedOn?.Val != null && basedOnStyle.StyleId != basedOnStyle.BasedOn?.Val; - - if (!string.IsNullOrEmpty(basedOnDisplayName)) - { - basedOnStyle.StyleId = basedOnDisplayName; - basedOnStyle.StyleName.Val = basedOnStyle.StyleId; - style.BasedOn.Val = basedOnStyle.StyleId; - } - - if (basedOnIsDifferent) - { - // If the parentNode is not null then the basedOnStyle came from the parentNode. - // If the parentNode is null then the basedOnStyle came from the style.BasedOn.Val and - // we should pass null to AddParagraphBasedOnStyle since no node is associated with the basedOnStyle. - AddParagraphBasedOnStyle(basedOnStyle, parentNode, propertyTable); - } - s_styleCollection.AddParagraphStyle(basedOnStyle, basedOnStyleName, basedOnStyle.StyleId, bulletInfo); - } - } - } - } - - /// - /// Gets the style from the dictionary (if it is in the dictionary). If not in the - /// dictionary then create the Word style from the LCM Style Sheet and add it to the dictionary. - /// - /// Returns null if it fails to find or create the character style. - private static Style GetOrCreateCharacterStyle(string nodeStyleName, string displayNameBase, ReadOnlyPropertyTable propertyTable) - { - Style retStyle = null; - // The calls to TryGetStyle() and AddStyle() need to be in the same lock. - lock (s_styleCollection) - { - if (s_styleCollection.TryGetStyle(nodeStyleName, displayNameBase, out StyleElement styleElem)) - { - retStyle = styleElem.Style; - if (retStyle.Type != StyleValues.Character) - { - return null; - } - } - else - { - retStyle = WordStylesGenerator.GenerateCharacterStyleFromLcmStyleSheet(nodeStyleName, WordStylesGenerator.DefaultStyle, propertyTable); - if (retStyle == null || retStyle.Type != StyleValues.Character) - { - return null; - } - - var cache = propertyTable.GetValue("cache"); - bool wsIsRtl = IsWritingSystemRightToLeft(cache, WordStylesGenerator.DefaultStyle); - s_styleCollection.AddCharacterStyle(retStyle, nodeStyleName, displayNameBase, WordStylesGenerator.DefaultStyle, wsIsRtl); - } - } - return retStyle; + s_styleCollection.AddGlobalCharacterStyles(); + s_styleCollection.AddGlobalParagraphStyles(); } /// @@ -2207,32 +2013,11 @@ private static Style GetOrCreateCharacterStyle(string nodeStyleName, string disp /// public string AddStyles(ConfigurableDictionaryNode node) { - // The css className isn't important for the Word export. - var className = $".{CssGenerator.GetClassAttributeForConfig(node)}"; - - Style style = WordStylesGenerator.GenerateParagraphStyleFromConfigurationNode(node, _propertyTable, out BulletInfo? bulletInfo); - - if (style == null) - return className; - - if (style.Type == StyleValues.Paragraph) + if(WordStylesGenerator.IsParagraphStyle(node.Style, _propertyTable)) { - lock (s_styleCollection) - { - if (!s_styleCollection.TryGetStyle(node.Style, style.StyleId, out StyleElement _)) - { - AddParagraphBasedOnStyle(style, node, _propertyTable); - string oldName = style.StyleId; - string newName = s_styleCollection.AddParagraphStyle(style, node.Style, style.StyleId, bulletInfo); - Debug.Assert(oldName == newName, "Not expecting the name for a paragraph style to ever change!"); - } - } + s_styleCollection.AddParagraphStyle(node); } - return className; - } - public void Init(ReadOnlyPropertyTable propertyTable) - { - _propertyTable = propertyTable; + return $".{CssGenerator.GetClassAttributeForConfig(node)}"; } #endregion ILcmStylesGenerator functions to implement @@ -2481,42 +2266,52 @@ internal static WP.Run CreateRun(string runText, string styleDisplayName) return run; } + internal WP.Run CreateDefaultBeforeAfterBetweenRun(ConfigurableDictionaryNode node, string text) + { + int wsId = WordStylesGenerator.DefaultStyle; + return CreateBeforeAfterBetweenRun(node, text, wsId); + } + /// /// Creates a BeforeAfterBetween run using the text and style provided. /// /// Text for the run. - /// The style name to base on, or the complete style name. /// The BeforeAfterBetween run. - internal static WP.Run CreateBeforeAfterBetweenRun(string text, string styleDisplayName) + internal WP.Run CreateBeforeAfterBetweenRun(ConfigurableDictionaryNode node, + string text, int wsId, string nodePathAdditions = null) { + // Get the unique display name to use in the run. string uniqueDisplayName = null; - // If there is no styleDisplayName then use the default BefAftBet display name. - if (string.IsNullOrEmpty(styleDisplayName)) + lock (_collectionLock) { - uniqueDisplayName = WordStylesGenerator.BeforeAfterBetweenDisplayName; - } - // If the styleDisplayName is already a BefAftBet style, then don't create a new style. - else if (styleDisplayName.StartsWith(WordStylesGenerator.BeforeAfterBetweenDisplayName)) - { - uniqueDisplayName = styleDisplayName; - } - // Create a new BefAftBet style similar to the default BefAftBet style but based on styleDisplayName. - else - { - // If the styleDisplayName is a language tag, then no need to add the separator. - string displayNameBaseCombined = WordStylesGenerator.BeforeAfterBetweenDisplayName; - displayNameBaseCombined += styleDisplayName.StartsWith(WordStylesGenerator.LangTagPre) ? - (styleDisplayName) : (WordStylesGenerator.StyleSeparator + styleDisplayName); - - // Get the BeforeAfterBetween style. - StyleElement befAftElem = s_styleCollection.GetStyleElement(WordStylesGenerator.BeforeAfterBetweenDisplayName); - - Style basedOnStyle = WordStylesGenerator.GenerateBasedOnCharacterStyle(befAftElem.Style, styleDisplayName, displayNameBaseCombined); - if (basedOnStyle != null) + string nodePath = CssGenerator.GetNodePath(node); + string befAftBetStyleName = WordStylesGenerator.BeforeAfterBetweenStyleName; + string befAftBetNodePath = nodePath + nodePathAdditions + befAftBetStyleName; + if (s_styleCollection.TryGetCharacterStyle(befAftBetNodePath, wsId, out CharacterElement existingBefAftBetStyle)) + { + uniqueDisplayName = existingBefAftBetStyle.Style.StyleId; + } + // If the style is not in the collection, then add it. + else { - uniqueDisplayName = s_styleCollection.AddCharacterStyle(basedOnStyle, WordStylesGenerator.BeforeAfterBetweenStyleName, - basedOnStyle.StyleId, befAftElem.WritingSystemId, befAftElem.WritingSystemIsRtl); + // Get the parent element and unique display name. + string parentUniqueDisplayName = null; + if (s_styleCollection.TryGetCharacterStyle(nodePath + nodePathAdditions, wsId, out CharacterElement parentElem)) + { + parentUniqueDisplayName = parentElem.Style.StyleId; + } + // If the parent element is not in the collection, then add it. + else + { + Debug.Assert(string.IsNullOrEmpty(nodePathAdditions)); // If there are additions then we are expecting an element with additions to already exist. + parentUniqueDisplayName = s_styleCollection.AddStyles(node, wsId); + parentElem = s_styleCollection.GetCharacterElement(parentUniqueDisplayName); + } + + string displayNameBaseCombined = parentElem.DisplayNameBase + WordStylesGenerator.BeforeAfterBetween; + uniqueDisplayName = s_styleCollection.AddSpecialCharacterStyle(befAftBetStyleName, + displayNameBaseCombined, parentUniqueDisplayName, node, nodePathAdditions + befAftBetStyleName, wsId); } } @@ -2543,139 +2338,72 @@ internal static WP.Run CreateBeforeAfterBetweenRun(string text, string styleDisp } /// - /// Worker method for AddRunStyle(), not intended to be called from other places. If it is - /// then the the pre-checks on 'style' should be added to this method. + /// Overrides the style used in the last run in the fragment. The override style will be + /// based on the current style that is being used by the run. /// - private void AddRunStyle_Worker(WP.Run run, string nodeStyleName, string displayNameBase) + /// The fragment containing the run that should have its styles overidden. + /// The FLEX style to apply to the runs in the fragment. + private void OverrideStyle(IFragment frag, ConfigurableDictionaryNode node, string nodeStyleName, string wsString) { - // Use the writing system that is already used in the run. - int wsId = WordStylesGenerator.DefaultStyle; - bool wsIsRtl = false; - var styleElem = GetStyleElementFromRun(run); - if (styleElem != null) - { - wsId = styleElem.WritingSystemId; - wsIsRtl = styleElem.WritingSystemIsRtl; - } - else - { - var cache = _propertyTable.GetValue("cache"); - wsIsRtl = IsWritingSystemRightToLeft(cache, wsId); - } - - Style rootStyle = WordStylesGenerator.GenerateWordStyleFromLcmStyleSheet(nodeStyleName, wsId, _propertyTable, out BulletInfo? _); - if (rootStyle == null || rootStyle.Type != StyleValues.Character) + string sDefaultTextStyle = "Default Paragraph Characters"; + if (string.IsNullOrEmpty(nodeStyleName) || nodeStyleName.StartsWith(sDefaultTextStyle)) { return; } - rootStyle.StyleId = displayNameBase; - rootStyle.StyleName.Val = rootStyle.StyleId; - if (run.RunProperties != null) + List runList = ((DocFragment)frag).DocBody.Elements().ToList(); + if (runList.Any()) { - if (run.RunProperties.Descendants().Any()) + var run = runList.Last(); + int wsId = WordStylesGenerator.DefaultStyle; + if (!string.IsNullOrEmpty(wsString)) + { + wsId = Cache.LanguageWritingSystemFactoryAccessor.GetWsFromStr(wsString); + } + else { - string currentRunStyle = run.RunProperties.Descendants().Last().Val; - // If the run has a current style, then make the new style based on the current style. - if (!string.IsNullOrEmpty(currentRunStyle)) + // Try to use the writing system that is already used in the run. + var characterElem = GetElementFromRun(run); + if (characterElem != null) { - // If the currentRun has one of the default global character styles then return. We do not - // want to create a new style based on these. - if (currentRunStyle.StartsWith(WordStylesGenerator.BeforeAfterBetweenDisplayName) || - currentRunStyle == WordStylesGenerator.SenseNumberDisplayName || - currentRunStyle == WordStylesGenerator.WritingSystemDisplayName) - { - return; - } + wsId = characterElem.WritingSystemId; + } + } - // If the current style is a language tag, then no need to add the separator. - string displayNameBaseCombined = currentRunStyle.StartsWith(WordStylesGenerator.LangTagPre) ? - (displayNameBase + currentRunStyle) : (displayNameBase + WordStylesGenerator.StyleSeparator + currentRunStyle); + // The calls to 'TryGet' and 'Add' need to be in the same lock. + lock (_collectionLock) + { + string nodePath = CssGenerator.GetNodePath(node); + s_styleCollection.TryGetCharacterStyle(nodePath, wsId, out CharacterElement rootStyle); + Debug.Assert(rootStyle != null); + string rootNodeUniqueName = rootStyle.UniqueDisplayName(); + string rootDisplayName = rootStyle.DisplayNameBase; - // The calls to TryGetStyle() and AddStyle() need to be in the same lock. - lock (s_styleCollection) - { - if (s_styleCollection.TryGetStyle(nodeStyleName, displayNameBaseCombined, out StyleElement existingStyle)) - { - ResetRunProperties(run, existingStyle.Style.StyleId); - } - else - { - // Don't create a new style if the current style already has the same root. - int separatorIndex = currentRunStyle.IndexOf(WordStylesGenerator.StyleSeparator); - separatorIndex = separatorIndex != -1 ? separatorIndex : currentRunStyle.IndexOf(WordStylesGenerator.LangTagPre); - bool hasSameRoot = separatorIndex == -1 ? currentRunStyle.Equals(displayNameBase) : - currentRunStyle.Substring(0, separatorIndex).Equals(displayNameBase); - if (hasSameRoot) - { - return; - } + // If the currentRun has one of the default global character styles then return. We do not + // want to create a new style based on these. + if (rootDisplayName.StartsWith(WordStylesGenerator.SenseNumberDisplayName) || + rootDisplayName.StartsWith(WordStylesGenerator.WritingSystemDisplayName) || + rootDisplayName.EndsWith(WordStylesGenerator.BeforeAfterBetween)) + { + return; + } - Style basedOnStyle = WordStylesGenerator.GenerateBasedOnCharacterStyle(rootStyle, currentRunStyle, displayNameBaseCombined); - if (basedOnStyle != null) - { - string uniqueDisplayName = s_styleCollection.AddCharacterStyle(basedOnStyle, nodeStyleName, basedOnStyle.StyleId, wsId, wsIsRtl); - ResetRunProperties(run, uniqueDisplayName); - } - } - } + string overrideNodePath = CssGenerator.GetNodePath(node) + nodeStyleName; + if (s_styleCollection.TryGetCharacterStyle(overrideNodePath, wsId, out CharacterElement overrideStyle)) + { + ResetRunProperties(run, overrideStyle.Style.StyleId); } else { - string uniqueDisplayName = s_styleCollection.AddCharacterStyle(rootStyle, nodeStyleName, displayNameBase, wsId, wsIsRtl); + string overrideDisplayName = rootDisplayName + WordStylesGenerator.StyleSeparator + nodeStyleName; + string uniqueDisplayName = s_styleCollection.AddSpecialCharacterStyle(nodeStyleName, + overrideDisplayName, rootNodeUniqueName, node, nodeStyleName, wsId); ResetRunProperties(run, uniqueDisplayName); } } - else - { - string uniqueDisplayName = s_styleCollection.AddCharacterStyle(rootStyle, nodeStyleName, displayNameBase, wsId, wsIsRtl); - ResetRunProperties(run, uniqueDisplayName); - } - } - else - { - string uniqueDisplayName = s_styleCollection.AddCharacterStyle(rootStyle, nodeStyleName, displayNameBase, wsId, wsIsRtl); - WP.RunProperties runProps = GenerateRunProperties(uniqueDisplayName); - // Prepend RunProperties so it appears before any text elements contained in the run - run.PrependChild(runProps); } } - /// - /// Adds the specified style to either all of the runs contained in the fragment or the last - /// run in the fragment. If a run does not contain RunProperties or a RunStyle then just add - /// the specified style. Otherwise create a new style for the run that uses the specified - /// style but makes it BasedOn the current style that is being used by the run. - /// - /// The fragment containing the runs that should have the new style applied. - /// The FLEX style to apply to the runs in the fragment. - /// The style name to display in Word. - /// If true then apply the style to all runs in the fragment. - /// If false then only apply the style to the last run in the fragment. - public void AddRunStyle(IFragment frag, string nodeStyleName, string displayNameBase, bool allRuns) - { - string sDefaultTextStyle = "Default Paragraph Characters"; - if (string.IsNullOrEmpty(nodeStyleName) || nodeStyleName.StartsWith(sDefaultTextStyle) || string.IsNullOrEmpty(displayNameBase)) - { - return; - } - - if (allRuns) - { - foreach (WP.Run run in ((DocFragment)frag).DocBody.Elements()) - { - AddRunStyle_Worker(run, nodeStyleName, displayNameBase); - } - } - else - { - List runList = ((DocFragment)frag).DocBody.Elements().ToList(); - if (runList.Any()) - { - AddRunStyle_Worker(runList.Last(), nodeStyleName, displayNameBase); - } - } - } /// /// Word does not support certain element types being nested inside Paragraphs (Paragraphs & Tables). @@ -2738,54 +2466,19 @@ private bool EndParagraph(WP.Paragraph paragraph, ConfigurableDictionaryNode nod // Add the style. if (!string.IsNullOrEmpty(node.Style)) { - // The calls to TryGetStyle() and AddStyle() need to be in the same lock. - lock(s_styleCollection) - { - BulletInfo? bulletInfo = null; - string uniqueDisplayName = null; + ParagraphElement paraElem = s_styleCollection.AddParagraphStyle(node); + string uniqueDisplayName = paraElem.UniqueDisplayName(); - // Try to get the continuation style. - if (continuationParagraph) - { - if (s_styleCollection.TryGetStyle(node.Style, node.DisplayLabel + WordStylesGenerator.EntryStyleContinue, - out StyleElement contStyleElem)) - { - bulletInfo = contStyleElem.BulletInfo; - uniqueDisplayName = contStyleElem.Style.StyleId; - } - } - - if (string.IsNullOrEmpty(uniqueDisplayName)) - { - // Try to get the regular style. - Style style = null; - if (s_styleCollection.TryGetStyle(node.Style, node.DisplayLabel, out StyleElement styleElem)) - { - style = styleElem.Style; - bulletInfo = styleElem.BulletInfo; - uniqueDisplayName = style.StyleId; - } - // Add the regular style. - else - { - style = WordStylesGenerator.GenerateParagraphStyleFromLcmStyleSheet(node.Style, WordStylesGenerator.DefaultStyle, _propertyTable, out bulletInfo); - style.StyleId = node.DisplayLabel; - style.StyleName.Val = style.StyleId; - AddParagraphBasedOnStyle(style, node, _propertyTable); - uniqueDisplayName = s_styleCollection.AddParagraphStyle(style, node.Style, style.StyleId, bulletInfo); - } - - // Add the continuation style. - if (continuationParagraph) - { - var contStyle = WordStylesGenerator.GenerateContinuationStyle(style); - uniqueDisplayName = s_styleCollection.AddParagraphStyle(contStyle, node.Style, contStyle.StyleId, null); - } - } - WP.ParagraphProperties paragraphProps = - new WP.ParagraphProperties(new ParagraphStyleId() { Val = uniqueDisplayName }); - paragraph.PrependChild(paragraphProps); + // Add the continuation style. + if (continuationParagraph) + { + ParagraphElement contElem = s_styleCollection.AddParagraphContinuationStyle(paraElem); + uniqueDisplayName = contElem.UniqueDisplayName(); } + + WP.ParagraphProperties paragraphProps = + new WP.ParagraphProperties(new ParagraphStyleId() { Val = uniqueDisplayName }); + paragraph.PrependChild(paragraphProps); } return true; } @@ -2805,7 +2498,8 @@ private void AddBulletAndNumberingData(IFragment elementContent, ConfigurableDic eachInAParagraph) { // Get the StyleElement. - if (s_styleCollection.TryGetStyle(node.Style, node.DisplayLabel, out StyleElement styleElem)) + string nodePath = CssGenerator.GetNodePath(node); + if (s_styleCollection.TryGetParagraphStyle(nodePath, out ParagraphElement styleElem)) { // This style uses bullet or numbering. if (styleElem.BulletInfo.HasValue) @@ -2815,7 +2509,7 @@ private void AddBulletAndNumberingData(IFragment elementContent, ConfigurableDic int? numberingFirstNumUniqueId = null; // We are potentially adding data to the StyleElement so it needs to be in a lock. - lock (s_styleCollection) + lock (_collectionLock) { // If the StyleElement does not already have the unique id then generate one. // Note: This number can be the same for all list items on all the lists associated with @@ -2848,9 +2542,9 @@ private void AddBulletAndNumberingData(IFragment elementContent, ConfigurableDic if (paraProps != null) { // Only add the uniqueId to paragraphs with the correct style. There could - // be paragraphs with different styles. + // be paragraphs with different styles (ie. pictures, continuation styles...). var paraStyle = paraProps.Elements().FirstOrDefault(); - if (paraStyle != null && paraStyle.Val == node.DisplayLabel) + if (paraStyle != null && paraStyle.Val == styleElem.UniqueDisplayName()) { int uniqueId = styleElem.BulletAndNumberingUniqueId.Value; @@ -2878,7 +2572,7 @@ private void AddBulletAndNumberingData(IFragment elementContent, ConfigurableDic /// /// Contains the bullet and numbering data. /// Part of the Word doc where bullet and numbering data is stored. - internal static void GenerateBulletAndNumberingData(StyleElement styleElement, NumberingDefinitionsPart numberingPart) + internal static void GenerateBulletAndNumberingData(ParagraphElement styleElement, NumberingDefinitionsPart numberingPart) { if (!styleElement.BulletInfo.HasValue) { @@ -3075,9 +2769,8 @@ public static RunProperties GenerateRunProperties(string uniqueDisplayName) var runProp = new RunProperties(new RunStyle() { Val = uniqueDisplayName }); if (IsBidi) { - StyleElement styleElem = s_styleCollection.GetStyleElement(uniqueDisplayName); - Debug.Assert(styleElem != null); - if (styleElem.WritingSystemIsRtl) + CharacterElement elem = s_styleCollection.GetCharacterElement(uniqueDisplayName); + if (elem.WritingSystemIsRtl) { runProp.RightToLeftText = new RightToLeftText(); } @@ -3086,87 +2779,33 @@ public static RunProperties GenerateRunProperties(string uniqueDisplayName) } /// - /// Iterate through the runs in the fragment looking for the style that - /// most closely matches the node.DisplayLabel. + /// Gets the unique display name out of a run. /// - private string GetUniqueDisplayName(ConfigurableDictionaryNode node, IFragment content) + /// The name, or null if the run does not contain the information. + public static string GetUniqueDisplayName(Run run) { - Debug.Assert(!string.IsNullOrEmpty(node.DisplayLabel), "Not expecting a node without a DisplayLabel."); - string endRunStyle = null; - string beginRunStyle = null; - var runs = ((DocFragment)content)?.DocBody.OfType(); - if (runs != null) - { - foreach (var run in runs) - { - string runStyle = run.RunProperties?.RunStyle?.Val; - if (runStyle != null) - { - // Remove the language tag and any appended numbers. - string runName = runStyle; - int langTagIndex = runName.IndexOf(WordStylesGenerator.LangTagPre); - if (langTagIndex != -1) - { - runName = runName.Substring(0, langTagIndex); - } - runName = runName.TrimEnd('0', '1', '2', '3', '4', '5', '6', '7', '8', '9'); - - // This is the common case: DisplayLabel followed by a possible integer and a language tag. - // Definition (or Gloss)[lang='en'] or - // Definition (or Gloss)2[lang='en'] - // If we find this, then there is no need to look further. This is the style we want. - if (runName == node.DisplayLabel) - { - return runStyle; - } - - // The second preference is a style that ends with the DisplayLabel. - // Strong : Example Sentence[lang='es'] or - // Strong : Example Sentence3[lang='es'] - if (endRunStyle == null && runName.EndsWith(node.DisplayLabel)) - { - // In this case don't use the complete runStyle. We want the base style, not - // a possible override applied to a specific run. - // Return just "Example Sentence[lang='es']" or "Example Sentence3[lang='es']" - endRunStyle = runStyle.Substring(runStyle.IndexOf(node.DisplayLabel)); - } - - // The third preference is a style that begins with the DisplayLabel. - // Grammatical Info.2 : Category Info.[lang='en'] - if (beginRunStyle == null && endRunStyle == null && runStyle.StartsWith(node.DisplayLabel)) - { - // In this case return the complete RunStyle. - // Return "Grammatical Info.2 : Category Info.[lang='en']" - beginRunStyle = runStyle; - } - } - } - } - // Default to returning the DisplayLabel if we don't have anything else. - // This is a common case for nodes that are collections. - return endRunStyle ?? beginRunStyle ?? node.DisplayLabel; + return run?.RunProperties?.RunStyle?.Val; } /// - /// Gets the unique display name out of a run. + /// Sets the unique display name in a run. /// - /// The name, or null if the run does not contain the information. - public string GetUniqueDisplayName(Run run) + public static void SetUniqueDisplayName(Run run, string newUniqueDisplayName) { - return run?.RunProperties?.RunStyle?.Val; + run.RunProperties.RunStyle.Val = newUniqueDisplayName; } /// /// Get the StyleElement associated with a run. /// /// The StyleElement, or null if the run does not contain the information. - public StyleElement GetStyleElementFromRun(Run run) + internal static CharacterElement GetElementFromRun(Run run) { string uniqueDisplayName = GetUniqueDisplayName(run); if (uniqueDisplayName == null) // Runs containing a 'Drawing' will not have RunProperties. return null; - StyleElement elem = s_styleCollection.GetStyleElement(uniqueDisplayName); + CharacterElement elem = s_styleCollection.GetCharacterElement(uniqueDisplayName); Debug.Assert(elem != null); // I don't think we should ever not find a styleElement. return elem; @@ -3200,7 +2839,8 @@ public static string GetFirstGuidewordStyle(DocFragment frag, DictionaryConfigur // Find the first run style with a value that begins with the guideword style. foreach (RunStyle runStyle in frag.DocBody.Descendants()) { - if (runStyle.Val.Value.StartsWith(guidewordStyle)) + if (runStyle.Val.Value.StartsWith(guidewordStyle) && + !runStyle.Val.Value.Contains(WordStylesGenerator.BeforeAfterBetween)) { return runStyle.Val.Value; } diff --git a/Src/xWorks/WordStyleCollection.cs b/Src/xWorks/WordStyleCollection.cs index 0f2f4a399c..3ba70dba73 100644 --- a/Src/xWorks/WordStyleCollection.cs +++ b/Src/xWorks/WordStyleCollection.cs @@ -3,55 +3,80 @@ // (http://www.gnu.org/licenses/lgpl-2.1.html) using DocumentFormat.OpenXml.Wordprocessing; +using SIL.LCModel; using SIL.LCModel.DomainServices; using System.Collections.Generic; using System.Linq; +using XCore; namespace SIL.FieldWorks.XWorks { public class WordStyleCollection { - // The dictionary Key is the displayNameBase without the added int that uniquely identifies the different Styles. - // Examples of Key: - // Definition (or Gloss)[lang='en'] - // Homograph-Number:Referenced Sense Headword[lang='fr'] - // The dictionary value is the list of StyleElements (ie. styles) that share the same displayNameBase. The - // style.StyleId values will be the unique display names that are based on the displayNameBase. - // Example: - // Key: Definition (or Gloss)[lang='en'] - // style.StyleId values: Definition (or Gloss)[lang='en'] (the first style does not have a '1' added to the unique name) - // Definition (or Gloss)2[lang='en'] - // Definition (or Gloss)3[lang='en'] - // - private Dictionary> styleDictionary = new Dictionary>(); + private ReadOnlyPropertyTable _propertyTable; + private object _collectionLock; + + // The Key is the displayNameBase without the added int or wsId that uniquely identifies the different Styles. + // The dictionary Value is the list of elements that share the same displayNameBase. + private Dictionary> characterStyles = new Dictionary>(); + private Dictionary> paragraphStyles = new Dictionary>(); + + // The Key is a path containing Non-Unique names for all the nodes from the root up to and + // including the 'node' of interest. + private Dictionary uniquePathToCharElement = new Dictionary(); + private Dictionary uniquePathToParaElement = new Dictionary(); + + // The Key is the unique display name for the element (ie. element.Style.StyleId) + private Dictionary uniqueNameToCharElement = new Dictionary(); + private int bulletAndNumberingUniqueIdCounter = 1; private int pictureUniqueIdCounter = 1; + public WordStyleCollection(ReadOnlyPropertyTable propertyTable, object collectionLock) + { + _propertyTable = propertyTable; + _collectionLock = collectionLock; + } + /// - /// Returns a single list containing all of the Styles. + /// Returns a single list containing all the used CharacterElements. /// - public List GetStyleElements() + internal List GetUsedCharacterElements() { - lock(styleDictionary) - { - // Get an enumerator to the flattened list of all StyleElements. - var enumerator = styleDictionary.Values.SelectMany(x => x); - // Create a single list of all the StyleElements. - return enumerator.ToList(); - } + // Get an enumerator to the flattened list of all StyleElements. + var enumerator = characterStyles.Values.SelectMany(x => x); + // Create a single list of all the StyleElements. + return enumerator.Where(x => x.Used).ToList(); } /// - /// Finds a StyleElement from the uniqueDisplayName. + /// Returns a single list containing all the ParagraphElements. + /// + internal List GetParagraphElements() + { + // Get an enumerator to the flattened list of all StyleElements. + var enumerator = paragraphStyles.Values.SelectMany(x => x); + // Create a single list of all the StyleElements. + return enumerator.ToList(); + } + + /// + /// Finds a CharacterElement from the uniqueDisplayName. /// /// The style name that uniquely identifies a style. - public StyleElement GetStyleElement(string uniqueDisplayName) + internal CharacterElement GetCharacterElement(string uniqueDisplayName) { - lock (styleDictionary) - { - return styleDictionary.Values.SelectMany(x => x) - .FirstOrDefault(styleElement => styleElement.Style.StyleId == uniqueDisplayName); - } + return uniqueNameToCharElement[uniqueDisplayName]; + } + + /// + /// Finds a ParagraphElement from the uniqueDisplayName. + /// + /// The style name that uniquely identifies a style. + internal ParagraphElement GetParagraphElement(string uniqueDisplayName) + { + return paragraphStyles.Values.SelectMany(x => x) + .FirstOrDefault(element => element.UniqueDisplayName() == uniqueDisplayName); } /// @@ -59,9 +84,13 @@ public StyleElement GetStyleElement(string uniqueDisplayName) /// public void Clear() { - lock(styleDictionary) + lock(_collectionLock) { - styleDictionary.Clear(); + characterStyles.Clear(); + paragraphStyles.Clear(); + uniquePathToCharElement.Clear(); + uniquePathToParaElement.Clear(); + uniqueNameToCharElement.Clear(); bulletAndNumberingUniqueIdCounter = 1; pictureUniqueIdCounter = 1; } @@ -70,184 +99,513 @@ public void Clear() /// /// Check if a style is already in the collection. /// NOTE: To support multiple threads this method must be called in the same lock that also - /// acts on the result (ie. calling AddStyle()). + /// acts on the result (ie. calling AddStyles()). /// - /// The unique FLEX style name, typically comes from node.Style. - /// The key value in the styleDictionary. - /// Returns the found style element, or returns null if not found. + /// Returns the found character style element, or returns null if not found. /// True if found, else false. - public bool TryGetStyle(string nodeStyleName, string displayNameBase, out StyleElement styleElem) + internal bool TryGetCharacterStyle(string nodePath, int wsId, out CharacterElement elem) { - lock (styleDictionary) - { - if (styleDictionary.TryGetValue(displayNameBase, out List stylesWithSameDisplayNameBase)) - { - foreach (var elem in stylesWithSameDisplayNameBase) - { - if (elem.NodeStyleName == nodeStyleName) - { - styleElem = elem; - return true; - } - } - } - } - styleElem = null; - return false; + return uniquePathToCharElement.TryGetValue(CharacterElement.GetUniquePath(nodePath, wsId), out elem); } /// - /// Check if a paragraph style already exists in any of the Lists in the entire collection. If it - /// does then return the first one that is found (there could be more than one). - /// NOTE: For most cases use TryGetStyle() instead of this method. This method allows us to re-use - /// existing styles for based-on values. The undesirable alternative would be to create a new style - /// that uses the FLEX name for the display name. + /// Check if a style is already in the collection. /// NOTE: To support multiple threads this method must be called in the same lock that also - /// acts on the result (ie. calling AddStyle()). + /// acts on the result (ie. calling AddParagraphStyle()). /// - /// The unique FLEX style name, typically comes from node.Style. - /// Returns the found Style, or returns null if not found. + /// Node path to the paragraph style. + /// Returns the found paragraph element, or returns null if not found. /// True if found, else false. - public bool TryGetParagraphStyle(string nodeStyleName, out Style style) + /// + internal bool TryGetParagraphStyle(string nodePath, out ParagraphElement paraElem) { - lock (styleDictionary) + return uniquePathToParaElement.TryGetValue(nodePath, out paraElem); + } + + /// + /// Worker method that can result in many styles getting created (both + /// character and paragraph styles). + /// + /// The uniqueDisplayName for the character style that is created. + private string AddStyle(string styleName, string displayNameBase, string nodePath, + string parentUniqueDisplayName, int wsId, bool pictureStyle, string sensesSubentriesUniqueDisplayName = null) + { + // Use a special name for a 'Headword' that is a '.subentries .headword' so that the + // '.subentries .headword' is not used as a guideword. + if (displayNameBase == WordStylesGenerator.HeadwordDisplayName && + nodePath.Contains(WordStylesGenerator.SubentriesClassName) && + nodePath.Contains(WordStylesGenerator.HeadwordClassName)) { - foreach (var keyValuePair in styleDictionary) + displayNameBase = WordStylesGenerator.SubentriesHeadword; + } + + lock(_collectionLock) + { + if (TryGetCharacterStyle(nodePath, wsId, out CharacterElement existingStyle)) + { + return existingStyle.UniqueDisplayName(); + } + + // If this is the first style with this root then add it to the Dictionary. + if (!characterStyles.TryGetValue(displayNameBase, out List stylesWithSameDisplayNameBase)) + { + stylesWithSameDisplayNameBase = new List(); + characterStyles.Add(displayNameBase, stylesWithSameDisplayNameBase); + } + + bool processParagraphStyle = ((nodePath != WordStylesGenerator.RootCharacterNodePath) && + (wsId == WordStylesGenerator.DefaultStyle) && + WordStylesGenerator.IsParagraphStyle(styleName, _propertyTable)); + + // Get a unique display name. + var cache = _propertyTable.GetValue("cache"); + var wsString = cache.WritingSystemFactory.GetStrFromWs(wsId); + bool retry; + string uniqueDisplayName; + // Append a number to all except the first. + int uniqueNumber = stylesWithSameDisplayNameBase.Where(x => x.WritingSystemId == wsId).Count() +1; + do { - foreach (var elem in keyValuePair.Value) + retry = false; + uniqueDisplayName = displayNameBase; + if (processParagraphStyle) { - if (elem.NodeStyleName == nodeStyleName && - elem.Style.Type == StyleValues.Paragraph) - { - style = elem.Style; - return true; - } + uniqueDisplayName += WordStylesGenerator.LinkedCharacterStyle; } + if (uniqueNumber != 1) + { + uniqueDisplayName += uniqueNumber.ToString(); + } + if (wsId != WordStylesGenerator.DefaultStyle) + { + uniqueDisplayName += WordStylesGenerator.GetWsString(wsString); + } + + // There could be multiple custom fields with names that only differ by an appended number. Make sure our + // final unique display name is truly unique. + if (uniqueNameToCharElement.ContainsKey(uniqueDisplayName)) + { + uniqueNumber++; + retry = true; + } + } while (retry); + + // Get the parent properties so we can include them in the style. + StyleRunProperties parentRunProps = null; + if(!string.IsNullOrEmpty(parentUniqueDisplayName)) + { + CharacterElement parentElem = GetCharacterElement(parentUniqueDisplayName); + var parentStyle = parentElem.Style; + parentRunProps = parentStyle.GetFirstChild(); } - } - style = null; - return false; + Style style = null; + if (!string.IsNullOrEmpty(styleName)) + { + // Get the style + style = WordStylesGenerator.GenerateCharacterStyleFromLcmStyleSheet(styleName, wsId, _propertyTable, parentRunProps); + } + if (style == null) + { + style = new Style(); + style.Type = StyleValues.Character; + style.Append(parentRunProps.CloneNode(true)); + } + + // Update the style name. + WordStylesGenerator.SetStyleName(style, uniqueDisplayName); + + // Update the BasedOn value. + CharacterElement basedOnElem = null; + if (nodePath != WordStylesGenerator.RootCharacterNodePath) + { + TryGetCharacterStyle(WordStylesGenerator.RootCharacterNodePath, wsId, out CharacterElement normalElem); + basedOnElem = normalElem.Redirect ?? normalElem; + WordStylesGenerator.SetBasedOn(style, basedOnElem.UniqueDisplayName()); + } + + // Add the character style element to the collection. + bool wsIsRtl = LcmWordGenerator.IsWritingSystemRightToLeft(cache, wsId); + var charElement = new CharacterElement(displayNameBase, style, uniqueNumber, + nodePath, wsId, wsIsRtl, basedOnElem, sensesSubentriesUniqueDisplayName); + stylesWithSameDisplayNameBase.Add(charElement); + uniquePathToCharElement.Add(CharacterElement.GetUniquePath(nodePath, wsId), charElement); + uniqueNameToCharElement.Add(uniqueDisplayName, charElement); + + // If the wsId is not the default and this is not the Letter Heading style, then add the default. + // When we are done creating styles and want to re-use styles, the defaults are what we want to + // try and use first. + if (wsId != WordStylesGenerator.DefaultStyle && + nodePath != WordStylesGenerator.LetterHeadingNodePath) + { + AddStyle(styleName, displayNameBase, nodePath, parentUniqueDisplayName, + WordStylesGenerator.DefaultStyle, pictureStyle, sensesSubentriesUniqueDisplayName); + } + + // Additional handling for paragraph style. + if (processParagraphStyle) + { + Style paraStyle = null; + BulletInfo? bulletInfo = null; + if (pictureStyle) + { + paraStyle = WordStylesGenerator.GenerateParagraphStyleFromPictureOptions(); + } + else + { + paraStyle = WordStylesGenerator.GenerateParagraphStyleFromLcmStyleSheet(styleName, _propertyTable, out bulletInfo); + } + + if (paraStyle == null) + { + paraStyle = new Style(); + paraStyle.Type = StyleValues.Paragraph; + } + AddParagraphStyle(paraStyle, bulletInfo, charElement); + } + + return uniqueDisplayName; + } } /// - /// Adds a character style to the collection. - /// If a style with the identical style information is already in the collection then just return + /// Adds all the necessary character and paragraph styles. This includes styles for all + /// nodes in the nodePath. + /// If a style for the nodePath is already in the collection then just return /// the unique name. - /// If the identical style is not already in the collection then generate a unique name, - /// update the style name values (with the unique name), and return the unique name. /// - /// The style to add to the collection. (It's name might get modified.) - /// The unique FLEX style name, typically comes from node.Style. - /// The base name that will be used to create the unique display name - /// for the style. The root of this name typically comes from the node.DisplayLabel but it can have - /// additional information if it is based on other styles and/or has a writing system. + /// The node to build styles from. /// The writing system id associated with this style. - /// True if the writing system is right to left. - /// The unique display name. The name that should be referenced in a Run. - public string AddCharacterStyle(Style style, string nodeStyleName, string displayNameBase, int wsId, bool wsIsRtl) + /// The unique display name that should be referenced in a Run. + public string AddStyles(ConfigurableDictionaryNode node, int wsId) { - return AddStyle(style, nodeStyleName, displayNameBase, null, wsId, wsIsRtl); + // Create a list of nodes for the path, from root to 'node'. + List nodes = new List(); + var pathNode = node; + while (pathNode != null) + { + nodes.Add(pathNode); + pathNode = pathNode.Parent; + } + nodes.Reverse(); + + // Generate the unique name and style for each node (starting from the root node). + string uniqueDisplayName = ""; + string sensesSubentriesUniqueDisplayName = ""; + string parentUniqueDisplayName = null; + string parentSensesSubentryUniqueDisplayName = null; + string nodePath = null; + string sensesSubentriesNodePath = null; + bool buildSenseSubentryRules = false; + for (int ii = 0; ii < nodes.Count; ii++) + { + var workingNode = nodes[ii]; + var workingDisplayName = workingNode.DisplayLabel; + var workingStyleName = workingNode.Style; + nodePath += $".{CssGenerator.GetClassAttributeForConfig(workingNode)} "; + bool pictureStyle = workingNode.DictionaryNodeOptions is DictionaryNodePictureOptions; + + // If this node is '.subentries' and the next node is '.mainentrysubentries', + // then we need to build the same set of rules twice for the child nodes, once + // for ".entry .subentries" and once for ".entry .senses .subentries". We do + // this so that later, if AddSenseData() is called, we can switch to the style + // built on the '.entry .senses .subentries' path. + // We only need to build the two sets for the children. The node + // '.entry .senses .subentries' does NOT need a second set of rules. It's rules + // will get created through the normal execution of this method. + if (nodes[ii].DisplayLabel == "Subentries" && (ii + 1 < nodes.Count) && + nodes[ii + 1].DisplayLabel == "MainEntrySubentries") + { + // Build the styles for the '.entry .senses .subentries' path. + var mainEntryNode = nodes[ii - 1]; + var sensesNode = mainEntryNode.Children.First(child => child.DisplayLabel == "Senses"); + var sensesSubentriesNode = sensesNode.Children.First(child => child.DisplayLabel == "Subentries"); + parentSensesSubentryUniqueDisplayName = AddStyles(sensesSubentriesNode, wsId); + sensesSubentriesNodePath = CssGenerator.GetNodePath(sensesSubentriesNode); + } + + if (workingDisplayName == "MainEntrySubentries") + { + buildSenseSubentryRules = true; + continue; + } + + if (buildSenseSubentryRules) + { + sensesSubentriesNodePath += $".{CssGenerator.GetClassAttributeForConfig(workingNode)} "; + sensesSubentriesUniqueDisplayName = AddStyle(workingStyleName, workingDisplayName, + sensesSubentriesNodePath, parentSensesSubentryUniqueDisplayName, wsId, pictureStyle); + parentSensesSubentryUniqueDisplayName = sensesSubentriesUniqueDisplayName; + } + uniqueDisplayName = AddStyle(workingStyleName, workingDisplayName, + nodePath, parentUniqueDisplayName, wsId, pictureStyle, sensesSubentriesUniqueDisplayName); + parentUniqueDisplayName = uniqueDisplayName; + } + return uniqueDisplayName; } /// - /// Adds a paragraph style to the collection. - /// If a style with the identical style information is already in the collection then just return - /// the unique name. - /// If the identical style is not already in the collection then generate a unique name, - /// update the style name values (with the unique name), and return the unique name. + /// Adds character styles that have additional data added to the nodePath. /// - /// The style to add to the collection. (It's name might get modified.) - /// The unique FLEX style name, typically comes from node.Style. - /// The base name that will be used to create the unique display name - /// for the style. The root of this name typically comes from the node.DisplayLabel but it can have - /// additional information if it is based on other styles and/or has a writing system. - /// Bullet and Numbering info used by some paragraph styles. - /// The unique display name. - public string AddParagraphStyle(Style style, string nodeStyleName, string displayNameBase, BulletInfo? bulletInfo) + /// The unique display name that should be referenced in a Run. + public string AddSpecialCharacterStyle(string styleName, string displayName, string parentUniqueDisplayName, + ConfigurableDictionaryNode node, string nodePathAdditions, int wsId) { - return AddStyle(style, nodeStyleName, displayNameBase, bulletInfo, WordStylesGenerator.DefaultStyle, false); + string nodePath = CssGenerator.GetNodePath(node) + nodePathAdditions; + return AddStyle(styleName, displayName, nodePath, parentUniqueDisplayName, wsId, false); + } + + internal void AddGlobalCharacterStyles() + { + var cache = _propertyTable.GetValue("cache"); + + // Add the Normal default character style. + AddStyle(WordStylesGenerator.NormalParagraphStyleName, WordStylesGenerator.RootCharacterDisplayName, + WordStylesGenerator.RootCharacterNodePath, null, WordStylesGenerator.DefaultStyle, false); + + // Add the Normal writing system styles. + foreach (var aws in cache.ServiceLocator.WritingSystems.AllWritingSystems) + { + AddStyle(WordStylesGenerator.NormalParagraphStyleName, WordStylesGenerator.RootCharacterDisplayName, + WordStylesGenerator.RootCharacterNodePath, null, aws.Handle, false); + } + + // Set the redirects for the Normal styles so BasedOn values can initially be set to the correct values + // and not need to be changed. + RedirectCharacterElements(); + + // Mark the normal styles as 'used' if they are not going to be redirected. + characterStyles.TryGetValue(WordStylesGenerator.RootCharacterDisplayName, out var elements); + foreach (var elem in elements) + { + if (elem.Redirect == null) + { + elem.Used = true; + } + } + + // Add the Letter Heading style. + var wsId = cache.ServiceLocator.WritingSystems.DefaultVernacularWritingSystem.Handle; + var letterHeadUniqueName = AddStyle(WordStylesGenerator.LetterHeadingStyleName, WordStylesGenerator.LetterHeadingDisplayName, + WordStylesGenerator.LetterHeadingNodePath, null, wsId, false); + GetCharacterElement(letterHeadUniqueName).Used = true; } /// - /// Adds a style to the collection. - /// If a style with the identical style information is already in the collection then just return - /// the unique name. - /// If the identical style is not already in the collection then generate a unique name, - /// update the style name values (with the unique name), and return the unique name. + /// Redirect character elements to other character elements that have identical properties. + /// Limit the redirection to elements with the same wsId or the defaultId. + /// The results of the redirection will be used to reduce the number of styles created in Word. /// - /// The style to add to the collection. (It's name might get modified.) - /// The unique FLEX style name, typically comes from node.Style. - /// The base name that will be used to create the unique display name - /// for the style. The root of this name typically comes from the node.DisplayLabel but it can have - /// additional information if it is based on other styles and/or has a writing system. - /// Bullet and Numbering info used by some paragraph styles. Not used for character styles. - /// The writing system id associated with this style. - /// True if the writing system is right to left. - /// The unique display name. The name that should be referenced in a Run. - public string AddStyle(Style style, string nodeStyleName, string displayNameBase, BulletInfo? bulletInfo, int wsId, bool wsIsRtl) + internal void RedirectCharacterElements() { - lock (styleDictionary) + foreach (var kvPair in characterStyles) { - if (styleDictionary.TryGetValue(displayNameBase, out List stylesWithSameDisplayNameBase)) + string DisplayNameBase = kvPair.Key; + List stylesWithSameDisplayNameBase = kvPair.Value; + + // First redirect all the default styles. + List defaultElements = stylesWithSameDisplayNameBase.Where(elem => + elem.WritingSystemId == WordStylesGenerator.DefaultStyle).ToList(); + + // Iterate through the elements, starting with the second, to see if it is the same + // as any of the elements preceding it. + for (int currentElem = 1; currentElem < defaultElements.Count; currentElem++) { - if (TryGetStyle(nodeStyleName, displayNameBase, out StyleElement existingStyle)) + // Iterate through the preceding elements to check if they have the same properties. + for (int precedingElem = 0; precedingElem < currentElem; precedingElem++) { - return existingStyle.Style.StyleId; + // If the preceding element is already re-directed then there is no need to compare the current + // element to this preceding element, since it is the same as one of its preceding elements that we would + // have already checked. + if (defaultElements[precedingElem].Redirect == null) + { + if (defaultElements[currentElem].Style.Descendants().First().OuterXml + .Equals(defaultElements[precedingElem].Style.Descendants().First().OuterXml)) + { + // Properties are the same, redirect the later element to the earlier one. + defaultElements[currentElem].Redirect = defaultElements[precedingElem]; + break; + } + } } } - // Else this is the first style with this root. Add it to the Dictionary. - else - { - stylesWithSameDisplayNameBase = new List(); - styleDictionary.Add(displayNameBase, stylesWithSameDisplayNameBase); - } - // Get a unique display name. - string uniqueDisplayName = displayNameBase; - // Append a number to all except the first. - int styleCount = stylesWithSameDisplayNameBase.Count; - if (styleCount > 0) + // Second redirect all the non-default styles. First try to redirect to a default style, second + // try to redirect to a non-default style with the same ws. + List nonDefaultElements = stylesWithSameDisplayNameBase.Where(elem => + elem.WritingSystemId != WordStylesGenerator.DefaultStyle).ToList(); + + // Iterate through the non-default elements, to see if it is the same as any of the default elements + // or the same as any of the non-default elements with the same WS that proceed it. + for (int currentElem = 0; currentElem < nonDefaultElements.Count; currentElem++) { - int separatorIndex = uniqueDisplayName.IndexOf(WordStylesGenerator.StyleSeparator); - separatorIndex = separatorIndex != -1 ? separatorIndex : uniqueDisplayName.IndexOf(WordStylesGenerator.LangTagPre); - // Append the number before the basedOn information. - // Note: We do not want to append the number to the end of the uniqueDisplayName if - // there is basedOn information because that could result in the name not being - // unique. (ex. The '2' in the unique name, "name : basedOn2" could then apply to the - // complete name, "name : basedOn" or just the basedOn name, "basedOn2". - if (separatorIndex != -1) + // Check if the current element is the same as any of the default elements. + // + // There is no need to check the default styles if the style the current element is based on is not also + // re-directed to the default style. + if (nonDefaultElements[currentElem].NodePath == WordStylesGenerator.RootCharacterNodePath || + nonDefaultElements[currentElem].BasedOnElement.WritingSystemId == WordStylesGenerator.DefaultStyle) { - uniqueDisplayName = uniqueDisplayName.Substring(0, separatorIndex) + - (styleCount + 1).ToString() + - uniqueDisplayName.Substring(separatorIndex); + foreach (var defaultElem in defaultElements) + { + // If the default element is already re-directed then there is no need to compare the current + // element to this default element, since it is the same as one of its preceding elements that we would + // have already checked. + if (defaultElem.Redirect == null) + { + if (nonDefaultElements[currentElem].Style.Descendants().First().OuterXml + .Equals(defaultElem.Style.Descendants().First().OuterXml)) + { + // Properties are the same, redirect to the default element. + nonDefaultElements[currentElem].Redirect = defaultElem; + break; + } + } + } } - // No basedOn information, append the number to the end. - else + + // Check if the current element is the same as any of the preceding elements with the same ws. + if (nonDefaultElements[currentElem].Redirect == null) { - uniqueDisplayName += (styleCount + 1).ToString(); + // Iterate through the preceding elements to check if they have the same properties. + for (int precedingElem = 0; precedingElem < currentElem; precedingElem++) + { + // If the preceding element is already re-directed then there is no need to compare the current + // element to this preceding element, since it is the same as a preceding element that we would + // have already checked. + if (nonDefaultElements[precedingElem].Redirect == null) + { + if (nonDefaultElements[currentElem].WritingSystemId == nonDefaultElements[precedingElem].WritingSystemId && + nonDefaultElements[currentElem].Style.Descendants().First().OuterXml + .Equals(nonDefaultElements[precedingElem].Style.Descendants().First().OuterXml)) + { + // Properties are the same, redirect the later element to the earlier one. + nonDefaultElements[currentElem].Redirect = nonDefaultElements[precedingElem]; + break; + } + } + } } } + } + } - // Update the style name. - style.StyleId = uniqueDisplayName; - if (style.StyleName == null) + /// + /// Adds the paragraph style and the linked character style. + /// + /// >The unique display name that should be referenced in a Paragraph. + internal ParagraphElement AddParagraphStyle(ConfigurableDictionaryNode node) + { + // Adding the styles for the DefaultStyle, will result in the paragraph style being added. + string charUniqueDispName = AddStyles(node, WordStylesGenerator.DefaultStyle); + var charElem = uniqueNameToCharElement[charUniqueDispName]; + return charElem.LinkedParagraphElement; + } + + /// + /// Worker method to add the paragraph style and link it to the associated character style. + /// + /// The associated character element. + private void AddParagraphStyle(Style paraStyle, BulletInfo? bulletInfo, CharacterElement charElem) + { + lock (_collectionLock) + { + // Update the paragraph style name. + string uniqueDisplayName = charElem.UniqueNumber == 1 ? charElem.DisplayNameBase : charElem.DisplayNameBase + charElem.UniqueNumber.ToString(); + WordStylesGenerator.SetStyleName(paraStyle, uniqueDisplayName); + + // Update the BasedOn value. + if (charElem.NodePath != WordStylesGenerator.RootCharacterNodePath) { - style.StyleName = new StyleName() { Val = style.StyleId }; + TryGetParagraphStyle(WordStylesGenerator.NormalParagraphNodePath, out ParagraphElement normalParaElem); + WordStylesGenerator.SetBasedOn(paraStyle, normalParaElem.UniqueDisplayName()); } - else + + // Add the paragraph element to the collection. + var paraElem = new ParagraphElement(charElem.DisplayNameBase, paraStyle, charElem.UniqueNumber, charElem.NodePath, bulletInfo); + AddParagraphElement(paraElem); + + // Link the paragraph and character styles. + paraElem.LinkedCharacterElement = charElem; + charElem.LinkedParagraphElement = paraElem; + paraElem.Style.Append(new LinkedStyle() { Val = charElem.UniqueDisplayName() }); + charElem.Style.Append(new LinkedStyle() { Val = paraElem.UniqueDisplayName() }); + + // Set the paragraph and character styles to used. + charElem.Used = true; + paraElem.Used = true; + } + } + + private void AddParagraphElement(ParagraphElement paraElem) + { + // If this is the first style with this base, then add it to the Dictionary. + if (!paragraphStyles.TryGetValue(paraElem.DisplayNameBase, out List paraStylesWithSameDisplayNameBase)) + { + paraStylesWithSameDisplayNameBase = new List(); + paragraphStyles.Add(paraElem.DisplayNameBase, paraStylesWithSameDisplayNameBase); + } + + // Add the paragraph element to the collection. + paraStylesWithSameDisplayNameBase.Add(paraElem); + uniquePathToParaElement[paraElem.NodePath] = paraElem; + } + + /// + /// Creates a paragraph continuation element. + /// + /// The element used to build the continuation element. + /// + internal ParagraphElement AddParagraphContinuationStyle(ParagraphElement paraElem) + { + lock (_collectionLock) + { + if (paraElem.ContinuationElement != null) { - style.StyleName.Val = style.StyleId; + return paraElem.ContinuationElement; } - // Add the style element to the collection. - var styleElement = new StyleElement(nodeStyleName, style, bulletInfo, wsId, wsIsRtl); - stylesWithSameDisplayNameBase.Add(styleElement); + var contStyle = WordStylesGenerator.GenerateContinuationStyle(paraElem.Style); - return uniqueDisplayName; + // Add the continuation element to the collection. + var contElem = new ParagraphElement(paraElem.DisplayNameBase + WordStylesGenerator.EntryStyleContinue, contStyle, paraElem.UniqueNumber, + paraElem.NodePath + WordStylesGenerator.EntryStyleContinue, null); + string uniqueDisplayName = contElem.UniqueNumber == 1 ? contElem.DisplayNameBase : contElem.DisplayNameBase + contElem.UniqueNumber.ToString(); + WordStylesGenerator.SetStyleName(contElem.Style, uniqueDisplayName); + AddParagraphElement(contElem); + paraElem.ContinuationElement = contElem; + contElem.Used = true; + return contElem; } } + internal void AddGlobalParagraphStyles() + { + // Normal style + var normStyle = WordStylesGenerator.GenerateParagraphStyleFromLcmStyleSheet(WordStylesGenerator.NormalParagraphStyleName, + _propertyTable, out BulletInfo? bulletInfo); + var normElem = new ParagraphElement(WordStylesGenerator.NormalParagraphDisplayName, + normStyle, 1, WordStylesGenerator.NormalParagraphNodePath, bulletInfo); + AddParagraphElement(normElem); + + // Page Header Style + TryGetCharacterStyle(WordStylesGenerator.RootCharacterNodePath, WordStylesGenerator.DefaultStyle, out CharacterElement rootElem); + var pageHeaderStyle = WordStylesGenerator.GeneratePageHeaderStyle(normStyle, rootElem.Style); + // Intentionally re-using the bulletInfo from Normal. + var pageHeaderElem = new ParagraphElement(WordStylesGenerator.PageHeaderDisplayName, + pageHeaderStyle, 1, WordStylesGenerator.PageHeaderNodePath, bulletInfo); + AddParagraphElement(pageHeaderElem); + + // Letter Header Style + var headStyle = WordStylesGenerator.GenerateParagraphStyleFromLcmStyleSheet(WordStylesGenerator.LetterHeadingStyleName, + _propertyTable, out bulletInfo); + WordStylesGenerator.SetStyleName(headStyle, WordStylesGenerator.LetterHeadingDisplayName); + WordStylesGenerator.SetBasedOn(headStyle, WordStylesGenerator.NormalParagraphDisplayName); + var headElem = new ParagraphElement(WordStylesGenerator.LetterHeadingDisplayName, + headStyle, 1, WordStylesGenerator.LetterHeadingNodePath, bulletInfo); + AddParagraphElement(headElem); + } + /// /// Returns a unique id that is used for bullet and numbering in paragraph styles. /// @@ -255,7 +613,7 @@ public int GetNewBulletAndNumberingUniqueId { get { - lock(styleDictionary) + lock (_collectionLock) { return bulletAndNumberingUniqueIdCounter++; } @@ -269,7 +627,7 @@ public int GetAndIncrementPictureUniqueIdCount { get { - lock (styleDictionary) + lock (_collectionLock) { return pictureUniqueIdCounter++; } @@ -277,39 +635,49 @@ public int GetAndIncrementPictureUniqueIdCount } } - // WordStyleCollection dictionary values. - public class StyleElement + internal class CharacterElement : BaseElement { - /// The unmodified FLEX style name. Typically comes from node.Style. Can be null. - /// The style with it's styleId set to the uniqueDisplayName. - /// Examples of uniqueDisplayName: - /// Definition (or Gloss)[lang='en'] - /// Definition (or Gloss)2[lang='en'] - /// Grammatical Info.2 : Category Info.[lang='en'] - /// Subentries : Grammatical Info.2 : Category Info.[lang='en'] - /// - /// Bullet and Numbering info used by some paragraph styles. Not used for character styles. /// The writing system id associated with this style. /// True if the writing system is right to left. - internal StyleElement(string nodeStyleName, Style style, BulletInfo? bulletInfo, int wsId, bool wsIsRtl) + /// The element this elements style is based on. + /// The unique display name for the associated senses subentries element. + internal CharacterElement(string displayNameBase, Style style, int uniqueNumber, + string nodePath, int wsId, bool wsIsRtl, CharacterElement basedOnElem, string sensesSubentriesUniqueDisplayName = null) : + base(displayNameBase, style, uniqueNumber, nodePath) { - this.NodeStyleName = nodeStyleName; - this.Style = style; this.WritingSystemId = wsId; this.WritingSystemIsRtl = wsIsRtl; - this.BulletInfo = bulletInfo; - NumberingFirstNumUniqueIds = new List(); + this.BasedOnElement = basedOnElem; + this.SensesSubentriesUniqueDisplayName = sensesSubentriesUniqueDisplayName; } - internal string NodeStyleName { get; } - internal Style Style { get; } internal int WritingSystemId { get; } internal bool WritingSystemIsRtl { get; } + internal string SensesSubentriesUniqueDisplayName { get; set; } + internal CharacterElement Redirect { get; set; } + internal CharacterElement BasedOnElement { get; } + internal ParagraphElement LinkedParagraphElement { get; set; } + internal static string GetUniquePath(string nodePath, int wsId) + { + return nodePath + wsId; + } + } + + internal class ParagraphElement : BaseElement + { + /// Bullet and Numbering info used by some paragraph styles. + internal ParagraphElement(string displayNameBase, Style paraStyle, int uniqueNumber, string nodePath, BulletInfo? bulletInfo) : + base(displayNameBase, paraStyle, uniqueNumber, nodePath) + { + BulletInfo = bulletInfo; + NumberingFirstNumUniqueIds = new List(); + } /// - /// Bullet and Numbering info used by some (not all) paragraph styles. Not used - /// for character styles. + /// Bullet and Numbering info used by some (not all) paragraph styles. /// internal BulletInfo? BulletInfo { get; } + internal CharacterElement LinkedCharacterElement { get; set; } + internal ParagraphElement ContinuationElement { get; set; } /// /// Unique id for this style that can be used for all bullet list items, and @@ -323,4 +691,33 @@ internal StyleElement(string nodeStyleName, Style style, BulletInfo? bulletInfo, /// internal List NumberingFirstNumUniqueIds { get; set; } } -} + + internal abstract class BaseElement + { + /// The unmodified FLEX node display name from node.DisplayLabel. + /// Does not include language tag or appended number. + /// The style with it's styleId set to the uniqueDisplayName. + /// Examples of uniqueDisplayName: + /// Definition (or Gloss)[lang=en] + /// Definition (or Gloss)2[lang=en] + /// The number to distinguish between styles with the same displayNameBase but different nodePaths. + /// Path from the root node to the leaf node. + internal BaseElement(string displayNameBase, Style style, int uniqueNumber, string nodePath) + { + this.DisplayNameBase = displayNameBase; + this.Style = style; + this.UniqueNumber = uniqueNumber; + this.NodePath = nodePath; + } + + internal string DisplayNameBase { get; } + internal Style Style { get; } + internal int UniqueNumber { get; } + internal string NodePath { get; } + internal bool Used { get; set; } + internal string UniqueDisplayName() + { + return Style.StyleId; + } + } +} \ No newline at end of file diff --git a/Src/xWorks/WordStylesGenerator.cs b/Src/xWorks/WordStylesGenerator.cs index 97958a4f1c..f2be055d86 100644 --- a/Src/xWorks/WordStylesGenerator.cs +++ b/Src/xWorks/WordStylesGenerator.cs @@ -25,93 +25,86 @@ public class WordStylesGenerator internal const int DefaultStyle = -1; // Global and default character styles. + internal const string RootCharacterDisplayName = "root"; + internal const string RootCharacterNodePath = ".root"; internal const string BeforeAfterBetweenStyleName = "Dictionary-Context"; - internal const string BeforeAfterBetweenDisplayName = "Context"; internal const string SenseNumberStyleName = "Dictionary-SenseNumber"; internal const string SenseNumberDisplayName = "Sense Number"; internal const string WritingSystemStyleName = "Writing System Abbreviation"; internal const string WritingSystemDisplayName = "Writing System Abbreviation"; internal const string HeadwordDisplayName = "Headword"; internal const string ReversalFormDisplayName = "Reversal Form"; - internal const string StyleSeparator = " : "; - internal const string LangTagPre = "[lang=\'"; - internal const string LangTagPost = "\']"; + internal const string StyleSeparator = "-"; + internal const string LangTagPre = "[lang="; + internal const string LangTagPost = "]"; + internal const string BeforeAfterBetween = "-Context"; + internal const string LinkedCharacterStyle = "-char"; + internal const string SubentriesHeadword = "Subheadword"; // Globals and default paragraph styles. internal const string NormalParagraphStyleName = "Normal"; + internal const string NormalParagraphDisplayName = "Normal"; + internal const string NormalParagraphNodePath = ".normal"; internal const string PageHeaderStyleName = "Header"; + internal const string PageHeaderDisplayName = "Header"; + internal const string PageHeaderNodePath = ".header"; internal const string MainEntryParagraphDisplayName = "Main Entry"; internal const string LetterHeadingStyleName = "Dictionary-LetterHeading"; internal const string LetterHeadingDisplayName = "Letter Heading"; - internal const string PictureAndCaptionTextframeStyle = "Image-Textframe-Style"; + internal const string LetterHeadingNodePath = ".letterHeading"; + internal const string PictureAndCaptionTextframeDisplayName = "Pictures"; internal const string EntryStyleContinue = "-Continue"; internal const string PageHeaderIdEven = "EvenPages"; internal const string PageHeaderIdOdd = "OddPages"; - - public static Style GenerateLetterHeaderParagraphStyle(ReadOnlyPropertyTable propertyTable, out BulletInfo? bulletInfo) - { - var cache = propertyTable.GetValue("cache"); - var wsId = cache.ServiceLocator.WritingSystems.DefaultVernacularWritingSystem.Handle; - var style = GenerateParagraphStyleFromLcmStyleSheet(LetterHeadingStyleName, wsId, propertyTable, out bulletInfo); - style.StyleId = LetterHeadingDisplayName; - style.StyleName.Val = style.StyleId; - return style; - } - - public static Style GenerateBeforeAfterBetweenCharacterStyle(ReadOnlyPropertyTable propertyTable, out int wsId) - { - var cache = propertyTable.GetValue("cache"); - wsId = cache.ServiceLocator.WritingSystems.DefaultVernacularWritingSystem.Handle; - var style = GenerateCharacterStyleFromLcmStyleSheet(BeforeAfterBetweenStyleName, wsId, propertyTable); - style.StyleId = BeforeAfterBetweenDisplayName; - style.StyleName.Val = style.StyleId; - return style; - } - - public static Style GenerateNormalParagraphStyle(ReadOnlyPropertyTable propertyTable, out BulletInfo? bulletInfo) - { - var style = GenerateParagraphStyleFromLcmStyleSheet(NormalParagraphStyleName, DefaultStyle, propertyTable, out bulletInfo); - return style; - } - - public static Style GenerateMainEntryParagraphStyle(ReadOnlyPropertyTable propertyTable, DictionaryConfigurationModel model, - out ConfigurableDictionaryNode mainEntryNode, out BulletInfo? bulletInfo) - { - Style style = null; - bulletInfo = null; - - // The user can change the style name that is associated with the Main Entry, so look up the node style name using the DisplayLabel. - mainEntryNode = model?.Parts.Find(node => node.DisplayLabel == MainEntryParagraphDisplayName); - if (mainEntryNode != null) - { - style = GenerateParagraphStyleFromLcmStyleSheet(mainEntryNode.Style, DefaultStyle, propertyTable, out bulletInfo); - style.StyleId = MainEntryParagraphDisplayName; - style.StyleName.Val = style.StyleId; - } - return style; - } + internal const string SubentriesClassName = ".subentries"; + internal const string HeadwordClassName = ".headword"; /// /// Generate the style that will be used for the header that goes on the top of - /// every page. The header style will be similar to the provided style, with the - /// addition of the tab stop. + /// every page. The header style will be similar to the provided paragraph style, with the + /// addition of the tab stop. It will also include the run properties from the root style, because + /// the Word header does not apply run properties applied to the run. They need to be added to the + /// paragraph. /// /// The style to based the header style on. /// The header style. - internal static Style GeneratePageHeaderStyle(Style style) + internal static Style GeneratePageHeaderStyle(Style style, Style rootStyle) { Style pageHeaderStyle = (Style)style.CloneNode(true); - pageHeaderStyle.StyleId = PageHeaderStyleName; - pageHeaderStyle.StyleName.Val = pageHeaderStyle.StyleId; + SetStyleName(pageHeaderStyle, PageHeaderStyleName); // Add the tab stop. var tabs = new Tabs(); tabs.Append(new TabStop() { Val = TabStopValues.End, Position = (int)(1440 * 6.5/*inches*/) }); pageHeaderStyle.StyleParagraphProperties.Append(tabs); + + // The Page Header paragraph needs the run properties directly added to it. + // Adding run properties to the runs in the page header do not seem to get applied. + var runProps = rootStyle.GetFirstChild(); + pageHeaderStyle.Append(runProps.CloneNode(true)); + return pageHeaderStyle; } + internal static bool IsParagraphStyle(string styleName, ReadOnlyPropertyTable propertyTable) + { + if(string.IsNullOrEmpty(styleName)) + { + return false; + } + var styleSheet = FontHeightAdjuster.StyleSheetFromPropertyTable(propertyTable); + if (styleSheet == null || !styleSheet.Styles.Contains(styleName)) + { + return false; + } + var projectStyle = styleSheet.Styles[styleName]; + var exportStyleInfo = new ExportStyleInfo(projectStyle); + + return exportStyleInfo.IsParagraphStyle; + } + + /// /// Generates a Word Paragraph Style for the requested FieldWorks style. /// @@ -121,10 +114,10 @@ internal static Style GeneratePageHeaderStyle(Style style) /// Returns the bullet and numbering info associated with the style. Returns null /// if there is none. /// Returns the WordProcessing.Style item. Can return null. - internal static Style GenerateParagraphStyleFromLcmStyleSheet(string styleName, int wsId, + internal static Style GenerateParagraphStyleFromLcmStyleSheet(string styleName, ReadOnlyPropertyTable propertyTable, out BulletInfo? bulletInfo) { - var style = GenerateWordStyleFromLcmStyleSheet(styleName, wsId, propertyTable, out bulletInfo); + var style = GenerateWordStyleFromLcmStyleSheet(true, styleName, DefaultStyle, propertyTable, out bulletInfo); Debug.Assert(style == null || style.Type == StyleValues.Paragraph); return style; } @@ -137,24 +130,28 @@ internal static Style GenerateParagraphStyleFromLcmStyleSheet(string styleName, /// To retrieve styles /// Returns the WordProcessing.Style item. Can return null. internal static Style GenerateCharacterStyleFromLcmStyleSheet(string styleName, int wsId, - ReadOnlyPropertyTable propertyTable) + ReadOnlyPropertyTable propertyTable, StyleRunProperties basedOnProps = null) { - var style = GenerateWordStyleFromLcmStyleSheet(styleName, wsId, propertyTable, out BulletInfo? _); + var style = GenerateWordStyleFromLcmStyleSheet(false, styleName, wsId, propertyTable, out BulletInfo? _, basedOnProps); Debug.Assert(style == null || style.Type == StyleValues.Character); return style; } /// - /// Generates a Word Style for the requested FieldWorks style. + /// Generates a Paragraph or Character Word Style for the requested FieldWorks style. + /// If the FieldWorks style is a paragraph style then either a paragraph or character style can + /// be returned. + /// If the FieldWorks style is a character style then only a character style can be returned. /// + /// True to get a paragraph data, False to get a character data. /// Name of the character or paragraph style. - /// writing system id + /// writing system id. Only used for character style. /// To retrieve styles /// Returns the bullet and numbering info associated with the style. Returns null /// if there is none. (For character styles always returns null.) /// Returns the WordProcessing.Style item. Can return null. - internal static Style GenerateWordStyleFromLcmStyleSheet(string styleName, int wsId, - ReadOnlyPropertyTable propertyTable, out BulletInfo? bulletInfo) + internal static Style GenerateWordStyleFromLcmStyleSheet(bool paragraphData, string styleName, int wsId, + ReadOnlyPropertyTable propertyTable, out BulletInfo? bulletInfo, StyleRunProperties basedOnProps = null) { bulletInfo = null; var styleSheet = FontHeightAdjuster.StyleSheetFromPropertyTable(propertyTable); @@ -165,21 +162,25 @@ internal static Style GenerateWordStyleFromLcmStyleSheet(string styleName, int w var projectStyle = styleSheet.Styles[styleName]; var exportStyleInfo = new ExportStyleInfo(projectStyle); + + // We can't return paragraph data from a character style. + if (!exportStyleInfo.IsParagraphStyle && paragraphData) + { + Debug.Assert(false, "Can't return paragraph data from a character style."); + return null; + } + var exportStyle = new Style(); // StyleId is used for style linking in the xml. exportStyle.StyleId = styleName.Trim('.'); // StyleName is the name a user will see for the given style in Word's style sheet. exportStyle.Append(new StyleName() {Val = exportStyle.StyleId}); - var parProps = new StyleParagraphProperties(); - var runProps = new StyleRunProperties(); - - if (exportStyleInfo.BasedOnStyle?.Name != null) - exportStyle.BasedOn = new BasedOn() { Val = exportStyleInfo.BasedOnStyle.Name }; // Create paragraph and run styles as specified by exportStyleInfo. // Only if the style to export is a paragraph style should we create paragraph formatting options like indentation, alignment, border, etc. - if (exportStyleInfo.IsParagraphStyle) + if (paragraphData) { + var parProps = new StyleParagraphProperties(); exportStyle.Type = StyleValues.Paragraph; var hangingIndent = 0.0f; @@ -328,77 +329,22 @@ internal static Style GenerateWordStyleFromLcmStyleSheet(string styleName, int w else { exportStyle.Type = StyleValues.Character; - } - // Getting the character formatting info to add to the run properties - runProps = AddFontInfoWordStyles(projectStyle, wsId, propertyTable.GetValue("cache")); - exportStyle.Append(runProps); - return exportStyle; - } - - /// - /// Generates paragraph styles from a configuration node. - /// - public static Style GenerateParagraphStyleFromConfigurationNode(ConfigurableDictionaryNode configNode, - ReadOnlyPropertyTable propertyTable, out BulletInfo? bulletInfo) - { - bulletInfo = null; - switch (configNode.DictionaryNodeOptions) - { - // TODO: handle listAndPara case and character portion of pictureOptions - // case IParaOption listAndParaOpts: - - case DictionaryNodePictureOptions pictureOptions: - var cache = propertyTable.GetValue("cache"); - return GenerateParagraphStyleFromPictureOptions(configNode, pictureOptions, cache, propertyTable); - - default: - { - // If the configuration node defines a paragraph style then add the style. - if (!string.IsNullOrEmpty(configNode.Style) && - (configNode.StyleType == ConfigurableDictionaryNode.StyleTypes.Paragraph)) - { - var style = GenerateParagraphStyleFromLcmStyleSheet(configNode.Style, DefaultStyle, propertyTable, out bulletInfo); - style.StyleId = configNode.DisplayLabel; - style.StyleName.Val = style.StyleId; - return style; - } - return null; - } - } - } - - /// - /// Generate the character styles (for the writing systems) that will be the base of all other character styles. - /// - /// - /// - public static List GenerateWritingSystemsCharacterStyles(ReadOnlyPropertyTable propertyTable) - { - var styleElements = new List(); - var cache = propertyTable.GetValue("cache"); - // Generate the styles for all the writing systems - foreach (var aws in cache.ServiceLocator.WritingSystems.AllWritingSystems) - { - // Get the character style information from the "Normal" paragraph style. - Style wsCharStyle = GetOnlyCharacterStyle(GenerateParagraphStyleFromLcmStyleSheet(NormalParagraphStyleName, aws.Handle, propertyTable, out BulletInfo? _)); - wsCharStyle.StyleId = GetWsString(aws.LanguageTag); - wsCharStyle.StyleName = new StyleName() { Val = wsCharStyle.StyleId }; - var styleElem = new StyleElement(wsCharStyle.StyleId, wsCharStyle, null, aws.Handle, aws.RightToLeftScript); - styleElements.Add(styleElem); + // Getting the character formatting info to add to the run properties + var runProps = AddFontInfoWordStyles(projectStyle, wsId, propertyTable.GetValue("cache"), basedOnProps); + exportStyle.Append(runProps); } - return styleElements; + return exportStyle; } - private static Style GenerateParagraphStyleFromPictureOptions(ConfigurableDictionaryNode configNode, DictionaryNodePictureOptions pictureOptions, - LcmCache cache, ReadOnlyPropertyTable propertyTable) + public static Style GenerateParagraphStyleFromPictureOptions() { // Creating a style for the paragraph that will contain the image and caption var textBoxStyle = new Style() { Type = StyleValues.Paragraph, - StyleId = PictureAndCaptionTextframeStyle, - StyleName = new StyleName() { Val = PictureAndCaptionTextframeStyle } + StyleId = PictureAndCaptionTextframeDisplayName, + StyleName = new StyleName() { Val = PictureAndCaptionTextframeDisplayName } }; var parProps = new ParagraphProperties(); @@ -408,13 +354,6 @@ private static Style GenerateParagraphStyleFromPictureOptions(ConfigurableDictio return textBoxStyle; } - private static Styles GenerateWordStylesFromListAndParaOptions(ConfigurableDictionaryNode configNode, - IParaOption listAndParaOpts, ref string baseSelection, LcmCache cache, ReadOnlyPropertyTable propertyTable) - { - // TODO: Generate these styles when we implement custom numbering as well as before/after + separate paragraphs in styles - return null; - } - /// /// Create a paragraph 'continuation' style based on a regular style. This is needed when a paragraph is split /// because part of the content cannot be nested in a paragraph (table, another paragraph). The @@ -425,14 +364,10 @@ internal static Style GenerateContinuationStyle(Style style) { Style contStyle = (Style)style.CloneNode(true); WordStylesGenerator.RemoveFirstLineIndentation(contStyle); - contStyle.StyleId = contStyle.StyleId + EntryStyleContinue; - contStyle.StyleName.Val = contStyle.StyleId; - if (contStyle.BasedOn != null && !string.IsNullOrEmpty(contStyle.BasedOn.Val) && - contStyle.BasedOn.Val != NormalParagraphStyleName) - { - contStyle.BasedOn.Val = contStyle.BasedOn.Val + EntryStyleContinue; - } + // Remove the link to the character style. A continuation should never need an associated character style. + contStyle.RemoveAllChildren(); + return contStyle; } @@ -467,31 +402,21 @@ private static void RemoveFirstLineIndentation(Style style) } /// - /// Generates a new character style similar to the rootStyle, but being based on the provided style name. + /// Builds the word styles for font info properties using the writing system overrides /// - /// The style we want the new style to be similar to. - /// The name of the style that the new style will be based on. - /// The name for the new style. - internal static Style GenerateBasedOnCharacterStyle(Style rootStyle, string styleToBaseOn, string newStyleName) + private static StyleRunProperties AddFontInfoWordStyles(BaseStyleInfo projectStyle, int wsId, + LcmCache cache, StyleRunProperties basedOnProps) { - if (rootStyle == null || string.IsNullOrEmpty(styleToBaseOn) || string.IsNullOrEmpty(newStyleName)) + StyleRunProperties charDefaults = null; + if (basedOnProps == null) { - return null; + charDefaults = new StyleRunProperties(); + } + else + { + charDefaults = (StyleRunProperties)basedOnProps.CloneNode(true); } - Style retStyle = GetOnlyCharacterStyle(rootStyle); - retStyle.Append(new BasedOn() { Val = styleToBaseOn }); - retStyle.StyleId = newStyleName; - retStyle.StyleName = new StyleName() { Val = retStyle.StyleId }; - return retStyle; - } - - /// - /// Builds the word styles for font info properties using the writing system overrides - /// - private static StyleRunProperties AddFontInfoWordStyles(BaseStyleInfo projectStyle, int wsId, LcmCache cache) - { - var charDefaults = new StyleRunProperties(); var wsFontInfo = projectStyle.FontInfoForWs(wsId); var defaultFontInfo = projectStyle.DefaultCharacterStyleInfo; @@ -509,7 +434,7 @@ private static StyleRunProperties AddFontInfoWordStyles(BaseStyleInfo projectSty // fontName still null means not set in Normal Style, then get default fonts from WritingSystems configuration. // Comparison, projectStyle.Name == "Normal", required to limit the font-family definition to the - // empty span (ie span[lang="en"]{}. If not included, font-family will be added to many more spans. + // empty span (ie char). If not included, font-family will be added to many more spans. if (fontName == null && projectStyle.Name == NormalParagraphStyleName) { var lgWritingSystem = cache.ServiceLocator.WritingSystemManager.get_EngineOrNull(wsId); @@ -534,6 +459,7 @@ private static StyleRunProperties AddFontInfoWordStyles(BaseStyleInfo projectSty ComplexScript = fontName, EastAsia = fontName }; + charDefaults.RemoveAllChildren(); charDefaults.Append(font); } @@ -556,6 +482,8 @@ private static StyleRunProperties AddFontInfoWordStyles(BaseStyleInfo projectSty fontSize = fontSize / 500; var size = new FontSize() { Val = fontSize.ToString() }; var sizeCS = new FontSizeComplexScript() { Val = fontSize.ToString() }; + charDefaults.RemoveAllChildren(); + charDefaults.RemoveAllChildren(); charDefaults.Append(size); charDefaults.Append(sizeCS); } @@ -567,6 +495,8 @@ private static StyleRunProperties AddFontInfoWordStyles(BaseStyleInfo projectSty { var boldFont = new Bold() { Val = true }; var boldCS = new BoldComplexScript() { Val = true }; + charDefaults.RemoveAllChildren(); + charDefaults.RemoveAllChildren(); charDefaults.Append(boldFont); charDefaults.Append(boldCS); } @@ -578,6 +508,8 @@ private static StyleRunProperties AddFontInfoWordStyles(BaseStyleInfo projectSty { var italFont = new Italic() { Val = true }; var italicCS = new ItalicComplexScript() { Val = true }; + charDefaults.RemoveAllChildren(); + charDefaults.RemoveAllChildren(); charDefaults.Append(italFont); charDefaults.Append(italicCS); } @@ -589,6 +521,7 @@ private static StyleRunProperties AddFontInfoWordStyles(BaseStyleInfo projectSty // note: open xml does not allow alpha string openXmlColor = GetOpenXmlColor(fontColor.R, fontColor.G, fontColor.B); var color = new Color() { Val = openXmlColor }; + charDefaults.RemoveAllChildren(); charDefaults.Append(color); } @@ -600,6 +533,7 @@ private static StyleRunProperties AddFontInfoWordStyles(BaseStyleInfo projectSty // though a percentage shading could be implemented using shading pattern options. string openXmlColor = GetOpenXmlColor(backColor.R, backColor.G, backColor.B); var backShade = new Shading() { Fill = openXmlColor }; + charDefaults.RemoveAllChildren(); charDefaults.Append(backShade); } @@ -619,6 +553,7 @@ private static StyleRunProperties AddFontInfoWordStyles(BaseStyleInfo projectSty oxmlSuperSub.Val = VerticalPositionValues.Baseline; break; } + charDefaults.RemoveAllChildren(); charDefaults.Append(oxmlSuperSub); } @@ -658,12 +593,13 @@ private static StyleRunProperties AddFontInfoWordStyles(BaseStyleInfo projectSty string openXmlColor = GetOpenXmlColor(color.R, color.G, color.B); oxmlUnderline.Color = openXmlColor; } - + charDefaults.RemoveAllChildren(); charDefaults.Append(oxmlUnderline); } // Else the underline is actually a strikethrough. else { + charDefaults.RemoveAllChildren(); charDefaults.Append(new Strike()); } } @@ -803,9 +739,9 @@ public static RunProperties GetExplicitFontProperties(FontInfo fontInfo) return runProps; } - public static string GetWsString(string wsId) + public static string GetWsString(string wsString) { - return LangTagPre + wsId + LangTagPost; + return LangTagPre + wsString + LangTagPost; } /// @@ -830,20 +766,6 @@ private static bool GetFontValue(InheritableStyleProp wsFontInfo, IStylePr return true; } - private static ConfigurableDictionaryNode AncestorWithParagraphStyle(ConfigurableDictionaryNode currentNode, - LcmStyleSheet styleSheet) - { - var parentNode = currentNode; - do - { - parentNode = parentNode.Parent; - if (parentNode == null) - return null; - } while (!IsParagraphStyle(parentNode, styleSheet)); - - return parentNode; - } - /// /// Gets the indentation information for a Table. /// @@ -902,127 +824,6 @@ private static float CalculateMarginLeft(ExportStyleInfo exportStyleInfo, float return leadingIndent; } - /// - /// Returns a style containing only the run properties from the full style declaration - /// - internal static Style GetOnlyCharacterStyle(Style fullStyleDeclaration) - { - Style charStyle = new Style() { Type = StyleValues.Character }; - if (fullStyleDeclaration.StyleId != null) - charStyle.StyleId = fullStyleDeclaration.StyleId; - if (fullStyleDeclaration.StyleRunProperties != null) - charStyle.Append(fullStyleDeclaration.StyleRunProperties.CloneNode(true)); - return charStyle; - } - - /// - /// Returns a style containing only the paragraph properties from the full style declaration - /// - internal static Style GetOnlyParagraphStyle(Style fullStyleDeclaration) - { - Style parStyle = new Style() { Type = StyleValues.Paragraph }; - if (fullStyleDeclaration.StyleId != null) - parStyle.StyleId = fullStyleDeclaration.StyleId; - if (fullStyleDeclaration.StyleParagraphProperties != null) - parStyle.Append(fullStyleDeclaration.StyleParagraphProperties.CloneNode(true)); - return parStyle; - } - - private static Styles AddRange(Styles styles, Styles moreStyles) - { - if (styles != null) - { - if (moreStyles != null) - { - foreach (Style style in moreStyles) - styles.Append(style.CloneNode(true)); - } - - return styles; - } - - // if we reach this point, moreStyles can only be null if style is also null, - // in which case we do actually wish to return null - return moreStyles; - } - - private static Styles AddRange(Styles moreStyles, Style style) - { - if (style != null) - { - if (moreStyles == null) - { - moreStyles = new Styles(); - } - - moreStyles.Append(style.CloneNode(true)); - } - - // if we reach this point, moreStyles can only be null if style is also null, - // in which case we do actually wish to return null - return moreStyles; - } - - private static Styles RemoveBeforeAfterSelectorRules(Styles styles) - { - Styles selectedStyles = new Styles(); - // TODO: once all styles are handled, shouldn't need this nullcheck anymore - if (styles != null) - { - foreach (Style style in styles) - if (!IsBeforeOrAfter(style)) - selectedStyles.Append(style.CloneNode(true)); - return selectedStyles; - } - - return null; - } - - public static Styles CheckRangeOfStylesForEmpties(Styles rules) - { - // TODO: once all styles are handled, shouldn't need this nullcheck anymore - //if (rules == null) - // return null; - Styles nonEmptyStyles = new Styles(); - foreach (Style style in rules.Descendants