All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.prismic.fragments.package.scala Maven / Gradle / Ivy

The newest version!
package io.prismic.fragments

import org.joda.time._
import spray.json._
import io.prismic._

case object Separator extends Fragment {
  def asHtml = "
" } sealed trait Link extends Fragment { def getUrl(linkResolver: DocumentLinkResolver): String def render(href: String, content: String): String } case class WebLink(url: String, target: Option[String] = None) extends Link { override def getUrl(linkResolver: DocumentLinkResolver) = url override def render(href: String, content: String): String = { val renderTarget = target.map { t => s""" target="$t" rel="noopener"""" }.getOrElse("") s"""$content""" } def asHtml(): String = render(href = url, content = url) } case class FileLink(url: String, kind: String, size: Long, filename: String, target: Option[String] = None) extends Link { override def getUrl(linkResolver: DocumentLinkResolver) = url override def render(href: String, content: String): String = { val renderTarget = target.map { t => s""" target="$t" rel="noopener"""" }.getOrElse("") s"""$content""" } def asHtml(): String = render(href = url, content = filename) } case class ImageLink(url: String, kind: String, size: Long, filename: String, target: Option[String] = None) extends Link { override def getUrl(linkResolver: DocumentLinkResolver) = url override def render(href: String, content: String): String = { val img = s"""$content""" target match { case Some(t) => s"""$img""" case None => img } } def asHtml(): String = render(href = url, content = filename) } case class DocumentLink(id: String, uid: Option[String], typ: String, tags: Seq[String], slug: String, lang: String, fragments: Map[String, Fragment], isBroken: Boolean, target: Option[String] = None) extends Link with WithFragments { override def getUrl(linkResolver: DocumentLinkResolver) = linkResolver(this) override def asHtml(linkResolver: DocumentLinkResolver): String = render(href = linkResolver(this), content = slug) override def render(href: String, content: String): String = { val renderTarget = target.map { t => s""" target="$t" rel="noopener"""" }.getOrElse("") s"""$content""" } } case class AlternateLanguage(id: String, uid: Option[String], typ: String, lang: String) // ------------------ case class Text(value: String) extends Fragment { def asHtml: String = s"""$value""" } // ------------------ case class Date(value: LocalDate) extends Fragment { def asText(pattern: String) = value.toString(pattern) def asHtml: String = s"""""" } case class Timestamp(value: DateTime) extends Fragment { def asText(pattern: String) = value.toString(pattern) def asHtml: String = s"""""" } // ------------------ case class Number(value: Double) extends Fragment { def asInt = value.toInt def asText(pattern: String) = new java.text.DecimalFormat(pattern).format(value) def asHtml: String = s"""$value""" } // ------------------ case class Color(hex: String) extends Fragment { def asRGB = Color.asRGB(hex) def asHtml: String = s"""$hex""" } object Color { private val HexColor = """#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})""".r def isValidColorValue(hex: String): Boolean = hex match { case HexColor(r, g, b) => true case _ => false } def asRGB(hex: String): (Int, Int, Int) = hex match { case HexColor(r, g, b) => (Integer.parseInt(r, 16), Integer.parseInt(g, 16), Integer.parseInt(b, 16)) case _ => (0, 0, 0) } } // ------------------ case class Embed(typ: String, provider: Option[String], url: String, width: Option[Int], height: Option[Int], html: Option[String], oembedJson: JsValue) extends Fragment { def asHtml(label: Option[String] = None, direction: Option[String] = None): String = { val attributes: Seq[(String, String)] = Seq( ("data-oembed", url), ("data-oembed-type", typ.toLowerCase) ) ++ label.map(l => ("class", l)).toSeq ++ direction.map(d => ("dir", d)).toSeq ++ provider.map(p => ("data-oembed-provider", p.toLowerCase)) html.map(html => s"""
s"""$k="$v""""}.mkString(" ")}>$html
""").getOrElse("") } } // ------------------ case class GeoPoint(latitude: Double, longitude: Double) extends Fragment { def asHtml: String = s"""
${latitude}${longitude}
""" } // ------------------ case class Image(main: Image.View, views: Map[String, Image.View] = Map.empty) extends Fragment { def getView(key: String): Option[Image.View] = key.toLowerCase match { case "main" => Some(main) case _ => views.get(key) } def asHtml: String = main.asHtml } object Image { case class View(url: String, width: Int, height: Int, alt: Option[String]) { def ratio = width / height def asHtml: String = s"""${alt.getOrElse(""" } } case class Group(docs: Seq[Group.Doc]) extends Fragment { def asHtml(linkResolver: DocumentLinkResolver): String = docs map (_ asHtml linkResolver) mkString "\n" } object Group { case class Doc(fragments: Map[String, Fragment]) extends WithFragments } case class StructuredText(blocks: Seq[StructuredText.Block]) extends Fragment { def getTitle: Option[StructuredText.Block.Heading] = blocks.collectFirst { case h: StructuredText.Block.Heading => h } def getFirstParagraph: Option[StructuredText.Block.Paragraph] = blocks.collectFirst { case p: StructuredText.Block.Paragraph => p } def getAllParagraphs: Seq[StructuredText.Block.Paragraph] = blocks.collect { case p: StructuredText.Block.Paragraph => p } def getFirstImage: Option[StructuredText.Block.Image] = blocks.collectFirst { case i: StructuredText.Block.Image => i } def asHtml(linkResolver: DocumentLinkResolver, htmlSerializer: HtmlSerializer = HtmlSerializer.empty): String = { StructuredText.asHtml(blocks, linkResolver, htmlSerializer) } } trait Slice { def sliceType: String def sliceLabel: Option[String] def asHtml(linkResolver: DocumentLinkResolver): String } case class CompositeSlice(sliceType: String, sliceLabel: Option[String], nonRepeat: Group.Doc, repeat: Group) extends Slice { def asHtml(linkResolver: DocumentLinkResolver): String = { var className = (Seq("slice") ++ sliceLabel.toSeq).mkString(" ") s"""
|
|${nonRepeat.fragments.toSeq.map{case (key, value) => Fragment.getHtml(value, linkResolver)}.mkString(" ")} |
|${repeat.asHtml(linkResolver)} |
""".stripMargin } } case class SimpleSlice(sliceType: String, sliceLabel: Option[String], value: Fragment) extends Slice { def asHtml(linkResolver: DocumentLinkResolver): String = { var className = (Seq("slice") ++ sliceLabel.toSeq).mkString(" ") s"""
${Fragment.getHtml(value, linkResolver)}
""" } } case class SliceZone(slices: Seq[Slice]) extends Fragment { def asHtml(linkResolver: DocumentLinkResolver, htmlSerializer: HtmlSerializer = HtmlSerializer.empty): String = slices map (_ asHtml linkResolver) mkString "\n" } // ------------------ object StructuredText { def asHtml(blocks: Seq[Block], linkResolver: DocumentLinkResolver, htmlSerializer: HtmlSerializer): String = { case class Group(htmlTag: Option[String], blocks: Seq[Block]) val grouped: List[Group] = blocks.foldLeft(List.empty[Group]) { case ((group@Group(Some("ul"), _)) :: rest, [email protected](_, _, false, _, _)) => group.copy(blocks = group.blocks :+ block) +: rest case ((group@Group(Some("ol"), _)) :: rest, [email protected](_, _, true, _, _)) => group.copy(blocks = group.blocks :+ block) +: rest case (groups, [email protected](_, _, false, _, _)) => Group(Some("ul"), Seq(block)) +: groups case (groups, [email protected](_, _, true, _, _)) => Group(Some("ol"), Seq(block)) +: groups case (groups, block) => Group(None, Seq(block)) +: groups }.reverse grouped.flatMap { case Group(Some(tag), bcks) => s"<$tag>" +: bcks.map(block => Block.asHtml(block, linkResolver, htmlSerializer)) :+ s"" case Group(None, bcks) => bcks.map(block => Block.asHtml(block, linkResolver, htmlSerializer)) }.mkString("\n\n") } private def asHtml(text: String, spans: Seq[Span], linkResolver: DocumentLinkResolver, serializer: HtmlSerializer): String = { def escape(character: String): String = { character.replace("<", "<").replace("\n", "
") } def serialize(element: Element, content: String): String = { serializer(element, content).getOrElse { element match { case b: Block => Block.asHtml(b, linkResolver) case _: Span.Em => s"$content" case _: Span.Strong => s"$content" case Span.Hyperlink(_, _, link: DocumentLink) => link.render(href = linkResolver(link), content) case Span.Hyperlink(_, _, link: FileLink) => link.render(href = link.url, content) case Span.Hyperlink(_, _, link: WebLink) => link.render(href = link.url, content) case Span.Label(_, _, label) => s"""$content""" case _ => s"$content" } } } case class OpenSpan(span: Span, content: String) @scala.annotation.tailrec def step(in: Seq[(Char, Int)], spans: Seq[Span], stack: Seq[OpenSpan] = Nil, html: String = ""): String = { in match { case ((_, pos) :: tail) if stack.headOption.map(_.span.end) == Some(pos) => { // Need to close a tag val tagHtml = serialize(stack.head.span, stack.head.content) stack.drop(1) match { case Nil => step(in, spans, Nil, html + tagHtml) case h :: t => step(in, spans, h.copy(content = h.content + tagHtml) :: t, html) } } case ((_, pos) :: tail) if spans.headOption.map(_.start) == Some(pos) => { // Need to open a tag step(in, spans.drop(1), OpenSpan(spans.head, "") +: stack, html) } case (current, pos) :: tail => { stack match { case Nil => // Top level step(tail, spans, stack, html + escape(current.toString)) case head :: t => // There is an open span, insert inside step(tail, spans, head.copy(content = head.content + escape(current.toString)) :: t, html) } } case Nil => stack match { case Nil => html case head :: Nil => // One last tag open, close it html + serialize(head.span, head.content) case head :: second :: tail => // At least 2 tags open, close the first and continue step(Nil, spans, second.copy(content = second.content + serialize(head.span, head.content)) :: tail, html) } } } step(text.toList.zipWithIndex, spans.sortWith { case (a, b) if a.start == b.start => (a.end - a.start) > (b.end - b.start) case (a, b) => a.start < b.start }) } sealed trait Element sealed trait Span extends Element { def start: Int def end: Int } object Span { case class Em(start: Int, end: Int) extends Span case class Strong(start: Int, end: Int) extends Span case class Hyperlink(start: Int, end: Int, link: Link) extends Span case class Label(start: Int, end: Int, label: String) extends Span } sealed trait Block extends Element { def label: Option[String] def direction: Option[String] } object Block { sealed trait Text extends Block { def text: String def spans: Seq[Span] } object Text { def unapply(t: Text): Option[(String, Seq[Span], Option[String])] = Some(t.text, t.spans, t.label) } def asHtml(block: Block, linkResolver: DocumentLinkResolver, htmlSerializer: HtmlSerializer = HtmlSerializer.empty): String = { val cls = ( block.label.map(l => s""" class="$l"""").toSeq ++ block.direction.map(d => s""" dir="$d"""").toSeq ).mkString(" ") val body = block match { case StructuredText.Block.Text(text, spans, _) => StructuredText.asHtml(text, spans, linkResolver, htmlSerializer) case _ => "" } htmlSerializer(block, body).getOrElse { block match { case StructuredText.Block.Heading(text, spans, level, _, _) => s"""$body""" case StructuredText.Block.Paragraph(text, spans, _, _) => s"""$body

""" case StructuredText.Block.Preformatted(text, spans, _, _) => s"""$body
""" case StructuredText.Block.ListItem(text, spans, _, _, _) => s"""$body""" case StructuredText.Block.Image(view, hyperlink, label, dir) => { val linkbody = hyperlink match { case Some(link: DocumentLink) => link.render(href = linkResolver(link), content = view.asHtml) case Some(link: WebLink) => link.render(href = link.url, content = view.asHtml) case Some(link: FileLink) => link.render(href = link.url, content = view.asHtml) case _ => view.asHtml } s""" s""" dir="$d"""").getOrElse("")} class="${(label.toSeq :+ "block-img").mkString(" ")}">$linkbody

""" } case StructuredText.Block.Embed(obj, label, direction) => obj.asHtml(label, direction) } } } case class Heading(text: String, spans: Seq[Span], level: Int, label: Option[String], direction: Option[String]) extends Text case class Paragraph(text: String, spans: Seq[Span], label: Option[String], direction: Option[String]) extends Text case class Preformatted(text: String, spans: Seq[Span], label: Option[String], direction: Option[String]) extends Text case class ListItem(text: String, spans: Seq[Span], ordered: Boolean, label: Option[String], direction: Option[String]) extends Text case class Image(view: fragments.Image.View, linkTo: Option[Link], label: Option[String], direction: Option[String]) extends Block { def url = view.url def width = view.width def height = view.height } case class Embed(obj: fragments.Embed, label: Option[String], direction: Option[String]) extends Block } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy