knockoff.XHTMLWriter.scala Maven / Gradle / Ivy
The newest version!
/*
## Output To XHTML
Knockoff's XHTMLWriter uses match expressions on it's object model to create a
very similar XHTML-style XML document.
Customization involves overriding one of these methods. At times, I've found it
easier to completely re-write or adjust the output method, so this technique may
not be 100% finished.
*/
package knockoff
import scala.util.{ Random }
import scala.xml.{ Group, Node, Text => XMLText, Unparsed }
trait XHTMLWriter {
/** Backwards compatibility? *cough* */
def toXML( blocks : collection.Seq[Block] ) : Node = toXHTML( blocks )
/** Creates a Group representation of the document. */
def toXHTML( blocks : collection.Seq[Block] ) : Node =
Group( blocks.map( blockToXHTML(_) ) )
def blockToXHTML : Block => Node = {
case Paragraph( spans, _ ) => paragraphToXHTML( spans )
case Header( level, spans, _ ) => headerToXHTML( level, spans )
case LinkDefinition( _, _, _, _ ) => Group( Nil )
case Blockquote( children, _ ) => blockquoteToXHTML( children )
case CodeBlock( text, _ ) => codeToXHTML( text )
case HorizontalRule( _ ) => hrXHTML
case OrderedItem( children, _ ) => liToXHTML( children )
case UnorderedItem( children, _ ) => liToXHTML( children )
case OrderedList( items ) => olToXHTML( items )
case UnorderedList( items ) => ulToXHTML( items )
case HTMLBlock( content, _ ) => htmlBlockToXHTML( content )
}
def htmlBlockToXHTML : String => Node = html => Unparsed( html )
def paragraphToXHTML : collection.Seq[Span] => Node = spans => {
def isHTML( s : Span ) = s match {
case y : HTMLSpan => true
case Text( content ) => if ( content.trim.isEmpty ) true else false
case _ => false
}
if ( spans.forall( isHTML ) )
Group( spans.map( spanToXHTML(_) ) )
else
{ spans.map( spanToXHTML(_) ) }
}
def headerToXHTML : ( Int, collection.Seq[Span] ) => Node = (level, spans) => {
val spanned = spans.map( spanToXHTML(_) )
level match {
case 1 => { spanned }
case 2 => { spanned }
case 3 => { spanned }
case 4 => { spanned }
case 5 => { spanned }
case 6 => { spanned }
case _ => { spanned }
}
}
def blockquoteToXHTML : collection.Seq[Block] => Node =
children => { children.map( blockToXHTML(_) ) }
def codeToXHTML : Text => Node =
text => { text.content }
def hrXHTML : Node =
def liToXHTML : collection.Seq[Block] => Node =
children => { simpleOrComplex( children ) }
private def simpleOrComplex( children : collection.Seq[Block] ) : collection.Seq[Node] = {
if ( children.length == 1 )
children.head match {
case Paragraph( spans, _ ) => spans.map( spanToXHTML(_) )
case _ => children.map( blockToXHTML(_) )
}
else
children.map( blockToXHTML(_) )
}
def olToXHTML : collection.Seq[Block] => Node =
items => { items.map( blockToXHTML(_) ) }
def ulToXHTML : collection.Seq[Block] => Node =
items => { items.map( blockToXHTML(_) ) }
def spanToXHTML : Span => Node = span => span match {
case Text( content ) => textToXHTML( content )
case HTMLSpan( html ) => htmlSpanToXHTML( html )
case CodeSpan( code ) => codeSpanToXHTML( code )
case Strong( children ) => strongToXHTML( children )
case Emphasis( children ) => emphasisToXHTML( children )
case Link( children, url, title ) => linkToXHTML( children, url, title )
case IndirectLink( children, definition ) =>
linkToXHTML( children, definition.url, definition.title )
case ImageLink( children, url, title ) => imageLinkToXHTML( children, url, title )
case IndirectImageLink( children, definition ) =>
imageLinkToXHTML( children, definition.url, definition.title )
}
def textToXHTML : String => Node = content => XMLText( unescape(content) )
def htmlSpanToXHTML : String => Node = html => Unparsed( html )
def codeSpanToXHTML : String => Node = code => { code }
def strongToXHTML : collection.Seq[Span] => Node =
spans => { spans.map( spanToXHTML(_) ) }
def emphasisToXHTML : collection.Seq[Span] => Node =
spans => { spans.map( spanToXHTML(_) ) }
def linkToXHTML : ( collection.Seq[Span], String, Option[String] ) => Node = {
( spans, url, title ) => {
spans.map( spanToXHTML(_) )
}
}
def imageLinkToXHTML : ( collection.Seq[Span], String, Option[String] ) => Node = {
( spans, url, title ) =>
}
def escapeURL( url : String ) : Node = {
if ( url.startsWith( "mailto:" ) ) {
val rand = new Random
val mixed = url.map { ch =>
rand.nextInt(2) match {
case 0 => java.lang.String.format( "%d;", int2Integer( ch.toInt ) )
case 1 => java.lang.String.format( "%s;", ch.toInt.toHexString )
}
}.mkString("")
Unparsed( mixed )
} else {
XMLText( url )
}
}
// TODO put this somewhere else
val escapeableChars = List( "\\", "`", "*", "_", "{", "}", "[", "]", "(", ")",
"#", "+", "-", ".", "!", ">" )
def unescape(source:String):String = {
var buf:String = source
for ((escaped, unescaped) <- escapeableChars.map(ch => ("\\" + ch, ch)))
buf = buf.replace(escaped, unescaped)
buf
}
}