diff --git a/com.xored.f4.astView/META-INF/MANIFEST.MF b/com.xored.f4.astView/META-INF/MANIFEST.MF index 415c87b0..1485e076 100644 --- a/com.xored.f4.astView/META-INF/MANIFEST.MF +++ b/com.xored.f4.astView/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: F4 AstView Bundle-SymbolicName: com.xored.f4.astView;singleton:=true -Bundle-Version: 1.1.4.qualifier +Bundle-Version: 1.1.6.qualifier Bundle-Vendor: Xored software, Inc. Bundle-RequiredExecutionEnvironment: J2SE-1.5 Require-Bundle: org.eclipse.core.runtime, diff --git a/com.xored.f4.astView/pom.xml b/com.xored.f4.astView/pom.xml index d7a6ab22..20f1ac9b 100644 --- a/com.xored.f4.astView/pom.xml +++ b/com.xored.f4.astView/pom.xml @@ -9,6 +9,6 @@ com.xored.f4 com.xored.f4.astView - 1.1.4-SNAPSHOT + 1.1.6-SNAPSHOT eclipse-plugin diff --git a/com.xored.f4.builder.ui/META-INF/MANIFEST.MF b/com.xored.f4.builder.ui/META-INF/MANIFEST.MF index cb5f7280..c62d5e42 100644 --- a/com.xored.f4.builder.ui/META-INF/MANIFEST.MF +++ b/com.xored.f4.builder.ui/META-INF/MANIFEST.MF @@ -2,12 +2,12 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: F4 Builder UI Bundle-SymbolicName: com.xored.f4.builder.ui;singleton:=true -Bundle-Version: 1.1.4.qualifier +Bundle-Version: 1.1.6.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Bundle-Vendor: Xored software, Inc. Require-Bundle: org.fantom.sys, org.fantom.concurrent, - com.xored.fanide.core;bundle-version="1.1.4", + com.xored.fanide.core;bundle-version="1.1.6", org.eclipse.core.runtime, org.eclipse.core.resources, org.eclipse.ui, diff --git a/com.xored.f4.builder.ui/pom.xml b/com.xored.f4.builder.ui/pom.xml index 2bacfeae..f04e015e 100644 --- a/com.xored.f4.builder.ui/pom.xml +++ b/com.xored.f4.builder.ui/pom.xml @@ -9,6 +9,6 @@ com.xored.f4 com.xored.f4.builder.ui - 1.1.4-SNAPSHOT + 1.1.6-SNAPSHOT eclipse-plugin diff --git a/com.xored.f4.builder/META-INF/MANIFEST.MF b/com.xored.f4.builder/META-INF/MANIFEST.MF index 445ecead..db5c357a 100644 --- a/com.xored.f4.builder/META-INF/MANIFEST.MF +++ b/com.xored.f4.builder/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: F4 Builder Bundle-SymbolicName: com.xored.f4.builder;singleton:=true -Bundle-Version: 1.1.4.qualifier +Bundle-Version: 1.1.6.qualifier Bundle-Localization: plugin Require-Bundle: org.eclipse.dltk.core, org.eclipse.core.runtime, @@ -17,8 +17,8 @@ Require-Bundle: org.eclipse.dltk.core, org.fantom.compiler, org.fantom.compilerJava, com.xored.fantom.classfile;bundle-version="1.0.0", - com.xored.f4.core;bundle-version="1.1.4", - com.xored.f4.launching;bundle-version="1.1.4", + com.xored.f4.core;bundle-version="1.1.6", + com.xored.f4.launching;bundle-version="1.1.6", org.fantom.concurrent, org.eclipse.dltk.logconsole, org.eclipse.core.externaltools;bundle-version="1.0.1" diff --git a/com.xored.f4.builder/f4builder.pod b/com.xored.f4.builder/f4builder.pod index c2360cf6..9fb6223d 100644 Binary files a/com.xored.f4.builder/f4builder.pod and b/com.xored.f4.builder/f4builder.pod differ diff --git a/com.xored.f4.builder/fan/CompileFan.fan b/com.xored.f4.builder/fan/CompileFan.fan index 3c3f6059..9d3dced2 100644 --- a/com.xored.f4.builder/fan/CompileFan.fan +++ b/com.xored.f4.builder/fan/CompileFan.fan @@ -75,10 +75,6 @@ class CompileFan : IScriptBuilder { return Status(IStatus.OK, pluginId, "OK") } - private FantomProject[] projectsFromElements(ISourceModule[] modules) { - modules.map { fantomProject(it.getScriptProject) }.unique - } - private Bool buildPod(FantomProject fp) { building = true clearMarkers(fp.project) diff --git a/com.xored.f4.builder/pom.xml b/com.xored.f4.builder/pom.xml index df39e3f2..5e937987 100644 --- a/com.xored.f4.builder/pom.xml +++ b/com.xored.f4.builder/pom.xml @@ -9,6 +9,6 @@ com.xored.f4 com.xored.f4.builder - 1.1.4-SNAPSHOT + 1.1.6-SNAPSHOT eclipse-plugin diff --git a/com.xored.f4.core/META-INF/MANIFEST.MF b/com.xored.f4.core/META-INF/MANIFEST.MF index 71e06425..ae38a487 100644 --- a/com.xored.f4.core/META-INF/MANIFEST.MF +++ b/com.xored.f4.core/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: F4 Core Bundle-SymbolicName: com.xored.f4.core;singleton:=true -Bundle-Version: 1.1.4.qualifier +Bundle-Version: 1.1.6.qualifier Require-Bundle: org.fantom.sys, org.fantom.concurrent, org.eclipse.core.resources, @@ -14,8 +14,8 @@ Require-Bundle: org.fantom.sys, org.eclipse.dltk.launching, org.eclipse.dltk.logconsole, com.xored.fanide.core, - com.xored.f4.parser;bundle-version="1.1.4", - com.xored.f4.model;bundle-version="1.1.4", + com.xored.f4.parser;bundle-version="1.1.6", + com.xored.f4.model;bundle-version="1.1.6", org.eclipse.text, org.eclipse.pde.core Bundle-RequiredExecutionEnvironment: JavaSE-1.7 diff --git a/com.xored.f4.core/f4core.pod b/com.xored.f4.core/f4core.pod index 3cc6a6df..7e8e2701 100644 Binary files a/com.xored.f4.core/f4core.pod and b/com.xored.f4.core/f4core.pod differ diff --git a/com.xored.f4.core/fan/manifest/FantomProjectListener.fan b/com.xored.f4.core/fan/manifest/FantomProjectListener.fan index 580939c7..0750bbde 100644 --- a/com.xored.f4.core/fan/manifest/FantomProjectListener.fan +++ b/com.xored.f4.core/fan/manifest/FantomProjectListener.fan @@ -38,6 +38,7 @@ internal class DeltaVisitor2 { IProject[] openedProjects := IProject[,] IProject[] updatedProjects := IProject[,] + ** Return 'true' to also visit children. Bool visit(IResourceDelta? delta) { resource := delta.getResource diff --git a/com.xored.f4.core/fan/manifest/FantomProjectManager.fan b/com.xored.f4.core/fan/manifest/FantomProjectManager.fan index 7ac86d3f..ecef34f5 100644 --- a/com.xored.f4.core/fan/manifest/FantomProjectManager.fan +++ b/com.xored.f4.core/fan/manifest/FantomProjectManager.fan @@ -52,30 +52,34 @@ internal class FantomProjectManagerState { // reset containers for all projects that depend on // closed or opened projects and for all projects with updated content - projectsToUpdate := [Str:IProject][:] { it.ordered = true } + projectsToUpdate := [Str:IProject][:] { it.ordered = true } + projectsToRebuild := [Str:IProject][:] { it.ordered = true } - // add updated and opened Fantom projects + // update updated and opened Fantom projects change.openedProjects.each { + // not sure why we need to check for Fantom projects? if (isFantomProject(it)) projectsToUpdate[it.getName] = it } change.updatedProjects.each { + // not sure why we need to check for Fantom projects? if (isFantomProject(it)) projectsToUpdate[it.getName] = it } - // add parent projects + // update the dependencies of parent projects change.openedProjects.each { - parentProjects(it).each { projectsToUpdate[it.getName] = it } + parentProjects(it).each { projectsToRebuild[it.getName] = it } } change.closedProjects.each { - parentProjects(it).each { projectsToUpdate[it.getName] = it } + parentProjects(it).each { projectsToRebuild[it.getName] = it } } - + // remove all closed projects change.closedProjects.each { - projectsToUpdate.remove(it.getName) - projects .remove(it.getName) + projectsToUpdate .remove(it.getName) + projectsToRebuild.remove(it.getName) + projects .remove(it.getName) } // remove all updated projects to ensure they get re-created @@ -86,7 +90,7 @@ internal class FantomProjectManagerState { // do the update projectsToUpdate.vals.each { - created := updateProject(it) + created := createProject(it) if (created) // resetting seems quite processor intensive, so only do it if we really need to @@ -97,6 +101,18 @@ internal class FantomProjectManagerState { projects[it.getName].reset } + projectsToRebuild.vals.each { + fp := projects[it.getName] + + // can't think why fp would be null but, you know, belts and braces when in the reactor! + if (fp != null) { + // reset and re-resolve to remove these annoying errors: + // - Project A is missing required script project: 'Project B' + fp.reset + fp.resolvedPods + } + } + return null } @@ -120,7 +136,7 @@ internal class FantomProjectManagerState { FantomProject? getOrAdd(Unsafe projectRef) { ip := (IProject) projectRef.val - updateProject(ip) + createProject(ip) return projects[ip.getName] } @@ -154,7 +170,7 @@ internal class FantomProjectManagerState { } ** Returns 'true' if the project was updated - private Bool updateProject(IProject ip) { + private Bool createProject(IProject ip) { // if the existing project looks okay, let's keep it! fp := projects[ip.getName] if (fp != null) diff --git a/com.xored.f4.core/fan/manifest/Manifest.fan b/com.xored.f4.core/fan/manifest/Manifest.fan index c99e4e82..ab703bb5 100644 --- a/com.xored.f4.core/fan/manifest/Manifest.fan +++ b/com.xored.f4.core/fan/manifest/Manifest.fan @@ -37,7 +37,7 @@ class Manifest { public static const Str filename := "build.fan" ** Lines of field initializers - Str:Int lines { private set } + Str:Int lines { private set } Str:Obj? vals { private set } Str? podName() { vals["podName"] } Version version() { vals["version"] ?: Version("1.0") } @@ -81,6 +81,7 @@ class Manifest { if (expr is CallExpr) { call := expr as CallExpr callee := call.callee + if (isVersionConstructor(callee)) { // first look for a Str ctor versionStr := (call.args.first as Literal)?.val as Str @@ -95,11 +96,29 @@ class Manifest { } } } + return null } private static Bool isVersionConstructor(Expr callee) { - (callee as UnresolvedRef)?.text == "Version" || (callee as StaticTargetExpr)?.ctype?.resolvedType?.qname == "sys::Version" + + // these are for standard syntax: Version("1.2") + if ((callee as UnresolvedRef)?.text == "Version") + return true + if ((callee as StaticTargetExpr)?.ctype?.resolvedType?.qname == "sys::Version") + return true + + // this is for explicit make syntax: Version.make("1.2") + if (callee is InvokeExpr) { + icallee := ((InvokeExpr) callee).callee + + if ((icallee as UnresolvedRef)?.text == "Version") + return true + if ((icallee as StaticTargetExpr)?.ctype?.resolvedType?.qname == "sys::Version") + return true + } + + return false } private static Range:Int buildOffsets(Str file) { diff --git a/com.xored.f4.core/fan/util/ParseUtil.fan b/com.xored.f4.core/fan/util/ParseUtil.fan index a9d26e12..06d7fb0c 100644 --- a/com.xored.f4.core/fan/util/ParseUtil.fan +++ b/com.xored.f4.core/fan/util/ParseUtil.fan @@ -21,8 +21,8 @@ class ParseUtil : TypeUtil { t != null && t.inheritance.any |Str baseName -> Bool| { - baseType := ns.findType(baseName) - return baseType.qname == qNameBase || inherits(baseType, qNameBase, ns) + baseType := ns.findType(baseName) + return baseType?.qname == qNameBase || inherits(baseType, qNameBase, ns) } } diff --git a/com.xored.f4.core/pom.xml b/com.xored.f4.core/pom.xml index a46549d4..99b84c12 100644 --- a/com.xored.f4.core/pom.xml +++ b/com.xored.f4.core/pom.xml @@ -9,6 +9,6 @@ com.xored.f4 com.xored.f4.core - 1.1.4-SNAPSHOT + 1.1.6-SNAPSHOT eclipse-plugin diff --git a/com.xored.f4.debug.ui/META-INF/MANIFEST.MF b/com.xored.f4.debug.ui/META-INF/MANIFEST.MF index a4b789fb..4ba009ff 100644 --- a/com.xored.f4.debug.ui/META-INF/MANIFEST.MF +++ b/com.xored.f4.debug.ui/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: F4 Debug UI Bundle-SymbolicName: com.xored.f4.debug.ui;singleton:=true -Bundle-Version: 1.1.4.qualifier +Bundle-Version: 1.1.6.qualifier Bundle-Vendor: Xored software, Inc. Bundle-Localization: plugin Bundle-RequiredExecutionEnvironment: J2SE-1.5 @@ -24,16 +24,16 @@ Require-Bundle: org.eclipse.debug.core, org.eclipse.jdt.debug, com.ibm.icu, org.eclipse.ui.ide, - com.xored.f4.core;bundle-version="1.1.4", - com.xored.f4.debug;bundle-version="1.1.4", - com.xored.f4.launching;bundle-version="1.1.4", - com.xored.f4.parser;bundle-version="1.1.4", + com.xored.f4.core;bundle-version="1.1.6", + com.xored.f4.debug;bundle-version="1.1.6", + com.xored.f4.launching;bundle-version="1.1.6", + com.xored.f4.parser;bundle-version="1.1.6", org.eclipse.dltk.debug, org.eclipse.jdt.debug.ui, org.eclipse.core.filesystem, org.eclipse.core.variables, - com.xored.fanide.core;bundle-version="1.1.4", - com.xored.f4.model;bundle-version="1.1.4", + com.xored.fanide.core;bundle-version="1.1.6", + com.xored.f4.model;bundle-version="1.1.6", org.eclipse.jdt.core, org.eclipse.jdt.ui, org.eclipse.core.filebuffers diff --git a/com.xored.f4.debug.ui/f4debugUi.pod b/com.xored.f4.debug.ui/f4debugUi.pod index 12060c7a..3b89b00b 100644 Binary files a/com.xored.f4.debug.ui/f4debugUi.pod and b/com.xored.f4.debug.ui/f4debugUi.pod differ diff --git a/com.xored.f4.debug.ui/fan/FanTabGroup.fan b/com.xored.f4.debug.ui/fan/FanTabGroup.fan index 12af9d49..3da0f652 100644 --- a/com.xored.f4.debug.ui/fan/FanTabGroup.fan +++ b/com.xored.f4.debug.ui/fan/FanTabGroup.fan @@ -1,11 +1,3 @@ -// -// Copyright (c) 2010 xored software, Inc. -// Licensed under Eclipse Public License version 1.0 -// -// History: -// Ivan Inozemtsev May 31, 2010 - Initial Contribution -// - using [java] org.eclipse.debug.ui using [java] org.eclipse.debug.core using [java] org.eclipse.debug.core::DebugPlugin @@ -20,108 +12,94 @@ using f4core::FantomProjectManager using f4launching using f4model -class FanTabGroup : AbstractLaunchConfigurationTabGroup -{ - override Void createTabs(ILaunchConfigurationDialog? dialog, Str? mode) - { - main := FanMainConfigTab(mode) - tabs := [main, ScriptArgumentsTab(), ProjectTab(), InterpreterTab(main), - EnvironmentTab(), CommonTab() ] - setTabs(tabs) - } +class FanTabGroup : AbstractLaunchConfigurationTabGroup { + + override Void createTabs(ILaunchConfigurationDialog? dialog, Str? mode) { + main := FanMainConfigTab(mode) + tabs := [main, ScriptArgumentsTab(), ProjectTab(), InterpreterTab(main), EnvironmentTab(), CommonTab()] + setTabs(tabs) + } } -class FanMainConfigTab : MainLaunchConfigurationTab -{ - new make(Str mode) : super(mode) {} - - override Str? getNatureID() { F4Nature.id } - - override protected Void doCreateControl(Composite? parent) - { - group := Group(parent, SWT.NONE) - group.setText("Launch class:") - group.setLayoutData(GridData(GridData.FILL_HORIZONTAL)) - layout := GridLayout() - layout.numColumns = 2 - group.setLayout(layout) - - classText = Text(group, SWT.SINGLE.or(SWT.BORDER)) - classText.setLayoutData(GridData(GridData.FILL_HORIZONTAL)) - classText.addModifyListener(getWidgetListener) - - searchButton = createPushButton(group, "Browse...", null) - searchButton.addSelectionListener(BrowseButtonListener(this)) - } - - private Button? searchButton - private Text? classText - - Void browseClicked() - { - - } - - override protected Void updateMainModuleFromConfig(ILaunchConfiguration? config) - { - classText.setText(mainClassName(config)) - } - - override protected Void doPerformApply(ILaunchConfigurationWorkingCopy? config) - { - super.doPerformApply(config) - config.setAttribute(LaunchConsts.fanClass, mainClassText) - config.removeAttribute(DebugPlugin.ATTR_PROCESS_FACTORY_ID) - } - - ** we don't need script name - override protected Str? getScriptName() { "" } - - ** Validate class name instead - override protected Bool validateScript() { validateClass } - - protected Bool validateClass() - { - className := mainClassText - if(className.isEmpty) className = "Main" - - ns := FantomProjectManager.instance.get(getProject.getProject).ns - type := ns.currPod.findType(className,false) - if(type == null) - { - setErrorMessage("Class $className is not found") - return false - } - mainSlot := type.findSlot("main", ns, false) - if(mainSlot == null) - { - setErrorMessage("Class $className does not have 'main' method") - return false - } - - return true - } - - private Str mainClassText() { classText.getText.trim } - - private Str mainClassName(ILaunchConfiguration? config) - { - LaunchConfigurationUtils.getString(config, LaunchConsts.fanClass, "") - } - - +class FanMainConfigTab : MainLaunchConfigurationTab { + new make(Str mode) : super(mode) {} + + override Str? getNatureID() { F4Nature.id } + + override protected Void doCreateControl(Composite? parent) { + group := Group(parent, SWT.NONE) + group.setText("Launch class:") + group.setLayoutData(GridData(GridData.FILL_HORIZONTAL)) + layout := GridLayout() + layout.numColumns = 2 + group.setLayout(layout) + + classText = Text(group, SWT.SINGLE.or(SWT.BORDER)) + classText.setLayoutData(GridData(GridData.FILL_HORIZONTAL)) + classText.addModifyListener(getWidgetListener) + + // as the Browse Button doesn't do anything, and I've NO idea how to bring up a filtering Type Dialog + // let's just disable it for now... + // see https://github.com/xored/f4/issues/22 +// searchButton = createPushButton(group, "Browse...", null) +// searchButton.addSelectionListener(BrowseButtonListener(this)) + } + + private Button? searchButton + private Text? classText + + Void browseClicked() { + // TODO empty listener - see https://github.com/xored/f4/issues/22 + } + + override protected Void updateMainModuleFromConfig(ILaunchConfiguration? config) { + classText.setText(mainClassName(config)) + } + + override protected Void doPerformApply(ILaunchConfigurationWorkingCopy? config) { + super.doPerformApply(config) + config.setAttribute(LaunchConsts.fanClass, mainClassText) + config.removeAttribute(DebugPlugin.ATTR_PROCESS_FACTORY_ID) + } + + ** we don't need script name + override protected Str? getScriptName() { "" } + + ** Validate class name instead + override protected Bool validateScript() { validateClass } + + protected Bool validateClass() { + className := mainClassText + if(className.isEmpty) className = "Main" + + ns := FantomProjectManager.instance.get(getProject.getProject).ns + type := ns.currPod.findType(className,false) + if(type == null) { + setErrorMessage("Class $className is not found") + return false + } + mainSlot := type.findSlot("main", ns, false) + if(mainSlot == null) { + setErrorMessage("Class $className does not have 'main' method") + return false + } + + return true + } + + private Str mainClassText() { classText.getText.trim } + + private Str mainClassName(ILaunchConfiguration? config) { + LaunchConfigurationUtils.getString(config, LaunchConsts.fanClass, "") + } } -************************************************************************** -** BrowseButtonListener -************************************************************************** -class BrowseButtonListener : SelectionAdapter -{ - private FanMainConfigTab tab - new make(FanMainConfigTab tab) { this.tab = tab } - - override Void widgetSelected(SelectionEvent? e) - { - tab.browseClicked - } +class BrowseButtonListener : SelectionAdapter { + private FanMainConfigTab tab + + new make(FanMainConfigTab tab) { this.tab = tab } + + override Void widgetSelected(SelectionEvent? e) { + tab.browseClicked + } } \ No newline at end of file diff --git a/com.xored.f4.debug.ui/pom.xml b/com.xored.f4.debug.ui/pom.xml index fc64cd13..961037be 100644 --- a/com.xored.f4.debug.ui/pom.xml +++ b/com.xored.f4.debug.ui/pom.xml @@ -9,6 +9,6 @@ com.xored.f4 com.xored.f4.debug.ui - 1.1.4-SNAPSHOT + 1.1.6-SNAPSHOT eclipse-plugin diff --git a/com.xored.f4.debug/META-INF/MANIFEST.MF b/com.xored.f4.debug/META-INF/MANIFEST.MF index 731faf8d..602b23b5 100644 --- a/com.xored.f4.debug/META-INF/MANIFEST.MF +++ b/com.xored.f4.debug/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: F4 Debug Bundle-SymbolicName: com.xored.f4.debug;singleton:=true -Bundle-Version: 1.1.4.qualifier +Bundle-Version: 1.1.6.qualifier Bundle-Vendor: Xored software, Inc. Bundle-RequiredExecutionEnvironment: J2SE-1.5 Require-Bundle: org.eclipse.debug.core, @@ -10,4 +10,4 @@ Require-Bundle: org.eclipse.debug.core, com.xored.fanide.core, org.fantom.sys, org.eclipse.dltk.debug, - com.xored.f4.core;bundle-version="1.1.4" + com.xored.f4.core;bundle-version="1.1.6" diff --git a/com.xored.f4.debug/pom.xml b/com.xored.f4.debug/pom.xml index bf9fdfcb..de5189ae 100644 --- a/com.xored.f4.debug/pom.xml +++ b/com.xored.f4.debug/pom.xml @@ -9,6 +9,6 @@ com.xored.f4 com.xored.f4.debug - 1.1.4-SNAPSHOT + 1.1.6-SNAPSHOT eclipse-plugin diff --git a/com.xored.f4.fantom-feature/feature.xml b/com.xored.f4.fantom-feature/feature.xml index 38789cd5..7036bb1c 100644 --- a/com.xored.f4.fantom-feature/feature.xml +++ b/com.xored.f4.fantom-feature/feature.xml @@ -2,7 +2,7 @@ @@ -19,10 +19,6 @@ This is the default API Fantom projects are compiled against. With it, F4 may be Academic Free License v3.0 - - - - diff --git a/com.xored.f4.fantom-feature/pom.xml b/com.xored.f4.fantom-feature/pom.xml index 06794863..60c51671 100644 --- a/com.xored.f4.fantom-feature/pom.xml +++ b/com.xored.f4.fantom-feature/pom.xml @@ -9,6 +9,6 @@ com.xored.f4.features com.xored.f4.fantom - 1.0.74-SNAPSHOT + 1.0.76-SNAPSHOT eclipse-feature diff --git a/com.xored.f4.fantom/META-INF/MANIFEST.MF b/com.xored.f4.fantom/META-INF/MANIFEST.MF index c1283cf3..03dd5d97 100644 --- a/com.xored.f4.fantom/META-INF/MANIFEST.MF +++ b/com.xored.f4.fantom/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: F4 Fantom Bundle-SymbolicName: com.xored.f4.fantom;singleton:=true -Bundle-Version: 1.0.74.qualifier +Bundle-Version: 1.0.76.qualifier Bundle-Vendor: Xored software, Inc. Eclipse-BundleShape: dir Require-Bundle: org.eclipse.dltk.launching diff --git a/com.xored.f4.fantom/fantom/etc/build/config.props b/com.xored.f4.fantom/fantom/etc/build/config.props index c68daaa7..45ded18d 100755 --- a/com.xored.f4.fantom/fantom/etc/build/config.props +++ b/com.xored.f4.fantom/fantom/etc/build/config.props @@ -11,7 +11,7 @@ // // default version used by build scripts -buildVersion=1.0.74 +buildVersion=1.0.76 // Must be configured boot build in substitute/release installation //devHome=file:/E:/fan/ diff --git a/com.xored.f4.fantom/fantom/etc/sys/config.props b/com.xored.f4.fantom/fantom/etc/sys/config.props index 27a2a26f..6b1bcedd 100755 --- a/com.xored.f4.fantom/fantom/etc/sys/config.props +++ b/com.xored.f4.fantom/fantom/etc/sys/config.props @@ -53,4 +53,4 @@ debug=true // Set this property to pass additional command line options to the Java VM. // The options should be listed together separated with a space. -java.options=-Xmx512M +// java.options=-Xmx512M \ No newline at end of file diff --git a/com.xored.f4.fantom/fantom/etc/sys/ext2mime.props b/com.xored.f4.fantom/fantom/etc/sys/ext2mime.props index b0ab5dea..d0ff3828 100755 --- a/com.xored.f4.fantom/fantom/etc/sys/ext2mime.props +++ b/com.xored.f4.fantom/fantom/etc/sys/ext2mime.props @@ -33,6 +33,7 @@ jpg=image/jpeg log=text/plain; charset=utf-8 js=text/javascript; charset=utf-8 json=application/json +jsonld=application/ld+json map=application/json mp3=audio/mpeg3 mpeg=video/mpeg @@ -55,8 +56,9 @@ tar=application/x-tar txt=text/plain; charset=utf-8 text=text/plain; charset=utf-8 tiff=image/tiff -trio=text/plain; charset=utf-8 +trio=text/trio; charset=utf-8 ttf=font/ttf +ttl=text/turtle tsv=text/tab-separated-values wav=audio/wav woff=font/woff @@ -66,4 +68,4 @@ xls=application/vnd.ms-excel xlsx=application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xml=text/xml; charset=utf-8 zip=application/zip -zinc=text/plain; charset=utf-8 +zinc=text/zinc; charset=utf-8 \ No newline at end of file diff --git a/com.xored.f4.fantom/fantom/etc/sys/units.txt b/com.xored.f4.fantom/fantom/etc/sys/units.txt index 1d853cee..d5fe1464 100755 --- a/com.xored.f4.fantom/fantom/etc/sys/units.txt +++ b/com.xored.f4.fantom/fantom/etc/sys/units.txt @@ -10,7 +10,7 @@ // -- dimensionless (null) -percent, %; ; 100 +percent, %; ; 0.01 pixel, px decibel, db power_factor, pf @@ -228,6 +228,8 @@ kilowatt_per_ton, kW/ton; ; 1 -- density (kg1*m-3) kilograms_per_cubic_meter, kg/m³; kg1*m-3 +grams_per_cubic_meter, g/m³; kg1*m-3; 1.0E-3 +milligrams_per_cubic_meter, mg/m³; kg1*m-3; 1.0E-6 micrograms_per_cubic_meter, µg/m³; kg1*m-3; 1.0E-9 -- electric charge (sec1*A1) diff --git a/com.xored.f4.fantom/fantom/lib/fan/build.pod b/com.xored.f4.fantom/fantom/lib/fan/build.pod index 5156a47a..7c393b7d 100755 Binary files a/com.xored.f4.fantom/fantom/lib/fan/build.pod and b/com.xored.f4.fantom/fantom/lib/fan/build.pod differ diff --git a/com.xored.f4.fantom/fantom/lib/fan/compiler.pod b/com.xored.f4.fantom/fantom/lib/fan/compiler.pod index 223fbc39..67f6ec8f 100755 Binary files a/com.xored.f4.fantom/fantom/lib/fan/compiler.pod and b/com.xored.f4.fantom/fantom/lib/fan/compiler.pod differ diff --git a/com.xored.f4.fantom/fantom/lib/fan/compilerDoc.pod b/com.xored.f4.fantom/fantom/lib/fan/compilerDoc.pod index 56de0933..f883b6dc 100755 Binary files a/com.xored.f4.fantom/fantom/lib/fan/compilerDoc.pod and b/com.xored.f4.fantom/fantom/lib/fan/compilerDoc.pod differ diff --git a/com.xored.f4.fantom/fantom/lib/fan/compilerJava.pod b/com.xored.f4.fantom/fantom/lib/fan/compilerJava.pod index 421344a6..5ba162f8 100644 Binary files a/com.xored.f4.fantom/fantom/lib/fan/compilerJava.pod and b/com.xored.f4.fantom/fantom/lib/fan/compilerJava.pod differ diff --git a/com.xored.f4.fantom/fantom/lib/fan/compilerJs.pod b/com.xored.f4.fantom/fantom/lib/fan/compilerJs.pod index 63cfe747..8485a5f3 100755 Binary files a/com.xored.f4.fantom/fantom/lib/fan/compilerJs.pod and b/com.xored.f4.fantom/fantom/lib/fan/compilerJs.pod differ diff --git a/com.xored.f4.fantom/fantom/lib/fan/concurrent.pod b/com.xored.f4.fantom/fantom/lib/fan/concurrent.pod index cfbf8d43..a2ce529b 100755 Binary files a/com.xored.f4.fantom/fantom/lib/fan/concurrent.pod and b/com.xored.f4.fantom/fantom/lib/fan/concurrent.pod differ diff --git a/com.xored.f4.fantom/fantom/lib/fan/docDomkit.pod b/com.xored.f4.fantom/fantom/lib/fan/docDomkit.pod index 10cb5b5a..53a1e606 100644 Binary files a/com.xored.f4.fantom/fantom/lib/fan/docDomkit.pod and b/com.xored.f4.fantom/fantom/lib/fan/docDomkit.pod differ diff --git a/com.xored.f4.fantom/fantom/lib/fan/docFanr.pod b/com.xored.f4.fantom/fantom/lib/fan/docFanr.pod index 1604b796..38a93ba6 100755 Binary files a/com.xored.f4.fantom/fantom/lib/fan/docFanr.pod and b/com.xored.f4.fantom/fantom/lib/fan/docFanr.pod differ diff --git a/com.xored.f4.fantom/fantom/lib/fan/docIntro.pod b/com.xored.f4.fantom/fantom/lib/fan/docIntro.pod index a4712dd9..ba2932f9 100755 Binary files a/com.xored.f4.fantom/fantom/lib/fan/docIntro.pod and b/com.xored.f4.fantom/fantom/lib/fan/docIntro.pod differ diff --git a/com.xored.f4.fantom/fantom/lib/fan/docLang.pod b/com.xored.f4.fantom/fantom/lib/fan/docLang.pod index 4ddeb96a..52767f69 100755 Binary files a/com.xored.f4.fantom/fantom/lib/fan/docLang.pod and b/com.xored.f4.fantom/fantom/lib/fan/docLang.pod differ diff --git a/com.xored.f4.fantom/fantom/lib/fan/docTools.pod b/com.xored.f4.fantom/fantom/lib/fan/docTools.pod index 24502777..e3b1eb3a 100755 Binary files a/com.xored.f4.fantom/fantom/lib/fan/docTools.pod and b/com.xored.f4.fantom/fantom/lib/fan/docTools.pod differ diff --git a/com.xored.f4.fantom/fantom/lib/fan/dom.pod b/com.xored.f4.fantom/fantom/lib/fan/dom.pod index 1c24cece..eaa0736d 100755 Binary files a/com.xored.f4.fantom/fantom/lib/fan/dom.pod and b/com.xored.f4.fantom/fantom/lib/fan/dom.pod differ diff --git a/com.xored.f4.fantom/fantom/lib/fan/domkit.pod b/com.xored.f4.fantom/fantom/lib/fan/domkit.pod index 2951382a..ff16615d 100644 Binary files a/com.xored.f4.fantom/fantom/lib/fan/domkit.pod and b/com.xored.f4.fantom/fantom/lib/fan/domkit.pod differ diff --git a/com.xored.f4.fantom/fantom/lib/fan/email.pod b/com.xored.f4.fantom/fantom/lib/fan/email.pod index 58d54cf5..3e6b2c2e 100755 Binary files a/com.xored.f4.fantom/fantom/lib/fan/email.pod and b/com.xored.f4.fantom/fantom/lib/fan/email.pod differ diff --git a/com.xored.f4.fantom/fantom/lib/fan/fandoc.pod b/com.xored.f4.fantom/fantom/lib/fan/fandoc.pod index 07e406c3..3933f773 100755 Binary files a/com.xored.f4.fantom/fantom/lib/fan/fandoc.pod and b/com.xored.f4.fantom/fantom/lib/fan/fandoc.pod differ diff --git a/com.xored.f4.fantom/fantom/lib/fan/fanr.pod b/com.xored.f4.fantom/fantom/lib/fan/fanr.pod index 8c7a9402..70ad2e56 100755 Binary files a/com.xored.f4.fantom/fantom/lib/fan/fanr.pod and b/com.xored.f4.fantom/fantom/lib/fan/fanr.pod differ diff --git a/com.xored.f4.fantom/fantom/lib/fan/fansh.pod b/com.xored.f4.fantom/fantom/lib/fan/fansh.pod index 11011636..31272cb4 100755 Binary files a/com.xored.f4.fantom/fantom/lib/fan/fansh.pod and b/com.xored.f4.fantom/fantom/lib/fan/fansh.pod differ diff --git a/com.xored.f4.fantom/fantom/lib/fan/flux.pod b/com.xored.f4.fantom/fantom/lib/fan/flux.pod index 8202b348..ab617e8f 100755 Binary files a/com.xored.f4.fantom/fantom/lib/fan/flux.pod and b/com.xored.f4.fantom/fantom/lib/fan/flux.pod differ diff --git a/com.xored.f4.fantom/fantom/lib/fan/fluxText.pod b/com.xored.f4.fantom/fantom/lib/fan/fluxText.pod index d4b1f861..c0b32a4b 100755 Binary files a/com.xored.f4.fantom/fantom/lib/fan/fluxText.pod and b/com.xored.f4.fantom/fantom/lib/fan/fluxText.pod differ diff --git a/com.xored.f4.fantom/fantom/lib/fan/fwt.pod b/com.xored.f4.fantom/fantom/lib/fan/fwt.pod index 4e894bd6..fdbe79c2 100755 Binary files a/com.xored.f4.fantom/fantom/lib/fan/fwt.pod and b/com.xored.f4.fantom/fantom/lib/fan/fwt.pod differ diff --git a/com.xored.f4.fantom/fantom/lib/fan/gfx.pod b/com.xored.f4.fantom/fantom/lib/fan/gfx.pod index 4e617203..02af8a6d 100755 Binary files a/com.xored.f4.fantom/fantom/lib/fan/gfx.pod and b/com.xored.f4.fantom/fantom/lib/fan/gfx.pod differ diff --git a/com.xored.f4.fantom/fantom/lib/fan/graphics.pod b/com.xored.f4.fantom/fantom/lib/fan/graphics.pod index 6048785d..dcf99da9 100644 Binary files a/com.xored.f4.fantom/fantom/lib/fan/graphics.pod and b/com.xored.f4.fantom/fantom/lib/fan/graphics.pod differ diff --git a/com.xored.f4.fantom/fantom/lib/fan/icons.pod b/com.xored.f4.fantom/fantom/lib/fan/icons.pod index 2555fa52..ba23d0a0 100755 Binary files a/com.xored.f4.fantom/fantom/lib/fan/icons.pod and b/com.xored.f4.fantom/fantom/lib/fan/icons.pod differ diff --git a/com.xored.f4.fantom/fantom/lib/fan/inet.pod b/com.xored.f4.fantom/fantom/lib/fan/inet.pod index c0b9b539..39978786 100755 Binary files a/com.xored.f4.fantom/fantom/lib/fan/inet.pod and b/com.xored.f4.fantom/fantom/lib/fan/inet.pod differ diff --git a/com.xored.f4.fantom/fantom/lib/fan/math.pod b/com.xored.f4.fantom/fantom/lib/fan/math.pod new file mode 100644 index 00000000..12398f09 Binary files /dev/null and b/com.xored.f4.fantom/fantom/lib/fan/math.pod differ diff --git a/com.xored.f4.fantom/fantom/lib/fan/obix.pod b/com.xored.f4.fantom/fantom/lib/fan/obix.pod index 0e00ab74..2d84d667 100755 Binary files a/com.xored.f4.fantom/fantom/lib/fan/obix.pod and b/com.xored.f4.fantom/fantom/lib/fan/obix.pod differ diff --git a/com.xored.f4.fantom/fantom/lib/fan/sql.pod b/com.xored.f4.fantom/fantom/lib/fan/sql.pod index b4589227..4efd8f08 100755 Binary files a/com.xored.f4.fantom/fantom/lib/fan/sql.pod and b/com.xored.f4.fantom/fantom/lib/fan/sql.pod differ diff --git a/com.xored.f4.fantom/fantom/lib/fan/syntax.pod b/com.xored.f4.fantom/fantom/lib/fan/syntax.pod index c2cb4d18..66236bbc 100755 Binary files a/com.xored.f4.fantom/fantom/lib/fan/syntax.pod and b/com.xored.f4.fantom/fantom/lib/fan/syntax.pod differ diff --git a/com.xored.f4.fantom/fantom/lib/fan/sys.pod b/com.xored.f4.fantom/fantom/lib/fan/sys.pod index 70abd6cb..767270cc 100755 Binary files a/com.xored.f4.fantom/fantom/lib/fan/sys.pod and b/com.xored.f4.fantom/fantom/lib/fan/sys.pod differ diff --git a/com.xored.f4.fantom/fantom/lib/fan/testCompiler.pod b/com.xored.f4.fantom/fantom/lib/fan/testCompiler.pod index 3c2a1fa0..ab2509e8 100755 Binary files a/com.xored.f4.fantom/fantom/lib/fan/testCompiler.pod and b/com.xored.f4.fantom/fantom/lib/fan/testCompiler.pod differ diff --git a/com.xored.f4.fantom/fantom/lib/fan/testDomkit.pod b/com.xored.f4.fantom/fantom/lib/fan/testDomkit.pod index e0e170e5..6dc69963 100644 Binary files a/com.xored.f4.fantom/fantom/lib/fan/testDomkit.pod and b/com.xored.f4.fantom/fantom/lib/fan/testDomkit.pod differ diff --git a/com.xored.f4.fantom/fantom/lib/fan/testJava.pod b/com.xored.f4.fantom/fantom/lib/fan/testJava.pod index 8218c528..40170b82 100755 Binary files a/com.xored.f4.fantom/fantom/lib/fan/testJava.pod and b/com.xored.f4.fantom/fantom/lib/fan/testJava.pod differ diff --git a/com.xored.f4.fantom/fantom/lib/fan/testNative.pod b/com.xored.f4.fantom/fantom/lib/fan/testNative.pod index 6b6b6c70..c44cf88f 100755 Binary files a/com.xored.f4.fantom/fantom/lib/fan/testNative.pod and b/com.xored.f4.fantom/fantom/lib/fan/testNative.pod differ diff --git a/com.xored.f4.fantom/fantom/lib/fan/testSys.pod b/com.xored.f4.fantom/fantom/lib/fan/testSys.pod index 3faf128f..9cc756c3 100755 Binary files a/com.xored.f4.fantom/fantom/lib/fan/testSys.pod and b/com.xored.f4.fantom/fantom/lib/fan/testSys.pod differ diff --git a/com.xored.f4.fantom/fantom/lib/fan/util.pod b/com.xored.f4.fantom/fantom/lib/fan/util.pod index 742c4c44..bb60749a 100755 Binary files a/com.xored.f4.fantom/fantom/lib/fan/util.pod and b/com.xored.f4.fantom/fantom/lib/fan/util.pod differ diff --git a/com.xored.f4.fantom/fantom/lib/fan/web.pod b/com.xored.f4.fantom/fantom/lib/fan/web.pod index c8527ee1..df99d322 100755 Binary files a/com.xored.f4.fantom/fantom/lib/fan/web.pod and b/com.xored.f4.fantom/fantom/lib/fan/web.pod differ diff --git a/com.xored.f4.fantom/fantom/lib/fan/webfwt.pod b/com.xored.f4.fantom/fantom/lib/fan/webfwt.pod index 372d855c..56ede572 100644 Binary files a/com.xored.f4.fantom/fantom/lib/fan/webfwt.pod and b/com.xored.f4.fantom/fantom/lib/fan/webfwt.pod differ diff --git a/com.xored.f4.fantom/fantom/lib/fan/webmod.pod b/com.xored.f4.fantom/fantom/lib/fan/webmod.pod index 740d1695..9eca0ca0 100755 Binary files a/com.xored.f4.fantom/fantom/lib/fan/webmod.pod and b/com.xored.f4.fantom/fantom/lib/fan/webmod.pod differ diff --git a/com.xored.f4.fantom/fantom/lib/fan/wisp.pod b/com.xored.f4.fantom/fantom/lib/fan/wisp.pod index befcea8c..9712f3af 100755 Binary files a/com.xored.f4.fantom/fantom/lib/fan/wisp.pod and b/com.xored.f4.fantom/fantom/lib/fan/wisp.pod differ diff --git a/com.xored.f4.fantom/fantom/lib/fan/xml.pod b/com.xored.f4.fantom/fantom/lib/fan/xml.pod index e1b94f2a..ceeeaf31 100755 Binary files a/com.xored.f4.fantom/fantom/lib/fan/xml.pod and b/com.xored.f4.fantom/fantom/lib/fan/xml.pod differ diff --git a/com.xored.f4.fantom/fantom/lib/java/sys.jar b/com.xored.f4.fantom/fantom/lib/java/sys.jar index de11015e..b7bf8bc2 100755 Binary files a/com.xored.f4.fantom/fantom/lib/java/sys.jar and b/com.xored.f4.fantom/fantom/lib/java/sys.jar differ diff --git a/com.xored.f4.fantom/fantom/src/build/build.fan b/com.xored.f4.fantom/fantom/src/build/build.fan index faac9e22..9adaf4a2 100755 --- a/com.xored.f4.fantom/fantom/src/build/build.fan +++ b/com.xored.f4.fantom/fantom/src/build/build.fan @@ -19,12 +19,12 @@ class Build : BuildPod podName = "build" summary = "Fantom build utility" meta = ["org.name": "Fantom", - "org.uri": "http://fantom.org/", + "org.uri": "https://fantom.org/", "proj.name": "Fantom Core", - "proj.uri": "http://fantom.org/", + "proj.uri": "https://fantom.org/", "license.name": "Academic Free License 3.0", - "vcs.name": "Mercurial", - "vcs.uri": "https://bitbucket.org/fantom/fan-1.0/"] + "vcs.name": "Git", + "vcs.uri": "https://github.com/fantom-lang/fantom"] depends = ["sys 1.0", "compiler 1.0"] srcDirs = [`fan/`, `fan/tasks/`] docSrc = true diff --git a/com.xored.f4.fantom/fantom/src/build/fan/BuildPod.fan b/com.xored.f4.fantom/fantom/src/build/fan/BuildPod.fan index cf47cd76..20a770b5 100755 --- a/com.xored.f4.fantom/fantom/src/build/fan/BuildPod.fan +++ b/com.xored.f4.fantom/fantom/src/build/fan/BuildPod.fan @@ -192,7 +192,7 @@ abstract class BuildPod : BuildScript // TODO: add additinal meta props defined by config file/env var // this behavior is not guaranteed in future versions, rather we // need to potentially overhaul how build data is defined - // See topic http://fantom.org/sidewalk/topic/1584 + // See topic https://fantom.org/forum/topic/1584 config("meta", "").split(',').each |pair| { if (pair.isEmpty) return diff --git a/com.xored.f4.fantom/fantom/src/build/fan/Main.fan b/com.xored.f4.fantom/fantom/src/build/fan/Main.fan index 230d006f..742ef2fc 100644 --- a/com.xored.f4.fantom/fantom/src/build/fan/Main.fan +++ b/com.xored.f4.fantom/fantom/src/build/fan/Main.fan @@ -154,7 +154,7 @@ internal class InitCmd summary = "Description of this pod" version = Version("1.0") // These values are optional, but recommended - // See: http://fantom.org/doc/docLang/Pods#meta + // See: https://fantom.org/doc/docLang/Pods#meta // meta = [ // "org.name": "My Org", // "org.uri": "http://myorg.org/", diff --git a/com.xored.f4.fantom/fantom/src/buildall.fan b/com.xored.f4.fantom/fantom/src/buildall.fan index 67964cb5..d297e50d 100755 --- a/com.xored.f4.fantom/fantom/src/buildall.fan +++ b/com.xored.f4.fantom/fantom/src/buildall.fan @@ -189,7 +189,6 @@ class Build : BuildGroup { superclean compile -// test examples deleteNonDist readme diff --git a/com.xored.f4.fantom/fantom/src/buildpods.fan b/com.xored.f4.fantom/fantom/src/buildpods.fan index 8a7af120..ca3ca0b0 100755 --- a/com.xored.f4.fantom/fantom/src/buildpods.fan +++ b/com.xored.f4.fantom/fantom/src/buildpods.fan @@ -30,6 +30,7 @@ class Build : BuildGroup `testNative/build.fan`, `testCompiler/build.fan`, `testJava/build.fan`, + `math/build.fan`, `util/build.fan`, `inet/build.fan`, `fansh/build.fan`, diff --git a/com.xored.f4.fantom/fantom/src/compiler/build.fan b/com.xored.f4.fantom/fantom/src/compiler/build.fan index b8927f30..0c214bee 100755 --- a/com.xored.f4.fantom/fantom/src/compiler/build.fan +++ b/com.xored.f4.fantom/fantom/src/compiler/build.fan @@ -19,12 +19,12 @@ class Build : BuildPod podName = "compiler" summary = "Fantom compiler" meta = ["org.name": "Fantom", - "org.uri": "http://fantom.org/", + "org.uri": "https://fantom.org/", "proj.name": "Fantom Core", - "proj.uri": "http://fantom.org/", + "proj.uri": "https://fantom.org/", "license.name": "Academic Free License 3.0", - "vcs.name": "Mercurial", - "vcs.uri": "https://bitbucket.org/fantom/fan-1.0/"] + "vcs.name": "Git", + "vcs.uri": "https://github.com/fantom-lang/fantom"] depends = ["sys 1.0"] srcDirs = [`fan/`, `fan/assembler/`, diff --git a/com.xored.f4.fantom/fantom/src/compiler/fan/ast/AstWriter.fan b/com.xored.f4.fantom/fantom/src/compiler/fan/ast/AstWriter.fan index dec232f2..d49c6cde 100755 --- a/com.xored.f4.fantom/fantom/src/compiler/fan/ast/AstWriter.fan +++ b/com.xored.f4.fantom/fantom/src/compiler/fan/ast/AstWriter.fan @@ -92,6 +92,7 @@ class AstWriter if (flags.and(FConst.Static) != 0) w("static ") if (flags.and(FConst.Storage) != 0) w("storage ") if (flags.and(FConst.Virtual) != 0) w("virtual ") + if (flags.and(FConst.Once) != 0) w("once ") if (flags.and(FConst.Synthetic) != 0) w("synthetic ") if (flags.and(FConst.Getter) != 0) w("getter ") diff --git a/com.xored.f4.fantom/fantom/src/compiler/fan/ast/MethodDef.fan b/com.xored.f4.fantom/fantom/src/compiler/fan/ast/MethodDef.fan index 1907c31c..bd3428a2 100755 --- a/com.xored.f4.fantom/fantom/src/compiler/fan/ast/MethodDef.fan +++ b/com.xored.f4.fantom/fantom/src/compiler/fan/ast/MethodDef.fan @@ -73,11 +73,6 @@ class MethodDef : SlotDef, CMethod ** Bool isFieldSetter() { accessorFor != null && paramDefs.size == 1 } - ** - ** Return if this is a once method - ** - Bool isOnce() { flags.and(Parser.Once) != 0 } - ** ** Return if this is a constructor with an it-block as last parameter ** diff --git a/com.xored.f4.fantom/fantom/src/compiler/fan/fcode/FConst.fan b/com.xored.f4.fantom/fantom/src/compiler/fan/fcode/FConst.fan index 55b1cc17..f09b09a3 100755 --- a/com.xored.f4.fantom/fantom/src/compiler/fan/fcode/FConst.fan +++ b/com.xored.f4.fantom/fantom/src/compiler/fan/fcode/FConst.fan @@ -41,7 +41,8 @@ mixin FConst const static Int Storage := 0x00010000 const static Int Synthetic := 0x00020000 const static Int Virtual := 0x00040000 - const static Int FlagsMask := 0x0007ffff + const static Int Once := 0x00080000 + const static Int FlagsMask := 0x000fffff ////////////////////////////////////////////////////////////////////////// // MethodVarFlags diff --git a/com.xored.f4.fantom/fantom/src/compiler/fan/fcode/FPrinter.fan b/com.xored.f4.fantom/fantom/src/compiler/fan/fcode/FPrinter.fan index 9abb860f..e25c9b96 100755 --- a/com.xored.f4.fantom/fantom/src/compiler/fan/fcode/FPrinter.fan +++ b/com.xored.f4.fantom/fantom/src/compiler/fan/fcode/FPrinter.fan @@ -249,6 +249,7 @@ class FPrinter : FConst if (flags.and(FConst.Storage) != 0) s.add("storage ") if (flags.and(FConst.Synthetic) != 0) s.add("synthetic ") if (flags.and(FConst.Virtual) != 0) s.add("virtual ") + if (flags.and(FConst.Once) != 0) s.add("once ") return s.toStr[0..-2] } diff --git a/com.xored.f4.fantom/fantom/src/compiler/fan/namespace/CSlot.fan b/com.xored.f4.fantom/fantom/src/compiler/fan/namespace/CSlot.fan index 9b1c0bd1..c4c059ce 100755 --- a/com.xored.f4.fantom/fantom/src/compiler/fan/namespace/CSlot.fan +++ b/com.xored.f4.fantom/fantom/src/compiler/fan/namespace/CSlot.fan @@ -39,6 +39,7 @@ mixin CSlot Bool isStorage() { flags.and(FConst.Storage) != 0 } Bool isSynthetic() { flags.and(FConst.Synthetic) != 0 } Bool isVirtual() { flags.and(FConst.Virtual) != 0 } + Bool isOnce() { flags.and(FConst.Once) != 0 } Bool isInstanceCtor() { isCtor && !isStatic } Bool isStaticCtor() { isCtor && isStatic } diff --git a/com.xored.f4.fantom/fantom/src/compiler/fan/namespace/GenericTypes.fan b/com.xored.f4.fantom/fantom/src/compiler/fan/namespace/GenericTypes.fan index 87c6deaf..23c0318d 100755 --- a/com.xored.f4.fantom/fantom/src/compiler/fan/namespace/GenericTypes.fan +++ b/com.xored.f4.fantom/fantom/src/compiler/fan/namespace/GenericTypes.fan @@ -82,6 +82,10 @@ abstract class GenericType : CType { t = parameterizeListType(nn) } + else if (nn is MapType) + { + t = parameterizeMapType(nn) + } else if (nn is FuncType) { t = parameterizeFuncType(nn) @@ -99,6 +103,11 @@ abstract class GenericType : CType return parameterize(t.v).toListOf } + private CType parameterizeMapType(MapType t) + { + return MapType(parameterize(t.k), parameterize(t.v)) + } + private CType parameterizeFuncType(FuncType t) { CType[] params := t.params.map |CType p->CType| { parameterize(p) } diff --git a/com.xored.f4.fantom/fantom/src/compiler/fan/parser/Parser.fan b/com.xored.f4.fantom/fantom/src/compiler/fan/parser/Parser.fan index 72502efa..40bde72a 100755 --- a/com.xored.f4.fantom/fantom/src/compiler/fan/parser/Parser.fan +++ b/com.xored.f4.fantom/fantom/src/compiler/fan/parser/Parser.fan @@ -283,7 +283,7 @@ public class Parser : CompilerSupport case Token.internalKeyword: flags = flags.or(FConst.Internal); protection = true case Token.nativeKeyword: flags = flags.or(FConst.Native) case Token.newKeyword: flags = flags.or(FConst.Ctor) - case Token.onceKeyword: flags = flags.or(Once) // Parser only flag + case Token.onceKeyword: flags = flags.or(FConst.Once) case Token.overrideKeyword: flags = flags.or(FConst.Override) case Token.privateKeyword: flags = flags.or(FConst.Private); protection = true case Token.protectedKeyword: flags = flags.or(FConst.Protected); protection = true @@ -466,7 +466,7 @@ public class Parser : CompilerSupport field := FieldDef(loc, parent) field.doc = doc field.facets = facets - field.flags = flags.and(ParserFlagsMask.not) + field.flags = flags field.name = name if (type != null) field.fieldType = type @@ -2416,11 +2416,6 @@ public class Parser : CompilerSupport // Parser Flags ////////////////////////////////////////////////////////////////////////// - // These are flags used only by the parser we merge with FConst - // flags by starting from most significant bit and working down - const static Int Once := 0x8000_0000 - const static Int ParserFlagsMask := 0 - // Bitwise and this mask to clear all protection scope flags const static Int ProtectionMask := (FConst.Public).or(FConst.Protected).or(FConst.Private).or(FConst.Internal).not diff --git a/com.xored.f4.fantom/fantom/src/compiler/fan/steps/CheckErrors.fan b/com.xored.f4.fantom/fantom/src/compiler/fan/steps/CheckErrors.fan index 83336083..40bf56b1 100755 --- a/com.xored.f4.fantom/fantom/src/compiler/fan/steps/CheckErrors.fan +++ b/com.xored.f4.fantom/fantom/src/compiler/fan/steps/CheckErrors.fan @@ -119,7 +119,7 @@ class CheckErrors : CompilerStep // these modifiers are never allowed on a type if (flags.and(FConst.Ctor) != 0) err("Cannot use 'new' modifier on type", loc) - if (flags.and(Parser.Once) != 0) err("Cannot use 'once' modifier on type", loc) + if (flags.and(FConst.Once) != 0) err("Cannot use 'once' modifier on type", loc) if (flags.and(FConst.Override) != 0) err("Cannot use 'override' modifier on type", loc) if (flags.and(FConst.Private) != 0) err("Cannot use 'private' modifier on type", loc) if (flags.and(FConst.Protected) != 0) err("Cannot use 'protected' modifier on type", loc) @@ -177,16 +177,9 @@ class CheckErrors : CompilerStep // in another check t.fieldDefs.each |FieldDef f| { - if (!f.isConst && !f.isStatic && f.isStorage && !isSys) + if (!f.isConst && !f.isStatic && f.isStorage && !isSys && !f.isOnce) err("Const type '$t.name' cannot contain non-const field '$f.name'", f.loc) } - - // check that no once methods - t.methodDefs.each |MethodDef m| - { - if (m.isOnce) - err("Const type '$t.name' cannot contain once method '$m.name'", m.loc) - } } private Void checkBase(TypeDef t, CType base) @@ -256,7 +249,7 @@ class CheckErrors : CompilerStep // these modifiers are never allowed on a field if (flags.and(FConst.Ctor) != 0) err("Cannot use 'new' modifier on field", loc) if (flags.and(FConst.Final) != 0) err("Cannot use 'final' modifier on field", loc) - if (flags.and(Parser.Once) != 0) err("Cannot use 'once' modifier on field", loc) + if (flags.and(FConst.Once) != 0 && flags.and(FConst.Synthetic) == 0) err("Cannot use 'once' modifier on field", loc) // check invalid protection combinations checkProtectionFlags(flags, loc) @@ -380,7 +373,7 @@ class CheckErrors : CompilerStep if (flags.and(FConst.Abstract) != 0) err("Invalid combination of 'new' and 'abstract' modifiers", loc) else if (flags.and(FConst.Override) != 0) err("Invalid combination of 'new' and 'override' modifiers", loc) else if (flags.and(FConst.Virtual) != 0) err("Invalid combination of 'new' and 'virtual' modifiers", loc) - if (flags.and(Parser.Once) != 0) err("Invalid combination of 'new' and 'once' modifiers", loc) + if (flags.and(FConst.Once) != 0) err("Invalid combination of 'new' and 'once' modifiers", loc) if (flags.and(FConst.Native) != 0) err("Invalid combination of 'new' and 'native' modifiers", loc) } @@ -390,18 +383,18 @@ class CheckErrors : CompilerStep if (flags.and(FConst.Abstract) != 0) err("Invalid combination of 'static' and 'abstract' modifiers", loc) else if (flags.and(FConst.Override) != 0) err("Invalid combination of 'static' and 'override' modifiers", loc) else if (flags.and(FConst.Virtual) != 0) err("Invalid combination of 'static' and 'virtual' modifiers", loc) - if (flags.and(Parser.Once) != 0) err("Invalid combination of 'static' and 'once' modifiers", loc) + if (flags.and(FConst.Once) != 0) err("Invalid combination of 'static' and 'once' modifiers", loc) } // check invalid abstract flags if (flags.and(FConst.Abstract) != 0) { if (flags.and(FConst.Native) != 0) err("Invalid combination of 'abstract' and 'native' modifiers", loc) - if (flags.and(Parser.Once) != 0) err("Invalid combination of 'abstract' and 'once' modifiers", loc) + if (flags.and(FConst.Once) != 0) err("Invalid combination of 'abstract' and 'once' modifiers", loc) } // mixins cannot have once methods - if (flags.and(Parser.Once) != 0) + if (flags.and(FConst.Once) != 0) { if (curType.isMixin) err("Mixins cannot have once methods", m.loc) @@ -444,7 +437,11 @@ class CheckErrors : CompilerStep t := p.paramType if (t.isVoid) { err("Cannot use Void as parameter type", p.loc); return } if (t.isThis) { err("Cannot use This as parameter type", p.loc); return } - if (t.toNonNullable.signature != "|sys::This->sys::Void|") checkValidType(p.loc, t) + func := t.deref.toNonNullable as FuncType + if (func != null) + checkParamFuncType(p, func) + else + checkValidType(p.loc, t) // check parameter default type if (p.def != null && !p.paramType.isGenericParameter) @@ -456,6 +453,19 @@ class CheckErrors : CompilerStep } } + private Void checkParamFuncType(ParamDef param, FuncType t) + { + if (!t.ret.isVoid && !t.ret.isValid) + err("Invalid return type '$t.ret' in func type of param '$param.name'", param.loc) + + // This type is allowed in func params as a co-variant position + t.params.each |p| + { + if (!p.isThis && !p.isValid) + err("Invalid param type '$p' in func type of param '$param.name'", param.loc) + } + } + private Void checkMethodReturn(MethodDef m) { if (m.ret.isThis) diff --git a/com.xored.f4.fantom/fantom/src/compiler/fan/steps/Normalize.fan b/com.xored.f4.fantom/fantom/src/compiler/fan/steps/Normalize.fan index e8bb7358..719eda91 100755 --- a/com.xored.f4.fantom/fantom/src/compiler/fan/steps/Normalize.fan +++ b/com.xored.f4.fantom/fantom/src/compiler/fan/steps/Normalize.fan @@ -173,8 +173,7 @@ class Normalize : CompilerStep loc := m.loc // we'll report these errors in CheckErrors - if (curType.isConst || curType.isMixin || - m.isStatic || m.isCtor || m.isFieldAccessor) + if (curType.isMixin || m.isStatic || m.isCtor || m.isFieldAccessor) return // error checking @@ -184,7 +183,7 @@ class Normalize : CompilerStep // generate storage field f := FieldDef(loc, curType) - f.flags = FConst.Private + FConst.Storage + FConst.Synthetic + f.flags = FConst.Private + FConst.Storage + FConst.Synthetic + FConst.Once f.name = m.name + "\$Store" f.fieldType = ns.objType.toNullable f.init = Expr.makeForLiteral(loc, ns, "_once_") diff --git a/com.xored.f4.fantom/fantom/src/compiler/fan/util/ApiDocWriter.fan b/com.xored.f4.fantom/fantom/src/compiler/fan/util/ApiDocWriter.fan index 17b28190..423fc37f 100755 --- a/com.xored.f4.fantom/fantom/src/compiler/fan/util/ApiDocWriter.fan +++ b/com.xored.f4.fantom/fantom/src/compiler/fan/util/ApiDocWriter.fan @@ -204,6 +204,7 @@ class ApiDocWriter if (flags.and(FConst.Synthetic) != 0) s.join("synthetic") if (flags.and(FConst.Virtual) != 0) s.join("virtual") if (flags.and(FConst.Ctor) != 0) s.join("new") + if (flags.and(FConst.Once) != 0) s.join("once") return s.toStr } diff --git a/com.xored.f4.fantom/fantom/src/compilerDoc/build.fan b/com.xored.f4.fantom/fantom/src/compilerDoc/build.fan index d2499784..275e3954 100755 --- a/com.xored.f4.fantom/fantom/src/compilerDoc/build.fan +++ b/com.xored.f4.fantom/fantom/src/compilerDoc/build.fan @@ -19,12 +19,12 @@ class Build : BuildPod podName = "compilerDoc" summary = "Compiler to model and generate API docs" meta = ["org.name": "Fantom", - "org.uri": "http://fantom.org/", + "org.uri": "https://fantom.org/", "proj.name": "Fantom Core", - "proj.uri": "http://fantom.org/", + "proj.uri": "https://fantom.org/", "license.name": "Academic Free License 3.0", - "vcs.name": "Mercurial", - "vcs.uri": "https://bitbucket.org/fantom/fan-1.0/"] + "vcs.name": "Git", + "vcs.uri": "https://github.com/fantom-lang/fantom"] depends = ["sys 1.0", "concurrent 1.0", "fandoc 1.0", diff --git a/com.xored.f4.fantom/fantom/src/compilerDoc/fan/env/DocEnv.fan b/com.xored.f4.fantom/fantom/src/compilerDoc/fan/env/DocEnv.fan index ab3e03ee..e34bcaf5 100755 --- a/com.xored.f4.fantom/fantom/src/compilerDoc/fan/env/DocEnv.fan +++ b/com.xored.f4.fantom/fantom/src/compilerDoc/fan/env/DocEnv.fan @@ -65,11 +65,21 @@ abstract const class DocEnv ** virtual Uri linkUri(DocLink link) { + if (link.absUri != null) return link.absUri s := StrBuf() if (link.from.isTopIndex) - s.add(link.target.space.spaceName).add("/") + { + if (!link.target.isTopIndex) + s.add(link.target.space.spaceName).add("/") + } + else if (link.target.isTopIndex) + { + s.add("../") + } else if (link.from.space !== link.target.space) + { s.add("../").add(link.target.space.spaceName).add("/") + } docName := link.target.docName if (docName == "pod-doc") docName = "index" s.add(docName) diff --git a/com.xored.f4.fantom/fantom/src/compilerDoc/fan/env/DocLink.fan b/com.xored.f4.fantom/fantom/src/compilerDoc/fan/env/DocLink.fan index 6dfc5d6d..8b2e5587 100755 --- a/com.xored.f4.fantom/fantom/src/compilerDoc/fan/env/DocLink.fan +++ b/com.xored.f4.fantom/fantom/src/compilerDoc/fan/env/DocLink.fan @@ -41,6 +41,15 @@ const class DocLink this.frag = frag } + ** Construct an absolute link such as "https://foo/" + new makeAbsUri(Doc from, Uri uri, Str dis) + { + this.from = from // not really used + this.target = from // not really used + this.absUri = uri + this.dis = dis + } + ** Document we are linking from const Doc from @@ -53,6 +62,9 @@ const class DocLink ** Optional fragment in the link document const Str? frag + ** If link resolves to an absolute URI + const Uri? absUri + ** Debug string representation override Str toStr() { diff --git a/com.xored.f4.fantom/fantom/src/compilerDoc/fan/env/DocTheme.fan b/com.xored.f4.fantom/fantom/src/compilerDoc/fan/env/DocTheme.fan index 941d0891..efa7df6d 100755 --- a/com.xored.f4.fantom/fantom/src/compilerDoc/fan/env/DocTheme.fan +++ b/com.xored.f4.fantom/fantom/src/compilerDoc/fan/env/DocTheme.fan @@ -55,10 +55,6 @@ const class DocTheme { // skip } - else if (doc is DocChapter) - { - out.li.a(`${doc.docName}$ext`).w(r.doc.title).aEnd.liEnd - } else if (doc is DocSrc) { src := (DocSrc)doc diff --git a/com.xored.f4.fantom/fantom/src/compilerDoc/fan/model/Doc.fan b/com.xored.f4.fantom/fantom/src/compilerDoc/fan/model/Doc.fan index 4b641843..13211b38 100755 --- a/com.xored.f4.fantom/fantom/src/compilerDoc/fan/model/Doc.fan +++ b/com.xored.f4.fantom/fantom/src/compilerDoc/fan/model/Doc.fan @@ -69,11 +69,18 @@ const abstract class Doc ** virtual Void onCrawl(DocCrawler crawler) {} + ** ** Get a chapter heading for this document by its anchor id + ** virtual DocHeading? heading(Str id, Bool checked := true) { if (checked) throw NameErr("Unknown header anchor id $space.spaceName::$docName#${id}") return null } + + ** + ** Return docName by default + ** + override Str toStr() { docName } } diff --git a/com.xored.f4.fantom/fantom/src/compilerDoc/fan/model/DocChapter.fan b/com.xored.f4.fantom/fantom/src/compilerDoc/fan/model/DocChapter.fan index e2b521f9..fd55919c 100755 --- a/com.xored.f4.fantom/fantom/src/compilerDoc/fan/model/DocChapter.fan +++ b/com.xored.f4.fantom/fantom/src/compilerDoc/fan/model/DocChapter.fan @@ -114,6 +114,9 @@ const class DocChapter : Doc ** Title is 'meta.title', or qualified name if not specified. override Str title() { meta["title"] ?: qname } + ** Use title for breadcrumb + override Str breadcrumb() { title } + ** Default renderer is `DocChapterRenderer` override Type renderer() { DocChapterRenderer# } diff --git a/com.xored.f4.fantom/fantom/src/compilerDoc/fan/model/DocFlags.fan b/com.xored.f4.fantom/fantom/src/compilerDoc/fan/model/DocFlags.fan index ff137fe0..0a3b278f 100755 --- a/com.xored.f4.fantom/fantom/src/compilerDoc/fan/model/DocFlags.fan +++ b/com.xored.f4.fantom/fantom/src/compilerDoc/fan/model/DocFlags.fan @@ -31,6 +31,7 @@ final const class DocFlags const static Int Storage := 0x00010000 const static Int Synthetic := 0x00020000 const static Int Virtual := 0x00040000 + const static Int Once := 0x00080000 static Bool isAbstract (Int flags) { flags.and(DocFlags.Abstract) != 0 } static Bool isConst (Int flags) { flags.and(DocFlags.Const) != 0 } @@ -51,6 +52,7 @@ final const class DocFlags static Bool isStorage (Int flags) { flags.and(DocFlags.Storage) != 0 } static Bool isSynthetic(Int flags) { flags.and(DocFlags.Synthetic) != 0 } static Bool isVirtual (Int flags) { flags.and(DocFlags.Virtual) != 0 } + static Bool isOnce (Int flags) { flags.and(DocFlags.Once) != 0 } static Int fromName(Str name) { diff --git a/com.xored.f4.fantom/fantom/src/compilerDoc/fan/model/DocMisc.fan b/com.xored.f4.fantom/fantom/src/compilerDoc/fan/model/DocMisc.fan index f95eef44..ffabe43c 100755 --- a/com.xored.f4.fantom/fantom/src/compilerDoc/fan/model/DocMisc.fan +++ b/com.xored.f4.fantom/fantom/src/compilerDoc/fan/model/DocMisc.fan @@ -28,7 +28,7 @@ const class DocTopIndex : Doc override DocSpace space() { throw UnsupportedErr() } ** Throw UnsupportedErr - override Str docName() { throw UnsupportedErr() } + override Str docName() { "index" } ** Default is "Doc Index" override const Str title := "Doc Index" @@ -38,6 +38,9 @@ const class DocTopIndex : Doc ** Return true override Bool isTopIndex() { true} + + ** Debug string + override Str toStr() { typeof.name } } ************************************************************************** diff --git a/com.xored.f4.fantom/fantom/src/compilerDoc/fan/model/DocPod.fan b/com.xored.f4.fantom/fantom/src/compilerDoc/fan/model/DocPod.fan index 99d520bf..f22f315a 100755 --- a/com.xored.f4.fantom/fantom/src/compilerDoc/fan/model/DocPod.fan +++ b/com.xored.f4.fantom/fantom/src/compilerDoc/fan/model/DocPod.fan @@ -13,17 +13,29 @@ const class DocPod : DocSpace { - ** Load from a zip file. + ** Load from a zip file using the given DocEnv as the gerror handler static DocPod load(DocEnv? env, File file) { - return DocPod(env, file) + loadFile(file) |err| + { + if (env == null) + echo("ERROR: $err") + else + env.errReport(err) + } + } + + ** Load from a zip file with given error handler + static DocPod loadFile(File file, |DocErr| onErr) + { + make(file, onErr) } ** Private constructor to copy loader fields - @NoDoc new make(DocEnv? env, File file) + @NoDoc new make(File file, |DocErr| onErr) { this.file = file - loader := DocPodLoader(env, file, this) + loader := DocPodLoader(file, this, onErr) zip := Zip.open(file) try { @@ -247,11 +259,11 @@ const class DocPodIndex : Doc internal class DocPodLoader { - new make(DocEnv? env, File file, DocPod pod) + new make(File file, DocPod pod, |DocErr| onErr) { - this.env = env - this.file = file - this.pod = pod + this.file = file + this.pod = pod + this.onErr = onErr } Void loadMeta(Zip zip) @@ -466,15 +478,12 @@ internal class DocPodLoader Void err(Str msg, DocLoc loc, Err? cause := null) { - if (env == null) - echo("ERROR: $msg [$loc]") - else - env.err(msg, loc, cause) + onErr(DocErr(msg, loc, cause)) } - DocEnv? env // ctor File file // ctor DocPod pod // ctor + |DocErr| onErr // ctor [Str:Str]? meta // load Str? name // loadMeta Str? summary // loadMeta diff --git a/com.xored.f4.fantom/fantom/src/compilerDoc/fan/model/DocSlot.fan b/com.xored.f4.fantom/fantom/src/compilerDoc/fan/model/DocSlot.fan index abef78da..6d5f7aeb 100755 --- a/com.xored.f4.fantom/fantom/src/compilerDoc/fan/model/DocSlot.fan +++ b/com.xored.f4.fantom/fantom/src/compilerDoc/fan/model/DocSlot.fan @@ -39,6 +39,12 @@ abstract const class DocSlot ** Flags mask - see `DocFlags` const Int flags + ** Is this a DocField + abstract Bool isField() + + ** Is this a DocMethod + abstract Bool isMethod() + ** Display name is Type.name Str dis() { parent.name + "." + name } @@ -92,6 +98,12 @@ const class DocField : DocSlot ** Flags for setting method if different from overall field level ** flags, otherwise null. const Int? setterFlags + + ** Return true + override Bool isField() { true } + + ** Return false + override Bool isMethod() { false } } ************************************************************************** @@ -117,6 +129,12 @@ const class DocMethod : DocSlot ** Parameters of the method const DocParam[] params + + ** Return false + override Bool isField() { false } + + ** Return true + override Bool isMethod() { true } } ************************************************************************** diff --git a/com.xored.f4.fantom/fantom/src/compilerDoc/fan/model/DocSpace.fan b/com.xored.f4.fantom/fantom/src/compilerDoc/fan/model/DocSpace.fan index 39d6a304..07b88878 100755 --- a/com.xored.f4.fantom/fantom/src/compilerDoc/fan/model/DocSpace.fan +++ b/com.xored.f4.fantom/fantom/src/compilerDoc/fan/model/DocSpace.fan @@ -33,4 +33,9 @@ const abstract class DocSpace ** Iterate all the documents in this space. ** abstract Void eachDoc(|Doc| f) + + ** + ** Return spaceName by default + ** + override Str toStr() { spaceName } } \ No newline at end of file diff --git a/com.xored.f4.fantom/fantom/src/compilerDoc/fan/model/DocType.fan b/com.xored.f4.fantom/fantom/src/compilerDoc/fan/model/DocType.fan index 9a3177bc..d0fed77f 100755 --- a/com.xored.f4.fantom/fantom/src/compilerDoc/fan/model/DocType.fan +++ b/com.xored.f4.fantom/fantom/src/compilerDoc/fan/model/DocType.fan @@ -15,7 +15,7 @@ const class DocType : Doc { ** Constructor - internal new make(DocPod pod, DocAttrs attrs, DocTypeRef ref, Str:DocSlot slotMap) + internal new make(DocPod pod, DocAttrs attrs, DocTypeRef ref, DocSlot[] list, Str:DocSlot slotMap) { this.pod = pod this.ref = ref @@ -29,8 +29,6 @@ const class DocType : Doc this.isErr = base.find {it.qname=="sys::Err"} != null this.isNoDoc = hasFacet("sys::NoDoc") - // create sorted list - list := slotMap.vals.sort|a, b| { a.name <=> b.name } // filter out slots which shouldn't be documented, // but leave them in the map for lookup @@ -41,7 +39,8 @@ const class DocType : Doc DocFlags.isPrivate(s.flags) || DocFlags.isSynthetic(s.flags) } - this.slots = list + this.declared = list + this.slots = list.sort|a, b| { a.name <=> b.name } } ** Pod which defines this type @@ -109,9 +108,12 @@ const class DocType : Doc ** Is this a subclass of 'sys::Err' const Bool isErr - ** List of the public, documented slots in this type. + ** List of the public, documented slots in this type (sorted). const DocSlot[] slots + ** List of the public, documented slots in this type (in declared order). + @NoDoc const DocSlot[] declared + ** Get slot by name. If not found return null or raise UknownSlotErr DocSlot? slot(Str name, Bool checked := true) { diff --git a/com.xored.f4.fantom/fantom/src/compilerDoc/fan/model/DocTypeRef.fan b/com.xored.f4.fantom/fantom/src/compilerDoc/fan/model/DocTypeRef.fan index 2e560e39..8ab52bc4 100755 --- a/com.xored.f4.fantom/fantom/src/compilerDoc/fan/model/DocTypeRef.fan +++ b/com.xored.f4.fantom/fantom/src/compilerDoc/fan/model/DocTypeRef.fan @@ -50,6 +50,9 @@ abstract const class DocTypeRef ** Is this a nullable type such as 'Str?' abstract Bool isNullable() + ** Is this one of the generic variable types such as 'sys::V' + abstract Bool isGenericVar() + ** Is this a parameterized generic type such as 'Str[]' abstract Bool isParameterized() @@ -88,6 +91,7 @@ internal const class BasicTypeRef : DocTypeRef override Str dis() { name } override Bool isNullable() { false } override Bool isParameterized() { false } + override Bool isGenericVar() { name.size == 1 && pod == "sys" } override DocTypeRef? v() { null } override DocTypeRef? k() { null } override DocTypeRef[]? funcParams() { null } @@ -109,6 +113,7 @@ internal const class NullableTypeRef : DocTypeRef override Str dis() { "${base.dis}?" } override Bool isNullable() { true } override Bool isParameterized() { base.isParameterized } + override Bool isGenericVar() { base.isGenericVar } override DocTypeRef? v() { base.v } override DocTypeRef? k() { base.k } override DocTypeRef[]? funcParams() { base.funcParams } @@ -129,6 +134,7 @@ internal const class ListTypeRef : DocTypeRef override Str dis() { "${v.dis}[]" } override Bool isNullable() { false } override Bool isParameterized() { true } + override Bool isGenericVar() { false } override const DocTypeRef? v override DocTypeRef? k() { null } override DocTypeRef[]? funcParams() { null } @@ -149,6 +155,7 @@ internal const class MapTypeRef : DocTypeRef override Str dis() { "[$k.dis:$v.dis]" } override Bool isNullable() { false } override Bool isParameterized() { true } + override Bool isGenericVar() { false } override const DocTypeRef? k override const DocTypeRef? v override DocTypeRef[]? funcParams() { null } @@ -187,6 +194,7 @@ internal const class FuncTypeRef : DocTypeRef } override Bool isNullable() { false } override Bool isParameterized() { true } + override Bool isGenericVar() { false } override DocTypeRef? v() { null } override DocTypeRef? k() { null } override const DocTypeRef[]? funcParams diff --git a/com.xored.f4.fantom/fantom/src/compilerDoc/fan/renderers/DocRenderer.fan b/com.xored.f4.fantom/fantom/src/compilerDoc/fan/renderers/DocRenderer.fan index 6a40df53..2932e54f 100755 --- a/com.xored.f4.fantom/fantom/src/compilerDoc/fan/renderers/DocRenderer.fan +++ b/com.xored.f4.fantom/fantom/src/compilerDoc/fan/renderers/DocRenderer.fan @@ -24,10 +24,9 @@ abstract class DocRenderer ** All subclasses must implement ctor with env, out, doc params. new make(DocEnv env, WebOutStream out, Doc doc) { - this.env = env - this.out = out - this.doc = doc - this.theme = env.theme + this.envRef = env + this.outRef = out + this.docRef = doc } ////////////////////////////////////////////////////////////////////////// @@ -35,17 +34,20 @@ abstract class DocRenderer ////////////////////////////////////////////////////////////////////////// ** Environment with access to model, theme, linking, etc - DocEnv env { private set } + virtual DocEnv env() { envRef } + private DocEnv envRef ** HTML output stream - WebOutStream out { private set } + virtual WebOutStream out() { outRef } + private WebOutStream outRef ** Document to be renderered - const Doc doc + virtual Doc doc() { docRef } + private Doc docRef ** Theme to use for rendering chrome and navigation. ** This field is initialized from `DocEnv.theme`. - const DocTheme theme + virtual DocTheme theme() { env.theme } ////////////////////////////////////////////////////////////////////////// // Hooks @@ -111,50 +113,17 @@ abstract class DocRenderer virtual Void writeFandoc(DocFandoc doc) { // parse fandoc - loc := doc.loc + docLoc := doc.loc parser := FandocParser() parser.silent = true - root := parser.parse(loc.file, doc.text.in) + root := parser.parse(docLoc.file, doc.text.in) // if no errors, then write as HTML if (parser.errs.isEmpty) { writer := HtmlDocWriter(out) - writer.onLink = |Link elem| - { - // don't process absolute links - orig := elem.uri - if (orig.startsWith("http:/") || - orig.startsWith("https:/") || - orig.startsWith("ftp:/")) return - - linkLoc := DocLoc(loc.file, loc.line + elem.line -1) - try - { - // route to DocEnv.link - link := resolveFandocLink(elem, true) - - // get environment URI for the DocLink - elem.uri = env.linkUri(link).encode - elem.isCode = link.target.isCode - - // extra checking - env.linkCheck(link, linkLoc) - - // if link text was original URI, then update with DocLink.dis - if (elem.children.first is DocText && elem.children.first.toStr == orig) - { - elem.removeAll.add(DocText(link.dis)) - } - } - catch (Err e) - { - if (elem.uri.startsWith("examples::")) - elem.uri = "http://fantom.org/doc/" + elem.uri.replace("::", "/") - else - env.err(e.toStr, linkLoc) - } - } + writer.onLink = |Link elem| { onFandocLink(elem, toFandocElemLoc(docLoc, elem.line)) } + writer.onImage = |Image elem| { onFandocImage(elem, toFandocElemLoc(docLoc, elem.line)) } root.children.each |child| { child.write(writer) } } @@ -164,7 +133,7 @@ abstract class DocRenderer // report each error parser.errs.each |err| { - env.err(err.msg, DocLoc(loc.file, loc.line + err.line - 1)) + env.err(err.msg, toFandocElemLoc(docLoc, err.line)) } // print as
@@ -172,6 +141,55 @@ abstract class DocRenderer
     }
   }
 
+  ** Map document location and element to the element location
+  private DocLoc toFandocElemLoc(DocLoc docLoc, Int line)
+  {
+    DocLoc(docLoc.file, docLoc.line + line - 1)
+  }
+
+  ** Fandoc handling for link nodes
+  @NoDoc
+  virtual Void onFandocLink(Link elem, DocLoc loc)
+  {
+    // don't process absolute links
+    orig := elem.uri
+    if (orig.startsWith("http:/") ||
+        orig.startsWith("https:/") ||
+        orig.startsWith("ftp:/")) return
+
+    try
+    {
+      // route to DocEnv.link
+      link := resolveFandocLink(elem, true)
+
+      // get environment URI for the DocLink
+      elem.uri = env.linkUri(link).encode
+      elem.isCode = link.target.isCode
+
+      // extra checking
+      env.linkCheck(link, loc)
+
+      // if link text was original URI, then update with DocLink.dis
+      if (elem.children.first is DocText && elem.children.first.toStr == orig)
+      {
+        elem.removeAll.add(DocText(link.dis))
+      }
+    }
+    catch (Err e)
+    {
+      if (elem.uri.startsWith("examples::"))
+        elem.uri = "https://fantom.org/doc/" + elem.uri.replace("::", "/")
+      else
+        onFandocErr(e, loc)
+    }
+  }
+
+  ** Fandoc handling for inage nodes
+  @NoDoc
+  virtual Void onFandocImage(Image elem, DocLoc loc)
+  {
+  }
+
   **
   ** Hook used to map a fandoc link to a doc link
   **
@@ -179,4 +197,11 @@ abstract class DocRenderer
   {
     env.link(this.doc, elem.uri, true)
   }
+
+  ** Handle a fandoc linking error
+  @NoDoc
+  virtual Void onFandocErr(Err e, DocLoc loc)
+  {
+    env.err(e.toStr, loc)
+  }
 }
\ No newline at end of file
diff --git a/com.xored.f4.fantom/fantom/src/compilerDoc/fan/renderers/DocTypeRenderer.fan b/com.xored.f4.fantom/fantom/src/compilerDoc/fan/renderers/DocTypeRenderer.fan
index f2b838f2..bed1f0ed 100755
--- a/com.xored.f4.fantom/fantom/src/compilerDoc/fan/renderers/DocTypeRenderer.fan
+++ b/com.xored.f4.fantom/fantom/src/compilerDoc/fan/renderers/DocTypeRenderer.fan
@@ -113,7 +113,7 @@ class DocTypeRenderer : DocRenderer
     if (DocFlags.isEnum(type.flags))
     {
       out.ul
-      type.slots.each |s|
+      type.declared.each |s|
       {
         if (DocFlags.isEnum(s.flags))
           out.li.a(`#$s.name`).esc(s.name).aEnd.liEnd
@@ -171,7 +171,14 @@ class DocTypeRenderer : DocRenderer
   {
     out.p("class='sig'").code
     slot.facets.each |f| { writeFacet(f); out.br }
+    writeSlotSigText(slot)
+    out.codeEnd.pEnd
+  }
 
+  ** Render slot signature inside the outer p element.
+  ** This does *not* include facets, but does include signature links.
+  @NoDoc Void writeSlotSigText(DocSlot slot)
+  {
     if (slot is DocField)
     {
       // field sig
@@ -209,8 +216,6 @@ class DocTypeRenderer : DocRenderer
       }
       out.w(")")
     }
-
-    out.codeEnd.pEnd
   }
 
 //////////////////////////////////////////////////////////////////////////
@@ -281,6 +286,11 @@ class DocTypeRenderer : DocRenderer
       else throw Err("Unsupported parameterized type: $ref")
       if (ref.isNullable) out.w("?")
     }
+    else if (ref.isGenericVar)
+    {
+      out.w(full ? ref.qname : ref.name)
+         .w(ref.isNullable ? "?" : "")
+    }
     else
     {
       // make link by hand to avoid having to resolve
@@ -302,6 +312,14 @@ class DocTypeRenderer : DocRenderer
   virtual Void writeFacet(DocFacet f)
   {
     out.code("class='sig'")
+    writeFacetText(f)
+    out.codeEnd
+  }
+
+  ** Write the facet content inside the outer code element
+  ** which includes links to the facet type and body values.
+  @NoDoc Void writeFacetText(DocFacet f)
+  {
     out.w("@")
     writeTypeRef(f.type)
     if (f.fields.size > 0)
@@ -309,7 +327,6 @@ class DocTypeRenderer : DocRenderer
       s := f.fields.join("; ") |v,n| { "$n.toXml=$v.toXml" }
       out.w(" { $s }")
     }
-    out.codeEnd
   }
 
   ** Map filename/line number to a source file link
@@ -330,5 +347,6 @@ class DocTypeRenderer : DocRenderer
     writeLink(link)
     out.pEnd
   }
+
 }
 
diff --git a/com.xored.f4.fantom/fantom/src/compilerDoc/fan/util/ApiDocParser.fan b/com.xored.f4.fantom/fantom/src/compilerDoc/fan/util/ApiDocParser.fan
index 75fc67c3..947bd5a8 100755
--- a/com.xored.f4.fantom/fantom/src/compilerDoc/fan/util/ApiDocParser.fan
+++ b/com.xored.f4.fantom/fantom/src/compilerDoc/fan/util/ApiDocParser.fan
@@ -68,16 +68,18 @@ internal class ApiDocParser
       this.typeLoc = attrs.loc
 
       // zero or more slots
-      slots := Str:DocSlot[:]
+      list := DocSlot[,]
+      map  := Str:DocSlot[:]
       while (true)
       {
         slot := parseSlot
         if (slot == null) break
-        slots[slot.name] = slot
+        list.add(slot)
+        map[slot.name] = slot
       }
 
       // construct DocType from my own fields
-      return DocType(pod, attrs, typeRef, slots)
+      return DocType(pod, attrs, typeRef, list, map)
     }
     finally { if (close) in.close }
   }
@@ -133,7 +135,8 @@ internal class ApiDocParser
     consumeLine
 
     // attrs, facets, and doc
-    attrs  := parseAttrs
+    attrs := parseAttrs
+    attrs.flags = attrs.flags.and(DocFlags.Const.not)
     return DocMethod(attrs, typeRef, name, returns, params)
   }
 
diff --git a/com.xored.f4.fantom/fantom/src/compilerJava/build.fan b/com.xored.f4.fantom/fantom/src/compilerJava/build.fan
index 9dbca156..1888f078 100755
--- a/com.xored.f4.fantom/fantom/src/compilerJava/build.fan
+++ b/com.xored.f4.fantom/fantom/src/compilerJava/build.fan
@@ -19,12 +19,12 @@ class Build : BuildPod
     podName    = "compilerJava"
     summary    = "Compiler FFI Plugin for Java"
     meta       = ["org.name":     "Fantom",
-                  "org.uri":      "http://fantom.org/",
+                  "org.uri":      "https://fantom.org/",
                   "proj.name":    "Fantom Core",
-                  "proj.uri":     "http://fantom.org/",
+                  "proj.uri":     "https://fantom.org/",
                   "license.name": "Academic Free License 3.0",
-                  "vcs.name":     "Mercurial",
-                  "vcs.uri":      "https://bitbucket.org/fantom/fan-1.0/"]
+                  "vcs.name":     "Git",
+                  "vcs.uri":      "https://github.com/fantom-lang/fantom"]
     depends    = ["sys 1.0", "compiler 1.0"]
     srcDirs    = [`fan/`, `fan/cp/`, `fan/dasm/`]
     docSrc     = true
diff --git a/com.xored.f4.fantom/fantom/src/compilerJava/fan/JavaBridge.fan b/com.xored.f4.fantom/fantom/src/compilerJava/fan/JavaBridge.fan
index 23ffd586..dd7e38c4 100755
--- a/com.xored.f4.fantom/fantom/src/compilerJava/fan/JavaBridge.fan
+++ b/com.xored.f4.fantom/fantom/src/compilerJava/fan/JavaBridge.fan
@@ -452,7 +452,7 @@ class JavaBridge : CBridge
 
     // handle Fantom to Java primitives
     if (expected.pod == primitives)
-      return coerceToPrimitive(expr, expected, onErr)
+      return coerceToPrimitive(expr, expected.toNonNullable, onErr)
 
     // handle Java primitives to Fan
     if (actual.pod == primitives)
diff --git a/com.xored.f4.fantom/fantom/src/compilerJs/build.fan b/com.xored.f4.fantom/fantom/src/compilerJs/build.fan
index 452cabd0..5ce1a280 100755
--- a/com.xored.f4.fantom/fantom/src/compilerJs/build.fan
+++ b/com.xored.f4.fantom/fantom/src/compilerJs/build.fan
@@ -19,12 +19,12 @@ class Build : BuildPod
     podName = "compilerJs"
     summary = "Fantom to JavaScript Compiler"
     meta    = ["org.name":     "Fantom",
-               "org.uri":      "http://fantom.org/",
+               "org.uri":      "https://fantom.org/",
                "proj.name":    "Fantom Core",
-               "proj.uri":     "http://fantom.org/",
+               "proj.uri":     "https://fantom.org/",
                "license.name": "Academic Free License 3.0",
-               "vcs.name":     "Mercurial",
-               "vcs.uri":      "https://bitbucket.org/fantom/fan-1.0/"]
+               "vcs.name":     "Git",
+               "vcs.uri":      "https://github.com/fantom-lang/fantom"]
     depends = ["sys 1.0", "compiler 1.0"]
     srcDirs = [`fan/`, `fan/ast/`, `fan/runner/`, `fan/util/`]
     resDirs = [`res/`]
diff --git a/com.xored.f4.fantom/fantom/src/compilerJs/fan/CompileJsPlugin.fan b/com.xored.f4.fantom/fantom/src/compilerJs/fan/CompileJsPlugin.fan
index d73405fe..5089ce0c 100755
--- a/com.xored.f4.fantom/fantom/src/compilerJs/fan/CompileJsPlugin.fan
+++ b/com.xored.f4.fantom/fantom/src/compilerJs/fan/CompileJsPlugin.fan
@@ -30,15 +30,16 @@ class CompileJsPlugin : CompilerStep
     buf       := StrBuf()
     support   := JsCompilerSupport(this)
     sourcemap := SourceMap(support)
+    jsOut     := JsWriter(buf.out, sourcemap)
 
     jsPod = JsPod(support, pod, types)
-    jsPod.write(JsWriter(buf.out, sourcemap))
+    jsPod.write(jsOut)
 
     compiler.jsPod = jsPod
     compiler.js    = buf.toStr
 
     buf.clear
-    sourcemap.write(buf.clear.out)
+    sourcemap.write(jsOut.line, buf.clear.out)
     compiler.jsSourceMap = buf.toStr
   }
 
@@ -48,4 +49,4 @@ class CompileJsPlugin : CompilerStep
 
   JsPod? jsPod  // JsPod AST
 
-}
+}
\ No newline at end of file
diff --git a/com.xored.f4.fantom/fantom/src/compilerJs/fan/ast/JsClosure.fan b/com.xored.f4.fantom/fantom/src/compilerJs/fan/ast/JsClosure.fan
index f4817043..d96fbfc0 100644
--- a/com.xored.f4.fantom/fantom/src/compilerJs/fan/ast/JsClosure.fan
+++ b/com.xored.f4.fantom/fantom/src/compilerJs/fan/ast/JsClosure.fan
@@ -36,7 +36,7 @@ class JsPodClosures : JsNode
     out.indent
 
     CType[] sigTypes := [,].addAll(ce.signature.params).add(ce.signature.ret)
-    isJs := sigTypes.all { JsType.isJsSafe(it) }
+    isJs := sigTypes.all { JsType.checkJsSafety(it, support, loc) }
     if (isJs)
     {
       // closure spec
diff --git a/com.xored.f4.fantom/fantom/src/compilerJs/fan/ast/JsPod.fan b/com.xored.f4.fantom/fantom/src/compilerJs/fan/ast/JsPod.fan
index d71b0251..7cb5f5d2 100755
--- a/com.xored.f4.fantom/fantom/src/compilerJs/fan/ast/JsPod.fan
+++ b/com.xored.f4.fantom/fantom/src/compilerJs/fan/ast/JsPod.fan
@@ -92,7 +92,6 @@ class JsPod : JsNode
       in.close
     }
     out.w("}).call(this);").nl
-    out.w("//# sourceMappingURL=/pod/${name}/${name}.js.map").nl
   }
 
   static Void writeNs(JsWriter out, Str name)
@@ -198,4 +197,4 @@ class JsPod : JsNode
   JsType[] types     // types in this pod
   Str:File natives   // natives
   JsProps[] props    // prop files in this pod
-}
+}
\ No newline at end of file
diff --git a/com.xored.f4.fantom/fantom/src/compilerJs/fan/ast/JsStmt.fan b/com.xored.f4.fantom/fantom/src/compilerJs/fan/ast/JsStmt.fan
index 5f5938ba..b8bd241a 100755
--- a/com.xored.f4.fantom/fantom/src/compilerJs/fan/ast/JsStmt.fan
+++ b/com.xored.f4.fantom/fantom/src/compilerJs/fan/ast/JsStmt.fan
@@ -76,6 +76,7 @@ class JsLocalDefStmt : JsStmt
     this.lds  = lds
     this.name = lds.name
     this.init = (lds.init != null) ? JsExpr.makeFor(s, lds.init) : null
+    JsType.checkJsSafety(lds.ctype, s, lds.loc)
   }
   override Void write(JsWriter out)
   {
diff --git a/com.xored.f4.fantom/fantom/src/compilerJs/fan/ast/JsType.fan b/com.xored.f4.fantom/fantom/src/compilerJs/fan/ast/JsType.fan
index acfc3e36..28b7968c 100755
--- a/com.xored.f4.fantom/fantom/src/compilerJs/fan/ast/JsType.fan
+++ b/com.xored.f4.fantom/fantom/src/compilerJs/fan/ast/JsType.fan
@@ -115,11 +115,29 @@ class JsType : JsNode
 
   override Str toStr() { sig }
 
-  ** Return true if type is javascript safe
-  static Bool isJsSafe(CType ctype)
+  static Bool checkJsSafety(CType ctype, CompilerSupport cs, Loc? loc)
   {
-    // TODO FIXIT: don't check sys yet
-    ctype.pod.name == "sys" || ctype.isSynthetic || ctype.facet("sys::Js") != null
+    if (ctype is TypeRef)           return checkJsSafety(ctype->t, cs, loc)
+    else if (ctype is NullableType) return checkJsSafety(ctype->root, cs, loc)
+    else if (ctype is ListType)     return checkJsSafety(ctype->v, cs, loc)
+    else if (ctype is MapType)
+    {
+      return checkJsSafety(ctype->k, cs, loc) && checkJsSafety(ctype->v, cs, loc)
+    }
+    else if (ctype is FuncType)
+    {
+      safe := true
+      ft := (FuncType)ctype
+      ft.params.each |param| { safe = safe && checkJsSafety(param, cs, loc) }
+      safe = safe && checkJsSafety(ft.ret, cs, loc)
+      return safe
+    }
+    else if (!(ctype.pod.name == "sys" || ctype.isSynthetic || ctype.facet("sys::Js") != null))
+    {
+      cs.warn("Type '$ctype.qname' not available in Js", loc)
+      return false
+    }
+    return true
   }
 
   TypeDef def            // compiler TypeDef
@@ -181,12 +199,7 @@ class JsTypeRef : JsNode
       v = JsTypeRef.make(cs, deref->v, loc)
     }
 
-    if (!JsType.isJsSafe(ref))
-    {
-      // TODO FIXIT: warn for now
-      //cs.err("Type '$ref.qname' not available in Js", loc)
-      cs.warn("Type '$ref.qname' not available in Js", loc)
-    }
+    JsType.checkJsSafety(ref, cs, loc)
   }
 
 
diff --git a/com.xored.f4.fantom/fantom/src/compilerJs/fan/runner/NodeRunner.fan b/com.xored.f4.fantom/fantom/src/compilerJs/fan/runner/NodeRunner.fan
index e5a69cef..a20d4df0 100644
--- a/com.xored.f4.fantom/fantom/src/compilerJs/fan/runner/NodeRunner.fan
+++ b/com.xored.f4.fantom/fantom/src/compilerJs/fan/runner/NodeRunner.fan
@@ -297,7 +297,7 @@ class NodeRunner
 //////////////////////////////////////////////////////////////////////////
 
   ** Copy all pod js files into /node_modules
-  ** Also copies in tz.js, units.js, and indexed-props.js
+  ** Also copies in mime.js, tz.js, units.js, and indexed-props.js
   private Void writeNodeModules()
   {
     moduleDir := nodeDir + `node_modules/`
@@ -316,11 +316,16 @@ class NodeRunner
     if (tempPod != null)
       (moduleDir + `${tempPod}.js`).out.writeChars(js).flush.close
 
+    // mime.js
+    out := (moduleDir + `mime.js`).out
+    JsExtToMime().write(out)
+    out.flush.close
+
     // tz.js
     (Env.cur.homeDir + `etc/sys/tz.js`).copyTo(moduleDir + `tz.js`, copyOpts)
 
     // units.js
-    out := (moduleDir + `units.js`).out
+    out = (moduleDir + `units.js`).out
     JsUnitDatabase().write(out)
     out.flush.close
 
@@ -338,6 +343,7 @@ class NodeRunner
       if ("sys" == pod.name)
       {
         buf.add("var fan = require('${pod.name}.js');\n")
+        buf.add("require('mime.js');\n")
         buf.add("require('tz.js');\n")
         buf.add("require('units.js');\n")
         buf.add("require('indexed-props.js');\n")
diff --git a/com.xored.f4.fantom/fantom/src/compilerJs/fan/util/JsExtToMime.fan b/com.xored.f4.fantom/fantom/src/compilerJs/fan/util/JsExtToMime.fan
new file mode 100644
index 00000000..571064cb
--- /dev/null
+++ b/com.xored.f4.fantom/fantom/src/compilerJs/fan/util/JsExtToMime.fan
@@ -0,0 +1,30 @@
+//
+// Copyright (c) 2020, Brian Frank and Andy Frank
+// Licensed under the Academic Free License version 3.0
+//
+// History:
+//   11 Sep 20  Matthew Giannini  Creation
+//
+
+**
+** JsExtToMime
+**
+class JsExtToMime
+{
+  Void write(OutStream out)
+  {
+    props := Env.cur.findFile(`etc/sys/ext2mime.props`).readProps
+    out.printLine(
+      "(function() {
+        ${JsPod.requireSys}
+        var c=fan.sys.MimeType.cache\$;
+        ")
+
+    props.each |mime, ext|
+    {
+      out.printLine("c($ext.toCode,$mime.toCode);")
+    }
+
+    out.printLine("}).call(this);")
+  }
+}
diff --git a/com.xored.f4.fantom/fantom/src/compilerJs/fan/util/JsIndexedProps.fan b/com.xored.f4.fantom/fantom/src/compilerJs/fan/util/JsIndexedProps.fan
index 70761a55..30ffa61f 100755
--- a/com.xored.f4.fantom/fantom/src/compilerJs/fan/util/JsIndexedProps.fan
+++ b/com.xored.f4.fantom/fantom/src/compilerJs/fan/util/JsIndexedProps.fan
@@ -35,8 +35,10 @@ class JsIndexedProps
          ${JsPod.requireSys}
          var i = fan.sys.Map.make(fan.sys.Str.\$type, new fan.sys.ListType(fan.sys.Str.\$type));")
 
-    index.each |vals, key|
+    keys := index.keys.sort
+    keys.each |key|
     {
+      vals := index[key].sort
       v := vals.join(",") |v| { v.toCode }
       out.printLine("  i.set(\"$key\", fan.sys.List.make(fan.sys.Str.\$type, [$v]));")
     }
diff --git a/com.xored.f4.fantom/fantom/src/compilerJs/fan/util/SourceMap.fan b/com.xored.f4.fantom/fantom/src/compilerJs/fan/util/SourceMap.fan
index 481a44e8..047cf51b 100644
--- a/com.xored.f4.fantom/fantom/src/compilerJs/fan/util/SourceMap.fan
+++ b/com.xored.f4.fantom/fantom/src/compilerJs/fan/util/SourceMap.fan
@@ -45,12 +45,13 @@ class SourceMap
 // Output
 //////////////////////////////////////////////////////////////////////////
 
-  Void write(OutStream out := Env.cur.out)
+  Void write(Int lineCount, OutStream out := Env.cur.out)
   {
     pod := support.pod.name
     out.writeChars("{\n")
     out.writeChars("\"version\": 3,\n")
     out.writeChars("\"file\": \"${pod}.js\",\n")
+    out.writeChars("\"x_fan_linecount\": $lineCount,\n")
     out.writeChars("\"sourceRoot\": \"/dev/${pod}/\",\n")
     writeSources(out)
     writeMappings(out)
@@ -69,7 +70,6 @@ class SourceMap
       else out.writeChars("\"${file.name}\"")
     }
     out.writeChars("],\n")
-
   }
 
   private Void writeMappings(OutStream out)
@@ -134,6 +134,141 @@ class SourceMap
     out.writeChars(";\"\n")
   }
 
+//////////////////////////////////////////////////////////////////////////
+// Pack
+//////////////////////////////////////////////////////////////////////////
+
+  ** Compile a list of pod JavaScript files into a single unified source
+  ** map file.  The list of files passed to this method should match
+  ** exactly the list of files used to create the corresponding JavaScript
+  ** FilePack.  If the file is the standard pod JS file, then we will include
+  ** an offset version of "{pod}.js.map" generated by the JavaScript compiler.
+  ** Otherwise if the file is another JavaScript file (such as units.js) then
+  ** we just add the appropiate offset.
+  **
+  ** The 'sourceRoot' option may be passed in to replace "/dev/{podName}"
+  ** as the root URI used to fetch source files from the server.
+  static Void pack(File[] files, OutStream out, [Str:Obj]? options := null)
+  {
+    // options
+    sourceRoot := options?.get("sourceRoot") as Str
+
+    // open compound source map file
+    out.printLine("{")
+       .printLine("\"version\": 3,")
+       .printLine("\"sections\": [")
+
+    // process each file
+    curOffset := 0
+    files.each |file, i|
+    {
+      // check if file is within a pod
+      uri := file.uri
+      pod := uri.scheme == "fan" ? Pod.find(uri.host, false) : null
+
+      // check if this standard pod JS file
+      Str? json := null
+      if (pod != null && isPodJsFile(file))
+      {
+        // lookup sourcemap for the pod
+        sm := pod.file(`/${pod.name}.js.map`, false)
+        if (sm != null)
+        {
+          // read into memory
+          json = sm.readAllStr
+
+          // apply options
+          if (sourceRoot != null) json = setSourceRoot(json, sourceRoot+pod.name)
+
+        }
+      }
+
+      // read number of lines from JSON if we can, otherwise count them
+      // echo("-- $uri.name  " + readNumLinesFromJson(json) + " ?= " + readNumLinesByCounting(file))
+      numLines := readNumLinesFromJson(json) ?: readNumLinesByCounting(file)
+
+      // if we have raw js file, then generate a synthetic sourcemap
+      if (json == null)
+      {
+        mappings := StrBuf().add("AAAA;")
+        buf := StrBuf()
+        buf.add("""{
+                   "version":3,
+                   "file": "core.js",
+                   "sources": ["${file.name}"],
+                   """)
+        if (pod != null) buf.add("\"sourceRoot\": \"").add(sourceRoot ?: "/dev/").add(pod.name).add("/\",\n")
+        buf.add(Str<|"mappings": "AAAA;|>)
+        (numLines+1).times |x| { buf.add("AACA;") }
+        buf.add("\"}")
+        json = buf.toStr
+      }
+
+      // add offset section and insert original JSON
+      out.print(Str<|{"offset": {"line":|>)
+         .print(curOffset)
+         .print(Str<|, "column":0}, "map":|>)
+         .print(json)
+         .print("}")
+      if (i+1 < files.size) out.printLine(",")  // cannot have trailing comma
+
+
+      // advance curOffset
+      curOffset += numLines
+    }
+
+    // close file
+    out.printLine("]")
+       .printLine("}")
+  }
+
+  ** Return if the file is the standard compilerJs pod transpiled source
+  private static Bool isPodJsFile(File f)
+  {
+    f.uri.scheme == "fan" && f.uri.pathStr == "/${f.uri.host}.js"
+  }
+
+  ** Try to parse "x_fan_linecount" key from JSON contents
+  private static Int? readNumLinesFromJson(Str? json)
+  {
+    r := findKeyValRange(json, Str<|"x_fan_linecount":|>)
+    if (r == null) return null
+    return json.getRange(r).toInt(10, false)
+  }
+
+  ** Fallback is to read each line to determine line count
+  private static Int readNumLinesByCounting(File file)
+  {
+    num := 0
+    file.eachLine { ++num }
+    return num
+  }
+
+  ** Set source root option
+  private static Str setSourceRoot(Str json, Str sourceRoot)
+  {
+    r := findKeyValRange(json, Str<|"sourceRoot":|>)
+    if (r == null) return json
+    return json[0..= maxTicks) break;
+      }
     }
 
-    // flush locals back to context
+    // keep track of time between start and now; for efficiency we only
+    // update this after a work cycle has ended - but this means its
+    // possible to be stuck continously processing the queue if never have
+    // to yield our thread, in which case we won't see this stat updated
+    receiveTicks += Duration.nowTicks() - startTicks;
+
+    // flush environment back to context
     context.locale = Locale.cur();
 
     // done dispatching, either clear the submitted
@@ -225,12 +264,13 @@ public final void _work()
 
   }
 
-  final void _dispatch(Future future)
+  final void _dispatch(ActorFuture future)
   {
     try
     {
       if (future.isCancelled()) return;
       if (pool.killed) { future.cancel(); return; }
+      receiveCount++;
       future.complete(receive(future.msg));
     }
     catch (Err e)
@@ -264,26 +304,8 @@ public void _kill()
 
   static Object _safe(Object obj)
   {
-    if (obj == null) return null;
-    if (FanObj.isImmutable(obj)) return obj;
-
-    if (_safeLogErr)
-    {
-      _safeLogErr = false;
-      System.out.println("==");
-      System.out.println("==");
-      System.out.println("== Actor msg serialization is deprecated;");
-      System.out.println("== See http://fantom.org/forum/topic/2428");
-      System.out.println("==");
-      System.out.println("==");
-    }
-
-    Buf buf = new MemBuf(512);
-    buf.out().writeObj(obj);
-    buf.flip();
-    return buf.in().readObj();
+    return FanObj.toImmutable(obj);
   }
-  private static volatile boolean _safeLogErr = true;
 
 //////////////////////////////////////////////////////////////////////////
 // Queue
@@ -291,10 +313,10 @@ static Object _safe(Object obj)
 
   static class Queue
   {
-    public Future get()
+    public ActorFuture get()
     {
       if (head == null) return null;
-      Future f = head;
+      ActorFuture f = head;
       head = f.next;
       if (head == null) tail = null;
       f.next = null;
@@ -302,7 +324,7 @@ public Future get()
       return f;
     }
 
-    public void add(Future f)
+    public void add(ActorFuture f)
     {
       if (tail == null) { head = tail = f; f.next = null; }
       else { tail.next = f; tail = f; }
@@ -310,7 +332,7 @@ public void add(Future f)
       if (size > peak) peak = size;
     }
 
-    public Future coalesce(Future f)
+    public ActorFuture coalesce(ActorFuture f)
     {
       return null;
     }
@@ -319,7 +341,7 @@ void dump(fan.sys.OutStream out)
     {
       int num = 0;
       int max = 50;
-      for (Future x = head; x != null; x = x.next)
+      for (ActorFuture x = head; x != null; x = x.next)
       {
         if (num < max) out.print("  ").printLine(x.msg);
         num++;
@@ -327,7 +349,7 @@ void dump(fan.sys.OutStream out)
       if (num > max) out.print("  " + (num-max) + " more messages...");
     }
 
-    Future head, tail;
+    ActorFuture head, tail;
     int size;
     int peak;
   }
@@ -344,9 +366,9 @@ static class CoalescingQueue extends Queue
       this.coalesceFunc = coalesceFunc;
     }
 
-    public Future get()
+    public ActorFuture get()
     {
-      Future f = super.get();
+      ActorFuture f = super.get();
       if (f != null)
       {
         try
@@ -362,7 +384,7 @@ public Future get()
       return f;
     }
 
-    public void add(Future f)
+    public void add(ActorFuture f)
     {
       try
       {
@@ -376,12 +398,12 @@ public void add(Future f)
       super.add(f);
     }
 
-    public Future coalesce(Future incoming)
+    public ActorFuture coalesce(ActorFuture incoming)
     {
       Object key = toKey(incoming.msg);
       if (key == null) return null;
 
-      Future orig = (Future)pending.get(key);
+      ActorFuture orig = (ActorFuture)pending.get(key);
       if (orig == null) return null;
 
       orig.msg = coalesce(orig.msg, incoming.msg);
@@ -419,14 +441,18 @@ public final Object dump(List args)
       out = (fan.sys.OutStream)args.get(0);
     try
     {
+      Duration ticksTotal = Duration.make(receiveTicks());
+      Duration ticksAvg = Duration.make(receiveCount <= 0 ? 0 : receiveTicks / receiveCount);
+
       out.printLine("Actor");
       out.printLine("  pool:      " + pool.name);
-      out.printLine("  submitted: " + submitted);
-      out.printLine("  queue:     " + queueSize());
-      out.printLine("  peak:      " + queuePeak());
-      out.printLine("  curMsg:    " + curMsg);
+      out.printLine("  state:     " + threadState());
+      out.printLine("  queue:     " + queueSize() + " (peak " + queuePeak() + ")");
+      out.printLine("  received:  " + receiveCount());
+      out.printLine("  ticks:     " + ticksTotal.toLocale() + " (avg " + ticksAvg.toLocale() + ")");
+      if (curMsg != idleMsg)
+        out.printLine("  curMsg:    " + curMsg);
       queue.dump(out);
-
     }
     catch (Exception e) { out.printLine("  " + e + "\n"); }
     return out;
@@ -448,12 +474,15 @@ static final class Context
 // Fields
 //////////////////////////////////////////////////////////////////////////
 
+  static final String idleMsg = new String("_idle_");
+
   final Context context;                 // mutable world state of actor
   private ActorPool pool;                // pooled controller
   private Func receive;                  // func to invoke on receive or null
   private Object lock = new Object();    // lock for message queue
   private Queue queue;                   // message queue linked list
-  private Object curMsg;                 // if currently processing a message
+  private Object curMsg = idleMsg;       // if currently processing a message
   private boolean submitted = false;     // is actor submitted to thread pool
-
+  private int receiveCount;              // total number of messages received
+  private long receiveTicks;             // total ticks spend in receive
 }
\ No newline at end of file
diff --git a/com.xored.f4.fantom/fantom/src/concurrent/java/ActorFuture.java b/com.xored.f4.fantom/fantom/src/concurrent/java/ActorFuture.java
new file mode 100644
index 00000000..66da57b4
--- /dev/null
+++ b/com.xored.f4.fantom/fantom/src/concurrent/java/ActorFuture.java
@@ -0,0 +1,274 @@
+//
+// Copyright (c) 2009, Brian Frank and Andy Frank
+// Licensed under the Academic Free License version 3.0
+//
+// History:
+//   26 Mar 09  Brian Frank  Creation
+//   24 Apr 20  Brian Frank  Make Future abstract
+//
+package fan.concurrent;
+
+import fan.sys.*;
+import java.util.concurrent.TimeUnit;
+import java.util.ArrayList;
+
+/**
+ * ActorFuture is future implementation used by actor framework
+ */
+public final class ActorFuture
+  extends Future
+{
+
+//////////////////////////////////////////////////////////////////////////
+// Construction
+//////////////////////////////////////////////////////////////////////////
+
+  public static ActorFuture make()
+  {
+    return new ActorFuture(null);
+  }
+
+  ActorFuture(Object msg)
+  {
+    this.msg   = msg;
+    this.state = PENDING;
+  }
+
+//////////////////////////////////////////////////////////////////////////
+// Obj
+//////////////////////////////////////////////////////////////////////////
+
+  public Type typeof()
+  {
+    if (type == null) type = Type.find("concurrent::ActorFuture");
+    return type;
+  }
+  private static Type type;
+
+//////////////////////////////////////////////////////////////////////////
+// Future
+//////////////////////////////////////////////////////////////////////////
+
+  public final FutureState state()
+  {
+    int state = this.state;
+    switch(state)
+    {
+      case PENDING:     return FutureState.pending;
+      case DONE_OK:     return FutureState.ok;
+      case DONE_ERR:    return FutureState.err;
+      case DONE_CANCEL: return FutureState.cancelled;
+    }
+    throw Err.make("Internal error " + state);
+  }
+
+  public final Object get() { return get(null); }
+  public final Object get(Duration timeout)
+  {
+    Object r = null;
+    try
+    {
+      synchronized (this)
+      {
+        // wait until we enter a done state, the only notifies
+        // on this object should be from cancel, complete, or completeErr
+        if (timeout == null)
+        {
+          // wait forever until done
+          while ((state & DONE) == 0) wait();
+        }
+        else
+        {
+          // if not done, then wait with timeout and then
+          // if still not done throw a timeout exception
+          if ((state & DONE) == 0)
+          {
+            // compute deadline in millis
+            long deadline = Duration.nowMillis() + timeout.millis();
+
+            // loop until we are done or our deadline has passed
+            while ((state & DONE) == 0)
+            {
+              long left = deadline - Duration.nowMillis();
+              if (left <= 0L) break;
+              wait(left);
+            }
+
+            // if we still aren't done this is a timeout
+            if ((state & DONE) == 0) throw TimeoutErr.make("Future.get timed out");
+          }
+        }
+
+        // if canceled throw CancelErr
+        if (state == DONE_CANCEL)
+          throw CancelledErr.make("Future cancelled");
+
+        // if error was raised, raise it to caller
+        if (state == DONE_ERR)
+          throw ((Err)result).rebase();
+
+        // assign result to local variable for return
+        r = result;
+      }
+    }
+    catch (InterruptedException e)
+    {
+      throw InterruptedErr.make(e);
+    }
+
+    // ensure immutable or safe copy
+    return Actor._safe(r);
+  }
+
+  public final Future waitFor() { return waitFor(null); }
+  public final Future waitFor(Duration timeout)
+  {
+    try
+    {
+      synchronized (this)
+      {
+        // wait until we enter a done state, the only notifies
+        // on this object should be from cancel, complete, or completeErr
+        if (timeout == null)
+        {
+          // wait forever until done
+          while ((state & DONE) == 0) wait();
+        }
+        else
+        {
+          // if not done, then wait with timeout and then
+          // if still not done throw a timeout exception
+          if ((state & DONE) == 0)
+          {
+            // compute deadline in millis
+            long deadline = Duration.nowMillis() + timeout.millis();
+
+            // loop until we are done or our deadline has passed
+            while ((state & DONE) == 0)
+            {
+              long left = deadline - Duration.nowMillis();
+              if (left <= 0L) break;
+              wait(left);
+            }
+
+            // if we still aren't done this is a timeout
+            if ((state & DONE) == 0) throw TimeoutErr.make("Future.get timed out");
+          }
+        }
+        return this;
+      }
+    }
+    catch (InterruptedException e)
+    {
+      throw InterruptedErr.make(e);
+    }
+  }
+
+  public final void cancel()
+  {
+    ArrayList wd;
+    synchronized (this)
+    {
+      if ((state & DONE) == 0) state = DONE_CANCEL;
+      msg = result = null;  // allow gc
+      notifyAll();
+      wd = whenDone; whenDone = null;
+    }
+    sendWhenDone(wd);
+  }
+
+  public final Future complete(Object r)
+  {
+    r = Actor._safe(r);
+    ArrayList wd;
+    synchronized (this)
+    {
+      if (state == DONE_CANCEL) return this;
+      if (state != PENDING) throw Err.make("Future already complete");
+      state = DONE_OK;
+      result = r;
+      notifyAll();
+      wd = whenDone; whenDone = null;
+    }
+    sendWhenDone(wd);
+    return this;
+  }
+
+  public final Future completeErr(Err e)
+  {
+    ArrayList wd;
+    synchronized (this)
+    {
+      if (state == DONE_CANCEL) return this;
+      if (state != PENDING) throw Err.make("Future already complete");
+      state = DONE_ERR;
+      result = e;
+      notifyAll();
+      wd = whenDone; whenDone = null;
+    }
+    sendWhenDone(wd);
+    return this;
+  }
+
+//////////////////////////////////////////////////////////////////////////
+// When Done
+//////////////////////////////////////////////////////////////////////////
+
+  final void sendWhenDone(Actor a, ActorFuture f)
+  {
+    // if already done, then set immediate flag
+    // otherwise add to our when done list
+    boolean immediate = false;
+    synchronized (this)
+    {
+      if (isDone()) immediate = true;
+      else
+      {
+        if (whenDone == null) whenDone = new ArrayList();
+        whenDone.add(new WhenDone(a, f));
+      }
+    }
+
+    // if immediate we are already done so enqueue immediately
+    if (immediate)
+    {
+      try { a._enqueue(f, false); }
+      catch (Throwable e) { e.printStackTrace(); }
+    }
+  }
+
+  static void sendWhenDone(ArrayList list)
+  {
+    if (list == null) return;
+    for (int i=0; ifan -version
   Fantom Launcher
-  Copyright (c) 2006-2013, Brian Frank and Andy Frank
+  Copyright (c) 2006-2020, Brian Frank and Andy Frank
   Licensed under the Academic Free License version 3.0
 
   Java Runtime:
-    java.version:    1.7.0_02
-    java.vm.name:    Java HotSpot(TM) Client VM
+    java.version:    1.8.0_91
+    java.vm.name:    Java HotSpot(TM) 64-Bit Server VM
     java.vm.vendor:  Oracle Corporation
-    java.vm.version: 22.0-b10
-    java.home:       C:\Program Files (x86)\Java\jre7
-    fan.platform:    win32-x86
-    fan.version:     1.0.62
+    java.vm.version: 25.91-b14
+    java.home:       /Library/Java/JavaVirtualMachines/jdk1.8.0_91.jdk/Contents/Home/jre
+    fan.platform:    macosx-x86_64
+    fan.version:     1.0.74
     fan.env:         sys::BootEnv
-    fan.home:        C:\dev\fan
+    fan.home:        /work/fan
 
 If that doesn't work then try these options:
   1. Review [setup]`docTools::Setup` instructions
-  2. Install Java 1.5 or greater and retry
+  2. Install Java 8 or greater and retry
   3. Explicitly [configure]`docTools::Setup#javaRuntime` where
      your JVM is installed
-  4. Turn on [launcher debugging]`docTools::Setup#debugging`
 
 Fantom Shell [#fansh]
 *********************
@@ -48,7 +47,7 @@ shell run the 'fansh' executable and call the [Obj.echo]`sys::Obj.echo`
 method:
 
   C:\dev\fan\bin>fansh
-  Fantom Shell v1.0.22 ('?' for help)
+  Fantom Shell v1.0.74 ('?' for help)
   fansh> echo("hello world #1")
   hello world #1
   fansh> quit
@@ -218,4 +217,4 @@ If you run this script:
   [09:57:40 11-Apr-08] [info] [fand] booting...
   [09:57:40 11-Apr-08] [info] [web] WispService started on port 8080
 
-You should be able to hit `http://localhost:8080/` with your browser!
+You should be able to hit `http://localhost:8080/` with your browser!
\ No newline at end of file
diff --git a/com.xored.f4.fantom/fantom/src/doc/docIntro/doc/Readme.fandoc b/com.xored.f4.fantom/fantom/src/doc/docIntro/doc/Readme.fandoc
index c6bee79b..52eb6a84 100755
--- a/com.xored.f4.fantom/fantom/src/doc/docIntro/doc/Readme.fandoc
+++ b/com.xored.f4.fantom/fantom/src/doc/docIntro/doc/Readme.fandoc
@@ -8,10 +8,10 @@
 
 Fantom Quick Links [#quickLinks]
 ********************************
-- [Setup]`http://fantom.org/doc/docTools/Setup`
-- [Getting Started]`http://fantom.org/doc/docIntro/StartHere`
-- [Hello World]`http://fantom.org/doc/docIntro/HelloWorld`
-- [Examples]`http://fantom.org/doc/examples/index.html`
+- [Setup]`https://fantom.org/doc/docTools/Setup`
+- [Getting Started]`https://fantom.org/doc/docIntro/StartHere`
+- [Hello World]`https://fantom.org/doc/docIntro/HelloWorld`
+- [Examples]`https://fantom.org/doc/examples/index.html`
 
 Setup [#Setup]
 **************
@@ -21,21 +21,21 @@ Setup [#Setup]
    - Windows: "bin/fan.exe" executable
 3. Verify setup "fan -version"
 
-For further information see [docTools::Setup]`http://fantom.org/doc/docTools/Setup`
+For further information see [docTools::Setup]`https://fantom.org/doc/docTools/Setup`
 
 Tools [#tools]
 **************
 Common tools you will use:
-  - [fan]`http://fantom.org/doc/docTools/Fan`: Fantom launcher for scripts and pods
-  - [fansh]`http://fantom.org/doc/docTools/Fansh`: Fantom interactive shell
-  - [fant]`http://fantom.org/doc/docTools/Fant`: Fantom test runner
-  - [IDEs]`http://fantom.org/doc/docTools/IDEs`: different IDE and editor options
+  - [fan]`https://fantom.org/doc/docTools/Fan`: Fantom launcher for scripts and pods
+  - [fansh]`https://fantom.org/doc/docTools/Fansh`: Fantom interactive shell
+  - [fant]`https://fantom.org/doc/docTools/Fant`: Fantom test runner
+  - [IDEs]`https://fantom.org/doc/docTools/IDEs`: different IDE and editor options
 
-For further information see [docTools]`http://fantom.org/doc/docTools/index`
+For further information see [docTools]`https://fantom.org/doc/docTools/index`
 
 Docs [#docs]
 ************
-The documentation maybe found online at `http://fantom.org/doc/`.
+The documentation maybe found online at `https://fantom.org/doc/`.
 
 You may also build the HTML docs in your local environment using the command:
 
@@ -46,8 +46,8 @@ working environment.
 
 Community [#community]
 **********************
-- Forum: `http://fantom.org/sidewalk/topic/`
-- Latest Download: `http://fantom.org/`
-- Mecurial Repo: `https://bitbucket.org/fantom/fan-1.0/`
+- Forum: `https://fantom.org/sidewalk/topic/`
+- Latest Download: `https://fantom.org/`
+- Git Repo: `https://github.com/fantom-lang/fantom`
 - IRC: `http://webchat.freenode.net/?channels=#fantom`
 
diff --git a/com.xored.f4.fantom/fantom/src/doc/docIntro/doc/Roadmap.fandoc b/com.xored.f4.fantom/fantom/src/doc/docIntro/doc/Roadmap.fandoc
deleted file mode 100755
index 9647f2f1..00000000
--- a/com.xored.f4.fantom/fantom/src/doc/docIntro/doc/Roadmap.fandoc
+++ /dev/null
@@ -1,50 +0,0 @@
-**************************************************************************
-** title:      Roadmap
-** author:     Brian Frank
-** created:    9 Jan 08
-** copyright:  Copyright (c) 2008, Brian Frank and Andy Frank
-** license:    Licensed under the Academic Free License version 3.0
-**************************************************************************
-
-Current Status [#status]
-************************
-Fantom is currently in development of its initial 1.0 release.  The core
-language and Java runtime are solid and used in production environments
-today. JavaScript support is beta quality - we use it oursevles in our
-commerical product, but it isn't quite complete.  The .NET runtime is
-best classified as a prototype, it is not recommended for production yet.
-
-Stability [#stability]
-**********************
-The functionality in each build should be stable.  We are very committed
-to fixing bugs and getting existing code solid before moving on to new
-features.  However Fantom is still new, and like all software platforms it
-still needs to be wrung out in lots of different applications.
-
-We can't promise that we won't make breaking language or API changes.  We
-need the freedom to change things as we learn what does and does not work.
-However in practice, we've made very few breaking changes over the past
-year.
-
-Future Work [#futureWork]
-***************************
-Random list of ideas for future work we have on our radar, or that
-we would like to see receive community attention:
-
-IDE Support [#ideSupport]
-=========================
-By far and awaw the biggest requirement for a successful ecosystem is
-awesome tooling.  The community is doing a great job addressing IDE
-tooling, and if you are interested please get involved!
-
-Crypto [#crypto]
-================
-We need standardized APIs for cryptographic operations and algorithms. This
-work will include support for the SSL protocol.
-
-.NET and Alternate Runtimes [#dotnet]
-=====================================
-The focus of the core Fantom team is Java and JavaScript support.  If you
-are interested in the .NET or other runtimes please get involved!
-
-
diff --git a/com.xored.f4.fantom/fantom/src/doc/docIntro/doc/StartHere.fandoc b/com.xored.f4.fantom/fantom/src/doc/docIntro/doc/StartHere.fandoc
index dd5a5fb3..5b88c6bd 100755
--- a/com.xored.f4.fantom/fantom/src/doc/docIntro/doc/StartHere.fandoc
+++ b/com.xored.f4.fantom/fantom/src/doc/docIntro/doc/StartHere.fandoc
@@ -10,7 +10,7 @@ Learn More [#learnMore]
 ***********************
 Try these links to learn more about Fantom:
   - `Tour`: quick introduction to Fantom's features by code example
-  - `WhyFantom`: why we're crazy enough to build Fantom
+  - `WhyFantom`: why we built Fantom
   - Hello world five different ways:
       - [Fantom shell]`HelloWorld#fansh`: interactive shell
       - [Fantom script]`HelloWorld#script`: executable scripts
@@ -22,7 +22,7 @@ Quick Start [#quickStart]
 *************************
 If you want to jump right in and start coding:
 
-  - [Download]`http://fantom.org` the latest build
+  - [Download]`https://fantom.org` the latest build
   - [Setup]`docTools::Setup`: your environment
   - `HelloWorld`: try your hand at hello world
   - [Examples]`examples::index`: snippets of example
@@ -33,9 +33,6 @@ Community Resources [#communityResources]
   - [Rosetta Code]`http://rosettacode.org/wiki/Category:Fantom`: code
     samples in Fantom with comparisons to other languages
 
-  - [langref.org]`http://langref.org/fantom/solved`: lots of code samples for
-    different problem sets
-
   - [Java to Fantom in 10 Steps]`http://www.fantomfactory.org/articles/from-java-to-fantom-in-10-steps`
 
   - [Fantom Cheat Sheet]`http://wiki.colar.net/fan_cheat_sheet`
@@ -72,14 +69,9 @@ make sure to review how to write [unit tests]`docTools::Fant` and
 Getting Involved [#gettingInvolved]
 ***********************************
 The place to ask questions and submit feedback is the
-[discussion group]`http://fantom.org/sidewalk/topic/`.
+[discussion group]`https://fantom.org/forum/topic/`.
 Or you can [contact us]`Faq#contact` directly.  Let us know
 what you think!
 
-If you are itching to contribute code, please take a look at the
-[open tickets]`http://fantom.org/sidewalk/ticket/?state=open`
-or [roadmap]`Roadmap#futureWork` to find what might interest
-you.
-
 
 
diff --git a/com.xored.f4.fantom/fantom/src/doc/docIntro/doc/WhyFantom.fandoc b/com.xored.f4.fantom/fantom/src/doc/docIntro/doc/WhyFantom.fandoc
index 7fdea7f0..eb160e2e 100755
--- a/com.xored.f4.fantom/fantom/src/doc/docIntro/doc/WhyFantom.fandoc
+++ b/com.xored.f4.fantom/fantom/src/doc/docIntro/doc/WhyFantom.fandoc
@@ -8,43 +8,32 @@
 
 Overview [#overview]
 ********************
-Do we really need another programming language?  Well obviously we thought
-so, or we wouldn't have built Fantom!  Fantom is designed as a practical programming
-language to make it easy and fun to get real work done.  It is not an
-academic language to explore bleeding edge theories, but based on solid real
-world experience.  During its design we set out to solve what we perceived
-were some real problems with Java and C#.  Our background is heavily Java, but
-many of Java's problems are shared by C# and .NET also.
+Fantom's raison d'être is to write portable software that runs on the Java
+VM server side and transpiles to JavaScript for web based front ends.
+Fantom was initially developed back in 2005 during a period of stagnation in
+the Java programming language.  It was also well before any statically
+typed options were available to transpile to JavaScript.
 
 Portability [#portability]
 **************************
-The primary reason we created Fantom is to write software that can seamlessly
-run on both the Java VM and the .NET CLR.  The reality is that many software
-organizations are committed to one or the other of these platforms.  Even
-dynamic languages like Python and Ruby are getting hosted on one of these VMs.
-Whether your business is in-house software or building software components to
-sell to other companies, you tend to pick one camp or the other.
-
-We built Fantom from the ground up to tackle portability between these VMs.
-Fantom's source language compiles into fcode - a bytecode representation that
-can be translated into both Java bytecode and IL easily.  This translation
-is typically done at runtime, which enables you to deploy Fantom modules as
-a single file and have them run on either VM.
-
-But getting a language to run on both Java and .NET is the easy part - in
-fact there are many solutions to this problem.  The hard part is getting
-portable APIs.  Fantom provides a set of APIs which abstract away the Java
-and .NET APIs.  We actually consider this one of Fantom's primary benefits,
-because it gives us a chance to develop a suite of system APIs that are
-elegant and easy to use compared to the Java and .NET counter parts.
-
-But portability means much more than just Java or .NET.  We also support
-compiling Fantom to JavaScript for use in browsers, including support for
-many of the standard libraries.
+The original mission for Fantom was to write software to seamlessly run on
+both the Java VM and the .NET CLR.  But over time, we shifted our focus
+to also target JavaScript to run Fantom in web browsers.  The .NET runtime
+is not actively developed anymore, but is complete enough to run the core
+stack.  Today our focus is exclusively on supporting the Java VM and
+transpiling to JavaScript for web applications.
+
+Fantom is unique in that is primary goal was to build a langauge portable
+to multiple runtimes.  Because of this mission, Fantom was built with
+a clean set of APIs to abstract away lower level Java, C#, and JavaScript
+APIs.  We actually consider this one of Fantom's primary benefits, because
+it gave us a chance to develop a suite of system APIs that are elegant
+and easy to use compared some of their lower level counter parts.
 
 Because Fantom is designed from the ground up to be portable, targeting
-new platforms is reasonably easy.  Future targets might include Objective-C
-for the iPhone, the [LLVM]`http://llvm.org/`, or [Parrot]`http://www.parrotcode.org/`.
+new platforms should be reasonably easy.  Future targets might include
+Objective-C or Swift for the iPhone, the [LLVM]`http://llvm.org/`, or
+[Parrot]`http://www.parrotcode.org/`.
 
 Elegant APIs [#elegantApis]
 ***************************
@@ -105,7 +94,7 @@ strongly typed, the Java and C# languages are moving to be more strongly
 typed.  Generic types illustrate this trend - a feature added to
 both Java and C# in the not so distant past.  A fully parameterized
 type system introduces a great deal of complexity - we are
-trying hard to find the [right balance]`http://fantom.org/sidewalk/topic/1433`
+trying hard to find the [right balance]`https://fantom.org/forum/topic/1433`
 between value and complexity.
 
 Currently Fantom takes a limited approach to generics.  There is no
@@ -116,35 +105,6 @@ array type syntax of Java and C#.  This trade-off seems to hit the
 sweet spot where generics make sense without complicating the overall
 type system.
 
-Mixins [#mixins]
-****************
-Building software is often a modeling problem - figuring out how
-to map a domain model into code.  In an object oriented language,
-this typically means modeling via classes and interfaces.  Both
-Java and C# use a similar approach: classes support single inheritance
-of both type and implementation.  Interfaces support multiple inheritance
-of type, but do not support inheritance of implementation.
-
-Anyone who has worked in Java or C# knows that choosing between a
-class or an interface is often a decision that haunts you.  Because
-once you choose a class you've burned your only chance for implementation
-inheritance.  If you have a complicated domain model, then interfaces
-become a necessary burden - but often end up resulting in a lot
-of busy work if you need them to have common implementation code.
-Interfaces are also fraught with peril when it comes to versioning
-because you can't add a method without breaking all of the
-implementing code.
-
-There are plenty of good reasons why Java and C# ended up using
-the class/interface model.  Multiple inheritance offers lots of
-power, but comes at the expense of complexity and some pretty nasty
-pitfalls.  Fantom takes a middle of the road approach called mixins.
-Mixins are essentially Java or C# interfaces that can have method
-implementations.  To avoid some of the pitfalls of true
-multiple inheritance, mixins restrict features such as fields
-which store state.  Mixins are a very nice feature in the Fantom
-toolbox when it comes to designing your object oriented models.
-
 Modularity [#modularity]
 ************************
 Designing software to be modular is one of those things you learn
@@ -153,16 +113,17 @@ should let you easily divide your programs up into reusable chunks
 which are easy to version, ship around, and combine with other
 modules via clear dependencies.
 
-What passes for module management in Java is the JAR file - which
-is basically to say Java really doesn't have any module management.
-There is a new JSR which might solve this problem, but for the last
-decade we've lived with classpath hell.  Java also suffers from some
-misguided marketing decisions which have resulted in a monolith
-J2SE that as of 1.6 weighs in at 44MB.  The process to subset this
-monolithic monstrosity into J2ME moves at a glacial pace.  Considering
-the brilliance in much of the core original Java technology, it
-is hard to understand why something as fundamental as modularity is
-lacking.
+What passed for module management in Java for decades is the JAR file - which
+is basically to say Java really didn't have any module management.  Project
+Jigsaw was was finally released in Java 9, but it has an uphill battle
+to conquer the inertia from so many years without a proper module
+system.  And its unnecessarily complex to compensate for overlaying
+modules over a system orginally designed without modules in mind.
+
+JavaScript is also a poster child for a language designed from scratch
+without built-in module support.  Like Java, adding modules to JavaScript
+was done as an afterthought and it didn't happen until Fantom had been
+around for many years.
 
 The .NET design was pretty serious about modularity, and at the
 high level it has a great design for versioning, GAC, etc.  However
@@ -222,12 +183,10 @@ automatically implements boxing and unboxing when necessary.
 
 Functional Programming [#functional]
 ************************************
-Both Java and .NET originally provided little support for functional
-programming.  At least .NET provided delegates, but functions in
-Java were limited to interfaces and quasi-closure support via inner
-classes.  Both languages seem to be moving towards true closure support
-and first class functions.  But they leave a huge legacy of APIs
-and code designed without functional programming in mind.
+When Fantom was originally developed, Java did not provide much in the
+way for functional programming.  Java 8 did add many features to support
+functional programming, but functions are still are not truly first class
+citizens in the type system.
 
 Fantom was designed from the ground up to support functions as first
 class objects.  Closures are a key feature of the language, and
@@ -273,8 +232,7 @@ frustrating about Java:
   - **Default parameters**: methods can have default arguments - no more
     writing boiler plate code for convenience methods
 
-  - **Type Inference**: local variables use type inference to avoid all
-    the noise that obscures typical Java code
+  - **Type Inference**: local variables use type inference
 
   - **Field Accessors**: field accessors are defined implicitly - another
     area where idiomatic Java code has a low signal to noise ratio
diff --git a/com.xored.f4.fantom/fantom/src/doc/docIntro/doc/index.fog b/com.xored.f4.fantom/fantom/src/doc/docIntro/doc/index.fog
index a0350f19..483ab602 100755
--- a/com.xored.f4.fantom/fantom/src/doc/docIntro/doc/index.fog
+++ b/com.xored.f4.fantom/fantom/src/doc/docIntro/doc/index.fog
@@ -12,7 +12,6 @@
   [`Tour`,       "whirlwind tour of Fantom's features"],
   [`WhyFantom`,  "why we built Fantom and what problems we desire to solve"],
   [`HelloWorld`, "getting started with hello world"],
-  [`Roadmap`,    "current development status and future plans"],
   [`Faq`,        "frequently asked questions"],
   [`License`,    "Academic Free License 3.0"],
   [`ChangeLog`,  "list of changes in each build"]
diff --git a/com.xored.f4.fantom/fantom/src/doc/docLang/build.fan b/com.xored.f4.fantom/fantom/src/doc/docLang/build.fan
index 4b0ed69d..d070cfe7 100755
--- a/com.xored.f4.fantom/fantom/src/doc/docLang/build.fan
+++ b/com.xored.f4.fantom/fantom/src/doc/docLang/build.fan
@@ -17,14 +17,14 @@ class Build : BuildPod
   new make()
   {
     podName = "docLang"
-    summary = "Language documentation"
+    summary = "Fantom language documentation"
     meta    = ["org.name":     "Fantom",
-               "org.uri":      "http://fantom.org/",
+               "org.uri":      "https://fantom.org/",
                "proj.name":    "Fantom Docs",
-               "proj.uri":     "http://fantom.org/",
+               "proj.uri":     "https://fantom.org/",
                "license.name": "Academic Free License 3.0",
-               "vcs.name":     "Mercurial",
-               "vcs.uri":      "https://bitbucket.org/fantom/fan-1.0/"]
+               "vcs.name":     "Git",
+               "vcs.uri":      "https://github.com/fantom-lang/fantom"]
     resDirs = [`doc/`]
   }
 }
\ No newline at end of file
diff --git a/com.xored.f4.fantom/fantom/src/doc/docLang/doc/Actors.fandoc b/com.xored.f4.fantom/fantom/src/doc/docLang/doc/Actors.fandoc
index 528b6e99..043c224d 100755
--- a/com.xored.f4.fantom/fantom/src/doc/docLang/doc/Actors.fandoc
+++ b/com.xored.f4.fantom/fantom/src/doc/docLang/doc/Actors.fandoc
@@ -99,14 +99,14 @@ Futures [#futures]
 ******************
 All three send methods return a [Future]`concurrent::Future` which may
 used to access the result of that message.  You can poll for the
-result using [isDone]`concurrent::Future.isDone` - a future
-enters the done state by one of three transitions:
+result using `concurrent::Future.state` - a future enters the complete
+state by one of three transitions:
   - The actor processes the message and returns a result
   - The actor raises an exception while processing the message
   - The future is cancelled (see [cancel]`concurrent::Future.cancel`)
 
-Once a future enters the done state, the result is available via
-the [get]`concurrent::Future.get` method.  If the future is not done,
+Once a future enters the complete state, the result is available via
+the [get]`concurrent::Future.get` method.  If the future is not complete,
 then calling 'get' will block the caller until the future becomes
 done.  A timeout may be used to block for a fixed period of time.
 Calling 'get' results in one of these outcomes:
@@ -166,22 +166,22 @@ the timer or poll/block until the message has been processed.
 
 Chaining [#chaining]
 ********************
-The [sendWhenDone]`concurrent::Actor.sendWhenDone` method is used to
+The [sendWhenComplete]`concurrent::Actor.sendWhenComplete` method is used to
 deliver a message once another message has completed processing.
-Using 'sendWhenDone' allows asynchronous message chaining.  Consider
+Using 'sendWhenComplete' allows asynchronous message chaining.  Consider
 this code:
 
   future := actor1.send(msg1)
-  actor2.sendWhenDone(future, msg2)
+  actor2.sendWhenComplete(future, msg2)
 
 In this example, 'msg2' is enqueued on 'actor2' only after
 'actor1' completes processing of 'msg1'.  Typically the future
 itself is passed as the message:
 
-  a.sendWhenDone(future, future)        // future is message itself
-  a.sendWhenDone(future, MyMsg(future)) // MyMsg references future
+  a.sendWhenComplete(future, future)        // future is message itself
+  a.sendWhenComplete(future, MyMsg(future)) // MyMsg references future
 
-Remember that 'sendWhenDone' is called no matter how the future
+Remember that 'sendWhenComplete' is called no matter how the future
 completes: successfully, with an error, or cancellation.
 
 Coalescing Messages [#coalescing]
diff --git a/com.xored.f4.fantom/fantom/src/doc/docLang/doc/Concurrency.fandoc b/com.xored.f4.fantom/fantom/src/doc/docLang/doc/Concurrency.fandoc
index da3a2ad1..4c7007b7 100755
--- a/com.xored.f4.fantom/fantom/src/doc/docLang/doc/Concurrency.fandoc
+++ b/com.xored.f4.fantom/fantom/src/doc/docLang/doc/Concurrency.fandoc
@@ -82,7 +82,7 @@ which will pass an object to another thread include:
 
   - [Actor.send]`concurrent::Actor.send`
   - [Actor.sendLater]`concurrent::Actor.sendLater`
-  - [Actor.sendWhenDone]`concurrent::Actor.sendWhenDone`
+  - [Actor.sendWhenComplete]`concurrent::Actor.sendWhenComplete`
   - [Future.get]`concurrent::Future.get`
 
 All of these APIs use the same pattern to safely pass an object
diff --git a/com.xored.f4.fantom/fantom/src/doc/docLang/doc/DateTime.fandoc b/com.xored.f4.fantom/fantom/src/doc/docLang/doc/DateTime.fandoc
index de973551..f29d28cf 100755
--- a/com.xored.f4.fantom/fantom/src/doc/docLang/doc/DateTime.fandoc
+++ b/com.xored.f4.fantom/fantom/src/doc/docLang/doc/DateTime.fandoc
@@ -18,7 +18,7 @@ The following sys types are used to represent and work with time:
   - `sys::Month`: enum for months of the year
   - `sys::Weekday`: enum for days of the week
 
-All these classes are immutable [const classes]`http://fantom.org/doc/docLang/Classes.html#const`.
+All these classes are immutable [const classes]`docLang::Classes#const`.
 
 Ticks [#ticks]
 **************
diff --git a/com.xored.f4.fantom/fantom/src/doc/docLang/doc/DotnetFFI.fandoc b/com.xored.f4.fantom/fantom/src/doc/docLang/doc/DotnetFFI.fandoc
deleted file mode 100755
index 17c6bbac..00000000
--- a/com.xored.f4.fantom/fantom/src/doc/docLang/doc/DotnetFFI.fandoc
+++ /dev/null
@@ -1,13 +0,0 @@
-**************************************************************************
-** title:      DotnetFFI
-** author:     Brian Frank
-** created:    13 Dec 08
-** copyright:  Copyright (c) 2008, Brian Frank and Andy Frank
-** license:    Licensed under the Academic Free License version 3.0
-**************************************************************************
-
-
-Overview [#overview]
-********************
-Unfortunately the .NET foreign function interface is not available yet.
-If you need it please let us know!
\ No newline at end of file
diff --git a/com.xored.f4.fantom/fantom/src/doc/docLang/doc/Env.fandoc b/com.xored.f4.fantom/fantom/src/doc/docLang/doc/Env.fandoc
index a17cae3f..9611c714 100755
--- a/com.xored.f4.fantom/fantom/src/doc/docLang/doc/Env.fandoc
+++ b/com.xored.f4.fantom/fantom/src/doc/docLang/doc/Env.fandoc
@@ -88,6 +88,21 @@ You can specify more than one directory using your operating system's
 [path separator]`sys::File.pathSep`.  Note that the path always implicitly
 includes "fan.home" as the last directory in search path.
 
+You can also setup a directory specific PathEnv with a file called "fan.props".
+If "fan.props" is found in any directory above your current working directory,
+then it used to specify your path.  The location of "fan.props" defines
+the working directory.  Or it can specify additional directories in the
+path with the property 'path' key:
+
+  // location of fan.props is always implicitly first in path (working dir)
+  path=/dir-a/;/dir-b/
+
+The "fan.props" file can also be used to override specific environment
+variables when working under that specific directory using keys prefixed
+with "key.":
+
+  env.ENV_VAR_KEY=var-value
+
 The list of paths is specified in *priority order*.  The first directory
 in the path is used for [workDir]`sys::Env.workDir`. Priority order is
 used to resolve pods and configuration files.  Typically you will use this
@@ -184,4 +199,4 @@ returns a list of values bound to a given key:
 
 Using the basic mechanisms of name/value pairs, you can construct fairly
 sophisticated solutions for discovering the types and resources bundled
-in the installed pods.
+in the installed pods.
\ No newline at end of file
diff --git a/com.xored.f4.fantom/fantom/src/doc/docLang/doc/Literals.fandoc b/com.xored.f4.fantom/fantom/src/doc/docLang/doc/Literals.fandoc
index 3bab0a7f..c9b06293 100755
--- a/com.xored.f4.fantom/fantom/src/doc/docLang/doc/Literals.fandoc
+++ b/com.xored.f4.fantom/fantom/src/doc/docLang/doc/Literals.fandoc
@@ -102,7 +102,7 @@ Decimal [#decimal]
 which provides better precision than a Float.  Decimals are ideal for
 financial applications where Floats may incur rounding errors.
 Decimals are backed by 'BigDecimal' in Java and 'System.Decimal'
-in .NET.
+in .NET.  They are not supported in JavaScript.
 
 Decimal literals are expressed just like Float literals except
 they use the "d" or "D" suffix.  Examples of 'Decimal' literals:
@@ -260,7 +260,7 @@ is the foundation of Fantom's subsystem for naming and resolution.  'Uris' have
 their own literal syntax using the back tick:
   `index.html`
   `/some/path/file.txt`
-  `http://fantom.org/`
+  `https://fantom.org/`
   `TPS Report.doc`
 
 Note that when working with URIs in Fantom and representing them as literals we
diff --git a/com.xored.f4.fantom/fantom/src/doc/docLang/doc/Methods.fandoc b/com.xored.f4.fantom/fantom/src/doc/docLang/doc/Methods.fandoc
index f4dc3a22..cb9d00c9 100755
--- a/com.xored.f4.fantom/fantom/src/doc/docLang/doc/Methods.fandoc
+++ b/com.xored.f4.fantom/fantom/src/doc/docLang/doc/Methods.fandoc
@@ -328,7 +328,6 @@ plate code:
   once Str fullName() { return "$firstName  $lastName" }
 
 Restrictions for once methods:
-  - Must not be declared within a const class
   - Must not be declared within a mixin
   - Must not be a constructor
   - Must not be static
@@ -340,6 +339,13 @@ If a once method throws an exception, then there is no cached
 value - subsequent calls will re-execute the method until it
 returns a value.
 
+A once method may be used on a const class with caveats.  In the JVM the
+cache field is compiled to a volatile field.  However, there is no
+guarantee that the method is called exactly once across multiple threads.
+So the computation must be a pure function that always returns the
+same value.  Furthermore there is no guarantee that all threads see the
+exact same instance returned.
+
 Covariance [#covariance]
 ************************
 Fantom supports [covariance]`Inheritance#covariance` - which allows
diff --git a/com.xored.f4.fantom/fantom/src/doc/docLang/doc/Natives.fandoc b/com.xored.f4.fantom/fantom/src/doc/docLang/doc/Natives.fandoc
index 16747c2d..ad5b2428 100755
--- a/com.xored.f4.fantom/fantom/src/doc/docLang/doc/Natives.fandoc
+++ b/com.xored.f4.fantom/fantom/src/doc/docLang/doc/Natives.fandoc
@@ -18,7 +18,7 @@ Here is a quick check-list for when and when not to write native code:
   1. If possible then the best solution is to write your code in 100% Fantom;
      then it is portable across any platform
   2. If you don't care about portability, then use a FFI to call out to
-     a native library - see `JavaFFI` and `DotnetFFI`.
+     a native library - see `JavaFFI`
   3. If you need to call out native APIs, but still wish your Fantom
      pod to be portable across multiple platforms then use natives
 
diff --git a/com.xored.f4.fantom/fantom/src/doc/docLang/doc/Structure.fandoc b/com.xored.f4.fantom/fantom/src/doc/docLang/doc/Structure.fandoc
index 0c7edfd3..8a3be73d 100755
--- a/com.xored.f4.fantom/fantom/src/doc/docLang/doc/Structure.fandoc
+++ b/com.xored.f4.fantom/fantom/src/doc/docLang/doc/Structure.fandoc
@@ -96,7 +96,7 @@ methods overloaded by parameter type like Java or C#.  Although you may
 find this to be a drag on occasion, there are a couple features in Fantom
 that make this restriction quite palatable.  First method parameters may
 have defaults - this eliminates the convenience methods commonly used in
-Java or C# APIs.  Second all types subclass from Obj - this eliminates
+Java APIs.  Second all types subclass from Obj - this eliminates
 the API bloat required to support all the primitives in an API like
 'java.io.PrintWriter'.  Lastly, constructors in Fantom are named which eliminates
 another common requirement for parameter based overloading.  The benefit of
diff --git a/com.xored.f4.fantom/fantom/src/doc/docLang/doc/index.fog b/com.xored.f4.fantom/fantom/src/doc/docLang/doc/index.fog
index 5dec37c0..cdee2bd7 100755
--- a/com.xored.f4.fantom/fantom/src/doc/docLang/doc/index.fog
+++ b/com.xored.f4.fantom/fantom/src/doc/docLang/doc/index.fog
@@ -30,7 +30,6 @@
   [`Closures`,      "deep dive into closures"],
 "Interop",
   [`JavaFFI`,       "Java foreign function interface"],
-  [`DotnetFFI`,     ".NET foreign function interface"],
   [`Natives`,       "implementing methods and fields in Java and C# code"],
   [`JavaScript`,    "compiling to JavaScript"],
 "Advanced",
diff --git a/com.xored.f4.fantom/fantom/src/doc/docTools/build.fan b/com.xored.f4.fantom/fantom/src/doc/docTools/build.fan
index f5623657..65612ba5 100755
--- a/com.xored.f4.fantom/fantom/src/doc/docTools/build.fan
+++ b/com.xored.f4.fantom/fantom/src/doc/docTools/build.fan
@@ -17,14 +17,14 @@ class Build : BuildPod
   new make()
   {
     podName = "docTools"
-    summary = "Documentation for command line tools"
+    summary = "Fantom command line tools"
     meta    = ["org.name":     "Fantom",
-               "org.uri":      "http://fantom.org/",
+               "org.uri":      "https://fantom.org/",
                "proj.name":    "Fantom Docs",
-               "proj.uri":     "http://fantom.org/",
+               "proj.uri":     "https://fantom.org/",
                "license.name": "Academic Free License 3.0",
-               "vcs.name":     "Mercurial",
-               "vcs.uri":      "https://bitbucket.org/fantom/fan-1.0/"]
+               "vcs.name":     "Git",
+               "vcs.uri":      "https://github.com/fantom-lang/fantom"]
     resDirs = [`doc/`]
   }
 }
\ No newline at end of file
diff --git a/com.xored.f4.fantom/fantom/src/doc/docTools/doc/Bootstrap.fandoc b/com.xored.f4.fantom/fantom/src/doc/docTools/doc/Bootstrap.fandoc
index c5c69a91..f5f7f459 100755
--- a/com.xored.f4.fantom/fantom/src/doc/docTools/doc/Bootstrap.fandoc
+++ b/com.xored.f4.fantom/fantom/src/doc/docTools/doc/Bootstrap.fandoc
@@ -19,7 +19,7 @@ bootstrap process requires two Fantom installations:
 
 By convention we structure our development directory tree like this:
 
-  dev/
+  work/
     rel/
       bin/
       lib/
@@ -31,33 +31,27 @@ By convention we structure our development directory tree like this:
       ...
 
 The "rel" directory always contains the last released build.  The "fan"
-directory contains our main development code branch.  We call our top
-level directory "dev", but for the purposes of this discussion "fan"
-is the development directory.
+directory contains our main development code branch.
 
 Bootstrap Script [#script]
 **************************
-The easiest way to perform a bootstrap build from hg tip is to use the
+The easiest way to perform a bootstrap build from git is to use the
 "bootstrap.fan" script:
-  1. Download latest released build from `http://fantom.org/`
+  1. Download latest released build from `https://fantom.org/`
   2. Unzip that build to '{relHome}'
-  3. Ensure 'hg' is installed in your path (see [Mercurial]`http://mercurial.selenic.com/`)
+  3. Ensure 'git' is installed in your path (see [Git]`https://git-scm.com/`)
   4. Verify 'java_home' env var points to your JDK installation
   5. Execute '{relHome}/bin/fan {relHome}/adm/bootstrap.fan'
   6. Go grab a cup of coffee
 
-NOTE: the default setup requries JDK 1.6 to recompile Fantom in order to target 1.5 JVMs.
-If you need to use a later JDK, see the comments in etc/build/config.props to clear this
-property:
-
-  javacParams=-target 1.5
+NOTE: the default setup requries JDK 1.8 to recompile Fantom.
 
 By default devHome is assumed to be a peer to relHome called "fan". If
 you'd like to use another directory then specify the "-devHome" option.
 
 This script will perform a number of steps:
   1. Verifies your environment
-  2. Clones hg tip or if hg repo exists performs a pull/update
+  2. Clones git repo or if repo exists performs a pull
   3. Configures your etc files
   4. Rebuilds your devHome environment from scratch
 
@@ -66,8 +60,8 @@ fully understand bootstrap building.
 
 Dev Home [#devHome]
 *******************
-By default the build assumes *devHome* to be the home directory
-of Fantom installation.  For example if you rebuild jfan, then the output
+By default the build assumes *devHome* to be the home directory of Fantom
+installation.  For example if you rebuild the Java runtime, then the output
 goes into "devHome/lib/java/sys.jar".  In the case of the *rel*
 installation we don't want this default because we will overwrite
 ourselves (which leads to some nasty problems).  So you need to set
@@ -75,12 +69,12 @@ the *devHome* prop in "etc/build/config.props" of your *rel* installation
 to reference the *dev* directory using a URI (not OS path):
 
   // Must be configured boot build in substitute/release installation
-  devHome=file:/C:/dev/fan/
+  devHome=file:/C:/work/fan/
 
 If you forget to do this, then you will get a build error which
 looks something like this:
 
-  C:\dev\fan\src>sys\java\build
+  C:\work\fan\src>sys\java\build
   ERR: Must update 'devHome' for bootstrap build
   BUILD FAILED [12ms]!
 
@@ -104,8 +98,7 @@ And of course you have to run your scripts as executables so that the shebang ta
 
 JDK [#otherTools]
 ********************************
-You need JDK 1.6 or greater to compile from source (only 1.5 is required
-for the Java runtime).
+You need JDK 1.8 to compile from source.
 
 In order to compile from source you will need to setup some config
 properties to reference your JDK home directory:
diff --git a/com.xored.f4.fantom/fantom/src/doc/docTools/doc/Fansh.fandoc b/com.xored.f4.fantom/fantom/src/doc/docTools/doc/Fansh.fandoc
index 77fd157e..302e6693 100755
--- a/com.xored.f4.fantom/fantom/src/doc/docTools/doc/Fansh.fandoc
+++ b/com.xored.f4.fantom/fantom/src/doc/docTools/doc/Fansh.fandoc
@@ -14,7 +14,7 @@ result of the expression is non-void, then it will print the result
 via the 'Obj.toStr' method.  Enter "quit" to exit:
 
   C:\dev\fan\bin>fansh
-  Fantom Shell v1.0.19 ('?' for help)
+  Fantom Shell v1.0.74 ('?' for help)
   fansh> 3 + 4
   7
   fansh> "3" + 4
@@ -31,7 +31,7 @@ evaluations.  You can enter the "scope" command to list the current
 local variables.  An example:
 
   C:\dev\fan\bin>fansh
-  Fantom Shell v1.0.19 ('?' for help)
+  Fantom Shell v1.0.74 ('?' for help)
   fansh> x := 5
   5
   fansh> y := 7
diff --git a/com.xored.f4.fantom/fantom/src/doc/docTools/doc/Setup.fandoc b/com.xored.f4.fantom/fantom/src/doc/docTools/doc/Setup.fandoc
index 19aaa446..fd015c3d 100755
--- a/com.xored.f4.fantom/fantom/src/doc/docTools/doc/Setup.fandoc
+++ b/com.xored.f4.fantom/fantom/src/doc/docTools/doc/Setup.fandoc
@@ -16,8 +16,7 @@ you should add "bin" into your path:
 The Fantom launchers are implemented as a set of Bash shell
 scripts which all route to common code sourced in "bin/fanlaunch".
 The current implementation is pretty simple and requires that
-you have a "java" command available in your environment (we
-haven't tried Mono with Fantom yet).
+you have a "java" command available in your environment.
 
 If the unpacked zip file doesn't have the executable permission
 set on script files, then the "adm/unixsetup" script will
@@ -73,7 +72,7 @@ The launcher for Windows and Unix determines which java to use in the following
 1. If the 'FAN_JAVA' environment variable is set, then we assume it points to the
 "java.exe" to run.
 2. If the 'JAVA_HOME' environment variable is set, then we use "${JAVA_HOME}/bin/java"
-3. Otherwise, we fallback to the system "java"
+3. Otherwise, we fallback to the system "java" executable
 
 You can configure the JVM options by editing "etc/sys/config.props" and setting the
 "java.options" property. This is useful for modifying memory settings for the JVM.
@@ -81,8 +80,8 @@ You can configure the JVM options by editing "etc/sys/config.props" and setting
   // etc/sys/config.props
   java.options=-Xmx128M
 
-Fantom currently requires Java version 1.5 or greater to run.
-Java 1.6 or greater is required to compile from source.
+Fantom currently requires Java version 1.8 or greater to run.
+Java 1.8 is required to compile from source.
 
 You can also run Fantom directly without the launcher by adding "sys.jar"
 to your classpath and ensuring either the "fan_home" environment variable
@@ -104,38 +103,6 @@ Another feature of the launcher scripts is the ability to map
 specific script files to use an alternate Fantom runtime.  This technique
 is used to manage the [bootstrap build]`Bootstrap#substitutes` process.
 
-Launcher Debugger [#debugging]
-==============================
-You can set the "FAN_LAUNCHER_DEBUG" environment variable to "true" to debug
-the launcher script. This can be helpful if you are having problems getting your
-Java environment working:
-
-  C:\Dev\fantom\fan> set FAN_LAUNCHER_DEBUG=true
-
-  C:\Dev\fantom\fan> fan -version
-  -- LAUNCHER DEBUG ON
-  -- Launcher Args: Fan -version
-  -- FAN_HOME: C:\Dev\fantom\fan
-  -- Java: java
-  -- Fantom classpath: "C:\Dev\fantom\fan\lib\java\sys.jar";"C:\Dev\fantom\fan\lib\java\jline.jar"
-  -- Java Options: -Xmx512M
-  -- Fantom Tool: Fan
-  -- Tool Args:  -version
-  Fantom Launcher
-  Copyright (c) 2006-2013, Brian Frank and Andy Frank
-  Licensed under the Academic Free License version 3.0
-
-  Java Runtime:
-    java.version:    1.8.0_51
-    java.vm.name:    Java HotSpot(TM) 64-Bit Server VM
-    java.vm.vendor:  Oracle Corporation
-    java.vm.version: 25.51-b03
-    java.home:       C:\Program Files\Java\jre1.8.0_51
-    fan.platform:    win32-x86_64
-    fan.version:     1.0.67
-    fan.env:         sys::BootEnv
-    fan.home:        C:\Dev\fantom\fan
-
 Fantom Programs as Windows Services [#windowsServices]
 ===================================
 The Fantom installation includes a binary called 'fansc.exe' in the "bin\" directory
@@ -183,7 +150,7 @@ be unpleasant in Unix and OS X environments because control keys such as the
 arrow keys are not handled.  You can add [JLine2]`https://github.com/jline/jline2`
 to your system classpath to fix this.  The built-in bash scripts will automatically
 put "lib/java/jline.jar" into the classpath.  You can download a pre-built
-"jline.jar" file from `http://repo1.maven.org/maven2/jline/jline/`.  If JLine
+"jline.jar" file from `https://repo1.maven.org/maven2/jline/jline/`.  If JLine
 is not installed, then 'java.lang.Console' is used as a fallback.  To integrate
 command line support into your application use `sys::Env.prompt`.
 
@@ -205,4 +172,4 @@ Env [#env]
 The default behavior of Fantom is to manage all its files under the
 installation directory.  But you can boot Fantom using alternative
 environments to customize how your deployment is structured.
-See `docLang::Env` for details.
+See `docLang::Env` for details.
\ No newline at end of file
diff --git a/com.xored.f4.fantom/fantom/src/dom/build.fan b/com.xored.f4.fantom/fantom/src/dom/build.fan
index 4a58279c..286895e9 100755
--- a/com.xored.f4.fantom/fantom/src/dom/build.fan
+++ b/com.xored.f4.fantom/fantom/src/dom/build.fan
@@ -20,12 +20,12 @@ class Build : BuildPod
     podName = "dom"
     summary = "Web Browser DOM API"
     meta    = ["org.name":     "Fantom",
-               "org.uri":      "http://fantom.org/",
+               "org.uri":      "https://fantom.org/",
                "proj.name":    "Fantom Core",
-               "proj.uri":     "http://fantom.org/",
+               "proj.uri":     "https://fantom.org/",
                "license.name": "Academic Free License 3.0",
-               "vcs.name":     "Mercurial",
-               "vcs.uri":      "https://bitbucket.org/fantom/fan-1.0/"]
+               "vcs.name":     "Git",
+               "vcs.uri":      "https://github.com/fantom-lang/fantom"]
     depends  = ["sys 1.0", "concurrent 1.0", "graphics 1.0", "web 1.0"]
     srcDirs  = [`fan/`, `test/`]
     jsDirs   = [`js/`]
diff --git a/com.xored.f4.fantom/fantom/src/dom/fan/Doc.fan b/com.xored.f4.fantom/fantom/src/dom/fan/Doc.fan
index 30858d49..1a4e0aad 100755
--- a/com.xored.f4.fantom/fantom/src/dom/fan/Doc.fan
+++ b/com.xored.f4.fantom/fantom/src/dom/fan/Doc.fan
@@ -42,7 +42,7 @@ class Doc
   ** Get the body element.
   native Elem body()
 
-  ** Get the currently focued element, or 'null' for none.
+  ** Get the currently focused element, or 'null' for none.
   native Elem? activeElem()
 
   **
diff --git a/com.xored.f4.fantom/fantom/src/dom/fan/Event.fan b/com.xored.f4.fantom/fantom/src/dom/fan/Event.fan
index e679a331..a87174d6 100644
--- a/com.xored.f4.fantom/fantom/src/dom/fan/Event.fan
+++ b/com.xored.f4.fantom/fantom/src/dom/fan/Event.fan
@@ -133,6 +133,9 @@ class Event
 
   private new make() {}
 
+  ** Create a mock `Event` manullay.
+  @NoDoc static native Event makeMock()
+
   ** Create an `Event` instance from a native JavaScript Event object.
   static native Event fromNative(Obj event)
 
diff --git a/com.xored.f4.fantom/fantom/src/dom/fan/HttpReq.fan b/com.xored.f4.fantom/fantom/src/dom/fan/HttpReq.fan
index 3d22118e..d2fc7d46 100755
--- a/com.xored.f4.fantom/fantom/src/dom/fan/HttpReq.fan
+++ b/com.xored.f4.fantom/fantom/src/dom/fan/HttpReq.fan
@@ -31,6 +31,11 @@ class HttpReq
   ** Defaults to 'true'
   Bool async := true
 
+  ** The type of data contained in the response. It also lets the
+  ** author change the response type. If an empty string is set as
+  ** the value, the default value of '"text"' is used.
+  @NoDoc Str resType := ""
+
   **
   ** Indicates whether or not cross-site 'Access-Control' requests
   ** should be made using credentials such as cookies, authorization
diff --git a/com.xored.f4.fantom/fantom/src/dom/js/ElemPeer.js b/com.xored.f4.fantom/fantom/src/dom/js/ElemPeer.js
index 925cb897..34b37ded 100755
--- a/com.xored.f4.fantom/fantom/src/dom/js/ElemPeer.js
+++ b/com.xored.f4.fantom/fantom/src/dom/js/ElemPeer.js
@@ -42,6 +42,7 @@ fan.dom.ElemPeer.prototype._make = function(self, tagName, ns)
 
 fan.dom.ElemPeer.fromNative = function(obj, type)
 {
+  if (obj instanceof fan.dom.Elem) return obj;
   return fan.dom.ElemPeer.wrap(obj, type.make());
 }
 
diff --git a/com.xored.f4.fantom/fantom/src/dom/js/EventPeer.js b/com.xored.f4.fantom/fantom/src/dom/js/EventPeer.js
index 7ba5a0dd..bb90078f 100644
--- a/com.xored.f4.fantom/fantom/src/dom/js/EventPeer.js
+++ b/com.xored.f4.fantom/fantom/src/dom/js/EventPeer.js
@@ -13,9 +13,14 @@ fan.dom.EventPeer = fan.sys.Obj.$extend(fan.sys.Obj);
 
 fan.dom.EventPeer.prototype.$ctor = function(self) {}
 
+fan.dom.EventPeer.makeMock = function()
+{
+  return fan.dom.EventPeer.make(new Event("mock"));
+}
+
 fan.dom.EventPeer.fromNative = function(obj)
 {
-  return fan.dom.EventPeer.make(obj)
+  return fan.dom.EventPeer.make(obj);
 }
 
 fan.dom.EventPeer.prototype.type = function(self) { return this.event.type; }
diff --git a/com.xored.f4.fantom/fantom/src/dom/js/HttpReqPeer.js b/com.xored.f4.fantom/fantom/src/dom/js/HttpReqPeer.js
index 741be07a..f140b70a 100755
--- a/com.xored.f4.fantom/fantom/src/dom/js/HttpReqPeer.js
+++ b/com.xored.f4.fantom/fantom/src/dom/js/HttpReqPeer.js
@@ -30,7 +30,7 @@ fan.dom.HttpReqPeer.prototype.send = function(self, method, content, f)
   }
 
   // open request
-  xhr.open(method.toUpperCase(), self.m_uri.m_str, self.m_async);
+  xhr.open(method.toUpperCase(), self.m_uri.encode(), self.m_async);
   if (self.m_async)
   {
     xhr.onreadystatechange = function ()
@@ -40,6 +40,9 @@ fan.dom.HttpReqPeer.prototype.send = function(self, method, content, f)
     }
   }
 
+  // set response type
+  xhr.responseType = self.m_resType;
+
   // setup headers
   var ct = false;
   var k = self.m_headers.keys();
@@ -87,9 +90,12 @@ fan.dom.HttpReqPeer.prototype.send = function(self, method, content, f)
 
 fan.dom.HttpReqPeer.makeRes = function(xhr)
 {
+  var isText = xhr.responseType == "" || xhr.responseType == "text";
+
   var res = fan.dom.HttpRes.make();
+  res.m_$xhr    = xhr;
   res.m_status  = xhr.status;
-  res.m_content = xhr.responseText;
+  res.m_content = isText ? xhr.responseText : "";
 
   var all = xhr.getAllResponseHeaders().split("\n");
   for (var i=0; i elements where class='bar'
 
 Elem [#elem]
-**********
+************
 
   // create Elems
   Elem("div")   // create new unattached 
element diff --git a/com.xored.f4.fantom/fantom/src/domkit/build.fan b/com.xored.f4.fantom/fantom/src/domkit/build.fan index fa279fb9..99093f84 100644 --- a/com.xored.f4.fantom/fantom/src/domkit/build.fan +++ b/com.xored.f4.fantom/fantom/src/domkit/build.fan @@ -19,12 +19,12 @@ class Build : BuildPod podName = "domkit" summary = "DOM Based UI Framework" meta = ["org.name": "Fantom", - "org.uri": "http://fantom.org/", + "org.uri": "https://fantom.org/", "proj.name": "Fantom Core", - "proj.uri": "http://fantom.org/", + "proj.uri": "https://fantom.org/", "license.name": "Academic Free License 3.0", - "vcs.name": "Mercurial", - "vcs.uri": "https://bitbucket.org/fantom/fan-1.0/"] + "vcs.name": "Git", + "vcs.uri": "https://github.com/fantom-lang/fantom"] depends = ["sys 1.0", "concurrent 1.0", "graphics 1.0", diff --git a/com.xored.f4.fantom/fantom/src/domkit/fan/Dialog.fan b/com.xored.f4.fantom/fantom/src/domkit/fan/Dialog.fan index dba307bd..77938966 100644 --- a/com.xored.f4.fantom/fantom/src/domkit/fan/Dialog.fan +++ b/com.xored.f4.fantom/fantom/src/domkit/fan/Dialog.fan @@ -31,6 +31,9 @@ using dom ** Protected sub-class callback invoked directly before dialog is opened. protected virtual Void onBeforeOpen() {} + ** Protected sub-class callback invoked directly after dialog is opened. + protected virtual Void onAfterOpen() {} + ** Callback when a key is pressed while Dialog is open, including ** events that where dispatched outside the dialog. protected Void onKeyDown(|Event e| f) { this.cbKeyDown = f } @@ -105,7 +108,7 @@ using dom frame.transition([ "transform": "scale(1)", "opacity": "1" - ], null, 100ms) { this.focus; fireOpen } + ], null, 100ms) { this.focus; onAfterOpen(); fireOpen } } ** Close this dialog. If dialog is already closed @@ -127,8 +130,8 @@ using dom ** Callback when popup is closed. Void onClose(|This| f) { cbClose = f } - private Void fireOpen() { cbOpen?.call(this) } - private Void fireClose() { cbClose?.call(this) } + private Void fireOpen() { cbOpen?.call(this) } + private Void fireClose() { cbClose?.call(this) } private const Int uid private static const AtomicRef nextId := AtomicRef(0) diff --git a/com.xored.f4.fantom/fantom/src/domkit/fan/Dnd.fan b/com.xored.f4.fantom/fantom/src/domkit/fan/Dnd.fan index ba6d33a7..2590be47 100644 --- a/com.xored.f4.fantom/fantom/src/domkit/fan/Dnd.fan +++ b/com.xored.f4.fantom/fantom/src/domkit/fan/Dnd.fan @@ -111,7 +111,10 @@ using graphics elem.onEvent("dragleave", false) |e| { if (e.target == elem) + { elem.style.removeClass("domkit-dnd-over") + cbLeave?.call() + } } elem.onEvent("drop", false) |e| { @@ -132,6 +135,9 @@ using graphics ** 'pagePos' is the current drag node. Void onOver(|Point pagePos| f) { this.cbOver = f } + ** Callback when drag target has left this drop target. + Void onLeave(|->| f) { this.cbLeave = f } + private Bool _canDrop(Obj data) { cbCanDrop == null ? true : cbCanDrop.call(data) @@ -140,6 +146,7 @@ using graphics private Func? cbCanDrop private Func? cbDrop private Func? cbOver + private Func? cbLeave private Int depth } diff --git a/com.xored.f4.fantom/fantom/src/domkit/fan/SashBox.fan b/com.xored.f4.fantom/fantom/src/domkit/fan/SashBox.fan index 1bd69a90..20f045ab 100644 --- a/com.xored.f4.fantom/fantom/src/domkit/fan/SashBox.fan +++ b/com.xored.f4.fantom/fantom/src/domkit/fan/SashBox.fan @@ -35,6 +35,9 @@ using graphics ** Allow user to resize sash positions. See `div`. Bool resizable := false + ** Callback when user resizes a sash pane if `resizable` is 'true'. + Void onSashResize(|This| f) { this.cbSashResize = f } + ** ** Size to apply to each child, width or height based on `dir`. Fixed ** 'px' and percentage sizes are allowed. Percentage sizes will be @@ -248,6 +251,7 @@ using graphics // update this.sizes = working applyStyle + cbSashResize?.call(this) } ** Convert `sizes` to % @@ -295,4 +299,5 @@ using graphics private Int? resizeIndex private Float? pivoff private Elem? splitter + private Func? cbSashResize } \ No newline at end of file diff --git a/com.xored.f4.fantom/fantom/src/domkit/fan/Table.fan b/com.xored.f4.fantom/fantom/src/domkit/fan/Table.fan index a0f92cc5..2facf354 100644 --- a/com.xored.f4.fantom/fantom/src/domkit/fan/Table.fan +++ b/com.xored.f4.fantom/fantom/src/domkit/fan/Table.fan @@ -44,7 +44,7 @@ using graphics // manually track focus so we can detect when // the browser window becomes unactive while // maintaining focus internally in document - this.onEvent("focus", false) |e| { manFocus=true; refresh } + this.onEvent("focus", false) |e| { if (!manFocus) { manFocus=true; refresh }} this.onEvent("blur", false) |e| { manFocus=false; refresh } // rebuild if size changes @@ -92,6 +92,7 @@ using graphics view.sort(col, dir) model.onSort(col, dir) refresh + cbSort?.call(this) } ** Scroll to the given row and column in table. Pass 'null' to @@ -158,6 +159,13 @@ using graphics ** Callback when row is double-clicked. Void onAction(|This| f) { cbAction = f } + ** Callback when table is sorted by a column + Void onSort(|This| f) { cbSort = f } + + ** Callback when a key is pressed in table. + // TODO: need to fix to take |This,Event| arg... + @NoDoc Void onKeyDown(|Event| f) { cbKeyDown = f } + ** Callback when a event occurs inside a table cell. Void onTableEvent(Str type, |TableEvent| f) { cbTableEvent[type] = f } @@ -924,6 +932,23 @@ using graphics ** Callback to handle selection changes from a mouse event. private Void onMouseEventSelect(Event e, Int row, Int vrow) { + // always force focus for mousedown + manFocus = true + + // short-circuit if we initiated a hyperlink so that + // the event can bubble properly down to the tag + if (e.target.tagName == "a") + { + // Chrome seems to be doing some weird stuff here; forcing + // an onblur call on the Table
inbetween firing the + // hyperlink. Technically that might be correct but complicates + // how we manage focus. So if we detect this manually invoke + // the click to skip over that behavoir + e.target->click + e.stop + return + } + cur := sel.indexes newsel := cur.dup @@ -1007,17 +1032,20 @@ using graphics if (cur < 0) cur = cur.not - 1 pre := colx[cur] == scrollx ? cur-1 : cur scrollTo(0.max(pre), null) + return case Key.right: cur := colx.binarySearch(scrollx) if (cur < 0) cur = cur.not - 1 scrollTo((numCols-1).min(cur+1), null) + return case Key.up: if (sel.indexes.isEmpty) { updateSel([selFirstVis]) scrollTo(null, firstVisRow) + return } else { @@ -1025,6 +1053,7 @@ using graphics prev := pivot - 1 updateSel([view.rowViewToModel(prev)]) scrollTo(null, prev) + return } case Key.down: @@ -1032,6 +1061,7 @@ using graphics { updateSel([selFirstVis]) scrollTo(null, firstVisRow) + return } else { @@ -1039,6 +1069,7 @@ using graphics next := pivot + 1 updateSel([view.rowViewToModel(next)]) scrollTo(null, next) + return } } @@ -1048,6 +1079,9 @@ using graphics cbAction?.call(this) return } + + // else bubble up to callback + if (e.type == "keydown") return cbKeyDown?.call(e) } @NoDoc Void updateSel(Int[] newsel) @@ -1083,6 +1117,8 @@ using graphics private Func? cbBeforeSelect private Func? cbSelect private Func? cbAction + private Func? cbSort + private Func? cbKeyDown private Str:Func cbTableEvent := [:] private Func? cbHeaderPopup @@ -1315,7 +1351,7 @@ internal const class TablePos override Void onUpdate(Int[] oldIndexes, Int[] newIndexes) { oldIndexes.each |i| { if (i < max) view.table.refreshRow(view.rowModelToView(i)) } - newIndexes.each |i| { view.table.refreshRow(view.rowModelToView(i)) } + newIndexes.each |i| { if (i < max) view.table.refreshRow(view.rowModelToView(i)) } } private TableView view } diff --git a/com.xored.f4.fantom/fantom/src/email/build.fan b/com.xored.f4.fantom/fantom/src/email/build.fan index 8fdca6c7..32421143 100755 --- a/com.xored.f4.fantom/fantom/src/email/build.fan +++ b/com.xored.f4.fantom/fantom/src/email/build.fan @@ -19,12 +19,12 @@ class Build : BuildPod podName = "email" summary = "Email support" meta = ["org.name": "Fantom", - "org.uri": "http://fantom.org/", + "org.uri": "https://fantom.org/", "proj.name": "Fantom Core", - "proj.uri": "http://fantom.org/", + "proj.uri": "https://fantom.org/", "license.name": "Academic Free License 3.0", - "vcs.name": "Mercurial", - "vcs.uri": "https://bitbucket.org/fantom/fan-1.0/"] + "vcs.name": "Git", + "vcs.uri": "https://github.com/fantom-lang/fantom"] depends = ["sys 1.0", "inet 1.0"] srcDirs = [`fan/`, `test/`] docSrc = true diff --git a/com.xored.f4.fantom/fantom/src/fandoc/build.fan b/com.xored.f4.fantom/fantom/src/fandoc/build.fan index 9c204d89..95f6fdb5 100755 --- a/com.xored.f4.fantom/fantom/src/fandoc/build.fan +++ b/com.xored.f4.fantom/fantom/src/fandoc/build.fan @@ -19,12 +19,12 @@ class Build : BuildPod podName = "fandoc" summary = "Fandoc parser and DOM" meta = ["org.name": "Fantom", - "org.uri": "http://fantom.org/", + "org.uri": "https://fantom.org/", "proj.name": "Fantom Core", - "proj.uri": "http://fantom.org/", + "proj.uri": "https://fantom.org/", "license.name": "Academic Free License 3.0", - "vcs.name": "Mercurial", - "vcs.uri": "https://bitbucket.org/fantom/fan-1.0/"] + "vcs.name": "Git", + "vcs.uri": "https://github.com/fantom-lang/fantom"] depends = ["sys 1.0"] srcDirs = [`fan/`, `test/`] docSrc = true diff --git a/com.xored.f4.fantom/fantom/src/fandoc/fan/DocModel.fan b/com.xored.f4.fantom/fantom/src/fandoc/fan/DocModel.fan index 1f99eac2..6d3816d9 100755 --- a/com.xored.f4.fantom/fantom/src/fandoc/fan/DocModel.fan +++ b/com.xored.f4.fantom/fantom/src/fandoc/fan/DocModel.fan @@ -53,6 +53,16 @@ abstract class DocNode ** abstract Void write(DocWriter out) + ** + ** Is this an inline versus a block node. + ** + abstract Bool isInline() + + ** + ** Is this a block element versus an inline element. + ** + Bool isBlock() { return !isInline } + ** ** Debug dump to output stream. ** @@ -112,6 +122,11 @@ abstract class DocNode { return parent?.children?.last === this } + + ** + ** Get all the DocText children as a string + ** + abstract Str toText() } ************************************************************************** @@ -135,7 +150,11 @@ class DocText : DocNode out.text(this) } - override Str toStr() { return str } + override Bool isInline() { true } + + override Str toText() { str } + + override Str toStr() { str } Str str } @@ -157,16 +176,6 @@ abstract class DocElem : DocNode ** abstract Str htmlName() - ** - ** Is this an inline versus a block element. - ** - abstract Bool isInline() - - ** - ** Is this a block element versus an inline element. - ** - Bool isBlock() { return !isInline } - ** ** Write this element and its children to the specified DocWriter. ** @@ -194,6 +203,11 @@ abstract class DocElem : DocNode ** DocNode[] children() { return kids.ro } + ** + ** Iterate the children nodes + ** + Void eachChild(|DocNode| f) { kids.each(f) } + @Deprecated { msg = "Use add()" } This addChild(DocNode node) { add(node) } @@ -274,6 +288,16 @@ abstract class DocElem : DocNode return this } + ** + ** Get all the DocText children as a string + ** + override Str toText() + { + s := StrBuf() + kids.each |kid| { s.join(kid.toText, " ") } + return s.toStr + } + ////////////////////////////////////////////////////////////////////////// // Path ////////////////////////////////////////////////////////////////////////// @@ -350,7 +374,7 @@ class Heading : DocElem override DocNodeId id() { return DocNodeId.heading } override Str htmlName() { return "h$level" } override Bool isInline() { return false } - Str title() { children.first.toStr } + Str title() { toText } const Int level } @@ -570,6 +594,7 @@ class Image : DocElem Str uri Str alt Str? size // formatted {w}x{h} + Int line } ************************************************************************** diff --git a/com.xored.f4.fantom/fantom/src/fandoc/fan/FandocDocWriter.fan b/com.xored.f4.fantom/fantom/src/fandoc/fan/FandocDocWriter.fan index 84c16e6e..f8287fad 100644 --- a/com.xored.f4.fantom/fantom/src/fandoc/fan/FandocDocWriter.fan +++ b/com.xored.f4.fantom/fantom/src/fandoc/fan/FandocDocWriter.fan @@ -265,7 +265,7 @@ internal class ListIndex private static Int:Str sortr(Int:Str unordered) { // no ordered literal map... grr... - // http://fantom.org/sidewalk/topic/1837#c14431 + // https://fantom.org/forum/topic/1837#c14431 sorted := [:] { it.ordered = true } unordered.keys.sortr.each { sorted[it] = unordered[it] } return sorted diff --git a/com.xored.f4.fantom/fantom/src/fandoc/fan/InlineParser.fan b/com.xored.f4.fantom/fantom/src/fandoc/fan/InlineParser.fan index 52ad0d0d..52e84751 100755 --- a/com.xored.f4.fantom/fantom/src/fandoc/fan/InlineParser.fan +++ b/com.xored.f4.fantom/fantom/src/fandoc/fan/InlineParser.fan @@ -225,6 +225,7 @@ internal class InlineParser uri := uri img := Image(uri, alt) img.size = size + img.line = this.line return img } diff --git a/com.xored.f4.fantom/fantom/src/fandoc/pod.fandoc b/com.xored.f4.fantom/fantom/src/fandoc/pod.fandoc index 43670488..9a39d70a 100755 --- a/com.xored.f4.fantom/fantom/src/fandoc/pod.fandoc +++ b/com.xored.f4.fantom/fantom/src/fandoc/pod.fandoc @@ -43,16 +43,16 @@ pre> - Strong: **foo bar** - Emphasis: *foo bar* - Strong+Emphasis: ** *foo bar* ** - - Hyperlink: `http://fantom.org/` - - Hyperlink: [Fantom Home Page]`http://fantom.org/` + - Hyperlink: `https://fantom.org/` + - Hyperlink: [Fantom Home Page]`https://fantom.org/`
-  ![fan logo]`http://fantom.org/pod/fantomws/res/img/fantom.png`
+  ![fan logo]`https://fantom.org/pod/fantomws/res/img/fantom.png`
 
-  ![fan logo][80x18]`http://fantom.org/pod/fantomws/res/img/fantom.png`
+  ![fan logo][80x18]`https://fantom.org/pod/fantomws/res/img/fantom.png`
 
- [![alt]`http://fantom.org/pod/fantomws/res/img/fantom.png`]`http://fantom.org/`
+ [![alt]`https://fantom.org/pod/fantomws/res/img/fantom.png`]`https://fantom.org/`
 
-#include 
-#include 
-#include "launcher.h"
-#include "utils.h"
-
-#define KEY_LEN 256
-typedef HRESULT (*_CorBindToRuntimeEx)(LPCWSTR,LPCWSTR,DWORD,REFCLSID,REFIID,LPVOID FAR *);
-ICLRRuntimeHost *pClrHost;
-
-//////////////////////////////////////////////////////////////////////////
-// Init CLR
-//////////////////////////////////////////////////////////////////////////
-
-/**
- * Check for .NET framework version 2.0 using registery.
- */
-int checkDotnetFwVer()
-{
-  if (debug) printf("-- findDotnetVer\n");
-
-  // query registry to get installed .NET Framework versions
-  const char* key= "SOFTWARE\\Microsoft\\.NETFramework\\policy";
-  HKEY hKey;
-  if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, key, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
-    //return err("Cannot read registry: %s", key);
-    return err(".NET Framework v2.0 not found");
-
-  // list all installed versions of fw
-  if (debug) printf("--   registry keys:\n");
-
-  DWORD dwIndex = 0;
-  DWORD cbName = KEY_LEN;
-  char val[KEY_LEN];
-  int fwFound = -1;
-
-  while ((ERROR_NO_MORE_ITEMS !=
-    RegEnumKeyEx(hKey, dwIndex, val, &cbName, NULL, NULL, NULL, NULL)))
-  {
-    if (debug) printf("--     %s\n", val);
-    if (strcmp(val, "v2.0") == 0) fwFound = ERROR_SUCCESS;
-    if (strcmp(val, "V2.0") == 0) fwFound = ERROR_SUCCESS;
-    dwIndex++;
-    cbName = KEY_LEN;
-  }
-
-  // close
-  RegCloseKey(hKey);
-
-  // return result
-  if (fwFound != ERROR_SUCCESS)
-    return err(".NET Framework v2.0 not found");
-
-  return 0;
-}
-
-//////////////////////////////////////////////////////////////////////////
-// Load the CLR
-//////////////////////////////////////////////////////////////////////////
-
-int loadClr()
-{
-  if (debug) printf("-- loadClr\n");
-
-  // dynamically load mscoree.dll
-  if (debug) printf("--   load mscoree.dll\n");
-  HINSTANCE dll = LoadLibrary("mscoree.dll");
-  if (dll == NULL)
-    return err("Cannot load library: mscoree.dll");
-
-  // query for CorBindToRuntimeEx
-  _CorBindToRuntimeEx corBindToRuntimeEx =
-    (_CorBindToRuntimeEx)GetProcAddress(dll, "CorBindToRuntimeEx");
-  if (corBindToRuntimeEx == NULL)
-    return err("Cannot find CorBindToRuntimeEx in mscoree.dll");
-
-  // load CLR
-  HRESULT hr = corBindToRuntimeEx(
-    NULL,                  // desired CLR version (NULL = latest)
-    NULL,                  // desired GC flavor (NULL = workstation)
-    0,                     // desired startup flags
-    CLSID_CLRRuntimeHost,  // CLSID of CLR
-    IID_ICLRRuntimeHost,   // IID of ICLRRuntimeHost
-    (PVOID*)&pClrHost);    // return COM interface
-
-  if (!SUCCEEDED(pClrHost))
-    return err("Cannot load CLR Host");
-
-  return 0;
-}
-
-//////////////////////////////////////////////////////////////////////////
-// Run the main method
-//////////////////////////////////////////////////////////////////////////
-
-int runDotnetMain()
-{
-  // initialize and start the CLR
-  pClrHost->Start();
-
-  // get sys path
-  char sysPath[MAX_PATH];
-  strcpy(sysPath, fanHome);
-  strcat(sysPath, "\\lib\\dotnet\\sys.dll");
-  ULONG sz = strlen(sysPath) + 1;
-  LPWSTR wSysPath = new WCHAR[sz];
-  MultiByteToWideChar(CP_ACP, 0, sysPath, sz, wSysPath, sz);
-  if (debug)
-  {
-    printf("--   sysPath  = %s\n", sysPath);
-    printf("--   wSysPath = %S\n", wSysPath);
-  }
-
-  // figure out main
-  char mainClassName[256];
-  sprintf(mainClassName, "Fanx.Tools.%s", FAN_TOOL);
-  sz = strlen(mainClassName) + 1;
-  LPWSTR wMainClassName = new WCHAR[sz];
-  MultiByteToWideChar(CP_ACP, 0, mainClassName, sz, wMainClassName, sz);
-  if (debug)
-  {
-    printf("--   mainClassName  = %s\n", mainClassName);
-    printf("--   wMainClassName = %S\n", wMainClassName);
-  }
-
-  // since we only have a single str to work with
-  // stuff everything into our reserved str, delimited
-  // with newlines
-
-  char reserved[1024];
-  reserved[0] = '\0'; // need this or we get garbage at beginning of str
-  strcat(reserved, fanHome);
-  strcat(reserved, "\n");
-  for (int i=0; i 0) strcat(reserved, "\n");
-    strcat(reserved, fanArgv[i]);
-  }
-  sz = strlen(reserved) + 1;
-  LPWSTR wReserved = new WCHAR[sz];
-  MultiByteToWideChar(CP_ACP, 0, reserved, sz, wReserved, sz);
-  if (debug) printf("--   wReserved = %S\n", wReserved);
-
-  DWORD retVal = 0;
-  HRESULT hr = pClrHost->ExecuteInDefaultAppDomain(
-      wSysPath, wMainClassName, L"run", wReserved, &retVal);
-
-  if (debug)
-  {
-    printf("--   Managed code returned %d\n", retVal);
-    printf("--   HRESULT = %d\n", hr);
-  }
-
-  if (!SUCCEEDED(hr))
-    return err("Could not run %s\n", mainClassName);
-
-  return 0;
-}
-
-//////////////////////////////////////////////////////////////////////////
-// .NET Main
-//////////////////////////////////////////////////////////////////////////
-
-int runDotnet()
-{
-  if (checkDotnetFwVer()) return -1;
-  if (loadClr())          return -1;
-  if (runDotnetMain())    return -1;
-  return 0;
-}
\ No newline at end of file
diff --git a/com.xored.f4.fantom/fantom/src/launcher/dotnet.h b/com.xored.f4.fantom/fantom/src/launcher/dotnet.h
deleted file mode 100755
index e0adf8da..00000000
--- a/com.xored.f4.fantom/fantom/src/launcher/dotnet.h
+++ /dev/null
@@ -1,14 +0,0 @@
-//
-// Copyright (c) 2006, Brian Frank and Andy Frank
-// Licensed under the Academic Free License version 3.0
-//
-// History:
-//   27 Dec 06  Brian Frank  Creation
-//
-
-#ifndef _DOTNET_H
-#define _DOTNET_H
-
-extern int runDotnet();
-
-#endif
\ No newline at end of file
diff --git a/com.xored.f4.fantom/fantom/src/launcher/java.cpp b/com.xored.f4.fantom/fantom/src/launcher/java.cpp
deleted file mode 100755
index 9162d1b8..00000000
--- a/com.xored.f4.fantom/fantom/src/launcher/java.cpp
+++ /dev/null
@@ -1,247 +0,0 @@
-//
-// Copyright (c) 2006, Brian Frank and Andy Frank
-// Licensed under the Academic Free License version 3.0
-//
-// History:
-//   27 Dec 06  Brian Frank  Creation
-//
-
-#include 
-#include 
-#include 
-#include "props.h"
-#include "launcher.h"
-#include "utils.h"
-
-//////////////////////////////////////////////////////////////////////////
-// TypeDefs
-//////////////////////////////////////////////////////////////////////////
-
-typedef jint (JNICALL *CreateJavaVMFunc)(JavaVM **pvm, void **penv, void *vm_args);
-
-//////////////////////////////////////////////////////////////////////////
-// Globals
-//////////////////////////////////////////////////////////////////////////
-
-const int MAX_OPTIONS = 32;        // max number of Java options
-JavaVMOption options[MAX_OPTIONS]; // Java options to pass to create VM
-char jvmPath[MAX_PATH];            // path to jvm.dll to dynamically load
-int nOptions;                      // Number of options
-JavaVM* vm;                        // VM created
-JNIEnv* env;                       // JNI environment
-
-//////////////////////////////////////////////////////////////////////////
-// Init Java VM
-//////////////////////////////////////////////////////////////////////////
-
-/**
- * Find the jvm.dll path to use by querying the registry.
- */
-int findJvmPath()
-{
-  if (debug) printf("-- findJvmPath\n");
-
-  // first see if explicitly specified in config.props
-  const char* prop = getProp(sysProps, "java.jvm");
-  if (prop != NULL)
-  {
-    if (debug) printf("--   java.jvm = %s\n", prop);
-    sprintf(jvmPath, prop);
-    return 0;
-  }
-
-  // query registry to get current Java version
-  const char* jreKey = "SOFTWARE\\JavaSoft\\Java Runtime Environment";
-  char curVer[MAX_PATH];
-  if (readRegistry(jreKey, "CurrentVersion", curVer, sizeof(curVer))) return -1;
-  if (debug) printf("--   registry query: CurrentVersion = %s\n", curVer);
-
-  // use curVer to get default jvm.dll to use
-  char jvmKey[MAX_PATH];
-  sprintf(jvmKey, "%s\\%s", jreKey, curVer);
-  if (readRegistry(jvmKey, "RuntimeLib", jvmPath, sizeof(jvmPath))) return -1;
-  if (debug) printf("--   registry query: RuntimeLib = %s\n", jvmPath);
-
-  return 0;
-}
-
-//////////////////////////////////////////////////////////////////////////
-// Init Options
-//////////////////////////////////////////////////////////////////////////
-
-/**
- * Get the full list of options to pass to the Java VM which
- * are the required options set by the launcher, plus any additional
- * options configured in etc/sys/config.props.
- */
-int initOptions()
-{
-  if (debug) printf("-- initOptions\n");
-
-  // predefined classpath, include every jar file
-  // found in lib/java/ext/win and lib/java/ext/win
-  static char optClassPath[MAX_PATH*10];
-  sprintf(optClassPath, "-Djava.class.path=%s\\lib\\java\\sys.jar", fanHome);
-  options[nOptions++].optionString = optClassPath;
-
-  // predefined fan.home
-  static char optHome[MAX_PATH];
-  sprintf(optHome, "-Dfan.home=%s", fanHome);
-  options[nOptions++].optionString = optHome;
-
-  // user specified options from config.props
-  const char* prop = getProp(sysProps, "java.options", "");
-  char* copy = new char[strlen(prop)+1];
-  strcpy(copy, prop);
-  char* tok = strtok(copy, " ");
-  while (tok != NULL)
-  {
-    if (nOptions >= MAX_OPTIONS) break;
-    options[nOptions++].optionString = tok;
-    tok = strtok(NULL, " ");
-  }
-
-  // debug
-  if (debug)
-  {
-    printf("--   options:\n");
-    for (int i=0; iFindClass("java/lang/System");
-  jmethodID getProp = env->GetStaticMethodID(sysClass, "getProperty", "(Ljava/lang/String;)Ljava/lang/String;");
-  jstring key = env->NewStringUTF("java.version");
-  jstring jver = (jstring)env->CallStaticObjectMethod(sysClass, getProp, key);
-  if (jver == NULL)
-    return err("Cannot query java.version system property");
-  const char* ver = env->GetStringUTFChars(jver, NULL);
-  if (debug) printf("--   java.version = %s\n", ver);
-
-  // parse major, minor
-  const char* p = ver;
-  int major = 0, minor = 0;
-  while (*p != '.' && *p != '\0') { major = major*10 + (*p-'0'); p++; }
-  p++;
-  while (*p != '.' && *p != '\0') { minor = minor*10 + (*p-'0'); p++; }
-  if (debug) printf("--   parsed = %d.%d\n", major, minor);
-
-  // check that we are running 1.5
-  if (major < 1 || minor < 5)
-    return err("Fan requires Java 1.5 or greater (you have %s)", ver);
-  if (debug) printf("--   version check ok\n");
-
-  // cleanup
-  env->ReleaseStringUTFChars(key, NULL);
-  env->ReleaseStringUTFChars(jver, ver);
-
-  return 0;
-}
-
-//////////////////////////////////////////////////////////////////////////
-// Load Java
-//////////////////////////////////////////////////////////////////////////
-
-/**
- * Find and invoke the Java Fan runtime main.
- */
-int runJavaMain()
-{
-  if (debug) printf("-- runJavaMain...\n");
-
-  // figure out main
-  char temp[256];
-  sprintf(temp, "fanx/tools/%s", FAN_TOOL);
-  const char* mainClassName = (const char*)temp;
-
-  // find the main class
-  if (debug) printf("--   find class %s...\n", mainClassName);
-  jclass mainClass = env->FindClass(mainClassName);
-  if (mainClass == NULL)
-    return err("Cannot find Java main %s", mainClassName);
-
-  // find the main method
-  if (debug) printf("--   find method %s.main(String[])...\n", mainClassName);
-  jmethodID mainMethod = env->GetStaticMethodID(mainClass, "main", "([Ljava/lang/String;)V");
-  if (mainMethod == NULL)
-    return err("Cannot find %s.main(String[])", mainClassName);
-
-  // map C string args to Java string args
-  if (debug) printf("--   c args to java args...\n");
-  jstring jstr = env->NewStringUTF("");
-  jobjectArray jargs = env->NewObjectArray(fanArgc, env->FindClass("java/lang/String"), jstr);
-  for (int i=0; iSetObjectArrayElement(jargs, i, env->NewStringUTF(fanArgv[i]));
-
-  // invoke main
-  env->CallStaticVoidMethod(mainClass, mainMethod, jargs);
-
-  return 0;
-}
-
-//////////////////////////////////////////////////////////////////////////
-// Java Main
-//////////////////////////////////////////////////////////////////////////
-
-int runJava()
-{
-  if (findJvmPath())  return -1;
-  if (initOptions())  return -1;
-  if (loadJava())     return -1;
-  if (checkVersion()) return -1;
-  if (runJavaMain())  return -1;
-  return 0;
-}
\ No newline at end of file
diff --git a/com.xored.f4.fantom/fantom/src/launcher/java.h b/com.xored.f4.fantom/fantom/src/launcher/java.h
deleted file mode 100755
index 1975fb9e..00000000
--- a/com.xored.f4.fantom/fantom/src/launcher/java.h
+++ /dev/null
@@ -1,14 +0,0 @@
-//
-// Copyright (c) 2006, Brian Frank and Andy Frank
-// Licensed under the Academic Free License version 3.0
-//
-// History:
-//   27 Dec 06  Brian Frank  Creation
-//
-
-#ifndef _JAVA_H
-#define _JAVA_H
-
-extern int runJava();
-
-#endif
\ No newline at end of file
diff --git a/com.xored.f4.fantom/fantom/src/launcher/launcher.cpp b/com.xored.f4.fantom/fantom/src/launcher/launcher.cpp
deleted file mode 100755
index 298a6d32..00000000
--- a/com.xored.f4.fantom/fantom/src/launcher/launcher.cpp
+++ /dev/null
@@ -1,279 +0,0 @@
-//
-// Copyright (c) 2006, Brian Frank and Andy Frank
-// Licensed under the Academic Free License version 3.0
-//
-// History:
-//   27 Dec 06  Brian Frank  Creation
-//
-
-#include 
-#include 
-#include "launcher.h"
-#include "java.h"
-#include "dotnet.h"
-#include "utils.h"
-
-#ifndef FAN_TOOL
-#error "Must defined FAN_TOOL to be Fan, Fant, Fanp, etc"
-#endif
-
-//////////////////////////////////////////////////////////////////////////
-// TypeDefs
-//////////////////////////////////////////////////////////////////////////
-
-typedef enum { JavaRuntime, DotnetRuntime } Runtime;
-
-//////////////////////////////////////////////////////////////////////////
-// Globals
-//////////////////////////////////////////////////////////////////////////
-
-const char* LAUNCHER_VERSION = "1.0.50 2-Feb-10";
-
-bool debug;                  // is debug turned on
-char fanHome[MAX_PATH];      // dir path of fan installation
-Prop* sysProps;              // {fanHome}\etc\sys\config.props
-int fanArgc;                 // argument count to pass to Fan runtime
-char** fanArgv;              // argument values to pass to Fan runtime
-Runtime runtime;             // runtime to use
-char* cmdLineRuntime = NULL; // runtime specified on cmd line
-
-
-//////////////////////////////////////////////////////////////////////////
-// Init
-//////////////////////////////////////////////////////////////////////////
-
-/**
- * Initialize the global variables for this process's environment.
- */
-int init(int argc, char** argv)
-{
-  // debug controlled by environment variable or --v argument
-  debug = getenv("fan_launcher_debug") != NULL;
-  for (int i=1; i 0; len--) if (p[len] == '\\') { p[len] = '\0'; break; }
-  for (; len > 0; len--) if (p[len] == '\\') { p[len] = '\0'; break; }
-  strcpy(fanHome, p);
-  if (debug) printf("--   fanHome = %s\n", fanHome);
-
-  // parse etc/sys/config.props
-  sprintf(p, "%s\\etc\\sys\\config.props", fanHome);
-  sysProps = readProps(p);
-  if (sysProps == NULL)
-    printf("WARN: Cannot read config.props: \"%s\"", p);
-
-  // debug props
-  if (debug)
-  {
-    printf("--   config.props:\n");
-    for (Prop* p = sysProps; p != NULL; p = p->next)
-      printf("--     %s=%s\n", p->name, p->val);
-  }
-
-  return 0;
-}
-
-//////////////////////////////////////////////////////////////////////////
-// Parse Arguments
-//////////////////////////////////////////////////////////////////////////
-
-/**
- * Parse arguments to initialize fanArgc and fanArgv, plus check for
- * arguments used by the launcher itself (prefixed via --).  Note that
- * the --v debug is handled in init(), not this method.
- */
-int parseArgs(int argc, char** argv)
-{
-  if (debug) printf("-- parseArgs\n");
-
-  fanArgc = 0;
-  fanArgv = new char*[argc];
-
-#ifdef FAN_MAIN
-  fanArgv[fanArgc++] = FAN_MAIN;
-#endif
-
-  for (int i=1; i= 3 && arg[0] == '-' && arg[1] == '-')
-    {
-      // --v (already handled in init)
-      if (strcmp(arg, "--v") == 0)
-      {
-        continue;
-      }
-
-      // --Dname=value
-      else if (arg[2] == 'D')
-      {
-        char* temp = new char[len];
-        strcpy(temp, arg+3);
-        char* name = strtok(temp, "=");
-        char* val  = strtok(NULL, "=");
-        if (val != NULL)
-        {
-          sysProps = setProp(sysProps, name, val);
-          if (debug) printf("--   override prop %s=%s\n", name, val);
-          if (strcmp(name, "runtime") == 0) cmdLineRuntime = val;
-        }
-        continue;
-      }
-    }
-
-    // pass thru to fan
-    fanArgv[fanArgc++] = arg;
-  }
-
-  if (debug)
-  {
-    printf("--   fanArgs (%d)\n", fanArgc);
-    for (int i=0; i "="  pairs separated by whitespace.  The
- * scriptUri identifies a file on the local machine in URI format
- * of a Fan script file.  The runtimeUri identifies the Fan home
- * directory of a Fan runtime installation.
- */
-int checkSubstitutes()
-{
-  // we don't check for substitutes except when running the Fan
-  // interpreter and we have at least one argument (which we
-  // assume to be the target script being run)
-  if (strcmp(FAN_TOOL, "Fan") != 0 || fanArgc < 1)
-    return 0;
-  const char* target = fanArgv[0];
-
-  // check for system prop
-  const char* prop = getProp(sysProps, "runtime.substitutes");
-  if (prop == NULL) return 0;
-
-  if (debug) printf("-- checkSubstitutes\n");
-
-  // get the full path of the script as a Fan URI
-  char targetUri[MAX_PATH];
-  targetUri[0] = '/';
-  if (GetFullPathName(target, sizeof(targetUri)-1, targetUri+1, NULL) == 0)
-    return 0;
-  for (int i=1; targetUri[i] != '\0'; ++i)
-    if (targetUri[i] == '\\') targetUri[i] = '/';
-  if (debug) printf("--   targetUri = %s\n", targetUri);
-
-  // make copy of value on heap
-  char* copy = new char[strlen(prop)+1];
-  strcpy(copy, prop);
-
-  // tokenize
-  char* scriptUri  = strtok(copy, " ");
-  char* eq         = strtok(NULL, " ");
-  char* runtimeUri = strtok(NULL, " ");
-  while (scriptUri != NULL && eq != NULL && runtimeUri != NULL)
-  {
-    if (debug) printf("--     %s = %s\n", scriptUri, runtimeUri);
-
-    // sanity check
-    if (strcmp(eq, "=") != 0)
-    {
-      err("Invalid format for sys prop \"runtime.substitutes\"\n");
-      break;
-    }
-
-    // if we found a match update fan.home and break
-    if (_stricmp(targetUri, scriptUri) == 0)
-    {
-      strcpy(fanHome, runtimeUri);
-      if (debug) printf("--   substitute fan.home = %s\n", fanHome);
-      break;
-    }
-
-    // move on to next token triple
-    scriptUri  = strtok(NULL, " ");
-    eq         = strtok(NULL, " ");
-    runtimeUri = strtok(NULL, " ");
-  }
-
-  delete [] copy;
-  return 0;
-}
-
-//////////////////////////////////////////////////////////////////////////
-// Get Runtime
-//////////////////////////////////////////////////////////////////////////
-
-/**
- * Set the runtime variable with the runtime to use.
- */
-int getRuntime()
-{
-  const char* rt = getProp(sysProps, "runtime", "java");
-
-  if (getenv("fan_runtime") != NULL)
-    rt = getenv("fan_runtime");
-
-  if (cmdLineRuntime != NULL)
-    rt = cmdLineRuntime;
-
-  if (debug) printf("-- getRuntime = %s\n", rt);
-
-  if (strcmp(rt, "java") == 0) runtime = JavaRuntime;
-  else if (strcmp(rt, "dotnet") == 0) runtime = DotnetRuntime;
-  else return err("Unknown runtime %s", rt);
-
-  // force stub apps to always use the right runtime
-  if (strcmp(FAN_TOOL, "Jstub") == 0) runtime = JavaRuntime;
-  else if (strcmp(FAN_TOOL, "Nstub") == 0) runtime = DotnetRuntime;
-
-  return 0;
-}
-
-//////////////////////////////////////////////////////////////////////////
-// Main
-//////////////////////////////////////////////////////////////////////////
-
-int main(int argc, char** argv)
-{
-  if (init(argc, argv)) return -1;
-  if (parseArgs(argc, argv)) return -1;
-  if (checkSubstitutes()) return -1;
-  if (getRuntime()) return -1;
-  switch (runtime)
-  {
-    case JavaRuntime:   return runJava();
-    case DotnetRuntime: return runDotnet();
-    default:          return err("internal error in main");
-  }
-  return 0;
-}
\ No newline at end of file
diff --git a/com.xored.f4.fantom/fantom/src/launcher/launcher.h b/com.xored.f4.fantom/fantom/src/launcher/launcher.h
deleted file mode 100755
index 7fead98f..00000000
--- a/com.xored.f4.fantom/fantom/src/launcher/launcher.h
+++ /dev/null
@@ -1,22 +0,0 @@
-//
-// Copyright (c) 2006, Brian Frank and Andy Frank
-// Licensed under the Academic Free License version 3.0
-//
-// History:
-//   27 Dec 06  Brian Frank  Creation
-//
-
-#ifndef _LAUNCHER_H
-#define _LAUNCHER_H
-
-#include 
-#include 
-#include "props.h"
-
-extern bool debug;               // is debug turned on
-extern char fanHome[MAX_PATH];   // dir path of fan installation
-extern Prop* sysProps;           // {fanHome}/etc/sys/config.props
-extern int fanArgc;              // argument count to pass to Fan runtime
-extern char** fanArgv;           // argument values to pass to Fan runtime
-
-#endif
\ No newline at end of file
diff --git a/com.xored.f4.fantom/fantom/src/launcher/props.cpp b/com.xored.f4.fantom/fantom/src/launcher/props.cpp
deleted file mode 100755
index 423a97f7..00000000
--- a/com.xored.f4.fantom/fantom/src/launcher/props.cpp
+++ /dev/null
@@ -1,314 +0,0 @@
-//
-// Copyright (c) 2006, Brian Frank and Andy Frank
-// Licensed under the Academic Free License version 3.0
-//
-// History:
-//   27 Dec 06  Brian Frank  Creation
-//
-
-#include 
-#include 
-#include 
-#include "props.h"
-
-//////////////////////////////////////////////////////////////////////////
-// Char Stream
-//////////////////////////////////////////////////////////////////////////
-
-// push back for read
-int unread = 0;
-
-/**
- * Read next character from stream or return EOF
- */
-int readChar(FILE* fp)
-{
-  if (unread != 0)
-  {
-    int c = unread;
-    unread = 0;
-    return c;
-  }
-  else
-  {
-    return fgetc(fp);
-  }
-}
-
-/**
- * Pushback a character to reuse for next readChar()
- */
-void unreadChar(int ch)
-{
-  assert(unread == 0);
-  unread = ch;
-}
-
-//////////////////////////////////////////////////////////////////////////
-// Utils
-//////////////////////////////////////////////////////////////////////////
-
-/**
- * Convert a hexadecimal digit char into its numeric
- * value or return -1 on error.
- */
-int hex(int c)
-{
-  if ('0' <= c && c <= '9') return c - '0';
-  if ('a' <= c && c <= 'f') return c - 'a' + 10;
-  if ('A' <= c && c <= 'F') return c - 'A' + 10;
-  return -1;
-}
-
-/**
- * Return if specified character is whitespace.
- */
-bool isSpace(int c)
-{
-  return c == ' ' || c == '\t';
-}
-
-/**
- * Given a pointer to a string of characters, trim the leading
- * and trailing whitespace and return a copy of the string from
- * heap memory.
- */
-char* makeTrimCopy(char* s, int num)
-{
-  // trim leading/trailing whitespace
-  int start = 0;
-  int end = num;
-  while (start < end) if (isSpace(s[start])) start++; else break;
-  while (end > start) if (isSpace(s[end-1])) end--; else break;
-  s[end] = '\0';
-  s = s+start;
-
-  // make copy on heap
-  char* copy = new char[end-start+1];
-  strcpy(copy, s);
-  return copy;
-}
-
-//////////////////////////////////////////////////////////////////////////
-// Read
-//////////////////////////////////////////////////////////////////////////
-
-/**
- * Parse the specified props file according to the file format
- * specified by sys::InStream - this is pretty much a C port of
- * the Java implementation.  Return a linked list of Props or if
- * error, then print error to stdout and return NULL.
- */
-Prop* readProps(const char* filename)
-{
-  char name[512];
-  char val[4096];
-  int nameNum = 0, valNum = 0;
-  bool inVal = false;
-  int inBlockComment = 0;
-  bool inEndOfLineComment = false;
-  int c = -1, last = -1;
-  int lineNum = 1;
-  FILE* fp;
-  Prop* head = NULL;
-  Prop* tail = NULL;
-
-  fp = fopen(filename, "r");
-  if (fp == NULL)
-  {
-    printf("File not found [%s]\n", filename);
-    return NULL;
-  }
-
-  for (;;)
-  {
-    last = c;
-    c = readChar(fp);
-    if (c == EOF) break;
-
-    // end of line
-    if (c == '\n' || c == '\r')
-    {
-      inEndOfLineComment = false;
-      if (last == '\r' && c == '\n') continue;
-      char* n = makeTrimCopy(name, nameNum);
-      if (inVal)
-      {
-        char* v = makeTrimCopy(val, valNum);
-
-        Prop* p = new Prop();
-        p->name = n;
-        p->val = v;
-        p->next = NULL;
-        if (head == NULL) { head = tail = p; }
-        else { tail->next = p; tail = p; }
-
-        inVal = false;
-        nameNum = valNum = 0;
-      }
-      else if (strlen(n) > 0)
-      {
-        printf("Invalid name/value pair [%s:%d]\n", filename, lineNum);
-        return NULL;
-      }
-      lineNum++;
-      continue;
-    }
-
-    // if in comment
-    if (inEndOfLineComment) continue;
-
-    // block comment
-    if (inBlockComment > 0)
-    {
-      if (last == '/' && c == '*') inBlockComment++;
-      if (last == '*' && c == '/') inBlockComment--;
-      continue;
-    }
-
-    // equal
-    if (c == '=' && !inVal)
-    {
-      inVal = true;
-      continue;
-    }
-
-    // comment
-    if (c == '/')
-    {
-      int peek = readChar(fp);
-      if (peek < 0) break;
-      if (peek == '/') { inEndOfLineComment = true; continue; }
-      if (peek == '*') { inBlockComment++; continue; }
-      unreadChar(peek);
-    }
-
-    // escape or line continuation
-    if (c == '\\')
-    {
-      int peek = readChar(fp);
-      if (peek < 0) break;
-      else if (peek == 'n')  c = '\n';
-      else if (peek == 'r')  c = '\r';
-      else if (peek == 't')  c = '\t';
-      else if (peek == '\\') c = '\\';
-      else if (peek == '\r' || peek == '\n')
-      {
-        // line continuation
-        lineNum++;
-        if (peek == '\r')
-        {
-          peek = readChar(fp);
-          if (peek != '\n') unreadChar(peek);
-        }
-        while (true)
-        {
-          peek = readChar(fp);
-          if (peek == ' ' || peek == '\t') continue;
-          unreadChar(peek);
-          break;
-        }
-        continue;
-      }
-      else if (peek == 'u')
-      {
-        int n3 = hex(readChar(fp));
-        int n2 = hex(readChar(fp));
-        int n1 = hex(readChar(fp));
-        int n0 = hex(readChar(fp));
-        if (n3 < 0 || n2 < 0 || n1 < 0 || n0 < 0)
-        {
-          printf("Invalid hex value for \\uxxxx [%s:%d]\n", filename, lineNum);
-          return NULL;
-        }
-        c = ((n3 << 12) | (n2 << 8) | (n1 << 4) | n0);
-      }
-      else
-      {
-        printf("Invalid escape sequence [%s:%d]\n", filename, lineNum);
-        return NULL;
-      }
-    }
-
-    // normal character
-    if (inVal)
-    {
-      if (valNum+1 < sizeof(val)) val[valNum++] = c;
-    }
-    else
-    {
-      if (nameNum+1 < sizeof(name)) name[nameNum++] = c;
-    }
-  }
-
-  char* n = makeTrimCopy(name, nameNum);
-  if (inVal)
-  {
-    char* v = makeTrimCopy(val, valNum);
-
-    Prop* p = new Prop();
-    p->name = n;
-    p->val = v;
-    p->next = NULL;
-    if (head == NULL) { head = tail = p; }
-    else { tail->next = p; tail = p; }
-  }
-  else if (strlen(n) > 0)
-  {
-    printf("Invalid name/value pair [%s:%d]\n", filename, lineNum);
-    return NULL;
-  }
-
-  return head;
-}
-
-//////////////////////////////////////////////////////////////////////////
-// Get
-//////////////////////////////////////////////////////////////////////////
-
-/**
- * Get a property from the linked list or return NULL if not found.
- */
-const char* getProp(Prop* props, const char* name) { return getProp(props, name, NULL); }
-
-/**
- * Get a property from the linked list or return def if not found.
- */
-const char* getProp(Prop* props, const char* name, const char* def)
-{
-  for (Prop* p = props; p != NULL; p = p->next)
-    if (strcmp(p->name, name) == 0)
-      return p->val;
-  return def;
-}
-
-//////////////////////////////////////////////////////////////////////////
-// Set
-//////////////////////////////////////////////////////////////////////////
-
-/**
- * Set a property in the linked list or add it if not found.
- */
-Prop* setProp(Prop* props, const char* name, const char* value)
-{
-  Prop* last = NULL;
-  for (Prop* p = props; p != NULL; p = p->next)
-  {
-    if (strcmp(p->name, name) == 0)
-    {
-      p->val = value;
-      return props;
-    }
-    last = p;
-  }
-
-  Prop* p = new Prop();
-  p->name = name;
-  p->val  = value;
-  p->next = NULL;
-
-  if (last == NULL) props = p;
-  else last->next = p;
-  return props;
-}
-
diff --git a/com.xored.f4.fantom/fantom/src/launcher/props.h b/com.xored.f4.fantom/fantom/src/launcher/props.h
deleted file mode 100755
index 78ffc8bc..00000000
--- a/com.xored.f4.fantom/fantom/src/launcher/props.h
+++ /dev/null
@@ -1,28 +0,0 @@
-//
-// Copyright (c) 2006, Brian Frank and Andy Frank
-// Licensed under the Academic Free License version 3.0
-//
-// History:
-//   27 Dec 06  Brian Frank  Creation
-//
-
-#ifndef _PROPS_H
-#define _PROPS_H
-
-#include 
-
-typedef struct Prop
-{
-  const char* name;
-  const char* val;
-  struct Prop* next;
-} Prop;
-
-extern Prop* readProps(const char* filename);
-
-extern const char* getProp(Prop* props, const char* name);
-extern const char* getProp(Prop* props, const char* name, const char* def);
-
-extern Prop* setProp(Prop* props, const char* name, const char* value);
-
-#endif
\ No newline at end of file
diff --git a/com.xored.f4.fantom/fantom/src/launcher/utils.cpp b/com.xored.f4.fantom/fantom/src/launcher/utils.cpp
deleted file mode 100755
index 2b971d66..00000000
--- a/com.xored.f4.fantom/fantom/src/launcher/utils.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-//
-// Copyright (c) 2006, Brian Frank and Andy Frank
-// Licensed under the Academic Free License version 3.0
-//
-// History:
-//   28 Dec 06  Brian Frank  Creation
-//
-
-#include 
-#include 
-#include "utils.h"
-
-//////////////////////////////////////////////////////////////////////////
-// Error Utils
-//////////////////////////////////////////////////////////////////////////
-
-/**
- * Print an error message and return -1.
- */
-int err(const char* msg, const char* arg1, const char* arg2)
-{
-  printf("ERROR: ");
-  printf(msg, arg1, arg2);
-  printf("\n");
-  return -1;
-}
-int err(const char* msg, const char* arg1) { return err(msg, arg1, "ignored"); }
-int err(const char* msg) { return err(msg, "ignored", "ignored"); }
-
-//////////////////////////////////////////////////////////////////////////
-// Registry Utils
-//////////////////////////////////////////////////////////////////////////
-
-/**
- * Read a registry string from HKEY_LOCAL_MACHINE.
- */
-int readRegistry(const char* subKey, char* name, char* buf, int bufLen)
-{
-  // open key
-  HKEY hKey;
-  if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, subKey, 0, KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS)
-    return err("Cannot read registry: %s %s", subKey, name);
-
-  // query
-  int query = RegQueryValueEx(hKey, name, NULL, NULL, (LPBYTE)buf, (LPDWORD)&bufLen);
-
-  // close
-  RegCloseKey(hKey);
-
-  // return result
-  if (query != ERROR_SUCCESS)
-    return err("Cannot read registry: %s %s", subKey, name);
-
-  return 0;
-}
\ No newline at end of file
diff --git a/com.xored.f4.fantom/fantom/src/launcher/utils.h b/com.xored.f4.fantom/fantom/src/launcher/utils.h
deleted file mode 100755
index ef67cb3b..00000000
--- a/com.xored.f4.fantom/fantom/src/launcher/utils.h
+++ /dev/null
@@ -1,18 +0,0 @@
-//
-// Copyright (c) 2006, Brian Frank and Andy Frank
-// Licensed under the Academic Free License version 3.0
-//
-// History:
-//   28 Dec 06  Brian Frank  Creation
-//
-
-#ifndef _UTILS_H
-#define _UTILS_H
-
-extern int err(const char* msg, const char* arg1, const char* arg2);
-extern int err(const char* msg, const char* arg1);
-extern int err(const char* msg);
-
-extern int readRegistry(const char* subKey, char* name, char* buf, int bufLen);
-
-#endif
\ No newline at end of file
diff --git a/com.xored.f4.fantom/fantom/src/math/build.fan b/com.xored.f4.fantom/fantom/src/math/build.fan
new file mode 100644
index 00000000..27dae908
--- /dev/null
+++ b/com.xored.f4.fantom/fantom/src/math/build.fan
@@ -0,0 +1,34 @@
+#! /usr/bin/env fan
+//
+// Copyright (c) 2020, Brian Frank and Andy Frank
+// Licensed under the Academic Free License version 3.0
+//
+// History:
+//   14 Aug 20  Matthew Giannini Creation
+//
+
+using build
+
+**
+** Build: math
+**
+class Build : BuildPod
+{
+  new make()
+  {
+    podName  = "math"
+    summary  = "Math utilities and functions"
+    meta     = ["org.name":     "Fantom",
+                "org.uri":      "https://fantom.org/",
+                "proj.name":    "Fantom Core",
+                "proj.uri":     "https://fantom.org/",
+                "license.name": "Academic Free License 3.0",
+                "vcs.name":     "Git",
+                "vcs.uri":      "https://github.com/fantom-lang/fantom"]
+    depends  = ["sys 1.0",]
+    srcDirs  = [`fan/`, `test/`]
+    javaDirs = [`java/`]
+    docSrc   = true
+  }
+}
+
diff --git a/com.xored.f4.fantom/fantom/src/math/fan/Math.fan b/com.xored.f4.fantom/fantom/src/math/fan/Math.fan
new file mode 100644
index 00000000..ceb610e8
--- /dev/null
+++ b/com.xored.f4.fantom/fantom/src/math/fan/Math.fan
@@ -0,0 +1,18 @@
+//
+// Copyright (c) 2020, Brian Frank and Andy Frank
+// Licensed under the Academic Free License version 3.0
+//
+// History:
+//   11 Aug 20  Matthew Giannini  Creation
+//
+
+
+**
+** This mixin contains a set of utilities and functions for various math operations.
+**
+final class Math
+{
+  ** Create a new `Matrix` with the given number of rows and columns.
+  ** All elements of the matrix are initialized to zero.
+  static Matrix matrix(Int numRows, Int numCols) { MMatrix(numRows, numCols) }
+}
\ No newline at end of file
diff --git a/com.xored.f4.fantom/fantom/src/math/fan/Matrix.fan b/com.xored.f4.fantom/fantom/src/math/fan/Matrix.fan
new file mode 100644
index 00000000..6db57093
--- /dev/null
+++ b/com.xored.f4.fantom/fantom/src/math/fan/Matrix.fan
@@ -0,0 +1,93 @@
+//
+// Copyright (c) 2020, Brian Frank and Andy Frank
+// Licensed under the Academic Free License version 3.0
+//
+// History:
+//   11 Aug 20  Matthew Giannini  Creation
+//
+
+**
+** Interface for matrix implementations. A matrix is a rectangular array of numbers.
+**
+mixin Matrix
+{
+  ** The number of rows in the matrix.
+  abstract Int numRows()
+
+  ** The number of columns in the matrix.
+  abstract Int numCols()
+
+  ** Return true if the matrix is square.
+  abstract Bool isSquare()
+
+  ** Get the element at 'A[i,j]', where 'i' is the row index, and 'j' is
+  ** column index.
+  abstract Float get(Int i, Int j)
+
+  ** Set the element at 'A[i,j]', where 'i' is the row index, and 'j' is
+  ** column index.
+  abstract This set(Int i, Int j, Float val)
+
+  ** Set every element in the matrix to the given val.
+  abstract This fill(Float val)
+
+  ** Get the transpose of the matrix.
+  abstract Matrix transpose()
+
+  ** Computes 'x * A'.
+  abstract This multScalar(Float x)
+
+  ** Computes 'A + B' and returns a new matrix.
+  @Operator abstract Matrix plus(Matrix b)
+
+  ** Computes 'A - B' and returns a new matrix.
+  @Operator abstract Matrix minus(Matrix b)
+
+  ** Computes 'A * B' and returns a new matrix.
+  @Operator abstract Matrix mult(Matrix b)
+
+  ** Compute the determinant of the matrix. The matrix must be square.
+  abstract Float determinant()
+
+  ** Compute the cofactor of the matrix. The matrix must be square.
+  abstract Matrix cofactor()
+
+  ** Compute the inverse of the matrix.
+  abstract Matrix inverse()
+}
+
+**
+** Native implementation of the Matrix mixin.
+**
+@NoDoc final native class MMatrix : Matrix
+{
+  new make(Int numRows, Int numCols)
+
+  override Int numRows()
+
+  override Int numCols()
+
+  override Bool isSquare()
+
+  override Float get(Int i, Int j)
+
+  override This set(Int i, Int j, Float val)
+
+  override This fill(Float val)
+
+  override Matrix transpose()
+
+  override This multScalar(Float x)
+
+  @Operator override Matrix plus(Matrix b)
+
+  @Operator override Matrix minus(Matrix b)
+
+  @Operator override Matrix mult(Matrix b)
+
+  override Float determinant()
+
+  override Matrix cofactor()
+
+  override Matrix inverse()
+}
\ No newline at end of file
diff --git a/com.xored.f4.fantom/fantom/src/math/java/MMatrix.java b/com.xored.f4.fantom/fantom/src/math/java/MMatrix.java
new file mode 100644
index 00000000..df0833c5
--- /dev/null
+++ b/com.xored.f4.fantom/fantom/src/math/java/MMatrix.java
@@ -0,0 +1,331 @@
+//
+// Copyright (c) 2020, Brian Frank and Andy Frank
+// Licensed under the Academic Free License version 3.0
+//
+// History:
+//   11 Aug 20  Matthew Giannini  Creation
+//
+package fan.math;
+
+import fan.sys.*;
+import java.util.Arrays;
+
+public class MMatrix extends FanObj implements Matrix
+{
+
+//////////////////////////////////////////////////////////////////////////
+// Construction
+//////////////////////////////////////////////////////////////////////////
+
+  public static MMatrix make(final long numRows, final long numCols)
+  {
+    return new MMatrix(numRows, numCols, new double[(int)numRows * (int)numCols]);
+  }
+
+  private MMatrix(final long numRows, final long numCols, final double[] array)
+  {
+    if (numRows <= 0 || numCols <= 0)
+      throw ArgErr.make(String.format("Invalid matrix dimensions %d x %d", numRows, numCols));
+    if (array.length != numRows * numCols)
+        throw ArgErr.make("Invalid array");
+
+    this.numRows = numRows;
+    this.numCols = numCols;
+    this.array   = array;
+  }
+
+//////////////////////////////////////////////////////////////////////////
+// Fields
+//////////////////////////////////////////////////////////////////////////
+
+  /* Model the storage as 1-dimensional array */
+  private double[] array;
+
+  private final long numRows;
+  private final long numCols;
+
+  /** Utility to get the matrix in a 2-dimensional array */
+  public double[][] toArray2D()
+  {
+    double[][] arr = new double[(int)numRows][(int)numCols];
+    for (int row = 0; row < numRows; ++row)
+    {
+      for (int col = 0; col < numCols; ++col)
+      {
+        arr[row] = new double[(int)numCols];
+        System.arraycopy(array, row * (int)numCols, arr[row], 0, (int)numCols);
+      }
+    }
+    return arr;
+  }
+
+//////////////////////////////////////////////////////////////////////////
+// Matrix
+//////////////////////////////////////////////////////////////////////////
+
+  public long numRows() { return numRows; }
+  public long numCols() { return numCols; }
+
+  public boolean isSquare() { return numRows == numCols; }
+
+  public double get(final long i, final long j)
+  {
+    return array[idx(i,j)];
+  }
+
+  public MMatrix set(final long i, final long j, double val)
+  {
+    array[idx(i,j)] = val;
+    return this;
+  }
+
+  public MMatrix fill(final double val)
+  {
+    Arrays.fill(array, val);
+    return this;
+  }
+
+  private int idx(final long i, final long j) { return (int)(i * numCols + j); }
+
+  public Matrix transpose()
+  {
+    MMatrix t = new MMatrix(numCols(), numRows(), new double[array.length]);
+    for (int r = 0; r < numRows; ++r)
+    {
+      for (int c = 0; c < numCols; ++c)
+      {
+        t.set(c, r, get(r, c));
+      }
+    }
+    return t;
+  }
+
+  public Matrix multScalar(final double x)
+  {
+    for (int i = 0; i < array.length; ++i) { array[i] *= x; }
+    return this;
+  }
+
+  public Matrix plus(final Matrix b)
+  {
+    checkDimsEq(b);
+    final MMatrix m = new MMatrix(numRows, numCols, new double[array.length]);
+    for (int i = 0; i < numRows; ++i)
+    {
+      for (int j = 0; j < numCols; ++j)
+      {
+        m.set(i, j, this.get(i,j) + b.get(i,j));
+      }
+    }
+    return m;
+  }
+
+  public Matrix minus(final Matrix b)
+  {
+    checkDimsEq(b);
+    final MMatrix m = new MMatrix(numRows, numCols, new double[array.length]);
+    for (int i = 0; i < numRows; ++i)
+    {
+      for (int j = 0; j < numCols; ++j)
+      {
+        m.set(i, j, this.get(i,j) - b.get(i,j));
+      }
+    }
+    return m;
+  }
+
+  public Matrix mult(final Matrix b)
+  {
+    if (this.numCols != b.numRows())
+      throw ArgErr.make(String.format("Matrix cols don't match rows: %d != %d", numCols, numRows));
+
+    final int numRows = (int)this.numRows();
+    final int numCols = (int)b.numCols();
+    final MMatrix m = new MMatrix(numRows, numCols, new double[numRows*numCols]);
+    for (int i = 0; i < numRows; ++i)
+    {
+      for (int j = 0; j < numCols; ++j)
+      {
+        double sum = 0d;
+        for (int k = 0; k < this.numCols; ++k) { sum += get(i,k) * b.get(k,j); }
+        m.set(i, j, sum);
+      }
+    }
+    return m;
+  }
+
+  public double determinant()
+  {
+    checkSquare();
+    if (numRows == 1) return get(0,0);
+    if (numRows == 2) return (get(0,0) * get(1,1)) - (get(0,1) * get(1,0));
+    return determinantLU();
+  }
+
+  public Matrix cofactor()
+  {
+    checkSquare();
+    final double[] m = new double[array.length];
+    for (int i=0; i java.lang.Math.abs(luColj[p])) p = i;
+      }
+      if (p != j)
+      {
+        for (int k=0; k Int[1, 2, 3]
+  **
+  L findNotNull()
+
   **
   ** Return a new list containing the items for which c returns
   ** false.  If c returns true for every item, then return an
@@ -478,6 +493,19 @@ final class List
   **
   Obj?[] map(|V item, Int index->Obj?| c)
 
+  **
+  ** Convenience for `map` and `findNotNull`.  Each item is
+  ** mapped by the given function and if null is returned it is
+  ** excluded from the result.  The resulting type is based on
+  ** on the return type of c but non-nullable.  This method is
+  ** readonly safe.
+  **
+  ** Example:
+  **   list := [3, 4, 5]
+  **   list.mapIfNotNull |Int v->Int?| { v.isOdd ? 10+v : null } => [13, 15]
+  **
+  Obj[] mapNotNull(|V item, Int index->Obj?| c)
+
   **
   ** This is a combination of `map` and `flatten`.  Each item in
   ** this list is mapped to zero or more new items by the given function
@@ -492,6 +520,28 @@ final class List
   **
   Obj?[] flatMap(|V item, Int index->Obj?[]| c)
 
+  **
+  ** Group items into buckets keyed by the given function.
+  ** The result is a map of lists where the map keys are generated by
+  ** the given function.  The map values are the items which share the
+  ** same key.  The resulting map key type is determined by the
+  ** return type of c.
+  **
+  ** Example:
+  **   // group by string size
+  **   list := ["ape", "bear", "cat", "deer"]
+  **   list.groupBy |s->Int| { s.size }  =>  [3:[ape, cat], 4:[bear, deer]]
+  **
+  // TODO add for 1.0.77
+  // Obj:L groupBy(|V item, Int index->Obj| c)
+
+  **
+  ** Group by into an existing map.  This method shares the same
+  ** semantics as `groupBy` except it adds into the given map.
+  **
+  // TODO add for 1.0.77
+  // Obj:L groupByInto(Obj:L map, |V item, Int index->Obj| c)
+
   **
   ** Reduce is used to iterate through every item in the list
   ** to reduce the list into a single value called the reduction.
diff --git a/com.xored.f4.fantom/fantom/src/sys/fan/Map.fan b/com.xored.f4.fantom/fantom/src/sys/fan/Map.fan
index 1a2710d0..2fd30065 100755
--- a/com.xored.f4.fantom/fantom/src/sys/fan/Map.fan
+++ b/com.xored.f4.fantom/fantom/src/sys/fan/Map.fan
@@ -122,10 +122,14 @@ final class Map
   **
   M add(K key, V val)
 
+  ** Deprecated - use addNotNull
+  @Deprecated { msg = "Use addNotNull" }
+  M addIfNotNull(K key, V? val)
+
   **
   ** Call `add` if val is non-null otherwise do nothing.  Return this.
   **
-  M addIfNotNull(K key, V? val)
+  M addNotNull(K key, V? val)
 
   **
   ** Get the value for the specified key, or if it doesn't exist
@@ -292,6 +296,13 @@ final class Map
   **
   M findAll(|V val, K key->Bool| c)
 
+  **
+  ** Return a new map containing all the key/value pairs where
+  ** value is not null.  If this map is `ordered` or `caseInsensitive`,
+  ** then the resulting map is too.  This method is readonly safe.
+  **
+  M findNotNull()
+
   **
   ** Return a new map containing the key/value pairs for which c
   ** returns false.  If c returns true for every item, then return
@@ -346,6 +357,15 @@ final class Map
   **
   Obj:Obj? map(|V val, K key->Obj?| c)
 
+  **
+  ** Convenience for `map` and `findNotNull`.  Each item is
+  ** mapped by the given function and if null is returned it is
+  ** excluded from the result.  The resulting type is based on
+  ** on the return type of c but non-nullable.  This method is
+  ** readonly safe.
+  **
+  Obj:Obj? mapNotNull(|V val, K key->Obj?| c)
+
 //////////////////////////////////////////////////////////////////////////
 // Readonly
 //////////////////////////////////////////////////////////////////////////
diff --git a/com.xored.f4.fantom/fantom/src/sys/fan/NullErr.fan b/com.xored.f4.fantom/fantom/src/sys/fan/NullErr.fan
index e85d7f41..130dfafc 100755
--- a/com.xored.f4.fantom/fantom/src/sys/fan/NullErr.fan
+++ b/com.xored.f4.fantom/fantom/src/sys/fan/NullErr.fan
@@ -7,8 +7,10 @@
 //
 
 **
-** NullErr is a runtime exception raised when using a null reference
-** or when null is passed for a method argument which must be non-null.
+** NullErr indicates an attempt to dereference null.  It is
+** often raised when attempting to access an instance field or method
+** on a null reference.  It may also be thrown when a parameter is
+** expecting a non-nullable argument and null is passed.
 **
 const class NullErr : Err
 {
diff --git a/com.xored.f4.fantom/fantom/src/sys/fan/Pod.fan b/com.xored.f4.fantom/fantom/src/sys/fan/Pod.fan
index 3b5ed0f9..45058ccf 100755
--- a/com.xored.f4.fantom/fantom/src/sys/fan/Pod.fan
+++ b/com.xored.f4.fantom/fantom/src/sys/fan/Pod.fan
@@ -165,4 +165,16 @@ final const class Pod
   **
   Str? locale(Str name, Str? def := "pod::name")
 
+  **
+  ** Expand a set of pods to include all their recurisve dependencies.
+  ** This method is does not order them; see `orderByDepends`.
+  **
+  static Pod[] flattenDepends(Pod[] pods)
+
+  **
+  ** Order a list of pods by their dependencies.
+  ** This method does not flatten dependencies - see `flattenDepends`.
+  **
+  static Pod[] orderByDepends(Pod[] pods)
+
 }
\ No newline at end of file
diff --git a/com.xored.f4.fantom/fantom/src/sys/fan/ReadonlyErr.fan b/com.xored.f4.fantom/fantom/src/sys/fan/ReadonlyErr.fan
index 456a03ac..f79cf639 100755
--- a/com.xored.f4.fantom/fantom/src/sys/fan/ReadonlyErr.fan
+++ b/com.xored.f4.fantom/fantom/src/sys/fan/ReadonlyErr.fan
@@ -7,8 +7,8 @@
 //
 
 **
-** ReadonlyErr indicates an attempt to modify a readonly instance;
-** it is commonly used with List and Map.
+** ReadonlyErr indicates an attempt to modify a readonly instance.
+** It is commonly used with List and Map.
 **
 const class ReadonlyErr : Err
 {
diff --git a/com.xored.f4.fantom/fantom/src/sys/fan/Unsafe.fan b/com.xored.f4.fantom/fantom/src/sys/fan/Unsafe.fan
index a0542f5b..ff12a727 100755
--- a/com.xored.f4.fantom/fantom/src/sys/fan/Unsafe.fan
+++ b/com.xored.f4.fantom/fantom/src/sys/fan/Unsafe.fan
@@ -7,8 +7,7 @@
 //
 
 **
-** Unsafe is used to wrap a non-const mutable objects so that
-** it can be passed as an immutable reference.
+** Unsafe wraps a mutable object as an immutable reference.
 **
 const final class Unsafe
 {
diff --git a/com.xored.f4.fantom/fantom/src/sys/fan/Zip.fan b/com.xored.f4.fantom/fantom/src/sys/fan/Zip.fan
index 8c69bb1e..a940b0b0 100755
--- a/com.xored.f4.fantom/fantom/src/sys/fan/Zip.fan
+++ b/com.xored.f4.fantom/fantom/src/sys/fan/Zip.fan
@@ -101,12 +101,22 @@ final class Zip
   ** writing the entire zip file.  Throw UnsupportedErr if zip is not writing
   ** to an output stream.
   **
+  ** Next entry options:
+  **   - comment: Str entry comment
+  **   - crc: Int CRC-32 of the uncompressed data
+  **   - extra: Buf for extra bytes data field
+  **   - level: Int between 9 (best compression) to 0 (no compression)
+  **   - compressedSize: Int for compressed size of data
+  **   - uncompressedSize: Int for uncompressed size of data
+  **
+  ** NOTE: setting level to 0 sets method to STORE, else to DEFLATED.
+  **
   ** Examples:
   **   out := zip.writeNext(`/docs/test.txt`)
   **   out.writeLine("test")
   **   out.close
   **
-  OutStream writeNext(Uri path, DateTime modifyTime := DateTime.now)
+  OutStream writeNext(Uri path, DateTime modifyTime := DateTime.now,  [Str:Obj?]? opts := null)
 
   **
   ** Finish writing the contents of this zip file, but leave the underlying
diff --git a/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/BootEnv.java b/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/BootEnv.java
index 0034a716..917453d3 100755
--- a/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/BootEnv.java
+++ b/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/BootEnv.java
@@ -81,16 +81,17 @@ private static String initHost()
   {
     try
     {
-      return java.net.InetAddress.getLocalHost().getHostName();
+      // use environment vars first to avoid DNS calls
+      String s;
+      s = System.getenv("FAN_HOSTNAME"); if (s != null) return s;
+      s = System.getenv("HOSTNAME");     if (s != null) return s;
     }
     catch (Throwable e) {}
 
     try
     {
-      // fallbacks if DNS resolution fails
-      String s;
-      s = System.getenv("HOSTNAME");     if (s != null) return s;
-      s = System.getenv("FAN_HOSTNAME"); if (s != null) return s;
+      // this will block if the network is down
+      return java.net.InetAddress.getLocalHost().getHostName();
     }
     catch (Throwable e) {}
 
diff --git a/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/Buf.java b/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/Buf.java
index ebf29046..4cfd82e7 100755
--- a/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/Buf.java
+++ b/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/Buf.java
@@ -782,7 +782,7 @@ public byte[] constArray()
     throw UnsupportedErr.make(typeof()+".constArray");
   }
 
-  /** Implements {@link Interop#toJava(Buf)} */
+  /** Implements {@link fanx.interop.Interop#toJava(Buf)} */
   public ByteBuffer toByteBuffer()
   {
     throw UnsupportedErr.make(typeof()+".toByteBuffer");
diff --git a/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/DateTimeStr.java b/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/DateTimeStr.java
index 89c1cfe1..bde99b1a 100755
--- a/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/DateTimeStr.java
+++ b/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/DateTimeStr.java
@@ -582,12 +582,14 @@ private int parseOptDigit()
 
   private Month parseMon()
   {
+    // TODO: this does not handle all the cases yet such as and fr "janv." and zh "10月"
     StringBuilder s = new StringBuilder();
     while (pos < str.length())
     {
       int ch = str.charAt(pos);
       if ('a' <= ch && ch <= 'z') { s.append((char)ch); pos++; continue; }
       if ('A' <= ch && ch <= 'Z') { s.append((char)FanInt.lower(ch)); pos++; continue; }
+      if (Character.isAlphabetic(ch)) { s.append((char)Character.toLowerCase(ch)); pos++; continue; }
       break;
     }
     Month m = locale().monthByName(s.toString());
diff --git a/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/Env.java b/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/Env.java
index 3c4ca158..b9131be2 100755
--- a/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/Env.java
+++ b/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/Env.java
@@ -215,9 +215,9 @@ public Class loadPodClass(Pod pod)
 
   /**
    * Load the Java class representations of a Fantom type:
-   *   - Fantom class => [class]
-   *   - Fantom mixin => [interface, body class]
-   *   - Fantom Err class => [class, val class]
+   *   - Fantom class => [class]
+   *   - Fantom mixin => [interface, body class]
+   *   - Fantom Err class => [class, val class]
    * Default implementation delegates to parent.
    */
   public Class[] loadTypeClasses(ClassType t)
diff --git a/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/FanBool.java b/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/FanBool.java
index 066c5c31..f6bd516e 100755
--- a/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/FanBool.java
+++ b/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/FanBool.java
@@ -12,8 +12,8 @@
 
 /**
  * FanBoolean defines the methods for sys::Bool:
- *   sys::Bool   =>  boolean primitive
- *   sys::Bool?  =>  java.lang.Boolean
+ *   sys::Bool   =>  boolean primitive
+ *   sys::Bool?  =>  java.lang.Boolean
  */
 public final class FanBool
 {
diff --git a/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/FanClassLoader.java b/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/FanClassLoader.java
index eeb0c7bc..738bc93b 100755
--- a/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/FanClassLoader.java
+++ b/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/FanClassLoader.java
@@ -113,7 +113,7 @@ protected Class findClass(String name)
       String s = e.toString();
       if (s.contains("swt"))
       {
-        String msg = "cannot load SWT library; see http://fantom.org/doc/docTools/Setup.html#swt";
+        String msg = "cannot load SWT library; see https://fantom.org/doc/docTools/Setup.html#swt";
         System.out.println("\nERROR: " + msg + "\n");
         e = new NoClassDefFoundError(e.getMessage() + ": " + msg);
       }
diff --git a/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/FanDecimal.java b/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/FanDecimal.java
index e34921f8..5e88bf7a 100755
--- a/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/FanDecimal.java
+++ b/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/FanDecimal.java
@@ -81,7 +81,7 @@ public static Type typeof()
   public static BigDecimal div(BigDecimal self, BigDecimal x)
   {
     // From https://github.com/groovy/groovy-core/blob/master/src/main/org/codehaus/groovy/runtime/typehandling/BigDecimalMath.java
-    // as suggested by 'saltnlight5' in "Decimal operation failure?" discussion http://fantom.org/sidewalk/topic/1743
+    // as suggested by 'saltnlight5' in "Decimal operation failure?" discussion https://fantom.org/sidewalk/topic/1743
     try
     {
       return self.divide(x);
diff --git a/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/FanFloat.java b/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/FanFloat.java
index 9cd0774c..2f0f7be5 100755
--- a/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/FanFloat.java
+++ b/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/FanFloat.java
@@ -13,8 +13,8 @@
 
 /**
  * FanFloat defines the methods for sys::Float:
- *   sys::Float   =>  double primitive
- *   sys::Float?  =>  java.lang.Double
+ *   sys::Float   =>  double primitive
+ *   sys::Float?  =>  java.lang.Double
  */
 public final class FanFloat
 {
@@ -187,6 +187,13 @@ public static double max(double self, double that)
     return that;
   }
 
+  public static double clip(double self, double min, double max)
+  {
+    if (self < min) return min;
+    if (self > max) return max;
+    return self;
+  }
+
   public static double ceil(double self)
   {
     return Math.ceil(self);
diff --git a/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/FanInt.java b/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/FanInt.java
index e9dbdc3b..45972e16 100755
--- a/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/FanInt.java
+++ b/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/FanInt.java
@@ -13,8 +13,8 @@
 
 /**
  * FanInt defines the methods for sys::Int
- *   sys::Int   =>  long primitive
- *   sys::Int?  =>  java.lang.Long
+ *   sys::Int   =>  long primitive
+ *   sys::Int?  =>  java.lang.Long
  */
 public final class FanInt
 {
@@ -169,6 +169,13 @@ public static long max(long self, long that)
     return that;
   }
 
+  public static long clip(long self, long min, long max)
+  {
+    if (self < min) return min;
+    if (self > max) return max;
+    return self;
+  }
+
   public static long pow(long self, long pow)
   {
     if (pow < 0) throw ArgErr.make("pow < 0");
diff --git a/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/File.java b/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/File.java
index d8fabd2f..7b0a18a1 100755
--- a/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/File.java
+++ b/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/File.java
@@ -140,6 +140,12 @@ public boolean isEmpty()
 
   public boolean isHidden() { return false; }
 
+  public boolean isReadable() { return true; }
+
+  public boolean isWritable() { return false; }
+
+  public boolean isExecutable() { return false; }
+
   public abstract String osPath();
 
   public abstract File parent();
diff --git a/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/FileStore.java b/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/FileStore.java
index 400e56b6..531d05a8 100644
--- a/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/FileStore.java
+++ b/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/FileStore.java
@@ -19,9 +19,8 @@ public abstract class FileStore
 
   protected static void make$() {}
 
-  protected static void makeNew$(File self, Uri uri)
+  protected static void makeNew$(FileStore self)
   {
-    self.uri = uri;
   }
 
   public abstract Long totalSpace();
diff --git a/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/List.java b/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/List.java
index c349ea5d..bab89f8a 100755
--- a/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/List.java
+++ b/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/List.java
@@ -185,22 +185,15 @@ public final Object getSafe(long index, Object def)
 
   public final List getRange(Range r)
   {
-    try
-    {
-      int s = r.startIndex(size);
-      int e = r.endIndex(size);
-      int n = e - s + 1;
-      if (n < 0) throw IndexErr.make(r);
+    int s = r.startIndex(size);
+    int e = r.endIndex(size);
+    int n = e - s + 1;
+    if (n < 0) throw IndexErr.make(r);
 
-      List acc = new List(of, n);
-      acc.size = n;
-      System.arraycopy(values, s, acc.values, 0, n);
-      return acc;
-    }
-    catch (ArrayIndexOutOfBoundsException e)
-    {
-      throw IndexErr.make(r);
-    }
+    List acc = new List(of, n);
+    acc.size = n;
+    System.arraycopy(values, s, acc.values, 0, n);
+    return acc;
   }
 
   public final boolean contains(Object value)
@@ -400,7 +393,10 @@ public final List add(Object value)
     return insert(size, value);
   }
 
-  public final List addIfNotNull(Object value)
+  // deprecated
+  public final List addIfNotNull(Object value) { return addNotNull(value); }
+
+  public final List addNotNull(Object value)
   {
     if (value == null) return this;
     return add(value);
@@ -758,6 +754,18 @@ public final List findType(Type t)
     return acc;
   }
 
+  public final List findNotNull()
+  {
+    List acc = new List(of.toNonNullable(), size);
+    for (int i=0; i V = Foo
+ * ListType is the GenericType for Lists: Foo[] -> V = Foo
  */
 public class ListType
   extends GenericType
diff --git a/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/LocalFile.java b/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/LocalFile.java
index 507157c0..3731b393 100755
--- a/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/LocalFile.java
+++ b/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/LocalFile.java
@@ -10,6 +10,7 @@
 import java.io.Closeable;
 import java.io.FileDescriptor;
 import java.io.RandomAccessFile;
+import java.nio.file.Files;
 import java.nio.MappedByteBuffer;
 import java.nio.channels.FileChannel;
 import java.nio.channels.FileChannel.MapMode;
@@ -224,6 +225,12 @@ public void modified(DateTime time)
 
   public boolean isHidden() { return file.isHidden(); }
 
+  public boolean isReadable() { return Files.isReadable(file.toPath()); }
+
+  public boolean isWritable() { return Files.isWritable(file.toPath()); }
+
+  public boolean isExecutable() { return Files.isExecutable(file.toPath()); }
+
   public String osPath()
   {
     return file.getPath();
diff --git a/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/Map.java b/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/Map.java
index ceaa4e79..7f4f3113 100755
--- a/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/Map.java
+++ b/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/Map.java
@@ -71,6 +71,11 @@ public final Type typeof()
     return type;
   }
 
+  MapType type()
+  {
+    return type;
+  }
+
 //////////////////////////////////////////////////////////////////////////
 // Methods
 //////////////////////////////////////////////////////////////////////////
@@ -163,6 +168,11 @@ public final Map add(Object key, Object value)
   }
 
   public final Map addIfNotNull(Object key, Object value)
+  {
+    return addNotNull(key, value);
+  }
+
+  public final Map addNotNull(Object key, Object value)
   {
     if (value == null) return this;
     return add(key, value);
@@ -417,6 +427,23 @@ public final Map findAll(Func f)
     return acc;
   }
 
+  public final Map findNotNull()
+  {
+    Map acc = new Map(type.k, type.v.toNonNullable());
+    if (this.ordered()) acc.ordered(true);
+    if (this.caseInsensitive()) acc.caseInsensitive(true);
+    Iterator it = pairsIterator();
+    while (it.hasNext())
+    {
+      Entry e = (Entry)it.next();
+      Object key = e.getKey();
+      Object val = e.getValue();
+      if (val != null)
+        acc.set(key, val);
+    }
+    return acc;
+  }
+
   public final Map exclude(Func f)
   {
     Map acc = new Map(type);
@@ -495,6 +522,24 @@ public final Map map(Func f)
     return acc;
   }
 
+  public final Map mapNotNull(Func f)
+  {
+    Type r = f.returns();
+    if (r == Sys.VoidType) r = Sys.ObjType;
+    Map acc = new Map(type.k, r.toNonNullable());
+    if (this.ordered()) acc.ordered(true);
+    if (this.caseInsensitive()) acc.caseInsensitive(true);
+    Iterator it = pairsIterator();
+    while (it.hasNext())
+    {
+      Entry e = (Entry)it.next();
+      Object key = e.getKey();
+      Object val = e.getValue();
+      acc.addNotNull(key, f.call(val, key));
+    }
+    return acc;
+  }
+
 //////////////////////////////////////////////////////////////////////////
 // Conversion
 //////////////////////////////////////////////////////////////////////////
diff --git a/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/MapType.java b/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/MapType.java
index a7e5ec30..1511fc67 100755
--- a/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/MapType.java
+++ b/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/MapType.java
@@ -15,7 +15,7 @@
 import fanx.emit.*;
 
 /**
- * MapType is the GenericType for Maps: Foo:Bar -> K = Foo, V = Bar
+ * MapType is the GenericType for Maps: Foo:Bar -> K = Foo, V = Bar
  */
 public class MapType
   extends GenericType
diff --git a/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/Pod.java b/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/Pod.java
index 27c0218c..64a578b1 100755
--- a/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/Pod.java
+++ b/com.xored.f4.fantom/fantom/src/sys/java/fan/sys/Pod.java
@@ -460,6 +460,54 @@ public final String locale(String key, String def)
     return Env.cur().locale(this, key, def);
   }
 
+  public static List flattenDepends(List pods)
+  {
+    Map acc = new Map(Sys.StrType, Sys.PodType);
+    for (int i=0; i.$Pod.
+ * FPodEmit translates FPod fcode to Java bytecode as a class called @{code <podName>.$Pod}.
  * The pod class itself defines all the constants used by it's types.
  */
 public class FPodEmit
diff --git a/com.xored.f4.fantom/fantom/src/sys/java/fanx/emit/FTypeEmit.java b/com.xored.f4.fantom/fantom/src/sys/java/fanx/emit/FTypeEmit.java
index 40157e2f..f27aec67 100755
--- a/com.xored.f4.fantom/fantom/src/sys/java/fanx/emit/FTypeEmit.java
+++ b/com.xored.f4.fantom/fantom/src/sys/java/fanx/emit/FTypeEmit.java
@@ -146,7 +146,12 @@ protected void emit(FField f)
   {
     if ((f.flags & FConst.Storage) != 0)
     {
-      FieldEmit fe = emitField(f.name, pod.typeRef(f.type).jsig(), jflags(f.flags));
+      // make const class once fields volatile
+      int jflags = jflags(f.flags);
+      if ((f.flags & FConst.Once) != 0 && (type.flags & FConst.Const) != 0)
+        jflags |= EmitConst.VOLATILE;
+
+      FieldEmit fe = emitField(f.name, pod.typeRef(f.type).jsig(), jflags);
       FFacetEmit.emitField(fe, pod, f.attrs);
     }
   }
diff --git a/com.xored.f4.fantom/fantom/src/sys/java/fanx/fcode/FConst.java b/com.xored.f4.fantom/fantom/src/sys/java/fanx/fcode/FConst.java
index ebac310f..fd872e31 100755
--- a/com.xored.f4.fantom/fantom/src/sys/java/fanx/fcode/FConst.java
+++ b/com.xored.f4.fantom/fantom/src/sys/java/fanx/fcode/FConst.java
@@ -42,7 +42,8 @@ public interface FConst
   public static final int Storage    = 0x00010000;
   public static final int Synthetic  = 0x00020000;
   public static final int Virtual    = 0x00040000;
-  public static final int FlagsMask  = 0x0007ffff;
+  public static final int Once       = 0x00080000;
+  public static final int FlagsMask  = 0x000fffff;
 
 //////////////////////////////////////////////////////////////////////////
 // MethodVarFlags
diff --git a/com.xored.f4.fantom/fantom/src/sys/java/fanx/fcode/FTypeRef.java b/com.xored.f4.fantom/fantom/src/sys/java/fanx/fcode/FTypeRef.java
index 70930422..307da469 100755
--- a/com.xored.f4.fantom/fantom/src/sys/java/fanx/fcode/FTypeRef.java
+++ b/com.xored.f4.fantom/fantom/src/sys/java/fanx/fcode/FTypeRef.java
@@ -262,7 +262,7 @@ public boolean isPrimitiveIntLike()
 
   /**
    * If this type is represented as a Java primitive array, get the
-   * stack type of component type: int[] -> INT.
+   * stack type of component type: int[] -> INT.
    */
   public int arrayOfStackType()
   {
diff --git a/com.xored.f4.fantom/fantom/src/sys/java/fanx/interop/Interop.java b/com.xored.f4.fantom/fantom/src/sys/java/fanx/interop/Interop.java
index 1aed8bd9..ecc798a9 100755
--- a/com.xored.f4.fantom/fantom/src/sys/java/fanx/interop/Interop.java
+++ b/com.xored.f4.fantom/fantom/src/sys/java/fanx/interop/Interop.java
@@ -45,7 +45,7 @@ public static Type toFan(Class cls)
   /**
    * Convert sys::Type to java.lang.Class.  You
    * can also access this functionality using the
-   * trap operator "->toClass" on Type.
+   * trap operator "->toClass" on Type.
    */
   public static Class toJava(Type type)
   {
diff --git a/com.xored.f4.fantom/fantom/src/sys/java/fanx/tools/Fan.java b/com.xored.f4.fantom/fantom/src/sys/java/fanx/tools/Fan.java
index 600e4586..613c0773 100755
--- a/com.xored.f4.fantom/fantom/src/sys/java/fanx/tools/Fan.java
+++ b/com.xored.f4.fantom/fantom/src/sys/java/fanx/tools/Fan.java
@@ -218,7 +218,7 @@ static void cleanup()
   static void version(String progName)
   {
     println(progName);
-    println("Copyright (c) 2006-2019, Brian Frank and Andy Frank");
+    println("Copyright (c) 2006-2021, Brian Frank and Andy Frank");
     println("Licensed under the Academic Free License version 3.0");
     println("");
     println("Java Runtime:");
diff --git a/com.xored.f4.fantom/fantom/src/sys/java/fanx/util/FanUtil.java b/com.xored.f4.fantom/fantom/src/sys/java/fanx/util/FanUtil.java
index 5551df49..4588c8ae 100755
--- a/com.xored.f4.fantom/fantom/src/sys/java/fanx/util/FanUtil.java
+++ b/com.xored.f4.fantom/fantom/src/sys/java/fanx/util/FanUtil.java
@@ -120,8 +120,8 @@ public static boolean isJavaRepresentation(Type t)
 
   /**
    * Given a Fantom qname, get the Java class name:
-   *   sys::Obj  =>  java.lang.Object
-   *   foo::Bar  =>  fan.foo.Bar
+   *   sys::Obj  =>  java.lang.Object
+   *   foo::Bar  =>  fan.foo.Bar
    */
   public static String toJavaClassName(String podName, String typeName)
   {
@@ -162,9 +162,9 @@ public static String toJavaClassName(String podName, String typeName)
 
   /**
    * Given a Fantom qname, get the Java implementation class name:
-   *   sys::Obj   =>  fan.sys.FanObj
-   *   sys::Float =>  fan.sys.FanFloat
-   *   sys::Obj   =>  fan.sys.FanObj
+   *   sys::Obj   =>  fan.sys.FanObj
+   *   sys::Float =>  fan.sys.FanFloat
+   *   sys::Obj   =>  fan.sys.FanObj
    */
   public static String toJavaImplClassName(String podName, String typeName)
   {
@@ -205,8 +205,8 @@ public static String toJavaImplClassName(String podName, String typeName)
 
   /**
    * Given a Fantom qname, get the Java type signature:
-   *   sys::Obj  =>  java/lang/Object
-   *   foo::Bar  =>  fan/foo/Bar
+   *   sys::Obj  =>  java/lang/Object
+   *   foo::Bar  =>  fan/foo/Bar
    */
   public static String toJavaTypeSig(String podName, String typeName, boolean nullable)
   {
@@ -378,8 +378,8 @@ public static int toJavaStackType(Type t)
   /**
    * Given a Java type signature, return the implementation
    * class signature for methods and fields:
-   *   java/lang/Object  =>  fan/sys/FanObj
-   *   java/lang/Long    =>  fan/sys/FanInt
+   *   java/lang/Object  =>  fan/sys/FanObj
+   *   java/lang/Long    =>  fan/sys/FanInt
    * Anything returns itself.
    */
   public static String toJavaImplSig(String jsig)
diff --git a/com.xored.f4.fantom/fantom/src/sys/java/fanx/util/TypeParser.java b/com.xored.f4.fantom/fantom/src/sys/java/fanx/util/TypeParser.java
index 5e28f7bc..c4bd72f9 100755
--- a/com.xored.f4.fantom/fantom/src/sys/java/fanx/util/TypeParser.java
+++ b/com.xored.f4.fantom/fantom/src/sys/java/fanx/util/TypeParser.java
@@ -18,7 +18,7 @@
  *   x::N
  *   x::V[]
  *   x::V[x::K]
- *   |x::A, ... -> x::R|
+ *   |x::A, ... -> x::R|
  */
 public class TypeParser
 {
diff --git a/com.xored.f4.fantom/fantom/src/sys/js/fan/Decimal.js b/com.xored.f4.fantom/fantom/src/sys/js/fan/Decimal.js
index 8831902f..2c9da7fd 100755
--- a/com.xored.f4.fantom/fantom/src/sys/js/fan/Decimal.js
+++ b/com.xored.f4.fantom/fantom/src/sys/js/fan/Decimal.js
@@ -101,4 +101,9 @@ fan.sys.Decimal.toLocale = function(self, pattern, locale)
   //
   // // route to common FanNum method
   // return FanNum.toLocale(p, d, df);
+}
+
+fan.sys.Decimal.toStr = function(self)
+{
+  return fan.sys.Float.toStr(self);
 }
\ No newline at end of file
diff --git a/com.xored.f4.fantom/fantom/src/sys/js/fan/Depend.js b/com.xored.f4.fantom/fantom/src/sys/js/fan/Depend.js
index 061a07fb..f07e3778 100755
--- a/com.xored.f4.fantom/fantom/src/sys/js/fan/Depend.js
+++ b/com.xored.f4.fantom/fantom/src/sys/js/fan/Depend.js
@@ -44,7 +44,7 @@ fan.sys.Depend.fromStr = function(str, checked)
 fan.sys.Depend.prototype.equals = function(obj)
 {
   if (obj instanceof fan.sys.Depend)
-    return this.toStr().equals(obj.toStr());
+    return this.toStr() == obj.toStr();
   else
     return false;
 }
diff --git a/com.xored.f4.fantom/fantom/src/sys/js/fan/Float.js b/com.xored.f4.fantom/fantom/src/sys/js/fan/Float.js
index 931c6396..401a26b4 100755
--- a/com.xored.f4.fantom/fantom/src/sys/js/fan/Float.js
+++ b/com.xored.f4.fantom/fantom/src/sys/js/fan/Float.js
@@ -140,6 +140,14 @@ fan.sys.Float.round = function(self) { return fan.sys.Float.make(Math.round(self
 fan.sys.Float.sqrt  = function(self) { return fan.sys.Float.make(Math.sqrt(self)); }
 fan.sys.Float.random = function() { return fan.sys.Float.make(Math.random()); }
 
+
+fan.sys.Float.clip = function(self, min, max)
+{
+  if (self < min) return min;
+  if (self > max) return max;
+  return self;
+}
+
 // arithmetic
 fan.sys.Float.plus     = function(a,b) { return fan.sys.Float.make(a+b); }
 fan.sys.Float.plusInt  = function(a,b) { return fan.sys.Float.make(a+b); }
diff --git a/com.xored.f4.fantom/fantom/src/sys/js/fan/Int.js b/com.xored.f4.fantom/fantom/src/sys/js/fan/Int.js
index 2a38b084..9f75438d 100755
--- a/com.xored.f4.fantom/fantom/src/sys/js/fan/Int.js
+++ b/com.xored.f4.fantom/fantom/src/sys/js/fan/Int.js
@@ -89,6 +89,13 @@ fan.sys.Int.abs = function(self)      { return self < 0 ? -self : self; }
 fan.sys.Int.min = function(self, val) { return self < val ? self : val; }
 fan.sys.Int.max = function(self, val) { return self > val ? self : val; }
 
+fan.sys.Int.clip = function(self, min, max)
+{
+  if (self < min) return min;
+  if (self > max) return max;
+  return self;
+}
+
 fan.sys.Int.isEven  = function(self) { return self % 2 == 0; }
 fan.sys.Int.isOdd   = function(self) { return self % 2 != 0; }
 fan.sys.Int.isSpace = function(self) { return self == 32 || self == 9 || self == 10 || self == 13; }
@@ -380,4 +387,4 @@ fan.sys.Int.m_GB = 1024*1024*1024;
 fan.sys.Int.localeIsUpper = function(self) { return fan.sys.Int.isUpper(self); }
 fan.sys.Int.localeIsLower = function(self) { return fan.sys.Int.isLower(self); }
 fan.sys.Int.localeUpper   = function(self) { return fan.sys.Int.upper(self); }
-fan.sys.Int.localeLower   = function(self) { return fan.sys.Int.lower(self); }
+fan.sys.Int.localeLower   = function(self) { return fan.sys.Int.lower(self); }
\ No newline at end of file
diff --git a/com.xored.f4.fantom/fantom/src/sys/js/fan/List.js b/com.xored.f4.fantom/fantom/src/sys/js/fan/List.js
index ffe78d82..b68b3df9 100755
--- a/com.xored.f4.fantom/fantom/src/sys/js/fan/List.js
+++ b/com.xored.f4.fantom/fantom/src/sys/js/fan/List.js
@@ -272,6 +272,11 @@ fan.sys.List.prototype.add = function(value)
 }
 
 fan.sys.List.prototype.addIfNotNull = function(value)
+{
+  return this.addNotNull(value);
+}
+
+fan.sys.List.prototype.addNotNull = function(value)
 {
   if (value == null) return this;
   return this.add(value);
@@ -575,6 +580,18 @@ fan.sys.List.prototype.findType = function(t)
   return acc;
 }
 
+fan.sys.List.prototype.findNotNull = function()
+{
+  var acc = fan.sys.List.make(this.m_of.toNonNullable());
+  for (var i=0; i 0)
-    {
-      params = fan.sys.MimeType.doParseParams(sub, semi+1);
-      sub = fan.sys.Str.trim(sub.substring(0, semi));
-    }
+    // check interned mime types
+    var mime = fan.sys.MimeType.byMime[s];
+    if (mime != null) return mime;
 
-    var r = new fan.sys.MimeType();
-    r.m_str = s;
-    r.m_mediaType = fan.sys.Str.lower(media);
-    r.m_subType   = fan.sys.Str.lower(sub);
-    r.m_params    = params.ro();
-    return r;
+    return fan.sys.MimeType.parseStr(s);
   }
   catch (err)
   {
@@ -69,6 +35,29 @@ fan.sys.MimeType.fromStr = function(s, checked)
   }
 }
 
+fan.sys.MimeType.parseStr = function(s)
+{
+  var slash = s.indexOf('/');
+  if (slash < 0) throw ParseErr.make(s);
+  var media = s.substring(0, slash);
+  var sub = s.substring(slash+1, s.length);
+  var params = fan.sys.MimeType.emptyParams();
+
+  var semi = sub.indexOf(';');
+  if (semi > 0)
+  {
+    params = fan.sys.MimeType.doParseParams(sub, semi+1);
+    sub = fan.sys.Str.trim(sub.substring(0, semi));
+  }
+
+  var r = new fan.sys.MimeType();
+  r.m_str = s;
+  r.m_mediaType = fan.sys.Str.lower(media);
+  r.m_subType   = fan.sys.Str.lower(sub);
+  r.m_params    = params.ro();
+  return r;
+}
+
 fan.sys.MimeType.parseParams = function(s, checked)
 {
   if (checked === undefined) checked = true;
@@ -194,23 +183,13 @@ fan.sys.MimeType.unescape = function(s)
 // Extension
 //////////////////////////////////////////////////////////////////////////
 
-fan.sys.MimeType.forExt = function(s)
+fan.sys.MimeType.forExt = function(ext)
 {
-  // TODO:FIXIT - we should really be loading this somehow from ext2mime.props
-  if (s == null) return null;
+  if (ext == null) return null;
   try
   {
-    s = s.toLowerCase();
-    var m = null;
-    switch (s)
-    {
-      case "png": m = "image/png"; break;
-      case "txt": m = "text/plain; charset=utf-8"; break ;
-      case "text": m = "text/plain; charset=utf-8"; break;
-    }
-    // TODO FIXIT
-    //return (MimeType)Repo.readSymbolsCached(etcUri, Duration.oneMin).get(FanStr.lower(s));
-    return fan.sys.MimeType.fromStr(m);
+    ext = ext.toLowerCase();
+    return fan.sys.MimeType.byExt[ext];
   }
   catch (err)
   {
@@ -256,18 +235,16 @@ fan.sys.MimeType.prototype.mediaType = function() { return this.m_mediaType; }
 fan.sys.MimeType.prototype.subType = function() { return this.m_subType; }
 fan.sys.MimeType.prototype.params = function() { return this.m_params; }
 
-/*
 fan.sys.MimeType.prototype.charset = function()
 {
-  String s = (String)params().get("charset");
-  if (s == null) return Charset.utf8;
-  return Charset.fromStr(s);
+  var s = this.params().get("charset");
+  if (s == null) return fan.sys.Charset.utf8();
+  return fan.sys.Charset.fromStr(s);
 }
-*/
 
 fan.sys.MimeType.prototype.noParams = function()
 {
-  if (this.params.isEmpty()) return this;
+  if (this.params().isEmpty()) return this;
   return fan.sys.MimeType.fromStr(this.mediaType() + "/" + this.subType());
 }
 
@@ -289,6 +266,28 @@ fan.sys.MimeType.emptyParams = function()
 }
 fan.sys.MimeType.emptyQuery = null;
 
+//////////////////////////////////////////////////////////////////////////
+// Cache - Populated by mime.js generated by MimeTool
+//////////////////////////////////////////////////////////////////////////
+
+fan.sys.MimeType.cache$ = function(ext, s)
+{
+  var mime = fan.sys.MimeType.parseStr(s);
+
+  // map ext to mime
+  fan.sys.MimeType.byExt[ext] = mime;
+
+  // map mime to its string encoding
+  fan.sys.MimeType.byMime[mime.toStr()] = mime;
+
+  // also map the no-parameter mime type by its string encoding
+  mime = mime.noParams();
+  fan.sys.MimeType.byMime[mime.toStr()] = mime;
+}
+
+fan.sys.MimeType.byExt  = {}
+fan.sys.MimeType.byMime = {}
+
 //////////////////////////////////////////////////////////////////////////
 // Predefined
 //////////////////////////////////////////////////////////////////////////
diff --git a/com.xored.f4.fantom/fantom/src/sys/js/fan/Type.js b/com.xored.f4.fantom/fantom/src/sys/js/fan/Type.js
index b22e04e4..54c3d9be 100755
--- a/com.xored.f4.fantom/fantom/src/sys/js/fan/Type.js
+++ b/com.xored.f4.fantom/fantom/src/sys/js/fan/Type.js
@@ -447,7 +447,13 @@ fan.sys.Type.prototype.doReflect = function()
   var nameToIndex = {};   // String -> Int
 
   // merge in base class and mixin classes
-  for (var i=0; i> c / 4).toString(16);
+    });
+  }
+  return fan.sys.Uuid.fromStr(uuid);
 }
 
 fan.sys.Uuid.makeStr = function(a, b, c, d, e)
diff --git a/com.xored.f4.fantom/fantom/src/sys/js/fan/staticInit.js b/com.xored.f4.fantom/fantom/src/sys/js/fan/staticInit.js
index 0d8cbf99..643463c6 100755
--- a/com.xored.f4.fantom/fantom/src/sys/js/fan/staticInit.js
+++ b/com.xored.f4.fantom/fantom/src/sys/js/fan/staticInit.js
@@ -106,20 +106,6 @@ fan.sys.UriPodBase = "/pod/"; // TODO
 //
 fan.sys.Locale.m_en = fan.sys.Locale.fromStr("en")
 
-//
-// MimeType
-//
-fan.sys.MimeType.m_imagePng  = fan.sys.MimeType.predefined("image", "png");
-fan.sys.MimeType.m_imageGif  = fan.sys.MimeType.predefined("image", "gif");
-fan.sys.MimeType.m_imageJpeg = fan.sys.MimeType.predefined("image", "jpeg");
-fan.sys.MimeType.m_textPlain = fan.sys.MimeType.predefined("text", "plain");
-fan.sys.MimeType.m_textHtml  = fan.sys.MimeType.predefined("text", "html");
-fan.sys.MimeType.m_textXml   = fan.sys.MimeType.predefined("text", "xml");
-fan.sys.MimeType.m_textPlainUtf8 = fan.sys.MimeType.predefined("text", "plain", "charset=utf-8");
-fan.sys.MimeType.m_textHtmlUtf8  = fan.sys.MimeType.predefined("text", "html", "charset=utf-8");
-fan.sys.MimeType.m_textXmlUtf8   = fan.sys.MimeType.predefined("text", "xml", "charset=utf-8");
-fan.sys.MimeType.m_dir       = fan.sys.MimeType.predefined("x-directory", "normal");
-
 //
 // LogLevel
 //
diff --git a/com.xored.f4.fantom/fantom/src/sys/js/fanx/ObjDecoder.js b/com.xored.f4.fantom/fantom/src/sys/js/fanx/ObjDecoder.js
index 15ceba05..ce1b72c6 100755
--- a/com.xored.f4.fantom/fantom/src/sys/js/fanx/ObjDecoder.js
+++ b/com.xored.f4.fantom/fantom/src/sys/js/fanx/ObjDecoder.js
@@ -447,6 +447,10 @@ fanx_ObjDecoder.prototype.readMap = function(mapType, firstKey)
     ? fan.sys.Map.make(fan.sys.Obj.$type, fan.sys.Obj.$type.toNullable())
     : fan.sys.Map.make(mapType);
 
+  // we don't encode whether the original map was ordered or not,
+  // so assume it was to ensure map is still ordered after decode
+  map.ordered$(true);
+
   // finish first pair
   this.consume(fanx_Token.COLON, "Expected ':'");
   map.set(firstKey, this.$readObj(null, null, false));
@@ -545,7 +549,10 @@ fanx_ObjDecoder.prototype.readType = function(lbracket)
   if (this.curt == fanx_Token.COLON)
   {
     this.consume();
-    t = new fan.sys.MapType(t, this.readType());
+    var lbracket2 = this.curt == fanx_Token.LBRACKET;
+    if (lbracket2) this.consume();
+    t = new fan.sys.MapType(t, this.readType(lbracket2));
+    if (lbracket2) this.consume(fanx_Token.RBRACKET, "Expected closeing ']'");
   }
   while (this.curt == fanx_Token.LRBRACKET)
   {
diff --git a/com.xored.f4.fantom/fantom/src/sys/locale/nl.props b/com.xored.f4.fantom/fantom/src/sys/locale/nl.props
index 995dac2c..2bfedaea 100755
--- a/com.xored.f4.fantom/fantom/src/sys/locale/nl.props
+++ b/com.xored.f4.fantom/fantom/src/sys/locale/nl.props
@@ -6,7 +6,7 @@ decimal=#.0##
 int=#
 numDecimal=,
 numGrouping=.
-weekdayStart=sun
+weekdayStart=mon
 janFull=januari
 febFull=februari
 marFull=maart
diff --git a/com.xored.f4.fantom/fantom/src/testCompiler/build.fan b/com.xored.f4.fantom/fantom/src/testCompiler/build.fan
index b920bc82..a387c124 100755
--- a/com.xored.f4.fantom/fantom/src/testCompiler/build.fan
+++ b/com.xored.f4.fantom/fantom/src/testCompiler/build.fan
@@ -19,12 +19,12 @@ class Build : BuildPod
     podName = "testCompiler"
     summary = "Test suite for compiler"
     meta    = ["org.name":     "Fantom",
-               "org.uri":      "http://fantom.org/",
+               "org.uri":      "https://fantom.org/",
                "proj.name":    "Fantom Core",
-               "proj.uri":     "http://fantom.org/",
+               "proj.uri":     "https://fantom.org/",
                "license.name": "Academic Free License 3.0",
-               "vcs.name":     "Mercurial",
-               "vcs.uri":      "https://bitbucket.org/fantom/fan-1.0/"]
+               "vcs.name":     "Git",
+               "vcs.uri":      "https://github.com/fantom-lang/fantom"]
     depends = ["sys 1.0", "compiler 1.0", "concurrent 1.0"]
     srcDirs = [`fan/`]
     docApi  = false
diff --git a/com.xored.f4.fantom/fantom/src/testCompiler/fan/CheckErrorsTest.fan b/com.xored.f4.fantom/fantom/src/testCompiler/fan/CheckErrorsTest.fan
index 685b2491..a23f6b6a 100755
--- a/com.xored.f4.fantom/fantom/src/testCompiler/fan/CheckErrorsTest.fan
+++ b/com.xored.f4.fantom/fantom/src/testCompiler/fan/CheckErrorsTest.fan
@@ -631,7 +631,7 @@ class CheckErrorsTest : CompilerTest
         const [Num:Duration]? ok1   // ok
         const [Num:Str[][]]? ok2    // ok
 
-        once Int p() { return 3 }  // 30
+        once Int p() { return 3 }  // 30 ok now
       }
 
       class Bar {}
@@ -695,7 +695,9 @@ class CheckErrorsTest : CompilerTest
 
          1,  7, "Const type 'Foo' cannot subclass non-const class 'Bar'", // further tests in testConstInheritance
         22,  3, "Const type 'Foo' cannot contain non-const field 'j'",
+        /*
         30,  3, "Const type 'Foo' cannot contain once method 'p'",
+        */
 
         34,  1, "Non-const type 'Roo' cannot subclass const class 'Foo'",
         35, 25, "Const type 'Boo' cannot contain non-const field 'x'",
@@ -1651,7 +1653,7 @@ class CheckErrorsTest : CompilerTest
          6, 12, "Cannot use This as parameter type",
          7, 12, "Cannot use Void as parameter type",
          8, 12, "Cannot use This as parameter type",
-         9, 12, "Invalid type '|->sys::This|?'",
+         9, 12, "Invalid return type 'sys::This' in func type of param 'a'",
         10, 15, "'null' is not assignable to 'sys::Void?'",
         10, 15, "Cannot use Void as local variable type",
         10, 33, "Cannot call method on Void",
diff --git a/com.xored.f4.fantom/fantom/src/testCompiler/fan/CompilerTest.fan b/com.xored.f4.fantom/fantom/src/testCompiler/fan/CompilerTest.fan
index 645c49e7..da518f37 100755
--- a/com.xored.f4.fantom/fantom/src/testCompiler/fan/CompilerTest.fan
+++ b/com.xored.f4.fantom/fantom/src/testCompiler/fan/CompilerTest.fan
@@ -36,6 +36,7 @@ abstract class CompilerTest : Test
     input.mode        = CompilerInputMode.str
     input.srcStr      = src
     input.srcStrLoc   = Loc.make("Script")
+    //input.fcodeDump = true
     f?.call(input)
 
     compiler = Compiler.make(input)
diff --git a/com.xored.f4.fantom/fantom/src/testCompiler/fan/ItBlockTest.fan b/com.xored.f4.fantom/fantom/src/testCompiler/fan/ItBlockTest.fan
index 2eb144c4..9eaa967e 100755
--- a/com.xored.f4.fantom/fantom/src/testCompiler/fan/ItBlockTest.fan
+++ b/com.xored.f4.fantom/fantom/src/testCompiler/fan/ItBlockTest.fan
@@ -412,6 +412,62 @@ class ItBlockTest : CompilerTest
     verifyEq(obj->m06, Obj?["G", "H", "I"])
   }
 
+//////////////////////////////////////////////////////////////////////////
+// This Params (allow This to be used func parameters in method params)
+//////////////////////////////////////////////////////////////////////////
+
+  Void testThisParams()
+  {
+    compile(
+     """class Acme
+        {
+          Obj c1() { x1 {} }
+          Obj c2() { x2 { this } }
+          Obj c3() { x3 |t, s| { t.s + "," + s } }
+          Obj c4() { x4 |s, t| { s + "," + t.s } }
+          Obj c5() { x5 |a, b| { a.s + "," + b.s } }
+          Obj c6() { x5 |a, b| { a.s + "," + b.s } }
+
+          Obj x1(|This| f) { f(this); return this }
+          Obj x2(|This->Obj| f) { return f(this) }
+          Obj x3(|This, Str->Obj| f) { f(this, "foo") }
+          Obj x4(|Str, This->Str| f) { f("foo", this) }
+          Obj x5(|This, This->Str| f) { f(this, this) }
+          Obj x6(|This?, This?->Str| f) { f(this, this) }
+
+          Str s() { typeof.name }
+        }""")
+
+    obj := pod.types.first.make
+    verifySame(obj->c1, obj)
+    verifySame(obj->c2, obj)
+    verifyEq(obj->c3, "Acme,foo")
+    verifyEq(obj->c4, "foo,Acme")
+    verifyEq(obj->c5, "Acme,Acme")
+    verifyEq(obj->c6, "Acme,Acme")
+  }
+
+  Void testThisParamsErr()
+  {
+    verifyErrors(
+     "class Foo
+      {
+        Void x3(|->This| a) {}
+        Void x4(|->This?| b) {}
+        Void x5(|Void->Str| c) {}
+        Void x6(|Str,Void->Str| d) {}
+        Void x7(|Str,Void?->Str| e) {}
+      }
+      ",
+      [
+       3, 11, "Invalid return type 'sys::This' in func type of param 'a'",
+       4, 11, "Invalid return type 'sys::This?' in func type of param 'b'",
+       5, 11, "Invalid param type 'sys::Void' in func type of param 'c'",
+       6, 11, "Invalid param type 'sys::Void' in func type of param 'd'",
+       7, 11, "Invalid param type 'sys::Void?' in func type of param 'e'",
+      ])
+  }
+
 //////////////////////////////////////////////////////////////////////////
 // ConstErr
 //////////////////////////////////////////////////////////////////////////
diff --git a/com.xored.f4.fantom/fantom/src/testCompiler/fan/MiscTest.fan b/com.xored.f4.fantom/fantom/src/testCompiler/fan/MiscTest.fan
index 84f1cfce..3db1b1ac 100755
--- a/com.xored.f4.fantom/fantom/src/testCompiler/fan/MiscTest.fan
+++ b/com.xored.f4.fantom/fantom/src/testCompiler/fan/MiscTest.fan
@@ -604,6 +604,34 @@ class MiscTest : CompilerTest
        ])
   }
 
+//////////////////////////////////////////////////////////////////////////
+// Once Const
+//////////////////////////////////////////////////////////////////////////
+
+  Void testOnceConst()
+  {
+    compile(
+     "const class A
+      {
+        virtual once Str x() { Int.random.toStr }
+        once DateTime bad() { throw Err.make }
+      }
+
+      const class B : A
+      {
+        override Str x() { Int.random.toStr }
+      }
+      ")
+
+     a := pod.type("A").make
+     b := pod.type("B").make
+     verifySame(a->x, a->x)
+     verifyNotSame(b->x, b->x)
+     verifyErr(Err#) { a->bad }
+     verifyErr(Err#) { a->bad }
+     verifyErr(Err#) { a->bad }
+  }
+
 //////////////////////////////////////////////////////////////////////////
 // Func Types
 //////////////////////////////////////////////////////////////////////////
diff --git a/com.xored.f4.fantom/fantom/src/testDomkit/build.fan b/com.xored.f4.fantom/fantom/src/testDomkit/build.fan
index 066229d4..5923e497 100644
--- a/com.xored.f4.fantom/fantom/src/testDomkit/build.fan
+++ b/com.xored.f4.fantom/fantom/src/testDomkit/build.fan
@@ -19,12 +19,12 @@ class Build : BuildPod
     podName = "testDomkit"
     summary = "Domkit Test Framework"
     meta    = ["org.name":     "Fantom",
-               "org.uri":      "http://fantom.org/",
+               "org.uri":      "https://fantom.org/",
                "proj.name":    "Fantom Core",
-               "proj.uri":     "http://fantom.org/",
+               "proj.uri":     "https://fantom.org/",
                "license.name": "Academic Free License 3.0",
-               "vcs.name":     "Mercurial",
-               "vcs.uri":      "https://bitbucket.org/fantom/fan-1.0/"]
+               "vcs.name":     "Git",
+               "vcs.uri":      "https://github.com/fantom-lang/fantom"]
     depends = ["sys 1.0",
                "concurrent 1.0",
                "util 1.0",
diff --git a/com.xored.f4.fantom/fantom/src/testDomkit/fan/Main.fan b/com.xored.f4.fantom/fantom/src/testDomkit/fan/Main.fan
index d7537ca6..0d92e0cb 100644
--- a/com.xored.f4.fantom/fantom/src/testDomkit/fan/Main.fan
+++ b/com.xored.f4.fantom/fantom/src/testDomkit/fan/Main.fan
@@ -32,10 +32,20 @@ class Main : AbstractMain
 
 const class DomkitTestMod : WebMod
 {
-  new make(|This| f) { f(this) }
+  new make(|This| f)
+  {
+    f(this)
+    pods := [typeof.pod]
+    this.jsPack  = FilePack(FilePack.toAppJsFiles(pods))
+    this.cssPack = FilePack(FilePack.toAppCssFiles(pods))
+  }
 
   const Bool useSampleCss := false
 
+  const FilePack jsPack
+
+  const FilePack cssPack
+
   override Void onService()
   {
     n := req.modRel.path.first
@@ -43,9 +53,9 @@ const class DomkitTestMod : WebMod
     {
       case null:       onIndex
       case "test":     onTest
+      case "app.js":   jsPack.onService
+      case "app.css":  cssPack.onService
       case "pod":      onPod
-      case "units.js": onUnits
-      case "tz.js":    onTz
       default:         res.sendErr(404)
     }
   }
@@ -84,17 +94,8 @@ const class DomkitTestMod : WebMod
     out.html
     out.head
       .title.w("Domkit Test").titleEnd
-      .includeCss(`/pod/domkit/res/css/domkit.css`)
-      .includeJs(`/pod/sys/sys.js`)
-      .includeJs(`/tz.js`)
-      .includeJs(`/units.js`)
-      .includeJs(`/pod/util/util.js`)
-      .includeJs(`/pod/concurrent/concurrent.js`)
-      .includeJs(`/pod/graphics/graphics.js`)
-      .includeJs(`/pod/web/web.js`)
-      .includeJs(`/pod/dom/dom.js`)
-      .includeJs(`/pod/domkit/domkit.js`)
-      .includeJs(`/pod/testDomkit/testDomkit.js`)
+      .includeCss(`/app.css`)
+      .includeJs(`/app.js`)
       .style.w(
        "html { height: 100%; }
         body {
@@ -130,18 +131,6 @@ const class DomkitTestMod : WebMod
     FileWeblet(file).onService
   }
 
-  Void onUnits()
-  {
-    res.headers["Content-Type"] = "text/javascript; charset=utf-8"
-    JsUnitDatabase().write(res.out)
-  }
-
-  Void onTz()
-  {
-    res.headers["Content-Type"] = "text/javascript; charset=utf-8"
-    res.out.writeBuf((Env.cur.homeDir + `etc/sys/tz.js`).readAllBuf)
-  }
-
   const Str sampleCss :=
  """.domkit-sel {
       background-color: #dcdcdc !important;
diff --git a/com.xored.f4.fantom/fantom/src/testJava/build.fan b/com.xored.f4.fantom/fantom/src/testJava/build.fan
index 1adf7500..1989442a 100755
--- a/com.xored.f4.fantom/fantom/src/testJava/build.fan
+++ b/com.xored.f4.fantom/fantom/src/testJava/build.fan
@@ -19,12 +19,12 @@ class Build : BuildPod
     podName = "testJava"
     summary = "Test suite for Java FFI compiler plugin"
     meta    = ["org.name":     "Fantom",
-               "org.uri":      "http://fantom.org/",
+               "org.uri":      "https://fantom.org/",
                "proj.name":    "Fantom Core",
-               "proj.uri":     "http://fantom.org/",
+               "proj.uri":     "https://fantom.org/",
                "license.name": "Academic Free License 3.0",
-               "vcs.name":     "Mercurial",
-               "vcs.uri":      "https://bitbucket.org/fantom/fan-1.0/"]
+               "vcs.name":     "Git",
+               "vcs.uri":      "https://github.com/fantom-lang/fantom"]
     depends = ["sys 1.0", "compiler 1.0", "compilerJava 1.0", "testCompiler 1.0"]
     srcDirs = [`fan/`]
     docApi  = false
diff --git a/com.xored.f4.fantom/fantom/src/testNative/build.fan b/com.xored.f4.fantom/fantom/src/testNative/build.fan
index ae1fe848..ad8810cf 100755
--- a/com.xored.f4.fantom/fantom/src/testNative/build.fan
+++ b/com.xored.f4.fantom/fantom/src/testNative/build.fan
@@ -19,12 +19,12 @@ class Build : BuildPod
     podName    = "testNative"
     summary    = "Sys natives test suite"
     meta       = ["org.name":     "Fantom",
-                  "org.uri":      "http://fantom.org/",
+                  "org.uri":      "https://fantom.org/",
                   "proj.name":    "Fantom Core",
-                  "proj.uri":     "http://fantom.org/",
+                  "proj.uri":     "https://fantom.org/",
                   "license.name": "Academic Free License 3.0",
-                  "vcs.name":     "Mercurial",
-                  "vcs.uri":      "https://bitbucket.org/fantom/fan-1.0/"]
+                  "vcs.name":     "Git",
+                  "vcs.uri":      "https://github.com/fantom-lang/fantom"]
     depends    = ["sys 1.0"]
     srcDirs    = [`fan/`]
     javaDirs   = [`java/`]
diff --git a/com.xored.f4.fantom/fantom/src/testSys/build.fan b/com.xored.f4.fantom/fantom/src/testSys/build.fan
index bef6d2eb..9fa0b180 100755
--- a/com.xored.f4.fantom/fantom/src/testSys/build.fan
+++ b/com.xored.f4.fantom/fantom/src/testSys/build.fan
@@ -19,12 +19,12 @@ class Build : BuildPod
     podName = "testSys"
     summary = "System and runtime test suite"
     meta    = ["org.name":     "Fantom",
-               "org.uri":      "http://fantom.org/",
+               "org.uri":      "https://fantom.org/",
                "proj.name":    "Fantom Core",
-               "proj.uri":     "http://fantom.org/",
+               "proj.uri":     "https://fantom.org/",
                "license.name": "Academic Free License 3.0",
-               "vcs.name":     "Mercurial",
-               "vcs.uri":      "https://bitbucket.org/fantom/fan-1.0/",
+               "vcs.name":     "Git",
+               "vcs.uri":      "https://github.com/fantom-lang/fantom",
                "testSys.foo":"got\n it \u0123"]
     depends = ["sys 1.0", "concurrent 1.0"]
     index   = [
diff --git a/com.xored.f4.fantom/fantom/src/testSys/fan/DateTimeTest.fan b/com.xored.f4.fantom/fantom/src/testSys/fan/DateTimeTest.fan
index 73f796f0..aa93023e 100755
--- a/com.xored.f4.fantom/fantom/src/testSys/fan/DateTimeTest.fan
+++ b/com.xored.f4.fantom/fantom/src/testSys/fan/DateTimeTest.fan
@@ -1232,6 +1232,23 @@ class DateTimeTest : Test
     verifyNull(Date.fromLocale("2-nomonth-1999", "D-MMMM-YYYY", false))
     verifyErr(ParseErr#) { Date.fromLocale("xyz", "YY-MM-DD") }
     verifyErr(ParseErr#) { Date.fromLocale("99-xxx-02", "YY-MMM-DD", true) }
+
+    // test different months for common locales
+    // note: fr doesn't work because the months have period after abbrevation
+    // TODO: This does not handle all the cases yet such as and fr "janv." and zh "10月";
+    // need to add fr, zh, ru, ja
+    ["de", "nl", "pt", "es", "it", "fa", "he"].each |localeStr|
+    {
+      Locale(localeStr).use
+      {
+        Month.vals.each |m|
+        {
+          dm := Date(2021, m, 01)
+          s := dm.toLocale("DD MMM YYYY")
+          verifyEq(Date.fromLocale(s, "DD MMM YYYY"), dm)
+        }
+      }
+    }
   }
 
   Void verifyDateLocale(Date d, Str pattern, Str expected)
@@ -1769,4 +1786,17 @@ class DateTimeTest : Test
     }
   }
 
+//////////////////////////////////////////////////////////////////////////
+// Js Float Drift
+//////////////////////////////////////////////////////////////////////////
+
+  Void testJsFloatDrift()
+  {
+    a := DateTime("2021-01-22T10:18:53.375+01:00 Madrid")
+    b := a.toTimeZone(TimeZone("New_York"))
+    verifyEq(a.ticks, b.ticks)
+    verifyEq(a.toStr, "2021-01-22T10:18:53.375+01:00 Madrid")
+    verifyEq(b.toStr, "2021-01-22T04:18:53.375-05:00 New_York")
+  }
+
 }
\ No newline at end of file
diff --git a/com.xored.f4.fantom/fantom/src/testSys/fan/FloatTest.fan b/com.xored.f4.fantom/fantom/src/testSys/fan/FloatTest.fan
index c534f76f..28a5379f 100755
--- a/com.xored.f4.fantom/fantom/src/testSys/fan/FloatTest.fan
+++ b/com.xored.f4.fantom/fantom/src/testSys/fan/FloatTest.fan
@@ -364,6 +364,15 @@ class FloatTest : Test
     verifyEq(0f.max(1f), 1f)
     verifyEq((-99.0f).max(-6666.0f), -99.0f)
 
+    // clip
+    verifyEq((3f).clip(0f, 100f), 3f)
+    verifyEq((-3f).clip(0f, 100f), 0f)
+    verifyEq((0f).clip(0f, 100f), 0f)
+    verifyEq((99f).clip(0f, 100f), 99f)
+    verifyEq((100f).clip(0f, 100f), 100f)
+    verifyEq((100.7f).clip(0f, 100f), 100f)
+    verifyEq((890.7f).clip(0f, 100f), 100f)
+
     // ceil
     verifyEq(88f.ceil, 88f)
     verifyEq(6.335f.ceil, 7f)
diff --git a/com.xored.f4.fantom/fantom/src/testSys/fan/IntTest.fan b/com.xored.f4.fantom/fantom/src/testSys/fan/IntTest.fan
index b51cc224..9e636cb8 100755
--- a/com.xored.f4.fantom/fantom/src/testSys/fan/IntTest.fan
+++ b/com.xored.f4.fantom/fantom/src/testSys/fan/IntTest.fan
@@ -320,6 +320,14 @@ class IntTest : Test
     verifyEq((-99).max(-6666), -99)
     verifyEq('a'.max(' '), 'a')
 
+    verifyEq(3.clip(0, 100), 3)
+    verifyEq(3.clip(4, 4), 4)
+    verifyEq((-3).clip(0, 100), 0)
+    verifyEq(0.clip(0, 100), 0)
+    verifyEq(100.clip(0, 100), 100)
+    verifyEq(101.clip(0, 100), 100)
+    verifyEq(201.clip(0, 100), 100)
+
     verifyEq(5.pow(0), 1)
     verifyEq(5.pow(1), 5)
     verifyEq(5.pow(2), 25)
@@ -773,4 +781,4 @@ class IntTest : Test
 
   const Bool js := Env.cur.runtime == "js"
 
-}
+}
\ No newline at end of file
diff --git a/com.xored.f4.fantom/fantom/src/testSys/fan/ListTest.fan b/com.xored.f4.fantom/fantom/src/testSys/fan/ListTest.fan
index cf9fbe35..e8cb48ac 100755
--- a/com.xored.f4.fantom/fantom/src/testSys/fan/ListTest.fan
+++ b/com.xored.f4.fantom/fantom/src/testSys/fan/ListTest.fan
@@ -372,17 +372,17 @@ class ListTest : Test
   }
 
 //////////////////////////////////////////////////////////////////////////
-// AddIfNotNull
+// AddNotNull
 //////////////////////////////////////////////////////////////////////////
 
-  Void testAddIfNotNull()
+  Void testAddNotNull()
   {
     x := Str[,]
-    verifySame(x.addIfNotNull("a"), x)
+    verifySame(x.addNotNull("a"), x)
     verifyEq(x, ["a"])
-    verifySame(x.addIfNotNull(null), x)
+    verifySame(x.addNotNull(null), x)
     verifyEq(x, ["a"])
-    verifySame(x.addIfNotNull("b"), x)
+    verifySame(x.addNotNull("b"), x)
     verifyEq(x, ["a", "b"])
   }
 
@@ -504,10 +504,10 @@ class ListTest : Test
   }
 
 //////////////////////////////////////////////////////////////////////////
-// Slicing
+// GetRange
 //////////////////////////////////////////////////////////////////////////
 
-  Void testSlicing()
+  Void testGetRange()
   {
     /* Ruby
     irb(main):001:0> a = [0, 1, 2, 3] => [0, 1, 2, 3]
@@ -537,36 +537,36 @@ class ListTest : Test
 
     list := [0, 1, 2, 3]
 
-    verifyEq(list[0..3],  [0, 1, 2, 3])
-    verifyEq(list[0..2],  [0, 1, 2])
-    verifyEq(list[0..1],  [0, 1])
-    verifyEq(list[0..0],  [0])
-    verifyEq(list[0..<0], Int[,])
-    verifyEq(list[0..<1], [0])
-    verifyEq(list[0..<2], [0, 1])
-    verifyEq(list[0..<3], [0, 1, 2])
-    verifyEq(list[0..<4], [0, 1, 2, 3])
-    verifyEq(list[1..3], [1, 2, 3])
-    verifyEq(list[1..1], [1])
-    verifyEq(list[1..-1], [1, 2, 3])
-    verifyEq(list[1..-2], [1, 2])
-    verifyEq(list[1..-3], [1])
-    verifyEq(list[1..-4], Int[,])
-    verifyEq(list[1..<-1], [1, 2])
-    verifyEq(list[1..<-2], [1])
-    verifyEq(list[1..<-3], Int[,])
-    verifyEq(list[-3..-1], [1, 2, 3])
-    verifyEq(list[-3..-2], [1, 2])
-    verifyEq(list[-3..-3], [1])
-    verifyEq(list[4..-1], Int[,])
+    verifyGetRange(list, 0..3,  [0, 1, 2, 3])
+    verifyGetRange(list, 0..2,  [0, 1, 2])
+    verifyGetRange(list, 0..1,  [0, 1])
+    verifyGetRange(list, 0..0,  [0])
+    verifyGetRange(list, 0..<0, Int[,])
+    verifyGetRange(list, 0..<1, [0])
+    verifyGetRange(list, 0..<2, [0, 1])
+    verifyGetRange(list, 0..<3, [0, 1, 2])
+    verifyGetRange(list, 0..<4, [0, 1, 2, 3])
+    verifyGetRange(list, 1..3, [1, 2, 3])
+    verifyGetRange(list, 1..1, [1])
+    verifyGetRange(list, 1..-1, [1, 2, 3])
+    verifyGetRange(list, 1..-2, [1, 2])
+    verifyGetRange(list, 1..-3, [1])
+    verifyGetRange(list, 1..-4, Int[,])
+    verifyGetRange(list, 1..<-1, [1, 2])
+    verifyGetRange(list, 1..<-2, [1])
+    verifyGetRange(list, 1..<-3, Int[,])
+    verifyGetRange(list, -3..-1, [1, 2, 3])
+    verifyGetRange(list, -3..-2, [1, 2])
+    verifyGetRange(list, -3..-3, [1])
+    verifyGetRange(list, 4..-1, Int[,])
 
     // examples
     ex := [0, 1, 2, 3]
-    verifyEq(ex[0..2], [0, 1, 2])
-    verifyEq(ex[3..3], [3])
-    verifyEq(ex[-2..-1], [2, 3])
-    verifyEq(ex[0..<2], [0, 1])
-    verifyEq(ex[1..-2], [1, 2])
+    verifyGetRange(ex, 0..2, [0, 1, 2])
+    verifyGetRange(ex, 3..3, [3])
+    verifyGetRange(ex, -2..-1, [2, 3])
+    verifyGetRange(ex, 0..<2, [0, 1])
+    verifyGetRange(ex, 1..-2, [1, 2])
 
     // errors
     verifyErr(IndexErr#) { x:=list[0..4] }
@@ -578,6 +578,21 @@ class ListTest : Test
     verifyErr(IndexErr#) { x:=list[1..5] }
   }
 
+  Void verifyGetRange(Int[] list, Range r, Int[]? expected)
+  {
+    if (expected != null)
+    {
+echo("-- $list [$r] => " + list.getRange(r) + " ?= " + expected)
+      verifyEq(list[r], expected)
+      verifyEq(list.getRange(r), expected)
+    }
+    else
+    {
+      verifyErr(IndexErr#) { x := list[r] }
+      verifyErr(IndexErr#) { x := list.getRange(r) }
+    }
+  }
+
 //////////////////////////////////////////////////////////////////////////
 // Remove
 //////////////////////////////////////////////////////////////////////////
@@ -1000,6 +1015,12 @@ class ListTest : Test
     verifyEq([null, "a", 3, "b", null, 5ms].findType(Duration#), [5ms])
     verifyEq(["a", 3, "b", 6sec, 5f].findType(Obj#), ["a", 3, "b", 6sec, 5f])
 
+    // findNonNull
+    verifyEq([1, null, 2].findNotNull, Int[1, 2])
+    verifyEq(Str?[null].findNotNull, Str[,])
+    verifyEq(Str?[null, "x", null, null, "y", null].findNotNull, Str["x", "y"])
+    verifyEq([null, 1, null, "foo"].findNotNull, Obj[1, "foo"])
+
     // exclude
     verifyEq(list.exclude|Int v, Int i->Bool| { return v % 20 == 0 }, [10, 30])
     verifyEq(list.exclude|Int v, Int i->Bool| { return i % 2 == 0 },  [10, 30, 60])
@@ -1040,6 +1061,19 @@ class ListTest : Test
     verifyEq(list.map |Int v, Int i->Bool| { return i%2==0 },  [true, false, true])
   }
 
+//////////////////////////////////////////////////////////////////////////
+// MapNotNull
+//////////////////////////////////////////////////////////////////////////
+
+  Void testMapNotNull()
+  {
+    list := [3, 4, 5]
+    verifyEq(list.mapNotNull |Int v->Int?| { v.isOdd ? 10+v : null },  Int[13, 15])
+    verifyEq(list.mapNotNull |Int v->Int?| { null },  Int[,])
+    verifyEq(list.mapNotNull |Int v->Str?| { v.toStr },  Str["3", "4", "5"])
+    verifyEq(list.mapNotNull |Int v->Str| { v.toStr },  Str["3", "4", "5"])
+  }
+
 //////////////////////////////////////////////////////////////////////////
 // FlatMap
 //////////////////////////////////////////////////////////////////////////
@@ -1053,6 +1087,28 @@ class ListTest : Test
     verifyEq(list.flatMap |v, i->Int[]| { [v, i] }, ['a', 0, 'b', 1])
   }
 
+//////////////////////////////////////////////////////////////////////////
+// GroupBy
+//////////////////////////////////////////////////////////////////////////
+
+  /* TODO add for 1.0.77
+  Void testGroupBy()
+  {
+    list := ["ape", "bear", "camel", "deer", "eel"]
+
+    verifyEq(list.groupBy { it.size }, Obj:Str[][3:["ape", "eel"], 4:["bear", "deer"], 5:["camel"]])
+    verifyEq(list.groupBy |s| { s.size }, Obj:Str[][3:["ape", "eel"], 4:["bear", "deer"], 5:["camel"]])
+    verifyEq(list.groupBy |s->Int| { s.size }, Int:Str[][3:["ape", "eel"], 4:["bear", "deer"], 5:["camel"]])
+    verifyEq(list.groupBy |Str s, Int i->Str| { i.toStr }, Str:Str[]["0":["ape"], "1":["bear"], "2":["camel"], "3":["deer"], "4":["eel"]])
+
+    map := Int:Str[][:]
+    list.groupByInto(map) { it.size }
+    verifyEq(map, Int:Str[][3:["ape", "eel"], 4:["bear", "deer"], 5:["camel"]])
+    ["fox", "goat"].groupByInto(map) |s| { s.size }
+    verifyEq(map, Int:Str[][3:["ape", "eel", "fox"], 4:["bear", "deer", "goat"], 5:["camel"]])
+  }
+  */
+
 //////////////////////////////////////////////////////////////////////////
 // Any/All
 //////////////////////////////////////////////////////////////////////////
diff --git a/com.xored.f4.fantom/fantom/src/testSys/fan/MapTest.fan b/com.xored.f4.fantom/fantom/src/testSys/fan/MapTest.fan
index 5ec60d6c..87bc8bbe 100755
--- a/com.xored.f4.fantom/fantom/src/testSys/fan/MapTest.fan
+++ b/com.xored.f4.fantom/fantom/src/testSys/fan/MapTest.fan
@@ -485,17 +485,17 @@ class MapTest : Test
   }
 
 //////////////////////////////////////////////////////////////////////////
-// AddIfNotNull
+// AddNotNull
 //////////////////////////////////////////////////////////////////////////
 
-  Void testAddIfNotNull()
+  Void testAddNotNull()
   {
     m := Str:Str[:]
-    verifySame(m.addIfNotNull("foo", null), m)
+    verifySame(m.addNotNull("foo", null), m)
     verifyEq(m, Str:Str[:])
-    verifySame(m.addIfNotNull("foo", "bar"), m)
+    verifySame(m.addNotNull("foo", "bar"), m)
     verifyEq(m, Str:Str["foo":"bar"])
-    verifySame(m.addIfNotNull("foo", null), m)
+    verifySame(m.addNotNull("foo", null), m)
     verifyEq(m, Str:Str["foo":"bar"])
   }
 
@@ -1044,6 +1044,11 @@ class MapTest : Test
     verifyEq(mx2.caseInsensitive, true)
     verifyEq(mx1["two"], 2)
     verifyEq(mx2["two"], 2)
+
+
+    // findNull
+    mc = Str:Int?["x":1, "y":2, "z":null]
+    verifyEq(mc.findNotNull, Str:Int["x":1, "y":2])
   }
 
 //////////////////////////////////////////////////////////////////////////
@@ -1114,6 +1119,38 @@ class MapTest : Test
     verifyEq(m["Foo"], "FOO")
   }
 
+
+//////////////////////////////////////////////////////////////////////////
+// MapNotNull
+//////////////////////////////////////////////////////////////////////////
+
+  Void testMapNotNull()
+  {
+    m1 := Int:Int?[1:10, 2:null, 3:30, 4:null]
+    verifyEq(m1.mapNotNull |v->Str?| { v?.toStr }, Int:Str[1:"10", 3:"30"])
+
+    // ordered
+    m2 := Int:Int[:]
+    m2.ordered = true
+    m2[1] = 'a'
+    m2[2] = 'b'
+    m2[3] = 'c'
+    m2x := m2.mapNotNull |v->Str?| { v == 'b' ? null : v.toChar.upper }
+    verifyEq(m2x.ordered, true)
+    verifyEq(m2x, Int:Str[1:"A", 3:"C"])
+
+    // case insensitive
+    m3 := Str:Int[:]
+    m3.caseInsensitive = true
+    m3["a"] = 100
+    m3["b"] = 200
+    m3["c"] = 300
+    m3x := m3.mapNotNull |v, k->Int?| { k == "b" ? null : v / 100 }
+    verifyEq(m3x.caseInsensitive, true)
+    verifyEq(m3x, ["a":1, "c":3])
+    verifyEq(m3x["C"], 3)
+  }
+
 //////////////////////////////////////////////////////////////////////////
 // AssignOps
 //////////////////////////////////////////////////////////////////////////
diff --git a/com.xored.f4.fantom/fantom/src/testSys/fan/PodTest.fan b/com.xored.f4.fantom/fantom/src/testSys/fan/PodTest.fan
index 10805d71..ee9422d8 100755
--- a/com.xored.f4.fantom/fantom/src/testSys/fan/PodTest.fan
+++ b/com.xored.f4.fantom/fantom/src/testSys/fan/PodTest.fan
@@ -127,6 +127,8 @@ class PodTest : Test
     verifyEq(f.name, "test.txt")
     verifyEq(f.size, 19)
     verifyEq(f.readAllStr, "hello world\nline 2")
+    verify(f.isReadable)
+    verifyFalse(f.isWritable)
   }
 
 //////////////////////////////////////////////////////////////////////////
@@ -155,6 +157,64 @@ class PodTest : Test
     verifySame(pod.props(`not/found`, 1ms), pod.props(`not/found`, 1ms))
   }
 
+
+//////////////////////////////////////////////////////////////////////////
+// Flatten Depends
+//////////////////////////////////////////////////////////////////////////
+
+  Void testFlattenDepends()
+  {
+    verifyFlattenDepends(
+      ["fluxText"],
+      ["sys", "concurrent", "compiler", "gfx", "fwt", "flux", "syntax", "fluxText"])
+
+    verifyFlattenDepends(
+      ["fluxText", "sys", "fwt"],
+      ["sys", "concurrent", "compiler", "gfx", "fwt", "flux", "syntax", "fluxText"])
+
+    verifyFlattenDepends(
+      ["fluxText", "webmod"],
+      ["sys", "concurrent", "compiler", "gfx", "fwt", "flux", "syntax", "fluxText", "util", "inet", "web", "webmod"])
+  }
+
+  Void verifyFlattenDepends(Str[] names, Str[] expected)
+  {
+    pods := (Pod[])names.map |n->Pod| { Pod.find(n) }
+    pods = Pod.flattenDepends(pods)
+    actual := pods.map |p->Str | { p.name }
+    verifyEq(expected.sort, actual.sort)
+  }
+
+//////////////////////////////////////////////////////////////////////////
+// Order By Depends
+//////////////////////////////////////////////////////////////////////////
+
+  Void testOrderByDepends()
+  {
+    verifyOrderByDepends(
+      ["sys"],
+      ["sys"])
+
+    verifyOrderByDepends(
+      ["concurrent", "sys"],
+      ["sys", "concurrent"])
+
+    verifyOrderByDepends(
+      ["web", "inet", "concurrent", "sys"],
+      ["sys", "concurrent", "inet", "web"])
+
+    big := ["sys", "concurrent", "gfx", "fwt", "flux", "syntax", "fluxText"]
+    10.times { verifyOrderByDepends(big.dup.shuffle, big) }
+  }
+
+  Void verifyOrderByDepends(Str[] names, Str[] expected)
+  {
+    pods := (Pod[])names.map |n->Pod| { Pod.find(n) }
+    pods = Pod.orderByDepends(pods)
+    actual := pods.map |p->Str | { p.name }
+    verifyEq(expected, actual)
+  }
+
 //////////////////////////////////////////////////////////////////////////
 // Reload
 //////////////////////////////////////////////////////////////////////////
diff --git a/com.xored.f4.fantom/fantom/src/testSys/fan/StrTest.fan b/com.xored.f4.fantom/fantom/src/testSys/fan/StrTest.fan
index c612b17a..46923764 100755
--- a/com.xored.f4.fantom/fantom/src/testSys/fan/StrTest.fan
+++ b/com.xored.f4.fantom/fantom/src/testSys/fan/StrTest.fan
@@ -310,10 +310,10 @@ class StrTest : Test
   }
 
 //////////////////////////////////////////////////////////////////////////
-// Slice
+// Get Range
 //////////////////////////////////////////////////////////////////////////
 
-  Void testSlice()
+  Void testGetRange()
   {
     /* Ruby
     irb(main):001:0> "abcd"[0..1]    => "ab"
@@ -349,48 +349,63 @@ class StrTest : Test
     // TODO: not clear how error handling should work...
 
     s := "abcd"
-    verifyEq(s[0..1],    "ab")
-    verifyEq(s[0..<0],   "")
-    verifyEq(s[0..2],    "abc")
-    verifyEq(s[0..3],    "abcd")
-    verifyEq(s[1..1],    "b")
-    verifyEq(s[1..2],    "bc")
-    verifyEq(s[1..3],    "bcd")
-    verifyEq(s[3..2],    "")
-    verifyEq(s[3..3],    "d")
-    verifyEq(s[4..-1],   "")
-    verifyEq(s[0..<1],   "a")
-    verifyEq(s[0..<2],   "ab")
-    verifyEq(s[0..<3],   "abc")
-    verifyEq(s[0..<4],   "abcd")
-    verifyEq(s[0..-1],   "abcd")
-    verifyEq(s[0..-2],   "abc")
-    verifyEq(s[0..-3],   "ab")
-    verifyEq(s[0..-4],   "a")
-    verifyEq(s[0..-5],   "")
-    verifyEq(s[0..<-1],  "abc")
-    verifyEq(s[0..<-2],  "ab")
-    verifyEq(s[0..<-3],  "a")
-    verifyEq(s[1..<-3],  "")
-    verifyEq(s[1..<-1],  "bc")
-    verifyEq(s[-3..<-1], "bc")
-    verifyEq(s[-2..<-1], "c")
-    verifyEq(s[-3..<-1], "bc")
-    verifyEq(s[-1..<-1], "")
+    verifyGetRange(s, 0..1,    "ab")
+    verifyGetRange(s, 0..<0,   "")
+    verifyGetRange(s, 0..2,    "abc")
+    verifyGetRange(s, 0..3,    "abcd")
+    verifyGetRange(s, 1..1,    "b")
+    verifyGetRange(s, 1..2,    "bc")
+    verifyGetRange(s, 1..3,    "bcd")
+    verifyGetRange(s, 3..2,    "")
+    verifyGetRange(s, 3..3,    "d")
+    verifyGetRange(s, 4..-1,   "")
+    verifyGetRange(s, 0..<1,   "a")
+    verifyGetRange(s, 0..<2,   "ab")
+    verifyGetRange(s, 0..<3,   "abc")
+    verifyGetRange(s, 0..<4,   "abcd")
+    verifyGetRange(s, 0..-1,   "abcd")
+    verifyGetRange(s, 0..-2,   "abc")
+    verifyGetRange(s, 0..-3,   "ab")
+    verifyGetRange(s, 0..-4,   "a")
+    verifyGetRange(s, 0..-5,   "")
+    verifyGetRange(s, 0..<-1,  "abc")
+    verifyGetRange(s, 0..<-2,  "ab")
+    verifyGetRange(s, 0..<-3,  "a")
+    verifyGetRange(s, 1..<-3,  "")
+    verifyGetRange(s, 1..<-1,  "bc")
+    verifyGetRange(s, -3..<-1, "bc")
+    verifyGetRange(s, -2..<-1, "c")
+    verifyGetRange(s, -3..<-1, "bc")
+    verifyGetRange(s, -1..<-1, "")
 
     // examples
-    verifyEq("abcd"[0..2],   "abc")
-    verifyEq("abcd"[3..3],   "d")
-    verifyEq("abcd"[-2..-1], "cd")
-    verifyEq("abcd"[0..<2],  "ab")
-    verifyEq("abcd"[1..-2],  "bc")
-
-
-    verifyErr(IndexErr#) { x:=s[0..4] }
-    verifyErr(IndexErr#) { x:=s[1..4] }
-    verifyErr(IndexErr#) { x:=s[3..1] }
-    verifyErr(IndexErr#) { x:=s[3..<2] }
-    verifyErr(IndexErr#) { x:=s[0..<5] }
+    verifyGetRange("abcd", 0..2,   "abc")
+    verifyGetRange("abcd", 3..3,   "d")
+    verifyGetRange("abcd", -2..-1, "cd")
+    verifyGetRange("abcd", 0..<2,  "ab")
+    verifyGetRange("abcd", 1..-2,  "bc")
+
+
+    verifyGetRange(s, 0..4, null)
+    verifyGetRange(s, 1..4, null)
+    verifyGetRange(s, 3..1, null)
+    verifyGetRange(s, 3..<2, null)
+    verifyGetRange(s, 0..<5, null)
+  }
+
+  Void verifyGetRange(Str s, Range r, Str? expected)
+  {
+    if (expected != null)
+    {
+echo("-- $s[$r] => " + s.getRange(r) + " ?= " + expected)
+      verifyEq(s[r], expected)
+      verifyEq(s.getRange(r), expected)
+    }
+    else
+    {
+      verifyErr(IndexErr#) { x := s[r] }
+      verifyErr(IndexErr#) { x := s.getRange(r) }
+    }
   }
 
 //////////////////////////////////////////////////////////////////////////
diff --git a/com.xored.f4.fantom/fantom/src/util/build.fan b/com.xored.f4.fantom/fantom/src/util/build.fan
index b53c8373..eeb969d9 100755
--- a/com.xored.f4.fantom/fantom/src/util/build.fan
+++ b/com.xored.f4.fantom/fantom/src/util/build.fan
@@ -20,12 +20,12 @@ class Build : BuildPod
     podName  = "util"
     summary  = "Utilities"
     meta     = ["org.name":     "Fantom",
-                "org.uri":      "http://fantom.org/",
+                "org.uri":      "https://fantom.org/",
                 "proj.name":    "Fantom Core",
-                "proj.uri":     "http://fantom.org/",
+                "proj.uri":     "https://fantom.org/",
                 "license.name": "Academic Free License 3.0",
-                "vcs.name":     "Mercurial",
-                "vcs.uri":      "https://bitbucket.org/fantom/fan-1.0/"]
+                "vcs.name":     "Git",
+                "vcs.uri":      "https://github.com/fantom-lang/fantom"]
     depends  = ["sys 1.0", "concurrent 1.0"]
     srcDirs  = [`fan/`, `test/`]
     javaDirs = [`java/`]
diff --git a/com.xored.f4.fantom/fantom/src/util/fan/BoolArray.fan b/com.xored.f4.fantom/fantom/src/util/fan/BoolArray.fan
index e24b8ab8..08f8f227 100755
--- a/com.xored.f4.fantom/fantom/src/util/fan/BoolArray.fan
+++ b/com.xored.f4.fantom/fantom/src/util/fan/BoolArray.fan
@@ -38,11 +38,20 @@ native final class BoolArray
   ** Negative indices are *not* supported.
   @Operator Void set(Int index, Bool val)
 
+  ** Set the value at given index and return the previous value.
+  Bool getAndSet(Int index, Bool val)
+
   ** Fill this array with the given boolean value.  If range is null
   ** then the entire array is filled, otherwise just the specified range.
   ** Return this.
   This fill(Bool val, Range? range := null)
 
+  ** Set entire array to false
+  This clear()
+
+  ** Iterate each index set to true
+  Void eachTrue(|Int index| f)
+
   ** Copy the booleans from 'that' array into this array and return this.
   This copyFrom(BoolArray that)
 
diff --git a/com.xored.f4.fantom/fantom/src/util/java/BoolArray.java b/com.xored.f4.fantom/fantom/src/util/java/BoolArray.java
index aa6af7b5..28844a0e 100755
--- a/com.xored.f4.fantom/fantom/src/util/java/BoolArray.java
+++ b/com.xored.f4.fantom/fantom/src/util/java/BoolArray.java
@@ -41,9 +41,22 @@ public final void set(long index, boolean v)
       words[i>>0x5] &= ~mask;
   }
 
+  public final boolean getAndSet(long index, boolean v)
+  {
+    int i = (int)index;
+    int mask = 1 << (i & 0x1F);
+    boolean prev = (words[i>>0x5] & mask) != 0;
+    if (v)
+      words[i>>0x5] |= mask;
+    else
+      words[i>>0x5] &= ~mask;
+    return prev;
+  }
+
   public BoolArray fill(boolean val) { return fill(val, null); }
   public BoolArray fill(boolean val, Range range)
   {
+    if (range == null && !val) return clear();
     int start, end;
     int size = (int)size();
     if (range == null) { start = 0; end = size-1; }
@@ -52,6 +65,25 @@ public BoolArray fill(boolean val, Range range)
     return this;
   }
 
+  public BoolArray clear()
+  {
+    for (int i=0; i 0) lastIsNewline = buf[-1] == '\n'
+        out.writeBuf(buf.flip, buf.remaining)
+      }
+      if (!lastIsNewline) out.writeChar('\n')
+    }
+    finally { in.close }
+  }
+
+//////////////////////////////////////////////////////////////////////////
+// JavaScript Utils
+//////////////////////////////////////////////////////////////////////////
+
+  ** Given a set of pods return a list of JavaScript files that
+  ** form a complete Fantom application:
+  **   - flatten the pods using `sys::Pod.flattenDepends`
+  **   - order them by dependencies using `sys::Pod.orderByDepends`
+  **   - insert `toEtcJsFiles` immediately after "sys.js"
+  static File[] toAppJsFiles(Pod[] pods)
+  {
+    pods = Pod.flattenDepends(pods)
+    pods = Pod.orderByDepends(pods)
+    files := toPodJsFiles(pods)
+    files.insertAll(1, toEtcJsFiles)
+    return files
+  }
+
+  ** Get the standard pod JavaScript file or null if no JS code.  The
+  ** standard location used by the Fantom JS compiler is "/{pod-name}.js"
+  static File? toPodJsFile(Pod pod)
+  {
+    pod.file(`/${pod.name}.js`, false)
+  }
+
+  ** Map a set of pods to "/{name}.js" JavaScript files.
+  ** Ignore pods that are missing a JavaScript file.
+  ** This method does *not* flatten/order the pods.
+  static File[] toPodJsFiles(Pod[] pods)
+  {
+    acc := File[,]
+    acc.capacity = pods.size
+    pods.each |pod|
+    {
+      js := toPodJsFile(pod)
+      if (js != null) acc.add(js)
+    }
+    return acc
+  }
+
+  ** Return the required sys etc files:
+  **  - add `toMimeJsFile`
+  **  - add `toUnitsJsFile`
+  **  - add `toTimezonesJsFile`
+  **  - add `toIndexPropsJsFile`
+  static File[] toEtcJsFiles()
+  {
+    [toMimeJsFile, toUnitsJsFile, toTimezonesJsFile, toIndexPropsJsFile]
+  }
+
+  ** Compile the mime type database into a Javascript file "mime.js"
+  static File toMimeJsFile()
+  {
+    buf := Buf(4096)
+    c := Type.find("compilerJs::JsExtToMime").make
+    c->write(buf.out)
+    return buf.toFile(`mime.js`)
+  }
+
+  ** Compile the unit database into a JavaScript file "unit.js"
+  static File toUnitsJsFile()
+  {
+    buf := Buf(50_000)
+    c := Type.find("compilerJs::JsUnitDatabase").make
+    c->write(buf.out)
+    return buf.toFile(`units.js`)
+  }
+
+  ** Compile the timezone database into a JavaScript file "tz.js"
+  static File toTimezonesJsFile()
+  {
+    Env.cur.homeDir + `etc/sys/tz.js`
+  }
+
+  ** Compile the indexed props database into a JavaScript file "index-props.js"
+  static File toIndexPropsJsFile(Pod[] pods := Pod.list)
+  {
+    buf := Buf(10_000)
+    c := Type.find("compilerJs::JsIndexedProps").make
+    c->write(buf.out, pods)
+    return buf.toFile(`index-props.js`)
+  }
+
+  ** Compile the locale props into a JavaScript file "{locale}.js"
+  static File toLocaleJsFile(Locale locale, Pod[] pods := Pod.list)
+  {
+    buf := Buf(1024)
+    m := Slot.findMethod("compilerJs::JsProps.writeProps")
+    path := `locale/${locale.toStr}.props`
+    pods.each |pod| { m.call(buf.out, pod, path, 1sec) }
+    return buf.toFile(`${locale}.js`)
+  }
+
+  ** Compile a list of pod JavaScript files into a single unified source
+  ** map file.  The list of files passed to this method should match
+  ** exactly the list of files used to create the corresponding JavaScript
+  ** FilePack.  If the file is the standard pod JS file, then we will include
+  ** an offset version of "{pod}.js.map" generated by the JavaScript compiler.
+  ** Otherwise if the file is another JavaScript file (such as units.js) then
+  ** we just add the appropiate offset.
+  **
+  ** The 'sourceRoot' option may be passed in to replace "/dev/{podName}"
+  ** as the root URI used to fetch source files from the server.
+  static File toPodJsMapFile(File[] files, [Str:Obj]? options := null)
+  {
+    buf := Buf(4 * 1024 * 1024)
+    m := Slot.findMethod("compilerJs::SourceMap.pack")
+    m.call(files, buf.out, options)
+    return buf.toFile(`js.map`)
+  }
+
+//////////////////////////////////////////////////////////////////////////
+// CSS Utils
+//////////////////////////////////////////////////////////////////////////
+
+  ** Given a set of pods return a list of CSS files that
+  ** form a complete Fantom application:
+  **   - flatten the pods using `sys::Pod.flattenDepends`
+  **   - order them by dependencies using `sys::Pod.orderByDepends`
+  **   - return `toPodCssFiles`
+  static File[] toAppCssFiles(Pod[] pods)
+  {
+    pods = Pod.flattenDepends(pods)
+    pods = Pod.orderByDepends(pods)
+    return toPodCssFiles(pods)
+  }
+
+  ** Map a set of pods to "/res/css/{name}.css" CSS files.
+  ** Ignore pods that are missing a CSS file.
+  ** This method does *not* flatten/order the pods.
+  static File[] toPodCssFiles(Pod[] pods)
+  {
+    acc := File[,]
+    pods.each |pod|
+    {
+      css := pod.file(`/res/css/${pod.name}.css`, false)
+      if (css != null) acc.add(css)
+    }
+    return acc
+  }
+
+//////////////////////////////////////////////////////////////////////////
+// Main
+//////////////////////////////////////////////////////////////////////////
+
+  ** Test program
+  @NoDoc static Void main(Str[] args)
+  {
+    pods := args.map |n->Pod| { Pod.find(n) }
+    mainReport(toAppJsFiles(pods))
+    mainReport(toAppCssFiles(pods))
+  }
+
+  private static Void mainReport(File[] f)
+  {
+    b := makeFiles(f)
+    gzip := b.buf.size.toLocale("B")
+    echo("$f.first.ext: $f.size files, $gzip, $b.mimeType")
+  }
+
+}
\ No newline at end of file
diff --git a/com.xored.f4.fantom/fantom/src/web/fan/FileWeblet.fan b/com.xored.f4.fantom/fantom/src/web/fan/FileWeblet.fan
index de2228de..e74a78a3 100755
--- a/com.xored.f4.fantom/fantom/src/web/fan/FileWeblet.fan
+++ b/com.xored.f4.fantom/fantom/src/web/fan/FileWeblet.fan
@@ -148,6 +148,10 @@ class FileWeblet : Weblet
     {
       if (mime.subType == "json") return true
     }
+    if (mime.mediaType == "image")
+    {
+      if (mime.subType == "svg+xml") return true
+    }
     return false
   }
 
@@ -159,12 +163,19 @@ class FileWeblet : Weblet
   ** "If-Modified-Since" modification time.
   **
   virtual protected Bool checkNotModified()
+  {
+    doCheckNotModified(req, res, etag, modified)
+  }
+
+  **
+  ** Utility for standard check modified logic
+  **
+  internal static Bool doCheckNotModified(WebReq req, WebRes res, Str etag, DateTime modified)
   {
     // check If-Match-None
     matchNone := req.headers["If-None-Match"]
     if (matchNone != null)
     {
-      etag := this.etag
       match := WebUtil.parseList(matchNone).any |Str s->Bool|
       {
         return s == etag || s == "*"
diff --git a/com.xored.f4.fantom/fantom/src/web/fan/WebClient.fan b/com.xored.f4.fantom/fantom/src/web/fan/WebClient.fan
index cd64ea19..f4d6cc71 100755
--- a/com.xored.f4.fantom/fantom/src/web/fan/WebClient.fan
+++ b/com.xored.f4.fantom/fantom/src/web/fan/WebClient.fan
@@ -260,6 +260,21 @@ class WebClient
     return Regex[,]
   }
 
+//////////////////////////////////////////////////////////////////////////
+// Authentication
+//////////////////////////////////////////////////////////////////////////
+
+  **
+  ** Authenticate request using HTTP Basic with given username
+  ** and password.
+  **
+  This authBasic(Str username, Str password)
+  {
+    enc := "${username}:${password}".toBuf.toBase64
+    reqHeaders["Authorization"] = "Basic ${enc}"
+    return this
+  }
+
 //////////////////////////////////////////////////////////////////////////
 // Get
 //////////////////////////////////////////////////////////////////////////
@@ -305,63 +320,85 @@ class WebClient
   }
 
 //////////////////////////////////////////////////////////////////////////
-// Post
+// Post/Patch
 //////////////////////////////////////////////////////////////////////////
 
   **
-  ** Make a post request to the URI with the given form data.
-  ** Set the Content-Type to application/x-www-form-urlencoded.
-  ** Upon completion the response is ready to be read.  This method
-  ** does not support the ["Expect" header]`pod-doc#expectContinue` (it
-  ** posts all form data before reading response).
+  ** Convenience for 'writeForm("POST", form).readRes'
   **
   This postForm(Str:Str form)
+  {
+    writeForm("POST", form).readRes
+  }
+
+  **
+  ** Convenience for 'writeStr("POST", content).readRes'
+  **
+  This postStr(Str content)
+  {
+    writeStr("POST", content).readRes
+  }
+
+  **
+  ** Convenience for 'writeFile("POST", file).readRes'
+  **
+  This postFile(File file)
+  {
+    writeFile("POST", file).readRes
+  }
+
+  **
+  ** Make a request with the given HTTP method to the URI with the given form data.
+  ** Set the Content-Type to application/x-www-form-urlencoded.
+  ** This method does not support the ["Expect" header]`pod-doc#expectContinue` (it
+  ** writes all form data before reading response). Should primarily be used for POST
+  ** and PATCH requests.
+  **
+  This writeForm(Str method, Str:Str form)
   {
     if (reqHeaders["Expect"] != null) throw UnsupportedErr("'Expect' header")
     body := Uri.encodeQuery(form)
-    reqMethod = "POST"
+    reqMethod = method
     reqHeaders["Content-Type"] = "application/x-www-form-urlencoded"
     reqHeaders["Content-Length"] = body.size.toStr // encoded form is ASCII
     writeReq
     reqOut.print(body).close
-    readRes
     return this
   }
 
   **
-  ** Make a post request to the URI using UTF-8 encoding of given
+  ** Make a request with the given HTTP method to the URI using UTF-8 encoding of given
   ** string.  If Content-Type is not already set, then set it
-  ** to "text/plain; charset=utf-8".  Upon completion the response
-  ** is ready to be read.  This method does not support the
-  ** ["Expect" header]`pod-doc#expectContinue` (it posts full string
-  ** before reading response).
+  ** to "text/plain; charset=utf-8".  This method does not support the
+  ** ["Expect" header]`pod-doc#expectContinue` (it writes full string
+  ** before reading response). Should primarily be used for "POST" and "PATCH"
+  ** requests.
   **
-  This postStr(Str content)
+  This writeStr(Str method, Str content)
   {
     if (reqHeaders["Expect"] != null) throw UnsupportedErr("'Expect' header")
     body := Buf().print(content).flip
-    reqMethod = "POST"
+    reqMethod = method
     ct := reqHeaders["Content-Type"]
     if (ct == null)
       reqHeaders["Content-Type"] = "text/plain; charset=utf-8"
     reqHeaders["Content-Length"] = body.size.toStr
     writeReq
     reqOut.writeBuf(body).close
-    readRes
     return this
   }
 
   **
-  ** Post a file to the URI.  If Content-Type header is not already
-  ** set, then it is set from the file extension's MIME type.  Upon
-  ** completion the response is ready to be read.  This method does
+  ** Write a file using the given HTTP method to the URI.  If Content-Type header is not already
+  ** set, then it is set from the file extension's MIME type. This method does
   ** not support the ["Expect" header]`pod-doc#expectContinue` (it
-  ** posts full file before reading response).
+  ** writes full file before reading response). Should primarily be used for "POST" and
+  ** "PATCH" requests.
   **
-  This postFile(File file)
+  This writeFile(Str method, File file)
   {
     if (reqHeaders["Expect"] != null) throw UnsupportedErr("'Expect' header")
-    reqMethod = "POST"
+    reqMethod = method
     ct := reqHeaders["Content-Type"]
     if (ct == null)
       reqHeaders["Content-Type"] = file.mimeType?.toStr ?: "application/octet-stream"
@@ -370,7 +407,6 @@ class WebClient
     writeReq
     file.in.pipe(reqOut, file.size)
     reqOut.close
-    readRes
     return this
   }
 
@@ -443,6 +479,7 @@ class WebClient
     socket.connect(IpAddr(proxy.host), proxy.port ?: 80)
     out := socket.out
     out.print("CONNECT ${reqUri.host}:${reqUri.port ?: 443} HTTP/${reqVersion}").print("\r\n")
+       .print("Host: ${reqUri.host}:${reqUri.port ?: 443}").print("\r\n")
        .print("\r\n")
     out.flush
 
@@ -471,7 +508,7 @@ class WebClient
     try
     {
       // parse status-line
-      res = in.readLine ?: throw IOErr("No response")
+      res = WebUtil.readLine(in)
       if (res.startsWith("HTTP/1.1")) resVersion = ver11
       else if (res.startsWith("HTTP/1.0")) resVersion = ver10
       else throw Err("Not HTTP")
diff --git a/com.xored.f4.fantom/fantom/src/web/fan/WebSocket.fan b/com.xored.f4.fantom/fantom/src/web/fan/WebSocket.fan
index c7fa2a76..d2f6510e 100644
--- a/com.xored.f4.fantom/fantom/src/web/fan/WebSocket.fan
+++ b/com.xored.f4.fantom/fantom/src/web/fan/WebSocket.fan
@@ -36,6 +36,7 @@ class WebSocket
     c.reqHeaders["Upgrade"] = "websocket"
     c.reqHeaders["Connection"] = "Upgrade"
     c.reqHeaders["Sec-WebSocket-Key"] = key
+    c.reqHeaders["Sec-WebSocket-Version"] = "13"
     if (headers != null) c.reqHeaders.addAll(headers)
     c.writeReq
 
diff --git a/com.xored.f4.fantom/fantom/src/web/fan/WebUtil.fan b/com.xored.f4.fantom/fantom/src/web/fan/WebUtil.fan
index 9ef52783..07d89c16 100755
--- a/com.xored.f4.fantom/fantom/src/web/fan/WebUtil.fan
+++ b/com.xored.f4.fantom/fantom/src/web/fan/WebUtil.fan
@@ -128,7 +128,7 @@ class WebUtil
       // continuation of last header field
       if (peek.isSpace && last != null)
       {
-        headers[last] += " " + in.readLine.trim
+        headers[last] += " " + WebUtil.readLine(in).trim
         continue
       }
 
@@ -371,6 +371,19 @@ class WebUtil
     return ChunkOutStream(out)
   }
 
+  **
+  ** Read line of HTTP protocol.  Raise exception if unexpected
+  ** end of stream or the line exceeds our max size.
+  **
+  @NoDoc static Str readLine(InStream in)
+  {
+    max := 65536 // 64KB
+    line := in.readLine(max)
+    if (line == null) throw IOErr("Unexpected end of stream")
+    if (line.size == max) throw IOErr("Max request line")
+    return line
+  }
+
 //////////////////////////////////////////////////////////////////////////
 // Multi-Part Forms
 //////////////////////////////////////////////////////////////////////////
@@ -385,7 +398,7 @@ class WebUtil
   static Void parseMultiPart(InStream in, Str boundary, |Str:Str headers, InStream in| cb)
   {
     boundary = "--" + boundary
-    line := in.readLine
+    line := WebUtil.readLine(in)
     if (line == boundary + "--") return
     if (line != boundary) throw IOErr("Expecting boundry line $boundary.toCode")
     while (true)
@@ -523,10 +536,10 @@ internal class ChunkInStream : InStream
       if (noMoreChunks) return false
 
       // we expect \r\n unless this is first chunk
-      if (chunkRem != -1 && !in.readLine.isEmpty) throw Err()
+      if (chunkRem != -1 && !WebUtil.readLine(in).isEmpty) throw Err()
 
       // read the next chunk status line
-      line := in.readLine
+      line := WebUtil.readLine(in)
       semi := line.index(";")
       if (semi != null) line = line[0..semi]
       chunkRem = line.trim.toInt(16)
diff --git a/com.xored.f4.fantom/fantom/src/web/test/FilePackTest.fan b/com.xored.f4.fantom/fantom/src/web/test/FilePackTest.fan
new file mode 100644
index 00000000..4c780b22
--- /dev/null
+++ b/com.xored.f4.fantom/fantom/src/web/test/FilePackTest.fan
@@ -0,0 +1,30 @@
+//
+// Copyright (c) 2020, Brian Frank and Andy Frank
+// Licensed under the Academic Free License version 3.0
+//
+// History:
+//   11 May 2020  Brian Frank  Creation
+//
+
+using concurrent
+
+**
+** FilePackTest
+**
+class FilePackTest : Test
+{
+
+  Void testPack()
+  {
+    buf := Buf()
+    FilePack.pack([
+      "a\n".toBuf.toFile(`a.txt`),
+      "b".toBuf.toFile(`b.txt`),
+      "c\n".toBuf.toFile(`c.txt`),
+      "".toBuf.toFile(`d.txt`),
+      "e".toBuf.toFile(`e.txt`),
+     ], buf.out)
+     buf = buf.toImmutable
+    verifyEq(buf.in.readAllStr, "a\nb\nc\ne\n")
+  }
+}
\ No newline at end of file
diff --git a/com.xored.f4.fantom/fantom/src/webfwt/build.fan b/com.xored.f4.fantom/fantom/src/webfwt/build.fan
index cbe8cdb4..b689e4ce 100644
--- a/com.xored.f4.fantom/fantom/src/webfwt/build.fan
+++ b/com.xored.f4.fantom/fantom/src/webfwt/build.fan
@@ -21,11 +21,11 @@ class Build : BuildPod
     podName = "webfwt"
     summary = "Web extensions to the FWT toolkit"
     meta    = ["org.name":     "Fantom",
-               "org.uri":      "http://fantom.org/",
+               "org.uri":      "https://fantom.org/",
                "proj.name":    "Fantom Core",
                "license.name": "Academic Free License 3.0",
-               "vcs.name":     "Mercurial",
-               "vcs.uri":      "https://bitbucket.org/fantom/fan-1.0/"]
+               "vcs.name":     "Git",
+               "vcs.uri":      "https://github.com/fantom-lang/fantom"]
     depends = ["sys 1.0", "gfx 1.0", "fwt 1.0", "web 1.0"]
     srcDirs = [`fan/`,
                //`fan/internal/`,
diff --git a/com.xored.f4.fantom/fantom/src/webmod/build.fan b/com.xored.f4.fantom/fantom/src/webmod/build.fan
index 78e5b7fd..5c5ee2b3 100755
--- a/com.xored.f4.fantom/fantom/src/webmod/build.fan
+++ b/com.xored.f4.fantom/fantom/src/webmod/build.fan
@@ -19,12 +19,12 @@ class Build : BuildPod
     podName = "webmod"
     summary = "Standard library of WebMods"
     meta    = ["org.name":     "Fantom",
-               "org.uri":      "http://fantom.org/",
+               "org.uri":      "https://fantom.org/",
                "proj.name":    "Fantom Core",
-               "proj.uri":     "http://fantom.org/",
+               "proj.uri":     "https://fantom.org/",
                "license.name": "Academic Free License 3.0",
-               "vcs.name":     "Mercurial",
-               "vcs.uri":      "https://bitbucket.org/fantom/fan-1.0/"]
+               "vcs.name":     "Git",
+               "vcs.uri":      "https://github.com/fantom-lang/fantom"]
     depends = ["sys 1.0", "inet 1.0", "web 1.0", "util 1.0"]
     srcDirs = [`fan/`]
     docSrc  = true
diff --git a/com.xored.f4.fantom/fantom/src/wisp/build.fan b/com.xored.f4.fantom/fantom/src/wisp/build.fan
index 8c6fd840..d666631b 100755
--- a/com.xored.f4.fantom/fantom/src/wisp/build.fan
+++ b/com.xored.f4.fantom/fantom/src/wisp/build.fan
@@ -19,12 +19,12 @@ class Build : BuildPod
     podName = "wisp"
     summary = "Wisp web Server"
     meta    = ["org.name":     "Fantom",
-               "org.uri":      "http://fantom.org/",
+               "org.uri":      "httsp://fantom.org/",
                "proj.name":    "Fantom Core",
-               "proj.uri":     "http://fantom.org/",
+               "proj.uri":     "https://fantom.org/",
                "license.name": "Academic Free License 3.0",
-               "vcs.name":     "Mercurial",
-               "vcs.uri":      "https://bitbucket.org/fantom/fan-1.0/"]
+               "vcs.name":     "Git",
+               "vcs.uri":      "https://github.com/fantom-lang/fantom"]
     depends = ["sys 1.0", "util 1.0", "concurrent 1.0", "inet 1.0", "web 1.0"]
     srcDirs = [`fan/`, `test/`]
     docSrc  = true
diff --git a/com.xored.f4.fantom/fantom/src/wisp/fan/WispActor.fan b/com.xored.f4.fantom/fantom/src/wisp/fan/WispActor.fan
index fb9c2113..30a0fdc0 100755
--- a/com.xored.f4.fantom/fantom/src/wisp/fan/WispActor.fan
+++ b/com.xored.f4.fantom/fantom/src/wisp/fan/WispActor.fan
@@ -112,13 +112,8 @@ internal const class WispActor : Actor
     {
       // skip leading CRLF (4.1)
       in := req.socket.in
-      line := in.readLine
-      if (line == null) throw Err("Empty request line")
-      while (line.isEmpty)
-      {
-        line = in.readLine
-        if (line == null) throw Err("Empty request line")
-      }
+      line := WebUtil.readLine(in)
+      while (line.isEmpty) line = WebUtil.readLine(in)
 
       // parse request-line (5.1)
       toks   := line.split
diff --git a/com.xored.f4.fantom/fantom/src/wisp/fan/WispService.fan b/com.xored.f4.fantom/fantom/src/wisp/fan/WispService.fan
index ede77d89..f1e62676 100755
--- a/com.xored.f4.fantom/fantom/src/wisp/fan/WispService.fan
+++ b/com.xored.f4.fantom/fantom/src/wisp/fan/WispService.fan
@@ -302,7 +302,7 @@ internal const class WispDefaultRootMod : WebMod
         .h1.w("Wisp").h1End
         .p.w("Wisp is running!").pEnd
         .p.w("Currently there is no WebMod installed on this server.").pEnd
-        .p.w("See wisp::pod-doc
+        .p.w("See wisp::pod-doc
               to configure a WebMod for the server.").pEnd
       .bodyEnd
     .htmlEnd
diff --git a/com.xored.f4.fantom/fantom/src/xml/build.fan b/com.xored.f4.fantom/fantom/src/xml/build.fan
index 2959f862..128b1803 100755
--- a/com.xored.f4.fantom/fantom/src/xml/build.fan
+++ b/com.xored.f4.fantom/fantom/src/xml/build.fan
@@ -19,12 +19,12 @@ class Build : BuildPod
     podName = "xml"
     summary = "XML Parser and Document Modeling"
     meta    = ["org.name":     "Fantom",
-               "org.uri":      "http://fantom.org/",
+               "org.uri":      "https://fantom.org/",
                "proj.name":    "Fantom Core",
-               "proj.uri":     "http://fantom.org/",
+               "proj.uri":     "https://fantom.org/",
                "license.name": "Academic Free License 3.0",
-               "vcs.name":     "Mercurial",
-               "vcs.uri":      "https://bitbucket.org/fantom/fan-1.0/"]
+               "vcs.name":     "Git",
+               "vcs.uri":      "https://github.com/fantom-lang/fantom"]
     depends = ["sys 1.0"]
     srcDirs = [`fan/`, `test/`]
     docSrc  = true
diff --git a/com.xored.f4.fantom/plugin.xml b/com.xored.f4.fantom/plugin.xml
index f1539e64..6d1b01ac 100644
--- a/com.xored.f4.fantom/plugin.xml
+++ b/com.xored.f4.fantom/plugin.xml
@@ -8,7 +8,7 @@
             home="fantom/bin/fant"
             id="com.xored.f4.fantom.bundled"
             interpreterInstallType="com.xored.fanide.internal.debug.ui.launcher.GenericFanInstallType"
-            name="fantom-1.0.74-embedded">
+            name="fantom-1.0.76-embedded">
       
    
 
diff --git a/com.xored.f4.fantom/pom.xml b/com.xored.f4.fantom/pom.xml
index 54a317ad..ad0b336e 100644
--- a/com.xored.f4.fantom/pom.xml
+++ b/com.xored.f4.fantom/pom.xml
@@ -9,6 +9,6 @@
   
   com.xored.f4
   com.xored.f4.fantom
-  1.0.74-SNAPSHOT
+  1.0.76-SNAPSHOT
   eclipse-plugin
 
diff --git a/com.xored.f4.fcode.ui/META-INF/MANIFEST.MF b/com.xored.f4.fcode.ui/META-INF/MANIFEST.MF
index 4631b772..297119f9 100644
--- a/com.xored.f4.fcode.ui/META-INF/MANIFEST.MF
+++ b/com.xored.f4.fcode.ui/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-Name: F4 Fcode UI
 Bundle-SymbolicName: com.xored.f4.fcode.ui;singleton:=true
-Bundle-Version: 1.1.4.qualifier
+Bundle-Version: 1.1.6.qualifier
 Require-Bundle: org.eclipse.core.runtime,
  org.eclipse.core.resources,
  org.fantom.sys,
@@ -10,11 +10,11 @@ Require-Bundle: org.eclipse.core.runtime,
  org.eclipse.ui,
  org.eclipse.dltk.ui,
  org.eclipse.ui.ide,
- com.xored.fanide.core;bundle-version="1.1.4",
+ com.xored.fanide.core;bundle-version="1.1.6",
  org.eclipse.dltk.core,
  org.eclipse.jface.text,
- com.xored.fanide.ui;bundle-version="1.1.4",
- com.xored.f4.fcode;bundle-version="1.1.4",
+ com.xored.fanide.ui;bundle-version="1.1.6",
+ com.xored.f4.fcode;bundle-version="1.1.6",
  org.eclipse.core.filebuffers
 Bundle-Vendor: Xored software, Inc.
 Bundle-RequiredExecutionEnvironment: J2SE-1.5
diff --git a/com.xored.f4.fcode.ui/pom.xml b/com.xored.f4.fcode.ui/pom.xml
index 814fc0eb..d696784e 100644
--- a/com.xored.f4.fcode.ui/pom.xml
+++ b/com.xored.f4.fcode.ui/pom.xml
@@ -9,6 +9,6 @@
   
   com.xored.f4
   com.xored.f4.fcode.ui
-  1.1.4-SNAPSHOT
+  1.1.6-SNAPSHOT
   eclipse-plugin
 
diff --git a/com.xored.f4.fcode/META-INF/MANIFEST.MF b/com.xored.f4.fcode/META-INF/MANIFEST.MF
index cf8ae43c..6a7e6b0f 100644
--- a/com.xored.f4.fcode/META-INF/MANIFEST.MF
+++ b/com.xored.f4.fcode/META-INF/MANIFEST.MF
@@ -2,14 +2,14 @@ Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-Name: F4 Fcode
 Bundle-SymbolicName: com.xored.f4.fcode;singleton:=true
-Bundle-Version: 1.1.4.qualifier
+Bundle-Version: 1.1.6.qualifier
 Bundle-Vendor: Xored software, Inc.
 Require-Bundle: org.eclipse.dltk.core,
- com.xored.fanide.core;bundle-version="1.1.4",
+ com.xored.fanide.core;bundle-version="1.1.6",
  org.fantom.sys,
  org.fantom.compiler,
- com.xored.f4.core;bundle-version="1.1.4",
- com.xored.f4.parser;bundle-version="1.1.4",
+ com.xored.f4.core;bundle-version="1.1.6",
+ com.xored.f4.parser;bundle-version="1.1.6",
  org.eclipse.core.runtime,
  org.eclipse.core.resources
 Bundle-RequiredExecutionEnvironment: J2SE-1.5
diff --git a/com.xored.f4.fcode/pom.xml b/com.xored.f4.fcode/pom.xml
index 70706787..98880d91 100644
--- a/com.xored.f4.fcode/pom.xml
+++ b/com.xored.f4.fcode/pom.xml
@@ -9,6 +9,6 @@
   
   com.xored.f4
   com.xored.f4.fcode
-  1.1.4-SNAPSHOT
+  1.1.6-SNAPSHOT
   eclipse-plugin
 
diff --git a/com.xored.f4.feature/feature.xml b/com.xored.f4.feature/feature.xml
index 0539af95..a173f814 100644
--- a/com.xored.f4.feature/feature.xml
+++ b/com.xored.f4.feature/feature.xml
@@ -2,7 +2,7 @@
 
 
@@ -18,12 +18,8 @@
       %license
    
 
-   
-      
-   
-
    
-      
+      
       
    
 
diff --git a/com.xored.f4.feature/pom.xml b/com.xored.f4.feature/pom.xml
index 77d53092..7ef8f9b4 100644
--- a/com.xored.f4.feature/pom.xml
+++ b/com.xored.f4.feature/pom.xml
@@ -9,6 +9,6 @@
   
   com.xored.f4
   com.xored.f4.feature
-  1.1.4-SNAPSHOT
+  1.1.6-SNAPSHOT
   eclipse-feature
 
diff --git a/com.xored.f4.jdt.launching.ui/META-INF/MANIFEST.MF b/com.xored.f4.jdt.launching.ui/META-INF/MANIFEST.MF
index d56fb87d..0d2f2611 100644
--- a/com.xored.f4.jdt.launching.ui/META-INF/MANIFEST.MF
+++ b/com.xored.f4.jdt.launching.ui/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-Name: F4 JDT Launching UI
 Bundle-SymbolicName: com.xored.f4.jdt.launching.ui;singleton:=true
-Bundle-Version: 1.1.4.qualifier
+Bundle-Version: 1.1.6.qualifier
 Bundle-Vendor: Xored software, Inc.
 Require-Bundle: org.fantom.sys,
  org.eclipse.debug.ui,
@@ -14,13 +14,13 @@ Require-Bundle: org.fantom.sys,
  org.eclipse.swt,
  org.eclipse.core.runtime,
  org.fantom.fwt,
- com.xored.f4.jdt.launching;bundle-version="1.1.4",
- com.xored.f4.launching;bundle-version="1.1.4",
- com.xored.f4.model;bundle-version="1.1.4",
+ com.xored.f4.jdt.launching;bundle-version="1.1.6",
+ com.xored.f4.launching;bundle-version="1.1.6",
+ com.xored.f4.model;bundle-version="1.1.6",
  org.eclipse.jdt.launching,
  org.eclipse.jdt.debug,
  org.eclipse.dltk.core,
- com.xored.fanide.core;bundle-version="1.1.4",
+ com.xored.fanide.core;bundle-version="1.1.6",
  org.eclipse.jface.text,
  org.eclipse.core.filebuffers,
  org.eclipse.core.filesystem,
diff --git a/com.xored.f4.jdt.launching.ui/pom.xml b/com.xored.f4.jdt.launching.ui/pom.xml
index b7f17718..465d1f1b 100644
--- a/com.xored.f4.jdt.launching.ui/pom.xml
+++ b/com.xored.f4.jdt.launching.ui/pom.xml
@@ -9,6 +9,6 @@
   
   com.xored.f4
   com.xored.f4.jdt.launching.ui
-  1.1.4-SNAPSHOT
+  1.1.6-SNAPSHOT
   eclipse-plugin
 
diff --git a/com.xored.f4.jdt.launching/META-INF/MANIFEST.MF b/com.xored.f4.jdt.launching/META-INF/MANIFEST.MF
index 774809fb..ed5ad1fc 100644
--- a/com.xored.f4.jdt.launching/META-INF/MANIFEST.MF
+++ b/com.xored.f4.jdt.launching/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-Name: F4 JDT Launching
 Bundle-SymbolicName: com.xored.f4.jdt.launching;singleton:=true
-Bundle-Version: 1.1.4.qualifier
+Bundle-Version: 1.1.6.qualifier
 Bundle-Vendor: Xored software, Inc.
 Require-Bundle: org.fantom.sys,
  org.eclipse.core.runtime,
diff --git a/com.xored.f4.jdt.launching/pom.xml b/com.xored.f4.jdt.launching/pom.xml
index c4dfad36..4315b79b 100644
--- a/com.xored.f4.jdt.launching/pom.xml
+++ b/com.xored.f4.jdt.launching/pom.xml
@@ -9,6 +9,6 @@
   
   com.xored.f4
   com.xored.f4.jdt.launching
-  1.1.4-SNAPSHOT
+  1.1.6-SNAPSHOT
   eclipse-plugin
 
diff --git a/com.xored.f4.launchEnv/META-INF/MANIFEST.MF b/com.xored.f4.launchEnv/META-INF/MANIFEST.MF
index 162da921..a55f471f 100644
--- a/com.xored.f4.launchEnv/META-INF/MANIFEST.MF
+++ b/com.xored.f4.launchEnv/META-INF/MANIFEST.MF
@@ -2,5 +2,5 @@ Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-Name: F4 LaunchEnv
 Bundle-SymbolicName: com.xored.f4.launchEnv;singleton:=true
-Bundle-Version: 1.1.4.qualifier
-Require-Bundle: org.fantom.sys;bundle-version="1.0.74"
+Bundle-Version: 1.1.6.qualifier
+Require-Bundle: org.fantom.sys;bundle-version="1.0.76"
diff --git a/com.xored.f4.launchEnv/pom.xml b/com.xored.f4.launchEnv/pom.xml
index 26626c73..5c1e54b7 100644
--- a/com.xored.f4.launchEnv/pom.xml
+++ b/com.xored.f4.launchEnv/pom.xml
@@ -9,6 +9,6 @@
   
   com.xored.f4
   com.xored.f4.launchEnv
-  1.1.4-SNAPSHOT
+  1.1.6-SNAPSHOT
   eclipse-plugin
 
diff --git a/com.xored.f4.launching/META-INF/MANIFEST.MF b/com.xored.f4.launching/META-INF/MANIFEST.MF
index 0bbbe137..939e1b92 100644
--- a/com.xored.f4.launching/META-INF/MANIFEST.MF
+++ b/com.xored.f4.launching/META-INF/MANIFEST.MF
@@ -2,17 +2,17 @@ Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-Name: F4 Launching
 Bundle-SymbolicName: com.xored.f4.launching;singleton:=true
-Bundle-Version: 1.1.4.qualifier
+Bundle-Version: 1.1.6.qualifier
 Bundle-Vendor: xored software, Inc.
 Require-Bundle: org.fantom.sys,
  org.eclipse.core.runtime,
  org.eclipse.dltk.launching,
  org.eclipse.dltk.core,
- com.xored.fanide.core;bundle-version="1.1.4",
+ com.xored.fanide.core;bundle-version="1.1.6",
  org.eclipse.debug.core,
  org.eclipse.dltk.debug,
  org.eclipse.core.variables,
  org.eclipse.emf.ecore.xmi,
  org.fantom.compiler,
- com.xored.f4.core;bundle-version="1.1.4",
- com.xored.f4.fcode;bundle-version="1.1.4"
+ com.xored.f4.core;bundle-version="1.1.6",
+ com.xored.f4.fcode;bundle-version="1.1.6"
diff --git a/com.xored.f4.launching/pom.xml b/com.xored.f4.launching/pom.xml
index 0ae2733e..0e4ac260 100644
--- a/com.xored.f4.launching/pom.xml
+++ b/com.xored.f4.launching/pom.xml
@@ -9,6 +9,6 @@
   
   com.xored.f4
   com.xored.f4.launching
-  1.1.4-SNAPSHOT
+  1.1.6-SNAPSHOT
   eclipse-plugin
 
diff --git a/com.xored.f4.model/META-INF/MANIFEST.MF b/com.xored.f4.model/META-INF/MANIFEST.MF
index 74514a19..8825075d 100644
--- a/com.xored.f4.model/META-INF/MANIFEST.MF
+++ b/com.xored.f4.model/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-Name: F4 Model
 Bundle-SymbolicName: com.xored.f4.model;singleton:=true
-Bundle-Version: 1.1.4.qualifier
+Bundle-Version: 1.1.6.qualifier
 Bundle-Vendor: xored software, Inc.
 Require-Bundle: org.fantom.sys,
  org.fantom.compiler,
diff --git a/com.xored.f4.model/f4model.pod b/com.xored.f4.model/f4model.pod
index 77f5f721..aec2fa35 100644
Binary files a/com.xored.f4.model/f4model.pod and b/com.xored.f4.model/f4model.pod differ
diff --git a/com.xored.f4.model/fan/model/IFanNamespace.fan b/com.xored.f4.model/fan/model/IFanNamespace.fan
index bc659d9c..507fec20 100644
--- a/com.xored.f4.model/fan/model/IFanNamespace.fan
+++ b/com.xored.f4.model/fan/model/IFanNamespace.fan
@@ -1,101 +1,117 @@
-abstract class IFanNamespace
-{ 
-  abstract IFanPod currPod()
-  abstract Str[] podNames()
-  abstract IFanPod? findPod(Str name)
-  virtual IFanType? findType(Str name) 
-  { 
-    if(name.isEmpty) return null
-    if(name[-1] == '?') name = name[0..-2]
 
-    //special handling for Lists and Maps
-    IFanType? result := trySpecial(name)
-    if(result != null) return result
-    
-    index := name.index("::")
-    Str? pod := null
-    if(index != null)
-    {
-      pod = name[0..")
+			if (idx != null) {
+				retType := findType(name[idx+2..<-1])
+				if (retType != null)
+					// there doesn't seem to be a need to parameterise the arguments too
+					func = func.parameterize(["sys::R":retType])
+			}
+			return func
+		}
+
+		return null
+	}
 }
\ No newline at end of file
diff --git a/com.xored.f4.model/fan/model/IFanType.fan b/com.xored.f4.model/fan/model/IFanType.fan
index 6d24ca1c..25f4e103 100644
--- a/com.xored.f4.model/fan/model/IFanType.fan
+++ b/com.xored.f4.model/fan/model/IFanType.fan
@@ -228,25 +228,27 @@ const mixin IFanType : DltkModelElement
     addSlotsTo(ns,result)
     return result.vals
   }
-  
-  private Void addSlotsTo(IFanNamespace ns, Str:IFanSlot map,Str[] excluded := [,])
-  {
-    excluded.add(qname)
-    //deep search
-    if (!excluded.contains("sys::Obj"))
-      ns.findType("sys::Obj")?.addSlotsTo(ns,map,excluded)
-    inheritance.each |t|
-    {
-      type := ns.findType(t)
-      if(type == null) return
-      if (!excluded.contains(type.qname))
-        type?.addSlotsTo(ns,map,excluded)
-    }
-    slotsMap.each |IFanSlot slot, Str name| { 
-      if( !map.containsKey(name))
-      {
-        map[name] = slot
-      }
-    }
-  }
+
+	
+	private Void addSlotsTo(IFanNamespace ns, Str:IFanSlot map, Str[] excluded := [,]) {
+		excluded.add(qname)
+
+		// add *our* slots first so they don't get overridden by Obj
+		slotsMap.each |IFanSlot slot, Str name| { 
+			if (!map.containsKey(name)) {
+				map[name] = slot
+			}
+		}
+
+		inheritance.each |t| {
+			type := ns.findType(t)
+			if (type == null) return
+			if (!excluded.contains(type.qname))
+				type.addSlotsTo(ns, map, excluded)
+		}
+
+		// always include Obj, think of mixins et al
+		if (!excluded.contains("sys::Obj"))
+			ns.findType("sys::Obj")?.addSlotsTo(ns, map, excluded)
+	}
 }
\ No newline at end of file
diff --git a/com.xored.f4.model/pom.xml b/com.xored.f4.model/pom.xml
index 2eb7e9b8..71d526e8 100644
--- a/com.xored.f4.model/pom.xml
+++ b/com.xored.f4.model/pom.xml
@@ -9,6 +9,6 @@
   
   com.xored.f4
   com.xored.f4.model
-  1.1.4-SNAPSHOT
+  1.1.6-SNAPSHOT
   eclipse-plugin
 
diff --git a/com.xored.f4.parser/META-INF/MANIFEST.MF b/com.xored.f4.parser/META-INF/MANIFEST.MF
index 4bffaa43..d1c286ad 100644
--- a/com.xored.f4.parser/META-INF/MANIFEST.MF
+++ b/com.xored.f4.parser/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-Name: F4 Parser
 Bundle-SymbolicName: com.xored.f4.parser;singleton:=true
-Bundle-Version: 1.1.4.qualifier
+Bundle-Version: 1.1.6.qualifier
 Require-Bundle: org.fantom.sys,
- com.xored.f4.model;bundle-version="1.1.4"
+ com.xored.f4.model;bundle-version="1.1.6"
 Bundle-RequiredExecutionEnvironment: JavaSE-1.7
diff --git a/com.xored.f4.parser/f4parser.pod b/com.xored.f4.parser/f4parser.pod
index 55f916cf..3c0be935 100644
Binary files a/com.xored.f4.parser/f4parser.pod and b/com.xored.f4.parser/f4parser.pod differ
diff --git a/com.xored.f4.parser/fan/ast/Statements.fan b/com.xored.f4.parser/fan/ast/Statements.fan
index 81e6dea1..a1a719c9 100644
--- a/com.xored.f4.parser/fan/ast/Statements.fan
+++ b/com.xored.f4.parser/fan/ast/Statements.fan
@@ -199,8 +199,9 @@ const class CatchStmt : Stmt
   {
     if (v.enterNode(this))
     {
-      errVar?.accept(v)
       block.accept(v)
+      // make sure to accept the Err var *inside* the catch block, to make it available
+      errVar?.accept(v)
       v.exitNode(this)
     }
   }
diff --git a/com.xored.f4.parser/fan/parser/Parser.fan b/com.xored.f4.parser/fan/parser/Parser.fan
index 94cc6707..a1de48e3 100644
--- a/com.xored.f4.parser/fan/parser/Parser.fan
+++ b/com.xored.f4.parser/fan/parser/Parser.fan
@@ -1048,22 +1048,48 @@ class Parser : AstFactory
     return operand
   }
   
-  Expr shortcut(Int start, Int end, ExprId id, Str methodName, Expr left, Expr? right, TokenVal op)
-  {
-    IFanType? rt := null
-    if (left.resolvedType != null)
-    {
-      method := left.resolvedType.method(methodName, false)
-      if (method == null)
-        err(locOf(op), ProblemKind.parser_methodNotFound, [left.resolvedType.name + "." + methodName])
-      else 
-      { 
-        rt = ns.findType(method.of)
-      }
-    }
-    if (right == null) return UnaryExpr(start, end, left, id, rt)
-    return BinaryExpr(start, end, left, right, id, rt)
-  }
+	Expr shortcut(Int start, Int end, ExprId id, Str methodName, Expr left, Expr? right, TokenVal op) {
+
+		// DateTime.minus(Duration)         = DateTime
+		// DateTime.minusDateTime(DateTime) = Duration
+		// problem is, we have to infer which method to use given the 2nd arg type
+		// it's a bit of a fath, but do-able...
+
+		retType := null as IFanType
+
+		if (left.resolvedType != null) {
+			opType := left.resolvedType
+			method := null as IFanMethod
+
+			if (right?.resolvedType != null && _opMethodPrefixes.contains(methodName)) {
+				// do the dance, looking for the actual method the shortcut corresponds to
+				method = opType.allSlots(ns).find |slot| {
+					if (slot.isMethod) {
+						mslot := (IFanMethod) slot
+						param := (mslot.params.size > 0 ? ns.findType(mslot.params.first.of) : null) as IFanType
+						if (param?.qname == right.resolvedType.qname)
+							return true
+					}
+					return false
+				}
+
+			} else
+				// for normal ops, just use the method direct
+				method = opType.method(methodName, false)
+
+			if (method != null)
+				retType = ns.findType(method.of)
+			else
+				err(locOf(op), ProblemKind.parser_methodNotFound, [left.resolvedType.name + "." + methodName])
+		}
+
+		return right == null
+			? UnaryExpr (start, end, left, id, retType)
+			: BinaryExpr(start, end, left, right, id, retType)
+	}
+
+	** see https://fantom.org/doc/docLang/Methods#operators
+	private static const Str[] _opMethodPrefixes := "plus minus mult div mod".split
 
 //////////////////////////////////////////////////////////////////////////
 // Term Expr
diff --git a/com.xored.f4.parser/pom.xml b/com.xored.f4.parser/pom.xml
index 10ae58e4..4f3402d6 100644
--- a/com.xored.f4.parser/pom.xml
+++ b/com.xored.f4.parser/pom.xml
@@ -9,6 +9,6 @@
   
   com.xored.f4
   com.xored.f4.parser
-  1.1.4-SNAPSHOT
+  1.1.6-SNAPSHOT
   eclipse-plugin
 
diff --git a/com.xored.f4.pathEnv/META-INF/MANIFEST.MF b/com.xored.f4.pathEnv/META-INF/MANIFEST.MF
index 1e1fc3aa..0e119c80 100644
--- a/com.xored.f4.pathEnv/META-INF/MANIFEST.MF
+++ b/com.xored.f4.pathEnv/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-Name: F4 PathEnv
 Bundle-SymbolicName: com.xored.f4.pathEnv;singleton:=true
-Bundle-Version: 1.1.4.qualifier
+Bundle-Version: 1.1.6.qualifier
 Require-Bundle: org.fantom.sys,
  com.xored.f4.core,
  com.xored.f4.builder.ui,
diff --git a/com.xored.f4.pathEnv/pom.xml b/com.xored.f4.pathEnv/pom.xml
index a9197237..ef26293f 100644
--- a/com.xored.f4.pathEnv/pom.xml
+++ b/com.xored.f4.pathEnv/pom.xml
@@ -9,6 +9,6 @@
   
   com.xored.f4
   com.xored.f4.pathEnv
-  1.1.4-SNAPSHOT
+  1.1.6-SNAPSHOT
   eclipse-plugin
 
diff --git a/com.xored.f4.platform/META-INF/MANIFEST.MF b/com.xored.f4.platform/META-INF/MANIFEST.MF
index 53d99e04..605bb251 100644
--- a/com.xored.f4.platform/META-INF/MANIFEST.MF
+++ b/com.xored.f4.platform/META-INF/MANIFEST.MF
@@ -4,7 +4,7 @@ Bundle-Localization: plugin
 Bundle-Name: %pluginName
 Eclipse-BundleShape: dir
 Bundle-SymbolicName: com.xored.f4.platform;singleton:=true
-Bundle-Version: 1.1.4.qualifier
+Bundle-Version: 1.1.6.qualifier
 Bundle-Vendor: %providerName
 Require-Bundle: org.eclipse.ui,
  org.eclipse.core.runtime
diff --git a/com.xored.f4.platform/about.ini b/com.xored.f4.platform/about.ini
index 72f49508..85b614ba 100644
--- a/com.xored.f4.platform/about.ini
+++ b/com.xored.f4.platform/about.ini
@@ -11,7 +11,7 @@ featureImage=featureImage.png
 
 aboutText=F4 Fantom IDE \n\
 \n\
-Version: 1.1.4\n\
+Version: 1.1.6\n\
 \n\
 Copyright (c) 2009-2020 Xored Software, Inc. All rights reserved.\n\
 \n\
diff --git a/com.xored.f4.platform/pom.xml b/com.xored.f4.platform/pom.xml
index b5da760c..807d440e 100644
--- a/com.xored.f4.platform/pom.xml
+++ b/com.xored.f4.platform/pom.xml
@@ -9,6 +9,6 @@
   
   com.xored.f4
   com.xored.f4.platform
-  1.1.4-SNAPSHOT
+  1.1.6-SNAPSHOT
   eclipse-plugin
 
diff --git a/com.xored.f4.search/META-INF/MANIFEST.MF b/com.xored.f4.search/META-INF/MANIFEST.MF
index f4cb0d75..563bf741 100644
--- a/com.xored.f4.search/META-INF/MANIFEST.MF
+++ b/com.xored.f4.search/META-INF/MANIFEST.MF
@@ -2,14 +2,14 @@ Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-Name: F4 Search
 Bundle-SymbolicName: com.xored.f4.search;singleton:=true
-Bundle-Version: 1.1.4.qualifier
+Bundle-Version: 1.1.6.qualifier
 Bundle-Vendor: Xored software, Inc.
 Bundle-RequiredExecutionEnvironment: JavaSE-1.7
 Require-Bundle: org.eclipse.core.runtime,
  org.eclipse.core.resources,
  org.fantom.sys,
- com.xored.f4.parser;bundle-version="1.1.4",
- com.xored.f4.core;bundle-version="1.1.4",
- com.xored.f4.model;bundle-version="1.1.4",
+ com.xored.f4.parser;bundle-version="1.1.6",
+ com.xored.f4.core;bundle-version="1.1.6",
+ com.xored.f4.model;bundle-version="1.1.6",
  org.eclipse.dltk.core,
  com.xored.fanide.core
diff --git a/com.xored.f4.search/f4search.pod b/com.xored.f4.search/f4search.pod
index 6581d7bb..29cbc206 100644
Binary files a/com.xored.f4.search/f4search.pod and b/com.xored.f4.search/f4search.pod differ
diff --git a/com.xored.f4.search/pom.xml b/com.xored.f4.search/pom.xml
index 790e0a70..f8951cf6 100644
--- a/com.xored.f4.search/pom.xml
+++ b/com.xored.f4.search/pom.xml
@@ -9,6 +9,6 @@
   
   com.xored.f4
   com.xored.f4.search
-  1.1.4-SNAPSHOT
+  1.1.6-SNAPSHOT
   eclipse-plugin
 
diff --git a/com.xored.f4.testing/META-INF/MANIFEST.MF b/com.xored.f4.testing/META-INF/MANIFEST.MF
index 404a5b40..016e6deb 100644
--- a/com.xored.f4.testing/META-INF/MANIFEST.MF
+++ b/com.xored.f4.testing/META-INF/MANIFEST.MF
@@ -2,18 +2,18 @@ Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-Name: F4 Testing
 Bundle-SymbolicName: com.xored.f4.testing;singleton:=true
-Bundle-Version: 1.1.4.qualifier
+Bundle-Version: 1.1.6.qualifier
 Bundle-Vendor: Xored software, Inc.
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: J2SE-1.5
 Require-Bundle: org.fantom.sys,
  org.eclipse.debug.core,
- com.xored.f4.jdt.launching;bundle-version="1.1.4",
- com.xored.f4.debug.ui;bundle-version="1.1.4",
- com.xored.f4.launching;bundle-version="1.1.4",
- com.xored.f4.parser;bundle-version="1.1.4",
- com.xored.f4.core;bundle-version="1.1.4",
- com.xored.f4.model;bundle-version="1.1.4",
+ com.xored.f4.jdt.launching;bundle-version="1.1.6",
+ com.xored.f4.debug.ui;bundle-version="1.1.6",
+ com.xored.f4.launching;bundle-version="1.1.6",
+ com.xored.f4.parser;bundle-version="1.1.6",
+ com.xored.f4.core;bundle-version="1.1.6",
+ com.xored.f4.model;bundle-version="1.1.6",
  org.eclipse.dltk.testing,
  org.eclipse.jface,
  org.eclipse.debug.ui,
diff --git a/com.xored.f4.testing/pom.xml b/com.xored.f4.testing/pom.xml
index c56e3c0f..f4f857c7 100644
--- a/com.xored.f4.testing/pom.xml
+++ b/com.xored.f4.testing/pom.xml
@@ -9,6 +9,6 @@
   
   com.xored.f4
   com.xored.f4.testing
-  1.1.4-SNAPSHOT
+  1.1.6-SNAPSHOT
   eclipse-plugin
 
diff --git a/com.xored.f4.ui.core/META-INF/MANIFEST.MF b/com.xored.f4.ui.core/META-INF/MANIFEST.MF
index f41de486..37371f69 100644
--- a/com.xored.f4.ui.core/META-INF/MANIFEST.MF
+++ b/com.xored.f4.ui.core/META-INF/MANIFEST.MF
@@ -2,12 +2,12 @@ Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-Name: F4 UI Core
 Bundle-SymbolicName: com.xored.f4.ui.core;singleton:=true
-Bundle-Version: 1.1.4.qualifier
+Bundle-Version: 1.1.6.qualifier
 Require-Bundle: org.eclipse.dltk.ui;bundle-version="3.0.0",
  org.fantom.sys,
  org.eclipse.ui,
  org.eclipse.core.runtime,
- com.xored.fanide.ui;bundle-version="1.1.4",
+ com.xored.fanide.ui;bundle-version="1.1.6",
  org.eclipse.jface.text,
  org.eclipse.dltk.core;bundle-version="3.0.0",
  org.eclipse.core.resources,
@@ -17,7 +17,7 @@ Require-Bundle: org.eclipse.dltk.ui;bundle-version="3.0.0",
  org.eclipse.core.filebuffers,
  org.eclipse.ui.console,
  org.eclipse.ui.editors,
- com.xored.fanide.core;bundle-version="1.1.4",
+ com.xored.fanide.core;bundle-version="1.1.6",
  com.ibm.icu,
  org.eclipse.dltk.launching;bundle-version="3.0.0"
 Bundle-Activator: com.xored.f4.ui.core.F4UIPlugin
diff --git a/com.xored.f4.ui.core/pom.xml b/com.xored.f4.ui.core/pom.xml
index 3a2c44c0..9da46113 100644
--- a/com.xored.f4.ui.core/pom.xml
+++ b/com.xored.f4.ui.core/pom.xml
@@ -9,6 +9,6 @@
   
   com.xored.f4
   com.xored.f4.ui.core
-  1.1.4-SNAPSHOT
+  1.1.6-SNAPSHOT
   eclipse-plugin
 
diff --git a/com.xored.f4.ui.jdt/META-INF/MANIFEST.MF b/com.xored.f4.ui.jdt/META-INF/MANIFEST.MF
index 28ca2a48..9921c604 100644
--- a/com.xored.f4.ui.jdt/META-INF/MANIFEST.MF
+++ b/com.xored.f4.ui.jdt/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-Name: F4 UI JDT
 Bundle-SymbolicName: com.xored.f4.ui.jdt;singleton:=true
-Bundle-Version: 1.1.4.qualifier
+Bundle-Version: 1.1.6.qualifier
 Bundle-Vendor: Xored software, Inc.
 Bundle-RequiredExecutionEnvironment: J2SE-1.5
 Require-Bundle: org.eclipse.core.resources,
@@ -16,7 +16,7 @@ Require-Bundle: org.eclipse.core.resources,
  org.eclipse.ui,
  org.eclipse.debug.core,
  org.fantom.sys,
- com.xored.f4.jdt.launching;bundle-version="1.1.4",
+ com.xored.f4.jdt.launching;bundle-version="1.1.6",
  org.eclipse.dltk.core,
  org.eclipse.dltk.ui
 Import-Package: org.eclipse.dltk.ui
diff --git a/com.xored.f4.ui.jdt/pom.xml b/com.xored.f4.ui.jdt/pom.xml
index 56fa4a40..ddbca0d2 100644
--- a/com.xored.f4.ui.jdt/pom.xml
+++ b/com.xored.f4.ui.jdt/pom.xml
@@ -9,6 +9,6 @@
   
   com.xored.f4
   com.xored.f4.ui.jdt
-  1.1.4-SNAPSHOT
+  1.1.6-SNAPSHOT
   eclipse-plugin
 
diff --git a/com.xored.f4.ui.text/META-INF/MANIFEST.MF b/com.xored.f4.ui.text/META-INF/MANIFEST.MF
index ae88c11e..f5f4750b 100644
--- a/com.xored.f4.ui.text/META-INF/MANIFEST.MF
+++ b/com.xored.f4.ui.text/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-Name: F4 UI Text
 Bundle-SymbolicName: com.xored.f4.ui.text;singleton:=true
-Bundle-Version: 1.1.4.qualifier
+Bundle-Version: 1.1.6.qualifier
 Bundle-Vendor: Xored software, Inc.
 Bundle-Localization: plugin
 Require-Bundle: org.eclipse.core.runtime,
@@ -16,11 +16,11 @@ Require-Bundle: org.eclipse.core.runtime,
  org.fantom.sys,
  org.eclipse.search,
  org.fantom.fandoc,
- com.xored.f4.parser;bundle-version="1.1.4",
- com.xored.f4.model;bundle-version="1.1.4",
- com.xored.f4.core;bundle-version="1.1.4",
- com.xored.f4.ui.core;bundle-version="1.1.4",
- com.xored.fanide.ui;bundle-version="1.1.4",
+ com.xored.f4.parser;bundle-version="1.1.6",
+ com.xored.f4.model;bundle-version="1.1.6",
+ com.xored.f4.core;bundle-version="1.1.6",
+ com.xored.f4.ui.core;bundle-version="1.1.6",
+ com.xored.fanide.ui;bundle-version="1.1.6",
  org.eclipse.core.filesystem,
  com.ibm.icu,
  org.eclipse.ui.ide,
diff --git a/com.xored.f4.ui.text/f4uiText.pod b/com.xored.f4.ui.text/f4uiText.pod
index 5ed2e968..d2701405 100644
Binary files a/com.xored.f4.ui.text/f4uiText.pod and b/com.xored.f4.ui.text/f4uiText.pod differ
diff --git a/com.xored.f4.ui.text/fan/contentassist/CompletionProvider.fan b/com.xored.f4.ui.text/fan/contentassist/CompletionProvider.fan
index c3286be9..542164c1 100644
--- a/com.xored.f4.ui.text/fan/contentassist/CompletionProvider.fan
+++ b/com.xored.f4.ui.text/fan/contentassist/CompletionProvider.fan
@@ -72,10 +72,13 @@ abstract class CompletionProvider {
 	** Helper method to report bunch of slots
 	** 
 	protected Void reportSlots(IFanSlot[] slots) {
-		slots = slots.findAll { it.name.lower.startsWith(prefix.lower) }
+		prefixLower := prefix.lower
+		
 		slots.each |slot| {
-			if (slot.isField) reportField(slot)
-			else if (slot.isMethod) reportMethod(slot) 
+			if (prefixLower.isEmpty || slot.name.lower.startsWith(prefixLower)) {
+				if (slot.isField ) reportField (slot)
+				if (slot.isMethod) reportMethod(slot)
+			}
 		}
 	}
 
@@ -87,11 +90,10 @@ abstract class CompletionProvider {
 		reporter.report(createProposal(ProposeKind.field, field.name, field.me, reporter.computeCaseRelevance(this.prefix, field.name)))
 	}
 
-	protected Void reportMethod(IFanMethod method, Str? mname := null) {
-		params := method.params
-	 
+	protected Void reportMethod(IFanMethod method, Str? mname := null) {	 
 		if (reporter.ignores(ProposeKind.method)) return
 
+		params := method.params
 		required := params.findIndex { it.hasDefault }
 		if (required == null) { required = params.size }
 
diff --git a/com.xored.f4.ui.text/fan/contentassist/DotCompletionProvider.fan b/com.xored.f4.ui.text/fan/contentassist/DotCompletionProvider.fan
index 0c29c8da..211f4f61 100644
--- a/com.xored.f4.ui.text/fan/contentassist/DotCompletionProvider.fan
+++ b/com.xored.f4.ui.text/fan/contentassist/DotCompletionProvider.fan
@@ -59,7 +59,8 @@ class DotCompletionProvider : CompletionProvider {
 		}
 
 		reportSlots(slots)
-
+		
+		// given reportSlots adds in slots for Obj, I believe we should always have a ctor - SlimerDude July 2021
 		if (isStatic && !slots.any { it.isCtor })
 			reportDefaultCtor
 		
diff --git a/com.xored.f4.ui.text/fan/contentassist/FanMethodCompletionProposal.fan b/com.xored.f4.ui.text/fan/contentassist/FanMethodCompletionProposal.fan
index 270ab519..5fbb806e 100644
--- a/com.xored.f4.ui.text/fan/contentassist/FanMethodCompletionProposal.fan
+++ b/com.xored.f4.ui.text/fan/contentassist/FanMethodCompletionProposal.fan
@@ -108,9 +108,8 @@ class FanMethodCompletionProposal : ScriptMethodCompletionProposal {
 			 argumentOffset += 1
 		}
 		
-		buffer.add("{   }")
-		replacementBuffer.addArgument(argumentOffset, 1)
-		// one space only
+		buffer.add("{  }")
+		replacementBuffer.addArgument(argumentOffset, 0)
 		
 		return buffer.toStr
 	}
diff --git a/com.xored.f4.ui.text/pom.xml b/com.xored.f4.ui.text/pom.xml
index f93e5359..e7b72bea 100644
--- a/com.xored.f4.ui.text/pom.xml
+++ b/com.xored.f4.ui.text/pom.xml
@@ -9,6 +9,6 @@
   
   com.xored.f4
   com.xored.f4.ui.text
-  1.1.4-SNAPSHOT
+  1.1.6-SNAPSHOT
   eclipse-plugin
 
diff --git a/com.xored.fanide.core/META-INF/MANIFEST.MF b/com.xored.fanide.core/META-INF/MANIFEST.MF
index e4fa60fd..2ed514bd 100644
--- a/com.xored.fanide.core/META-INF/MANIFEST.MF
+++ b/com.xored.fanide.core/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-Name: %pluginName
 Bundle-SymbolicName: com.xored.fanide.core;singleton:=true
-Bundle-Version: 1.1.4.qualifier
+Bundle-Version: 1.1.6.qualifier
 Bundle-Activator: com.xored.fanide.core.FanCore
 Bundle-Vendor: %providerName
 Require-Bundle: org.eclipse.core.runtime,
diff --git a/com.xored.fanide.core/pom.xml b/com.xored.fanide.core/pom.xml
index 55f9827a..b783a789 100644
--- a/com.xored.fanide.core/pom.xml
+++ b/com.xored.fanide.core/pom.xml
@@ -9,6 +9,6 @@
   
   com.xored.f4
   com.xored.fanide.core
-  1.1.4-SNAPSHOT
+  1.1.6-SNAPSHOT
   eclipse-plugin
 
diff --git a/com.xored.fanide.ui/META-INF/MANIFEST.MF b/com.xored.fanide.ui/META-INF/MANIFEST.MF
index 9a52171a..66576a12 100644
--- a/com.xored.fanide.ui/META-INF/MANIFEST.MF
+++ b/com.xored.fanide.ui/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-Name: %pluginName
 Bundle-SymbolicName: com.xored.fanide.ui;singleton:=true
-Bundle-Version: 1.1.4.qualifier
+Bundle-Version: 1.1.6.qualifier
 Bundle-Activator: com.xored.fanide.internal.ui.FanUI
 Bundle-Vendor: %pluginProvider
 Bundle-Localization: plugin
@@ -23,7 +23,7 @@ Require-Bundle: org.eclipse.core.runtime,
  org.eclipse.dltk.launching,
  org.eclipse.dltk.debug.ui,
  org.eclipse.dltk.ui,
- com.xored.fanide.core;bundle-version="1.1.4",
+ com.xored.fanide.core;bundle-version="1.1.6",
  org.eclipse.core.filesystem,
  org.fantom,
  org.eclipse.jdt.core,
diff --git a/com.xored.fanide.ui/pom.xml b/com.xored.fanide.ui/pom.xml
index ff2e3b71..8db53de9 100644
--- a/com.xored.fanide.ui/pom.xml
+++ b/com.xored.fanide.ui/pom.xml
@@ -9,6 +9,6 @@
   
   com.xored.f4
   com.xored.fanide.ui
-  1.1.4-SNAPSHOT
+  1.1.6-SNAPSHOT
   eclipse-plugin
 
diff --git a/com.xored.fanide.ui/src/com/xored/fanide/internal/ui/preferences/FanEditorColoringConfigurationBlock.java b/com.xored.fanide.ui/src/com/xored/fanide/internal/ui/preferences/FanEditorColoringConfigurationBlock.java
index 8044156c..223b40d7 100644
--- a/com.xored.fanide.ui/src/com/xored/fanide/internal/ui/preferences/FanEditorColoringConfigurationBlock.java
+++ b/com.xored.fanide.ui/src/com/xored/fanide/internal/ui/preferences/FanEditorColoringConfigurationBlock.java
@@ -16,6 +16,7 @@
 import org.eclipse.dltk.ui.preferences.IPreferenceConfigurationBlock;
 import org.eclipse.dltk.ui.preferences.OverlayPreferenceStore;
 import org.eclipse.dltk.ui.preferences.PreferencesMessages;
+import org.eclipse.dltk.ui.text.DLTKColorConstants;
 import org.eclipse.dltk.ui.text.IColorManager;
 import org.eclipse.dltk.ui.text.ScriptSourceViewerConfiguration;
 import org.eclipse.dltk.ui.text.ScriptTextTools;
@@ -113,7 +114,15 @@ public class FanEditorColoringConfigurationBlock extends
 			//WTFs
 			{ PreferencesMessages.DLTKEditorPreferencePage_decorators,
 					FanPreferenceConstants.EDITOR_DECORATOR_COLOR,
-					sCoreCategory }
+					sCoreCategory },
+					
+			// The Missing Colour for Dark Mode! - SlimerDude, July 2021
+			{ "Default",		DLTKColorConstants.DLTK_DEFAULT,	sCoreCategory }
+
+			// these other colours don't seem to be used - SlimerDude, July 2021
+//			{ "x-Operator",		DLTKColorConstants.DLTK_OPERATOR,	sCoreCategory },
+//			{ "x-Argument",		DLTKColorConstants.DLTK_ARGUMENT,	sCoreCategory },
+//			{ "x-Base Class",	DLTKColorConstants.DLTK_BASE_CLASS,	sCoreCategory }
 		};
 	
 	
diff --git a/com.xored.fanide.ui/src/com/xored/fanide/internal/ui/preferences/PreviewFile.txt b/com.xored.fanide.ui/src/com/xored/fanide/internal/ui/preferences/PreviewFile.txt
index ac697bff..0a86f1d5 100644
--- a/com.xored.fanide.ui/src/com/xored/fanide/internal/ui/preferences/PreviewFile.txt
+++ b/com.xored.fanide.ui/src/com/xored/fanide/internal/ui/preferences/PreviewFile.txt
@@ -1,20 +1,17 @@
 #!empty
 ** TODO: Something
-class Hello
-{
+class Hello : Obj {
   Str member := "value"
-  static Void main() 
-  {
+ 
+  static Void main(Str[] args) {
   	localVar := "world"
 	echo("hello ${localVar}!")
   }
 	
   /* This comment may span multiple lines. */
-  Int func()
-  {
+  Int func() {
 	// FIXME: Nothing
-	echo(Str <|no \ or $ escapes need, and
-      multi-line works too|>)
-	return 4
+	echo(Str<|no \ or $ escapes needed|>)
+	return 4 + 2
   }
-}
\ No newline at end of file
+}
diff --git a/f4tests/pom-server.xml b/f4tests/pom-server.xml
index e4e10e96..ac4350f8 100644
--- a/f4tests/pom-server.xml
+++ b/f4tests/pom-server.xml
@@ -160,7 +160,7 @@
 	
 	
 		1.1.1-SNAPSHOT
-		1.1.4-SNAPSHOT
+		1.1.6-SNAPSHOT
 	
 	
 
diff --git a/pom.xml b/pom.xml
index 399248af..56e6615c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -113,22 +113,18 @@
         
           p2
           
-          
             
               win32
               win32
-              x86
+              x86_64
             
           
+			-->
             
               win32
               win32
-              x86_64
+              x86
             
-          
             
               linux
               gtk
@@ -144,7 +140,7 @@
               cocoa
               x86_64
             
-          
           
           
diff --git a/product/category.xml b/product/category.xml
index 28c20cb1..d68a7067 100644
--- a/product/category.xml
+++ b/product/category.xml
@@ -3,13 +3,13 @@
    
       F4 Fantom IDE update site
    
-   
+   
       
    
-   
+   
       
    
-   
+   
       
    
    
diff --git a/product/com.xored.f4.ide.p2.inf b/product/com.xored.f4.ide.p2.inf
index 9912c4f4..e6d42d89 100644
--- a/product/com.xored.f4.ide.p2.inf
+++ b/product/com.xored.f4.ide.p2.inf
@@ -3,6 +3,4 @@ instructions.configure=\
   addRepository(type:1,location:http${#58}//download.eclipse.org/releases/photon,name:Photon);\
   addRepository(type:0,location:http${#58}//download.eclipse.org/eclipse/updates/4.8,name:The Eclipse Project Updates);\
   addRepository(type:1,location:http${#58}//download.eclipse.org/eclipse/updates/4.8,name:The Eclipse Project Updates);\
-  addRepository(type:0,location:http${#58}//download.xored.com/f4/updates/stable,name:F4 Update Site);\
-  addRepository(type:1,location:http${#58}//download.xored.com/f4/updates/stable,name:F4 Update Site);\
   setProgramProperty(propName:osgi.instance.area.default,propValue:@user.home/f4workspace);
diff --git a/product/com.xored.f4.ide.product b/product/com.xored.f4.ide.product
index bb9112ce..d607fece 100644
--- a/product/com.xored.f4.ide.product
+++ b/product/com.xored.f4.ide.product
@@ -1,7 +1,7 @@
 
 
 
-
+
 
    
    
diff --git a/product/pom.xml b/product/pom.xml
index c38c38ff..213eaddb 100644
--- a/product/pom.xml
+++ b/product/pom.xml
@@ -9,7 +9,7 @@
 
   com.xored.f4
   product
-  1.1.4-SNAPSHOT
+  1.1.6-SNAPSHOT
   eclipse-repository
 
   
diff --git a/repository/category.xml b/repository/category.xml
index b20a0051..5fd4026a 100644
--- a/repository/category.xml
+++ b/repository/category.xml
@@ -3,14 +3,14 @@
    
       F4 Fantom IDE update site
    
-   
+   
       
    
-   
+   
       
    
 
-   
+   
       
    
    
diff --git a/repository/pom.xml b/repository/pom.xml
index a7752093..7ccd747b 100644
--- a/repository/pom.xml
+++ b/repository/pom.xml
@@ -9,6 +9,6 @@
 
 	com.xored.f4
 	repository
-	1.1.4-SNAPSHOT
+	1.1.6-SNAPSHOT
 	eclipse-repository