From 811eee602fcd483d7b563eeb7b5a96c6d641ccb7 Mon Sep 17 00:00:00 2001
From: Jan Grewe <jan@faked.org>
Date: Sun, 4 Jun 2023 20:38:45 +0200
Subject: [PATCH] gzip data

---
 .gitignore                          |   1 +
 data/jquery.simple.websocket.min.js |   1 -
 {data => data_src}/favicon.ico      | Bin
 {data => data_src}/index.html       |  43 +++++++++++++--------
 {data => data_src}/main.js          |   5 ++-
 {data => data_src}/styles.css       |   0
 gzip_data.py                        |  58 ++++++++++++++++++++++++++++
 platformio.ini                      |   2 +
 8 files changed, 91 insertions(+), 19 deletions(-)
 delete mode 100644 data/jquery.simple.websocket.min.js
 rename {data => data_src}/favicon.ico (100%)
 rename {data => data_src}/index.html (66%)
 rename {data => data_src}/main.js (97%)
 rename {data => data_src}/styles.css (100%)
 create mode 100644 gzip_data.py

diff --git a/.gitignore b/.gitignore
index 20ea4e0..dba6614 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@
 .vscode/launch.json
 .vscode/ipch
 /src/config.h
+/data
diff --git a/data/jquery.simple.websocket.min.js b/data/jquery.simple.websocket.min.js
deleted file mode 100644
index 7dd5539..0000000
--- a/data/jquery.simple.websocket.min.js
+++ /dev/null
@@ -1 +0,0 @@
-!function(a){"object"==typeof module&&"string"===module.exports?module.exports=a(jQuery):a(jQuery)}(function(a){var b=function(b){if(this._isEmpty(b,"url"))throw new Error('Missing argument, example usage: $.simpleWebSocket({ url: "ws://127.0.0.1:3000" }); ');this._opt=b,this._ws=null,this._reConnectTries=60,this._reConnectDeferred=null,this._closeDeferred=null,this._dataType=this._prop(this._opt,"dataType","json"),this._listeners=[],this._onOpen=this._prop(this._opt,"onOpen",null),this._onClose=this._prop(this._opt,"onClose",null),this._onError=this._prop(this._opt,"onError",null);var c=this;return this._api=function(){return{connect:function(){return a.extend(c._api,c._reConnect.apply(c,[]))},isConnected:function(a){return a?(a.apply(this,[c._isConnected.apply(c,[])]),c._api):c._isConnected.apply(c,[])},send:function(b){return a.extend(c._api,c._send.apply(c,[b]))},listen:function(b){return a.extend(c._api,c._listenReconnect.apply(c,[b]))},remove:function(a){return c._remove.apply(c,[a]),c._api},removeAll:function(){return c._removeAll.apply(c,[]),c._api},close:function(){return c._reset.apply(c,[]),a.extend(c._api,c._close.apply(c,[]))},getWsAdapter:function(){return this._ws}}}(),this._api};return b.prototype={_createWebSocket:function(a){var b=null;if(a.protocols)if(void 0===window.MozWebSocket){if(!window.WebSocket)throw new Error("Error, websocket could not be initialized.");b=new WebSocket(a.url,a.protocols)}else b=new MozWebSocket(a.url,a.protocols);else if(void 0===window.MozWebSocket){if(!window.WebSocket)throw new Error("Error, websocket could not be initialized.");b=new WebSocket(a.url)}else b=new MozWebSocket(a.url);return b},_bindSocketEvents:function(b,c){var d=this;a(b).bind("open",c.open).bind("close",c.close).bind("message",function(a){try{if("function"==typeof c.message)if(d._dataType&&"json"===d._dataType.toLowerCase()){var b=JSON.parse(a.originalEvent.data);c.message.call(this,b)}else if(d._dataType&&"xml"===d._dataType.toLowerCase()){var e=new DOMParser,f=e.parseFromString(a.originalEvent.data,"text/xml");c.message.call(this,f)}else c.message.call(this,a.originalEvent.data)}catch(a){"function"==typeof c.error&&c.error.call(this,a)}}).bind("error",function(a){"function"==typeof c.error&&c.error.call(this,a)})},_webSocket:function(a){var b=this._createWebSocket(a);return this._bindSocketEvents(b,a),b},_getSocketEventHandler:function(a){var b=this;return{open:function(c){b._onOpen&&b._onOpen.apply(b,[c]);var d=this;a&&a.resolve(d)},close:function(c){b._closeDeferred&&b._closeDeferred.resolve(),b._onClose&&b._onClose.apply(b,[c]),a&&a.rejectWith(c)},message:function(a){for(var c=0,d=b._listeners.length;c<d;c++)try{b._listeners[c].deferred.notify.apply(b,[a])}catch(a){}},error:function(c){b._ws=null,b._onError&&b._onError.apply(b,[c]);for(var d=0,e=b._listeners.length;d<e;d++)b._listeners[d].deferred.reject.apply(b,[c]);a&&a.rejectWith.apply(b,[c])}}},_connect:function(){var b=a.Deferred();if(this._ws)if(2===this._ws.readyState)this._ws.close();else if(3===this._ws.readyState)this._ws.close();else{if(0===this._ws.readyState)return b.promise();if(1===this._ws.readyState)return b.resolve(this._ws),b.promise()}return this._ws=this._webSocket(a.extend(this._opt,this._getSocketEventHandler(b))),b.promise()},_reset:function(){this._reConnectTries=this._prop(this._opt,"attempts",60),this._reConnectDeferred=a.Deferred()},_close:function(){return this._closeDeferred=a.Deferred(),this._ws&&(this._ws.close(),this._ws=null),this._closeDeferred.promise()},_isConnected:function(){return null!==this._ws&&1===this._ws.readyState},_reConnectTry:function(){var a=this;this._connect().done(function(){a._reConnectDeferred.resolve.apply(a,[a._ws])}).fail(function(b){a._reConnectTries--,a._reConnectTries>0?window.setTimeout(function(){a._reConnect.apply(a,[])},a._prop.apply(a,[a._opt,"timeout",1e4])):a._reConnectDeferred.rejectWith.apply(a,[b])})},_reConnect:function(){var a=this;return null===this._reConnectDeferred?this._reset():"resolved"===this._reConnectDeferred.state()?this._reset():"rejected"===this._reConnectDeferred.state()&&this._reset(),this._ws&&1===this._ws.readyState?this._reConnectDeferred.resolve(this._ws):this._reConnectTry(),a._reConnectDeferred.promise.apply(a,[])},_preparePayload:function(a){return this._opt.dataType&&"text"===this._opt.dataType.toLowerCase()?a:this._opt.dataType&&"xml"===this._opt.dataType.toLowerCase()?a:(this._opt.dataType&&this._opt.dataType.toLowerCase(),JSON.stringify(a))},_send:function(b){var c=this,d=a.Deferred();return function(a){c._reConnect.apply(c,[]).done(function(b){b.send(a),d.resolve.apply(c,[c._api])}).fail(function(a){d.rejectWith.apply(c,[a])})}(this._preparePayload(b)),d.promise()},_indexOfListener:function(a){for(var b=0,c=this._listeners.length;b<c;b++)if(this._listeners[b].listener===a)return b;return-1},_isEmpty:function(a,b){return"string"===a||(null===a||(void 0===b||(null===b||(""===b||(void 0===a[b]||null===a[b])))))},_prop:function(a,b,c){return this._isEmpty(a,b)?c:a[b]},_listen:function(b){var c=this,d=a.Deferred();return c._reConnect.apply(c,[]).done(function(){d.progress(function(){b.apply(this,arguments)}),c._remove.apply(c,[b]),c._listeners.push({deferred:d,listener:b})}).fail(function(a){d.reject(a)}),d.promise()},_listenReconnect:function(b){var c=a.Deferred(),d=this;return this._listen(b).fail(function(){c.notify(arguments),d._listenReconnect.apply(d,[b])}).done(function(){c.resolve()}),c.promise()},_remove:function(a){var b=this._indexOfListener(a);0<=b&&(this._listeners[b].deferred.resolve(),this._listeners.splice(b,1))},_removeAll:function(){for(var a=0,b=this._listeners.length;a<b;a++)this._listeners[a].deferred.resolve();this._listeners=[]}},a.extend({simpleWebSocket:function(a){return new b(a)}}),a.simpleWebSocket});
\ No newline at end of file
diff --git a/data/favicon.ico b/data_src/favicon.ico
similarity index 100%
rename from data/favicon.ico
rename to data_src/favicon.ico
diff --git a/data/index.html b/data_src/index.html
similarity index 66%
rename from data/index.html
rename to data_src/index.html
index 1f75d39..9ce4494 100644
--- a/data/index.html
+++ b/data_src/index.html
@@ -5,7 +5,8 @@
   <meta charset="utf-8">
   <meta name="viewport" content="width=device-width, initial-scale=1">
   <title>Matrix of Life</title>
