diff --git a/.gitignore b/.gitignore
index ad7d22a..fa842ad 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,3 +26,10 @@
Network Trash Folder
Temporary Items
.apdisk
+
+# Linux
+# =========================
+
+# Backup files produced by some editors
+*~
+*.bak
diff --git a/README.md b/README.md
index f345ed2..8090091 100644
--- a/README.md
+++ b/README.md
@@ -48,7 +48,7 @@ Download the [Repository](https://github.com/s00500/ESPUI/archive/master.zip), G
## Getting started
-ESPUI serves several Files to the browser to build up its webinterface. This can be achieved in 2 wasy:
+ESPUI serves several files to the browser to build up its webinterface. This can be achieved in 2 ways:
*PROGMEM* or *SPIFFS*
*When `ESPUI.begin()` is called the default is serving files from Memory and ESPUI should work out of the box!*
@@ -159,7 +159,14 @@ The library is designed to be easy to use and can still be extended with a lot o
# Notes for Development
-All changes to the client side files can be made in the examples/gui/data directory. Using the file uploader thin can be used for development. After this you have to compress them and then you can gzip them. I wrote a little useful jsfiddle for this, [CHECK IT OUT](https://jsfiddle.net/s00500/yvLbhuuv/)
+If you want to work on the HTML/CSS/JS files, do make changes in the `examples/gui/data` directory. When you need to transfer that code to the ESP, run `tools/prepare_static_ui_sources.py -a` (this script needs python3 with the modules htmlmin, jsmin and csscompressor).
+This will generate a) minified files next to the original files to be uploaded with the ESP32 sketch data uploader mentioned above and b) the C header files in `src` that contain the minified and gzipped HTML/CSS/JS data (which are used by the **prepareFileSystem** example sketch or when they are served from PROGMEM; see above in the section "Getting started").
+Alternatively, you can duplicate the `examples/gui` directory and work on the copy. Then specify the `--source` and `--target` arguments to the `prepare_static_ui_sources.py` script (run the script without arguments for help).
+
+If you don't have a python environment, you need to minify and gzip the HTML/CSS/JS files manually. I wrote a little useful jsfiddle for this, [see here](https://jsfiddle.net/s00500/yvLbhuuv/).
+
+If you change something in HTML/CSS/JS and want to create a pull request, please do include the minified versions and corresponding C header files in your commits.
+
# Contribute
Liked this Library? You can **support** me by sending me a :coffee: [Coffee](https://paypal.me/lukasbachschwell/3).
diff --git a/examples/gui/data/index.min.htm b/examples/gui/data/index.min.htm
new file mode 100644
index 0000000..b135bf6
--- /dev/null
+++ b/examples/gui/data/index.min.htm
@@ -0,0 +1 @@
+
Control
Control
Offline
\ No newline at end of file
diff --git a/tools/prepare_static_ui_sources.py b/tools/prepare_static_ui_sources.py
index 5be705e..671dd12 100755
--- a/tools/prepare_static_ui_sources.py
+++ b/tools/prepare_static_ui_sources.py
@@ -19,21 +19,27 @@ const uint8_t {constant}_GZIP[{gziplen}] PROGMEM = {{ {gzipdata} }};
def parse_arguments(args=None):
parser = argparse.ArgumentParser(
- description="Prepares ESPUI header files by minifying and gzipping JS and CSS source files.")
- parser.add_argument("--sources", "-s", dest="sources", default="../examples/gui/data",
- help="Sources directory containing CSS or JS files")
- parser.add_argument("--target", "-t", dest="target", default="../src",
- help="Target directory containing header files")
+ description="Prepares ESPUI header files by minifying and gzipping HTML, JS and CSS source files.")
+ parser.add_argument("--auto", "--all", "-a", dest="auto", action="store_true",
+ help="Automatically find all source files in examples/gui/data/ and write C header files to src/")
+ parser.add_argument("--source", "--sources", "-s", dest="sources", default=None,
+ help="Sources directory containing CSS or JS files OR one specific file to minify")
+ parser.add_argument("--target", "-t", dest="target", default=None,
+ help="Target directory containing C header files OR one C header file")
parser.add_argument("--nostoremini", "-m", action="store_false", dest="storemini",
- help="Store intermediate minified files")
+ help="Do not store intermediate minified files next to the originals (i.e. only write to the C header files)")
args = parser.parse_args(args)
+ if not args.auto and (not args.sources or not args.target):
+ print("ERROR: You need to specify either --auto or both --source and --target\n")
+ parser.print_help()
+ sys.exit(1)
return args
def get_context(infile, outfile):
infile = os.path.realpath(infile)
dir, name, type = (os.path.basename(os.path.dirname(infile)), os.path.basename(infile).split(os.path.extsep)[0], os.path.basename(infile).split(os.path.extsep)[-1] )
- type = type.strip(".")
- if dir == type:
+ type = type.strip(".").lower()
+ if dir.lower() == type:
dir = os.path.basename(os.path.dirname(os.path.dirname(infile)))
if type == "htm":
type = 'html'
@@ -60,7 +66,11 @@ def perform_gzip(c):
def perform_minify(c):
with open(c['infile']) as infile:
- minifier = cssminify if c['type'] == 'css' else jsminify if c['type'] == 'js' else htmlminify
+ minifier = {
+ 'css': cssminify,
+ 'js': jsminify,
+ 'html': htmlminify
+ }.get(c['type']) or htmlminify
print(" Using %s minifier" % c['type'])
c['minidata'] = minifier(infile.read())
return perform_gzip(c)
@@ -93,15 +103,32 @@ def process_dir(sourcedir, outdir, recursive=True, storemini=True):
process_file(f, outdir, storemini)
elif not os.path.isfile(f.replace(".min.", ".")):
process_file(f, outdir, storemini)
+
+def check_args(args):
+ abort = 0
+ if not os.path.exists(args.sources):
+ print("ERROR: Source %s does not exist" % args.sources)
+ abort += 2
+ if not os.path.isdir(os.path.dirname(args.target)):
+ print("ERROR: Parent directory of target %s does not exist" % args.target)
+ abort += 4
+ if os.path.isdir(args.sources) and not os.path.isdir(args.target):
+ print("ERROR: Source %s is a directory, target %s is not" % (args.sources, args.target))
+ abort += 8
+ if abort > 0:
+ print("Aborting.")
+ sys.exit(abort)
def main(args):
- if not args.sources is None:
- if os.path.isfile(args.sources):
- print("Source %s is a file, will process one file only." % args.sources)
- process_file(args.sources, args.target, storemini = args.storemini)
- elif os.path.isdir(args.sources):
- print("Source %s is a directory, searching for files recursively..." % args.sources)
- process_dir(args.sources, args.target, recursive = True, storemini = args.storemini)
+ args.sources = os.path.realpath(args.sources or os.sep.join((os.path.dirname(os.path.realpath(__file__)), "..", "examples", "gui", "data")))
+ args.target = os.path.realpath(args.target or os.sep.join((os.path.dirname(os.path.realpath(__file__)), "..", "src")))
+ check_args(args)
+ if os.path.isfile(args.sources):
+ print("Source %s is a file, will process one file only." % args.sources)
+ process_file(args.sources, args.target, storemini = args.storemini)
+ elif os.path.isdir(args.sources):
+ print("Source %s is a directory, searching for files recursively..." % args.sources)
+ process_dir(args.sources, args.target, recursive = True, storemini = args.storemini)
if __name__ == "__main__" and "get_ipython" not in dir():
main(parse_arguments())