diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml
index b5c3447871..0d4e963b66 100644
--- a/.github/workflows/CI.yml
+++ b/.github/workflows/CI.yml
@@ -43,6 +43,7 @@ jobs:
with:
submodules: 'recursive'
- run: sudo apt-get install -qq g++-8 cmake build-essential python-pip python-virtualenv nodejs tar gzip libpthread-stubs0-dev libc6-dbg gdb
+ - run: sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-8 90
- run: make install-test-dependencies
- name: Run headless test
uses: GabrielBB/xvfb-action@v1
diff --git a/.gitignore b/.gitignore
index 0fb1134a47..1f6b392483 100644
--- a/.gitignore
+++ b/.gitignore
@@ -68,8 +68,8 @@ demos/NK/web/NK.js.mem
doc/doxygen/
examples/*/*
-!examples/*/*.cc
-!examples/*/*.h
+!examples/*/*.cpp
+!examples/*/*.hpp
!examples/*/*.html
!examples/*/images
!examples/*/Makefile
@@ -86,4 +86,3 @@ third-party/doxygen/
third-party/emsdk/
build/
doc/_build/
-
diff --git a/doc/dev/guide-to-testing.md b/doc/dev/guide-to-testing.md
index 101274a307..6306e2a095 100644
--- a/doc/dev/guide-to-testing.md
+++ b/doc/dev/guide-to-testing.md
@@ -120,3 +120,43 @@ This allows for easier debugging of failed tests.
Catch provides several different frameworks for constructing test cases
which are detailed within [their
documentation](https://github.com/philsquared/Catch/blob/master/docs/tutorial.md).
+
+## Running Tests with Docker
+-------------------------
+
+A [devosoft/empirical](https://hub.docker.com/r/devosoft/empirical) Docker image has been set up
+to make recreating a development environment on your machine easier.
+The first step is to download Docker.
+
+To download and run the Docker image, enter the following commands in the Docker terminal
+``` bash
+docker pull devosoft/empirical:latest
+docker run --name emp-tests -dit devosoft/empirical:latest /sbin/init
+docker exec -it emp-tests bash -l
+```
+
+To exit Docker containter shell
+``` bash
+CTRL+D
+```
+
+Commands to stop and start the Docker container
+``` bash
+docker start emp-tests
+docker stop emp-tests
+```
+
+If you get `error: cannot open display: 99` when running Mocha web tests, try
+```bash
+Xvfb :99 -ac -screen 0 1024x768x8 &
+export DISPLAY=:99
+```
+
+If you get an error prompting you to *check if server X is already running* after
+entering `Xvfb :99 -ac -screen 0 1024x768x8 &`, try this to kill the process
+``` bash
+ps -ef | grep Xvfb
+kill *pid*
+```
+
+**Note:** Instructions adapted from and
diff --git a/doc/library/prefab/prefab.rst b/doc/library/prefab/prefab.rst
new file mode 100644
index 0000000000..554da916c1
--- /dev/null
+++ b/doc/library/prefab/prefab.rst
@@ -0,0 +1,355 @@
+Prefabricated Web Tools (for use with Emscripten)
+=================================================
+
+These prefabricated tools were created to help you quickly create interesting web applicications without being overwhelmed with the underlying HTML, CSS, and Bootstrap classes required.
+These tools use Empirical's web tools to provide structure for the site, and many of the prefab tools inherit from web tools so you can add your own styling and stream them into other web components in a similar way.
+
+When using these prefab tools be sure to link to the Bootstrap library, jQuery, and the default style stylesheet for this class in the head section of your HTML file.
+.. code-block:: html
+
+
+
+
+
+
+
+
+You can view these tools in action `here `_.
+
+Card
+~~~~
+The Card class allows you to define a Bootstrap style card into your project.
+A card that is not collapsible will have its state set to :code:`STATIC`.
+Cards are static by default.
+A card can be collapsible if its state parameter it set to :code:`INIT_OPEN` or :code:`INIT_CLOSED`.
+By default, if a card is collapsible, it will have toggle icons in the header, but this can be overridden by setting the :code:`showGlyphs` parameter to :code:`false`.
+
+Since this class inherits from :code:`web::Div`, you can set styling and attributes with :code:`SetCSS` and :code:`SetAttr` respectively.
+You can also stream your Card into other web components with the :code:`<<` operator.
+
+Example:
+********
+.. code-block:: c++
+
+ #include "web/web.h"
+ #include "prefab/Card.h"
+
+ emp::web::Document doc("emp_base");
+
+ emp::prefab::Card my_card("STATIC");
+ doc << my_card;
+
+ my_card.AddHeaderContent("Title");
+ my_card.AddBodyContent("Content for the card's body");
+ // Web components can also be passed as parameters to AddHeaderContent and AddBodyContent
+
+**Note**: The toggle icons that are avalible for collapsible cards use the `FontAwesome`_ library.
+You will need to add the CSS file for this library to the head of your HTML file:
+
+.. code-block:: html
+
+
+
+CodeBlock
+~~~~~~~~~
+The CardBlock class provides an interface for the `HighlightJS Library`_ which allows you to display code on web pages with language specific highlighting.
+You can find a list of `all languages`_ on their GitHub page.
+
+To use this class, you need to pass the code you want displayed and the programming language to the constructor.
+
+Since this class inherits from :code:`web::Element`, you can stream your CodeBlock into other web components with the :code:`<<` operator.
+
+Example:
+********
+.. code-block:: c++
+
+ #include "web/web.h"
+ #include "prefab/CodeBlock.h"
+
+ emp::web::Document doc("emp_base");
+
+ std::string code_str =
+ R"(
+ int num = 9;
+ std::cout << num << " is a square number" << std::endl;
+ )";
+ emp::prefab::CodeBlock code_block(code_str, "c++");
+
+ doc << code_block;
+
+**Note**: You will also need to add the following code to the bottom of the body section of your HTML file:
+
+.. code-block:: html
+
+
+
+
+
+
+.. _HighlightJS Library: https://highlightjs.org/
+.. _all languages: https://github.com/highlightjs/highlight.js/blob/master/SUPPORTED_LANGUAGES.md
+
+Collapse
+~~~~~~~~
+The CollapseCouple maintains a group of targets and controllers.
+When a controller is clicked on a web page, all the associated targets will change state (expand/collapse).
+
+By default, the target element will start off closed, but this can be set to open by passing :code:`true` for the :code:`expanded` parameter.
+
+Since the collapse controller and collapse target element will not necessarily directly neighbor eachother, call :code:`GetControllerDiv()` and :code:`GetTargetDiv()` to obtain a vector of all the asspociated controllers and targets, respectively.
+To obtain just one controller or target, pass its index into a get div function call.
+
+Example:
+********
+.. code-block:: cpp
+
+ #include "web/web.h"
+ #include "web/Div.h"
+ #include "prefab/CommentBox.h"
+
+ #include "prefab/Collapse.h"
+
+ emp::web::Document doc("emp_base");
+
+ emp::prefab::CommentBox box1;
+ box1.AddContent("
Box 1
");
+ emp::web::Div btn1;
+ btn1.SetAttr("class", "btn btn-info");
+ btn1 << "Button 1: controls box 1";
+
+ emp::prefab::CollapseCoupling collapse1(btn1, box1, true);
+
+ doc << collapse1.GetControllerDiv(0);
+ doc << collapse1.GetTargetDiv(0);
+
+CommentBox
+~~~~~~~~~~
+A CommentBox is a simple grey comment bubble.
+Content can be added to it using :code:`AddContent()`.
+If there is data you only want to be visible on mobile devices, use :code:`AddMobileContent()`.
+
+Since this class inherits from :code:`web::Div`, you can set styling and attributes with :code:`SetCSS()` and :code:`SetAttr()` respectively.
+You can also stream your CommentBox into other web components with the :code:`<<` operator.
+
+Example:
+********
+.. code-block:: cpp
+
+ #include "web/web.h"
+ #include "prefab/CommentBox.h"
+
+ emp::web::Document doc("emp_base");
+
+ emp::prefab::CommentBox my_box;
+ doc << my_box;
+
+ my_box.AddContent("
Content that shows on all screen sizes
");
+ my_box.AddMobileContent("Content that only shows on small screens");
+ // Web components can also be passed as parameters to AddContent and AddMobileContent
+
+ConfigPanel
+~~~~~~~~~~~
+The ConfigPanel allows developers to easily set up a user interface for their configuration options.
+It allows web apps to be interactive and dynamic, allowing users to change configuration settings within the applicaiton and providing a better user experiance.
+
+Using the ConfigPanel class, a configuration panel is constructed when passed a Config file.
+It uses other Prefabricated components to add styling and structure to the panel.
+Use :code:`GetConfigPanelDiv()` to stream this component into another web component or document.
+
+It is important to note that ConfigPanel instances are destroyed when they go out of scope.
+This causes the form to no longer respond to changes made by the user.
+You will need to initialize an instance outside of :code:`main()` if you would like the user to be able to interact with the panel.
+
+Example:
+********
+.. code-block:: cpp
+
+ #include "web/web.h"
+ #include "prefab/ConfigPanel.h"
+ #include "config/ArgManager.h"
+
+ #include "SampleConfig.h" // Config file
+
+ emp::web::Document doc("emp_base");
+ Config cfg;
+
+ emp::prefab::ConfigPanel config_panel(cfg);
+
+ // apply configuration query params and config files to Config
+ auto specs = emp::ArgManager::make_builtin_specs(&cfg);
+ emp::ArgManager am(emp::web::GetUrlParams(), specs);
+ // cfg.Read("config.cfg");
+ am.UseCallbacks();
+ if (am.HasUnused()) std::exit(EXIT_FAILURE);
+
+ // setup configuration panel
+ config_panel.Setup();
+ doc << config_panel.GetConfigPanelDiv();
+
+FontAwesomeIcon
+~~~~~~~~~~~~~~~
+`FontAwesome`_ is a free library of icons that can be used in web pages.
+
+To use this class:
+
+1. Find the icon you wish to use in the `FontAwesome library`_
+2. Pass :code:`"fa-" + *icon name*` as a parameter to the constructor.
+3. Add the following CSS file to the head of your HTML document.
+
+.. code-block:: html
+
+
+
+Since this class inherits from :code:`web::Element`, you can set styling and attributes with :code:`SetCSS()` and :code:`SetAttr()` respectively.
+You can also stream your FontAwesomeIcon into other web components with the :code:`<<` operator.
+
+Example:
+********
+.. code-block:: cpp
+
+ #include "web/web.h"
+ #include "prefab/FontAwesomeIcon.h"
+
+ emp::web::Document doc("emp_base");
+
+ emp::prefab::FontAwesomeIcon my_icon("fa-paw");
+ doc << my_icon;
+
+ my_icon.AddClass("custom_class");
+
+.. _FontAwesome: https://fontawesome.com/v4.7.0/
+.. _FontAwesome library: https://fontawesome.com/v4.7.0/icons/
+
+LoadingIcon
+~~~~~~~~~~~
+The LoadingIcon class is used to add an animated loading icon.
+One possible use for this icon is to be displayed while the contents of a web page is loading.
+The icon is provided by `FontAwesome`_, so you will need to add its CSS to your HTML file to use this class.
+
+.. code-block:: html
+
+
+
+Since this class inherits from :code:`web::Element`, you can set styling and attributes with :code:`SetCSS()` and :code:`SetAttr()` respectively.
+You can also stream your LoadingIcon into other web components with the :code:`<<` operator.
+
+Example:
+********
+.. code-block:: cpp
+
+ #include "web/web.h"
+ #include "prefab/LoadingIcon.h"
+
+ emp::web::Document doc("emp_base");
+
+ emp::prefab::LoadingIcon spinner;
+ doc << spinner;
+
+LoadingModal
+~~~~~~~~~~~~
+The LoadingModal header file makes adding a loading modal to a web page easy.
+It will appear while the content of the page is rendering and will disappear when the page has completed loading.
+
+This header file is slightly different from the other prefab web tools.
+To place the loading modal on your web page, you must import the LoadingModal.js script into your HTML file right after the opening body tag.
+To close the modal you must call the :code:`CloseLoadingModal()` function in your .cc file after loading the desired content into the doc.
+
+Example:
+********
+.. code-block:: cpp
+
+ // .cc file
+ #include "web/web.h"
+ #include "LoadingModal.h"
+
+ emp::web::Document doc("emp_base");
+
+ // Add elements to the doc a normal
+
+ emp::prefab::CloseLoadingModal();
+
+.. code-block:: html
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Modal
+~~~~~
+The Modal class can be used to create Bootstrap modals that pops up in the middle of the screen.
+
+Since this class inherits from :code:`web::Div`, you can stream your Modal into other web components with the :code:`<<` operator.
+You can also set the background color of the Modal with :code:`SetBackground()` passing it a string with a color name or its hex code value.
+
+Example:
+********
+.. code-block:: cpp
+
+ #include "web/web.h"
+ #include "web/Button.h"
+ #include "prefab/Modal.h"
+
+ emp::web::Document doc("emp_base");
+
+ emp::prefab::Modal modal;
+ doc << modal;
+
+ modal.AddHeaderContent("
Modal Header Section
");
+ modal.AddBodyContent("This is the content of the modal");
+
+ modal.AddFooterContent("Modal Footer Section");
+ UI::Button close_btn([](){;}, "Close");
+ close_btn.SetAttr("class", "btn btn-secondary");
+ modal.AddFooterContent(close_btn);
+ modal.AddButton(close_btn);
+
+ modal.AddClosingX();
+
+ UI::Button modal_btn([](){;}, "Show Modal");
+ doc << modal_btn;
+ modal_btn.SetAttr("class", "btn btn-info");
+ modal.AddButton(modal_btn);
+
+ToggleSwitch
+~~~~~~~~~~~~
+The ToggleSwitch class wraps checkbox input with Bootstrap custom swtich classes.
+If you need to add a CSS class to the Input, do it after the creating the ToggleSwitch instance with :code:`AddClass()`.
+
+
+Since this class inherits from :code:`web::Element`, you can set styling and attributes with :code:`SetCSS()` and :code:`SetAttr()` respectively.
+You can also stream your ToggleSwitch into other web components with the :code:`<<` operator.
+
+Example:
+********
+.. code-block:: cpp
+
+ #include "web/web.h"
+ #include "prefab/ToggleSwitch.h"
+
+ emp::prefab::ToggleSwitch on_switch([](std::string val){}, "Switch Defult On", true, "user_defined_switch_id");
+ doc << on_switch;
+
+ doc << " ";
+
+ emp::prefab::ToggleSwitch off_switch([](std::string val){}, NULL, false);
+ doc << off_switch;
+ off_switch.AddLabel("Switch Defult Off");
+
+Add the link to Bootstrap in the head of your HTML file:
+.. code-block:: html
+
+
diff --git a/examples/Makefile b/examples/Makefile
index 0e276fd4fe..007415d975 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -14,6 +14,7 @@ default:
$(MAKE) -C io/
$(MAKE) -C math/
$(MAKE) -C meta/
+ $(MAKE) -C prefab/
$(MAKE) -C ProjectTemplate/
$(MAKE) -C testing/
$(MAKE) -C tools/
@@ -35,6 +36,7 @@ clean:
$(MAKE) clean -C io/
$(MAKE) clean -C math/
$(MAKE) clean -C meta/
+ $(MAKE) clean -C prefab/
$(MAKE) clean -C ProjectTemplate/
$(MAKE) clean -C testing/
$(MAKE) clean -C tools/
@@ -56,12 +58,15 @@ debug:
$(MAKE) debug -C io/
$(MAKE) debug -C math/
$(MAKE) debug -C meta/
+ $(MAKE) debug -C prefab/
$(MAKE) debug -C ProjectTemplate/
$(MAKE) debug -C testing/
$(MAKE) debug -C tools/
$(MAKE) debug -C web/
web-test:
+ $(MAKE) debug -C prefab/
+ $(MAKE) -C prefab/
$(MAKE) debug -C web/
$(MAKE) -C web/
diff --git a/examples/prefab/Card.cpp b/examples/prefab/Card.cpp
new file mode 100644
index 0000000000..fa0eaca22b
--- /dev/null
+++ b/examples/prefab/Card.cpp
@@ -0,0 +1,34 @@
+// This file is part of Empirical, https://github.com/devosoft/Empirical
+// Copyright (C) Michigan State University, 2020.
+// Released under the MIT Software license; see doc/LICENSE
+
+#include
+
+#include "emp/web/web.hpp"
+#include "emp/prefab/Card.hpp"
+
+namespace UI = emp::web;
+
+UI::Document doc("emp_base");
+
+int main()
+{
+ // Plain Card
+ emp::prefab::Card pCard("STATIC");
+ pCard.AddHeaderContent("Plain card");
+ pCard.AddBodyContent("Plain body content");
+ doc << pCard;
+
+ // Collapsible Card, default open
+ emp::prefab::Card openCard("INIT_OPEN", true);
+ // Header content with bootstrap link properties
+ openCard.AddHeaderContent("Open card", true);
+ openCard.AddBodyContent("Open body content Glyphs Linked title");
+ doc << openCard;
+
+ // Collapsible Card, default closed
+ emp::prefab::Card closedCard("INIT_CLOSED", false);
+ closedCard.AddHeaderContent("Closed card");
+ closedCard.AddBodyContent("Closed body content No Glyphs Plain title");
+ doc << closedCard;
+}
diff --git a/examples/prefab/Card.html b/examples/prefab/Card.html
new file mode 100644
index 0000000000..d370e966cc
--- /dev/null
+++ b/examples/prefab/Card.html
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+Card Examples
+
+
+
+
+
Card Examples
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/prefab/CodeBlock.cpp b/examples/prefab/CodeBlock.cpp
new file mode 100644
index 0000000000..dc172560dd
--- /dev/null
+++ b/examples/prefab/CodeBlock.cpp
@@ -0,0 +1,23 @@
+// This file is part of Empirical, https://github.com/devosoft/Empirical
+// Copyright (C) Michigan State University, 2020.
+// Released under the MIT Software license; see doc/LICENSE
+
+#include
+
+#include "emp/web/web.hpp"
+#include "emp/prefab/CodeBlock.hpp"
+
+emp::web::Document doc("emp_base");
+
+int main()
+{
+ std::cout << "entering main\n";
+ std::string my_code =
+ R"(
+ int num = 9;
+ std::cout << num << " is a square number" << std::endl;
+ )";
+
+ emp::prefab::CodeBlock code_block(my_code, "c++");
+ doc << code_block;
+}
diff --git a/examples/prefab/CodeBlock.html b/examples/prefab/CodeBlock.html
new file mode 100644
index 0000000000..ac91f0a931
--- /dev/null
+++ b/examples/prefab/CodeBlock.html
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+Code Block Examplep
+
+
+
+
+
Code Block Example
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/prefab/Collapse.cpp b/examples/prefab/Collapse.cpp
new file mode 100644
index 0000000000..f53e84f3b0
--- /dev/null
+++ b/examples/prefab/Collapse.cpp
@@ -0,0 +1,49 @@
+// This file is part of Empirical, https://github.com/devosoft/Empirical
+// Copyright (C) Michigan State University, 2020.
+// Released under the MIT Software license; see doc/LICENSE
+
+#include
+
+#include "emp/web/web.hpp"
+#include "emp/web/Div.hpp"
+
+#include "emp/prefab/Collapse.hpp"
+#include "emp/prefab/CommentBox.hpp"
+
+
+namespace UI = emp::web;
+
+UI::Document doc("emp_base");
+
+int main()
+{
+ emp::prefab::CommentBox box1;
+ box1.AddContent("
+
+
+
+
+
diff --git a/examples/prefab/ToggleSwitch.cpp b/examples/prefab/ToggleSwitch.cpp
new file mode 100644
index 0000000000..db2d93a9ff
--- /dev/null
+++ b/examples/prefab/ToggleSwitch.cpp
@@ -0,0 +1,26 @@
+// This file is part of Empirical, https://github.com/devosoft/Empirical
+// Copyright (C) Michigan State University, 2020.
+// Released under the MIT Software license; see doc/LICENSE
+
+#include
+
+#include "emp/web/web.hpp"
+#include "emp/prefab/ToggleSwitch.hpp"
+
+namespace UI = emp::web;
+
+UI::Document doc("emp_base");
+
+int main()
+{
+ emp::prefab::ToggleSwitch on_switch([](std::string val){}, "Switch Defult On", true, "user_defined_switch_id");
+ doc << on_switch;
+
+ doc << " ";
+
+ emp::prefab::ToggleSwitch off_switch([](std::string val){}, NULL, false);
+ doc << off_switch;
+ off_switch.AddLabel("Switch Defult Off");
+
+ std::cout << "end of main... !" << std::endl;
+}
diff --git a/examples/prefab/ToggleSwitch.html b/examples/prefab/ToggleSwitch.html
new file mode 100644
index 0000000000..cd477eee6e
--- /dev/null
+++ b/examples/prefab/ToggleSwitch.html
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+Toggle Switch Example
+
+
+
+
+
Toggle Switch Example
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/prefab/assets/SampleConfig.hpp b/examples/prefab/assets/SampleConfig.hpp
new file mode 100644
index 0000000000..853e116e3d
--- /dev/null
+++ b/examples/prefab/assets/SampleConfig.hpp
@@ -0,0 +1,44 @@
+// Adapted from Emily's memic_model project
+// https://github.com/emilydolson/memic_model
+
+#pragma once
+
+#include "emp/config/config.hpp"
+
+EMP_BUILD_CONFIG( Config,
+ GROUP(MAIN, "Global settings"),
+ VALUE(BOOL_EX, bool, true, "example description"),
+ VALUE(SEED, int, -1, "Random number generator seed"),
+ VALUE(TIME_STEPS, int, 1000, "Number of time steps to run for"),
+ VALUE(PLATE_LENGTH, double, 10.0, "Length of plate in mm"),
+ VALUE(PLATE_WIDTH, double, 6.0, "Width of plate in mm"),
+ VALUE(PLATE_DEPTH, double, 1.45, "Depth of plate in mm"),
+ VALUE(CELL_DIAMETER, double, 20.0, "Cell length and width in microns"),
+ VALUE(INIT_POP_SIZE, int, 100, "Number of cells to seed population with"),
+ VALUE(DATA_RESOLUTION, int, 10, "How many updates between printing data?"),
+
+ GROUP(CELL, "Cell settings"),
+ VALUE(NEUTRAL_MUTATION_RATE, double, .05, "Probability of a neutral mutation (only relevant for phylogenetic signature)"),
+ VALUE(ASYMMETRIC_DIVISION_PROB, double, 0, "Probability of a change in stemness"),
+ VALUE(MITOSIS_PROB, double, .5, "Probability of mitosis"),
+ VALUE(HYPOXIA_DEATH_PROB, double, .25, "Probability of dieing, given hypoxic conditions"),
+ VALUE(AGE_LIMIT, int, 100, "Age over which non-stem cells die"),
+ VALUE(BASAL_OXYGEN_CONSUMPTION, double, .00075, "Base oxygen consumption rate"),
+ VALUE(OXYGEN_CONSUMPTION_DIVISION, double, .00075*5, "Amount of oxygen a cell consumes on division"),
+
+ GROUP(OXYGEN, "Oxygen settings"),
+ VALUE(INITIAL_OXYGEN_LEVEL, double, .5, "Initial oxygen level (will be placed in all cells)"),
+ VALUE(OXYGEN_DIFFUSION_COEFFICIENT, double, .1, "Oxygen diffusion coefficient"),
+ VALUE(DIFFUSION_STEPS_PER_TIME_STEP, int, 100, "Rate at which diffusion is calculated relative to rest of model"),
+ VALUE(OXYGEN_THRESHOLD, double, .1, "How much oxygen do cells need to survive?"),
+ VALUE(KM, double, 0.01, "Michaelis-Menten kinetic parameter"),
+
+ GROUP(TREATMENT, "Treatment settings"),
+ VALUE(RADIATION_DOSES, int, 1, "Number of radiation doses to apply (for use in web interface - use a radiation prescription file for command-line)"),
+ VALUE(RADIATION_DOSE_SIZE, double, 2, "Dose size (Gy) (for use in web interface - use a radiation prescription file for command-line)"),
+ VALUE(RADIATION_PRESCRIPTION_FILE, std::string, "none", "File containing radiation prescription"),
+ VALUE(K_OER, double, 3.28, "Effective OER constant"),
+ VALUE(OER_MIN, double, 1, "OER min constant"),
+ VALUE(OER_ALPHA_MAX, double, 1.75, "OER alpha max constant"),
+ VALUE(OER_BETA_MAX, double, 3.25, "OER alpha (? this is what the paper says but I feel like it's supposed to be beta) max constant"),
+);
diff --git a/examples/web/Makefile b/examples/web/Makefile
index 3272866403..6eebe68f5f 100644
--- a/examples/web/Makefile
+++ b/examples/web/Makefile
@@ -30,13 +30,12 @@ CFLAGS_web_opt := $(CFLAGS_all) $(OFLAGS_web_opt) --js-library ../../include/emp
JS_TARGETS := Animate.js Animate2.js Attributes.js Canvas.js Div.js DP.js Example.js Font.js Graph.js Hello.js Image.js keypress.js Sudoku.js Table.js TextArea.js Tween.js Web.js
-TARGETS := debug-Animate debug-keypress debug-Slate debug-web
-
-default: web
-
CXX := $(CXX_web)
CFLAGS_web := $(CFLAGS_web_opt)
+%.js: %.cpp
+ $(CXX_web) $(CFLAGS_web) $< -o $@
+
debug: CFLAGS_web := $(CFLAGS_web_debug)
debug: $(JS_TARGETS)
@@ -48,16 +47,7 @@ web-debug: CXX := $(CXX_web)
web-debug: CFLAGS := $(CFLAGS_web_debug)
web-debug: all
-native: all
-
-all: $(TARGETS)
-
-$(JS_TARGETS): %.js : %.cpp # $(WEB)/%.h
- $(CXX_web) $(CFLAGS_web) $< -o $@
-
-
-debug-%: $*.cpp
- $(CXX_native) $(CFLAGS_native) $< -o $@
+default: web
clean:
rm -f debug-* $(JS_TARGETS) *.js.map *.js.mem *.wasm *.wasm.map *.wast *~ source/*.o source/*/*.o
diff --git a/include/emp/config/config.hpp b/include/emp/config/config.hpp
index b75852e86b..ecaca5de81 100644
--- a/include/emp/config/config.hpp
+++ b/include/emp/config/config.hpp
@@ -187,8 +187,10 @@ namespace emp {
~ConfigGroup() { ; }
size_t GetSize() const { return entry_set.size(); }
- std::string GetName() const {return name;}
- std::string GetDesc() const {return desc;}
+
+ std::string GetName() const { return name; }
+ std::string GetDesc() const { return desc; }
+
ConfigEntry * GetEntry(size_t id) { return entry_set[id]; }
ConfigEntry * GetLastEntry() { emp_assert(GetSize() > 0); return entry_set.back(); }
@@ -675,6 +677,11 @@ namespace emp {
std::bind(&ConfigManager::UseObject, new_manager, _1) );
}
+ /// Access group_set using this method since it is protected
+ emp::vector GetGroupSet(){
+ return group_set;
+ }
+
};
}
diff --git a/include/emp/prefab/Card.hpp b/include/emp/prefab/Card.hpp
new file mode 100644
index 0000000000..c01a29e391
--- /dev/null
+++ b/include/emp/prefab/Card.hpp
@@ -0,0 +1,113 @@
+#ifndef EMP_CARD_HPP
+#define EMP_CARD_HPP
+
+#include "../tools/string_utils.hpp"
+#include "../web/Div.hpp"
+
+#include "Collapse.hpp"
+#include "FontAwesomeIcon.hpp"
+
+namespace emp {
+namespace prefab {
+ /// Use Card class to create Bootstrap style cards.
+ class Card : public web::Div {
+ private:
+ // ID of card Div to be used in ID of associated card sub components
+ std::string card_base = this->GetID();
+ // all header content will be added here
+ web::Div card_header{emp::to_string(card_base, "_card_header")};
+ // all body content will be added here
+ web::Div card_body{emp::to_string(card_base, "_card_body")};
+ // Asssigns classes to card elements for styling
+ void AddBootstrap() {
+ this->SetAttr("class", "card");
+ card_header.SetAttr("class", "card-header");
+ card_body.SetAttr("class", "card-body");
+ }
+
+ public:
+ /**
+ * @param state indicate whether card should be STAITC, INIT_OPEN, or INIT_CLOSED (default STAITC)
+ * @param show_glyphs should toggle icons show in collapsible card header? (default true)
+ * @param id user defined ID for card Div, (default emscripten generated)
+ */
+ Card(
+ const std::string & state="STATIC",
+ const bool & show_glyphs=true,
+ const std::string & id=""
+ ): web::Div(id) {
+
+ AddBootstrap();
+ if (state == "STATIC") { // static card with no toggle enabled
+ *this << card_header;
+ *this << card_body;
+ } else {
+ // card is collapsible, make the collapse link the head of the card
+ prefab::CollapseCoupling accordion(card_header,
+ card_body,
+ state == "INIT_OPEN",
+ emp::to_string(card_base+ "_card_collapse")
+ );
+ *this << accordion.GetControllerDiv();
+ *this << accordion.GetTargetDiv();
+
+ if (show_glyphs) { // by default add glyphs to a collapsible card
+ prefab::FontAwesomeIcon up("fa-angle-double-up");
+ prefab::FontAwesomeIcon down("fa-angle-double-down");
+ card_header << up;
+ card_header << down;
+ up.AddAttr("class", "toggle_glyph");
+ down.AddAttr("class", "toggle_glyph");
+ }
+ card_header.AddAttr("class", "collapse_toggle_card_header");
+ }
+ }
+
+ /**
+ * Add content to header section of card
+ *
+ * @param val content to be added to header, can be a web element or primitive type
+ * @param link_content indicates whether the content should have Bootstrap link properties (default false)
+ */
+ template
+ void AddHeaderContent(T val, const bool link_content=false) {
+ /*
+ * Note: val can be a controller of a target area (made with CollapseCoupling class)
+ * but when added to header of the card, it will also trigger the card
+ * to collapse/expand
+ */
+ if (link_content) {
+ /*
+ * Add bootstrap link properities to content (hover, underline, etc.),
+ * but does not set a target or href because it is assumed that
+ * this content will control the card collapse, which is done in the
+ * constructor.
+ */
+ web::Div btn_link;
+ btn_link.SetAttr("class", "btn-link");
+ card_header << btn_link << val;
+ } else {
+ card_header << val;
+ }
+ }
+
+ /**
+ * Add content to body section of card
+ *
+ * @param val can be a web element or primitive type
+ */
+ template
+ void AddBodyContent(T val) {
+ card_body << val;
+ }
+
+ /*
+ * TODO: override operator<< so that it streams into body of card
+ * Methods tried so far (without success) are listed in
+ * Issue #345 (https://github.com/devosoft/Empirical/issues/345)
+ */
+ };
+}
+}
+
+#endif
diff --git a/include/emp/prefab/CodeBlock.hpp b/include/emp/prefab/CodeBlock.hpp
new file mode 100644
index 0000000000..07b4f8fe43
--- /dev/null
+++ b/include/emp/prefab/CodeBlock.hpp
@@ -0,0 +1,59 @@
+#ifndef EMP_CODE_BLOCK_HPP
+#define EMP_CODE_BLOCK_HPP
+
+#include "../base/errors.hpp"
+#include "../tools/string_utils.hpp"
+#include "../web/Element.hpp"
+#include "../web/Widget.hpp"
+
+namespace emp {
+namespace prefab {
+ /**
+ * Use CodeBlock class to easily add code to a web
+ * application that is highlighted based on the language used.
+ */
+ class CodeBlock: public web::Element {
+ private:
+ // code element to hold user's code
+ web::Element code{emp::to_string("code")};
+
+ public:
+ /**
+ * List of all supported languages: https://highlightjs.org/static/demo/
+ *
+ * @note Due to JavaScript callback functions, can only set code for block
+ * in constructor.
+ *
+ * @param code_block string of the code to be placed in code block
+ * @param lang programming language to base syntax highlighting
+ */
+ CodeBlock(const std::string code_block, const std::string lang, const std::string & id="")
+ : web::Element("pre", id) {
+ this->SetAttr("class", lang);
+ // trigger HighlightJS library to apply syntax highlighting
+ this->RegisterUpdateJS([]() {
+ emscripten_run_script("hljs.initHighlighting.called = false; hljs.initHighlighting();");
+ });
+ /*
+ * make sure special characters (", ', &, <, >) appear as their symbol,
+ * not rendered as html on web page
+ */
+ code << emp::to_web_safe_string(code_block);
+ *this << code;
+ }
+
+ /*
+ * TODO: << operator throws error
+ * The method below throws an error when trying to stream
+ * a code block into anything else (web element, html).
+ *
+ * template
+ * void operator<<(T invalid) {
+ * emp::LibraryError("Not allowed to add code to the code block after construction due to JavaScript callback order");
+ * }
+ */
+ };
+}
+}
+
+#endif
diff --git a/include/emp/prefab/Collapse.hpp b/include/emp/prefab/Collapse.hpp
new file mode 100644
index 0000000000..1d4085a520
--- /dev/null
+++ b/include/emp/prefab/Collapse.hpp
@@ -0,0 +1,221 @@
+#ifndef EMP_COLLAPSE_HPP
+#define EMP_COLLAPSE_HPP
+
+#include "../tools/string_utils.hpp"
+#include "../web/Div.hpp"
+#include "../web/Widget.hpp"
+#include "../web/_FacetedWidget.hpp"
+
+namespace emp {
+namespace prefab {
+ namespace internal {
+
+ /**
+ * CollpaseController class adds necessary html attributes to controller
+ * to function as the controller for a group or groups of target areas.
+ * Only ever called by CollapseCoupling class.
+ */
+ class CollapseController {
+ private:
+ web::Div inner_controller; // reference to controller passed to constructor
+
+ public:
+ /**
+ * @param controller web element that cause target area(s) to expand/collapse when clicked
+ * @param expanded whether or not the target(s) are initially in an expanded/open state
+ */
+ template
+ CollapseController(
+ T controller,
+ const std::string controls_class,
+ bool expanded, std::string id=""
+ ) : inner_controller(id) {
+
+ inner_controller = controller;
+ inner_controller.SetAttr(
+ "role", "button",
+ "data-toggle", "collapse"
+ );
+ inner_controller.AddAttr(
+ "data-target", "." + controls_class,
+ "aria-controls", "." + controls_class,
+ "class", "collapse_toggle"
+ );
+
+ inner_controller.SetAttr("aria-expanded", expanded ? "true" : "false");
+
+ if (!expanded) {
+ inner_controller.AddAttr("class", "collapsed");
+ }
+ }
+
+ web::Div & GetLinkDiv() { return inner_controller; }
+ };
+ }
+
+ /**
+ * CollapseCoupling class maintains a group of targets and controllers.
+ * When a controller is clicked on a web page, all the associated targets
+ * will change state (expand/collapse).
+ */
+ class CollapseCoupling {
+ private:
+ // all web elements that expand/collapse with this coupling
+ emp::vector targets;
+ // all web elements that control expanding/collapsing with this coupling
+ emp::vector controllers;
+ // class associated with this
+ std::string target_class;
+ // counter used to generate unique class names, shared by all instances of this class
+ inline static int counter = 0;
+
+ public:
+ /**
+ * Constructor which takes web elements as the controller and target.
+ * It will put each in a vector and call the constructor which takes
+ * vectors as controller and target parameters.
+ */
+ CollapseCoupling(
+ web::Widget controller,
+ web::Widget target,
+ const bool expanded=false,
+ const std::string in_class=""
+ ) : CollapseCoupling(emp::vector{controller},
+ emp::vector{target}, expanded, in_class) { ; }
+
+ /**
+ * Constructor which takes vectors of web elements as the controller and target
+ * It will create the collapse coupling by adding necessary HTML attributes
+ * to all controllers and targets
+ */
+ CollapseCoupling(
+ emp::vector controllers,
+ emp::vector targets,
+ const bool expanded=false,
+ const std::string in_class=""
+ ) {
+ /*
+ * if a class is defined by the user, use it
+ * Otherwise generate a unique class
+ */
+ if (in_class == "") {
+ target_class = "emp__collapse_class_" + std::to_string(counter);
+ counter++;
+ } else {
+ target_class = in_class;
+ }
+
+ // add controllers to this coupling
+ for (auto & widget : controllers) {
+ AddController(widget, expanded);
+ }
+ // add targets to this coupling
+ for (auto & widget : targets) {
+ AddTarget(widget, expanded);
+ }
+ }
+
+ /*
+ * TODO: Ideally, this constructor will be templated and can handle any input that is
+ * not a Widget or vector of Widgets.
+ * When we tried this before, all input would go through to this constructor.
+ * This caused issues when it tried to stream widgets into a div but the widget
+ * already had another parent.
+ *
+ * Note: Maybe if we web::internal::FacetedWidget intead of web::Widget in the
+ * first constructor, templating this constructor will work?
+ */
+ /**
+ * Constructor which takes strings as the controller and target
+ * It will put each in a Div element and call the constructor which takes
+ * web elements as controller and target parameters.
+ */
+ CollapseCoupling(
+ const std::string controller,
+ const std::string target,
+ const bool expanded=false,
+ const std::string in_class=""
+ ): CollapseCoupling(web::Div{} << controller, web::Div{} << target, expanded, in_class) { ; }
+
+ /**
+ * Adds a controller to the vector of controllers for this CollapseCouple
+ *
+ * @param controller new controller to add to coupling is of type Widget
+ * @param expaned initial state of the target(s), is it expaned or not?
+ */
+ void AddController(web::Widget controller, const bool expanded) {
+ internal::CollapseController controller_widget(controller, target_class, expanded);
+ controllers.push_back(controller_widget.GetLinkDiv());
+ }
+
+ /** Adds a controller to the vector of controllers for this CollapseCouple.
+ *
+ * @param controller new controller to add to coupling is of type string
+ * @param expaned initial state of the target(s), is it expaned or not?
+ */
+
+ /*
+ * TODO: Ideally, this method would be templated, but running into same issues
+ * as when trying to do this with the constructor
+ */
+ void AddController(const std::string controller, const bool expanded) {
+ AddController(web::Div{} << controller, expanded);
+ }
+
+ /**
+ * Adds a target to the vector of targets for this CollapseCouple
+ *
+ * @param widget new target to add to coupling is a web element
+ * @param expaned initial state of the target(s), is it expaned or not?
+ */
+ void AddTarget(web::internal::FacetedWidget widget, const bool expanded) {
+ if (expanded) {
+ widget.AddAttr("class", "collapse show");
+ } else {
+ widget.AddAttr("class", "collapse");
+ }
+ widget.AddAttr("class", target_class);
+ targets.push_back(widget);
+ }
+
+ /**
+ * Adds a target to the vector of targets for this CollapseCouple
+ *
+ * @param widget new target to add to coupling is a string
+ * @param expaned initial state of the target(s), is it expaned or not?
+ */
+
+ /*
+ * If the target is not a web widget, place it in a div and call the other AddTarget function
+ * TODO: Ideally, this method would be templated, but running into same issues
+ * as when trying to do this with the constructor
+ */
+ void AddTarget(const std::string target, const bool expanded) {
+ AddTarget(web::Div{} << target, expanded);
+ }
+
+ /// Returns the target_class associated with this CollapseCouple
+ std::string GetTargetClass() { return target_class; }
+
+ /*
+ * Functions used to retrieve controllers and targets
+ * TODO: In the future, add capability to call controllers and targets by name
+ * (Like dictionary accesses, key/value pairs)
+ */
+
+ /// Returns the vector of all controllers associated with this CollapseCouple
+ emp::vector & GetControllerDivs() { return controllers; }
+
+ /// Returns the controller at the given index
+ web::Widget & GetControllerDiv(const int index=0) { return controllers[index]; }
+
+ /// Returns the vector of all targets associated with this CollapseCouple
+ emp::vector & GetTargetDivs() { return targets; }
+
+ /// Returns the target at the given index
+ web::Widget & GetTargetDiv(const int index=0) { return targets[index]; }
+ };
+}
+}
+
+#endif
diff --git a/include/emp/prefab/CommentBox.hpp b/include/emp/prefab/CommentBox.hpp
new file mode 100644
index 0000000000..57418afede
--- /dev/null
+++ b/include/emp/prefab/CommentBox.hpp
@@ -0,0 +1,78 @@
+#ifndef EMP_COMMENT_BOX_HPP
+#define EMP_COMMENT_BOX_HPP
+
+#include "../tools/string_utils.hpp"
+#include "../web/Div.hpp"
+
+/*
+ * TODO: When prefab tools for adding mobile only and desktop only
+ * content are created, remove AddMobileContent(), desktop_content
+ * and mobile_content divs, and ConfigPanel as a friend class.
+ * AddConent() should stream into all_content div.
+ */
+
+namespace emp {
+namespace prefab {
+ class ConfigPanel;
+ /**
+ * Use CommentBox class to create a light grey "comment bubble".
+ * Optionally, it can contain text and other web elements.
+ */
+ class CommentBox: public web::Div {
+ friend prefab::ConfigPanel;
+ private:
+ // ID for the comment box Div
+ std::string box_base = this->GetID();
+ // Provides stylized "carrot" for box
+ web::Div triangle{emp::to_string(box_base, "_triangle")};
+ // Contains Divs for mobile and desktop content
+ web::Div all_content{emp::to_string(box_base, "_all_content")};
+ // Content that shows on all screen sizes
+ web::Div desktop_content{emp::to_string(box_base, "_desktop_content")};
+ // Content that shows only on mobile devices
+ web::Div mobile_content{emp::to_string(box_base, "_mobile_content")};
+
+ protected:
+ /// Add content only to be shown on small screens
+
+ /*
+ * TODO: In the future, remove this method and instead
+ * have a prefab tool that can add mobile content to
+ * web element.
+ */
+ template
+ void AddMobileContent(T val) {
+ mobile_content << val;
+ }
+
+ public:
+ CommentBox(const std::string id="") : web::Div(id) {
+ *this << triangle;
+ *this << all_content;
+ all_content << desktop_content;
+ all_content << mobile_content;
+
+ triangle.SetAttr("class", "commentbox_triangle");
+ all_content.SetAttr("class", "commentbox_content");
+ mobile_content.SetAttr("class", "mobile_commentbox");
+ }
+
+ /**
+ * Take input of any type and add it to the comment box.
+ * Content will show on all screen sizes.
+ */
+
+ /*
+ * TODO: override the input operator to stream into the correct div
+ * See issue #345 (https://github.com/devosoft/Empirical/issues/345)
+ * for methods tried already.
+ */
+ template
+ void AddContent(T val) {
+ desktop_content << val;
+ }
+ };
+}
+}
+
+#endif
diff --git a/include/emp/prefab/ConfigPanel.hpp b/include/emp/prefab/ConfigPanel.hpp
new file mode 100644
index 0000000000..65ed8c695a
--- /dev/null
+++ b/include/emp/prefab/ConfigPanel.hpp
@@ -0,0 +1,481 @@
+#ifndef EMP_CONFIG_WEB_INTERFACE_HPP
+#define EMP_CONFIG_WEB_INTERFACE_HPP
+
+#include