Package freenet.clients.http

Source Code of freenet.clients.http.PageMaker$RenderParameters

package freenet.clients.http;

import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import freenet.client.filter.PushingTagReplacerCallback;
import freenet.l10n.NodeL10n;
import freenet.node.DarknetPeerNode;
import freenet.node.Node;
import freenet.node.SecurityLevels;
import freenet.pluginmanager.FredPluginL10n;
import freenet.support.HTMLNode;
import freenet.support.Logger;
import freenet.support.api.HTTPRequest;

/** Simple class to output standard heads and tail for web interface pages.
*/
public final class PageMaker {
 
  public enum THEME {
    BOXED("boxed", "Boxed (Top menu)", "", false, false),
    BOXED_CLASSIC("boxed-classic", "Boxed (Classic menu)", "", false, false),
    BOXED_DROPDOWN("boxed-dropdown", "Boxed (Dropdown menu)", "", false, false),
    BOXED_DYNAMIC("boxed-classic", "Boxed (Dynamic menu)", "", false, false),
    BOXED_STATIC("boxed-static", "Boxed (Static menu)", "", false, false),
    CLEAN("clean", "Clean", "Mr. Proper", false, false),
    CLEAN_CLASSIC("clean-classic", "Clean (Classic menu)", "Clean theme with a classic menu.", false, false),
    CLEAN_DROPDOWN("clean-dropdown", "Clean (Dropdown menu)", "Clean theme with a dropdown menu.", false, false),
    CLEAN_STATIC("clean-static", "Clean (Static menu)", "Clean theme with a static menu.", false, false),
    CLEAN_TOP("clean-top", "Clean (Top menu)", "Clean theme with a static top menu.", false, false),
    GRAYANDBLUE("grayandblue", "Gray And Blue (Classic menu)", "", false, false),
    GRAYANDBLUE_DYNAMIC("grayandblue-dynamic", "Gray And Blue (Dynamic menu)", "", false, false),
    GRAYANDBLUE_DROPDOWN("grayandblue-dropdown", "Gray And Blue (Dropdown menu)", "", false, false),
    GRAYANDBLUE_STATIC("grayandblue-static", "Gray And Blue (Static menu)", "", false, false),
    GRAYANDBLUE_TOP("grayandblue-top", "Gray And Blue (Top menu)", "", false, false),
    SKY("sky", "Sky (Top menu)", "", false, false),
    SKY_CLASSIC("sky-classic", "Sky (Classic menu)", "", false, false),
    SKY_DROPDOWN("sky-dropdown", "Sky (Dropdown menu)", "", false, false),
    SKY_DYNAMIC("sky-dynamic", "Sky (Dynamic menu)", "", false, false),
    SKY_STATIC("sky-static", "Sky (Static menu)", "", false, false),
    MINIMALBLUE("minimalblue", "Minimal Blue", "A minimalistic theme in blue", false, false),
    MINIMALISTIC("minimalist", "Minimalistic", "A very minimalistic theme based on Google's designs", true, true),
    RABBIT_HOLE("rabbit-hole", "Into the Rabbit Hole", "Simple and clean theme", false, false);

   
    public static final String[] possibleValues = {
      BOXED.code,
      BOXED_CLASSIC.code,
      BOXED_DROPDOWN.code,
      BOXED_DYNAMIC.code,
      BOXED_STATIC.code,
      CLEAN.code,
      CLEAN_CLASSIC.code,
      CLEAN_DROPDOWN.code,
      CLEAN_STATIC.code,
      CLEAN_TOP.code,
      GRAYANDBLUE.code,
      GRAYANDBLUE_DYNAMIC.code,
      GRAYANDBLUE_DROPDOWN.code,
      GRAYANDBLUE_STATIC.code,
      GRAYANDBLUE_TOP.code,
      SKY.code,
      SKY_CLASSIC.code,
      SKY_DROPDOWN.code,
      SKY_DYNAMIC.code,
      SKY_STATIC.code,
      MINIMALBLUE.code,
      MINIMALISTIC.code,
      RABBIT_HOLE.code
    };
   
    public final String code;  // the internal name
    public final String name;  // the name in "human form"
    public final String description; // description
    /**
     * If true, the activelinks will appear on the welcome page, whether
     * the user has enabled them or not.
     */
    public final boolean forceActivelinks;
    /**
     * If true, the "Fetch a key" infobox will appear above the bookmarks
     * infobox on the welcome page.
     */
    public final boolean fetchKeyBoxAboveBookmarks;
   
