commit 55972b4ab76248f6f04272bb20ce50ae1ee4cb79 Author: Clemens Schwaighofer Date: Fri Mar 7 14:46:38 2025 +0900 Move all "edit.js" functions into Modules Create new repository to track these diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c2658d7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/ReadMe.md b/ReadMe.md new file mode 100644 index 0000000..d198b66 --- /dev/null +++ b/ReadMe.md @@ -0,0 +1,39 @@ +# JavaScript Utils + +Collection of Javascript functions. This will build into a single js file that was formaly known as "edit.js" (edit.jq.js). + +## Build + +Build with the following commands, the output will be stored in "build/js/utilsAll.js" and "build/js/utilsAll.min.js" + +For full not minified version + +```sh +npm build utilsAll-build +``` + +For minified build (but keeps the function names as is) + +```sh +npm build utilsAll-min-build +``` + +Or build both at the same time + +```sh +npm build utilsAll-build-all +``` + +## Develop + +Install the core npm modules + +```sh +npm install +``` + +Run watch + +```sh +npm run utilsAll-develop +``` diff --git a/build/js/general/jquery-3.6.0.min.js b/build/js/general/jquery-3.6.0.min.js new file mode 100644 index 0000000..c4c6022 --- /dev/null +++ b/build/js/general/jquery-3.6.0.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.6.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(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=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))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(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&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(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&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(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 ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);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&&E){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=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(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(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===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]||se.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]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(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(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(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=se.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 j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.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 S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(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 je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.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)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="
",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.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),m(t)&&(t=t.call(e,n,S.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)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.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"===S.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"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(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)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(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?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.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)}}),S.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){S.fn[n]=function(e,t){return 0 + + JavaScript Test + + + + + +
+

JavaScript tests

