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

laika.helium.generate.ConfigGenerator.scala Maven / Gradle / Ivy

/*
 * Copyright 2012-2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package laika.helium.generate

import laika.ast.Path.Root
import laika.ast._
import laika.config.ConfigEncoder.ObjectBuilder
import laika.config._
import laika.helium.Helium
import laika.helium.config._

private[laika] object ConfigGenerator {

  implicit val releaseEncoder: ConfigEncoder[ReleaseInfo] = ConfigEncoder[ReleaseInfo] { releaseInfo =>
    ConfigEncoder.ObjectBuilder.empty
      .withValue("title", releaseInfo.title)
      .withValue("version", releaseInfo.version)
      .build
  }

  implicit val teaserEncoder: ConfigEncoder[Teaser] = ConfigEncoder[Teaser] { teaser =>
    ConfigEncoder.ObjectBuilder.empty
      .withValue("title", teaser.title)
      .withValue("description", teaser.description)
      .build
  }
  
  private def buildTeaserRows (teasers: Seq[Teaser]): Seq[ObjectValue] = if (teasers.isEmpty) Nil else
    BalancedGroups.create(teasers.toVector, Math.ceil(teasers.size.toDouble / 3).toInt).map { row =>
      ObjectBuilder.empty.withValue("teasers", row).build
    }

  implicit val landingPageEncoder: ConfigEncoder[LandingPage] = ConfigEncoder[LandingPage] { landingPage =>
    val titleLinks = 
      if (landingPage.titleLinks.isEmpty) None
      else Some(GenericLinkGroup(landingPage.titleLinks))
    ConfigEncoder.ObjectBuilder.empty
      .withValue("logo", landingPage.logo)
      .withValue("title", landingPage.title)
      .withValue("subtitle", landingPage.subtitle)
      .withValue("latestReleases", landingPage.latestReleases)
      .withValue("license", landingPage.license)
      .withValue("titleLinks", titleLinks)
      .withValue("documentationLinks", landingPage.documentationLinks)
      .withValue("projectLinks", landingPage.projectLinks)
      .withValue("teaserRows", buildTeaserRows(landingPage.teasers))
      .build
  }

  implicit val topNavBarEncoder: ConfigEncoder[TopNavigationBar] = ConfigEncoder[TopNavigationBar] { navBar =>
    ConfigEncoder.ObjectBuilder.empty
      .withValue("home", navBar.homeLink)
      .withValue("links", navBar.navLinks)
      .withValue("phoneLinks", navBar.navLinks.collect { case s: ThemeLinkSpan => s })
      .withValue("versionMenu", navBar.versionMenu)
      .build
  }

  implicit val mainNavEncoder: ConfigEncoder[MainNavigation] = ConfigEncoder[MainNavigation] { mainNav =>
    def encodeLink (link: TextLink): ConfigValue = ConfigEncoder.ObjectBuilder.empty
      .withValue("target", link.target.render())
      .withValue("title", link.text)
      .withValue("depth", 1)
      .build
    def encodeSection (section: ThemeNavigationSection): ConfigValue = ConfigEncoder.ObjectBuilder.empty
      .withValue("title", section.title)
      .withValue("entries", section.links.map(encodeLink).toList)
      .build
    ConfigEncoder.ObjectBuilder.empty
      .withValue("depth", mainNav.depth)
      .withValue("excludeSections", !mainNav.includePageSections)
      .withValue("prependLinks", mainNav.prependLinks.map(encodeSection))
      .withValue("appendLinks", mainNav.appendLinks.map(encodeSection))
      .build
  }

  private case class SourceEditLinks (baseURL: String, text: String, icon: Icon)

  private implicit val sourceEditLinksEncoder: ConfigEncoder[SourceEditLinks] = ConfigEncoder[SourceEditLinks] { editLinks =>
    ConfigEncoder.ObjectBuilder.empty
      .withValue("baseURL", editLinks.baseURL)
      .withValue("text", editLinks.text)
      .withValue("icon", editLinks.icon)
      .build
  }
  
  implicit val pageNavEncoder: ConfigEncoder[PageNavigation] = ConfigEncoder[PageNavigation] { pageNav =>
    val editLinks = pageNav.sourceBaseURL.map { baseURL =>
      SourceEditLinks(baseURL.stripSuffix("/"), pageNav.sourceLinkText, HeliumIcon.edit)
    }
    ConfigEncoder.ObjectBuilder.empty
      .withValue("enabled", pageNav.enabled)
      .withValue("depth", pageNav.depth)
      .withValue("sourceEditLinks", editLinks)
      .build
  }

  implicit val pdfLayoutEncoder: ConfigEncoder[PDFLayout] = ConfigEncoder[PDFLayout] { layout =>
    ConfigEncoder.ObjectBuilder.empty
      .withValue("pageHeight", layout.pageHeight.displayValue)
      .withValue("pageWidth", layout.pageWidth.displayValue)
      .withValue("marginTop", layout.marginTop.displayValue)
      .withValue("marginBottom", layout.marginBottom.displayValue)
      .withValue("marginLeft", layout.marginLeft.displayValue)
      .withValue("marginRight", layout.marginRight.displayValue)
      .build
  }

  implicit val themeFontsEncoder: ConfigEncoder[ThemeFonts] = ConfigEncoder[ThemeFonts] { fonts =>
    ConfigEncoder.ObjectBuilder.empty
      .withValue("headlines", fonts.headlines)
      .withValue("body", fonts.body)
      .withValue("code", fonts.code)
      .build
  }
  
  implicit val favIconEncoder: ConfigEncoder[Favicon] = ConfigEncoder[Favicon] { icon =>
    ConfigEncoder.ObjectBuilder.empty
      .withValue("target", icon.target.render())
      .withValue("sizes", icon.sizes)
      .withValue("type", icon.mediaType)
      .build
  }
  
  private val templatePaths: Map[String, Path] = {
    Seq("head", "topNav", "mainNav", "pageNav", "footer").map { name =>
      name -> Root / "helium" / "templates" / s"$name.template.html"
    }.toMap
  }

  def populateConfig (helium: Helium): Config =
    ConfigBuilder.empty
      .withValue("helium.site.landingPage", helium.siteSettings.content.landingPage)
      .withValue("helium.site.topNavigation", helium.siteSettings.content.topNavigationBar)
      .withValue("helium.site.mainNavigation", helium.siteSettings.content.mainNavigation)
      .withValue("helium.site.pageNavigation", helium.siteSettings.content.pageNavigation)
      .withValue("helium.site.footer", helium.siteSettings.content.footer)
      .withValue("helium.site.favIcons", helium.siteSettings.content.favIcons)
      .withValue("helium.site.templates", templatePaths)
      .withValue("laika.site.metadata", helium.siteSettings.metadata)
      .withValue("laika.epub", helium.epubSettings.bookConfig)
      .withValue("laika.pdf", helium.pdfSettings.bookConfig)
      .withValue("helium.pdf", helium.pdfSettings.layout)
      .withValue("helium.webFonts", helium.siteSettings.fontResources.flatMap { _.resource.webCSS })
      .withValue("helium.site.includeEPUB", helium.siteSettings.content.downloadPage.fold(false)(_.includeEPUB))
      .withValue("helium.site.includePDF", helium.siteSettings.content.downloadPage.fold(false)(_.includePDF))
      .withValue(LaikaKeys.site.css.child("globalSearchPaths"), (Root / "helium") +: helium.siteSettings.content.htmlIncludes.includeCSS)
      .withValue(LaikaKeys.site.js.child("globalSearchPaths"), (Root / "helium") +: helium.siteSettings.content.htmlIncludes.includeJS)
      .withValue(LaikaKeys.epub.css.child("globalSearchPaths"), (Root / "helium") +: helium.epubSettings.htmlIncludes.includeCSS)
      .withValue(LaikaKeys.epub.js.child("globalSearchPaths"), (Root / "helium") +: helium.epubSettings.htmlIncludes.includeJS)
      .withValue("helium.site.fontFamilies", helium.siteSettings.themeFonts)
      .withValue("helium.epub.fontFamilies", helium.epubSettings.themeFonts)
      .withValue("helium.pdf.fontFamilies", helium.pdfSettings.themeFonts)
      .withValue(LaikaKeys.siteBaseURL, helium.siteSettings.baseURL)
      .withValue(LaikaKeys.versions, helium.siteSettings.versions)
      .withValue("laika.pdf.coverImages", helium.pdfSettings.coverImages)
      .withValue("laika.epub.coverImages", helium.epubSettings.coverImages)
      .withValue("laika.pdf.coverImage", helium.pdfSettings.coverImages.find(_.classifier.isEmpty).map(_.path))
      .withValue("laika.epub.coverImage", helium.epubSettings.coverImages.find(_.classifier.isEmpty).map(_.path))
      .withValue(HeliumIcon.registry)
      .withValue("helium.landingPage", helium.siteSettings.content.landingPage) // legacy key
      .withValue("helium.topBar", helium.siteSettings.content.topNavigationBar) // legacy key
      .withValue("helium.favIcons", helium.siteSettings.content.favIcons) // legacy key
      .build
  
}

/** Utility for splitting a collection into a balanced group of items as opposed to the unbalanced 
  * `grouped` method of the Scala collection API.
  */
object BalancedGroups {

  /** Creates a balanced group of items based on the given desired size.
    */
  def create[A] (items: Vector[A], size: Int): Vector[Vector[A]] = {
    val mod = items.size % size
    val loSize = items.size / size
    val hiSize = loSize + 1
    val (hi,lo) = items.splitAt(mod * hiSize)
    val hiBatch = if (mod > 0)    hi.grouped(hiSize) else Vector()
    val loBatch = if (loSize > 0) lo.grouped(loSize) else Vector()
    hiBatch.toVector ++ loBatch.toVector
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy