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

net.liftweb.sitemap.FlexMenuBuilder.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2007-2012 WorldWide Conferencing, LLC
 *
 * 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 net.liftweb.sitemap

import net.liftweb.common._
import net.liftweb.http.{LiftRules, S}
import scala.xml.{Elem, Text, NodeSeq}
import net.liftweb.util.Helpers


trait FlexMenuBuilder {
  // a hack to use structural typing to get around the private[http] on Loc.buildItem
  type StructBuildItem = {def buildItem(kids: List[MenuItem], current: Boolean, path: Boolean): Box[MenuItem]}

  /**
   * Override if you want a link to the current page
   */
  def linkToSelf = false

  /**
   * Should all the menu items be expanded?  Defaults to false
   */
  def expandAll = false

  /**
   * Should any of the menu items be expanded?
   */
  protected def expandAny = false

  /**
   * This is used to build a MenuItem for a single Loc
   */
  protected def buildItemMenu[A](loc: Loc[A], currLoc: Box[Loc[_]], expandAll: Boolean): List[MenuItem] = {
    val isInPath = currLoc.map { cur =>
      def isInPath(loc: Loc[_]): Boolean = (cur == loc) || loc.menu.kids.exists(k => isInPath(k.loc))
      isInPath(loc)
    } openOr false

    val kids: List[MenuItem] = if (expandAll) loc.buildKidMenuItems(loc.menu.kids) else Nil
    loc.buildItem(kids, currLoc == Full(loc), isInPath).toList
  }


  /**
   * Compute the MenuItems to be rendered by looking at the 'item' and 'group' attributes
   */
  def toRender: Seq[MenuItem] = {
    val res = (S.attr("item"), S.attr("group")) match {
      case (Full(item), _) =>
        for {
          sm <- LiftRules.siteMap.toList
          req <- S.request.toList
          loc <- sm.findLoc(item).toList
          item <- buildItemMenu(loc, req.location, expandAll)
        } yield item

      case (_, Full(group)) =>
        for {
          sm <- LiftRules.siteMap.toList
          loc <- sm.locForGroup(group)
          req <- S.request.toList
          item <- buildItemMenu(loc, req.location, expandAll)
        } yield item
      case _ => renderWhat(expandAll)
    }
    res

  }

  /**
   * If a group is specified and the group is empty what to display
   */
  protected def emptyGroup: NodeSeq = NodeSeq.Empty

  /**
   * If the whole menu hierarchy is empty, what to display
   */
  protected def emptyMenu: NodeSeq = Text("No Navigation Defined.")

  /**
   * What to display when the placeholder is empty (has no kids)
   */
  protected def emptyPlaceholder: NodeSeq = NodeSeq.Empty

  /**
   * Take the incoming Elem and add any attributes based on
   * path which is true if this Elem is the path to the current page
   */
  protected def updateForPath(nodes: Elem, path: Boolean): Elem = nodes

  /**
   * Take the incoming Elem and add any attributes based on
   * current which is a flag that indicates this is the currently viewed page
   */
  protected def updateForCurrent(nodes: Elem, current: Boolean): Elem = nodes

  /**
   * By default, create an li for a menu item
   */
  protected def buildInnerTag(contents: NodeSeq, path: Boolean, current: Boolean): Elem =
    updateForCurrent(updateForPath(
  • {contents}
  • , path), current) /** * Render a placeholder */ protected def renderPlaceholder(item: MenuItem, renderInner: Seq[MenuItem] => NodeSeq): Elem = { buildInnerTag({item.text}{renderInner(item.kids)}, item.path, item.current) } /** * Render a link that's the current link, but the "link to self" flag is set to true */ protected def renderSelfLinked(item: MenuItem, renderInner: Seq[MenuItem] => NodeSeq): Elem = buildInnerTag({renderLink(item.uri, item.text, item.path, item.current)}{renderInner(item.kids)}, item.path, item.current) /** * Render the currently selected menu item, but with no a link back to self */ protected def renderSelfNotLinked(item: MenuItem, renderInner: Seq[MenuItem] => NodeSeq): Elem = buildInnerTag({renderSelf(item)}{renderInner(item.kids)}, item.path, item.current) /** * Render the currently selected menu item */ protected def renderSelf(item: MenuItem): NodeSeq = {item.text} /** * Render a generic link */ protected def renderLink(uri: NodeSeq, text: NodeSeq, path: Boolean, current: Boolean): NodeSeq = {text} /** * Render an item in the current path */ protected def renderItemInPath(item: MenuItem, renderInner: Seq[MenuItem] => NodeSeq): Elem = buildInnerTag({renderLink(item.uri, item.text, item.path, item.current)}{renderInner(item.kids)}, item.path, item.current) /** * Render a menu item that's neither in the path nor */ protected def renderItem(item: MenuItem, renderInner: Seq[MenuItem] => NodeSeq): Elem = buildInnerTag({renderLink(item.uri, item.text, item.path, item.current)}{renderInner(item.kids)}, item.path, item.current) /** * Render the outer tag for a group of menu items */ protected def renderOuterTag(inner: NodeSeq, top: Boolean): NodeSeq =
      {inner}
    /** * The default set of MenuItems to be rendered */ protected def renderWhat(expandAll: Boolean): Seq[MenuItem] = (if (expandAll) for { sm <- LiftRules.siteMap; req <- S.request } yield sm.buildMenu(req.location).lines else S.request.map(_.buildMenu.lines)) openOr Nil def render: NodeSeq = { val level: Box[Int] = for (lvs <- S.attr("level"); i <- Helpers.asInt(lvs)) yield i val toRender: Seq[MenuItem] = this.toRender def ifExpandCurrent(f: => NodeSeq): NodeSeq = if (expandAny || expandAll) f else NodeSeq.Empty def ifExpandAll(f: => NodeSeq): NodeSeq = if (expandAll) f else NodeSeq.Empty toRender.toList match { case Nil if S.attr("group").isDefined => emptyGroup case Nil => emptyMenu case xs => def buildANavItem(i: MenuItem): NodeSeq = { i match { // Per Loc.PlaceHolder, placeholder implies HideIfNoKids case m@MenuItem(text, uri, kids, _, _, _) if m.placeholder_? && kids.isEmpty => emptyPlaceholder case m@MenuItem(text, uri, kids, _, _, _) if m.placeholder_? => renderPlaceholder(m, buildLine _) case m@MenuItem(text, uri, kids, true, _, _) if linkToSelf => renderSelfLinked(m, k => ifExpandCurrent(buildLine(k))) case m@MenuItem(text, uri, kids, true, _, _) => renderSelfNotLinked(m, k => ifExpandCurrent(buildLine(k))) // Not current, but on the path, so we need to expand children to show the current one case m@MenuItem(text, uri, kids, _, true, _) => renderItemInPath(m, buildLine _) case m =>renderItem(m, buildLine _) } } def buildLine(in: Seq[MenuItem]): NodeSeq = buildUlLine(in, false) def buildUlLine(in: Seq[MenuItem], top: Boolean): NodeSeq = if (in.isEmpty) { NodeSeq.Empty } else { renderOuterTag(in.flatMap(buildANavItem), top) } val realMenuItems = level match { case Full(lvl) if lvl > 0 => def findKids(cur: Seq[MenuItem], depth: Int): Seq[MenuItem] = if (depth == 0) cur else findKids(cur.flatMap(mi => mi.kids), depth - 1) findKids(xs, lvl) case _ => xs } buildUlLine(realMenuItems, true) } } }




    © 2015 - 2024 Weber Informatics LLC | Privacy Policy