TAP5-2612: Allow the usage of Tapestry without Bootstrap (CSS framework)
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/SymbolConstants.java b/tapestry-core/src/main/java/org/apache/tapestry5/SymbolConstants.java
index b11a3cd..899d6b8 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/SymbolConstants.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/SymbolConstants.java
@@ -17,9 +17,11 @@
 import org.apache.tapestry5.corelib.components.BeanEditor;
 import org.apache.tapestry5.corelib.mixins.FormGroup;
 import org.apache.tapestry5.internal.services.AssetDispatcher;
+import org.apache.tapestry5.modules.NoBootstrapModule;
 import org.apache.tapestry5.services.Html5Support;
 import org.apache.tapestry5.services.assets.AssetPathConstructor;
 import org.apache.tapestry5.services.assets.ResourceMinimizer;
+import org.apache.tapestry5.services.compatibility.Trait;
 import org.apache.tapestry5.services.javascript.JavaScriptStack;
 
 /**
@@ -209,10 +211,15 @@
     public static final String START_PAGE_NAME = "tapestry.start-page-name";
 
     /**
-     * The default stylesheet automatically inserted into every rendered HTML page.
+     * The default stylesheet automatically inserted into every rendered HTML page when
+     * no Bootstrap version is enabled (i.e both {@link Trait#BOOTSTRAP_3} and {@link Trait#BOOTSTRAP_4}
+     * traits are disabled, something done by {@linkplain NoBootstrapModule}). 
+     * 
+     * It was deprecated in 5.4 with no replacement (the stylesheet is now associated with the core {@link JavaScriptStack}.),
+     * but undeprecated in 5.5.0 with the caveat described above.
      *
+     * @see NoBootstrapModule
      * @since 5.2.0
-     * @deprecated Deprecated in 5.4 with no replacement; the stylesheet is now associated with the core {@link JavaScriptStack}.
      */
     public static final String DEFAULT_STYLESHEET = "tapestry.default-stylesheet";
 
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/modules/JavaScriptModule.java b/tapestry-core/src/main/java/org/apache/tapestry5/modules/JavaScriptModule.java
index 19081fe..4f267d8 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/modules/JavaScriptModule.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/modules/JavaScriptModule.java
@@ -164,6 +164,11 @@
             addCoreStylesheets(configuration, "${" + SymbolConstants.BOOTSTRAP_ROOT + "}/css/bootstrap.css");
             addCoreStylesheets(configuration, "${" + SymbolConstants.BOOTSTRAP_ROOT + "}/css/bootstrap-grid.css");
         }
+        
+        if (!compatibility.enabled(Trait.BOOTSTRAP_3) && !compatibility.enabled(Trait.BOOTSTRAP_4))
+        {
+            configuration.add("defaultcss", StackExtension.stylesheet("${" + SymbolConstants.DEFAULT_STYLESHEET + "}"));
+        }
 
         for (String name : bundledModules)
         {
@@ -393,15 +398,9 @@
         
         if (compatibility.enabled(Trait.BOOTSTRAP_3))
         {
-            configuration.add("bootstrap/transition", new AMDWrapper(transition).require("jquery", "$").asJavaScriptModuleConfiguration());
-
-            for (String name : new String[]{"affix", "alert", "button", "carousel", "collapse", "dropdown", "modal",
-                    "scrollspy", "tab", "tooltip"})
-            {
-                Resource lib = transition.forFile(name + ".js");
-
-                configuration.add("bootstrap/" + name, new AMDWrapper(lib).require("bootstrap/transition").asJavaScriptModuleConfiguration());
-            }
+            final String[] modules = new String[]{"affix", "alert", "button", "carousel", "collapse", "dropdown", "modal",
+                    "scrollspy", "tab", "tooltip"};
+            addBootstrap3Modules(configuration, transition, modules);
 
             Resource popover = transition.forFile("popover.js");
 
@@ -428,12 +427,31 @@
             }
         }
 
+        // Just the minimum to have alerts and AJAX validation working when Bootstrap
+        // is completely disabled
+        if (!compatibility.enabled(Trait.BOOTSTRAP_3) && !compatibility.enabled(Trait.BOOTSTRAP_4))
+        {
+            final String[] modules = new String[]{"alert", "dropdown", "collapse"};
+            addBootstrap3Modules(configuration, transition, modules);
+        }
+
         configuration.add("t5/core/typeahead", new JavaScriptModuleConfiguration(typeahead).dependsOn("jquery"));
 
         configuration.add("moment", new JavaScriptModuleConfiguration(moment));
 
     }
 
