diff --git a/ESP32_PrusaConnectCam/cfg.cpp b/ESP32_PrusaConnectCam/cfg.cpp index a774765..71dc883 100644 --- a/ESP32_PrusaConnectCam/cfg.cpp +++ b/ESP32_PrusaConnectCam/cfg.cpp @@ -37,6 +37,7 @@ void Configuration::Init() { Log->AddEvent(LogLevel_Warning, "First MCU start! Set factory cfg"); DefaultCfg(); SaveFirstMcuStartFlag(CFG_FIRST_MCU_START_NAK); + Log->SetLogLevel(LoadLogLevel()); } /* set reset pin */ @@ -1062,6 +1063,23 @@ uint8_t Configuration::LoadAgcGain() { return ret; } +/** + * @brief Load log level from EEPROM + * + * @return LogLevel_enum - log level + */ +LogLevel_enum Configuration::LoadLogLevel() { + LogLevel_enum ret = (LogLevel_enum) EEPROM.read(EEPROM_ADDR_LOG_LEVEL); + Log->AddEvent(LogLevel_Info, "LogLevel: " + String(ret)); + + return ret; +} + +/** + * @brief Load PrusaConnect hostname from EEPROM + * + * @return String - hostname + */ String Configuration::LoadPrusaConnectHostname() { Log->AddEvent(LogLevel_Info, "PrusaConnect hostname: ", false); String ret = LoadString(EEPROM_ADDR_HOSTNAME_START, EEPROM_ADDR_HOSTNAME_LENGTH, true); diff --git a/ESP32_PrusaConnectCam/cfg.h b/ESP32_PrusaConnectCam/cfg.h index 6d36b38..5ca4965 100644 --- a/ESP32_PrusaConnectCam/cfg.h +++ b/ESP32_PrusaConnectCam/cfg.h @@ -97,6 +97,7 @@ public: uint16_t LoadAecValue(); bool LoadGainCtrl(); uint8_t LoadAgcGain(); + LogLevel_enum LoadLogLevel(); String LoadPrusaConnectHostname(); private: diff --git a/ESP32_PrusaConnectCam/connect.cpp b/ESP32_PrusaConnectCam/connect.cpp index 8349c3d..8a20dd8 100644 --- a/ESP32_PrusaConnectCam/connect.cpp +++ b/ESP32_PrusaConnectCam/connect.cpp @@ -76,6 +76,7 @@ void PrusaConnect::TakePicture() { */ bool PrusaConnect::SendDataToBackend(String *i_data, String i_content_type, String i_type, String i_url_path, bool i_fragmentation) { WiFiClientSecure client; + Server_pause(); BackendReceivedStatus = ""; bool ret = false; log->AddEvent(LogLevel_Info, "Sending " + i_type + " to PrusaConnect"); @@ -83,6 +84,7 @@ bool PrusaConnect::SendDataToBackend(String *i_data, String i_content_type, Stri /* check fingerprint and token length */ if ((Fingerprint.length() > 0) && (Token.length() > 0)) { client.setCACert(root_CAs); + client.setTimeout(1000); log->AddEvent(LogLevel_Verbose, "Connecting to server..."); /* connecting to server */ @@ -167,6 +169,7 @@ bool PrusaConnect::SendDataToBackend(String *i_data, String i_content_type, Stri } log->AddEvent(LogLevel_Info, "Upload done. Response code: " + BackendReceivedStatus + " ,BA:" + CovertBackendAvailabilitStatusToString(BackendAvailability)); + Server_resume(); return ret; } @@ -459,4 +462,52 @@ String PrusaConnect::CovertBackendAvailabilitStatusToString(BackendAvailabilitSt return ret; } +/** + * @brief Increase sending interval counter + * + * @param none + * @return none + */ +void PrusaConnect::IncreaseSendingIntervalCounter() { + SendingIntervalCounter++; +} + +/** + * @brief Set sending interval counter + * + * @param uint8_t i_data - counter + * @return none + */ +void PrusaConnect::SetSendingIntervalCounter(uint8_t i_data) { + SendingIntervalCounter = i_data; +} + +void PrusaConnect::SetSendingIntervalExpired() { + SendingIntervalCounter = RefreshInterval; +} + +/** + * @brief Get sending interval counter + * + * @return uint8_t - counter + */ +uint8_t PrusaConnect::GetSendingIntervalCounter() { + return SendingIntervalCounter; +} + +/** + * @brief Check if sending interval is expired. and can I send the data to the backend. [seconds] + * + * @return true + * @return false + */ +bool PrusaConnect::CheckSendingIntervalExpired() { + bool ret = false; + if (SendingIntervalCounter >= RefreshInterval) { + ret = true; + } + + return ret; +} + /* EOF */ \ No newline at end of file diff --git a/ESP32_PrusaConnectCam/connect.h b/ESP32_PrusaConnectCam/connect.h index bc75c40..4de2c5c 100644 --- a/ESP32_PrusaConnectCam/connect.h +++ b/ESP32_PrusaConnectCam/connect.h @@ -25,6 +25,7 @@ #include "camera.h" #include "cfg.h" #include "Certificate.h" +#include "server.h" /** * @brief BackendAvailabilitStatus enum @@ -42,6 +43,7 @@ private: String BackendReceivedStatus; ///< status of backend response BackendAvailabilitStatus BackendAvailability; ///< status of backend availability bool SendDeviceInformationToBackend; ///< flag for sending device information to backend + uint8_t SendingIntervalCounter; ///< counter for sending interval, represents seconds String Token; ///< token for backend communication String Fingerprint; ///< fingerprint for backend communication @@ -81,6 +83,12 @@ public: String GetPrusaConnectHostname(); BackendAvailabilitStatus GetBackendAvailabilitStatus(); String CovertBackendAvailabilitStatusToString(BackendAvailabilitStatus); + + void IncreaseSendingIntervalCounter(); + void SetSendingIntervalCounter(uint8_t); + void SetSendingIntervalExpired(); + uint8_t GetSendingIntervalCounter(); + bool CheckSendingIntervalExpired(); }; extern PrusaConnect Connect; ///< PrusaConnect object diff --git a/ESP32_PrusaConnectCam/server.cpp b/ESP32_PrusaConnectCam/server.cpp index a6b3b95..cea17d5 100644 --- a/ESP32_PrusaConnectCam/server.cpp +++ b/ESP32_PrusaConnectCam/server.cpp @@ -387,7 +387,7 @@ void Server_InitWebServer_Actions() { SystemLog.AddEvent(LogLevel_Verbose, "WEB server: /action_send send photo to cloud"); if (Server_CheckBasicAuth(request) == false) return; - Connect.SendPhotoToBackend(); + Connect.SetSendingIntervalExpired(); request->send_P(200, "text/plain", "Send Photo"); }); @@ -882,10 +882,35 @@ void Server_InitWebServer_Update() { }); } +/** + @brief Init WEB server stream + @param none + @return none +*/ void Server_InitWebServer_Stream() { server.on("/stream.mjpg", HTTP_GET, Server_streamJpg); } +/** + @brief Pause WEB server + @param none + @return none +*/ +void Server_pause() { + server.end(); + SystemLog.AddEvent(LogLevel_Verbose, "WEB server: pause"); +} + +/** + @brief Resume WEB server + @param none + @return none +*/ +void Server_resume() { + server.begin(); + SystemLog.AddEvent(LogLevel_Verbose, "WEB server: resume"); +} + /** * @brief Handle cache request * diff --git a/ESP32_PrusaConnectCam/server.h b/ESP32_PrusaConnectCam/server.h index fb6ff9d..55d5cae 100644 --- a/ESP32_PrusaConnectCam/server.h +++ b/ESP32_PrusaConnectCam/server.h @@ -50,6 +50,9 @@ void Server_InitWebServer_Sets(); void Server_InitWebServer_Update(); void Server_InitWebServer_Stream(); +void Server_pause(); +void Server_resume(); + void Server_handleCacheRequest(AsyncWebServerRequest*, const char*, const char*); void Server_handleNotFound(AsyncWebServerRequest *); String Server_GetJsonData(); diff --git a/ESP32_PrusaConnectCam/system.cpp b/ESP32_PrusaConnectCam/system.cpp index ccaae5b..306d50f 100644 --- a/ESP32_PrusaConnectCam/system.cpp +++ b/ESP32_PrusaConnectCam/system.cpp @@ -95,7 +95,9 @@ void System_UpdateInit() { void System_Main() { /* check new FW version */ if (false == FirmwareUpdate.CheckNewVersionAfterBoot) { + Server_pause(); System_CheckNewVersion(); + Server_resume(); } /* task for download and flash FW from server */ @@ -494,11 +496,10 @@ void System_TaskMain(void *pvParameters) { void System_TaskCaptureAndSendPhoto(void *pvParameters) { SystemLog.AddEvent(LogLevel_Info, "Task photo processing. core: " + String(xPortGetCoreID())); TickType_t xLastWakeTime = xTaskGetTickCount(); - uint16_t SendingIntervalCounter = 0; ///< counter for sending interval while (1) { - if (Connect.GetRefreshInterval() <= SendingIntervalCounter) { - SendingIntervalCounter = 0; + if (Connect.CheckSendingIntervalExpired()) { + Connect.SetSendingIntervalCounter(0); /* send network information to backend */ if ((WL_CONNECTED == WiFi.status()) && (false == FirmwareUpdate.Processing)) { esp_task_wdt_reset(); @@ -513,7 +514,7 @@ void System_TaskCaptureAndSendPhoto(void *pvParameters) { } else { /* update counter */ - SendingIntervalCounter++; + Connect.IncreaseSendingIntervalCounter(); } SystemLog.AddEvent(LogLevel_Verbose, "Photo processing task. Stack free size: " + String(uxTaskGetStackHighWaterMark(NULL)) + " bytes"); diff --git a/ESP32_PrusaConnectCam/wifi_mngt.cpp b/ESP32_PrusaConnectCam/wifi_mngt.cpp index 98398bf..2d89702 100644 --- a/ESP32_PrusaConnectCam/wifi_mngt.cpp +++ b/ESP32_PrusaConnectCam/wifi_mngt.cpp @@ -88,6 +88,8 @@ void WiFiMngt::Init() { } else { log->AddEvent(LogLevel_Warning, "Wifi unavailable. Skip connecting to WiFi: " + WifiSsid); } + } else { + ScanWiFiNetwork(); } TaskAp_previousMillis = millis(); diff --git a/README.md b/README.md index d224313..e8ea6ac 100644 --- a/README.md +++ b/README.md @@ -271,3 +271,4 @@ Standard commands sequence for camera basic settings is ## Potential issue - A potential issue may arise with connecting to the service AP. If the connection fails and an authentication error occurs, it is necessary to clear the FLASH memory of the processor, and FLASH FW again. This can be done either through the Arduino IDE or using official software. +- While sending the photo to the backend, the WEB server is temporarily disabled for this short interval. After the photo is sent to the backend, the WEB server is re-enabled. This may cause short unavailability in the WEB server on the camera, lasting several seconds, depending on the internet connection and the quality of the WiFi connection diff --git a/webpage/jquery-3.7.0.min.js b/webpage/jquery-3.7.0.min.js new file mode 100644 index 0000000..e7e29d5 --- /dev/null +++ b/webpage/jquery-3.7.0.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.7.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(ie,e){"use strict";var oe=[],r=Object.getPrototypeOf,ae=oe.slice,g=oe.flat?function(e){return oe.flat.call(e)}:function(e){return oe.concat.apply([],e)},s=oe.push,se=oe.indexOf,n={},i=n.toString,ue=n.hasOwnProperty,o=ue.toString,a=o.call(Object),le={},v=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},y=function(e){return null!=e&&e===e.window},C=ie.document,u={type:!0,src:!0,nonce:!0,noModule:!0};function m(e,t,n){var r,i,o=(n=n||C).createElement("script");if(o.text=e,t)for(r in u)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function x(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[i.call(e)]||"object":typeof e}var t="3.7.0",l=/HTML$/i,ce=function(e,t){return new ce.fn.init(e,t)};function c(e){var t=!!e&&"length"in e&&e.length,n=x(e);return!v(e)&&!y(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+ge+")"+ge+"*"),x=new RegExp(ge+"|>"),j=new RegExp(g),A=new RegExp("^"+t+"$"),D={ID:new RegExp("^#("+t+")"),CLASS:new RegExp("^\\.("+t+")"),TAG:new RegExp("^("+t+"|[*])"),ATTR:new RegExp("^"+p),PSEUDO:new RegExp("^"+g),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+ge+"*(even|odd|(([+-]|)(\\d*)n|)"+ge+"*(?:([+-]|)"+ge+"*(\\d+)|))"+ge+"*\\)|)","i"),bool:new RegExp("^(?:"+f+")$","i"),needsContext:new RegExp("^"+ge+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+ge+"*((?:-\\d)?\\d*)"+ge+"*\\)|)(?=[^-]|$)","i")},N=/^(?:input|select|textarea|button)$/i,q=/^h\d$/i,L=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,H=/[+~]/,O=new RegExp("\\\\[\\da-fA-F]{1,6}"+ge+"?|\\\\([^\\r\\n\\f])","g"),P=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},R=function(){V()},M=J(function(e){return!0===e.disabled&&fe(e,"fieldset")},{dir:"parentNode",next:"legend"});try{k.apply(oe=ae.call(ye.childNodes),ye.childNodes),oe[ye.childNodes.length].nodeType}catch(e){k={apply:function(e,t){me.apply(e,ae.call(t))},call:function(e){me.apply(e,ae.call(arguments,1))}}}function I(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(V(e),e=e||T,C)){if(11!==p&&(u=L.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return k.call(n,a),n}else if(f&&(a=f.getElementById(i))&&I.contains(e,a)&&a.id===i)return k.call(n,a),n}else{if(u[2])return k.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&e.getElementsByClassName)return k.apply(n,e.getElementsByClassName(i)),n}if(!(h[t+" "]||d&&d.test(t))){if(c=t,f=e,1===p&&(x.test(t)||m.test(t))){(f=H.test(t)&&z(e.parentNode)||e)==e&&le.scope||((s=e.getAttribute("id"))?s=ce.escapeSelector(s):e.setAttribute("id",s=S)),o=(l=Y(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+Q(l[o]);c=l.join(",")}try{return k.apply(n,f.querySelectorAll(c)),n}catch(e){h(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return re(t.replace(ve,"$1"),e,n,r)}function W(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function F(e){return e[S]=!0,e}function $(e){var t=T.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function B(t){return function(e){return fe(e,"input")&&e.type===t}}function _(t){return function(e){return(fe(e,"input")||fe(e,"button"))&&e.type===t}}function X(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&M(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function U(a){return F(function(o){return o=+o,F(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function z(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}function V(e){var t,n=e?e.ownerDocument||e:ye;return n!=T&&9===n.nodeType&&n.documentElement&&(r=(T=n).documentElement,C=!ce.isXMLDoc(T),i=r.matches||r.webkitMatchesSelector||r.msMatchesSelector,ye!=T&&(t=T.defaultView)&&t.top!==t&&t.addEventListener("unload",R),le.getById=$(function(e){return r.appendChild(e).id=ce.expando,!T.getElementsByName||!T.getElementsByName(ce.expando).length}),le.disconnectedMatch=$(function(e){return i.call(e,"*")}),le.scope=$(function(){return T.querySelectorAll(":scope")}),le.cssHas=$(function(){try{return T.querySelector(":has(*,:jqfake)"),!1}catch(e){return!0}}),le.getById?(b.filter.ID=function(e){var t=e.replace(O,P);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&C){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(O,P);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&C){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):t.querySelectorAll(e)},b.find.CLASS=function(e,t){if("undefined"!=typeof t.getElementsByClassName&&C)return t.getElementsByClassName(e)},d=[],$(function(e){var t;r.appendChild(e).innerHTML="",e.querySelectorAll("[selected]").length||d.push("\\["+ge+"*(?:value|"+f+")"),e.querySelectorAll("[id~="+S+"-]").length||d.push("~="),e.querySelectorAll("a#"+S+"+*").length||d.push(".#.+[+~]"),e.querySelectorAll(":checked").length||d.push(":checked"),(t=T.createElement("input")).setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),r.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&d.push(":enabled",":disabled"),(t=T.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||d.push("\\["+ge+"*name"+ge+"*="+ge+"*(?:''|\"\")")}),le.cssHas||d.push(":has"),d=d.length&&new RegExp(d.join("|")),l=function(e,t){if(e===t)return a=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!le.sortDetached&&t.compareDocumentPosition(e)===n?e===T||e.ownerDocument==ye&&I.contains(ye,e)?-1:t===T||t.ownerDocument==ye&&I.contains(ye,t)?1:o?se.call(o,e)-se.call(o,t):0:4&n?-1:1)}),T}for(e in I.matches=function(e,t){return I(e,null,null,t)},I.matchesSelector=function(e,t){if(V(e),C&&!h[t+" "]&&(!d||!d.test(t)))try{var n=i.call(e,t);if(n||le.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){h(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(O,P),e[3]=(e[3]||e[4]||e[5]||"").replace(O,P),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||I.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&I.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return D.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&j.test(n)&&(t=Y(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(O,P).toLowerCase();return"*"===e?function(){return!0}:function(e){return fe(e,t)}},CLASS:function(e){var t=s[e+" "];return t||(t=new RegExp("(^|"+ge+")"+e+"("+ge+"|$)"))&&s(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=I.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function T(e,n,r){return v(n)?ce.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?ce.grep(e,function(e){return e===n!==r}):"string"!=typeof n?ce.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(ce.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||k,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:S.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof ce?t[0]:t,ce.merge(this,ce.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:C,!0)),w.test(r[1])&&ce.isPlainObject(t))for(r in t)v(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=C.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):v(e)?void 0!==n.ready?n.ready(e):e(ce):ce.makeArray(e,this)}).prototype=ce.fn,k=ce(C);var E=/^(?:parents|prev(?:Until|All))/,j={children:!0,contents:!0,next:!0,prev:!0};function A(e,t){while((e=e[t])&&1!==e.nodeType);return e}ce.fn.extend({has:function(e){var t=ce(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,Ce=/^$|^module$|\/(?:java|ecma)script/i;xe=C.createDocumentFragment().appendChild(C.createElement("div")),(be=C.createElement("input")).setAttribute("type","radio"),be.setAttribute("checked","checked"),be.setAttribute("name","t"),xe.appendChild(be),le.checkClone=xe.cloneNode(!0).cloneNode(!0).lastChild.checked,xe.innerHTML="",le.noCloneChecked=!!xe.cloneNode(!0).lastChild.defaultValue,xe.innerHTML="",le.option=!!xe.lastChild;var ke={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function Se(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&fe(e,t)?ce.merge([e],n):n}function Ee(e,t){for(var n=0,r=e.length;n",""]);var je=/<|&#?\w+;/;function Ae(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function Me(e,t){return fe(e,"table")&&fe(11!==t.nodeType?t:t.firstChild,"tr")&&ce(e).children("tbody")[0]||e}function Ie(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function We(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Fe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(_.hasData(e)&&(s=_.get(e).events))for(i in _.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),C.head.appendChild(r[0])},abort:function(){i&&i()}}});var Jt,Kt=[],Zt=/(=)\?(?=&|$)|\?\?/;ce.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Kt.pop()||ce.expando+"_"+jt.guid++;return this[e]=!0,e}}),ce.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Zt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Zt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=v(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Zt,"$1"+r):!1!==e.jsonp&&(e.url+=(At.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||ce.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=ie[r],ie[r]=function(){o=arguments},n.always(function(){void 0===i?ce(ie).removeProp(r):ie[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Kt.push(r)),o&&v(i)&&i(o[0]),o=i=void 0}),"script"}),le.createHTMLDocument=((Jt=C.implementation.createHTMLDocument("").body).innerHTML="
",2===Jt.childNodes.length),ce.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(le.createHTMLDocument?((r=(t=C.implementation.createHTMLDocument("")).createElement("base")).href=C.location.href,t.head.appendChild(r)):t=C),o=!n&&[],(i=w.exec(e))?[t.createElement(i[1])]:(i=Ae([e],t,o),o&&o.length&&ce(o).remove(),ce.merge([],i.childNodes)));var r,i,o},ce.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(ce.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},ce.expr.pseudos.animated=function(t){return ce.grep(ce.timers,function(e){return t===e.elem}).length},ce.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=ce.css(e,"position"),c=ce(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=ce.css(e,"top"),u=ce.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),v(t)&&(t=t.call(e,n,ce.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},ce.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){ce.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===ce.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===ce.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=ce(e).offset()).top+=ce.css(e,"borderTopWidth",!0),i.left+=ce.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-ce.css(r,"marginTop",!0),left:t.left-i.left-ce.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===ce.css(e,"position"))e=e.offsetParent;return e||J})}}),ce.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;ce.fn[t]=function(e){return R(this,function(e,t,n){var r;if(y(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),ce.each(["top","left"],function(e,n){ce.cssHooks[n]=Ye(le.pixelPosition,function(e,t){if(t)return t=Ge(e,n),_e.test(t)?ce(e).position()[n]+"px":t})}),ce.each({Height:"height",Width:"width"},function(a,s){ce.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){ce.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return R(this,function(e,t,n){var r;return y(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?ce.css(e,t,i):ce.style(e,t,n,i)},s,n?e:void 0,n)}})}),ce.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){ce.fn[t]=function(e){return this.on(t,e)}}),ce.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),ce.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){ce.fn[n]=function(e,t){return 0