    private THEME(String code, String name, String description) {
      this(code, name, description, false, false);
    }

    private THEME(String code, String name, String description, boolean forceActivelinks, boolean fetchKeyBoxAboveBookmarks) {
      this.code = code;
      this.name = name;
      this.description = description;
      this.forceActivelinks = forceActivelinks;
      this.fetchKeyBoxAboveBookmarks = fetchKeyBoxAboveBookmarks;
    }

    public static THEME themeFromName(String cssName) {
      for(THEME t : THEME.values()) {
        if(t.code.equalsIgnoreCase(cssName) ||
           t.name.equalsIgnoreCase(cssName))
        {
          return t;
        }
      }
      return getDefault();
    }

    public static THEME getDefault() {
      return THEME.CLEAN;
    }
  } 
 
  public static final int MODE_SIMPLE = 1;
  public static final int MODE_ADVANCED = 2;

  /** Parameter for simple/advanced mode switch. */
  private static final String MODE_SWITCH_PARAMETER = "fproxyAdvancedMode";

  private THEME theme;
  private String override;
  private final Node node;
 
  private List<SubMenu> menuList = new ArrayList<SubMenu>();
  private Map<String, SubMenu> subMenus = new HashMap<String, SubMenu>();
 
  private static class SubMenu {
   
    /** Name of the submenu */
    private final String navigationLinkText;
    /** Link if the user clicks on the submenu itself */
    private final String defaultNavigationLink;
    /** Tooltip */
    private final String defaultNavigationLinkTitle;
   
    private final FredPluginL10n plugin;
   
    private final List<String> navigationLinkTexts = new ArrayList<String>();
    private final List<String> navigationLinkTextsNonFull = new ArrayList<String>();
    private final Map<String, String> navigationLinkTitles = new HashMap<String, String>();
    private final Map<String, String> navigationLinks = new HashMap<String, String>();
    private final Map<String, LinkEnabledCallback>  navigationLinkCallbacks = new HashMap<String, LinkEnabledCallback>();
    private final Map<String, FredPluginL10n> navigationLinkL10n = new HashMap<String, FredPluginL10n>();
   
    public SubMenu(String link, String name, String title, FredPluginL10n plugin) {
      this.navigationLinkText = name;
      this.defaultNavigationLink = link;
      this.defaultNavigationLinkTitle = title;
      this.plugin = plugin;
    }

    public void addNavigationLink(String path, String name, String title, boolean fullOnly, LinkEnabledCallback cb, FredPluginL10n l10n) {
      navigationLinkTexts.add(name);
      if(!fullOnly)
        navigationLinkTextsNonFull.add(name);
      navigationLinkTitles.put(name, title);
      navigationLinks.put(name, path);
      if(cb != null)
        navigationLinkCallbacks.put(name, cb);
      if (l10n != null)
        navigationLinkL10n.put(name, l10n);
    }

    /** Remove a link from this sub-menu. */
    public void removeNavigationLink(String name) {
      navigationLinkTexts.remove(name);
      navigationLinkTextsNonFull.remove(name);
      navigationLinkTitles.remove(name);
      navigationLinks.remove(name);
      navigationLinkL10n.remove(name); //Should this be here? If so, why not remove from navigationLinkCallbacks too
    }

  }
 
  protected PageMaker(THEME t, Node n) {
    setTheme(t);
    this.node = n;
  }
 
  void setOverride(String pointTo) {
    this.override = pointTo;
  }
 
  public void setTheme(THEME theme2) {
    if (theme2 == null) {
      this.theme = THEME.getDefault();
    } else {
      URL themeurl = getClass().getResource("staticfiles/themes/" + theme2.code + "/theme.css");
      if (themeurl == null)
        this.theme = THEME.getDefault();
      else
        this.theme = theme2;
    }
  }

  public synchronized void addNavigationCategory(String link, String name, String title, FredPluginL10n plugin) {
    SubMenu menu = new SubMenu(link, name, title, plugin);
    subMenus.put(name, menu);
    menuList.add(menu);
  }
 
  /**
   * Add a navigation category to the menu at a given offset.
   * @param menuOffset The position of the link in FProxy's menu. 0 = left.
   */
  public synchronized void addNavigationCategory(String link, String name, String title, FredPluginL10n plugin, int menuOffset) {
    SubMenu menu = new SubMenu(link, name, title, plugin);
    subMenus.put(name, menu);
    menuList.add(menuOffset, menu);
  }
 