-  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
+  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"
+    integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
   <link href="styles.css" rel="stylesheet">
 </head>
 
@@ -22,14 +23,16 @@
           </div>
         </h5>
         <div class="card-body text-center">
-      
+
           <div class="row mb-3 px-3">
             <label for="rangeBrightness" class="col-form-label">Brightness</label>
-            <input type="range" class="form-range rangeConfig" id="rangeBrightness" data-name="brightness" min="1" max="100" steps="1">
+            <input type="range" class="form-range rangeConfig" id="rangeBrightness" data-name="brightness" min="1"
+              max="100" steps="1" disabled>
           </div>
           <div class="row mb-3 px-3">
             <label for="rangeSpeed" class="col-form-label">Speed</label>
-            <input type="range" class="form-range rangeConfig" id="rangeSpeed" data-name="interval" min="1" max="100" steps="10">
+            <input type="range" class="form-range rangeConfig" id="rangeSpeed" data-name="interval" min="1" max="100"
+              steps="10" disabled>
           </div>
 
         </div>
@@ -37,19 +40,21 @@
         <div class="card-footer text-body-secondary py-3">
           <div class="row">
             <div class="col-6">
-              <button type="button" class="btn btn-sm btn-primary btnAction" data-action="addGlider">Add Glider</button>
+              <button type="button" class="btn btn-sm btn-primary btnAction" data-action="addGlider" disabled>Add
+                Glider</button>
             </div>
             <div class="col-6 text-end">
