/// <summary> /// Shift any translationGroup level style setting to child editable divs that lack a style. /// This is motivated by the fact HTML/CSS underlining cannot be turned off in child nodes. /// So if underlining is enabled for Normal, everything everywhere would be underlined /// regardless of style or immediate character formatting. If a style sets underlining /// on, then immediate character formatting cannot turn it off anywhere in a text box that /// uses that style. See https://silbloom.myjetbrains.com/youtrack/issue/BL-6282. /// </summary> private static void FixGroupStyleSettings(XmlNode pageDiv) { foreach ( XmlElement groupElement in pageDiv.SafeSelectNodes("descendant-or-self::*[contains(@class,'bloom-translationGroup')]")) { var groupStyle = HtmlDom.GetStyle(groupElement); if (String.IsNullOrEmpty(groupStyle)) { continue; } // Copy the group's style setting to any child div with the bloom-editable class that lacks one. // Then remove the group style if it does have any child divs with the bloom-editable class. bool hasInternalEditableDiv = false; foreach (XmlElement element in groupElement.SafeSelectNodes("child::div[contains(@class, 'bloom-editable')]")) { var divStyle = HtmlDom.GetStyle(element); if (String.IsNullOrEmpty(divStyle)) { HtmlDom.AddClass(element, groupStyle); } hasInternalEditableDiv = true; } if (hasInternalEditableDiv) { HtmlDom.RemoveClass(groupElement, groupStyle); } } }
// At publish time, we strip out pages with the bloom-enterprise class. // But we don't want to do that for comic pages in derivatives. See BL-10586. private void StripBloomEnterpriseClassFromComicPages(BookStorage storage) { foreach (XmlElement pageDiv in storage.Dom.RawDom.SafeSelectNodes("//div[contains(@class,'bloom-page')]")) { if (HtmlDom.HasClass(pageDiv, "comic")) { HtmlDom.RemoveClass(pageDiv, "enterprise-only"); } } }
/// <summary> /// For each group (meaning they have a common parent) of editable items, we /// need to make sure there are the correct set of copies, with appropriate @lang attributes /// </summary> public static XmlElement MakeElementWithLanguageForOneGroup(XmlElement groupElement, string isoCode) { if (groupElement.GetAttribute("class").Contains("STOP")) { Console.Write("stop"); } // If we don't have the relevant language code in this collection, don't make a block for it! if (string.IsNullOrEmpty(isoCode)) { return(null); } XmlNodeList editableChildrenOfTheGroup = groupElement.SafeSelectNodes("*[self::textarea or contains(@class,'bloom-editable')]"); var elementsAlreadyInThisLanguage = from XmlElement x in editableChildrenOfTheGroup where x.GetAttribute("lang") == isoCode select x; if (elementsAlreadyInThisLanguage.Any()) { //don't mess with this set, it already has a vernacular (this will happen when we're editing a shellbook, not just using it to make a vernacular edition) return(elementsAlreadyInThisLanguage.First()); } if (groupElement.SafeSelectNodes("ancestor-or-self::*[contains(@class,'bloom-translationGroup')]").Count == 0) { return(null); } var prototype = editableChildrenOfTheGroup[0] as XmlElement; XmlElement newElementInThisLanguage; if (prototype == null) //this was an empty translation-group (unusual, but we can cope) { newElementInThisLanguage = groupElement.OwnerDocument.CreateElement("div"); newElementInThisLanguage.SetAttribute("class", "bloom-editable"); newElementInThisLanguage.SetAttribute("contenteditable", "true"); if (groupElement.HasAttribute("data-placeholder")) { newElementInThisLanguage.SetAttribute("data-placeholder", groupElement.GetAttribute("data-placeholder")); } groupElement.AppendChild(newElementInThisLanguage); } else //this is the normal situation, where we're just copying the first element { //what we want to do is copy everything in the element, except that which is specific to a language. //so classes on the element, non-text children (like images), etc. should be copied newElementInThisLanguage = (XmlElement)prototype.ParentNode.InsertAfter(prototype.Clone(), prototype); //if there is an id, get rid of it, because we don't want 2 elements with the same id newElementInThisLanguage.RemoveAttribute("id"); // Since we change the ID, the corresponding mp3 will change, which means the duration is no longer valid newElementInThisLanguage.RemoveAttribute("data-duration"); // No need to copy over the audio-sentence markup // Various code expects elements with class audio-sentence to have an ID. // Both will be added when and if we do audio recording (in whole-text-box mode) on the new div. // Until then it makes things more consistent if we make sure elements without ids // don't have this class. // Also, if audio recording markup is done using one audio-sentence span per sentence, we won't copy it. (Because we strip all out the text underneath this node) // So, it's more consistent to treat all scenarios the same way (don't copy the audio-sentence markup) HtmlDom.RemoveClass(newElementInThisLanguage, "audio-sentence"); // Nor any need to copy over other audio markup // We want to clear up all the audio markup so it's not left in an inconsistent state // where the Talking Book JS code thinks it's been initialized already, but actually all the audio-sentence markup has been stripped out :( // See BL-8215 newElementInThisLanguage.RemoveAttribute("data-audiorecordingmode"); newElementInThisLanguage.RemoveAttribute("data-audiorecordingendtimes"); HtmlDom.RemoveClass(newElementInThisLanguage, "bloom-postAudioSplit"); //OK, now any text in there will belong to the prototype language, so remove it, while retaining everything else StripOutText(newElementInThisLanguage); } newElementInThisLanguage.SetAttribute("lang", isoCode); return(newElementInThisLanguage); }