  public synchronized void removeNavigationCategory(String name) {
    SubMenu menu = subMenus.remove(name);
    if (menu == null) {
      Logger.error(this, "can't remove navigation category, name="+name);
      return;
   
    menuList.remove(menu);
  }
 
  public synchronized void addNavigationLink(String menutext, String path, String name, String title, boolean fullOnly, LinkEnabledCallback cb, FredPluginL10n l10n) {
    SubMenu menu = subMenus.get(menutext);
    if(menu == null)
      throw new NullPointerException("there is no menu named "+menutext);
    menu.addNavigationLink(path, name, title, fullOnly, cb, l10n);
  }

  /** Remove a navigation link from a sub-menu. Applies globally, do not use this to customise
   * menus when sending one page! */
  public synchronized void removeNavigationLink(String menutext, String name) {
    SubMenu menu = subMenus.get(menutext);
    // The menu may have already been removed.
    if(menu != null)
        menu.removeNavigationLink(name);
  }
 
  public HTMLNode createBackLink(ToadletContext toadletContext, String name) {
    String referer = toadletContext.getHeaders().get("referer");
    if (referer != null) {
      return new HTMLNode("a", new String[] { "href", "title" }, new String[] { referer, name }, name);
    }
    return new HTMLNode("a", new String[] { "href", "title" }, new String[] { "javascript:back()", name }, name);
  }

  /**
   * Generates an FProxy template page suitable for adding content to.
   *
   * @param title
   *            Title of the page.
   * @param ctx
   *            ToadletContext to use to render the page.
   * @return A template PageNode.
   */
  public PageNode getPageNode(String title, ToadletContext ctx) {
    return getPageNode(title, true, ctx);
  }

  /**
   * Generates an FProxy template page with optional navigation bar suitable
   * for adding content to.
   *
   * @param title
   *            Title of the page.
   * @param renderNavigationLinks
   *            Whether to render navigation links.
   * @param ctx
   *            ToadletContext to use to render the page.
   * @return A template PageNode.
   * @deprecated Use
   *             {@link #getPageNode(String, ToadletContext, RenderParameters)}
   *             instead
   */
  @Deprecated
  public PageNode getPageNode(String title, boolean renderNavigationLinks, ToadletContext ctx) {
    return getPageNode(title, renderNavigationLinks, true, ctx);
  }

  /**
   * Generates an FProxy template page with optional navigation bar and status
   * information suitable for adding content to.
   *
   * @param title
   *            Title of the page.
   * @param renderNavigationLinks
   *            Whether to render navigation links.
   * @param renderStatus
   *            Whether to render the status display.
   * @param ctx
   *            ToadletContext to use to render the page.
   * @return A template PageNode.
   * @deprecated Use
   *             {@link #getPageNode(String, ToadletContext, RenderParameters)}
   *             instead
   */
  @Deprecated
  public PageNode getPageNode(String title, boolean renderNavigationLinks, boolean renderStatus, ToadletContext ctx) {
    return getPageNode(title, ctx, new RenderParameters().renderNavigationLinks(renderNavigationLinks).renderStatus(renderStatus).renderModeSwitch(true));
  }

  /**
   * Generates an FProxy template page suitable for adding content to.
   *
   * @param title
   *            Title of the page.
   * @param ctx
   *            ToadletContext to use to render the page. Can be null, e.g. if the HTML is not
   *            being generated as part of a toadlet request, for example if it's using the old
   *            FredPluginHTTP interface.
   * @param renderParameters
   *            Parameters for inclusion or omission of certain page elements
   * @return A template PageNode.
   */
  public PageNode getPageNode(String title, ToadletContext ctx, RenderParameters renderParameters) {
    boolean fullAccess = ctx == null ? false : ctx.isAllowedFullAccess();
    HTMLNode pageNode = new HTMLNode.HTMLDoctype("html", "-//W3C//DTD XHTML 1.1//EN");
    HTMLNode htmlNode = pageNode.addChild("html", "xml:lang", NodeL10n.getBase().getSelectedLanguage().isoCode);
    HTMLNode headNode = htmlNode.addChild("head");
    headNode.addChild("meta", new String[] { "http-equiv", "content" }, new String[] { "Content-Type", "text/html; charset=utf-8" });
    headNode.addChild("title", title + " - Freenet");
    //To make something only rendered when javascript is on, then add the jsonly class to it
    headNode.addChild("noscript").addChild("style"," .jsonly {display:none;}");
    if(override != null)
      headNode.addChild(getOverrideContent());
    else
      headNode.addChild("link", new String[] { "rel", "href", "type", "title" }, new String[] { "stylesheet", "/static/themes/" + theme.code + "/theme.css", "text/css", theme.code });
   
    boolean sendAllThemes =  ctx != null && ctx.getContainer().sendAllThemes();
   
    if(sendAllThemes) {
      for (THEME t: THEME.values()) {
        String themeName = t.code;
        headNode.addChild("link", new String[] { "rel", "href", "type", "media", "title" }, new String[] { "alternate stylesheet", "/static/themes/" + themeName + "/theme.css", "text/css", "screen", themeName });
      }
    }
   
    boolean webPushingEnabled =
      ctx != null && ctx.getContainer().isFProxyJavascriptEnabled() && ctx.getContainer().isFProxyWebPushingEnabled();
   
    // Add the generated javascript, if it and pushing is enabled
    if (webPushingEnabled) headNode.addChild("script", new String[] { "type", "language", "src" }, new String[] {
        "text/javascript", "javascript", "/static/freenetjs/freenetjs.nocache.js" });
   
    Toadlet t;
    if (ctx != null) {
      t = ctx.activeToadlet();
      t = t.showAsToadlet();
    } else
      t = null;
    String activePath = "";
    if(t != null) activePath = t.path();
    HTMLNode bodyNode = htmlNode.addChild("body",
            new String[] { "class", "id" },
            new String[] { "fproxy-page", filterCSSIdentifier("page-"+activePath) });
    //Add a hidden input that has the request's id
    if(webPushingEnabled)
      bodyNode.addChild("input",new String[]{"type","name","value","id"},new String[]{"hidden","requestId",ctx.getUniqueId(),"requestId"});
   
    // Add the client-side localization only when pushing is enabled
    if (webPushingEnabled) {
      bodyNode.addChild("script", new String[] { "type", "language" }, new String[] { "text/javascript", "javascript" }).addChild("%", PushingTagReplacerCallback.getClientSideLocalizationScript());
    }
   
    HTMLNode pageDiv = bodyNode.addChild("div", "id", "page");
    HTMLNode topBarDiv = pageDiv.addChild("div", "id", "topbar");

    if (renderParameters.isRenderStatus() && fullAccess) {
      final HTMLNode statusBarDiv = pageDiv.addChild("div", "id", "statusbar-container").addChild("div", "id", "statusbar");

       if (node != null && node.clientCore != null) {
         final HTMLNode alerts = ctx.getAlertManager().createSummary(true);
         if (alerts != null) {
           statusBarDiv.addChild(alerts).addAttribute("id", "statusbar-alerts");
           statusBarDiv.addChild("div", "class", "separator", "\u00a0");
         }
       }


      statusBarDiv.addChild("div", "id", "statusbar-language").addChild("a", "href", "/config/node#l10n", NodeL10n.getBase().getSelectedLanguage().fullName);

      if (node.clientCore != null && ctx != null && renderParameters.isRenderModeSwitch()) {
        boolean isAdvancedMode = ctx.isAdvancedModeEnabled();
        String uri = ctx.getUri().getQuery();
        Map<String, List<String>> parameters = HTTPRequestImpl.parseUriParameters(uri, true);
        List<String> newModeSwitchValues = new ArrayList<String>();
        newModeSwitchValues.add(String.valueOf(isAdvancedMode ? MODE_SIMPLE : MODE_ADVANCED));
        /* overwrite any previously existing parameter value. */
        parameters.put(MODE_SWITCH_PARAMETER, newModeSwitchValues);

        statusBarDiv.addChild("div", "class", "separator", "\u00a0");
        final HTMLNode switchMode = statusBarDiv.addChild("div", "id", "statusbar-switchmode");
        switchMode.addAttribute("class", isAdvancedMode ? "simple" : "advanced");
        switchMode.addChild("a", "href", "?" + HTTPRequestImpl.createQueryString(parameters, false), isAdvancedMode ? NodeL10n.getBase().getString("StatusBar.switchToSimpleMode") : NodeL10n.getBase().getString("StatusBar.switchToAdvancedMode"));
      }

      if (node != null && node.clientCore != null) {
        statusBarDiv.addChild("div", "class", "separator", "\u00a0");
        final HTMLNode secLevels = statusBarDiv.addChild("div", "id", "statusbar-seclevels", NodeL10n.getBase().getString("SecurityLevels.statusBarPrefix"));

        final HTMLNode network = secLevels.addChild("a", "href", "/seclevels/", SecurityLevels.localisedName(node.securityLevels.getNetworkThreatLevel()) + "\u00a0");
        network.addAttribute("title", NodeL10n.getBase().getString("SecurityLevels.networkThreatLevelShort"));
        network.addAttribute("class", node.securityLevels.getNetworkThreatLevel().toString().toLowerCase());

        final HTMLNode physical = secLevels.addChild("a", "href", "/seclevels/", SecurityLevels.localisedName(node.securityLevels.getPhysicalThreatLevel()));
        physical.addAttribute("title", NodeL10n.getBase().getString("SecurityLevels.physicalThreatLevelShort"));
        physical.addAttribute("class", node.securityLevels.getPhysicalThreatLevel().toString().toLowerCase());

        statusBarDiv.addChild("div", "class", "separator", "\u00a0");

        final int connectedPeers = node.peers.countConnectedPeers();
        int darknetTotal = 0;
        for(DarknetPeerNode n : node.peers.getDarknetPeers()) {
          if(n == null) continue;
          if(n.isDisabled()) continue;
          darknetTotal++;
        }
        final int connectedDarknetPeers = node.peers.countConnectedDarknetPeers();
        final int totalPeers = (node.getOpennet() == null) ? (darknetTotal > 0 ? darknetTotal : Integer.MAX_VALUE) : node.getOpennet().getNumberOfConnectedPeersToAimIncludingDarknet();
        final double connectedRatio = ((double)connectedPeers) / (double)totalPeers;
        final String additionalClass;

        // If we use Opennet, we color the bar by the ratio of connected nodes
        if(connectedPeers > connectedDarknetPeers) {
          if (connectedRatio < 0.3D || connectedPeers < 3) {
            additionalClass = "very-few-peers";
          } else if (connectedRatio < 0.5D) {
            additionalClass = "few-peers";
          } else if (connectedRatio < 0.75D) {
            additionalClass = "avg-peers";
          } else {
            additionalClass = "full-peers";
          }
        } else {
          // If we are darknet only, we color by absolute connected peers
          if (connectedDarknetPeers < 3) {
            additionalClass = "very-few-peers";
          } else if (connectedDarknetPeers < 5) {
            additionalClass = "few-peers";
          } else if (connectedDarknetPeers < 10) {
            additionalClass = "avg-peers";
          } else {
            additionalClass = "full-peers";
          }
        }

        HTMLNode progressBar = statusBarDiv.addChild("div", "class", "progressbar");
        progressBar.addChild("div", new String[] { "class", "style" }, new String[] { "progressbar-done progressbar-peers " + additionalClass, "width: " +
            Math.min(100,Math.floor(100*connectedRatio)) + "%;" });

        progressBar.addChild("div", new String[] { "class", "title" }, new String[] { "progress_fraction_finalized", NodeL10n.getBase().getString("StatusBar.connectedPeers", new String[]{"X", "Y"},
            new String[]{Integer.toString(node.peers.countConnectedDarknetPeers()), Integer.toString(node.peers.countConnectedOpennetPeers())}) },
            Integer.toString(connectedPeers) + ((totalPeers != Integer.MAX_VALUE) ? " / " + Integer.toString(totalPeers) : ""));
      }
    }

    topBarDiv.addChild("h1", title);
    if (renderParameters.isRenderNavigationLinks()) {
      SubMenu selected = null;
      // Render the full menu.
      HTMLNode navbarDiv = pageDiv.addChild("div", "id", "navbar");
      HTMLNode navbarUl = navbarDiv.addChild("ul", "id", "navlist");
      synchronized (this) {
        for (SubMenu menu : menuList) {
          HTMLNode subnavlist = new HTMLNode("ul");
          boolean isSelected = false;
          boolean nonEmpty = false;
          for (String navigationLink :  fullAccess ? menu.navigationLinkTexts : menu.navigationLinkTextsNonFull) {
            LinkEnabledCallback cb = menu.navigationLinkCallbacks.get(navigationLink);
            if(cb != null && !cb.isEnabled(ctx)) continue;
            nonEmpty = true;
            String navigationTitle = menu.navigationLinkTitles.get(navigationLink);
            String navigationPath = menu.navigationLinks.get(navigationLink);
            HTMLNode sublistItem;
            if(activePath.equals(navigationPath)) {
              sublistItem = subnavlist.addChild("li", "class", "submenuitem-selected");
              isSelected = true;
            } else {
              sublistItem = subnavlist.addChild("li", "class", "submenuitem-not-selected");
            }
           
            FredPluginL10n l10n = menu.navigationLinkL10n.get(navigationLink);
            if(l10n == null) l10n = menu.plugin;
            if(l10n != null) {
              // From a plugin. Include the plugin name in the id.
              sublistItem.addAttribute("id", getPluginL10nCSSIdentifier(l10n, navigationTitle));

              if(navigationTitle != null) {
                String newNavigationTitle = l10n.getString(navigationTitle);
                if(newNavigationTitle == null) {
                  Logger.error(this, "Plugin '"+l10n+"' did return null in getString(key)!");
                } else {
                  navigationTitle = newNavigationTitle;
                }
              }
              if(navigationLink != null) {
                String newNavigationLink = l10n.getString(navigationLink);
                if(newNavigationLink == null) {
                  Logger.error(this, "Plugin '"+l10n+"' did return null in getString(key)!");
                } else {
                  navigationLink = newNavigationLink;
                }
              }
            } else {
              // Not from a plugin. Add the localization key as id.
              sublistItem.addAttribute("id", filterCSSIdentifier(navigationTitle));

              if(navigationTitle != null) navigationTitle = NodeL10n.getBase().getString(navigationTitle);
              if(navigationLink != null) navigationLink = NodeL10n.getBase().getString(navigationLink);
            }
            if(navigationTitle != null)
              sublistItem.addChild("a", new String[] { "href", "title" }, new String[] { navigationPath, navigationTitle }, navigationLink);
            else
              sublistItem.addChild("a", "href", navigationPath, navigationLink);
          }
          if(nonEmpty) {
            HTMLNode listItem;
            if(isSelected) {
              selected = menu;
              subnavlist.addAttribute("class", "subnavlist-selected");
              listItem = new HTMLNode("li", "class", "navlist-selected");
            } else {
              subnavlist.addAttribute("class", "subnavlist");
              listItem = new HTMLNode("li", "class", "navlist-not-selected");
            }
            String menuItemTitle = menu.defaultNavigationLinkTitle;
            String text = menu.navigationLinkText;
            if(menu.plugin == null) {
              // Not from a plugin. Add the localization key as id.
              listItem.addAttribute("id", filterCSSIdentifier(menuItemTitle));

              menuItemTitle = NodeL10n.getBase().getString(menuItemTitle);
              text = NodeL10n.getBase().getString(text);
            } else {
              /*
               * From a plugin. Include the plugin name in the id.
               *
               * Note that a plugin could misbehave and fail to register its
               * menu with proper localization keys.
               */
              listItem.addAttribute("id", getPluginL10nCSSIdentifier(menu.plugin, text));

              String newTitle = menu.plugin.getString(menuItemTitle);
              if(newTitle == null) {
                Logger.error(this, "Plugin '"+menu.plugin+"' did return null in getString(key)!");
              } else {
                menuItemTitle = newTitle;
              }
              String newText = menu.plugin.getString(text);
              if(newText == null) {
                Logger.error(this, "Plugin '"+menu.plugin+"' did return null in getString(key)!");
              } else {
                text = newText;
              }
            }
           
            listItem.addChild("a", new String[] { "href", "title" }, new String[] { menu.defaultNavigationLink, menuItemTitle }, text);
            listItem.addChild(subnavlist);
            navbarUl.addChild(listItem);
          }
        }
      }
      // Some themes want the selected submenu separately.
      if(selected != null) {
        HTMLNode div = new HTMLNode("div", "id", "selected-subnavbar");
        HTMLNode subnavlist = div.addChild("ul", "id", "selected-subnavbar-list");
        boolean nonEmpty = false;
        for (String navigationLink :  fullAccess ? selected.navigationLinkTexts : selected.navigationLinkTextsNonFull) {
          LinkEnabledCallback cb = selected.navigationLinkCallbacks.get(navigationLink);
          if(cb != null && !cb.isEnabled(ctx)) continue;
          nonEmpty = true;
          String navigationTitle = selected.navigationLinkTitles.get(navigationLink);
          String navigationPath = selected.navigationLinks.get(navigationLink);
          HTMLNode sublistItem;
          if(activePath.equals(navigationPath)) {
            sublistItem = subnavlist.addChild("li", "class", "submenuitem-selected");
          } else {
            sublistItem = subnavlist.addChild("li", "class", "submenuitem-not-selected");
          }
         
          FredPluginL10n l10n = selected.navigationLinkL10n.get(navigationLink);
          if (l10n == null) l10n = selected.plugin;
          if(l10n != null) {
            if(navigationTitle != null) navigationTitle = l10n.getString(navigationTitle);
            if(navigationLink != null) navigationLink = l10n.getString(navigationLink);
          } else {
            if(navigationTitle != null) navigationTitle = NodeL10n.getBase().getString(navigationTitle);
            if(navigationLink != null) navigationLink = NodeL10n.getBase().getString(navigationLink);
          }
          if(navigationTitle != null)
            sublistItem.addChild("a", new String[] { "href", "title" }, new String[] { navigationPath, navigationTitle }, navigationLink);
          else
            sublistItem.addChild("a", "href", navigationPath, navigationLink);
        }
        if(nonEmpty)
          pageDiv.addChild(div);
      }
    }
    HTMLNode contentDiv = pageDiv.addChild("div", "id", "content");
    return new PageNode(pageNode, headNode, contentDiv);
  }

  /**
   * Create a CSS identifier incorporating both a class name and a localization key.
   * @param plugin plugin localization instance (used for class name)
   * @param key localization key
   * @return valid CSS identifier.
   */
  // TODO: Less-stupid name.
  public static String getPluginL10nCSSIdentifier(FredPluginL10n plugin, String key) {
    return filterCSSIdentifier(plugin.getClass().getName()+'-'+key);
  }

  /**
   * Filters a given string so that it will be a valid CSS identifier. It replaces all characters that are not
   * a dash, underscore, or alphanumeric with an underscore. If the first character is a dash and the second
   * character is not a letter or underscore, replaces the second character with an underscore. This filter is
   * overly strict as it does not allow non-ASCII characters or escapes. If the given string is below two
   * characters in length, it appends underscores until it is not.
   * @param input string to filter
   * @return a filtered string guaranteed to be a syntactically valid CSS identifier.
   * @link http://www.w3.org/TR/CSS21/syndata.html#tokenization
   * @link http://www.w3.org/TR/CSS21/grammar.html#scanner
   * @link http://stackoverflow.com/questions/448981/
   */
  public static String filterCSSIdentifier(String input) {
    while (input.length() < 2) input = input.concat("_");

    return input.replaceFirst("^-[^_a-zA-Z]", "-_").replaceAll("[^-_a-zA-Z0-9]", "_");
  }

  public THEME getTheme() {
    return this.theme;
  }

  public InfoboxNode getInfobox(String header) {
    return getInfobox(header, null, false);
  }

  public InfoboxNode getInfobox(HTMLNode header) {
    return getInfobox(header, null, false);
  }

  public InfoboxNode getInfobox(String category, String header) {
    return getInfobox(category, header, null, false);
  }

  public HTMLNode getInfobox(String category, String header, HTMLNode parent) {
    return getInfobox(category, header, parent, null, false);
  }

  public InfoboxNode getInfobox(String category, HTMLNode header) {
    return getInfobox(category, header, null, false);
  }

  public InfoboxNode getInfobox(String header, String title, boolean isUnique) {
    if (header == null) throw new NullPointerException();
    return getInfobox(new HTMLNode("#", header), title, isUnique);
  }
 
  public InfoboxNode getInfobox(HTMLNode header, String title, boolean isUnique) {
    if (header == null) throw new NullPointerException();
    return getInfobox(null, header, title, isUnique);
  }

  public InfoboxNode getInfobox(String category, String header, String title, boolean isUnique) {
    if (header == null) throw new NullPointerException();
    return getInfobox(category, new HTMLNode("#", header), title, isUnique);
  }

  /** Create an infobox, attach it to the given parent, and return the content node. */
  public HTMLNode getInfobox(String category, String header, HTMLNode parent, String title, boolean isUnique) {
    InfoboxNode node = getInfobox(category, header, title, isUnique);
    parent.addChild(node.outer);
    return node.content;
  }

  /**
   * Returns an infobox with the given style and header.
   *
   * @param category
   *            The CSS styles, separated by a space (' ')
   * @param header
   *            The header HTML node
   * @return The infobox
   */
  public InfoboxNode getInfobox(String category, HTMLNode header, String title, boolean isUnique) {
    if (header == null) throw new NullPointerException();

    StringBuffer classes = new StringBuffer("infobox");
    if(category != null) {
      classes.append(" ");
      classes.append(category);
    }
    if(title != null && !isUnique) {
      classes.append(" ");
      classes.append(title);
    }

    HTMLNode infobox = new HTMLNode("div", "class", classes.toString());

    if(title != null && isUnique) {
      infobox.addAttribute("id", title);
    }

    infobox.addChild("div", "class", "infobox-header").addChild(header);
    return new InfoboxNode(infobox, infobox.addChild("div", "class", "infobox-content"));
  }
 
  private HTMLNode getOverrideContent() {
    return new HTMLNode("link", new String[] { "rel", "href", "type", "media", "title" }, new String[] { "stylesheet", override, "text/css", "screen", "custom" });
  }

  public boolean advancedMode(HTTPRequest req, ToadletContainer container) {
    return parseMode(req, container) >= MODE_ADVANCED;
  }

  /** Call this before getPageNode(), so the menus reflect the advanced mode setting. */
  public int parseMode(HTTPRequest req, ToadletContainer container) {
    int mode = container.isAdvancedModeEnabled() ? MODE_ADVANCED : MODE_SIMPLE;

    if(req.isParameterSet(MODE_SWITCH_PARAMETER)) {
      mode = req.getIntParam(MODE_SWITCH_PARAMETER, mode);
      if(mode == MODE_ADVANCED)
        container.setAdvancedMode(true);
      else
        container.setAdvancedMode(false);
    }
   
    return mode;
  }
 
  /**
   * Bundles parameters that are used to create the page node. The default for
   * the render parameters is to include all optional render tasks. Individual
   * tasks may be enabled or disabled by calling the appropriate methods which
   * returns a new {@link RenderParameters} object as {@link RenderParameters}
   * are immutable.
   *
   * @see PageMaker#getPageNode(String, ToadletContext, RenderParameters)
   * @author <a href="mailto:bombe@pterodactylus.net">David ?Bombe? Roden</a>
   */
  public static class RenderParameters {

    /** Whether to include navigation links in the page. */
    private final boolean renderNavigationLinks;

    /** Whether to include the status bar in the page. */
    private final boolean renderStatus;

    /** Whether to include the mode switch in the page. */
    private final boolean renderModeSwitch;

    /**
     * Creates default render parameters that include all elements.
     */
    public RenderParameters() {
      this(true, true, true);
    }

    /**
     * Creates render parameters.
     *
     * @param renderNavigationLinks
     *            {@code true} to include navigation links in the page
     * @param renderStatus
     *            {@code true} to include the status bar in the page
     * @param renderModeSwitch
     *            {@code true} to include the mode switch in the status bar
     */
    private RenderParameters(boolean renderNavigationLinks, boolean renderStatus, boolean renderModeSwitch) {
      this.renderNavigationLinks = renderNavigationLinks;
      this.renderStatus = renderStatus;
      this.renderModeSwitch = renderModeSwitch;
    }

    //
    // ACCESSORS
    //

    /**
     * Returns whether the navigation links should be included in the page.
     *
     * @return {@code true} if the navigation links should be included in
     *         the page, {@code false} otherwise
     */
    public boolean isRenderNavigationLinks() {
      return renderNavigationLinks;
    }

    /**
     * Returns a new {@link RenderParameters} object that renders the
     * navigation links according to the given parameter.
     *
     * @param renderNavigationLinks
     *            {@code true} to render the navigation links, {@code false}
     *            otherwise
     * @return A new {@link RenderParameters} object
     */
    public RenderParameters renderNavigationLinks(boolean renderNavigationLinks) {
      return new RenderParameters(renderNavigationLinks, renderStatus, renderModeSwitch);
    }

    /**
     * Returns whether the status bar should be included in the page.
     *
     * @return {@code true} if the status bar should be included in the
     *         page, {@code false} otherwise
     */
    public boolean isRenderStatus() {
      return renderStatus;
    }

    /**
     * Returns a new {@link RenderParameters} object that renders the status
     * bar according to the given parameter.
     *
     * @param renderStatus
     *            {@code true} to render the status bar, {@code false}
     *            otherwise
     * @return A new {@link RenderParameters} object
     */
    public RenderParameters renderStatus(boolean renderStatus) {
      return new RenderParameters(renderNavigationLinks, renderStatus, renderModeSwitch);
    }

    /**
     * Returns whether the mode switch should be included in the page.
     *
     * @return {@code true} if the mode switch should be included in the
     *         page, {@code false} otherwise
     */
    public boolean isRenderModeSwitch() {
      return renderModeSwitch;
    }

    /**
     * Returns a new {@link RenderParameters} object that renders the mode
     * switch according to the given parameter.
     *
     * @param renderModeSwitch
     *            {@code true} to render the mode switch, {@code false}
     *            otherwise
     * @return A new {@link RenderParameters} object
     */
    public RenderParameters renderModeSwitch(boolean renderModeSwitch) {
      return new RenderParameters(renderNavigationLinks, renderStatus, renderModeSwitch);
    }

  }

}
TOP

Related Classes of freenet.clients.http.PageMaker$RenderParameters

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.