| Author | DaveJarvis <email> |
|---|---|
| Date | 2020-11-11 22:16:42 GMT-0800 |
| Commit | 92db824950bfb820154c593e2386c3a91fc8561d |
| Parent | d8ac1c2 |
| Delta | 99 lines added, 45 lines removed, 54-line increase |
| +package com.keenwrite.preview; | ||
| + | ||
| +import org.jsoup.helper.W3CDom; | ||
| +import org.w3c.dom.DOMImplementation; | ||
| +import org.w3c.dom.Document; | ||
| + | ||
| +import javax.xml.parsers.DocumentBuilder; | ||
| +import javax.xml.parsers.DocumentBuilderFactory; | ||
| + | ||
| +/** | ||
| + * Responsible for converting JSoup document object model (DOM) to a W3C DOM. | ||
| + * Provides a lighter implementation than the superclass by overriding the | ||
| + * {@link #fromJsoup(org.jsoup.nodes.Document)} method to reuse factories, | ||
| + * builders, and implementations. | ||
| + */ | ||
| +class DomConverter extends W3CDom { | ||
| + private final static DocumentBuilderFactory DOCUMENT_FACTORY; | ||
| + private static DocumentBuilder DOCUMENT_BUILDER; | ||
| + private static DOMImplementation DOM_IMPL; | ||
| + | ||
| + static { | ||
| + DOCUMENT_FACTORY = DocumentBuilderFactory.newInstance(); | ||
| + DOCUMENT_FACTORY.setNamespaceAware( true ); | ||
| + | ||
| + try { | ||
| + DOCUMENT_BUILDER = DOCUMENT_FACTORY.newDocumentBuilder(); | ||
| + DOM_IMPL = DOCUMENT_BUILDER.getDOMImplementation(); | ||
| + } catch( final Exception ignored ) { | ||
| + } | ||
| + } | ||
| + | ||
| + @Override | ||
| + public Document fromJsoup( final org.jsoup.nodes.Document in ) { | ||
| + assert in != null; | ||
| + assert DOCUMENT_BUILDER != null; | ||
| + assert DOM_IMPL != null; | ||
| + | ||
| + final var out = DOCUMENT_BUILDER.newDocument(); | ||
| + final org.jsoup.nodes.DocumentType doctype = in.documentType(); | ||
| + | ||
| + if( doctype != null ) { | ||
| + out.appendChild( | ||
| + DOM_IMPL.createDocumentType( | ||
| + doctype.name(), | ||
| + doctype.publicId(), | ||
| + doctype.systemId() | ||
| + ) | ||
| + ); | ||
| + } | ||
| + | ||
| + out.setXmlStandalone( true ); | ||
| + convert( in, out ); | ||
| + return out; | ||
| + } | ||
| +} | ||
| import javafx.beans.property.BooleanProperty; | ||
| import javafx.beans.property.SimpleBooleanProperty; | ||
| -import org.jsoup.helper.W3CDom; | ||
| import org.xhtmlrenderer.layout.SharedContext; | ||
| import org.xhtmlrenderer.render.Box; | ||
| * editor. | ||
| */ | ||
| - private static class HyperlinkListener extends LinkListener { | ||
| + private static final class HyperlinkListener extends LinkListener { | ||
| @Override | ||
| public void linkClicked( final BasicPanel panel, final String link ) { | ||
| try { | ||
| switch( getProtocol( link ) ) { | ||
| - case HTTP: | ||
| + case HTTP -> { | ||
| final var desktop = getDesktop(); | ||
| if( desktop.isSupported( BROWSE ) ) { | ||
| desktop.browse( new URI( link ) ); | ||
| } | ||
| - break; | ||
| - case FILE: | ||
| + } | ||
| + case FILE -> { | ||
| // TODO: #88 -- publish a message to the event bus. | ||
| - break; | ||
| + } | ||
| } | ||
| } catch( final Exception ex ) { | ||
| clue( ex ); | ||
| } | ||
| } | ||
| } | ||
| - private static final W3CDom W3C_DOM = new W3CDom(); | ||
| + private static final DomConverter CONVERTER = new DomConverter(); | ||
| private static final XhtmlNamespaceHandler XNH = new XhtmlNamespaceHandler(); | ||
| + /** | ||
| + * | ||
| + */ | ||
| public HtmlPanel() { | ||
| addDocumentListener( new DocumentEventHandler() ); | ||
| removeMouseTrackingListeners(); | ||
| addMouseTrackingListener( new HyperlinkListener() ); | ||
| } | ||
| + /** | ||
| + * Updates the document model displayed by the renderer. Effectively, this | ||
| + * updates the HTML document to provide new content. | ||
| + * | ||
| + * @param html A complete HTML5 document, including doctype. | ||
| + * @param baseUri URI to use for finding relative files, such as images. | ||
| + */ | ||
| public void render( final String html, final String baseUri ) { | ||
| - setDocument( W3C_DOM.fromJsoup( parse( html ) ), baseUri, XNH ); | ||
| + setDocument( CONVERTER.fromJsoup( parse( html ) ), baseUri, XNH ); | ||
| } | ||
| import javafx.embed.swing.SwingNode; | ||
| -import javafx.scene.Node; | ||
| import org.xhtmlrenderer.render.Box; | ||
| import org.xhtmlrenderer.swing.SwingReplacedElementFactory; | ||
| public void clear() { | ||
| process( "" ); | ||
| + } | ||
| + | ||
| + /** | ||
| + * Sets the base URI to the containing directory the file being edited. | ||
| + * | ||
| + * @param path The path to the file being edited. | ||
| + */ | ||
| + public void setBaseUri( final Path path ) { | ||
| + final var parent = path.getParent(); | ||
| + mBaseUriPath = parent == null ? "" : parent.toUri().toString(); | ||
| + mBaseUriHtml = format( HTML_BASE, mBaseUriPath ); | ||
| + } | ||
| + | ||
| + public void repaintScrollPane() { | ||
| + getScrollPane().repaint(); | ||
| } | ||
| private void scrollTo( final Point point ) { | ||
| mView.scrollTo( point ); | ||
| - } | ||
| - | ||
| - private String decorate( final String html ) { | ||
| - // Trim the HTML back to only the prefix. | ||
| - mHtmlDocument.setLength( HTML_PREFIX_LENGTH ); | ||
| - | ||
| - // Write the HTML body element followed by closing tags. | ||
| - return mHtmlDocument.append( mBaseUriHtml ) | ||
| - .append( HTML_HEAD_CLOSE ) | ||
| - .append( html ) | ||
| - .append( HTML_TAIL ) | ||
| - .toString(); | ||
| - } | ||
| - | ||
| - /** | ||
| - * Sets the base URI to the containing directory the file being edited. | ||
| - * | ||
| - * @param path The path to the file being edited. | ||
| - */ | ||
| - public void setBaseUri( final Path path ) { | ||
| - final var parent = path.getParent(); | ||
| - mBaseUriPath = parent == null ? "" : parent.toUri().toString(); | ||
| - mBaseUriHtml = format( HTML_BASE, mBaseUriPath ); | ||
| - } | ||
| - | ||
| - /** | ||
| - * Content to embed in a panel. | ||
| - * | ||
| - * @return The content to display to the user. | ||
| - */ | ||
| - public Node getNode() { | ||
| - return this; | ||
| - } | ||
| - | ||
| - public void repaintScrollPane() { | ||
| - getScrollPane().repaint(); | ||
| } | ||
| return new Point( x, y ); | ||
| + } | ||
| + | ||
| + private String decorate( final String html ) { | ||
| + // Trim the HTML back to only the prefix. | ||
| + mHtmlDocument.setLength( HTML_PREFIX_LENGTH ); | ||
| + | ||
| + // Write the HTML body element followed by closing tags. | ||
| + return mHtmlDocument.append( mBaseUriHtml ) | ||
| + .append( HTML_HEAD_CLOSE ) | ||
| + .append( html ) | ||
| + .append( HTML_TAIL ) | ||
| + .toString(); | ||
| } | ||