XmlHelpers.toNodes() is too damned fragile

(Bug, Closed -> Fixed, Priority: Critical, Test Status: Unit tested , Reported By Bad Link: Thing 3y284oe not found, )
Summary: Eric hit a problem where an embedded conversation node choked, probably because of something that didn't get escaped properly.
I suspect the right answer is to switch to using JSoup for this stuff. I just have to check that I can do what I need with that. My guess is that, as a first approximation, we could simply run the text through jsoup.clean(), using org.jsoup.nodes.Entities.EscapeMode.xhtml; that ought to do what we are trying to accomplish at UIModule::150, more cleanly and correctly.
Here's the recipe for a relatively modest example of the problem:
[[""This is text with a & -- does it work?"" -> _class(""foo"")]]
That renders as "<span>This is text with a & -- does it work?</span>", because the ampersand messes up _class().
The actual original exception was:
Exception while trying to parse XML <div class="_conversationData" data-thingid=".bu6ocps" data-cancomment="true"><div class="_convCommentData" data-commentid="1" data-thingid=".bu6ocps" data-authorid=".1z142fv" data-authorname="Dylan Thurston" data-time="1515681797808">
<div class="para">Mantle of Dread would not count, like for Bringer of Dreams and Nightmare's innate power.</div>
</div><div class="_convCommentData" data-commentid="3" data-thingid=".bu6ocps" data-authorid=".1z1429d" data-authorname="Eric Reuss" data-time="1515686077091">

<div class="para">To clarify: Fear from Mantle of Dread wouldn't trigger Ominous, just as it wouldn't count for Bringer's special rule "To Dream a Thousand Deaths".</div><div class="para">Fear generated <em>from</em> Bringer of Dreams & Nightmares' innate power Night Terrors or special rule To Dream a Thousand Deaths would count for Ominious. (Like for Dread Apparitions.)</div>
</div></div>

<div class="_conversationData" data-thingid=".bu6ocps" data-cancomment="true"><div class="_convCommentData" data-commentid="2" data-thingid=".bu6ocps" data-authorid=".1z1429d" data-authorname="Eric Reuss" data-time="1515685899965">

<div class="para">Correct, Mantle of Dread would not count.</div>
</div></div>


	
<12>1 2018-01-11T17:29:29.912Z ae-prod-querki-app01.querki.net ConductR - - [mdc bundleId="a27a5fc9997c7de73aa51f0df7a38738"][akka system-name="conductr" thread="conductr-bundle-execution-dispatcher-836"] 
:4:289: ';' expected instead of ' '                                                                                                                                                                                                                                                                                                ^
:4:289: '/' expected instead of '\x00'                                                                                                                                                                                                                                                                                                ^
:4:289: name expected, but char '\x00' cannot start a name                                                                                                                                                                                                                                                                                                ^
scala.xml.parsing.FatalError: expected closing tag of div
	at scala.xml.parsing.MarkupParser$class.errorNoEnd(MarkupParser.scala:40)
	at scala.xml.parsing.XhtmlParser.errorNoEnd(XhtmlParser.scala:21)
	at scala.xml.parsing.MarkupParserCommon$class.xEndTag(MarkupParserCommon.scala:98)
	at scala.xml.parsing.XhtmlParser.xEndTag(XhtmlParser.scala:21)
	at scala.xml.parsing.MarkupParser$class.element1(MarkupParser.scala:589)
	at scala.xml.parsing.XhtmlParser.element1(XhtmlParser.scala:21)
	at scala.xml.parsing.MarkupParser$class.content1(MarkupParser.scala:433)
	at scala.xml.parsing.XhtmlParser.content1(XhtmlParser.scala:21)
	at scala.xml.parsing.MarkupParser$class.content(MarkupParser.scala:459)
	at scala.xml.parsing.XhtmlParser.content(XhtmlParser.scala:21)
	at scala.xml.parsing.MarkupParser$class.element1(MarkupParser.scala:588)
	at scala.xml.parsing.XhtmlParser.element1(XhtmlParser.scala:21)
	at scala.xml.parsing.MarkupParser$class.content1(MarkupParser.scala:433)
	at scala.xml.parsing.XhtmlParser.content1(XhtmlParser.scala:21)
	at scala.xml.parsing.MarkupParser$class.content(MarkupParser.scala:459)
	at scala.xml.parsing.XhtmlParser.content(XhtmlParser.scala:21)
	at scala.xml.parsing.MarkupParser$class.element1(MarkupParser.scala:588)
	at scala.xml.parsing.XhtmlParser.element1(XhtmlParser.scala:21)
	at querki.util.XmlHelpers$.parseXhtmlFragment(XmlHelpers.scala:45)
	at querki.util.XmlHelpers$.toNodes(XmlHelpers.scala:57)
	at querki.html.UIModule$HtmlModifier$$anonfun$qlApply$2$$anonfun$apply$1.apply(UIModule.scala:150)
	at querki.html.UIModule$HtmlModifier$$anonfun$qlApply$2$$anonfun$apply$1.apply(UIModule.scala:145)
	at querki.ql.InvocationValueImpl$$anonfun$1$$anonfun$apply$1.apply(Invocation.scala:46)
	at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:234)
	at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:234)
	at scala.collection.immutable.List.foreach(List.scala:392)
	at scala.collection.TraversableLike$class.map(TraversableLike.scala:234)
	at scala.collection.immutable.List.map(List.scala:296)
	at querki.ql.InvocationValueImpl$$anonfun$1.apply(Invocation.scala:46)
	at querki.ql.InvocationValueImpl$$anonfun$1.apply(Invocation.scala:46)
	at scala.util.Success$$anonfun$map$1.apply(Try.scala:237)
	at scala.util.Try$.apply(Try.scala:192)
	at scala.util.Success.map(Try.scala:237)
	at scala.concurrent.Future$$anonfun$map$1.apply(Future.scala:237)
	at scala.concurrent.Future$$anonfun$map$1.apply(Future.scala:237)
	at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:36)
	at scala.concurrent.impl.ExecutionContextImpl$AdaptedForkJoinTask.exec(ExecutionContextImpl.scala:121)
	at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
	at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.pollAndExecAll(ForkJoinPool.java:1253)
	at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1346)
	at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
	at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)