diff --git a/src/AutoConnectDefs.h b/src/AutoConnectDefs.h
index b02fc0e..a29ac80 100644
--- a/src/AutoConnectDefs.h
+++ b/src/AutoConnectDefs.h
@@ -3,7 +3,7 @@
  * @file AutoConnectDefs.h
  * @author hieromon@gmail.com
  * @version  0.9.9
- * @date 2019-04-30
+ * @date 2019-05-14
  * @copyright  MIT license.
  */
 
@@ -200,4 +200,18 @@
 // Explicitly avoiding unused warning with token handler of PageBuilder
 #define AC_UNUSED(expr) do { (void)(expr); } while (0)
 
+// Generates a template that determines whether the class owns the
+// specified member function.
+// The purpose of this macro is to avoid the use of invalid member
+// functions due to differences in the version of the library which
+// AutoConnect depends on.
+#define AC_HAS_FUNC(func) \
+template<typename T> \
+struct has_func_##func { \
+  static auto check(...) -> decltype(std::false_type()); \
+  template<typename U> \
+  static auto check(U&) -> decltype(static_cast<decltype(U::func)*>(&U::func), std::true_type()); \
+  enum : bool { value = decltype(check(std::declval<T&>()))::value }; \
+}
+
 #endif // _AUTOCONNECTDEFS_H_
diff --git a/src/AutoConnectUpdate.cpp b/src/AutoConnectUpdate.cpp
index 4f4d8f2..3d1bdea 100644
--- a/src/AutoConnectUpdate.cpp
+++ b/src/AutoConnectUpdate.cpp
@@ -3,11 +3,12 @@
  * @file   AutoConnectUpdate.cpp
  * @author hieromon@gmail.com
  * @version    0.9.9
- * @date   2019-05-03
+ * @date   2019-05-14
  * @copyright  MIT license.
  */
 
 #include <functional>
+#include <type_traits>
 #include "AutoConnectUpdate.h"
 #include "AutoConnectUpdatePage.h"
 #include "AutoConnectJsonDefs.h"
@@ -65,6 +66,40 @@
  *   HTTP and also needs to attach an MD5 hash value to the x-MD5 header.
  */
 
