public static void MakeImagePathsOfImportedPagePointToOriginalLocations(XmlElement pageDiv, string folderPath) { foreach (XmlElement img in pageDiv.SafeSelectNodes("descendant::img")) { img.SetAttribute("src", "file://"+ Path.Combine(folderPath, img.GetAttribute("src"))); } }
/// <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> private static void MakeElementWithLanguageForOneGroup(XmlElement groupElement, string isoCode, string elementTag) { //<label>s are annotations on the translation, group, we don't want to mess with them here. //the caller at the moment is using '*' for element, so it takes this xpath to filter them out... XmlNodeList editableElementsWithinTheIndicatedElement = groupElement.SafeSelectNodes(elementTag+"[not(self::label)]"); //true, this is a weird situation... if (editableElementsWithinTheIndicatedParagraph.Count == 0) // return; var elementsAlreadyInThisLanguage = from XmlElement x in editableElementsWithinTheIndicatedElement where x.GetAttribute("lang") == isoCode select x; if (elementsAlreadyInThisLanguage.Count() > 0)//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; if (groupElement.SafeSelectNodes("ancestor-or-self::*[contains(@class,'bloom-translationGroup')]").Count == 0) return; XmlElement prototype = editableElementsWithinTheIndicatedElement[0] as XmlElement; XmlElement newElementInThisLanguage; if (prototype == null)// note that we currently (version 1.0) get this when the prototype was the recommended lang='x'. Which is unfortunate, because it means the prototype is deleted by other code before we can copy it. { 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 { newElementInThisLanguage = (XmlElement)prototype.ParentNode.InsertAfter(prototype.Clone(), prototype); } newElementInThisLanguage.SetAttribute("lang", isoCode); //if there is an id, get rid of it, because we don't want 2 elements with the same id newElementInThisLanguage.RemoveAttribute("id"); newElementInThisLanguage.InnerText = string.Empty; }
/* The following, to use normal url query parameters to say if we wanted transparency, * was a nice idea, but turned out to not be necessary. I'm leave the code here in * case in the future we do find a need to add query parameters. public void SetImagesForMode(bool editMode) { SetImagesForMode((XmlNode)RawDom, editMode); } public static void SetImagesForMode(XmlNode pageNode, bool editMode) { foreach(XmlElement imgNode in pageNode.SafeSelectNodes(".//img")) { var src = imgNode.GetAttribute("src"); const string kTransparent = "?makeWhiteTransparent=true"; src = src.Replace(kTransparent, ""); if (editMode) src = src + kTransparent; imgNode.SetAttribute("src",src); } } */ public static void ProcessPageAfterEditing(XmlElement destinationPageDiv, XmlElement edittedPageDiv) { // strip out any elements that are part of bloom's UI; we don't want to save them in the document or show them in thumbnails etc. // Thanks to http://stackoverflow.com/questions/1390568/how-to-match-attributes-that-contain-a-certain-string for the xpath. // The idea is to match class attriutes which have class bloom-ui, but may have other classes. We don't want to match // classes where bloom-ui is a substring, though, if there should be any. So we wrap spaces around the class attribute // and then see whether it contains bloom-ui surrounded by spaces. // However, we need to do this in the edited page before copying to the storage page, since we are about to suck // info from the edited page into the dataDiv and we don't want the bloom-ui elements in there either! foreach( var node in edittedPageDiv.SafeSelectNodes("//*[contains(concat(' ', @class, ' '), ' bloom-ui ')]").Cast<XmlNode>().ToArray()) node.ParentNode.RemoveChild(node); destinationPageDiv.InnerXml = edittedPageDiv.InnerXml; //Enhance: maybe we should just copy over all attributes? destinationPageDiv.SetAttribute("class", edittedPageDiv.GetAttribute("class")); //The SIL LEAD SHRP templates rely on "lang" on some ancestor to trigger the correct rules in labels.css. //Those get set by putting data-metalanguage on Page, which then leads to a lang='xyz'. Let's save that //back to the html in keeping with our goal of having the page look right if you were to just open the //html file in Firefox. destinationPageDiv.SetAttribute("lang", edittedPageDiv.GetAttribute("lang")); // Upon save, make sure we are not in layout mode. Otherwise we show the sliders. foreach( var node in destinationPageDiv.SafeSelectNodes(".//*[contains(concat(' ', @class, ' '), ' origami-layout-mode ')]") .Cast<XmlNode>() .ToArray()) { string currentValue = node.Attributes["class"].Value; node.Attributes["class"].Value = currentValue.Replace("origami-layout-mode", ""); } }
/// <summary> /// For each div in the page which has the specified class, find the corresponding div with that class in newPage, /// and replace its contents with the contents of the source page. /// Also inserts any needed styles we know about. /// </summary> /// <param name="page"></param> /// <param name="parentClass"></param> /// <param name="newPage"></param> private static void MigrateChildren(XmlElement page, string parentClass, XmlElement newPage) { //the leading '.' here is needed because newPage is an element in a larger DOM, and we only want to search in this page var xpath = ".//div[contains(concat(' ', @class, ' '), ' " + parentClass + " ')]"; var oldParents = page.SafeSelectNodes(xpath); var newParents = newPage.SafeSelectNodes(xpath); // The Math.Min is not needed yet; in fact, we don't yet have any cases where there is more than one // thing to copy or where the numbers are not equal. It's just a precaution. for(int i = 0; i < Math.Min(newParents.Count, oldParents.Count); i++) { var oldParent = (XmlElement) oldParents[i]; var newParent = (XmlElement) newParents[i]; foreach(var child in newParent.ChildNodes.Cast<XmlNode>().ToArray()) newParent.RemoveChild(child); // apparently we are modifying the ChildNodes collection by removing the child from there to insert in the new location, // which messes things up unless we make a copy of the collection. foreach(XmlNode child in oldParent.ChildNodes.Cast<XmlNode>().ToArray()) { newParent.AppendChild(child); AddKnownStyleIfMissing(child); } } }
void EnsureEditableDivsHaveIds(XmlElement pageElt) { int count = 1; foreach (XmlElement elt in pageElt.SafeSelectNodes(".//div")) { if (!HasClass(elt, "bloom-editable")) continue; if (elt.Attributes["id"] != null) continue; elt.SetAttribute("id", tempIdMarker + count++); } }
void RemoveTempIds(XmlElement pageElt) { int count = 1; foreach (XmlElement elt in pageElt.SafeSelectNodes(".//div")) { if (!HasClass(elt, "bloom-editable")) continue; if (!elt.Attributes["id"].Value.StartsWith(tempIdMarker)) continue; elt.RemoveAttribute("id"); } }
private void CheckEditableText(XmlElement page, string lang, string text, int groupIndex = 0) { var transGroup = (XmlElement)page.SafeSelectNodes(".//div[contains(@class,'bloom-translationGroup')]")[groupIndex]; var editDiv = (XmlElement)transGroup.SafeSelectNodes("div[@lang='" + lang + "' and contains(@class,'bloom-editable')]")[0]; var actualText = editDiv.InnerXml; Assert.That(actualText.Trim(), Is.EqualTo(text)); }
/// <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> private static void MakeElementWithLanguageForOneGroup(XmlElement groupElement, string isoCode) { if (groupElement.GetAttribute("class").Contains("STOP")) { Console.Write("stop"); } 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; if (groupElement.SafeSelectNodes("ancestor-or-self::*[contains(@class,'bloom-translationGroup')]").Count == 0) return; 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"); //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); }
private static void RemoveAudioMarkup(XmlElement newpageDiv) { foreach (var span in newpageDiv.SafeSelectNodes(".//span[contains(@class,'audio-sentence')]").Cast<XmlElement>().ToList()) { XmlNode after = span; foreach (XmlNode child in span.ChildNodes) { span.ParentNode.InsertAfter(child, after); after = child; } span.ParentNode.RemoveChild(span); } }