-              <button type="button" class="btn btn-sm btn-outline-secondary me-1" data-bs-toggle="collapse" data-bs-target="#debug">Debug</button>       
-              <button type="button" class="btn btn-sm btn-danger btnAction " data-action="reboot">Reboot</button>
+              <button type="button" class="btn btn-sm btn-outline-secondary me-1" data-bs-toggle="collapse"
+                data-bs-target="#debug">Debug</button>
+              <button type="button" class="btn btn-sm btn-danger btnAction " data-action="reboot"
+                disabled>Reboot</button>
             </div>
           </div>
-        </div>    
-
+        </div>
 
       </div>
 
-    </div>
+    </div> <!-- main -->
 
     <div class="col-lg-4 offset-lg-4 mt-3 collapse" id="debug">
 
@@ -65,20 +70,26 @@
               <span class="input-group-text">➡️</span>
               <input type="text" id="prompt" class="form-control form-control-sm fs-6 font-monospace">
             </div>
-            
+
           </div>
-        
+
         </div>
       </div>
 
-    </div>
+    </div> <!-- debug -->
 
   </div>
 
-  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script>
-  <script src="https://code.jquery.com/jquery-3.7.0.min.js" integrity="sha256-2Pmvv0kuTBOenSvLm6bvfBSSHrUJ+3A7x6P5Ebd07/g=" crossorigin="anonymous"></script>
-  <script src="jquery.simple.websocket.min.js"></script>
+  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"
+    integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz"
+    crossorigin="anonymous"></script>
+  <script src="https://code.jquery.com/jquery-3.7.0.min.js"
+    integrity="sha256-2Pmvv0kuTBOenSvLm6bvfBSSHrUJ+3A7x6P5Ebd07/g=" crossorigin="anonymous"></script>
+  <script
+    src="https://cdn.jsdelivr.net/gh/jbloemendal/jquery-simple-websocket@master/dist/jquery.simple.websocket.min.js"
+    crossorigin="anonymous"></script>
   <script src="main.js"></script>
+
 </body>
 
 </html>
diff --git a/data/main.js b/data_src/main.js
similarity index 97%
rename from data/main.js
rename to data_src/main.js
index 6be0cf0..368dbc0 100644
--- a/data/main.js
+++ b/data_src/main.js
@@ -1,6 +1,5 @@
 var wsUrl = 'ws://' + document.location.host + '/ws'
 