+/**
+ * The following two templates absorb callbacks that are enabled/disabled
+ * by the Update library version in the core.
+ * The old UpdateClass of the ESP8266/ESP32 arduino core does not have
+ * the onProgress function for registering callbacks. These templates
+ * avoid the onProgress calls on older library versions.
+ * In versions where the update function callback is disabled, the
+ * dialog on the client browser does not show the progress of the update.
+ */
+#if defined(ARDUINO_ARCH_ESP8266)
+using UpdateVariedClass = UpdaterClass;
+#elif defined(ARDUINO_ARCH_ESP32)
+using UpdateVariedClass = UpdateClass;
+#endif
+
+namespace AutoConnectUtil {
+AC_HAS_FUNC(onProgress);
+
+template<typename T>
+typename std::enable_if<AutoConnectUtil::has_func_onProgress<T>::value, AutoConnectUpdate::AC_UPDATEDIALOG_t>::type onProgress(const T& updater, std::function<void(size_t, size_t)> fn) {
+  updater.onProgress(fn);
+  AC_DBG("Callback enabled\n");
+  return AutoConnectUpdate::UPDATEDIALOG_METER;
+}
+
+template<typename T>
+typename std::enable_if<!AutoConnectUtil::has_func_onProgress<T>::value, AutoConnectUpdate::AC_UPDATEDIALOG_t>::type onProgress(const T& updater, std::function<void(size_t, size_t)> fn) {
+  (void)(updater);
+  (void)(fn);
+  AC_DBG("Callback disabled\n");
+  return AutoConnectUpdate::UPDATEDIALOG_LOADER;
+}
+}
+
 /**
  * A destructor. Release the update processing dialogue page generated
  * as AutoConnectAux.
@@ -108,9 +143,29 @@ void AutoConnectUpdate::attach(AutoConnect& portal) {
 
   _status = UPDATE_IDLE;
 
-#ifdef ARDUINO_ARCH_ESP32
-  Update.onProgress(std::bind(&AutoConnectUpdate::_inProgress, this, std::placeholders::_1, std::placeholders::_2));
-#endif
+  // Register the callback to inform the update progress 
+  _dialog = AutoConnectUtil::onProgress<UpdateVariedClass>(Update, std::bind(&AutoConnectUpdate::_inProgress, this, std::placeholders::_1, std::placeholders::_2));
+  // Update.onProgress(std::bind(&AutoConnectUpdate::_inProgress, this, std::placeholders::_1, std::placeholders::_2));
+  // _dialog = UPDATEDIALOG_METER;
+
+  // Adjust the client dialog pattern according to the callback validity
+  // of the UpdateClass.
+  AutoConnectElement* loader = _progress->getElement(String(F("loader")));
+  AutoConnectElement* progress_meter = _progress->getElement(String(F("progress_meter")));
+  AutoConnectElement* inprogress_meter = _progress->getElement(String(F("inprogress_meter")));
+  AutoConnectElement* progress_loader = _progress->getElement(String(F("progress_loader")));
+  AutoConnectElement* inprogress_loader = _progress->getElement(String(F("inprogress_loader")));
+  switch (_dialog) {
+  case UPDATEDIALOG_LOADER:
+    progress_meter->enable =false;
+    inprogress_meter->enable = false;
+    break;
+  case UPDATEDIALOG_METER:
+    loader->enable = false;
+    progress_loader->enable =false;
+    inprogress_loader->enable = false;
+    break;
+  }
 
   // Attach this to the AutoConnectUpdate
   portal._update.reset(this);
@@ -161,8 +216,22 @@ void AutoConnectUpdate::handleUpdate(void) {
       // Evaluate the processing status of AutoConnectUpdate and
       // execute it accordingly. It is only this process point that
       // requests update processing.
-      if (_status == UPDATE_START)
-        update();
+      if (_status == UPDATE_START) {
+        _ws->loop();  // Crawl the connection request.
+        unsigned long tm = millis();
+        while (_ws->connectedClients() <= 0) {
+          if (millis() - tm > AUTOCONNECT_UPDATE_TIMEOUT) {
+            AC_DBG("WebSocket client connection timeout, update ignored\n");
+            break;
+          }
+          _ws->loop();  // Crawl the connection request.
+        }
+        // Launch the update
+        if (_ws->connectedClients())
+          update();
+        else
+          _status = UPDATE_IDLE;
+      }
       else if (_status == UPDATE_RESET) {
         AC_DBG("Restart on %s updated...\n", _binName.c_str());
         ESP.restart();
@@ -181,10 +250,6 @@ void AutoConnectUpdate::handleUpdate(void) {
  * @return  AC_UPDATESTATUS_t
  */
 AC_UPDATESTATUS_t AutoConnectUpdate::update(void) {
-  // Crawl queued requests.
-  if (_ws)
-    _ws->loop();
-
   // Start update
   String  uriBin = uri + '/' + _binName;
   if (_binName.length()) {
@@ -198,6 +263,7 @@ AC_UPDATESTATUS_t AutoConnectUpdate::update(void) {
     case HTTP_UPDATE_FAILED:
       _status = UPDATE_FAIL;
       AC_DBG_DUMB(" %s\n", getLastErrorString().c_str());
+      AC_DBG("update returns HTTP_UPDATE_FAILED\n");
       break;
     case HTTP_UPDATE_NO_UPDATES:
       _status = UPDATE_IDLE;
@@ -210,11 +276,7 @@ AC_UPDATESTATUS_t AutoConnectUpdate::update(void) {
     }
     _WiFiClient.reset(nullptr);
     // Request the client to close the WebSocket.
-    if (_ws) {
-      String  cmdClose = String("#e");
-      _ws->sendTXT(_wsClient, cmdClose);
-      _ws->loop();
-    }
+    _ws->sendTXT(_wsClient, "#e", 2);
   }
   else {
     AC_DBG("An update has not specified");
@@ -375,26 +437,23 @@ String AutoConnectUpdate::_onCatalog(AutoConnectAux& catalog, PageArgument& args
 String AutoConnectUpdate::_onUpdate(AutoConnectAux& progress, PageArgument& args) {
   AC_UNUSED(args);
   // launch the WebSocket server
-  WebSocketsServer* ws = new WebSocketsServer(AUTOCONNECT_WEBSOCKETPORT);
-  if (ws) {
-    ws->begin();
-    ws->onEvent(std::bind(&AutoConnectUpdate::_wsEvent, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
+  _ws.reset(new WebSocketsServer(AUTOCONNECT_WEBSOCKETPORT));
+  if (_ws) {
+    _ws->begin();
+    _ws->onEvent(std::bind(&AutoConnectUpdate::_wsEvent, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
   }
-  else {
+  else
     AC_DBG("WebSocketsServer allocation failed\n");
-  }
-  _ws.reset(ws);
 
   // Constructs the dialog page.
-  AutoConnectText&  binName = progress.getElement<AutoConnectText>(String(F("binname")));
+  AutoConnectElement* binName = progress.getElement(String(F("binname")));
   _binName = _catalog->getElement<AutoConnectRadio>(String(F("firmwares"))).value();
-  binName.value = _binName;
-  AutoConnectText& url = progress.getElement<AutoConnectText>(String("url"));
-  url.value = host + ':' + port;
-  AutoConnectElement& inprogress = progress.getElement<AutoConnectElement>(String(F("inprogress")));
-  String js = inprogress.value;
-  js.replace(String(F("#wsserver#")), WiFi.localIP().toString() + ':' + AUTOCONNECT_WEBSOCKETPORT);
-  inprogress.value = js;
+  binName->value = _binName;
+  AutoConnectElement* url = progress.getElement(String("url"));
+  url->value = host + ':' + port;
+  AutoConnectElement* wsurl = progress.getElement(String(F("wsurl")));
+  wsurl->value = "ws://" + WiFi.localIP().toString() + ':' + AUTOCONNECT_WEBSOCKETPORT;
+  AC_DBG("Cast WS %s\n", wsurl->value.c_str());
   _status = UPDATE_START;
   return String("");
 }
@@ -412,6 +471,11 @@ String AutoConnectUpdate::_onResult(AutoConnectAux& result, PageArgument& args)
   String  resColor;
   bool    restart = false;
 
+  if (_ws) {
+    _ws->close();
+    _ws.reset(nullptr);
+  }
+
   switch (_status) {
   case UPDATE_SUCCESS:
     resForm = String(F(" sucessfully updated. restart..."));
@@ -443,16 +507,11 @@ void AutoConnectUpdate::_inProgress(size_t amount, size_t size) {
     _binSize = size;
     String  payload = "#p," + String(_amount) + ':' + String(_binSize); 
     _ws->sendTXT(_wsClient, payload);
-    _ws->loop();
   }
 }
 
 void AutoConnectUpdate::_wsEvent(uint8_t client, WStype_t event, uint8_t* payload, size_t length) {
-  AC_DBG("WS event(%d)\n", event);
+  AC_DBG("WS client:%d event(%d)\n", client, event);
   if (event == WStype_CONNECTED)
     _wsClient = client;
-  else if (event == WStype_DISCONNECTED) {
-    if (client == _wsClient)
-      _ws.reset(nullptr);
-  }
 }
diff --git a/src/AutoConnectUpdate.h b/src/AutoConnectUpdate.h
index 30894d5..879d908 100644
--- a/src/AutoConnectUpdate.h
+++ b/src/AutoConnectUpdate.h
@@ -22,7 +22,7 @@
  * @file AutoConnectUpdate.h
  * @author hieromon@gmail.com
  * @version  0.9.9
- * @date 2019-05-03
+ * @date 2019-05-14
  * @copyright  MIT license.
  */
 
@@ -92,6 +92,12 @@ class AutoConnectUpdate : public HTTPUpdateClass {
   uint16_t  port;           /**< Port number of the update server */
   String    uri;            /**< The path on the update server that contains the sketch binary to be updated */
 
+  //
+  typedef enum {
+    UPDATEDIALOG_LOADER,
+    UPDATEDIALOG_METER
+  } AC_UPDATEDIALOG_t;
+
  protected:
   // Attribute definition of the element to be placed on the update page.
   typedef struct {
@@ -129,7 +135,8 @@ class AutoConnectUpdate : public HTTPUpdateClass {
   size_t  _binSize;                           /**< Updater binary size */
 
  private:
-  AC_UPDATESTATUS_t _status;
+  AC_UPDATEDIALOG_t _dialog;                  /**< The type of updating dialog displayed on the client */
+  AC_UPDATESTATUS_t _status;                  /**< Status of update processing during the cycle of receiving a request */
   String            _binName;                 /**< .bin name to update */
   unsigned long     _period;                  /**< Duration of WiFiClient holding for the connection with the update server */
   std::unique_ptr<WiFiClient> _WiFiClient;    /**< Provide to HTTPUpdate class */
diff --git a/src/AutoConnectUpdatePage.h b/src/AutoConnectUpdatePage.h
index b0a4527..663f578 100644
--- a/src/AutoConnectUpdatePage.h
+++ b/src/AutoConnectUpdatePage.h
@@ -4,7 +4,7 @@
  * @file   AutoConnectUpdatePage.h
  * @author hieromon@gmail.com
  * @version    0.9.9
- * @date   2019-05-04
+ * @date   2019-05-14
  * @copyright  MIT license.
  */
 
@@ -25,12 +25,22 @@ const AutoConnectUpdate::ACPage_t AutoConnectUpdate::_auxCatalog PROGMEM = {
 // Define the AUTOCONNECT_URI_UPDATE_ACT page to display during the
 // update process.
 const AutoConnectUpdate::ACElementProp_t AutoConnectUpdate::_elmProgress[] PROGMEM = {
-  { AC_Element, "caption", "<div style=\"display:inline-block\"", nullptr },
-  { AC_Text, "binname", nullptr, "%s&ensp;from&ensp;" },
-  { AC_Text, "url", nullptr, "%s</div>" },
-  { AC_Element, "progress", "<div id=\"progress\">Updating...&ensp;<meter min=\"0\"></meter></div>", nullptr },
+  { AC_Element, "loader", "<style>.loader{border:2px solid #f3f3f3;border-radius:50%;border-top:2px solid #555;width: 12px;height:12px;-webkit-animation:spin 1s linear infinite;animation:spin 1s linear infinite}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg)}}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}</style>", nullptr },
+  { AC_Element, "c1", "<div style=\"display:inline-block\">", nullptr },
+  { AC_Element, "binname", nullptr, nullptr },
+  { AC_Element, "c2", "&ensp;from&ensp;", nullptr },
+  { AC_Element, "url", "</dv>", nullptr },
+  { AC_Element, "c3", "<div id=\"progress\">Updating...<span style=\"display:inline-block;vertical-align:middle;margin-left:7px\">", nullptr },
+  { AC_Element, "progress_meter", "<meter min=\"0\" />", nullptr },
+  { AC_Element, "progress_loader", "<div class=\"loader\" />", nullptr },
+  { AC_Element, "c4", "</span></div></div>", nullptr },
   { AC_Text, "status", nullptr, nullptr },
-  { AC_Element, "inprogress", "<script type='text/javascript'>var ws;window.onload=function(){ws=new WebSocket('ws://'+'#wsserver#');ws.onopen=function(e){ws.onmessage=function(e){var pl=e.data.split(',');if(pl[0]=='#p'){var iv=pl[1].split(':');var pb=document.getElementById('progress').getElementsByTagName('meter');pb[0].setAttribute('value',iv[0]);pb[0].setAttribute('max',iv[1]);}else if(pl[0]=='#e'){location.href='" AUTOCONNECT_URI_UPDATE_RESULT "';}};ws.onclose=function(e){console.log('WS close('+e.code+') '+e.reason);};};ws.onerror=function(e){console.log(e);document.getElementById('status').textContent='Connection failed.';};};window.onbeforeunload=function(){ws.close();};</script>", nullptr }
+  { AC_Element, "c5", "<script type=\"text/javascript\">var ws;window.onload=function(){ws=new WebSocket('", nullptr },
+  { AC_Element, "wsurl", nullptr, nullptr },
+  { AC_Element, "c6", "');ws.onopen=function(){ws.onmessage=function(e){var pl=e.data.split(',');if(pl[0]=='#e'){location.href='/_ac/update_result';}else if(pl[0]=='#p'){incr(pl[1]);}};};ws.onclose=function(e){console.log('WS close('+e.code+')'+e.reason);if(e.code!=1000){document.getElementById('status').textContent='WebSocket connection closed. ('+e.code+')';}};ws.onerror=function(e){if(ws.readyState==1){document.getElementById('status').textContent='WebSocket '+e.type;}};};window.onbeforeunload=function(){ws.close();};", nullptr },
+  { AC_Element, "inprogress_meter", "function incr(pv){var iv=pv.split(':');var pb=document.getElementById('progress').getElementsByTagName('meter');pb[0].setAttribute('value',iv[0]);pb[0].setAttribute('max',iv[1]);}", nullptr },
+  { AC_Element, "inprogress_loader", "function incr(pv){}", nullptr },
+  { AC_Element, "c7", "</script>", nullptr },
 };
 const AutoConnectUpdate::ACPage_t AutoConnectUpdate::_auxProgress PROGMEM = {
   AUTOCONNECT_URI_UPDATE_ACT, "Update", false, AutoConnectUpdate::_elmProgress
diff --git a/src/AutoConnectUploadImpl.h b/src/AutoConnectUploadImpl.h
index baf935e..2073e61 100644
--- a/src/AutoConnectUploadImpl.h
+++ b/src/AutoConnectUploadImpl.h
@@ -2,8 +2,8 @@
  * The default upload handler implementation.
  * @file AutoConnectUploadImpl.h
  * @author hieromon@gmail.com
- * @version  0.9.8
- * @date 2019-03-19
+ * @version  0.9.9
+ * @date 2019-05-14
  * @copyright  MIT license.
  */
 
@@ -39,6 +39,20 @@ typedef SDFile        SDFileT;
 #include "AutoConnectDefs.h"
 #include "AutoConnectUpload.h"
 
+namespace AutoConnectUtil {
+AC_HAS_FUNC(end);
+
+template<typename T>
+typename std::enable_if<AutoConnectUtil::has_func_end<T>::value, void>::type end(const T* media) {
+  media->end();
+}
+
+template<typename T>
+typename std::enable_if<!AutoConnectUtil::has_func_end<T>::value, void>::type end(const T* media) {
+  (void)(media);
+}
+}
+
 /**
  * Handles the default upload process depending on the upload status.
  * This handler function supports the status of UPLOAD_FILE_START,
@@ -133,9 +147,7 @@ class AutoConnectUploadSD : public AutoConnectUploadHandler {
   void  _close(void) override {
     if (_file)
       _file.close();
-#if defined(ARDUINO_ARCH_ESP32) || (defined(ARDUINO_ARCH_ESP8266) && (!defined(ARDUINO_ESP8266_RELEASE_2_4_0) && !defined(ARDUINO_ESP8266_RELEASE_2_4_1) && !defined(ARDUINO_ESP8266_RELEASE_2_4_2)))
-    _media->end();
-#endif
+    AutoConnectUtil::end<SDClassT>(_media);
   }
 
  private: