sourceafFandocViewer::Browser.fan

using gfx::Border
using fwt::BorderPane
using fwt::Desktop
using fwt::Event
using fwt::WebBrowser
using flux::View
using flux::FileResource
using fandoc::FandocParser
using compilerDoc::Doc
using compilerDoc::DocRenderer

** A View with a Web Browser to show html, fandocs and files. All browser navigation is saved in
** the flux history.
** 
** If like me, you're in the habit of hitting F5 to refresh the contents of a browser, you'll want 
** to bind the 'Reload View' command with F5. Here's how.
** 
** Create or open up '%FAN_HOME%/etc/flux/locale/en.props' and remove any reference to F5 to disassociate it 
** from the Find command. e.g. change it to
** 
**   find.name=Find
**   find.accelerator=
** 
** Then edit '%FAN_HOME%/etc/flux/keys.fog' to add the new command binding:
**  
**   "refresh": "F5"
** 
** Note: Only tested on Win7 64bit - Web Browser stability on other platforms is unassured!
class Browser : View {
    private static const Log        log     := Browser#.pod.log
    
    private WebBrowser? browser
    
    override Void onLoad() {
        browser = WebBrowser() {
            it.onHyperlink.add |e| { this.onHyperlink(e) }  
        }

        content = BorderPane {
            it.content = browser
            it.border  = Border("0,0,0,1 $Desktop.sysNormShadow")
        }
        
        // see http://fantom.org/sidewalk/topic/2024#c13355
        Desktop.callLater(100ms) |->| {
            browser.focus
        }
    }

    override Void onActive() {
        if (resource is FandocResource) {
            log.info(" - displaying Fandoc -> $resource.uri")
            fanRes  := resource as FandocResource
            browser.loadStr(fanRes.toHtml)

//          TODO: How can you load an anchor?
//          if (resource.uri.frag != null)
//              browser.load(`#${resource.uri.frag}`)               // -> displaying web http:///#line205
//              browser.load(`about:#${resource.uri.frag}`)         // -> JVM Crash
//              browser.load(`about:blank#${resource.uri.frag}`)    // -> displaying fandoc src-Variant.fan
//              browser.load(`blank#${resource.uri.frag}`)          // -> JVM Crash
        }
        
        if (resource is InternetResource) { 
            log.info(" - displaying Internet -> $resource.uri")
            // see http://fantom.org/sidewalk/topic/2069
            browser.load(resource.uri.plusQuery(["dodgyLink":"true"]))
        }
        
        if (resource is FileResource) {
            log.info(" - displaying File -> $resource.uri")
            file    := (resource as FileResource).file
            fandoc  := file.readAllStr
            html    := fandocToHtml(fandoc, resource.uri)
            browser.loadStr(html)
        }
    }
    
    private Void onHyperlink(Event event) {
        // don't hyperlink in place, instead we route the hyperlink through the flux frame to save 
        // the uri in the history and give consistent navigation
        Uri uri := event.data

        // stop an infinite loop and return early
        // see http://fantom.org/sidewalk/topic/2069
        if (uri.query.containsKey("dodgyLink"))
            return

        // ignore links to anchors on the same page (IE defines these links as "about:blank#anchor")
        if (uri.scheme == "about" && uri.name == "blank")
            return
        
        // anything beyond this point will have its uri resolved and routed through `Frame.load` 
        // so cancel the link event in the browser
        event.data = null
        
        // IE gives relative links the scheme 'about' so strip it off and return an absolute URI, 
        // using the existing resource as the base 
        if (uri.scheme == "about" && !uri.isPathAbs) 
            uri = `${resource.uri.parent}${uri.pathStr}`

        // route the URI through flux so it gets stored in the history
        frame.load(uri)
    }

    ** internal for testing
    internal static Str fandocToHtml(Str fandoc, Uri? base := null) {
        writer  := FandocWriter.fromNew(base)
        doc     := FandocParser().parseStr(fandoc)
        doc.write(writer)
        return writer.toHtml
    }
}