+    private static void addBootstrap3Modules(MappedConfiguration<String, Object> configuration, Resource transition, final String[] modules) {
+        configuration.add("bootstrap/transition", new AMDWrapper(transition).require("jquery", "$").asJavaScriptModuleConfiguration());
+
+        for (String name : modules)
+        {
+            Resource lib = transition.forFile(name + ".js");
+
+            configuration.add("bootstrap/" + name, new AMDWrapper(lib).require("bootstrap/transition").asJavaScriptModuleConfiguration());
+        }
+    }
+
     @Contribute(SymbolProvider.class)
     @FactoryDefaults
     public static void setupFactoryDefaults(MappedConfiguration<String, Object> configuration)
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/modules/NoBootstrapModule.java b/tapestry-core/src/main/java/org/apache/tapestry5/modules/NoBootstrapModule.java
new file mode 100644
index 0000000..bb894df
--- /dev/null
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/modules/NoBootstrapModule.java
@@ -0,0 +1,37 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.modules;
+
+import org.apache.tapestry5.SymbolConstants;
+import org.apache.tapestry5.ioc.MappedConfiguration;
+import org.apache.tapestry5.ioc.annotations.Contribute;
+import org.apache.tapestry5.services.compatibility.Compatibility;
+import org.apache.tapestry5.services.compatibility.Trait;
+
+/**
+ * Module which defines the options needed to run Tapestry without Bootstrap. You'll need
+ * to define the CSS file to be included in all pages by setting the 
+ * <code>tapestry.default-stylesheet</code> configuration symbol 
+ * ({@link SymbolConstants#DEFAULT_STYLESHEET}). Notice Tapestry will not provide any 
+ * CSS, so you're on your own regarding stylesheets.
+ * 
+ * @since 5.5
+ */
+public class NoBootstrapModule
+{
+    @Contribute(Compatibility.class)
+    public static void setupCompatibilityDefaults(MappedConfiguration<Trait, Boolean> configuration)
+    {
+        configuration.add(Trait.BOOTSTRAP_3, false);
+    }
+}
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java b/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java
index ad96765..9ecddaa 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java
@@ -2023,8 +2023,6 @@
 
         configuration.add(SymbolConstants.START_PAGE_NAME, "start");
 
-        configuration.add(SymbolConstants.DEFAULT_STYLESHEET, "${" + SymbolConstants.BOOTSTRAP_ROOT + "}/css/bootstrap.css");
-
         configuration.add(SymbolConstants.PRODUCTION_MODE, true);
 
         configuration.add(SymbolConstants.CLUSTERED_SESSIONS, true);
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java
index 89300a2..c47e7b9 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java
@@ -42,6 +42,7 @@
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry5.ioc.services.ServiceOverride;
 import org.apache.tapestry5.modules.Bootstrap4Module;
+import org.apache.tapestry5.modules.NoBootstrapModule;
 import org.apache.tapestry5.services.BaseURLSource;
 import org.apache.tapestry5.services.BeanBlockContribution;
 import org.apache.tapestry5.services.BeanBlockSource;
@@ -55,6 +56,8 @@
 import org.apache.tapestry5.services.Response;
 import org.apache.tapestry5.services.ValueEncoderFactory;
 import org.apache.tapestry5.services.ValueLabelProvider;
+import org.apache.tapestry5.services.compatibility.Compatibility;
+import org.apache.tapestry5.services.compatibility.Trait;
 import org.apache.tapestry5.services.pageload.PagePreloader;
 import org.apache.tapestry5.services.pageload.PreloaderMode;
 import org.apache.tapestry5.services.security.ClientWhitelist;
@@ -66,6 +69,7 @@
  * I was just dying to see how fast requests are!
  */
 //@ImportModule(Bootstrap4Module.class)
+//@ImportModule(NoBootstrapModule.class)
 public class AppModule
 {
 
@@ -177,6 +181,7 @@
         configuration.add(D3_URL_SYMBOL, "cdnjs.cloudflare.com/ajax/libs/d3/3.0.0/d3.js");
         configuration.add(SymbolConstants.PRELOADER_MODE, PreloaderMode.ALWAYS);
 //        configuration.add(SymbolConstants.ERROR_CSS_CLASS, "yyyy");
+//        configuration.add(SymbolConstants.DEFAULT_STYLESHEET, "classpath:/org/apache/tapestry5/integration/app1/app1.css");
     }
 
     public static void contributeIgnoredPathsFilter(Configuration<String> configuration)
diff --git a/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/app1.css b/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/app1.css
new file mode 100644
index 0000000..3f0e07d
--- /dev/null
+++ b/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/app1.css
@@ -0,0 +1,3 @@
+.well {
+	background-color: grey;
+}
\ No newline at end of file