-var ws = null;
 var config = {
     brightness: 0,
     interval: 0,
@@ -75,6 +74,7 @@ function wsSend(msg) {
     });
 }
 
+var ws;
 function startSocket() {
     ws = $.simpleWebSocket({
         url: wsUrl,
@@ -93,6 +93,7 @@ function startSocket() {
             addMessage("❗️ error");
         },
     });
+
     ws.listen(function (data) {
         var json = JSON.stringify(data);
         var msg = "⬅️ " + json;
@@ -111,6 +112,6 @@ function loadConfig(data) {
     $('#rangeSpeed').val(mapRange(data.interval, 1000, 10, 1, 100));
 }
 
-function mapRange (number, inMin, inMax, outMin, outMax) {
+function mapRange(number, inMin, inMax, outMin, outMax) {
     return Math.round((number - inMin) * (outMax - outMin) / (inMax - inMin) + outMin);
 }
diff --git a/data/styles.css b/data_src/styles.css
similarity index 100%
rename from data/styles.css
rename to data_src/styles.css
diff --git a/gzip_data.py b/gzip_data.py
new file mode 100644
index 0000000..97ff223
--- /dev/null
+++ b/gzip_data.py
@@ -0,0 +1,58 @@
+Import('env', 'projenv')
+
+import os
+import gzip
+import shutil
+import glob
+
+def prepare_www_files(source, target, env):
+    #WARNING -  this script will DELETE your 'data' dir and recreate an empty one to copy/gzip files from 'data_src'
+    #           so make sure to edit your files in 'data_src' folder as changes madt to files in 'data' woll be LOST
+    #           
+    #           If 'data_src' dir doesn't exist, and 'data' dir is found, the script will autimatically
+    #           rename 'data' to 'data_src
+
+
+    #add filetypes (extensions only) to be gzipped before uploading. Everything else will be copied directly
+    filetypes_to_gzip = ['html', 'js', 'css', 'ico']
+    
+    print('[COPY/GZIP DATA FILES]')
+
+    data_dir = env.get('PROJECT_DATA_DIR')
+    data_src_dir = os.path.join(env.get('PROJECT_DIR'), 'data_src')
+
+    if(os.path.exists(data_dir) and not os.path.exists(data_src_dir) ):
+        print('  "data" dir exists, "data_src" not found.')
+        print('  renaming "' + data_dir + '" to "' + data_src_dir + '"')
+        os.rename(data_dir, data_src_dir)
+
+    if(os.path.exists(data_dir)):
+        print('  Deleting data dir ' + data_dir)
+        shutil.rmtree(data_dir)
+
+    print('  Re-creating empty data dir ' + data_dir)
+    os.mkdir(data_dir)
+
+    files_to_gzip = []
+    for extension in filetypes_to_gzip:
+        files_to_gzip.extend(glob.glob(os.path.join(data_src_dir, '*.' + extension)))
+    
+    print('  files to gzip: ' + str(files_to_gzip))
+
+    all_files = glob.glob(os.path.join(data_src_dir, '*.*'))
+    files_to_copy = list(set(all_files) - set(files_to_gzip))
+
+    print('  files to copy: ' + str(files_to_copy))
+
+    for file in files_to_copy:
+        print('  Copying file: ' + file + ' to data dir')
+        shutil.copy(file, data_dir)
+    
+    for file in files_to_gzip:
+        print('  GZipping file: ' + file + ' to data dir')
+        with open(file, 'rb') as src, gzip.open(os.path.join(data_dir, os.path.basename(file) + '.gz'), 'wb') as dst:        
+            dst.writelines(src)
+
+    print('[/COPY/GZIP DATA FILES]')
+    
+env.AddPreAction('$BUILD_DIR/littlefs.bin', prepare_www_files)
diff --git a/platformio.ini b/platformio.ini
index e97b462..628b5ea 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -17,6 +17,8 @@ monitor_port = COM8
 monitor_speed = 115200
 monitor_filters = esp32_exception_decoder
 board_build.filesystem = littlefs
+extra_scripts =
+    post:gzip_data.py
 platform_packages = framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#2.0.9
 board_build.arduino.upstream_packages = no
 lib_deps = 
-- 
GitLab