+
+
+
+ + diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..ff91f7e --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,59 @@ +import globals from 'globals'; +import pluginJs from '@eslint/js'; + +/* +module.exports = { + // in globals block + 'extends': 'eslint:recommended', + 'parserOptions': { + 'ecmaVersion': 6 + }, + // rules copied +}; +*/ + +/** @type {import('eslint').Linter.Config[]} */ +export default [ + {languageOptions: { + globals: { + ...globals.browser, + ...globals.jquery + } + }}, + pluginJs.configs.recommended, + { + 'rules': { + 'indent': [ + 'error', + 'tab', + { + 'SwitchCase': 1 + } + ], + 'linebreak-style': [ + 'error', + 'unix' + ], + // 'quotes': [ + // 'error', + // 'single' + // ], + 'semi': [ + 'error', + 'always' + ], + 'no-console': 'off', + 'no-unused-vars': [ + 'error', { + 'vars': 'all', + 'args': 'after-used', + 'ignoreRestSiblings': false + } + ], + // Requires eslint >= v8.14.0 + 'no-constant-binary-expression': 'error' + } + } +]; + +// __END__ diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..9d08c67 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,17 @@ +// https://www.typescriptlang.org/tsconfig/#compilerOptions +{ + "compilerOptions": { + "module": "ESNext", + "moduleResolution": "Node", + "target": "ES2020", + "jsx": "react", + "checkJs": true, + "allowImportingTsExtensions": true, + "strictNullChecks": true, + "strictFunctionTypes": true + }, + "exclude": [ + "node_modules", + "**/node_modules/*" + ] +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..6ce6a54 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1554 @@ +{ + "name": "javascript-utils", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "javascript-utils", + "version": "1.0.0", + "devDependencies": { + "@eslint/js": "^9.20.0", + "esbuild": "^0.25.0", + "eslint": "^9.20.1", + "globals": "^15.15.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", + "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz", + "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz", + "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz", + "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz", + "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz", + "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz", + "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz", + "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz", + "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz", + "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz", + "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz", + "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz", + "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz", + "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz", + "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz", + "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz", + "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz", + "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz", + "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz", + "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz", + "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz", + "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz", + "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz", + "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz", + "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz", + "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", + "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.0.tgz", + "integrity": "sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.21.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.21.0.tgz", + "integrity": "sha512-BqStZ3HX8Yz6LvsF5ByXYrtigrV5AXADWLAGc7PH/1SxOb7/FIYYMszZZWiUou/GB9P2lXWk2SV4d+Z8h0nknw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.7.tgz", + "integrity": "sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.12.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", + "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", + "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.0", + "@esbuild/android-arm": "0.25.0", + "@esbuild/android-arm64": "0.25.0", + "@esbuild/android-x64": "0.25.0", + "@esbuild/darwin-arm64": "0.25.0", + "@esbuild/darwin-x64": "0.25.0", + "@esbuild/freebsd-arm64": "0.25.0", + "@esbuild/freebsd-x64": "0.25.0", + "@esbuild/linux-arm": "0.25.0", + "@esbuild/linux-arm64": "0.25.0", + "@esbuild/linux-ia32": "0.25.0", + "@esbuild/linux-loong64": "0.25.0", + "@esbuild/linux-mips64el": "0.25.0", + "@esbuild/linux-ppc64": "0.25.0", + "@esbuild/linux-riscv64": "0.25.0", + "@esbuild/linux-s390x": "0.25.0", + "@esbuild/linux-x64": "0.25.0", + "@esbuild/netbsd-arm64": "0.25.0", + "@esbuild/netbsd-x64": "0.25.0", + "@esbuild/openbsd-arm64": "0.25.0", + "@esbuild/openbsd-x64": "0.25.0", + "@esbuild/sunos-x64": "0.25.0", + "@esbuild/win32-arm64": "0.25.0", + "@esbuild/win32-ia32": "0.25.0", + "@esbuild/win32-x64": "0.25.0" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.21.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.21.0.tgz", + "integrity": "sha512-KjeihdFqTPhOMXTt7StsDxriV4n66ueuF/jfPNC3j/lduHwr/ijDwJMsF+wyMJethgiKi5wniIE243vi07d3pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.19.2", + "@eslint/core": "^0.12.0", + "@eslint/eslintrc": "^3.3.0", + "@eslint/js": "9.21.0", + "@eslint/plugin-kit": "^0.2.7", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.2.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", + "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", + "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..ca7e6cd --- /dev/null +++ b/package.json @@ -0,0 +1,21 @@ +{ + "name": "javascript-utils", + "version": "1.0.0", + "main": "", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "utilsAll-min-build": "node_modules/esbuild/bin/esbuild utilsAll.min=src/utilsAll.mjs --outdir=build/js/output/ --format=esm --bundle --charset=utf8 --tree-shaking=false --minify-whitespace --minify-syntax --sourcemap", + "utilsAll-build": "node_modules/esbuild/bin/esbuild utilsAll=src/utilsAll.mjs --outdir=build/js/output/ --format=esm --bundle --charset=utf8 --tree-shaking=false", + "utilsAll-build-all": "npm run utilsAll-min-build && npm run utilsAll-build", + "utilsAll-develop": "node_modules/esbuild/bin/esbuild utilsAll=src/utilsAll.mjs --outdir=build/js/output/ --format=esm --bundle --charset=utf8 --tree-shaking=false --watch" + }, + "author": "Clemens Schwaighofer", + "license": "", + "description": "Javascript Utils Collection", + "devDependencies": { + "@eslint/js": "^9.20.0", + "esbuild": "^0.25.0", + "eslint": "^9.20.1", + "globals": "^15.15.0" + } +} diff --git a/src/utils/ActionBox.mjs b/src/utils/ActionBox.mjs new file mode 100644 index 0000000..52d4670 --- /dev/null +++ b/src/utils/ActionBox.mjs @@ -0,0 +1,438 @@ +/* +Description: Action Box handling +Date: 2025/3/7 +Creator: Clemens Schwaighofer +*/ + +export { ActionBox }; +import { keyInObject, getObjectCount } from './JavaScriptHelpers.mjs'; +import { exists } from './DomHelpers.mjs'; +import { setCenter, getWindowSize } from './ResizingAndMove.mjs'; + +class ActionBox { + + // open overlay boxes counter for z-index + zIndex = { + base: 100, + max: 110, + indicator: 0, + boxes: {}, + active: [], + top: '' + }; + // general action box storage + action_box_storage = {}; + // set to 10 min (*60 for seconds, *1000 for microseconds) + action_box_cache_timeout = 10 * 60 * 1000; + + hec; + l10n; + + /** + * action box creator + * @param {Object} hec HtmlElementCreator + * @param {Object} l10n l10nTranslation + */ + constructor(hec, l10n) + { + this.hec = hec; + this.l10n = l10n; + } + + /** + * Show an action box + * @param {string} [target_id='actionBox'] where to attach content to, if not exists, create new + * @param {string} [content=''] content to add to the box + * @param {array} [action_box_css=[]] additional css elements for the action box + * @param {number} [override=0] override size adjust + * @param {number} [content_override=0] override content size adjust + */ + showFillActionBox(target_id = 'actionBox', content = '', action_box_css = [], override = 0, content_override = 0) + { + // fill content + this.fillActionBox(target_id, content, action_box_css); + // show the box + this.showActionBox(target_id, override, content_override); + } + + /** + * Fill action box with content, create it if it does not existgs + * @param {string} [target_id='actionBox'] where to attach content to, if not exists, create new + * @param {string} [content=''] content to add to the box + * @param {array} [action_box_css=[]] additional css elements for the action box + */ + fillActionBox(target_id = 'actionBox', content = '', action_box_css = []) + { + // show action box, calc height + center + if (!exists(target_id)) { + // add at the bottom + $('#mainContainer').after( + this.hec.phfo(this.hec.cel('div', target_id, '', ['actionBoxElement', 'hide'].concat(action_box_css))) + ); + } + // add the info box + $('#' + target_id).html(content); + } + + /** + * Adjust the size of the action box + * @param {string} [target_id='actionBox'] which actionBox to work on + * @param {number} [override=0] override size adjust + * @param {number} [content_override=0] override content size adjust + */ + adjustActionBox(target_id = 'actionBox', override = 0, content_override = 0) + { + // adjust box size + this.adjustActionBoxHeight(target_id, override, content_override); + // center the alert box + setCenter(target_id, true, true); + } + + /** + * hide any open action boxes and hide overlay + */ + hideAllActionBoxes() + { + // hide all action boxes that might exist + $('#actionBox, div[id^="actionBox-"].actionBoxElement').hide(); + // hideOverlayBoxLayers(); + $('#overlayBox').hide(); + } + + /** + * hide action box, but do not clear content + * DEPRECATED + * @param {string} [target_id='actionBox'] + */ + hideActionBox(target_id = 'actionBox') + { + this.closeActionBoxFloat(target_id, false); + } + + /** + * Just show and adjust the box + * DEPRECAED + * @param {string} [target_id='actionBox'] which actionBox to work on + * @param {number} [override=0] override size adjust + * @param {number} [content_override=0] override content size adjust + * @param {Boolean} [hide_all=false] if set to true, hide all other action boxes + */ + showActionBox(target_id = 'actionBox', override = 0, content_override = 0, hide_all = true) + { + this.showActionBoxFloat(target_id, override, content_override, hide_all); + } + + /** + * close an action box with default clear content + * for just hide use hideActionBox + * DEPRECATED + * @param {String} [target_id='actionBox'] which action box to close, default is set + * @param {Boolean} [clean=true] if set to false will not remove html content, just hide + */ + closeActionBox(target_id = 'actionBox', clean = true) + { + // set the target/content ids + this.closeActionBoxFloat(target_id, clean); + } + + /** + * TODO: better stacked action box: OPEN + * @param {string} [target_id='actionBox'] which actionBox to work on + * @param {number} [override=0] override size adjust + * @param {number} [content_override=0] override content size adjust + * @param {boolean} [hide_all=false] if set to true, hide all other action boxes + */ + showActionBoxFloat(target_id = 'actionBox', override = 0, content_override = 0, hide_all = false) + { + if (hide_all === true) { + // hide all action boxes if they are currently open + this.hideAllActionBoxes(); + } + // if no box, created if + if (!exists('overlayBox')) { + $('body').prepend(this.hec.phfo(this.hec.cel('div', 'overlayBox', '', ['overlayBoxElement']))); + $('#overlayBox').css('zIndex', this.zIndex.base); + } + // adjust zIndex so its above all other and set action box zindex +1 + $('#overlayBox').show(); + if (!keyInObject(target_id, this.zIndex.boxes)) { + this.zIndex.boxes[target_id] = this.zIndex.max; + // increase by ten + this.zIndex.max += 10; + } else if (this.zIndex.boxes[target_id] + 10 < this.zIndex.max) { + // see if this is the highest level, if not move up and write no max zIndex + // move it up to be the new top and move the others down + // [loop, order by value] + // current hack, increase max + this.zIndex.boxes[target_id] = this.zIndex.max; + this.zIndex.max += 10; + } + // make sure the overlayBox is one level below this + // unless there is an active "indicator" index + if (!this.zIndex.indicator) { + $('#overlayBox').css('zIndex', this.zIndex.boxes[target_id] - 1); + } + $('#' + target_id).css('zIndex', this.zIndex.boxes[target_id]).show(); + // set target to this new level + // @ts-ignore + if (this.zIndex.active.indexOf(target_id) == -1) { + // @ts-ignore + this.zIndex.active.push(target_id); + } + this.zIndex.top = target_id; + // adjust size + this.adjustActionBox(target_id, override, content_override); + } + + /** + * TODO: better stacked action box: CLOSE + * @param {String} [target_id='actionBox'] which action box to close, default is set + * @param {Boolean} [clean=true] if set to false will not remove html content, just hide + */ + closeActionBoxFloat(target_id = 'actionBox', clean = true) + { + // do nothing if this does not exist + if (!exists(target_id)) { + return; + } + // clear storage object + if ( + keyInObject(target_id, this.action_box_storage) && clean === true + ) { + this.action_box_storage[target_id] = {}; + } + if (clean === true) { + $('#' + target_id).html(''); + } + $('#' + target_id).hide(); + // remove from active list + // @ts-ignore + let idx = this.zIndex.active.indexOf(target_id); + this.zIndex.active.splice(idx, 1); + // do we have any visible action boxes. + // find the highest zIndex and set overlayBox to this -1 + // @ts-ignore + let visible_zIndexes = $('#actionBox:visible, div[id^="actionBox-"].actionBoxElement:visible').map((i, el) => ({ + id: el.id, + zIndex: $('#' + el.id).css('zIndex') + })).get(); + if (visible_zIndexes.length > 0) { + let max_zIndex = 0; + let max_el_id = ''; + for (let zIndex_el of visible_zIndexes) { + if (parseInt(zIndex_el.zIndex) > max_zIndex) { + max_zIndex = parseInt(zIndex_el.zIndex); + max_el_id = zIndex_el.id; + } + } + $('#overlayBox').css('zIndex', max_zIndex - 1); + this.zIndex.top = max_el_id; + } else { + $('#overlayBox').hide(); + } + } + + /** + * create a new action box and fill it with basic elements + * @param {String} [target_id='actionBox'] + * @param {String} [title=''] + * @param {Object} [contents={}] + * @param {Object} [headers={}] + * @param {Boolean} [show_close=true] + * @param {Object} [settings={}] Optional settings, eg style sheets + */ + createActionBox( + target_id = 'actionBox', + title = '', + contents = {}, + headers = {}, + settings = {}, + show_close = true + ) { + if (!keyInObject(target_id, this.action_box_storage)) { + this.action_box_storage[target_id] = {}; + } + // settings can have the following + // : header_css:[] + // : action_box_css:[] + let header_css = []; + if (keyInObject('header_css', settings)) { + header_css = settings.header_css; + } + let action_box_css = []; + if (keyInObject('action_box_css', settings)) { + action_box_css = settings.action_box_css; + } + let elements = []; + // add title + close button to actionBox + elements.push(this.hec.phfo( + this.hec.aelx(this.hec.cel('div', target_id + '_title', '', ['actionBoxTitle', 'flx-spbt'].concat(header_css)), + ...show_close === true ? [ + // title + this.hec.cel('div', '', title, ['fs-b', 'w-80']), + // close button + this.hec.aelx(this.hec.cel('div', target_id + '_title_close_button', '', ['w-20', 'tar']), + this.hec.cel('input', target_id + '_title_close', '', ['button-close', 'fs-s'], + { + type: 'button', + value: this.l10n.__('Close'), + OnClick: 'closeActionBox(\'' + target_id + '\', false);' + } + ) + ) + ] : [ + this.hec.cel('div', '', title, ['fs-b', 'w-100']) + ] + ) + )); + // if we have header content, add that here + if (getObjectCount(headers) > 0) { + // if the element has an entry called "raw_string" then this does not need to be converted + if (keyInObject('raw_string', headers)) { + elements.push(headers.raw_string); + } else { + elements.push(this.hec.phfo(headers)); + } + } + // main content part (this should NOT be empty), if empty, add empty _content block + if (getObjectCount(contents) > 0) { + // if the element has an entry called "raw_string" then this does not need to be converted + if (keyInObject('raw_string', contents)) { + elements.push(contents.raw_string); + } else { + elements.push(this.hec.phfo(contents)); + } + } else { + elements.push(this.hec.phfo(this.hec.cel('div', target_id + '_content', '', []))); + } + // footer clear call + elements.push(this.hec.phfo( + this.hec.aelx(this.hec.cel('div', target_id + '_footer', '', ['pd-5', 'flx-spbt']), + ...show_close === true ? [ + // dummy spacer + this.hec.cel('div', '', '', ['fs-b', 'w-80']), + // close button + this.hec.aelx(this.hec.cel('div', target_id + '_footer_close_button', '', ['tar', 'w-20']), + this.hec.cel('input', target_id + '_footer_close', '', ['button-close', 'fs-s'], + { + type: 'button', + value: this.l10n.__('Close'), + OnClick: 'closeActionBox(\'' + target_id + '\', false);' + } + ) + ) + ] : [ + this.hec.cel('div', '', '', ['fs-b', 'w-100']) + ] + ) + )); + elements.push(this.hec.phfo(this.hec.cel('input', target_id + '-cache_time', '', [], { + type: 'hidden', + value: Date.now() + }))); + this.fillActionBox(target_id, elements.join(''), action_box_css); + } + + /** + * adjusts the action box height based on content and window height of browser + * TODO: border on outside/and other margin things need to be added in overall adjustment + * @param {String} [target_id='actionBox'] target id, if not set, fall back to default + * @param {Number} [override=0] override value to add to the actionBox height + * @param {Number} [content_override=0] override the value from _content block if it exists + */ + adjustActionBoxHeight(target_id = 'actionBox', override = 0, content_override = 0) + { + var new_height = 0; + var dim = {}; + var abc_dim = {}; + var content_id = ''; + // make sure it is a number + if (isNaN(override)) { + override = 0; + } + if (isNaN(content_override)) { + content_override = 0; + } + // set the target/content ids + switch (target_id) { + case 'actionBox': + content_id = 'action_box'; + break; + case 'actionBoxSub': + content_id ='action_box_sub'; + break; + default: + content_id = target_id; + break; + } + // first remove any height, left, top style entris from target and content blocks + // @ts-ignore + $.each([target_id, content_id + '_content'], function(i, v) { + $('#' + v).css({ + 'height': '', + 'width': '' + }); + }); + if (exists(content_id + '_title')) { + dim.height = $('#' + content_id + '_title').outerHeight(); + console.log('Target: %s, Action box Title: %s', target_id, dim.height); + new_height += dim.height ?? 0; + } + if (exists(content_id + '_header')) { + dim.height = $('#' + content_id + '_header').outerHeight(); + console.log('Target: %s, Action box Header: %s', target_id, dim.height); + new_height += dim.height ?? 0; + } + if (exists(content_id + '_content')) { + if (content_override > 0) { + console.log('Target: %s, Action box Content Override: %s', target_id, content_override); + new_height += content_override; + } else { + abc_dim.height = $('#' + content_id + '_content').outerHeight(); + console.log('Target: %s, Action box Content: %s', target_id, abc_dim.height); + new_height += abc_dim.height ?? 0; + } + } + // always there sets + if (exists(content_id + '_footer')) { + dim.height = $('#' + content_id + '_footer').outerHeight(); + console.log('Target: %s, Action box Footer: %s', target_id, dim.height); + new_height += dim.height ?? 0; + } + // get difference for the rest from outer box + // console.log('Target: %s, Action box outer: %s, Content: %s, New: %s', target_id, $('#' + target_id).outerHeight(), $('#' + content_id + '_content').outerHeight(), new_height); + // new_height += ($('#' + target_id).outerHeight() - new_height) + override; + new_height += override; + // get border width top-bottom from action Box, we need to remove this from the final height + // console.log('Target: %s, Border top: %s', target_id, $('#' + target_id).css('border-top-width')); + // get window size and check if content is bigger + var viewport = getWindowSize(); + if (new_height >= viewport.height) { + // resize the action box content and set overflow [of-s-y] + if (exists(content_id + '_content')) { + if (!$('#' + content_id + '_content').hasClass('of-s-y')) { + $('#' + content_id + '_content').addClass('of-s-y'); + } + } + console.log('Target: %s, Viewport: %s, ActionBox (NH): %s, ABcontent: %s, ABouter: %s', target_id, viewport.height, new_height, abc_dim.height, $('#' + target_id).outerHeight()); + // the height off window - all - action box gives new action box height + var m_height = viewport.height - (new_height - (abc_dim.height ?? 0)); + console.log('Target: %s, New ABcontent: %s', target_id, m_height); + $('#' + content_id + '_content').css('height', m_height + 'px'); + new_height = new_height - (abc_dim.height ?? 0) + m_height; + console.log('Target: %s, New Hight: %s', target_id, new_height); + } else { + // if size ok, check if overflow scoll is set, remove it + if (exists(content_id + '_content')) { + if ($('#' + content_id + '_content').hasClass('of-s-y')) { + $('#' + content_id + '_content').removeClass('of-s-y'); + } + } + } + console.log('Target: %s, Action Box new height: %s px (override %s px, content override %s px), window height: %s px, Visible Height: %s px', target_id, new_height, override, content_override, viewport.height, $('#' + content_id).outerHeight()); + // adjust height + $('#' + target_id).css('height', new_height + 'px'); + } +} + +// __EMD__ diff --git a/src/utils/ActionIndicatorOverlayBox.mjs b/src/utils/ActionIndicatorOverlayBox.mjs new file mode 100644 index 0000000..5b9100f --- /dev/null +++ b/src/utils/ActionIndicatorOverlayBox.mjs @@ -0,0 +1,305 @@ +/* +Description: Action Indicator and Overlay +Date: 2025/2/7 +Creator: Clemens Schwaighofer +*/ + +import { setCenter } from './ResizingAndMove.mjs'; +export { + ActionIndicatorOverlayBox, + actionIndicator, actionIndicatorShow, actionIndicatorHide, overlayBoxShow, + overlayBoxHide, setOverlayBox, hideOverlayBox, ClearCall +}; + +/************************************************************* + * OLD action indicator and overlay boxes calls + * DO NOT USE + * actionIndicator -> showActionIndicator + * actionIndicator -> hideActionIndicator + * actionIndicatorShow -> showActionIndicator + * actionIndicatorHide -> hideActionIndicator + * overlayBoxShow -> showOverlayBoxLayers + * overlayBoxHide -> hideOverlayBoxLayers + * setOverlayBox -> showOverlayBoxLayers + * hideOverlayBox -> hideOverlayBoxLayers + * ClearCall -> clearCallActionBox + * ***********************************************************/ + +/** + * show or hide the "do" overlay + * @param {String} loc location name for action indicator + * default empty. for console.log + * @param {Boolean} [overlay=true] override the auto hide/show over the overlay div block + * @deprecated showActionIndicator, hideActionIndicator + */ +function actionIndicator(loc, overlay = true) +{ + if ($('#indicator').is(':visible')) { + this.actionIndicatorHide(loc, overlay); + } else { + this.actionIndicatorShow(loc, overlay); + } +} + +/** + * explicit show for action Indicator + * instead of automatically show or hide, do on command show + * @param {String} loc location name for action indicator + * default empty. for console.log + * @param {Boolean} [overlay=true] override the auto hide/show over the overlay div block + * @deprecated showActionIndicator, hideActionIndicator + */ +function actionIndicatorShow(loc, overlay = true) +{ + // console.log('{Indicator}: SHOW [%s]', loc); + if (!$('#indicator').is(':visible')) { + if (!$('#indicator').hasClass('progress')) { + $('#indicator').addClass('progress'); + } + setCenter('indicator', true, true); + $('#indicator').show(); + } + if (overlay === true) { + this.overlayBoxShow(); + } +} + +/** + * explicit hide for action Indicator + * instead of automatically show or hide, do on command hide + * @param {String} loc location name for action indicator + * default empty. for console.log + * @param {Boolean} [overlay=true] override the auto hide/show over the overlay div block + * @deprecated hideActionIndicator + */ +function actionIndicatorHide(loc, overlay = true) +{ + // console.log('{Indicator}: HIDE [%s]', loc); + $('#indicator').hide(); + if (overlay === true) { + overlayBoxHide(); + } +} + +/** + * shows the overlay box or if already visible, bumps the zIndex to 100 + * @deprecated showOverlayBoxLayers + */ +function overlayBoxShow() +{ + // check if overlay box exists and if yes set the z-index to 100 + if ($('#overlayBox').is(':visible')) { + $('#overlayBox').css('zIndex', '100'); + } else { + $('#overlayBox').show(); + $('#overlayBox').css('zIndex', '98'); + } +} + +/** + * hides the overlay box or if zIndex is 100 bumps it down to previous level + * @deprecated hideOverlayBoxLayers + */ +function overlayBoxHide() +{ + // if the overlay box z-index is 100, do no hide, but set to 98 + if (parseInt($('#overlayBox').css('zIndex')) >= 100) { + $('#overlayBox').css('zIndex', '98'); + } else { + $('#overlayBox').hide(); + } +} + +/** + * position the overlay block box and shows it + * @deprecated showOverlayBoxLayers + */ +function setOverlayBox() +{ + if (!$('#overlayBox').is(':visible')) { + $('#overlayBox').show(); + } +} + +/** + * opposite of set, always hides overlay box + * @deprecated hideOverlayBoxLayers + */ +function hideOverlayBox() +{ + if ($('#overlayBox').is(':visible')) { + $('#overlayBox').hide(); + } +} + +/** + * the abort call, clears the action box and hides it and the overlay box + * @deprecated clearCallActionBox + */ +function ClearCall() +{ + $('#actionBox').html(''); + $('#actionBox').hide(); + $('#overlayBox').hide(); +} + +class ActionIndicatorOverlayBox { + + // open overlay boxes counter for z-index + #GL_OB_S = 100; + #GL_OB_BASE = 100; + + /** + * show action indicator + * - checks if not existing and add + * - only shows if not visible (else ignore) + * - overlaybox check is called and shown on a fixzed + * zIndex of 1000 + * - indicator is page centered + * @param {String} loc ID string, only used for console log + */ + showActionIndicator(loc) // eslint-disable-line no-unused-vars + { + // console.log('{Indicator}: SHOW [%s]', loc); + // check if indicator element exists + if ($('#indicator').length == 0) { + var el = document.createElement('div'); + el.className = 'progress hide'; + el.id = 'indicator'; + $('body').append(el); + } else if (!$('#indicator').hasClass('progress')) { + // if I add a class it will not be hidden anymore + // hide it + $('#indicator').addClass('progress').hide(); + } + // indicator not visible + if (!$('#indicator').is(':visible')) { + // check if overlay box element exits + this.checkOverlayExists(); + // if not visible show + if (!$('#overlayBox').is(':visible')) { + $('#overlayBox').show(); + } + // always set to 1000 zIndex to be top + $('#overlayBox').css('zIndex', 1000); + // show indicator + $('#indicator').show(); + // center it + setCenter('indicator', true, true); + } + } + + /** + * hide action indicator, if it is visiable + * If the global variable GL_OB_S is > GL_OB_BASE then + * the overlayBox is not hidden but the zIndex + * is set to this value + * @param {String} loc ID string, only used for console log + */ + hideActionIndicator(loc) // eslint-disable-line no-unused-vars + { + // console.log('{Indicator}: HIDE [%s]', loc); + // check if indicator is visible + if ($('#indicator').is(':visible')) { + // hide indicator + $('#indicator').hide(); + // if global overlay box count is > 0 + // then set it to this level and keep + if (this.#GL_OB_S > this.#GL_OB_BASE) { + $('#overlayBox').css('zIndex', this.#GL_OB_S); + } else { + // else hide overlay box and set zIndex to 0 + $('#overlayBox').hide(); + $('#overlayBox').css('zIndex', this.#GL_OB_BASE); + } + } + } + + /** + * checks if overlayBox exists, if not it is + * added as hidden item at the body end + */ + checkOverlayExists() + { + // check if overlay box exists, if not create it + if ($('#overlayBox').length == 0) { + var el = document.createElement('div'); + el.className = 'overlayBoxElement hide'; + el.id = 'overlayBox'; + $('body').append(el); + } + } + + /** + * show overlay box + * if not visible show and set zIndex to 10 (GL_OB_BASE) + * if visible, add +1 to the GL_OB_S variable and + * up zIndex by this value + */ + showOverlayBoxLayers(el_id) + { + // console.log('SHOW overlaybox: %s', GL_OB_S); + // if overlay box is not visible show and set zIndex to 0 + if (!$('#overlayBox').is(':visible')) { + $('#overlayBox').show(); + $('#overlayBox').css('zIndex', this.#GL_OB_BASE); + // also set start variable to 0 + this.#GL_OB_S = this.#GL_OB_BASE; + } + // up the overlay box counter by 1 + this.#GL_OB_S ++; + // set zIndex + $('#overlayBox').css('zIndex', this.#GL_OB_S); + // if element given raise zIndex and show + if (el_id) { + if ($('#' + el_id).length > 0) { + $('#' + el_id).css('zIndex', this.#GL_OB_S + 1); + $('#' + el_id).show(); + } + } + // console.log('SHOW overlaybox NEW zIndex: %s', $('#overlayBox').css('zIndex')); + } + + /** + * hide overlay box + * lower GL_OB_S value by -1 + * if we are 10 (GL_OB_BASE) or below hide the overlayIndex + * and set zIndex and GL_OB_S to 0 + * else just set zIndex to the new GL_OB_S value + * @param {String} el_id Target to hide layer + */ + hideOverlayBoxLayers(el_id='') + { + // console.log('HIDE overlaybox: %s', GL_OB_S); + // remove on layer + this.#GL_OB_S --; + // if 0 or lower (overflow) hide it and + // set zIndex to 0 + if (this.#GL_OB_S <= this.#GL_OB_BASE) { + this.#GL_OB_S = this.#GL_OB_BASE; + $('#overlayBox').hide(); + $('#overlayBox').css('zIndex', this.#GL_OB_BASE); + } else { + // if OB_S > 0 then set new zIndex + $('#overlayBox').css('zIndex', this.#GL_OB_S); + } + if (el_id) { + $('#' + el_id).hide(); + $('#' + el_id).css('zIndex', 0); + } + // console.log('HIDE overlaybox NEW zIndex: %s', $('#overlayBox').css('zIndex')); + } + + /** + * only for single action box + */ + clearCallActionBox() + { + $('#actionBox').html(''); + $('#actionBox').hide(); + this.hideOverlayBoxLayers(); + } +} + + +// __END__ diff --git a/src/utils/DateTimeHelpers.mjs b/src/utils/DateTimeHelpers.mjs new file mode 100644 index 0000000..ae1bd94 --- /dev/null +++ b/src/utils/DateTimeHelpers.mjs @@ -0,0 +1,19 @@ +/* +Description: Date Time functions +Date: 2025//3/6 +Creator: Clemens Schwaighofer +*/ + +export { getTimestamp }; + +/** + * returns current timestamp (unix timestamp) + * @return {Number} timestamp (in milliseconds) + */ +function getTimestamp() +{ + var date = new Date(); + return date.getTime(); +} + +// __END__ diff --git a/src/utils/DomHelpers.mjs b/src/utils/DomHelpers.mjs new file mode 100644 index 0000000..eae789b --- /dev/null +++ b/src/utils/DomHelpers.mjs @@ -0,0 +1,75 @@ +/* +Description: DOM Helpers +Date: 2025//3/6 +Creator: Clemens Schwaighofer +*/ + +export { loadEl, pop, expandTA, exists }; + +/** + * Gets html element or throws an error + * @param {string} el_id Element ID to get + * @returns {HTMLElement} + * @throws Error + */ +function loadEl(el_id) +{ + let el = document.getElementById(el_id); + if (el === null) { + throw new Error('Cannot find: ' + el_id); + } + return el; +} + +/** + * opens a popup window with winName and given features (string) + * @param {String} theURL the url + * @param {String} winName window name + * @param {Object} features popup features + */ +function pop(theURL, winName, features) +{ + let __winName = window.open(theURL, winName, features); + if (__winName == null) { + return; + } + __winName.focus(); +} + +/** + * automatically resize a text area based on the amount of lines in it + * @param {string} ta_id element id + */ +function expandTA(ta_id) +{ + let ta = this.loadEl(ta_id); + if (ta instanceof HTMLElement && ta.getAttribute('type') !== "textarea") { + throw new Error("Element is not a textarea: " + ta_id); + } + let maxChars = parseInt(ta.getAttribute('cols') ?? "0"); + let ta_value = ta.getAttribute('value'); + let theRows = []; + if (ta_value != null) { + theRows = ta_value.split('\n'); + } + var numNewRows = 0; + + for ( var i = 0; i < theRows.length; i++ ) { + if ((theRows[i].length+2) > maxChars) { + numNewRows += Math.ceil( (theRows[i].length+2) / maxChars ) ; + } + } + ta.setAttribute('row', (numNewRows + theRows.length).toString()); +} + +/** + * checks if a DOM element actually exists + * @param {String} id Element id to check for + * @return {Boolean} true if element exists, false on failure + */ +function exists(id) +{ + return $('#' + id).length > 0 ? true : false; +} + +// __END__ diff --git a/src/utils/FormatBytes.mjs b/src/utils/FormatBytes.mjs new file mode 100644 index 0000000..5a717e7 --- /dev/null +++ b/src/utils/FormatBytes.mjs @@ -0,0 +1,80 @@ +/* +Description: Byte string formatting +Date: 2025//3/6 +Creator: Clemens Schwaighofer +*/ + +export { formatBytes, formatBytesLong, stringByteFormat }; + +/** + * converts a int number into bytes with prefix in two decimals precision + * currently precision is fixed, if dynamic needs check for max/min precision + * @param {Number} bytes bytes in int + * @return {String} string in GB/MB/KB + */ +function formatBytes(bytes) +{ + var i = -1; + do { + bytes = bytes / 1024; + i++; + } while (bytes > 99); + return ( + Math.round(bytes * Math.pow(10, 2)) / Math.pow(10, 2) + ) + ['kB', 'MB', 'GB', 'TB', 'PB', 'EB'][i]; +} + +/** + * like formatBytes, but returns bytes for <1KB and not 0.n KB + * @param {Number} bytes bytes in int + * @return {String} string in GB/MB/KB + */ +function formatBytesLong(bytes) +{ + if (isNaN(bytes)) { + return bytes.toString(); + } + var i = Math.floor(Math.log(bytes) / Math.log(1024)); + var sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; + return ( + ( + bytes / + Math.pow(1024, i) + ).toFixed(2) + // * 1 + ' ' + sizes[i] + + ' ' + sizes[i] + ).toString(); +} + +/** + * Convert a string with B/K/M/etc into a byte number + * @param {String|Number|Object} bytes Any string with B/K/M/etc + * @return {String|Number} A byte number, or original string as is + */ +function stringByteFormat(bytes) +{ + // if anything not string return + if (!(typeof bytes === 'string' || bytes instanceof String)) { + return bytes.toString(); + } + // for pow exponent list + let valid_units = 'bkmgtpezy'; + // valid string that can be converted + let regex = /([\d.,]*)\s?(eb|pb|tb|gb|mb|kb|e|p|t|g|m|k|b)$/i; + let matches = bytes.match(regex); + // if nothing found, return original input + if (matches !== null) { + // remove all non valid entries outside numbers and . + // convert to float number + let m1 = parseFloat(matches[1].replace(/[^0-9.]/,'')); + // only get the FIRST letter from the size, convert it to lower case + let m2 = matches[2].replace(/[^bkmgtpezy]/i, '').charAt(0).toLowerCase(); + if (m2) { + // use the position in the valid unit list to do the math conversion + bytes = m1 * Math.pow(1024, valid_units.indexOf(m2)); + } + } + return bytes; +} + +// __END__ diff --git a/src/utils/HtmlElementCreator.mjs b/src/utils/HtmlElementCreator.mjs new file mode 100644 index 0000000..f7eed51 --- /dev/null +++ b/src/utils/HtmlElementCreator.mjs @@ -0,0 +1,276 @@ +/* +Description: DOM Management and HTML builder +Date: 2025//3/6 +Creator: Clemens Schwaighofer +*/ + +export { + HtmlElementCreator, + // deprecated name + HtmlElementCreator as DomManagement +}; +import { deepCopyFunction, isObject } from './JavaScriptHelpers.mjs'; + +class HtmlElementCreator { + /** + * reates object for DOM element creation flow + * @param {String} tag must set tag (div, span, etc) + * @param {String} [id=''] optional set for id, if input, select will be used for name + * @param {String} [content=''] text content inside, is skipped if sub elements exist + * @param {Array} [css=[]] array for css tags + * @param {Object} [options={}] anything else (value, placeholder, OnClick, style) + * @return {Object} created element as an object + */ + cel(tag, id = '', content = '', css = [], options = {}) + { + return { + tag: tag, + id: id, + name: options.name, // override name if set [name gets ignored in tree build anyway] + content: content, + css: css, + options: options, + sub: [] + }; + } + + /** + * attach a cel created object to another to create a basic DOM tree + * @param {Object} base object where to attach/search + * @param {Object} attach the object to be attached + * @param {String} [id=''] optional id, if given search in base for this id and attach there + * @return {Object} "none", technically there is no return needed as it is global attach + */ + ael(base, attach, id = '') + { + if (id) { + // base id match already + if (base.id == id) { + // base.sub.push(Object.assign({}, attach)); + base.sub.push(deepCopyFunction(attach)); + } else { + // sub check + if (isObject(base.sub) && base.sub.length > 0) { + for (var i = 0; i < base.sub.length; i ++) { + // recursive call to sub element + this.ael(base.sub[i], attach, id); + } + } + } + } else { + // base.sub.push(Object.assign({}, attach)); + base.sub.push(deepCopyFunction(attach)); + } + return base; + } + + /** + * directly attach n elements to one master base element + * this type does not support attach with optional id + * @param {Object} base object to where we attach the elements + * @param {...Object} attach attach 1..n: attach directly to the base element those attachments + * @return {Object} "none", technically there is no return needed, global attach + */ + aelx(base, ...attach) + { + for (var i = 0; i < attach.length; i ++) { + // base.sub.push(Object.assign({}, attach[i])); + base.sub.push(deepCopyFunction(attach[i])); + } + return base; + } + + /** + * same as aelx, but instead of using objects as parameters + * get an array of objects to attach + * @param {Object} base object to where we attach the elements + * @param {Array} attach array of objects to attach + * @return {Object} "none", technically there is no return needed, global attach + */ + aelxar(base, attach) + { + for (var i = 0; i < attach.length; i ++) { + // base.sub.push(Object.assign({}, attach[i])); + base.sub.push(deepCopyFunction(attach[i])); + } + return base; + } + + /** + * resets the sub elements of the base element given + * @param {Object} base cel created element + * @return {Object} returns reset base element + */ + rel(base) + { + base.sub = []; + return base; + } + + /** + * searches and removes style from css array + * @param {Object} _element element to work one + * @param {String} css style sheet to remove (name) + * @return {Object} returns full element + */ + rcssel(_element, css) + { + var css_index = _element.css.indexOf(css); + if (css_index > -1) { + _element.css.splice(css_index, 1); + } + return _element; + } + + /** + * adds a new style sheet to the element given + * @param {Object} _element element to work on + * @param {String} css style sheet to add (name) + * @return {Object} returns full element + */ + acssel(_element, css) + { + var css_index = _element.css.indexOf(css); + if (css_index == -1) { + _element.css.push(css); + } + return _element; + } + + /** + * removes one css and adds another + * is a wrapper around rcssel/acssel + * @param {Object} _element element to work on + * @param {String} rcss style to remove (name) + * @param {String} acss style to add (name) + * @return {Object} returns full element + */ + scssel(_element, rcss, acss) + { + this.rcssel(_element, rcss); + this.acssel(_element, acss); + } + + /** + * parses the object tree created with cel/ael and converts it into an HTML string + * that can be inserted into the page + * @param {Object} tree object tree with dom element declarations + * @return {String} HTML string that can be used as innerHTML + */ + phfo(tree) + { + let name_elements = [ + 'button', + 'fieldset', + 'form', + 'iframe', + 'input', + 'map', + 'meta', + 'object', + 'output', + 'param', + 'select', + 'textarea', + ]; + let skip_options = [ + 'id', + 'name', + 'class', + ]; + let no_close = [ + 'input', + 'br', + 'img', + 'hr', + 'area', + 'col', + 'keygen', + 'wbr', + 'track', + 'source', + 'param', + 'command', + // only in header + 'base', + 'meta', + 'link', + 'embed', + ]; + // holds the elements + var content = []; + // main part line + var line = '<' + tree.tag; + var i; + // first id, if set + if (tree.id) { + line += ' id="' + tree.id + '"'; + // if anything input (input, textarea, select then add name too) + if (name_elements.includes(tree.tag)) { + line += ' name="' + (tree.name ? tree.name : tree.id) + '"'; + } + } + // second CSS + if (isObject(tree.css) && tree.css.length > 0) { + line += ' class="'; + for (i = 0; i < tree.css.length; i ++) { + line += tree.css[i] + ' '; + } + // strip last space + line = line.slice(0, -1); + line += '"'; + } + // options is anything key = "data" + if (isObject(tree.options)) { + // ignores id, name, class as key + for (const [key, item] of Object.entries(tree.options)) { + if (!skip_options.includes(key)) { + line += ' ' + key + '="' + item + '"'; + } + } + } + // finish open tag + line += '>'; + // push finished line + content.push(line); + // dive into sub tree to attach sub nodes + // NOTES: we can have content (text) AND sub nodes at the same level + // CONTENT (TEXT) takes preference over SUB NODE in order + if (isObject(tree.sub) && tree.sub.length > 0) { + if (tree.content) { + content.push(tree.content); + } + for (i = 0; i < tree.sub.length; i ++) { + content.push(this.phfo(tree.sub[i])); + } + } else if (tree.content) { + content.push(tree.content); + } + // if not input, image or br, then close + if ( + !no_close.includes(tree.tag) + ) { + content.push(''); + } + // combine to string + return content.join(''); + } + + /** + * Create HTML elements from array list + * as a flat element without master object file + * Is like tree.sub call + * @param {Array} list Array of cel created objects + * @return {String} HTML String + */ + phfa(list) + { + var content = []; + for (var i = 0; i < list.length; i ++) { + content.push(this.phfo(list[i])); + } + return content.join(''); + } +} + +// __EMD__ diff --git a/src/utils/HtmlHelpers.mjs b/src/utils/HtmlHelpers.mjs new file mode 100644 index 0000000..214c9cb --- /dev/null +++ b/src/utils/HtmlHelpers.mjs @@ -0,0 +1,217 @@ +/* +Description: HTML Helpers +Date: 2025//3/6 +Creator: Clemens Schwaighofer +*/ + +export { escapeHtml, unescapeHtml, html_options, html_options_block, html_options_refill }; +import { loadEl} from './DomHelpers.mjs'; +import { DomManagement } from './HtmlElementCreator.mjs'; +let dom = new DomManagement(); + +/** + * Escapes HTML in string + * @param {String} string Text to escape HTML in + * @returns {String} + */ +function escapeHtml(string) +{ + return string.replace(/[&<>"'/]/g, function (s) { + var entityMap = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + '\'': ''', + '/': '/' + }; + + return entityMap[s]; + }); +} + +/** + * Unescape a HTML encoded string + * @param {String} string Text to unescape HTML in + * @returns {String} + */ +function unescapeHtml(string) +{ + return string.replace(/&[#\w]+;/g, function (s) { + var entityMap = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + ''': '\'', + '/': '/' + }; + + return entityMap[s]; + }); +} + +// BLOCK: html wrappers for quickly creating html data blocks + +/** + * NOTE: OLD FORMAT which misses multiple block set + * creates an select/options drop down block. + * the array needs to be key -> value format. + * key is for the option id and value is for the data output + * @param {String} name name/id + * @param {Object} data array for the options + * @param {String} [selected=''] selected item uid + * @param {Boolean} [options_only=false] if this is true, it will not print the select part + * @param {Boolean} [return_string=false] return as string and not as element + * @param {String} [sort=''] if empty as is, else allowed 'keys', + * 'values' all others are ignored + * @return {String} html with build options block + * @deprecated html_options_block + */ +function html_options(name, data, selected = '', options_only = false, return_string = false, sort = '') +{ + // wrapper to new call + return this.html_options_block( + name, data, selected, 0, options_only, return_string, sort + ); +} + +/** + * NOTE: USE THIS CALL, the above one is deprecated + * creates an select/options drop down block. + * the array needs to be key -> value format. + * key is for the option id and value is for the data output + * @param {String} name name/id + * @param {Object} data array for the options + * @param {String} [selected=''] selected item uid + * @param {Number} [multiple=0] if this is 1 or larger, the drop down + * will be turned into multiple select + * the number sets the size value unless it is 1, + * then it is default + * @param {Boolean} [options_only=false] if this is true, it will not print the select part + * @param {Boolean} [return_string=false] return as string and not as element + * @param {String} [sort=''] if empty as is, else allowed 'keys', + * 'values' all others are ignored + * @param {String} [onchange=''] onchange trigger call, default unset + * @return {String} html with build options block + */ +function html_options_block( + name, data, selected = '', multiple = 0, options_only = false, return_string = false, sort = '', onchange = '' +) { + var content = []; + var element_select; + var select_options = {}; + var element_option; + var data_list = []; // for sorted output + var value; + var options = {}; + // var option; + if (multiple > 0) { + select_options.multiple = ''; + if (multiple > 1) { + select_options.size = multiple; + } + } + if (onchange) { + select_options.OnChange = onchange; + } + // set outside select, gets stripped on return if options only is true + element_select = dom.cel('select', name, '', [], select_options); + // console.log('Call for %s, options: %s', name, options_only); + if (sort == 'keys') { + data_list = Object.keys(data).sort(); + } else if (sort == 'values') { + data_list = Object.keys(data).sort((a, b) => ('' + data[a]).localeCompare(data[b])); + } else { + data_list = Object.keys(data); + } + // console.log('ORDER: %s', data_list); + // use the previously sorted list + // for (const [key, value] of Object.entries(data)) { + for (const key of data_list) { + value = data[key]; + // console.log('create [%s] options: key: %s, value: %s', name, key, value); + // basic options init + options = { + 'label': value, + 'value': key, + 'selected': '' + }; + // add selected if matching + if (multiple == 0 && !Array.isArray(selected) && selected == key) { + options.selected = ''; + } + // for multiple, we match selected as array + if (multiple == 1 && Array.isArray(selected) && selected.indexOf(key) != -1) { + options.selected = ''; + } + // create the element option + element_option = dom.cel('option', '', value, [], options); + // attach it to the select element + dom.ael(element_select, element_option); + } + // if with select part, convert to text + if (!options_only) { + if (return_string) { + content.push(dom.phfo(element_select)); + return content.join(''); + } else { + return element_select; + } + } else { + // strip select part + if (return_string) { + for (var i = 0; i < element_select.sub.length; i ++) { + content.push(dom.phfo(element_select.sub[i])); + } + return content.join(''); + } else { + return element_select.sub; + } + } +} + +/** + * refills a select box with options and keeps the selected + * @param {String} name name/id + * @param {Object} data array of options + * @param {String} [sort=''] if empty as is, else allowed 'keys', 'values' + * all others are ignored + */ +function html_options_refill(name, data, sort = '') +{ + var element_option; + var option_selected; + var data_list = []; // for sorted output + var value; + // skip if not exists + if (document.getElementById(name)) { + // console.log('Call for %s, options: %s', name, options_only); + if (sort == 'keys') { + data_list = Object.keys(data).sort(); + } else if (sort == 'values') { + data_list = Object.keys(data).sort((a, b) => ('' + data[a]).localeCompare(data[b])); + } else { + data_list = Object.keys(data); + } + // first read in existing ones from the options and get the selected one + [].forEach.call(document.querySelectorAll('#' + name + ' :checked'), function(elm) { + option_selected = elm.value; + }); + loadEl(name).innerHTML = ''; + for (const key of data_list) { + value = data[key]; + // console.log('add [%s] options: key: %s, value: %s', name, key, value); + element_option = document.createElement('option'); + element_option.label = value; + element_option.value = key; + element_option.innerHTML = value; + if (key == option_selected) { + element_option.selected = true; + } + loadEl(name).appendChild(element_option); + } + } +} + +// __EMD__ diff --git a/src/utils/JavaScriptHelpers.mjs b/src/utils/JavaScriptHelpers.mjs new file mode 100644 index 0000000..7583cb1 --- /dev/null +++ b/src/utils/JavaScriptHelpers.mjs @@ -0,0 +1,159 @@ +/* +Description: JavaScript Helpers +Date: 2025//3/6 +Creator: Clemens Schwaighofer +*/ + +export { + errorCatch, isFunction, executeFunctionByName, isObject, getObjectCount, + keyInObject, getKeyByValue,valueInObject, deepCopyFunction +}; + +/** + * prints out error messages based on data available from the browser + * @param {Object} err error from try/catch block + */ +function errorCatch(err) +{ + // for FF & Chrome + if (err.stack) { + // only FF + if (err.lineNumber) { + console.error('ERROR[%s:%s] ', err.name, err.lineNumber, err); + } else if (err.line) { + // only Safari + console.error('ERROR[%s:%s] ', err.name, err.line, err); + } else { + console.error('ERROR[%s] ', err.name, err); + } + } else if (err.number) { + // IE + console.error('ERROR[%s:%s] %s', err.name, err.number, err.message); + console.error('ERROR[description] %s', err.description); + } else { + // the rest + console.error('ERROR[%s] %s', err.name, err.message); + } +} + +/** + * check if name is a function + * @param {string} name Name of function to check if exists + * @return {Boolean} true/false + */ +function isFunction(name) +{ + if (typeof window[name] !== 'undefined' && + typeof window[name] === 'function') { + return true; + } else { + return false; + } +} + +/** + * call a function by its string name + * https://stackoverflow.com/a/359910 + * example: executeFunctionByName("My.Namespace.functionName", window, arguments); + * @param {string} functionName The function name or namespace + function + * @param {any} context context (window or first namespace) + * hidden next are all the arguments + * @return {any} Return values from functon + */ +function executeFunctionByName(functionName, context /*, args */) +{ + var args = Array.prototype.slice.call(arguments, 2); + var namespaces = functionName.split('.'); + var func = namespaces.pop(); + if (func == undefined) { + throw new Error("Cannot get function from namespaces: " + functionName); + } + for (var i = 0; i < namespaces.length; i++) { + context = context[namespaces[i]]; + } + return context[func].apply(context, args); +} + +/** + * checks if a variable is an object + * @param {any} val possible object + * @return {Boolean} true/false if it is an object or not + */ +function isObject(val) +{ + if (val === null) { + return false; + } + return ((typeof val === 'function') || (typeof val === 'object')); +} + +/** + * get the length of an object (entries) + * @param {Object} object object to check + * @return {Number} number of entry + */ +function getObjectCount(object) +{ + return Object.keys(object).length; +} + +/** + * checks if a key exists in a given object + * @param {String} key key name + * @param {Object} object object to search key in + * @return {Boolean} true/false if key exists in object + */ +function keyInObject(key, object) +{ + return Object.prototype.hasOwnProperty.call(object, key) ? true : false; +} + +/** + * returns matching key of value + * @param {Object} object object to search value in + * @param {any} value any value (String, Number, etc) + * @return {String} the key found for the first matching value + */ +function getKeyByValue(object, value) +{ + return Object.keys(object).find(key => object[key] === value) ?? ''; +} + +/** + * returns true if value is found in object with a key + * @param {Object} object object to search value in + * @param {any} value any value (String, Number, etc) + * @return {Boolean} true on value found, false on not found + */ +function valueInObject(object, value) +{ + return Object.keys(object).find(key => object[key] === value) ? true : false; +} + +/** + * true deep copy for Javascript objects + * if Object.assign({}, obj) is not working (shallow) + * or if JSON.parse(JSON.stringify(obj)) is failing + * @param {Object} inObject Object to copy + * @return {Object} Copied Object + */ +function deepCopyFunction(inObject) +{ + var outObject, value, key; + if (typeof inObject !== 'object' || inObject === null) { + // Return the value if inObject is not an object + return inObject; + } + // Create an array or object to hold the values + outObject = Array.isArray(inObject) ? [] : {}; + // loop over ech entry in object + for (key in inObject) { + value = inObject[key]; + // Recursively (deep) copy for nested objects, including arrays + outObject[key] = deepCopyFunction(value); + } + + return outObject; +} + +// __END__ diff --git a/src/utils/LoginMenu.mjs b/src/utils/LoginMenu.mjs new file mode 100644 index 0000000..b8ac107 --- /dev/null +++ b/src/utils/LoginMenu.mjs @@ -0,0 +1,111 @@ +/* +Description: Login access and menu +Date: 2025//3/6 +Creator: Clemens Schwaighofer +*/ + +export { loginLogout, createLoginRow, createNavMenu }; +import { isObject, getObjectCount } from './JavaScriptHelpers.mjs'; +import { exists } from './DomHelpers.mjs'; +import { HtmlElementCreator } from './HtmlElementCreator.mjs'; +import { l10nTranslation } from './l10nTranslation.mjs'; +let hec = new HtmlElementCreator(); +let l10n = new l10nTranslation(i18n ?? {}); + +/** + * submits basic data for form logout + */ +function loginLogout() +{ + const form = document.createElement('form'); + form.method = 'post'; + const hiddenField = document.createElement('input'); + hiddenField.type = 'hidden'; + hiddenField.name = 'login_logout'; + hiddenField.value = 'Logout'; + form.appendChild(hiddenField); + document.body.appendChild(form); + form.submit(); +} + +/** + * create login string and logout button elements + * @param {String} login_string the login string to show on the left + * @param {String} [header_id='mainHeader'] the target for the main element block + * if not set mainHeader is assumed + * this is the target div for the "loginRow" + */ +function createLoginRow(login_string, header_id = 'mainHeader') +{ + // if header does not exist, we do nothing + if (exists(header_id)) { + // that row must exist already, if not it must be the first in the "mainHeader" + if (!exists('loginRow')) { + $('#' + header_id).html(hec.phfo(hec.cel('div', 'loginRow', '', ['loginRow', 'flx-spbt']))); + } + // clear out just in case for first entry + // fill with div name & login/logout button + $('#loginRow').html(hec.phfo(hec.cel('div', 'loginRow-name', login_string))); + $('#loginRow').append(hec.phfo(hec.cel('div', 'loginRow-info', ''))); + $('#loginRow').append(hec.phfo( + hec.aelx( + // outer div + hec.cel('div', 'loginRow-logout'), + // inner element + hec.cel('input', 'logout', '', [], { + value: l10n.__('Logout'), + type: 'button', + onClick: 'loginLogout()' + }) + ) + )); + } +} + +/** + * create the top nav menu that switches physical between pages + * (edit access data based) + * @param {Object} nav_menu the built nav menu with highlight info + * @param {String} [header_id='mainHeader'] the target for the main element block + * if not set mainHeader is assumed + * this is the target div for the "menuRow" + */ +function createNavMenu(nav_menu, header_id = 'mainHeader') +{ + // must be an object + if (isObject(nav_menu) && getObjectCount(nav_menu) > 1) { + // do we have more than one entry, if not, do not show (single page) + if (!exists('menuRow')) { + $('#' + header_id).html(hec.phfo(hec.cel('div', 'menuRow', '', ['menuRow', 'flx-s']))); + } + var content = []; + $.each(nav_menu, function(key, item) { + // key is number + // item is object with entries + if (key != 0) { + content.push(hec.phfo(hec.cel('div', '', '·', ['pd-2']))); + } + // ignore item.popup for now + if (item.enabled) { + // set selected based on window.location.href as the php set will not work + if (window.location.href.indexOf(item.url) != -1) { + item.selected = 1; + } + // create the entry + content.push(hec.phfo( + hec.aelx( + hec.cel('div'), + hec.cel('a', '', item.name, ['pd-2'].concat(item.selected ? 'highlight': ''), { + href: item.url + }) + ) + )); + } + }); + $('#menuRow').html(content.join('')); + } else { + $('#menuRow').hide(); + } +} + +// __END__ diff --git a/src/utils/MathHelpers.mjs b/src/utils/MathHelpers.mjs new file mode 100644 index 0000000..a52c36f --- /dev/null +++ b/src/utils/MathHelpers.mjs @@ -0,0 +1,50 @@ +/* +Description: Math Helpers +Date: 2025/3/6 +Creator: Clemens Schwaighofer +*/ + +export { dec2hex, getRandomIntInclusive, roundPrecision }; + +/** + * dec2hex :: Integer -> String + * i.e. 0-255 -> '00'-'ff' + * @param {Number} dec decimal string + * @return {String} hex encdoded number + */ +function dec2hex(dec) +{ + return ('0' + dec.toString(16)).substring(-2); +} + +/** + * generate a number between min/max + * with min/max inclusive. + * eg: 1,5 will create a number ranging from 1 o 5 + * @param {Number} min minimum int number inclusive + * @param {Number} max maximumg int number inclusive + * @return {Number} Random number + */ +function getRandomIntInclusive(min, max) +{ + min = Math.ceil(min); + max = Math.floor(max); + // The maximum is inclusive and the minimum is inclusive + return Math.floor(Math.random() * (max - min + 1) + min); +} + +/** + * Round a number to precision + * @param {Number} number Number to round + * @param {Number} precision Precision value + * @returns {Number} + */ +function roundPrecision(number, precision) +{ + if (!isNaN(number) || !isNaN(precision)) { + return number; + } + return Math.round(number * Math.pow(10, precision)) / Math.pow(10, precision); +} + +// __END__ diff --git a/src/utils/ResizingAndMove.mjs b/src/utils/ResizingAndMove.mjs new file mode 100644 index 0000000..67c2a59 --- /dev/null +++ b/src/utils/ResizingAndMove.mjs @@ -0,0 +1,128 @@ +/* +Description: Resize and Move Javascript +Date: 2025//3/6 +Creator: Clemens Schwaighofer +*/ + +import { errorCatch} from './JavaScriptHelpers.mjs'; +import { loadEl } from './DomHelpers.mjs'; +export { getWindowSize, getScrollOffset, getScrollOffsetOpener, setCenter, goToPos, goTo }; + +/** + * wrapper to get the real window size for the current browser window + * @return {Object} object with width/height + */ +function getWindowSize() +{ + var width, height; + width = window.innerWidth || (window.document.documentElement.clientWidth || window.document.body.clientWidth); + height = window.innerHeight || (window.document.documentElement.clientHeight || window.document.body.clientHeight); + return { + width: width, + height: height + }; +} + +/** + * wrapper to get the correct scroll offset + * @return {Object} object with x/y px + */ +function getScrollOffset() +{ + var left, top; + left = window.pageXOffset || (window.document.documentElement.scrollLeft || window.document.body.scrollLeft); + top = window.pageYOffset || (window.document.documentElement.scrollTop || window.document.body.scrollTop); + return { + left: left, + top: top + }; +} + +/** + * wrapper to get the correct scroll offset for opener page (from popup) + * @return {Object} object with x/y px + */ +function getScrollOffsetOpener() +{ + var left, top; + left = opener.window.pageXOffset || (opener.document.documentElement.scrollLeft || opener.document.body.scrollLeft); + top = opener.window.pageYOffset || (opener.document.documentElement.scrollTop || opener.document.body.scrollTop); + return { + left: left, + top: top + }; +} + +/** + * centers div to current window size middle + * @param {String} id element to center + * @param {Boolean} left if true centers to the middle from the left + * @param {Boolean} top if true centers to the middle from the top + */ +function setCenter(id, left, top) +{ + // get size of id + var dimensions = { + height: $('#' + id).height() ?? 0, + width: $('#' + id).width() ?? 0 + }; + var type = $('#' + id).css('position'); + var viewport = this.getWindowSize(); + var offset = this.getScrollOffset(); + + // console.log('Id %s, type: %s, dimensions %s x %s, viewport %s x %s', id, type, dimensions.width, dimensions.height, viewport.width, viewport.height); + // console.log('Scrolloffset left: %s, top: %s', offset.left, offset.top); + // console.log('Left: %s, Top: %s (%s)', parseInt((viewport.width / 2) - (dimensions.width / 2) + offset.left), parseInt((viewport.height / 2) - (dimensions.height / 2) + offset.top), parseInt((viewport.height / 2) - (dimensions.height / 2))); + if (left) { + $('#' + id).css({ + left: (viewport.width / 2) - (dimensions.width / 2) + offset.left + 'px' + }); + } + if (top) { + // if we have fixed, we do not add the offset, else it moves out of the screen + var top_pos = type == 'fixed' ? + (viewport.height / 2) - (dimensions.height / 2) : + (viewport.height / 2) - (dimensions.height / 2) + offset.top; + $('#' + id).css({ + top: top_pos + 'px' + }); + } +} + +/** + * goes to an element id position + * @param {String} element element id to move to + * @param {Number} [offset=0] offset from top, default is 0 (px) + * @param {Number} [duration=500] animation time, default 500ms + * @param {String} [base='body,html'] base element for offset scroll + */ +function goToPos(element, offset = 0, duration = 500, base = 'body,html') +{ + try { + let element_offset = $('#' + element).offset(); + if (element_offset == undefined) { + return; + } + if ($('#' + element).length) { + $(base).animate({ + scrollTop: element_offset.top - offset + }, duration); + } + } catch (err) { + errorCatch(err); + } +} + +/** + * go to element, scroll + * non jquery + * @param {string} target +*/ +function goTo(target) +{ + loadEl(target).scrollIntoView({ + behavior: 'smooth' + }); +} + +// __END__ diff --git a/src/utils/StringHelpers.mjs b/src/utils/StringHelpers.mjs new file mode 100644 index 0000000..e488ce3 --- /dev/null +++ b/src/utils/StringHelpers.mjs @@ -0,0 +1,49 @@ +/* +Description: String Helpers +Date: 2025//3/6 +Creator: Clemens Schwaighofer +*/ + +export { formatString, numberWithCommas, convertLBtoBR }; + +/** + * simple sprintf formater for replace + * usage: "{0} is cool, {1} is not".format("Alpha", "Beta"); + * First, checks if it isn't implemented yet. + * @param {String} string String with {..} entries + * @param {...any} args List of replacement + * @returns {String} Escaped string + */ +function formatString(string, ...args) +{ + return string.replace(/{(\d+)}/g, function(match, number) + { + return typeof args[number] != 'undefined' ? + args[number] : + match + ; + }); +} +/** + * formats flat number 123456 to 123,456 + * @param {Number} number number to be formated + * @return {String} formatted with , in thousands + */ +function numberWithCommas(number) +{ + var parts = number.toString().split('.'); + parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ','); + return parts.join('.'); +} + +/** + * converts line breaks to br + * @param {String} string any string + * @return {String} string with
+ */ +function convertLBtoBR(string) +{ + return string.replace(/(?:\r\n|\r|\n)/g, '
'); +} + +// __END__ diff --git a/src/utils/UniqIdGenerators.mjs b/src/utils/UniqIdGenerators.mjs new file mode 100644 index 0000000..0b51fb6 --- /dev/null +++ b/src/utils/UniqIdGenerators.mjs @@ -0,0 +1,36 @@ +/* +Description: Generate unique ids +Date: 2025/3/7 +Creator: Clemens Schwaighofer +*/ + +export { generateId, randomIdF }; + + +/** + * generateId :: Integer -> String + * only works on mondern browsers + * @param {Number} len length of unique id string + * @return {String} random string in length of len + */ +function generateId(len) +{ + var arr = new Uint8Array((len || 40) / 2); + ( + window.crypto || + // @ts-ignore + window.msCrypto + ).getRandomValues(arr); + return Array.from(arr, self.dec2hex).join(''); +} + +/** + * creates a pseudo random string of 10 characters + * works on all browsers + * after many runs it will create duplicates + * @return {String} not true random string + */ +function randomIdF() +{ + return Math.random().toString(36).substring(2); +} diff --git a/src/utils/UrlParser.mjs b/src/utils/UrlParser.mjs new file mode 100644 index 0000000..eb54e04 --- /dev/null +++ b/src/utils/UrlParser.mjs @@ -0,0 +1,108 @@ +/* +Description: HTML Helpers +Date: 2025//3/6 +Creator: Clemens Schwaighofer +*/ + +export { parseQueryString, getQueryStringParam }; +import { keyInObject } from './JavaScriptHelpers.mjs'; + +/** + * parses a query string from window.location.search.substring(1) + * ALTERNATIVE CODE + * var url = new URL(window.location.href); + * param_uid = url.searchParams.get('uid'); + * @param {String} [query=''] the query string to parse + * if not set will auto fill + * @param {String} [return_key=''] if set only returns this key entry + * or empty for none + * @return {Object|String} parameter entry list + */ +function parseQueryString(query = '', return_key = '') +{ + if (!query) { + query = window.location.search.substring(1); + } + var vars = query.split('&'); + var query_string = {}; + for (var i = 0; i < vars.length; i++) { + var pair = vars[i].split('='); + var key = decodeURIComponent(pair[0]); + var value = decodeURIComponent(pair[1]); + // skip over run if there is nothing + if (!key || value === 'undefined') { + continue; + } + // If first entry with this name + if (typeof query_string[key] === 'undefined') { + query_string[key] = decodeURIComponent(value); + // If second entry with this name + } else if (typeof query_string[key] === 'string') { + var arr = [query_string[key], decodeURIComponent(value)]; + query_string[key] = arr; + // If third or later entry with this name + } else { + query_string[key].push(decodeURIComponent(value)); + } + } + if (return_key) { + if (keyInObject(return_key, query_string)) { + return query_string[return_key]; + } else { + return ''; + } + } else { + return query_string; + } +} + +/** + * searches query parameters for entry and returns data either as string or array + * if no search is given the whole parameters are returned as an object + * if a parameter is set several times it will be returned as an array + * if search parameter set and nothing found and empty string is returned + * if no parametes exist and no serach is set and empty object is returned + * @param {String} [search=''] if set searches for this entry, if empty + * all parameters are returned + * @param {String} [query=''] different query string to parse, if not + * set (default) the current window href is used + * @param {Boolean} [single=false] if set to true then only the first found + * will be returned + * @return {Object|Array|String} if search is empty, object, if search is set + * and only one entry, then string, else array + * unless single is true + */ +function getQueryStringParam(search = '', query = '', single = false) +{ + if (!query) { + query = window.location.href; + } + const url = new URL(query); + let param = null; + if (search) { + let _params = url.searchParams.getAll(search); + if (_params.length == 1 || single === true) { + param = _params[0]; + } else if (_params.length > 1) { + param = _params; + } + } else { + // will be object, so declare it one + param = {}; + // loop over paramenters + for (const [key] of url.searchParams.entries()) { + // check if not yet set + if (typeof param[key] === 'undefined') { + // get the parameters multiple + let _params = url.searchParams.getAll(key); + // if 1 set as string, else attach array as is + param[key] = _params.length < 2 || single === true ? + _params[0] : + _params; + } + } + } + return param; +} + +// __EMD__ diff --git a/src/utils/l10nTranslation.mjs b/src/utils/l10nTranslation.mjs new file mode 100644 index 0000000..9d5d560 --- /dev/null +++ b/src/utils/l10nTranslation.mjs @@ -0,0 +1,34 @@ +/* +Description: Translation call +Date: 2025//3/6 +Creator: Clemens Schwaighofer +*/ + +export { l10nTranslation }; +import { isObject } from './JavaScriptHelpers.mjs'; + +class l10nTranslation { + + #i18n = {}; + + constructor(i18n) { + this.#i18n = i18n; + + } + /** + * uses the i18n object created in the translation template + * that is filled from gettext in PHP + * @param {String} string text to translate + * @return {String} translated text (based on PHP selected language) + */ + __(string) + { + if (typeof this.#i18n !== 'undefined' && isObject(this.#i18n) && this.#i18n[string]) { + return this.#i18n[string]; + } else { + return string; + } + } +} + +// __END__ diff --git a/src/utilsAll.mjs b/src/utilsAll.mjs new file mode 100644 index 0000000..8465545 --- /dev/null +++ b/src/utilsAll.mjs @@ -0,0 +1,1238 @@ +/* + * general edit javascript + * former name: edit.jq.js + * This is the jquery version + * NOTE: jquey parts will be deprecated +*/ + +import { + errorCatch as _errorCatch, + isFunction as _isFunction, + executeFunctionByName as _executeFunctionByName, + isObject as _isObject, + getObjectCount as _getObjectCount, + keyInObject as _keyInObject, + getKeyByValue as _getKeyByValue, + valueInObject as _valueInObject, + deepCopyFunction as _deepCopyFunction +} from './utils/JavaScriptHelpers.mjs'; +import { + escapeHtml as _escapeHtml, + unescapeHtml as _unescapeHtml, + html_options as _html_options, + html_options_block as _html_options_block, + html_options_refill as _html_options_refill +} from './utils/HtmlHelpers.mjs'; +import { + loadEl as _loadEl, + pop as _pop, + expandTA as _expandTA, + exists as _exists +} from './utils/DomHelpers.mjs'; +import { + dec2hex as _dec2hex, + getRandomIntInclusive as _getRandomIntInclusive, + roundPrecision as _roundPrecision +} from './utils/MathHelpers.mjs'; +import { + formatString as _formatString, + numberWithCommas as _numberWithCommas, + convertLBtoBR as _convertLBtoBR +} from './utils/StringHelpers.mjs'; +import { + getTimestamp as _getTimestamp +} from './utils/DateTimeHelpers.mjs'; +import { + generateId as _generateId, + randomIdF as _randomIdF, +} from './utils/UniqIdGenerators.mjs'; +import { + getWindowSize as _getWindowSize, + getScrollOffset as _getScrollOffset, + getScrollOffsetOpener as _getScrollOffsetOpener, + setCenter as _setCenter, + goToPos as _goToPos, + goTo as _goTo +} from './utils/ResizingAndMove.mjs'; +import { + formatBytes as _formatBytes, + formatBytesLong as _formatBytesLong, + stringByteFormat as _stringByteFormat +} from './utils/FormatBytes.mjs'; +import { + parseQueryString as _parseQueryString, + getQueryStringParam as _getQueryStringParam +} from './utils/UrlParser.mjs'; +import { + loginLogout as _loginLogout, + createLoginRow as _createLoginRow, + createNavMenu as _createNavMenu +} from './utils/LoginMenu.mjs'; +import { + ActionIndicatorOverlayBox, + actionIndicator as _actionIndicator, + actionIndicatorShow as _actionIndicatorShow, + actionIndicatorHide as _actionIndicatorHide, + overlayBoxShow as _overlayBoxShow, + overlayBoxHide as _overlayBoxHide, + setOverlayBox as _setOverlayBox, + hideOverlayBox as _hideOverlayBox, + ClearCall as _ClearCall +} from './utils/ActionIndicatorOverlayBox.mjs'; +import { l10nTranslation } from './utils/l10nTranslation.mjs'; +import { HtmlElementCreator } from './utils/HtmlElementCreator.mjs'; +import { ActionBox } from './utils/ActionBox.mjs'; + +let aiob = new ActionIndicatorOverlayBox(); +let hec = new HtmlElementCreator(); +// @ts-ignore +// eslint-disable-next-line no-undef +let l10n = new l10nTranslation(i18n ?? {}); +let ab = new ActionBox(hec, l10n); + +/* export { + escapeHtml, + roundPrecision, + formatString, + unescapeHtml, + loadEl, + pop, + expandTA, + getWindowSize, + getScrollOffset, + getScrollOffsetOpener, + setCenter, + goToPos, + goTo, + __, + numberWithCommas, + convertLBtoBR, + getTimestamp, + dec2hex, + generateId, + randomIdF, + getRandomIntInclusive, + isFunction, + executeFunctionByName, + isObject, + getObjectCount, + keyInObject, + getKeyByValue, + valueInObject, + deepCopyFunction, + exists, + formatBytes, + formatBytesLong, + stringByteFormat, + errorCatch, + actionIndicator, + actionIndicatorShow, + actionIndicatorHide, + overlayBoxShow, + overlayBoxHide, + setOverlayBox, + hideOverlayBox, + ClearCall, + showActionIndicator, + hideActionIndicator, + checkOverlayExists, + showOverlayBoxLayers, + hideOverlayBoxLayers, + clearCallActionBox, + cel, + ael, + aelx, + aelxar, + rel, + rcssel, + acssel, + scssel, + phfo, + phfa, + html_options, + html_options_block, + html_options_refill, + parseQueryString, + getQueryStringParam, + loginLogout, + createLoginRow, + createNavMenu, + showFillActionBox, + fillActionBox, + adjustActionBox, + hideAllActionBoxes, + hideActionBox, + showActionBox, + closeActionBox, + showActionBoxFloat, + closeActionBoxFloat, + createActionBox, + adjustActionBoxHeight, +}; */ + +// MARK: deprecated String/Number override + +/** + * simple sprintf formater for replace + * usage: "{0} is cool, {1} is not".format("Alpha", "Beta"); + * First, checks if it isn't implemented yet. + * @param {String} String.prototype.format string with elements to be replaced + * @return {String} Formated string + * @deprecated StringHelpers.formatString + */ +// @ts-ignore +if (!String.prototype.format) { + // @ts-ignore + String.prototype.format = function() + { + console.error('[DEPRECATED] use StringHelpers.formatString'); + // @ts-ignore + return _formatString(this, arguments); + }; +} + +/** + * round to digits (float) + * @param {Number} Number.prototype.round Float type number to round + * @param {Number} prec Precision to round to + * @return {Float} Rounded number + * @deprecated use MathHelpers.roundPrecision + */ +// @ts-ignore +if (Number.prototype.round) { + // @ts-ignore + Number.prototype.round = function (prec) { + console.error('[DEPRECATED] use MathHelpers.roundPrecision'); + // @ts-ignore + return _roundPrecision(this, prec); + }; +} + +/** + * escape HTML string + * @param {String} String.prototype.escapeHTML HTML data string to be escaped + * @return {String} escaped string + * @deprecated use HtmlHelpers.escapeHtml + */ +// @ts-ignore +if (!String.prototype.escapeHTML) { + // @ts-ignore + String.prototype.escapeHTML = function() { + console.error('[DEPRECATED] use HtmlHelpers.escapeHtml'); + // @ts-ignore + return _escapeHtml(this); + }; +} + +/** + * unescape a HTML encoded string + * @param {String} String.prototype.unescapeHTML data with escaped entries + * @return {String} HTML formated string + * @deprecated use HtmlHelpers.unescapeHtml + */ +// @ts-ignore +if (!String.prototype.unescapeHTML) { + // @ts-ignore + String.prototype.unescapeHTML = function() { + console.error('[DEPRECATED] use HtmlHelpers.unescapeHtml'); + // @ts-ignore + return _unescapeHtml(this); + }; +} + +// MARK: general collection + +/** + * + * @param {String} string + * @returns {String} + */ +// @ts-ignore +function escapeHtml(string) // eslint-disable-line no-unused-vars +{ + return _escapeHtml(string); +} + +/** + * round to digits (float) + * @param {Number} number Float type number to round + * @param {Number} prec Precision to round to + * @return {Number} Rounded number + */ +// @ts-ignore +function roundPrecision(number, prec) // eslint-disable-line no-unused-vars +{ + return _roundPrecision(number, prec); +} + +/** + * simple sprintf formater for replace + * usage: "{0} is cool, {1} is not".format("Alpha", "Beta"); + * First, checks if it isn't implemented yet. + * @param {String} string String with elements to be replaced + * @return {String} Formated string + * @deprecated StringHelpe + */ +// @ts-ignore +function formatString(string, ...args) // eslint-disable-line no-unused-vars +{ + return _formatString(string, args); +} + +/** + * + * @param {String} string + * @returns {String} + */ +// @ts-ignore +function unescapeHtml(string) // eslint-disable-line no-unused-vars +{ + return _unescapeHtml(string); +} + +/** + * Gets html element or throws an error + * @param {string} el_id Element ID to get + * @returns {HTMLElement} + * @throws Error + */ +// @ts-ignore +function loadEl(el_id) // eslint-disable-line no-unused-vars +{ + return _loadEl(el_id); +} + +/** + * opens a pop_ window with winName and given features (string) + * @param {String} theURL the url + * @param {String} winName window name + * @param {Object} features pop_ features + */ +// @ts-ignore +function pop(theURL, winName, features) // eslint-disable-line no-unused-vars +{ + _pop(theURL, winName, features); +} + +/** + * automatically resize a text area based on the amount of lines in it + * @param {string} ta_id element id + */ +// @ts-ignore +function expandTA(ta_id) // eslint-disable-line no-unused-vars +{ + _expandTA(ta_id); +} + +/** + * wrapper to get the real window size for the current browser window + * @return {Object} object with width/height + */ +// @ts-ignore +function getWindowSize() // eslint-disable-line no-unused-vars +{ + return _getWindowSize(); +} + +/** + * wrapper to get the correct scroll offset + * @return {Object} object with x/y px + */ +// @ts-ignore +function getScrollOffset() // eslint-disable-line no-unused-vars +{ + return _getScrollOffset(); +} + +/** + * wrapper to get the correct scroll offset for opener page (from pop_) + * @return {Object} object with x/y px + */ +// @ts-ignore +function getScrollOffsetOpener() // eslint-disable-line no-unused-vars +{ + return _getScrollOffsetOpener(); +} + +/** + * centers div to current window size middle + * @param {String} id element to center + * @param {Boolean} left if true centers to the middle from the left + * @param {Boolean} top if true centers to the middle from the top + */ +// @ts-ignore +function setCenter(id, left, top) // eslint-disable-line no-unused-vars +{ + _setCenter(id, left, top); +} + +/** + * goes to an element id position + * @param {String} element element id to move to + * @param {Number} [offset=0] offset from top, default is 0 (px) + * @param {Number} [duration=500] animation time, default 500ms + * @param {String} [base='body,html'] base element for offset scroll + */ +// @ts-ignore +function goToPos(element, offset = 0, duration = 500, base = 'body,html') // eslint-disable-line no-unused-vars +{ + _goToPos(element, offset, duration, base); +} + +/** + * go to element, scroll + * non jquery + * @param {string} target +*/ +// @ts-ignore +function goTo(target) // eslint-disable-line no-unused-vars +{ + _goTo(target); +} + +/** + * uses the i18n object created in the translation template + * that is filled from gettext in PHP + * @param {String} string text to translate + * @return {String} translated text (based on PHP selected language) + */ +// @ts-ignore +function __(string) // eslint-disable-line no-unused-vars +{ + return l10n.__(string); +} + +/** + * formats flat number 123456 to 123,456 + * @param {Number} x number to be formated + * @return {String} formatted with , in thousands + */ +// @ts-ignore +function numberWithCommas(x) // eslint-disable-line no-unused-vars +{ + return _numberWithCommas(x); +} + +/** + * converts line breaks to br + * @param {String} string any string + * @return {String} string with
+ */ +// @ts-ignore +function convertLBtoBR(string) // eslint-disable-line no-unused-vars +{ + return _convertLBtoBR(string); +} + +/** + * returns current timestamp (unix timestamp) + * @return {Number} timestamp (in milliseconds) + */ +// @ts-ignore +function getTimestamp() // eslint-disable-line no-unused-vars +{ + return _getTimestamp(); +} + +/** + * dec2hex :: Integer -> String + * i.e. 0-255 -> '00'-'ff' + * @param {Number} dec decimal string + * @return {String} hex encdoded number + */ +// @ts-ignore +function dec2hex(dec) // eslint-disable-line no-unused-vars +{ + return _dec2hex(dec); +} + +/** + * generateId :: Integer -> String + * only works on mondern browsers + * @param {Number} len length of unique id string + * @return {String} random string in length of len + */ +// @ts-ignore +function generateId(len) // eslint-disable-line no-unused-vars +{ + return _generateId(len); +} + +/** + * creates a pseudo random string of 10 characters + * works on all browsers + * after many runs it will create d_licates + * @return {String} not true random string + */ +// @ts-ignore +function randomIdF() // eslint-disable-line no-unused-vars +{ + return _randomIdF(); +} + +/** + * generate a number between min/max + * with min/max inclusive. + * eg: 1,5 will create a number ranging from 1 o 5 + * @param {Number} min minimum int number inclusive + * @param {Number} max maximumg int number inclusive + * @return {Number} Random number + */ +// @ts-ignore +function getRandomIntInclusive(min, max) // eslint-disable-line no-unused-vars +{ + return _getRandomIntInclusive(min, max); +} + +/** + * check if name is a function + * @param {string} name Name of function to check if exists + * @return {Boolean} true/false + */ +// @ts-ignore +function isFunction(name) // eslint-disable-line no-unused-vars +{ + return _isFunction(name); +} + +/** + * call a function by its string name + * https://stackoverflow.com/a/359910 + * example: executeFunctionByName("My.Namespace.functionName", window, arguments); + * @param {string} functionName The function name or namespace + function + * @param {any} context context (window or first namespace) + * hidden next are all the arguments + * @return {any} Return values from functon + */ +// @ts-ignore +function executeFunctionByName(functionName, context) // eslint-disable-line no-unused-vars +{ + return _executeFunctionByName(functionName, context); +} + +/** + * checks if a variable is an object + * @param {any} val possible object + * @return {Boolean} true/false if it is an object or not + */ +// @ts-ignore +function isObject(val) // eslint-disable-line no-unused-vars +{ + return _isObject(val); +} + +/** + * get the length of an object (entries) + * @param {Object} object object to check + * @return {Number} number of entry + */ +// @ts-ignore +function getObjectCount(object) // eslint-disable-line no-unused-vars +{ + return _getObjectCount(object); +} + +/** + * checks if a key exists in a given object + * @param {String} key key name + * @param {Object} object object to search key in + * @return {Boolean} true/false if key exists in object + */ +// @ts-ignore +function keyInObject(key, object) // eslint-disable-line no-unused-vars +{ + return _keyInObject(key, object); +} + +/** + * returns matching key of value + * @param {Object} object object to search value in + * @param {any} value any value (String, Number, etc) + * @return {String} the key found for the first matching value + */ +// @ts-ignore +function getKeyByValue(object, value) // eslint-disable-line no-unused-vars +{ + return _getKeyByValue(object, value); +} + +/** + * returns true if value is found in object with a key + * @param {Object} object object to search value in + * @param {any} value any value (String, Number, etc) + * @return {Boolean} true on value found, false on not found + */ +// @ts-ignore +function valueInObject(object, value) // eslint-disable-line no-unused-vars +{ + return _valueInObject(object, value); +} + +/** + * true deep copy for Javascript objects + * if Object.assign({}, obj) is not working (shallow) + * or if JSON.parse(JSON.stringify(obj)) is failing + * @param {Object} inObject Object to copy + * @return {Object} Copied Object + */ +// @ts-ignore +function deepCopyFunction(inObject) // eslint-disable-line no-unused-vars +{ + return _deepCopyFunction(inObject); +} + +/** + * checks if a DOM element actually exists + * @param {String} id Element id to check for + * @return {Boolean} true if element exists, false on failure + */ +// @ts-ignore +function exists(id) // eslint-disable-line no-unused-vars +{ + return _exists(id); +} + +/** + * converts a int number into bytes with prefix in two decimals precision + * currently precision is fixed, if dynamic needs check for max/min precision + * @param {Number} bytes bytes in int + * @return {String} string in GB/MB/KB + */ +// @ts-ignore +function formatBytes(bytes) // eslint-disable-line no-unused-vars +{ + return _formatBytes(bytes); +} + +/** + * like formatBytes, but returns bytes for <1KB and not 0.n KB + * @param {Number} bytes bytes in int + * @return {String} string in GB/MB/KB + */ +// @ts-ignore +function formatBytesLong(bytes) // eslint-disable-line no-unused-vars +{ + return _formatBytesLong(bytes); +} + +/** + * Convert a string with B/K/M/etc into a byte number + * @param {String|Number} bytes Any string with B/K/M/etc + * @return {String|Number} A byte number, or original string as is + */ +// @ts-ignore +function stringByteFormat(bytes) // eslint-disable-line no-unused-vars +{ + return _stringByteFormat(bytes); +} + +/** + * prints out error messages based on data available from the browser + * @param {Object} err error from try/catch block + */ +// @ts-ignore +function errorCatch(err) // eslint-disable-line no-unused-vars +{ + _errorCatch(err); +} + +// MARK: ActionIndicatorOverlayBoxLegacy + +/************************************************************* + * OLD action indicator and overlay boxes calls + * DO NOT USE + * actionIndicator -> showActionIndicator + * actionIndicator -> hideActionIndicator + * actionIndicatorShow -> showActionIndicator + * actionIndicatorHide -> hideActionIndicator + * overlayBoxShow -> showOverlayBoxLayers + * overlayBoxHide -> hideOverlayBoxLayers + * setOverlayBox -> showOverlayBoxLayers + * hideOverlayBox -> hideOverlayBoxLayers + * ClearCall -> ClearCallActionBox + * ***********************************************************/ + +/** + * show or hide the "do" overlay + * @param {String} loc location name for action indicator + * default empty. for console.log + * @param {Boolean} [overlay=true] override the auto hide/show over the overlay div block + */ +// @ts-ignore +function actionIndicator(loc, overlay = true) // eslint-disable-line no-unused-vars +{ + _actionIndicator(loc, overlay); +} + +/** + * explicit show for action Indicator + * instead of automatically show or hide, do on command show + * @param {String} loc location name for action indicator + * default empty. for console.log + * @param {Boolean} [overlay=true] override the auto hide/show over the overlay div block + */ +// @ts-ignore +function actionIndicatorShow(loc, overlay = true) // eslint-disable-line no-unused-vars +{ + _actionIndicatorShow(loc, overlay); +} + +/** + * explicit hide for action Indicator + * instead of automatically show or hide, do on command hide + * @param {String} loc location name for action indicator + * default empty. for console.log + * @param {Boolean} [overlay=true] override the auto hide/show over the overlay div block + */ +// @ts-ignore +function actionIndicatorHide(loc, overlay = true) // eslint-disable-line no-unused-vars +{ + _actionIndicatorHide(loc, overlay); +} + +/** + * shows the overlay box or if already visible, bumps the zIndex to 100 + */ +// @ts-ignore +function overlayBoxShow() // eslint-disable-line no-unused-vars +{ + _overlayBoxShow(); +} + +/** + * hides the overlay box or if zIndex is 100 bumps it down to previous level + */ +// @ts-ignore +function overlayBoxHide() // eslint-disable-line no-unused-vars +{ + _overlayBoxHide(); +} + +/** + * position the overlay block box and shows it + */ +// @ts-ignore +function setOverlayBox() // eslint-disable-line no-unused-vars +{ + _setOverlayBox(); +} + +/** + * opposite of set, always hides overlay box + */ +// @ts-ignore +function hideOverlayBox() // eslint-disable-line no-unused-vars +{ + _hideOverlayBox(); +} + +/** + * the abort call, clears the action box and hides it and the overlay box + */ +// @ts-ignore +function ClearCall() // eslint-disable-line no-unused-vars +{ + _ClearCall(); +} + +// MARK: ActionIndicatorOverlayBox + +/************************************************************* + * NEW action indicator and overlay box calls + * USE THIS + * ***********************************************************/ + +/** + * show action indicator + * - checks if not existing and add + * - only shows if not visible (else ignore) + * - overlaybox check is called and shown on a fixzed + * zIndex of 1000 + * - indicator is page centered + * @param {String} loc ID string, only used for console log + */ +// @ts-ignore +function showActionIndicator(loc) // eslint-disable-line no-unused-vars +{ + aiob.showActionIndicator(loc); +} + +/** + * hide action indicator, if it is visiable + * If the global variable GL_OB_S is > GL_OB_BASE then + * the overlayBox is not hidden but the zIndex + * is set to this value + * @param {String} loc ID string, only used for console log + */ +// @ts-ignore +function hideActionIndicator(loc) // eslint-disable-line no-unused-vars +{ + aiob.hideActionIndicator(loc); +} + +/** + * checks if overlayBox exists, if not it is + * added as hidden item at the body end + */ +// @ts-ignore +function checkOverlayExists() // eslint-disable-line no-unused-vars +{ + aiob.checkOverlayExists(); +} + +/** + * show overlay box + * if not visible show and set zIndex to 10 (GL_OB_BASE) + * if visible, add +1 to the GL_OB_S variable and + * _ zIndex by this value + */ +// @ts-ignore +function showOverlayBoxLayers(el_id) // eslint-disable-line no-unused-vars +{ + aiob.showOverlayBoxLayers(el_id); +} + +/** + * hide overlay box + * lower GL_OB_S value by -1 + * if we are 10 (GL_OB_BASE) or below hide the overlayIndex + * and set zIndex and GL_OB_S to 0 + * else just set zIndex to the new GL_OB_S value + * @param {String} el_id Target to hide layer + */ +// @ts-ignore +function hideOverlayBoxLayers(el_id='') // eslint-disable-line no-unused-vars +{ + aiob.hideOverlayBoxLayers(el_id); +} + +/** + * only for single action box + */ +// @ts-ignore +function clearCallActionBox() // eslint-disable-line no-unused-vars +{ + aiob.clearCallActionBox(); +} + +// MARK: DOM MANAGEMENT FUNCTIONS +/** + * reates object for DOM element creation flow + * @param {String} tag must set tag (div, span, etc) + * @param {String} [id=''] optional set for id, if input, select will be used for name + * @param {String} [content=''] text content inside, is skipped if sub elements exist + * @param {Array} [css=[]] array for css tags + * @param {Object} [options={}] anything else (value, placeholder, OnClick, style) + * @return {Object} created element as an object + */ +// @ts-ignore +function cel(tag, id = '', content = '', css = [], options = {}) // eslint-disable-line no-unused-vars +{ + return hec.cel(tag, id, content, css, options); +} + +/** + * attach a cel created object to another to create a basic DOM tree + * @param {Object} base object where to attach/search + * @param {Object} attach the object to be attached + * @param {String} [id=''] optional id, if given search in base for this id and attach there + * @return {Object} "none", technically there is no return needed as it is global attach + */ +// @ts-ignore +function ael(base, attach, id = '') // eslint-disable-line no-unused-vars +{ + return hec.ael(base, attach, id); +} + +/** + * directly attach n elements to one master base element + * this type does not s_port attach with optional id + * @param {Object} base object to where we attach the elements + * @param {...Object} attach attach 1..n: attach directly to the base element those attachments + * @return {Object} "none", technically there is no return needed, global attach + */ +// @ts-ignore +function aelx(base, ...attach) // eslint-disable-line no-unused-vars +{ + return hec.aelx(base, attach); +} + +/** + * same as aelx, but instead of using objects as parameters + * get an array of objects to attach + * @param {Object} base object to where we attach the elements + * @param {Array} attach array of objects to attach + * @return {Object} "none", technically there is no return needed, global attach + */ +// @ts-ignore +function aelxar(base, attach) // eslint-disable-line no-unused-vars +{ + return hec.aelxar(base, attach); +} + +/** + * resets the sub elements of the base element given + * @param {Object} base cel created element + * @return {Object} returns reset base element + */ +// @ts-ignore +function rel(base) // eslint-disable-line no-unused-vars +{ + return hec.rel(base); +} + +/** + * searches and removes style from css array + * @param {Object} _element element to work one + * @param {String} css style sheet to remove (name) + * @return {Object} returns full element + */ +// @ts-ignore +function rcssel(_element, css) // eslint-disable-line no-unused-vars +{ + return hec.rcssel(_element, css); +} + +/** + * adds a new style sheet to the element given + * @param {Object} _element element to work on + * @param {String} css style sheet to add (name) + * @return {Object} returns full element + */ +// @ts-ignore +function acssel(_element, css) // eslint-disable-line no-unused-vars +{ + return hec.acssel(_element, css); +} + +/** + * removes one css and adds another + * is a wrapper around rcssel/acssel + * @param {Object} _element element to work on + * @param {String} rcss style to remove (name) + * @param {String} acss style to add (name) + * @return {Object} returns full element + */ +// @ts-ignore +function scssel(_element, rcss, acss) // eslint-disable-line no-unused-vars +{ + hec.scssel(_element, rcss, acss); +} + +/** + * parses the object tree created with cel/ael and converts it into an HTML string + * that can be inserted into the page + * @param {Object} tree object tree with dom element declarations + * @return {String} HTML string that can be used as innerHTML + */ +// @ts-ignore +function phfo(tree) // eslint-disable-line no-unused-vars +{ + return hec.phfo(tree); +} + +/** + * Create HTML elements from array list + * as a flat element without master object file + * Is like tree.sub call + * @param {Array} list Array of cel created objects + * @return {String} HTML String + */ +// @ts-ignore +function phfa(list) // eslint-disable-line no-unused-vars +{ + return hec.phfa(list); +} +// *** DOM MANAGEMENT FUNCTIONS + +// MARK: HTML Helpers +// BLOCK: html wrappers for quickly creating html data blocks + +/** + * NOTE: OLD FORMAT which misses multiple block set + * creates an select/options drop down block. + * the array needs to be key -> value format. + * key is for the option id and value is for the data output + * @param {String} name name/id + * @param {Object} data array for the options + * @param {String} [selected=''] selected item uid + * @param {Boolean} [options_only=false] if this is true, it will not print the select part + * @param {Boolean} [return_string=false] return as string and not as element + * @param {String} [sort=''] if empty as is, else allowed 'keys', + * 'values' all others are ignored + * @return {String} html with build options block + */ +// @ts-ignore +function html_options(name, data, selected = '', options_only = false, return_string = false, sort = '') // eslint-disable-line no-unused-vars +{ + return _html_options(name, data, selected, options_only, return_string, sort); +} + +/** + * NOTE: USE THIS CALL, the above one is deprecated + * creates an select/options drop down block. + * the array needs to be key -> value format. + * key is for the option id and value is for the data output + * @param {String} name name/id + * @param {Object} data array for the options + * @param {String} [selected=''] selected item uid + * @param {Number} [multiple=0] if this is 1 or larger, the drop down + * will be turned into multiple select + * the number sets the size value unless it is 1, + * then it is default + * @param {Boolean} [options_only=false] if this is true, it will not print the select part + * @param {Boolean} [return_string=false] return as string and not as element + * @param {String} [sort=''] if empty as is, else allowed 'keys', + * 'values' all others are ignored + * @param {String} [onchange=''] onchange trigger call, default unset + * @return {String} html with build options block + */ +// @ts-ignore +function html_options_block( // eslint-disable-line no-unused-vars + name, data, selected = '', multiple = 0, options_only = false, return_string = false, sort = '', onchange = '' +) { + return _html_options_block( + name, data, selected, multiple, options_only, return_string, sort, onchange + ); +} + +/** + * refills a select box with options and keeps the selected + * @param {String} name name/id + * @param {Object} data array of options + * @param {String} [sort=''] if empty as is, else allowed 'keys', 'values' + * all others are ignored + */ +// @ts-ignore +function html_options_refill(name, data, sort = '') // eslint-disable-line no-unused-vars +{ + _html_options_refill(name, data, sort); +} + +// MARK: URL + +/** + * parses a query string from window.location.search.substring(1) + * ALTERNATIVE CODE + * var url = new URL(window.location.href); + * param_uid = url.searchParams.get('uid'); + * @param {String} [query=''] the query string to parse + * if not set will auto fill + * @param {String} [return_key=''] if set only returns this key entry + * or empty for none + * @return {Object|String} parameter entry list + */ +// @ts-ignore +function parseQueryString(query = '', return_key = '') // eslint-disable-line no-unused-vars +{ + return _parseQueryString(query, return_key); +} + +/** + * searches query parameters for entry and returns data either as string or array + * if no search is given the whole parameters are returned as an object + * if a parameter is set several times it will be returned as an array + * if search parameter set and nothing found and empty string is returned + * if no parametes exist and no serach is set and empty object is returned + * @param {String} [search=''] if set searches for this entry, if empty + * all parameters are returned + * @param {String} [query=''] different query string to parse, if not + * set (default) the current window href is used + * @param {Boolean} [single=false] if set to true then only the first found + * will be returned + * @return {Object|Array|String} if search is empty, object, if search is set + * and only one entry, then string, else array + * unless single is true + */ +// @ts-ignore +function getQueryStringParam(search = '', query = '', single = false) // eslint-disable-line no-unused-vars +{ + return _getQueryStringParam(search, query, single); +} + +// MARK: ACL LOGIN +// *** MASTER logout call +/** + * submits basic data for form logout + */ +// @ts-ignore +function loginLogout() // eslint-disable-line no-unused-vars +{ + _loginLogout(); +} + +/** + * create login string and logout button elements + * @param {String} login_string the login string to show on the left + * @param {String} [header_id='mainHeader'] the target for the main element block + * if not set mainHeader is assumed + * this is the target div for the "loginRow" + */ +// @ts-ignore +function createLoginRow(login_string, header_id = 'mainHeader') // eslint-disable-line no-unused-vars +{ + _createLoginRow(login_string, header_id); +} + +/** + * create the top nav menu that switches physical between pages + * (edit access data based) + * @param {Object} nav_menu the built nav menu with highlight info + * @param {String} [header_id='mainHeader'] the target for the main element block + * if not set mainHeader is assumed + * this is the target div for the "menuRow" + */ +// @ts-ignore +function createNavMenu(nav_menu, header_id = 'mainHeader') // eslint-disable-line no-unused-vars +{ + _createNavMenu(nav_menu, header_id); +} + +// MARK: ACTION BOX + +/** + * Show an action box + * @param {string} [target_id='actionBox'] where to attach content to, if not exists, create new + * @param {string} [content=''] content to add to the box + * @param {array} [action_box_css=[]] additional css elements for the action box + * @param {number} [override=0] override size adjust + * @param {number} [content_override=0] override content size adjust + */ +// @ts-ignore +function showFillActionBox(target_id = 'actionBox', content = '', action_box_css = [], override = 0, content_override = 0) // eslint-disable-line no-unused-vars +{ + ab.showFillActionBox(target_id, content, action_box_css, override, content_override); +} + +/** + * Fill action box with content, create it if it does not existgs + * @param {string} [target_id='actionBox'] where to attach content to, if not exists, create new + * @param {string} [content=''] content to add to the box + * @param {array} [action_box_css=[]] additional css elements for the action box + */ +// @ts-ignore +function fillActionBox(target_id = 'actionBox', content = '', action_box_css = []) // eslint-disable-line no-unused-vars +{ + // show action box, calc height + center + ab.fillActionBox(target_id, content, action_box_css); +} + +/** + * Adjust the size of the action box + * @param {string} [target_id='actionBox'] which actionBox to work on + * @param {number} [override=0] override size adjust + * @param {number} [content_override=0] override content size adjust + */ +// @ts-ignore +function adjustActionBox(target_id = 'actionBox', override = 0, content_override = 0) // eslint-disable-line no-unused-vars +{ + ab.adjustActionBox(target_id, override, content_override); +} + +/** + * hide any open action boxes and hide overlay + */ +// @ts-ignore +function hideAllActionBoxes() // eslint-disable-line no-unused-vars +{ + ab.hideAllActionBoxes(); +} + +/** + * hide action box, but do not clear content + * DEPRECATED + * @param {string} [target_id='actionBox'] + */ +// @ts-ignore +function hideActionBox(target_id = 'actionBox') // eslint-disable-line no-unused-vars +{ + ab.hideActionBox(target_id); +} + +/** + * Just show and adjust the box + * DEPRECAED + * @param {string} [target_id='actionBox'] which actionBox to work on + * @param {number} [override=0] override size adjust + * @param {number} [content_override=0] override content size adjust + * @param {Boolean} [hide_all=false] if set to true, hide all other action boxes + */ +// @ts-ignore +function showActionBox(target_id = 'actionBox', override = 0, content_override = 0, hide_all = true) // eslint-disable-line no-unused-vars +{ + ab.showActionBox(target_id, override, content_override, hide_all); +} + +/** + * close an action box with default clear content + * for just hide use hideActionBox + * DEPRECATED + * @param {String} [target_id='actionBox'] which action box to close, default is set + * @param {Boolean} [clean=true] if set to false will not remove html content, just hide + */ +// @ts-ignore +function closeActionBox(target_id = 'actionBox', clean = true) // eslint-disable-line no-unused-vars +{ + // set the target/content ids + ab.closeActionBox(target_id, clean); +} + +/** + * TODO: better stacked action box: OPEN + * @param {string} [target_id='actionBox'] which actionBox to work on + * @param {number} [override=0] override size adjust + * @param {number} [content_override=0] override content size adjust + * @param {Boolean} [hide_all=false] if set to true, hide all other action boxes + */ +// @ts-ignore +function showActionBoxFloat(target_id = 'actionBox', override = 0, content_override = 0, hide_all = false) // eslint-disable-line no-unused-vars +{ + ab.showActionBoxFloat(target_id, override, content_override, hide_all); +} + +/** + * TODO: better stacked action box: CLOSE + * @param {String} [target_id='actionBox'] which action box to close, default is set + * @param {Boolean} [clean=true] if set to false will not remove html content, just hide + */ +// @ts-ignore +function closeActionBoxFloat(target_id = 'actionBox', clean = true) // eslint-disable-line no-unused-vars +{ + ab.closeActionBoxFloat(target_id, clean); +} + +/** + * create a new action box and fill it with basic elements + * @param {String} [target_id='actionBox'] + * @param {String} [title=''] + * @param {Object} [contents={}] + * @param {Object} [headers={}] + * @param {Boolean} [show_close=true] + * @param {Object} [settings={}] Optional settings, eg style sheets + */ +// @ts-ignore +function createActionBox( // eslint-disable-line no-unused-vars + target_id = 'actionBox', + title = '', + contents = {}, + headers = {}, + settings = {}, + show_close = true +) { + ab.createActionBox(target_id, title, contents, headers, settings, show_close); +} + +/** + * adjusts the action box height based on content and window height of browser + * TODO: border on outside/and other margin things need to be added in overall adjustment + * @param {String} [target_id='actionBox'] target id, if not set, fall back to default + * @param {Number} [override=0] override value to add to the actionBox height + * @param {Number} [content_override=0] override the value from _content block if it exists + */ +// @ts-ignore +function adjustActionBoxHeight(target_id = 'actionBox', override = 0, content_override = 0) // eslint-disable-line no-unused-vars +{ + ab.adjustActionBoxHeight(target_id, override, content_override); +} + +/* END */