/**
   * Inserts a paragraph before or after the table containing the selection.
   *
   * <p>We decided to use Control/Meta+UpArrow for inserting a paragraph before a table and
   * Control/Meta+DownArrow for inserting a paragraph after a table. Here's the rationale:
   *
   * <ul>
   *   <li>We can't reliably detect if the user can place the caret before of after a table. Our
   *       playground is the DOM tree which we fully control but in the end the browser decides how
   *       to render each node. The table can have previous siblings or previous nodes in the DOM
   *       tree but they may not be rendered at all (as it happens with HTML garbage like empty
   *       elements) or not rendered before/after the table (as it happens with absolute positioned
   *       elements). So we have to insert the paragraph each time.
   *   <li>We can't use the same key to insert a paragraph before and after the table because we can
   *       have a table with just one empty cell. So we can't rely on the Enter key.
   *   <li>We can't use just the navigation keys because they would insert a paragraph before/after
   *       the table even when the user can navigate outside of the table.
   * </ul>
   *
   * We can replace the Ctrl with Alt. The idea is to use the Up/Down arrow keys with a modifier.
   * They will work form any table cell.
   *
   * @param event the native event that was fired
   * @param before {@code true} to insert a paragraph before the table, {@code false} to insert a
   *     paragraph after the table
   */
  protected void navigateOutsideTableCell(Event event, boolean before) {
    // Navigate only if the Control or Meta modifiers are pressed along with the Up/Down arrow keys.
    if (event.getAltKey() || event.getShiftKey() || !(event.getCtrlKey() ^ event.getMetaKey())) {
      return;
    }

    Selection selection = getTextArea().getDocument().getSelection();
    if (selection.getRangeCount() == 0) {
      return;
    } else {
      selection.collapseToStart();
    }

    Range range = selection.getRangeAt(0);
    Node ancestor = domUtils.getFirstAncestor(range.getStartContainer(), "table");
    if (ancestor == null) {
      return;
    }

    event.xPreventDefault();

    Document document = getTextArea().getDocument();
    Node paragraph = document.createPElement();
    paragraph.appendChild(document.createTextNode(""));

    if (before) {
      ancestor.getParentNode().insertBefore(paragraph, ancestor);
    } else {
      domUtils.insertAfter(paragraph, ancestor);
    }

    range.selectNodeContents(paragraph.getFirstChild());
    selection.removeAllRanges();
    selection.addRange(range);
  }
 /**
  * Updates the meta data of the given anchor.
  *
  * @param anchor the anchor whose meta data will be updated
  * @param reference the new link reference
  * @param linkType the new link type
  */
 private void updateMetaData(AnchorElement anchor, String reference, LinkType linkType) {
   Document document = (Document) anchor.getOwnerDocument();
   DocumentFragment metaData = document.createDocumentFragment();
   metaData.appendChild(
       document.createComment("startwikilink:" + EscapeUtils.escapeComment(reference)));
   metaData.appendChild(document.createSpanElement());
   if (CLASS_NAME_MAPPING.containsKey(linkType)) {
     Element.as(metaData.getChild(1)).setClassName(CLASS_NAME_MAPPING.get(linkType));
   }
   metaData.getChild(1).appendChild(document.createTextNode(Element.INNER_HTML_PLACEHOLDER));
   metaData.appendChild(document.createComment("stopwikilink"));
   Element.as(anchor).setMetaData(metaData);
 }
Example #3
0
  /**
   * Create a table from TableConfig configuration.
   *
   * <p>We create the table using innerHTML instead of creating each DOM node in order to improve
   * the speed. In most of the browsers setting the innerHTML is faster than creating the DOM nodes
   * and appending them.
   *
   * @param doc currently edited document.
   * @param config table configuration (row number, etc).
   * @return the newly created table.
   */
  public Element createTable(Document doc, TableConfig config) {
    StringBuffer table = new StringBuffer("<table>");

    StringBuffer row = new StringBuffer("<tr>");
    for (int i = 0; i < config.getColNumber(); i++) {
      row.append("<td>");
      // The default cell content depends on the browser. In Firefox the best option is to use a BR.
      // Firefox
      // itself uses BRs in order to allow the user to place the caret inside empty block elements.
      // In Internet
      // Explorer the best option is to set the inner HTML of each cell to the empty string, after
      // creation. For
      // now lets keep the non-breaking space. At some point we should have browser specific
      // implementations for
      // FF and IE. Each will overwrite this method and add specific initialization.
      row.append(TableUtils.CELL_DEFAULTHTML);
      row.append("</td>");
    }
    row.append("</tr>");

    if (config.hasHeader()) {
      table.append("<thead>");
      if (config.getRowNumber() > 0) {
        table.append(row.toString().replace("td", "th"));
      }
      table.append("</thead>");
    }

    table.append("<tbody>");
    for (int i = config.hasHeader() ? 1 : 0; i < config.getRowNumber(); i++) {
      table.append(row.toString());
    }
    table.append("</tbody></table>");

    Element container = doc.createDivElement().cast();
    container.setInnerHTML(table.toString());
    Element tableElement = (Element) container.getFirstChild();
    container.removeChild(tableElement);
    return tableElement;
  }