{ "version": 3, "sources": ["../src/js/gui/filter/wd-filter-matches.js", "../src/js/funcs/wdc.js", "../src/js/funcs/prototypes/date.js", "../src/js/funcs/prototypes/number.js", "../src/js/funcs/prototypes/storage.js", "../src/js/funcs/prototypes/string.js", "../src/js/core/wd-error.js", "../src/js/core/wd-format.js", "../src/js/core/wd-util.js", "../src/js/core/wd-proxy.js", "../src/js/core/fetch/wd-fetch-error.js", "../src/js/core/fetch/wd-fetch.js", "../src/js/core/fetch/wd-fetch-array.js", "../src/js/core/fetch/wd-fetch-object.js", "../src/js/core/fetch/wd-fetch-upload.js", "../src/js/core/fetch/wp-fetch-object.js", "../src/js/core/fetch/wp-fetch-upload.js", "../src/js/core/fetch/wp-fetch.js", "../src/js/gui/wd-element.js", "../src/js/gui/dropdown/wd-dropdown.js", "../src/js/gui/button/wd-button.js", "../src/js/gui/button/wd-toggle-button.js", "../src/js/gui/dropdown/wd-checklist.js", "../src/js/gui/dropdown/wd-dropdown-form.js", "../src/js/gui/dropdown/wd-dropdown-multi-form.js", "../src/js/gui/dropdown/wd-dropdown-object-form.js", "../src/js/gui/dropdown/wd-dropdown-object-multi-form.js", "../src/js/gui/navbar/wd-navbar-menu.js", "../src/js/gui/navbar/wd-navbar-menu-pager.js", "../src/js/gui/form/wd-form-basic.js", "../src/js/gui/form/wd-form-select.js", "../src/js/gui/form/wd-form-input.js", "../src/js/gui/form/wd-form-hidden.js", "../src/js/gui/filter/wd-navbar-filter.js", "../src/js/gui/navbar/wd-navbar.js", "../src/js/gui/pane/wd-pane.js", "../src/js/gui/pane/wd-panes.js", "../src/js/gui/pane/wd-pane-stack.js", "../src/js/gui/form/wd-form.js", "../src/js/gui/form/wd-form-proxy.js", "../src/js/gui/pane/wd-pane-form.js", "../src/js/gui/lists/wd-list-item.js", "../src/js/gui/lists/wd-list.js", "../src/js/gui/lists/wd-list-proxy.js", "../src/js/gui/lists/wd-list-sort.js", "../src/js/gui/pane/wd-pane-list.js", "../src/js/gui/modal/wd-modal.js", "../src/js/gui/modal/wd-activity.js", "../src/js/gui/lists/wd-list-form-item.js", "../src/js/gui/lists/wd-list-form.js", "../src/js/gui/pane/wd-pane-list-form.js", "../src/js/gui/pane/wd-pane-list-pager.js", "../src/js/gui/pane/wd-pane-list-cursor.js", "../src/js/gui/modal/wd-modal-panes.js", "../src/js/gui/modal/wd-modal-form.js", "../src/js/gui/form/wd-form-dropdown.js", "../src/js/gui/form/wd-form-boolean.js", "../src/js/gui/form/wd-form-button.js", "../src/js/gui/form/wd-form-button-multi.js", "../src/js/gui/form/wd-form-checkbox.js", "../src/js/gui/form/wd-form-checklist.js", "../src/js/gui/form/wd-form-color.js", "../src/js/gui/form/wd-form-datalist.js", "../src/js/gui/form/wd-form-dropdown-multi.js", "../src/js/gui/form/wd-form-dropdown-object-multi.js", "../src/js/gui/form/wd-form-dropdown-object.js", "../src/js/gui/form/wd-form-html.js", "../src/js/gui/form/wd-form-textlist.js", "../src/js/gui/form/wd-form-text.js", "../src/js/gui/form/wd-form-textpicker.js", "../src/js/gui/form/wd-form-password.js", "../src/js/gui/form/wd-form-number.js", "../src/js/gui/form/wd-form-currency.js", "../src/js/gui/form/wd-form-email.js", "../src/js/gui/form/wd-form-range.js", "../src/js/gui/form/wd-form-date.js", "../src/js/gui/form/wd-form-datetimelocal.js", "../src/js/gui/form/wd-form-textarea.js", "../src/js/gui/form/wd-form-daterange.js", "../src/js/gui/filter/wd-filter.js", "../src/js/gui/form/wd-form-filter.js", "../src/js/gui/form/wd-form-daterange-dropdown.js", "../src/js/gui/form/wd-form-numberrange-dropdown.js", "../src/js/gui/pill/wd-pillbox.js", "../src/js/gui/card/wd-card.js", "../src/js/gui/card/wd-accordion-card.js", "../src/js/gui/card/wd-accordion.js", "../src/js/gui/filter/wd-pillbox-filter.js", "../src/js/gui/filter/index.js", "../src/js/gui/pill/wd-pillbox-proxy.js", "../src/js/gui/card/wd-cards.js", "../src/js/gui/card/wd-card-form.js", "../src/js/gui/card/wd-pane-cards.js", "../src/js/gui/card/wd-pane-accordion.js", "../src/js/gui/card/wd-card-tabs.js", "../src/js/gui/card/wd-card-list.js", "../src/js/gui/card/wd-card-list-form.js", "../src/js/gui/card/wd-card-list-sort.js", "../src/js/gui/card/wd-card-list-pager.js", "../src/js/gui/util/wd-uploader.js", "../src/js/index.js"], "sourcesContent": ["\nglobalThis.WDFilterMatches = {\n\n text:\t[ \n { label:'contains', value:'contains', short_label: '**' },\n { label:'does not contain', value:'not_contains', short_label: '!**' },\n { label:'equals', value:'equals', short_label: '=' },\n { label:'does not equal', value:'not_equals', short_label: '!=' },\n { label:'is not set', value:'is_null', short_label: '= null' },\n ],\n\n textlistDisabled:\t[ \n { label:'contains any', value:'contains_any', short_label: '** ANY' },\n { label:'contains all', value:'contains_all', short_label: '** ALL' },\n { label:'does not contain all', value:'not_contains_all', short_label: '!** ALL' },\n { label:'equals any', value:'equals_any', short_label: '= ANY' },\n { label:'does not equal all', value:'not_equals_all', short_label: '!= ALL' },\n ],\n \n number: [ \n { label:'equals', value:'equals', short_label: '=' },\n { label:'does not equal', value:'not_equals', short_label: '!=' },\n { label:'greater than', value:'greater_than', short_label: '>' },\n { label:'greater than or equals', value:'greater_than_equals', short_label: '>=' },\n { label:'less than', value:'less_than', short_label: '<' },\n { label:'less than or equals', value:'less_than_equals', short_label: '<=' },\n { label:'is not set', value:'is_null', short_label: '= null' },\n ],\n\n date: [ \n { label:'equals', value:'equals', short_label: '=' },\n { label:'does not equal', value:'not_equals', short_label: '!=' },\n { label:'greater than', value:'greater_than', short_label: '>' },\n { label:'greater than or equals', value:'greater_than_equals', short_label: '>=' },\n { label:'less than', value:'less_than', short_label: '<' },\n { label:'less than or equals', value:'less_than_equals', short_label: '<=' },\n { label:'is not set', value:'is_null', short_label: '= null' },\n ],\n\n dropdown: [ \n { label:'equals', value:'equals', short_label: '=' },\n { label:'does not equal', value:'not_equals', short_label: '!=' },\n ],\n\n};\n\n//some fallbacks I don't think we actualy need these anymore\nWDFilterMatches.basic = WDFilterMatches.text;\nWDFilterMatches.string = WDFilterMatches.text;\nWDFilterMatches.currency = WDFilterMatches.number;\nWDFilterMatches['datetime-local'] = WDFilterMatches.date;\nWDFilterMatches.boolean = WDFilterMatches.dropdown;\nWDFilterMatches.select = WDFilterMatches.dropdown;\n", "\nwindow.wdc = function(elemstr) \n{\n let ret = null;\n\n if (elemstr.substr(0,1) == '<')\n {\n const tmp = document.createElement('div');\n tmp.innerHTML = elemstr;\n ret = tmp.firstChild;\n }\n else\n {\n ret = document.querySelector(elemstr);\n }\n \n return ret;\n};\n", "\nDate.prototype.addHours = function(h){\n this.setHours(this.getHours() + parseInt(h) );\n return this;\n}\n\nDate.prototype.addMinutes = function(h){\n this.setMinutes(this.getMinutes() + parseInt(h) );\n return this;\n}\n\nDate.prototype.addSeconds = function(h){\n this.setSeconds(this.getSeconds() + parseInt(h) );\n return this;\n}\n\nDate.prototype.addMonths = function(h){\n this.setMonth(this.getMonth() + parseInt(h) );\n return this;\n}\n\nDate.prototype.addDays = function(h){\n this.setDate(this.getDate() + parseInt(h) );\n return this;\n}\n\nDate.prototype.addWeeks = function(w){\n\t\treturn this.addDays(w*7);\n}\n\nDate.prototype.addYears = function(h){\n this.setFullYear(this.getFullYear() + parseInt(h) );\n return this;\n}\n\nDate.prototype.firstOfWeek = function(h){\n this.addDays(0 - this.getDay());\n return this;\n}\n\nDate.prototype.endOfWeek = function(h){\n this.addDays(6 - this.getDay());\n return this;\n}\n\nDate.prototype.firstOfMonth = function(h){\n this.setDate(1);\n return this;\n}\n\nDate.prototype.endOfMonth = function(h){\n this.setDate(-1);\n return this;\n}\n\nDate.prototype.firstOfYear = function(h){\n\t\tthis.setMonth(0);\n this.setDate(1);\n return this;\n}\n\nDate.prototype.endOfYear = function(h){\n\t\tthis.setMonth(11);\n this.setDate(31);\n return this;\n}\n\nDate.prototype.midnight = function(str)\n{\n\tthis.setHours(0);\n\tthis.setMinutes(0);\n\tthis.setSeconds(0);\n\tthis.setMilliseconds(0);\n\treturn this;\n};\n\nDate.prototype.endOfDay = function(str)\n{\n\tthis.setHours(23);\n\tthis.setMinutes(59);\n\tthis.setSeconds(59);\n\tthis.setMilliseconds(999);\n\treturn this;\n};\n\nDate.prototype.isToday = function()\n{\n\tvar d = new Date();\n\n\tif (this >= d.midnight() && this <= d.endOfDay()) return true;\n\telse return false;\n}\n\nDate.prototype.isThisWeek = function()\n{\n\tvar d = new Date();\n\n\tif (this >= d.firstOfWeek() && this <= d.endOfWeek()) return true;\n\telse return false;\n}\n\nDate.prototype.toHalfHour = function()\n{\n\tif (this.getMinutes() >= 30) this.setMinutes(30);\n\telse this.setMinutes(0);\n\n\tthis.setSeconds(0);\n\tthis.setMilliseconds(0);\n\n\treturn this;\n};\n\nDate.prototype.nextHour = function()\n{\n\tvar min = this.getMinutes();\n\tif (min > 0) this.addMinutes(60-min);\n\n\tthis.setMinutes(0);\n\tthis.setSeconds(0);\n\tthis.setMilliseconds(0);\n\n\treturn this;\n};\n\nDate.prototype.nextHalfHour = function()\n{\n\tvar min = this.getMinutes();\n\tif (min > 0 && min != 30)\n\t{\n\t\tif (min < 30) this.addMinutes(30-min);\n\t\telse this.addMinutes(60-min);\n\n\t\tthis.setMinutes(0);\n\t\tthis.setSeconds(0);\n\t\tthis.setMilliseconds(0);\n\n\t}\n\treturn this;\n};\n\nDate.prototype.localDate = function()\n{\n\treturn this.addMinutes(this.getTimezoneOffset());\n};\n\nDate.prototype.parts = function()\n{\t\n\t//https://www.php.net/manual/en/datetime.format.php\n\tlet hours = this.getHours();\n\n\tlet hoursTwelve = (hours > 12) ? hours - 12 : hours;\n\tif (hoursTwelve == '0') hoursTwelve = 12;\n\n\tconst weekShort = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];\n\tconst weekLong = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];\n\n\tconst monthShort = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];\n\tconst monthLong = ['January','February','March','April','May','June','July','August','September','October','November','December'];\n\n\tconst parts = {\t//year\n\t\t'Y': String(this.getFullYear()).padStart(2,'0'),\n\n\t\t//month\n\t\t'm': String(this.getMonth() + 1).padStart(2,'0'),\n\t\t'j': String(this.getMonth() + 1),\n\t\t'M': monthShort[this.getMonth()],\n\t\t'F': monthLong[this.getMonth()],\n\n\t\t//days and days of the week\n\t\t'd': String(this.getDate()).padStart(2,'0'),\n\t\t'N': this.getDay(),\n\t\t'l': weekLong[this.getDay()],\n\t\t'D': weekShort[this.getDay()],\n\n\t\t//12/24 hour time with leading zeros\n\t\t'H': String(this.getHours()).padStart(2,'0'),\n\t\t'h': String(hoursTwelve).padStart(2,'0'),\n\n\t\t//12/24 hour time without leading zeros\n\t\t'G': String(this.getHours()),\n\t\t'g': String(hoursTwelve),\n\n\t\t'i': String(this.getMinutes()).padStart(2,'0'),\n\t\t's': String(this.getSeconds()).padStart(2,'0'),\n\n\t\t'Z': 0 - (this.getTimezoneOffset()/60),\n\n\t\t//am/pm\n\t\t'A': ( (hours >= 12) ? 'PM' : 'AM' ),\n\t\t'a': ( (hours >= 12) ? 'pm' : 'am' ),\n\n\t};\n\n\treturn parts;\n};\n\nDate.prototype.format = function(str)\n{\n\t//break each char into it's own array we will process separately\n\tvar arr = str.split('');\n\tvar dref = this;\n\tvar parts = this.parts();\n\t\t\n\tarr.forEach( (char,idx) => {\n\n\t\tif (char.trim().length > 0 && typeof(parts[char]) != 'undefined')\n\t\t{\n\t\t\tarr[idx] = parts[char];\n\t\t}\n\t});\n\n\treturn arr.join('');\n};\n\nDate.prototype.formatDate = function()\n{\n\treturn this.format('m/d/Y');\n};\n\nDate.prototype.formatDateTime = function()\n{\n\treturn this.format('m/d/Y h:i A');\n};\n\n\nDate.prototype.toDateString = function(str)\n{\n if (!str) str = 'mm/dd/yy';\n return this.format(str);\n};\n\nDate.prototype.toRawDateString = function(str)\n{\n\treturn this.format('yy-mm-dd');\n};\n\n\n\nDate.prototype.toTimeString = function(str)\n{\n\t//get local timezone offset\n\tconst tz = 0 - new Date().getTimezoneOffset();\n\n if (!str) str = 'h:mm:ss TT';\n\n return this.format(str);\n};\n\nDate.prototype.toRawTimeString = function(str)\n{\n\treturn this.format('HH:mm:ssZ');\n};\n\nDate.prototype.toRawDateTimeString = function(str)\n{\n\treturn this.toRawDateString() + 'T' + this.toRawTimeString();\n};\n\nDate.prototype.toDateTimeString = function()\n{\n\treturn this.toDateString() + ' ' + this.toTimeString();\n};\n\nDate.prototype.toDataString = function()\n{\n\tif (UTIL.isMobileDevice()) \n\t{\n\t\treturn this.toRawDateString() + 'T' + this.toRawTimeString();\n\t}\n\telse \n\t{\n\t\treturn this.toDateString() + ' ' + this.toTimeString();\n\t}\n};\n\nDate.prototype.toDateTimeFormString = function()\n{\n\treturn this.format('yy-mm-dd') + 'T' + this.format('hh:mm:ss');\n};\n\nDate.prototype.toRawDateString = function(str)\n{\n return this.toDateString('yy-mm-dd');\n};\n\nDate.prototype.toNearestHourString = function(str)\n{\n if (!str) str = 'h:mm:ss TT';\n\n\tif (this.getMinutes() >= 30) this.addHours(1);\n\n\t//get local timezone offset\n\tvar tz = 0 - new Date().getTimezoneOffset();\n\n return this.format(str);\n};\n\nDate.prototype.clone = function()\n{\n\treturn new Date(this);\n};\n\nDate.prototype.toTimeStamp = function()\n{\n\treturn this.getTime()/1000;\n};\n\nDate.prototype.dayDiff = function(d2)\n{\n\tlet msPerDay = 1000 * 60 * 60 * 24;\n\n\tlet utc1 = Date.UTC(this.getFullYear(), this.getMonth(), this.getDate());\n\tlet utc2 = Date.UTC(d2.getFullYear(), d2.getMonth(), d2.getDate());\n\n return Math.floor((utc2 - utc1) / msPerDay);\n};\n\n", "\nNumber.prototype.formatDecimals = function(decimals)\n{\n\treturn this.toFixed(decimals);\n}\n\nNumber.prototype.formatGroups = function(decimals)\n{\n\treturn this.format({decimals:decimals,group:','});\n}\n\nNumber.prototype.formatPercent = function(decimals)\n{\n\treturn String(this).formatPercent(decimals);\n}\n\nNumber.prototype.formatCurrency = function(decimals)\n{\n\treturn String(this).formatCurrency(decimals);\n}\n\nNumber.prototype.formatSeconds = function(showSeconds)\n{\n\treturn String(this).formatSeconds(showSeconds);\n}\n\nNumber.prototype.formatSize = function()\n{\n const bytes = parseInt(this);\n\n const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];\n const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));\n\n if (bytes === 0)\n {\n return '0 Bytes';\n }\n else if (i === 0) \n {\n return bytes + ' ' + sizes[i];\n }\n\n return (bytes / Math.pow(1024, i)).toFixed(2) + ' ' + sizes[i];\n};\n\nNumber.prototype.formatBoolean = function()\n{\n\treturn (this == true || this == '1') ? 'Yes' : 'No';\n};\n\n", "\n\n/***********************************************************************\n\tLocal Storage Functions\n***********************************************************************/\n\nStorage.prototype.setObject = function(key, value) {\n\n this.setItem(key, JSON.stringify(value));\n};\n \nStorage.prototype.getObject = function(key) {\n\n return JSON.parse(this.getItem(key));\n\n};\n\n//setup setString for consistency sake (setObject & setString)\nStorage.prototype.setString = function(key, value) {\n this.setItem(key, value);\n};\n \nStorage.prototype.getString = function(key) {\n return this.getItem(key);\n};\n\n\n/** for interoperability with wdStorage. assumes all values are objects **/\n\nStorage.prototype.get = function(key)\n{\n if ( typeof(this.getItem(key)) == 'string') return JSON.parse(this.getItem(key));\n else return null;\n};\n\nStorage.prototype.set = function(key,val)\n{\n this.setItem(key,JSON.stringify(val));\n};\n\n", "\n\n/**\n\tstring prototypes\n\t*/\n\nString.prototype.ltrim = function() {\n\treturn this.replace(/^\\s+/,\"\");\n};\n\nString.prototype.rtrim = function() {\n\treturn this.replace(/\\s+$/,\"\");\n};\n\nString.prototype.reverse = function()\n{\t\n\treturn this.split(\"\").reverse().join(\"\");\n};\n\nString.prototype.pad = function(len,padstr,mode)\n{\t\n\n\tif (typeof(mode)==\"undefined\") mode = \"back\";\n\tif (typeof(padstr)==\"undefined\") padstr = \" \";\n\n\tvar str = String.from(this);\n\n\tif (mode==\"front\") {\n\n\t\tvar diff = parseInt(len) - str.length;\n\t\tfor (var i=0;i= 8 && !isNaN( Date.parse(this) );\n};\n\n\nString.prototype.formatNumeric = function(decimals)\n{\n\tif (this.trim().length==0 || this == 'null') return \"\";\n\n\tif (!decimals) decimals = 2;\n\n\tvar num = parseFloat(this);\n\t\n\tif (num) return num.toFixed(decimals);\n\telse return 0;\n\n};\n\nString.prototype.formatDuration = function()\n{\n\tvar seconds = parseInt(this);\n\tvar ret = \"\";\n\n\tvar sec = seconds % 60;\n\tvar min = parseInt((seconds / 60)) % 60;\n\tvar hours = parseInt(seconds / 3600);\n\n\tif (String(min).length < 2) min = \"0\" + min;\n\tif (String(sec).length < 2) sec = \"0\" + sec;\n\n\tif (hours < 1) ret = min + \":\" + sec;\n\telse ret = hours + \":\" + min + \":\" + sec;\n\n\treturn ret;\n\n};\n\nString.prototype.formatCurrency = function(decimals)\n{\n\treturn this.formatPrice(decimals);\n}\n\nString.prototype.formatPrice = function(decimals)\n{\n\tif (this.trim().length==0 || this == 'null') return \"$0.00\";\n\n\tvar num = parseFloat(this);\n\n\tif (num) \n\t{\n\t\tvar data = num.toFixed(decimals);\n\t\tvar abv = Math.abs(data);\n\n\t\tif (data < 0) return \"($\" + abv + \")\";\n\t\telse return \"$\" + abv;\n\t}\n\telse\n\t{\n\t\treturn \"$0.00\";\n\t}\n\t\n};\n\nString.prototype.formatPercent = function(decimals)\n{\n\tif (this.trim().length==0 || this==\"infinity\" || this == 'null') return \"0%\";\n\n\tif (!decimals) decimals = \"0\";\n\n\tvar num = parseFloat(this);\n\n\tif (num)\n\t{\n\t\tvar data = parseFloat(this).toFixed(decimals);\n\t\tvar abv = Math.abs(data);\n\n\t\tif (data < 0) return \"(\" + abv + \"%)\";\n\t\telse return abv + \"%\";\n\t}\n\telse\n\t{\n\t\treturn \"0%\";\n\t}\n\n};\n\nString.prototype.formatSize = function()\n{\n\treturn Number(this).formatSize();\n};\n\nString.prototype.formatPhone = function()\n{\n\n\tif (this.trim().length==0 || this == 'null') return \"\";\n\n\tvar str = this.replace(/[^0-9]+/g,'');\n\t\n\tif (str.length==11)\n\t{\n\t\tstr = str.substr(1);\n\t\tstr = \"(\" + str.substr(0,3) + \") \" + str.substr(3,3) + \"-\" + str.substr(6);\n\t}\n\telse if (str.length==12)\n\t{\n\t\tstr = str.substr(1);\n\t\tstr = \"(\" + str.substr(1,3) + \") \" + str.substr(4,3) + \"-\" + str.substr(7);\n\t}\n\telse if (str.length==10)\n\t{\n\t\tstr = \"(\" + str.substr(0,3) + \") \" + str.substr(3,3) + \"-\" + str.substr(6);\n\t}\n\telse if (str.length==7)\n\t{\n\t\tstr = str.substr(0,3) + \"-\" + str.substr(3);\n\t}\n\n\treturn str;\n\n};\n\nString.prototype.processPhone = function()\n{\n\tvar str = this.replace(/[^0-9]+/g,'');\n\t\n\tif (str.length == '10') str = '+1' + str;\n\telse if (str.length == '11') str = '+' + str;\n\t\n\treturn str;\n};\n\nString.prototype.parseDate = function()\n{\n\t//if we are passed a date string only, we need to adjust for time zones\n\tif (this.indexOf(' ')==-1 && this.indexOf('T')==-1) var d = new Date(this).localDate();\n\telse var d = new Date(this);\n\n\treturn d;\n};\n\nString.prototype.formatDate = function()\n{\n\tif (this.trim().length==0 || this == 'null') return \"\";\n\t//console.log(this + ' => ' + this.parseDate());\n\treturn new Date(this).localDate().format('m/d/Y');\n};\n\nString.prototype.formatDateTime = function(showSeconds)\n{\n\tif (this.trim().length==0 || this == 'null') return \"\";\n\t\n\t//show seconds only if asked\n\tconst formatStr = (showSeconds) ? 'm/d/Y h:i:s a' : 'm/d/Y h:i a';\n\t\n\treturn new Date(this).format(formatStr);\n};\n\nString.prototype.formatTime = function(str)\n{\n\tif (this.trim().length==0 || this == 'null') return \"\";\n\treturn this.parseDate().toTimeString(str);\n};\n\nString.prototype.formatBoolean = function()\n{\n\treturn ( this == true ) ? 'Yes' : 'No';\n};\n\nString.prototype.formatLink = function(txt)\n{\n\tif (!txt) txt = this;\n\treturn $('').attr('href',this).text(txt);\n}\n\nString.prototype.capitalize = function()\n{\n\tvar prefix = this.substring(0,1).toUpperCase();\n\tvar suffix = this.substring(1);\n\treturn prefix + suffix;\n};\n\nString.prototype.parseName = function()\n{\n\tvar ret = {};\n\n\tvar arr = this.trim().split(' ');\n\n\tif (arr.length == 1)\n\t{\n\t\tret.last_name = arr[0].capitalize();\n\t}\n\telse if (arr.length == 2)\n\t{\n\t\tret.first_name = arr[0].capitalize();\n\t\tret.last_name = arr[1].capitalize();\n\t}\n\telse if (arr.length > 2)\n\t{\n\t\tret.first_name = arr[0].capitalize();\n\t\tret.middle_name = arr[1].capitalize();\n\t\tret.last_name = arr[arr.length-1].capitalize();\n\t}\n\n\treturn ret;\n\n};\n\nString.prototype.fileExtension = function()\n{\n\tvar pos = this.indexOf('.');\n\treturn this.substr(pos+1).toLowerCase();\n};\n\nString.prototype.capitalize = function() {\n\treturn this.charAt(0).toUpperCase() + this.slice(1);\n}\n\nString.prototype.camelCase = function() {\n\treturn this.toLowerCase().replace(/[^a-zA-Z0-9]+(.)/g, (m, chr) => chr.toUpperCase());\n};\n\nString.prototype.titleCase = function() {\n\n\treturn this.replace(/(_|-)/g, ' ')\n \t\t\t\t\t\t.trim()\n \t\t\t\t\t\t.replace(/\\w\\S*/g, (str) => {\n \t\t\t\t\t\t\treturn str.charAt(0).toUpperCase() + str.substring(1)\n\t\t\t\t\t\t\t}) \n\t\t\t\t\t\t\t.replace(/([a-z])([A-Z])/g, '$1 $2')\n\t\t\t\t\t\t\t.replace(/([A-Z])([A-Z][a-z])/g, '$1 $2');\n\n// \t\t\t\t\t\treturn this.toLowerCase().split(' ').map((s) => s.charAt(0).toUpperCase() + s.substring(1)).join(' ');\n\n};\n\nString.prototype.parseRange = function()\n{\n\tvar output = [];\n\tvar arr = this.split(',');\n\n\tarr.forEach( range => {\n\t\n\t\tif (range.indexOf('-') != -1) \n\t\t{\n\t\t\tvar ends = range.split('-');\n\n\t\t\tfor (var i=ends[0]; i<= ends[1]; i++)\n\t\t\t{\n\t\t\t\toutput.push( String(i) );\n\t\t\t}\n\n\t\t}\n\t\telse\n\t\t{\n\t\t\toutput.push(range);\n\t\t}\n\t\n\t});\n\n\treturn output;\n\n};\n\nString.prototype.formatSeconds = function(showSeconds)\n{\n\tif (this.length == '0' || this == 'null') return null;\n\tif (typeof(showSeconds) == 'undefined') showSeconds = true;\n\t\n\tvar strlen = (showSeconds == true) ? 8 : 5;\n\t\n\treturn new Date(parseInt(this) * 1000).toISOString().substr(11, strlen)\n};\n\nString.prototype.interpolate = function(params) {\n\n\tconst names = Object.keys(params);\n\tconst values = Object.values(params);\n\n\ttry {\t\t\n\t\treturn new Function(...names, `return \\`${this}\\`;`)(...values);\n\t}\n\tcatch (err)\n\t{\n\t\tconsole.log('Interpolate Error',err,this);\n\t\treturn this;\n\t}\n};\n\nString.prototype.getTemplateKeys = function()\n{\n\tconst regex = /\\${([^}]+)}/g;\n\n\tconst templateNames = [];\n\tlet match = null;\n\t\n\twhile ((match = regex.exec( this )) !== null) {\n templateNames.push(match[1]);\n\t}\n\n\treturn templateNames;\n};\n", "\nexport default class WDError extends Error {\n\n\t#debugObj;\n\t\n\t/**\n\t\tparams can contain\n\t\t- code\n\t\t- details\n\t\t- name (override error name)\n\t\t*/\n\tconstructor(message,params) \n\t{\n\t\tsuper(message);\n\n\t\tif (typeof(params) != 'undefined')\n\t\t{\n\t\t\tObject.assign(this,params);\n\t\t}\n\t\t\n\t\t//if we have a code, map the name to known codes \n\t\tif (typeof(this.code) != 'undefined') this.mapCodeName();\n\n\t};\n\n\tmapCodeName()\n\t{\n\t\tconst map = { '401': 'AuthError',\n\t\t\t\t\t\t\t\t\t'404': 'NotFound'\n\t\t\t\t\t\t\t\t};\n\n\t\tif (typeof(map[this.code]) != 'undefined') this.name = map[this.code];\n\t};\n\t\n};\n\n", "\nexport default class WDFormat {\n\n static boolean(val)\n {\n return Number(val).formatBoolean();\n };\n\n static date(val,str)\n {\n if (!str) str = 'm/d/Y';\n\n const dateVal = (typeof(val) == 'object') ? val : new Date(val);\n\n //utc fix, if not passed any time, add one day\n if ( typeof(val) == 'string' && val.indexOf(':') == -1 ) dateVal.addDays(1);\n\n return dateVal.format(str);\n };\n\n static dateTime(val)\n {\n const dateVal = (typeof(val) == 'object') ? val : new Date(val);\n return dateVal.format('m/d/Y h:i a');\n };\n\n static dateTime24(val)\n {\n const dateVal = (typeof(val) == 'object') ? val : new Date(val);\n return dateVal.format('m/d/Y H:i:s');\n };\n\n static size(val)\n {\n return Number(val).formatSize();\n };\n\n static currency(val)\n {\n if (typeof(val) == 'string') \n {\n val = val.replace(/[^0-9.\\-()]/g,'');\n }\n return Number(val).formatCurrency(0);\n }; \n\n static currencyDecimals(val)\n {\n if (typeof(val) == 'string') \n {\n val = val.replace(/[^0-9.\\-()]/g,'');\n }\n return Number(val).formatCurrency(2);\n }; \n\n static percent(val,precision)\n {\n if (!precision) precision = '0';\n return String(val).formatPercent(precision);\n };\n\n};\n\n", "\nexport default class WDUtil {\n\n static isObject(item)\n {\n return (item !== null && typeof item === 'object' && !(item instanceof Array) );\n }\n\n /**\n * for the purpose of merging. these aren't cloned\n */\n static isReference(item)\n {\n return (\titem instanceof Promise || \n typeof(item) == 'function' || \n item.constructor.toString().substring(0, 5) == 'class' ||\n item.isProxy\n );\n };\n\n /* From ChatGPT - need to test - also need to decide if it should clone or reference Proxies */\n static mergeObjects(target, ...sources)\n {\n if (!WDUtil.isObject(target)) throw new Error('Target must be an object');\n\n for (const source of sources) \n {\n if (!WDUtil.isObject(source)) throw new Error('Source must be an object');\n\n for (const key in source) \n {\n if (WDUtil.isObject(source[key])) \n {\n if (!target[key] || !WDUtil.isObject(target[key])) \n {\n target[key] = {};\n }\n WDUtil.mergeObjects(target[key], source[key]);\n } \n else \n {\n if (source[key]?.isProxy)\n {\n target[key] = source[key].toObject();\n }\n else if (WDUtil.isReference(source[key])) \n {\n target[key] = source[key];\n } \n else \n {\n target[key] = JSON.parse(JSON.stringify(source[key]));\n }\n }\n }\n }\n \n return target;\n }\n\n\n /**\n * Deep merge two objects.\n * @param target\n * @param ...sources\n */\n static merge(target, ...sources)\n {\n if (!sources.length) return target;\n\n const source = sources.shift();\n\n if (WDUtil.isObject(target) && WDUtil.isObject(source)) \n {\n for (const key in source) \n {\n //handle nested objects\n if (WDUtil.isObject(source[key])) \n {\n //promises, functions, classes and proxies aren't cloned, just passed by reference\n if ( WDUtil.isReference(source[key]) )\n {\n target[key] = source[key];\n }\n else\n {\n //target doesn't exist or is null, clone into a new object\n if (target[key] === undefined || target[key] === null) Object.assign(target, { [key]: {} });\n\n //\n WDUtil.merge(target[key], source[key]);\n }\n\n }\n else if (source[key] !== undefined)\n {\n Object.assign(target, { [key]: source[key] });\n }\n }\n }\n \n return target;\n };\n\n static extend(...items)\n {\n const target = items.shift();\n return this.merge(target,items);\n };\n\n static param(params)\n {\n if (!params) return '';\n\n const urlParams = new URLSearchParams();\n\n for (const [name, value] of Object.entries(params))\n {\n urlParams.append(name, value || '');\n }\n\n return urlParams.toString();\n\n };\n\n static uuid()\n {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n let r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n }; \n\n static belowBreakpoint()\n {\n return (window.innerWidth < WDConfig.panes.breakpoint);\n };\n\n static fileToClass(fileName)\n {\n return fileName\n .split('-') // Split by hyphen\n .map((word, index) =>\n index === 0 && word.toLowerCase() === 'wd'\n ? 'WD' // Ensure 'WD' is always capitalized\n : word.charAt(0).toUpperCase() + word.slice(1) // Capitalize the rest\n )\n .join('');\n };\n\n static newClassToFile(className) {\n return className\n .replace(/([A-Z]+)([A-Z][a-z]|[a-z]|$)/g, (match, p1, p2) => {\n return `${p1.toLowerCase()}-${p2.toLowerCase()}`;\n })\n .replace(/-$/, ''); // Remove trailing hyphen if any\n };\n \n static classToFile(className)\n {\n const cn = className.replace('WD','wd');\n return cn\n .replace(/([A-Z])/g, '-$1') // Insert hyphen before each capital letter\n .toLowerCase() // Convert the whole string to lowercase\n .replace(/^-/, ''); // Remove leading hyphen if any\n }\n\n static isLDAPEnabled()\n {\n return (typeof(WDConfig.api.config.LDAPEnabled) != 'undefined' && WDConfig.api.config.LDAPEnabled == true);\n };\n\n};\n\n", "\n/**\n creates an observable object from the passed one, or\n creates a new observable object if one isn't passed.\n \n allows for adding data formatters to automatically\n return a formatted value for a specific property\n \n also works with arrays passed to the constructor\n */\n\nimport WDUtil from './wd-util';\n\nexport class WDProxy {\n\n __proxy;\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t//proxies dont work with private vars\n __subscribers = { get:{}, set:{}, delete:{} };\t\t\t//can monitor get or set, but \"get\" is currently disabled\n __isRecursive = true;\n __maxNestLevel = 10;\n __nestLevel = 0;\n \n constructor(obj)\n {\n if (typeof(obj) == 'undefined') obj = {};\n \n this.__proxy = new Proxy(obj,this);\n return this.__proxy;\n }; \n\n /**\n * If set, automatically converts objects and arrays below this one into proxies too.\n * Ususally used for forms because of nested objects.\n */\n setRecursive(val) { \n this.__proxy.__isRecursive = val; \n };\n\n isRecursive()\n {\n return this.__isRecursive;\n };\n\n isProxy()\n {\n return true;\n }\n\n nestProxy(target,prop)\n {\n const objClass = target[prop].constructor.name;\n\n //convert nested objects to proxies so we can watch for changes and bubble the events to the main proxy\n if (objClass == 'Object' || objClass == 'Array')\n {\n target[prop] = target[prop] instanceof Array ? new WDProxyArray(target[prop]) : new WDProxy(target[prop]);\n\n //bubble events to the top\n target[prop].on('set', (evt) => {\n \n const childKey = prop + '.' + evt.property;\n const childVal = evt.value;\n \n this._notify('set', childKey, { property:childKey, value:childVal });\n });\n\n //bubble events to the top\n target[prop].on('delete', (evt) => {\n const childKey = prop + '.' + evt.property;\n this._notify('delete', childKey, { property:childKey });\n });\n }\n \n };\n\n get(target,prop,receiver)\n {\n if ( typeof(target[prop]) != 'undefined' )\n {\n //we have an object that's not a proxy. turn it into one\n if (this.isRecursive() && target[prop] !== null && typeof(target[prop]) == 'object' && !target[prop]?.isProxy)\n {\n this.nestProxy(target,prop);\n }\n \n return Reflect.get(target, prop, receiver);\n \n //notify our watchers - let's disable this for now for speed, not sure it will ever be needed\n //this._notify('get',prop, { property:prop, value:target[prop] });\n //return retVal;\n }\n //own class reference?\n else if ( typeof(this[prop]) != 'undefined')\n {\n return this[prop];\n }\n else\n {\n return undefined;\n }\n };\n \n set(target,prop,receiver)\n {\n //don't set the previous value now, I feel like it's going to add overhead\n //we could always inherit proxy with WDProxyHistory here all it does is append the history\n //same with the formatting stuff\n \n //const prevVal = target[prop];\n const retVal = Reflect.set(target, prop, receiver);\n\n this._notify('set',prop, { property:prop, value:target[prop] }); //, prev_value:prevVal });\n \n return retVal;\n };\n\n deleteProperty(target, prop) \n { \n // to intercept property deletion\n if (prop.startsWith('__')) \n {\n throw new Error(\"Access denied\");\n } \n else \n {\n delete target[prop];\n this._notify('delete',prop, { property:prop }); //, prev_value:prevVal });\n\n return true;\n }\n };\n \n ownKeys(target) { // to intercept property list\n return Object.keys(target).filter(key => !key.startsWith('__'));\n }\n\n /**\n internal methods for the object\n */\n on(mode,handler)\n {\n this.onProp(mode,'__all',handler);\n };\n\n onProp(mode,prop,handler)\n {\n const subscribers = this.__subscribers[mode];\n \n\t //make sure we have a subscriber element for this property\n\t if (typeof(subscribers[prop]) == 'undefined') subscribers[prop] = [];\n\n //add the subscriber to the watcher for this property\t \n subscribers[prop].push(handler);\n };\n\n off(mode,handler)\n {\n this.offProp(mode,'__all',handler);\n };\n \n offProp(mode,prop,handler)\n {\n const subscribers = this.__subscribers[mode];\n\n if (typeof(handler) == 'undefined') \n {\n delete( subscribers[prop] );\n }\n else\n {\n const idx = subscribers[prop].indexOf(handler);\n subscribers[prop].splice(idx,1);\n }\n };\n\n toObject()\n {\n const ret = {};\n \n Object.keys( this.__proxy ).forEach( key => {\n \n const val = this.__proxy[key];\n \n if (val === null)\n {\n ret[key] = null;\n }\n else if (typeof(val) == 'object')\n {\n if (val?.isProxy)\n {\n ret[key] = val instanceof Array ? val.toArray() : val.toObject();\n }\n else\n {\n ret[key] = (val instanceof Array) ? Object.assign([],val) : Object.assign({},val);\n }\n }\n else\n {\n ret[key] = val;\n }\n\n });\n\n\t return ret;\n\n };\n\n assign(objData)\n {\n Object.assign(this,objData);\n return this;\n };\n\n merge(objData)\n {\n WDUtil.merge(this,objData);\n return this;\n };\n\n replace(objData)\n {\n const keepKeys = Object.keys( objData );\n\n //remove any keys not in the object we are replacing with\n Object.keys( this.__proxy ).forEach( key => {\n if (keepKeys.indexOf(key) == -1) delete( this.__proxy[key] );\n });\n\n //copy in the new data\n Object.assign(this,objData);\n };\n\n empty()\n {\n this.replace({});\n };\n\n _notify(mode,prop,evtData)\n {\n this._notifyProp(mode,prop,evtData);\n this._notifyProp(mode,'__all',evtData);\n };\n \n _notifyProp(mode,prop,evtData)\n {\n const subscribers = this.__subscribers[mode];\n\n if (typeof(subscribers[prop]?.forEach) != 'undefined')\n {\n //evtData.proxy = this;\t//this is probably a bad idea - makes it possible to create infinite loops\n subscribers[prop].forEach( handler => {\n if (typeof(handler) == 'function') handler(evtData);\n });\n }\n };\n\n};\n\n\n/**\n creates an observable object from the passed one, or\n creates a new observable object if one isn't passed.\n \n allows for adding data formatters to automatically\n return a formatted value for a specific property\n \n also works with arrays passed to the constructor\n */\n\nexport class WDProxyArray extends WDProxy {\n\n constructor(arr)\n {\n if (!arr) arr = [];\n return super(arr);\n }; \n\n /**\n replace all members of this array with new one\n */\n replace(newArr)\n {\n while (this.length > 0) this.pop();\n\n newArr.forEach( val => {\n this.push(val);\n });\n \n //this.length = 0;\t\t\t\t//clear this array \n //this.push(...newArr);\t\t//push new array in (are there limits to size here?)\n };\n\n ownKeys(target){\n\n return Reflect.ownKeys(target).filter(key => {\n const numKey = +key;\n return Number.isNaN(numKey) || numKey >= 0;\n });\n \n };\n \n toArray()\n {\n const output = [];\n \n this.forEach( val => {\n\n if (typeof(val) == 'object')\n {\n if (val?.isProxy)\n {\n const newVal = val instanceof Array ? val.toArray() : val.toObject();\n output.push(newVal);\n }\n else\n {\n const newVal = Object.assign({},val);\n delete(newVal.__isRecursive);\t\t\t\t\t\t//bugfix, only happens when creating a proxyObj from a proxy array\n \n output.push( newVal );\n }\n }\n else\n {\n output.push(val);\n }\n \n });\n\n return output; \n };\n\n /**\n * because this really an object in disguise, we have to convert to an array\n * and reorder, then replace.\n */\n reorder(from,to)\n {\n const arr = this.toArray();\n arr.splice(to, 0, arr.splice(from, 1)[0]);\n \n this.replace(arr);\n };\n\n remove(idx)\n {\n this.splice(idx,1);\n };\n\n insert(idx,item)\n {\n this.splice(idx,0,item);\n };\n};\n\n", "\nexport default class WDFetchError extends Error {\n\n\tconstructor(message,params) \n\t{\n\t\tsuper(message);\n\n\t\tif (typeof(params) != 'undefined')\n\t\t{\n\t\t\tObject.assign(this,params);\n\t\t}\n\t\t\n\t\tif (params.response)\n\t\t{\n\t\t\tparams.response.text().then( results => {\n\t\t\t\tconsole.log('Server Error',results);\n\t\t\t});\n\t\t};\n\n\t};\n\n};\n\n", "\nimport WDUtil from '../wd-util';\nimport WDError from '../wd-error';\nimport { WDProxy } from '../wd-proxy';\nimport WDFetchError from './wd-fetch-error';\n\n/**\n\tWDFetch\n\t*/\n\nexport default class WDFetch extends WDProxy {\n\n\t#controller = null;\n\n\t__className = 'WDFetch';\n\t__config = { \tcontentType: 'application/json',\n\t\t\t\t\t\t\t\tauth: {},\n\t\t\t\t\t\t\t\tbaseURL: null,\n\t\t\t\t\t\t\t\tbaseURI: null,\n\t\t\t\t\t\t\t\tbaseObject: null,\n\t\t\t\t\t\t\t\tallowMultipleRequests: false,\n\t\t\t\t\t\t\t\theaders: {}\n\t\t\t\t\t\t\t}; \n\n\t\t\t\n\tconstructor(config,values)\n\t{\n\t\t//if we are passed a base object to work with, use it to init underlying proxy. otherise start empty\n\t\tconst initObj = (typeof(config) == 'object' && typeof(config.baseObject) == 'object') ? config.baseObject : {};\n\t\tsuper(initObj);\n\n\t\tif (typeof(config) == 'string') \n\t\t{\n\t\t\tthis.__config.baseURI = config;\n\t\t}\n\t\telse if (typeof(config) == 'object')\n\t\t{\n\t\t\tWDUtil.merge(this.__config,config);\n\n\t\t\t//passed a base object with a uri to link to\t\t\t\n\t\t\tif (!this.__config.baseURI && this.__config.baseObject && this.__config.baseObject.uri)\n\t\t\t{\n\t\t\t\tthis.__config.baseURI = this.__config.baseObject.uri;\n\t\t\t}\n\t\t}\n\n\t\tif (values != undefined) this.add(values);\n\t};\n\n\t//backwards compatiblity - for now\n\tadd(obj)\n\t{\n\t\treturn this.assign(obj);\n\t};\n\n\tabort()\n\t{\n\t\tconsole.log('Aborting request');\n\t\tthis.#controller.abort('queue');\n\t};\n\n\tgetURI(addURI)\n\t{\n\t\tlet uri = '';\n\t\t\n\t\t//given a base uri to work with\n\t\tif (this.__config.baseURI) uri += this.__config.baseURI;\n\n\t\t//given a supplemental uri to the base url and base uri\n\t\tif (addURI) uri += addURI;\n\n\t\treturn uri;\n\t};\n\n\tsetBaseURI(uri)\n\t{\n\t\tthis.__config.baseURI = uri;\n\t};\n\n\tbuildURL(uri)\n\t{\n\t\tlet url = '';\n\t\tif (this.__config.baseURL) url += this.__config.baseURL;\n\n\t\tconst addURI = this.getURI(uri);\n\t\tif (addURI) url += addURI;\n\n\t\ttry {\n\t\t\treturn new URL(url);\n\t\t}\n\t\tcatch(err)\n\t\t{\n\t\t\tconsole.log('Error forming URL',url,err);\t\t\n\t\t}\n\t};\n\n\tbuildHeaders()\n\t{\n\t\tconst headers = { \n\t\t\t'Content-Type': this.__config.contentType,\n\t\t\t'X-Requested-With': 'XMLHttpRequest' \n\t\t};\n\n\t\tthis.buildAuth();\n\n\t\tObject.assign(headers,this.__config.headers);\n\n\t\treturn new Headers(headers);\n\t};\n\n\tbuildAuth()\n\t{\n\t\tif (this.__config.auth)\n\t\t{\n\t\t\tconst auth = this.__config.auth;\n\n\t\t\tif (auth.mode == 'basic')\n\t\t\t{\n\t\t\t\tthis.__config.headers.Authorization = 'Basic ' + btoa(auth.login + ':' + auth.password);\n\t\t\t}\n\t\t\telse if (auth.mode == 'digest')\n\t\t\t{\n\t\t\t}\n\t\t}\n\t};\n\n\n\tbuildRequest(method,uri)\n\t{\n\t\t//setup a controller so we can do aborts\n\t\tthis.#controller = (this.__config.allowMultipleRequests === false) ? new AbortController() : null;\n\n\t\t//create a base url\n\t\tconst url = this.buildURL(uri);\n\n\t\tif (typeof(method) == 'undefined') method = 'GET';\n\n\t\t//options for the Request object\n\t\tconst options = this.buildOptions(method,url);\n\n\t\t//return the request\n\t\treturn new Request(url, options);\n\t};\n\n\tbuildOptions(method,url)\n\t{\n\t\tconst options = {\tmethod: method,\n\t\t\t\t\t\t\t\t\t\t\theaders: this.buildHeaders()\n\t\t\t\t\t\t\t\t\t\t};\n\n\t\tif (this.#controller !== null) options.signal = this.#controller.signal;\n\t\tif (this.__config.auth != false) options.credentials = 'include';\n\t\t\n\t\tconst dataObj = this.toObject();\n\t\tconst dataKeys = Object.keys(dataObj);\n\n\t\tif (dataKeys.length > 0)\n\t\t{\n\t\t\t//if it's a method that can have a body, add the body\t\n\t\t\tif (method == 'PUT' && dataObj.file)\n\t\t\t{\n\t\t\t\toptions.body = dataObj.file;\n\t\t\t}\n\t\t\telse if (method == 'GET')\n\t\t\t{\n\t\t\t\tdataKeys.forEach(key => url.searchParams.append(key, dataObj[key]) );\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\toptions.body = JSON.stringify(dataObj);\n\t\t\t}\n\t\t}\n\n\t\treturn options;\n\t\t\t\n\t};\n\n\tresponseContentType(response)\n\t{\n\t\tconst ct = response.headers.get(\"content-type\");\n\t\tconst pos = ct.indexOf(';');\n\t\t\n\t\treturn (pos !== -1) ? ct.substr(0,pos) : ct;\n\t};\n\n\treq(method,uri)\n\t{\n\t\tconsole.log('Running ' + method + ' request',this.__config.baseURI,uri); //,this.toObject());\n\t\t\n\t\tif (this.trace_call) console.trace();\t\t\n\t\t\n\t\t//if (this.__config.baseURI.indexOf('/components') != -1) console.trace();\n\n\t\t//cancel any current request\n\t\tif (this.#controller) this.abort();\n\n\t\t//build the request\n\t\tconst request = this.buildRequest(method,uri);\n\n\t\t//for some error handling later\n\t\tlet cloneResp = null;\n\n\t\t//run the fetch\n\t\tconst ftch = fetch(request).then( response => {\n\n\t\t\t//clone the response for error handling\n\t\t\tcloneResp = response.clone();\n\t\t\t\n\t\t\t//console.log(\"Response\",cloneResp.json());\n\t\t\tconst contentType = this.responseContentType(response);\n\n\t\t\t//console.log('Headers',...response.headers);\n\n\t\t\tif (response.ok)\n\t\t\t{\n\t\t\t\t//everything came back okay, and it was the right response type\n\t\t\t\tif (contentType == this.__config.contentType)\n\t\t\t\t{\n\t\t\t\t\treturn (contentType == 'application/json') ? response.json() : response.text();\n\t\t\t\t}\n\t\t\t\t//usually some sort of server side parse error\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tthrow new WDFetchError(response.message, {code:response.status, response:response});\n\t\t\t\t}\n\t\t\t}\n\t\t\t//this is usually a caught error, formatted properly\n\t\t\telse\n\t\t\t{\n\t\t\t\tthrow new WDError(response.message, \n\t\t\t\t\t\t\t\t\t\t{\tcode:response.status, \n\t\t\t\t\t\t\t\t\t\t\tname:'FetchError', \n\t\t\t\t\t\t\t\t\t\t\tcontentType: contentType,\n\t\t\t\t\t\t\t\t\t\t\tresponse: cloneResp\n\t\t\t\t\t\t\t\t\t\t});\n\t\t\t}\n\t\t})\n\t\t.catch( err => {\n\n\t\t\t//do nothing on abort errors\n\t\t\tif (err.name == 'AbortError') \n\t\t\t{\n\t\t\t\tthrow new Error('AbortError');\n\t\t\t}\n\t\t\t//auth errors get special treatment\n\t\t\telse if (err.code == 401)\n\t\t\t{\n\t\t\t\tthrow new Error('Unauthorized',{ code:err.code});\n\t\t\t}\n\t\t\telse if (err.name == 'FetchError')\n\t\t\t{\n\t\t\t\tconst wdClone = cloneResp.clone();\n\n\t\t\t\t//once for the console\t\t\t\t\n\t\t\t\tcloneResp.text().then( str => {\n\t\t\t\t\tconsole.log(err.name,str);\n\t\t\t\t});\n\n\t\t\t\t//throw another copy of the error to a catch later in the chain\n\t\t\t\tthrow new WDError(err.message, \n\t\t\t\t\t\t\t\t\t\t{\tcode:err.status, \n\t\t\t\t\t\t\t\t\t\t\tname:err.name,\n\t\t\t\t\t\t\t\t\t\t\tcontentType: err.contentType,\n\t\t\t\t\t\t\t\t\t\t\tresponse: wdClone\n\t\t\t\t\t\t\t\t\t\t});\n\n\t\t\t}\n\t\t\telse if (err.name == 'SyntaxError')\n\t\t\t{\n\t\t\t\tcloneResp.text().then( str => {\n\t\t\t\t\tconsole.log(err.name,str);\n\t\t\t\t});\n\t\t\t}\n\t\t\t//flush out the response for more detailed info on what happened\n\t\t\telse\n\t\t\t{\n\t\t\t\t//throw another copy of the error to a catch later in the chain\n\t\t\t\tthrow new WDError(err.message, \n\t\t\t\t\t\t\t\t\t\t{\tcode:err.status, \n\t\t\t\t\t\t\t\t\t\t\tname:err.name,\n\t\t\t\t\t\t\t\t\t\t\tcontentType: err.contentType,\n\t\t\t\t\t\t\t\t\t\t\tresponse: err.response\n\t\t\t\t\t\t\t\t\t\t});\n\t\t\t}\n\t\n\t\t})\n\t\t.finally( data => {\n\t\t\tthis.#controller = null;\n\t\t});\n\n\t\treturn ftch;\n\t\t\n\t};\n\n\n\t//wrappers for sending to the api\n\tpost(uri) { return this.req('POST',uri); };\n\tput(uri) { return this.req('PUT',uri); };\n\tdel(uri) { return this.req('DELETE',uri); };\n\n};\n\n", "/**\n\tWDFetchArray\n\t- what would this do? Create a fetch and just store all the results? What are we accessing,\n\t\tthe results or the fetch object itself? wouldn't be able to subclass this either. May just\n\t\tbe easier to assign the results of WDFetch into a proxy array\n\t\t\n\t\tother possibilities\n\t\t- can always access the fetch from within this, teh results are part of the core object\n\t\t- the results are an array of WDFetchObjects that can be worked with\n\t\t- parameters are the same as WDFetch, but we create a fetch object that can be accessed\n\t\t- directly as needed. \n\t*/\n\nimport { WDProxyArray } from '../wd-proxy';\n\nexport default class WDFetchArray extends WDProxyArray {\n\n\t#fetch;\n\n\tconstructor(config)\n\t{\n\t\tsuper();\n\n\t\tthis.#fetch = new WDFetch(config);\n\t};\n\n\tfetch()\n\t{\n\t\treturn this.#fetch;\n\t};\n\n\treq(method)\n\t{\n\t\tif (!method) mode = 'GET';\n\t\t\n\t\treturn this.#fetch.req(method).then( results => {\n\t\t\tif (results instanceof Array) this.replace(results);\n\t\t});\n\t};\n\n};\n\n", "/**\n\t*\tstores the results of a WDFetch in a proxy and allows for direct updating\n\t* with the remote uri\n\t*\n\t*\t@class WDFetchObject\n\t*/\n\nimport WDFetch from './wd-fetch';\n\nexport default class WDFetchObject extends WDFetch {\n\n\treq(method,uri)\n\t{\n\t\treturn super.req(method,uri).then( results => {\n\n\t\t\t\t\t\t\t//merge our results with our local object\n\t\t\t\t\t\t\tthis.assign(results);\n\n\t\t\t\t\t\t\t//update our base uri with the one in the result\n\t\t\t\t\t\t\tif (results.uri) \n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tthis.__config.baseURI = results.uri;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse \n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t//this is really only a problem if we're saving a new item and expect the real\n\t\t\t\t\t\t\t\t//uri back from the api\n\t\t\t\t\t\t\t\tconsole.log('Warning! No uri returned for WDFetchObject. Using original uri');\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t};\n\n};\n\n", "/**\n\tWDFetch\n\t*/\n\nimport { WDProxy } from '../wd-proxy'\nimport WDUtil from '../wd-util';\nimport WDError from '../wd-error';\nimport WDFetchError from './wd-fetch-error'\n\nexport default class WDFetchUpload extends WDProxy {\n\n\t#progressHandler = null;\n\t\t\n\t__config = { \tcontentType: 'application/json',\n\t\t\t\t\t\t\t\tauth: {},\n\t\t\t\t\t\t\t\tbaseURL: null,\n\t\t\t\t\t\t\t\tbaseURI: null,\n\t\t\t\t\t\t\t\tbaseObject: null,\n\t\t\t\t\t\t\t\tallowMultipleRequests: false,\n\t\t\t\t\t\t\t\theaders: {},\n\t\t\t\t\t\t\t}; \n\t\t\t\n\tconstructor(config, values)\n\t{\n\t\t//if we are passed a base object to work with, use it to init underlying proxy. otherise start empty\n\t\tconst initObj = (typeof(config) == 'object' && typeof(config.baseObject) == 'object') ? config.baseObject : {};\n\t\tsuper(initObj);\n\t\t\n\t\tif (typeof(config) == 'string') \n\t\t{\n\t\t\tthis.__config.baseURI = config;\n\t\t}\n\t\telse if (typeof(config) == 'object')\n\t\t{\n\t\t\tWDUtil.merge(this.__config,config);\n\n\t\t\t//passed a base object with a uri to link to\t\t\t\n\t\t\tif (!this.__config.baseURI && this.__config.baseObject && this.__config.baseObject.uri)\n\t\t\t{\n\t\t\t\tthis.__config.baseURI = this.__config.baseObject.uri;\n\t\t\t}\n\t\t}\n\n\t\tif (values != undefined) this.add(values);\n\t};\n\n\tsetProgressHandler(val) { this.#progressHandler = val; };\n\n responseContentType(response)\n {\n const ct = response.headers.get(\"content-type\");\n const pos = ct.indexOf(';');\n \n return (pos !== -1) ? ct.substr(0,pos) : ct;\n };\n\n\t//backwards compatiblity - for now\n\tadd(obj)\n\t{\n\t\treturn this.assign(obj);\n\t};\n\n\tgetURI(addURI)\n\t{\n\t\tlet uri = '';\n\t\t\n\t\t//given a base uri to work with\n\t\tif (this.__config.baseURI) uri += this.__config.baseURI;\n\n\t\t//given a supplemental uri to the base url and base uri\n\t\tif (addURI) uri += addURI;\n\n\t\treturn uri;\n\t};\n\n setBaseURI(uri)\n {\n this.__config.baseURI = uri;\n };\n\n\tbuildURL(uri)\n\t{\n\t\tlet url = '';\n\t\tif (this.__config.baseURL) url += this.__config.baseURL;\n\n\t\tconst addURI = this.getURI(uri);\n\t\tif (addURI) url += addURI;\n\n\t\treturn url;\n\t};\n\n\tbuildAuth(request)\n\t{\n\t\tif (this.__config.auth)\n\t\t{\n\t\t\tconst auth = this.__config.auth;\n\t\t\trequest.withCredentials = true;\n\t\t\t\n\t\t\tif (auth.mode == 'basic')\n\t\t\t{\n\t\t\t\tconst authStr = 'Basic ' + btoa(auth.login + ':' + auth.password);\n\t\t\t\trequest.setRequestHeader ('Authorization', authStr);\n\t\t\t}\n\t\t\telse if (auth.mode == 'digest')\n\t\t\t{\n\t\t\t}\n\t\t}\n\t};\n\n\tgetFormData()\n\t{\n\t\tconst formData = new FormData();\n\t\tformData.append('file',this.file);\n\n\t\tconst formObj = this.toObject();\n\t\t\n\t\tfor (let key in formObj)\n\t\t{\n\t\t\tif ( typeof(formObj[key]) != 'object' ) formData.append( key, formObj[key] );\n\t\t}\n\n\t\treturn formData;\n\t};\n\n\t/**\n\t\t*\n\t\t*/\n\tbuildRequest(method,uri)\n\t{\n\t\tconst url = this.buildURL(uri);\n\n\t\tconst request = new XMLHttpRequest();\n\t\trequest.open(method,url);\n\t\trequest.setRequestHeader('X-Requested-With', 'XMLHttpRequest');\n\n\t\tfor (let key in this.__config.headers)\n\t\t{\n\t\t\trequest.setRequestHeader(key,this.__config.headers[key]);\n\t\t}\n\n\t\t//add authorization\t\t\n this.buildAuth(request);\n\t\t\t\t\n\t\t// upload progress event\n if (method == 'PUT' && this.#progressHandler)\n {\n \trequest.upload.addEventListener('progress',(evt) => {\n\n \t\t//build a progress object\n \t\tthis.file.loaded = ( this.file.loaded) ? this.file.loaded += evt.loaded : evt.loaded;\n \t\tthis.file.percent_loaded = ( parseFloat(this.file.loaded) / parseFloat(this.file.size) ) * 100;\n \t\t\n \t\tthis.#progressHandler( {file:this.file, event:evt });\n\t\t\t});\n\t\t}\n\t\t\n\t\treturn request;\n\t\t\t\n\t};\n\n\tput(uri)\n\t{\n\t\treturn new Promise( (resolve,reject) => {\n\n\t\t\t//build the request\n\t\t\tconst request = this.buildRequest('PUT',uri);\n\t\t\n \t// upload completed, resolve the promise\n \trequest.addEventListener('load', (evt) => {\n\n \t\tthis.file.loaded = this.file.size;\n \t\tthis.file.percent_loaded = 100;\n\n\t\t\t\tconst respData = this.parseResponse(request);\n\n\t\t\t\t//if there is a message, then the server threw an error\n\t\t\t\tif (respData?.message)\n\t\t\t\t{\n\t \t\treject({\n\t \t\t\tresponse: respData,\n\t \t\t\tfile: this.file,\n\t \t\t\tevent:evt, \n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t \t\tresolve({\n\t \t\t\tresponse: respData,\n\t \t\t\tfile: this.file,\n\t \t\t\tevent:evt, \n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t});\n\n\t //error handling\n \t request.addEventListener('error', (evt) => {\n\n \t\treject({\n \t\t\tresponse: this.parseResponse(request),\n \t\t\tfile: this.file,\n \t\t\tevent:evt, \n\t\t\t\t});\n\n\t\t\t});\n\n\t\t\t//send the request\t\t\t\n\t\t\trequest.send( this.getFormData() );\n\n\t\t});\t\t\t\n\t};\n\n\tparseResponse(request)\n\t{\n\t\tlet resp = null;\n\t\t\n\t\ttry {\n\t\t\tresp = JSON.parse(request.response);\n\t\t}\n\t\tcatch(err) {\n\t\t\tresp = request.response;\n\t\t}\n\n\t\treturn resp;\n\t};\n\n\tmkcol(uri)\n\t{\n\t\treturn new Promise( (resolve,reject) => {\n\n\t\t\t//build the request\n\t\t\tconst request = this.buildRequest('MKCOL',uri);\n\t\t\n \t// upload completed, resolve the promise\n \trequest.addEventListener('load', (evt) => {\n \t\tresolve( this.parseResponse(request) );\n\t\t\t});\n\n\t //error handling\n \t request.addEventListener('error', (evt) => {\n \t\treject( this.parseResponse(request) );\n\t\t\t});\n\n\t\t\t//send the request\t\t\t\n\t\t\trequest.send();\n\t\t});\t\t\t\n\t};\n\n\n\treq(method,uri)\n\t{\n\t\t//cancel any current request\n\t\t//if (this.#controller) this.abort();\n\n\t\t//build the request\n\t\tconst request = this.buildRequest(method,uri);\n\n\t\t//for some error handling later\n\t\tlet cloneResp = null;\n\n\t\t//run the fetch\n\t\tconst ftch = fetch(request).then( response => {\n\n\t\t\t//clone the response for error handling\n\t\t\tcloneResp = response.clone();\n\t\t\tconst contentType = this.responseContentType(response);\n\n\t\t\tif (response.ok)\n\t\t\t{\n\t\t\t\t//everything came back okay, and it was the right response type\n\t\t\t\tif (contentType == this.__config.contentType)\n\t\t\t\t{\n\t\t\t\t\treturn (contentType == 'application/json') ? response.json() : response.text();\n\t\t\t\t}\n\t\t\t\t//usually some sort of server side parse error\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tthrow new WDFetchError(response.message, {code:response.status, response:response});\n\t\t\t\t}\n\t\t\t}\n\t\t\t//this is usually a caught error, formatted properly\n\t\t\telse\n\t\t\t{\n\t\t\t\tthrow new WDError(response.message, \n\t\t\t\t\t\t\t\t\t\t{\tcode:response.status, \n\t\t\t\t\t\t\t\t\t\t\tname:'FetchError', \n\t\t\t\t\t\t\t\t\t\t\tcontentType: contentType,\n\t\t\t\t\t\t\t\t\t\t\tresponse: cloneResp\n\t\t\t\t\t\t\t\t\t\t});\n\t\t\t}\n\t\t})\n\t\t.catch( err => {\n\n\t\t\t//do nothing on abort errors\n\t\t\tif (err.name == 'AbortError') \n\t\t\t{\n\t\t\t\tthrow new Error('AbortError');\n\t\t\t}\n\t\t\t//auth errors get special treatment\n\t\t\telse if (err.code == 401)\n\t\t\t{\n\t\t\t\tthrow new Error('Unauthorized',{ code:err.code});\n\t\t\t}\n\t\t\telse if (err.name == 'FetchError')\n\t\t\t{\n\t\t\t\tconst wdClone = cloneResp.clone();\n\n\t\t\t\t//once for the console\t\t\t\t\n\t\t\t\tcloneResp.text().then( str => {\n\t\t\t\t\tconsole.log(err.name,str);\n\t\t\t\t});\n\n\t\t\t\t//throw another copy of the error to a catch later in the chain\n\t\t\t\tthrow new WDError(err.message, \n\t\t\t\t\t\t\t\t\t\t{\tcode:err.status, \n\t\t\t\t\t\t\t\t\t\t\tname:err.name,\n\t\t\t\t\t\t\t\t\t\t\tcontentType: err.contentType,\n\t\t\t\t\t\t\t\t\t\t\tresponse: wdClone\n\t\t\t\t\t\t\t\t\t\t});\n\n\t\t\t}\n\t\t\telse if (err.name == 'SyntaxError')\n\t\t\t{\n\t\t\t\tcloneResp.text().then( str => {\n\t\t\t\t\tconsole.log(err.name,str);\n\t\t\t\t});\n\t\t\t}\n\t\t\t//flush out the response for more detailed info on what happened\n\t\t\telse\n\t\t\t{\n\t\t\t\t//throw another copy of the error to a catch later in the chain\n\t\t\t\tthrow new WDError(err.message, \n\t\t\t\t\t\t\t\t\t\t{\tcode:err.status, \n\t\t\t\t\t\t\t\t\t\t\tname:err.name,\n\t\t\t\t\t\t\t\t\t\t\tcontentType: err.contentType,\n\t\t\t\t\t\t\t\t\t\t\tresponse: err.response\n\t\t\t\t\t\t\t\t\t\t});\n\t\t\t}\n\t\n\t\t})\n\t\t.finally( data => {\n\t\t\t//this.#controller = null;\n\t\t});\n\n\t\treturn ftch;\n\t\t\n\t};\n\n};\n\n", "/**\n\t*\tstores the results of a WDFetch in a proxy and allows for direct updating\n\t* with the remote uri\n\t*\n\t*\t@class WDApiObject\n\t*/\n\nimport WDFetchObject from './wd-fetch-object';\n\nclass WPFetchObject extends WDFetchObject {\n\n\tconstructor(cfg,values)\n\t{\n\t\tconst restURL = WPRestSettings.url;\n\n\t\t//remove the trailing slash from the rest url before setting\n\t\t//add the WP Nonce header also\t\t\n\t\tconst baseCfg = { baseURL: restURL.substr(0,restURL.length-1),\n\t\t\t\t\t\t\t\t\t\t\theaders: { 'X-WP-Nonce': WPRestSettings.nonce }\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\n\t\tif (cfg) \n\t\t{\n\t\t\t//passed a uri, set it up as the baseURI for the query\n\t\t\tif (typeof(cfg) == 'string') baseCfg.baseURI = cfg;\n\t\t\telse Object.assign(baseCfg,cfg);\n\t\t}\n\n\t\tsuper(baseCfg,values);\n };\n\n};\n\nexport default WPFetchObject;\n", "/**\n\tWPFetch\n\t*/\n\nimport WDFetchUpload from './wd-fetch-upload';\n\nclass WPFetchUpload extends WDFetchUpload {\n\n\tconstructor(cfg)\n\t{\n\t\tconst restURL = WPRestSettings.url;\n\n\t\t//remove the trailing slash from the rest url before setting\n\t\t//add the WP Nonce header also\t\t\n\t\tconst baseCfg = { baseURL: restURL.substr(0,restURL.length-1),\n\t\t\t\t\t\t\t\t\t\t\theaders: { 'X-WP-Nonce': WPRestSettings.nonce }\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\n\t\tif (cfg) \n\t\t{\n\t\t\t//passed a uri, set it up as the baseURI for the query\n\t\t\tif (typeof(cfg) == 'string') baseCfg.baseURI = cfg;\n\t\t\telse Object.assign(baseCfg,cfg);\n\t\t}\n\n\t\tsuper(baseCfg);\n };\n\n};\n\nexport default WPFetchUpload;\n", "/**\n\tWPFetch\n\t*/\n\nimport WDFetch from './wd-fetch';\n\nclass WPFetch extends WDFetch {\n\n\t__className = 'WPFetch';\n\t\n\tconstructor(cfg,values)\n\t{\n\t\tconst restURL = WPRestSettings.url;\n\n\t\t//remove the trailing slash from the rest url before setting\n\t\t//add the WP Nonce header also\t\t\n\t\tconst baseCfg = { baseURL: restURL.substr(0,restURL.length-1),\n\t\t\t\t\t\t\t\t\t\t\theaders: { 'X-WP-Nonce': WPRestSettings.nonce }\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\n\t\tif (cfg) \n\t\t{\n\t\t\t//passed a uri, set it up as the baseURI for the query\n\t\t\tif (typeof(cfg) == 'string') baseCfg.baseURI = cfg;\n\t\t\telse Object.assign(baseCfg,cfg);\n\t\t}\n\t\t\t\t\t\t\t\t\n\t\tsuper(baseCfg,values);\n };\n\n};\n\nexport default WPFetch;", "\nexport default class WDElement extends HTMLElement {\n \n //some shortcuts\n query = this.querySelector;\n queryAll = this.querySelectorAll;\n\n //maybe these?\n q = this.querySelector;\n qa = this.querySelectorAll;\n\n #listeners = new Map();\n #listenerId = 0;\n \n connectedCallback()\n {\n /*\n for ( const [key,listener] of this.#listeners )\n {\n this.addEventListener(listener.type,listener.callback,listener.options);\n }\n */\n };\n\n disconnectedCallback()\n {\n /*\n for ( const [key,listener] of this.#listeners )\n {\n this.removeEventListener(listener.type,listener.callback,listener.options);\n }\n */\n };\n\n empty(elem)\n {\n this.replaceChildren(elem);\n };\n\n getTag() { return this.tagName.toLowerCase(); };\n\n\n text(str)\n {\n if (str === undefined) return this.innerText;\n else \n {\n this.innerText = str;\n return this;\n }\n };\n\n html(str)\n {\n if (str === undefined) return this.innerHTML;\n else \n {\n this.innerHTML = str;\n return this;\n }\n };\n\n hide() { this.style.display = 'none'; };\n show() { this.style.display = ''; };\n\n /**\n * event and listener handling\n */\n on(evtNames,callback,options)\n {\n const evts = Array.isArray(evtNames) ? evtNames : evtNames.split(' ');\n const evtIds = [];\n \n evts.forEach( evt => {\n\n try {\n const evtId = this.addListener({type:evt,callback:callback,options:options});\n evtIds.push(evtId);\n }\n catch (err)\n {\n console.log('Error adding event listener',err);\n }\n \n });\n\n return Array.isArray(evtNames) ? evtIds : evtIds[0];\n };\n \n \n off(evtNames,callback,options)\n {\n //try to find the key and remove it that way\n const evts = Array.isArray(evtNames) ? evtNames : evtNames.split(' ');\n \n evts.forEach( evt => {\n \n const listenerKeys = this.getListenerKeys({type:evt,callback:callback,options:options});\n\n if (listenerKeys.length > 0)\n {\n listenerKeys.forEach( key => this.offByKey(key) );\n }\n else\n {\n this.removeEventListener(evt,callback,options);\n }\n \n });\n\n };\n\n offByKey(evtKeys)\n {\n const keys = Array.isArray(evtKeys) ? evtKeys : [evtKeys];\n \n keys.forEach( key => {\n\n if ( this.#listeners.has(key) )\n {\n const l = this.#listeners.get(key);\n\n this.removeEventListener(l.type,l.callback,l.options);\n this.#listeners.delete(key);\n }\n });\n };\n\n offAll()\n {\n for (const [key,value] of this.#listeners)\n {\n this.offByKey(key);\n }\n \n this.#listeners.clear();\n };\n\n once(evtNames,callback)\n {\n //try to find the key and remove it that way\n const evts = Array.isArray(evtNames) ? evtNames : evtNames.split(' ');\n const evtIds = [];\n \n evts.forEach( evt => {\n\n //get the listener key first, so we can remove this listener from the key once its fired\n const params = {\n type:evt, \n options:{once:true},\n key: this.nextListenerKey(),\n };\n\n //our callback wrapper to handle listener cleanup\n params.callback = (event) => {\n callback(event);\n this.#listeners.delete(params.key);\n };\n \n const evtId = this.addListener(params);\n evtIds.push(evtId);\n });\n\n return Array.isArray(evtNames) ? evtIds : evtIds[0];\n };\n\n one(evtNames,callback)\n {\n return this.once(evtNames,callback);\n };\n\n nextListenerKey()\n {\n this.#listenerId++;\n return `e${this.#listenerId}`;\n };\n\n getListenerKeys(params)\n {\n const listenerKeys = [];\n\n //just passed an event type, so we're removing everything with that type\n if (params?.callback === undefined)\n {\n for (const [key,value] of this.#listeners)\n {\n if (params?.type == value?.type) listenerKeys.push(key);\n }\n }\n else\n {\n for ( const [key,value] of this.#listeners )\n {\n if (params?.type == value?.type && params?.callback === value?.callback && params?.options == value?.options)\n {\n listenerKeys.push(key);\n break;\n }\n }\n }\n\n return listenerKeys;\n }\n\n addListener(params)\n {\n try { \t \n \n this.addEventListener(params.type,params.callback,params.options);\n \n const key = params?.key || this.nextListenerKey();\n delete(params.key);\n \n this.#listeners.set( key, params );\n return key;\n }\n catch (err)\n {\n console.log('Error adding event listener',err);\n }\n };\n \n trigger(evtNames,data)\n {\n const evts = Array.isArray(evtNames) ? evtNames : evtNames.split(' ');\n\n evts.forEach( evt => {\n\n const params = data || {};\n const event = new CustomEvent(evt,{detail:params});\n\n this.dispatchEvent(event);\n });\n \n return this;\n };\n};\n\ncustomElements.define('wd-element',WDElement);\n", "\nimport WDElement from '../wd-element'\nimport WDButton from '../button/wd-button.js';\n\nexport default class WDDropdown extends WDElement {\n\n\t#element = null;\n\t#items = [];\n\n\tconstructor()\n\t{\n\t\tsuper().classList.add('dropdown-menu');\n\t\tthis.setAttribute('role','menu');\n\t\treturn this;\n\t};\n\n\t//accessors\n\titems() { return this.#items; };\n\t\t\t\t\t\n\treset() \n\t{ \n\t\tthis.empty(); \n\t\tthis.#items = [];\n\t\treturn this;\n\t};\n\n\taddDivider()\n\t{\n\t\tconst d = wdc('
');\n\t\tthis.append(d);\n\t\treturn d;\n\t};\n\n\taddPrefix(obj)\n\t{\n\t\tconst prefix = wdc('
');\n\t\tprefix.on('click',(evt) => evt.preventDefault() );\n\n\t\tthis.prepend(prefix);\n\t\t\n\t\tif (obj) prefix.append(obj);\n\t\t\n\t\treturn prefix;\n\t};\n\n\tadd(title)\n\t{\n\t\tconst item = this.insertItemAt( this.#items.length );\n\t\tif (title) item.label(title);\n\n\t\treturn item;\n\t};\n\n\tcreateItem()\n\t{\n\t\tconst item = new WDButton();\n\t\titem.classList.remove('btn');\n\t\titem.classList.add('dropdown-item');\n\t\t\n\t\treturn item;\n\t};\n\n insertItemAt(idx)\n {\n const item = this.createItem(); //WDDropdownItem();\n\n if (idx == 0) \n {\n this.prepend(item);\n this.#items.unshift(item);\n }\n else if (idx < this.#items.length)\n {\n \tthis.#items[idx].before(item);\n this.#items.splice(idx,0,item);\n }\n else\n {\n this.append(item);\n this.#items.push(item); \n }\n\n return item;\n };\n\n replaceItemAt(idx)\n { \n const item = this.createItem(); //DropdownItem();\n\t\n if (idx < this.#items.length)\n {\n const r = this.#items[idx];\n\n\t\t\tr.before(item);\n r.remove();\n\n this.#items[idx] = item;\n }\n else\n {\n this.append(item);\n this.#items.push(item); \n }\n\n return item;\n };\n\n removeItemAt(idx)\n {\n if (typeof(this.#items[idx]) != 'undefined')\n {\n this.#items[idx].remove();\n this.#items.splice( idx, 1);\n }\n };\n\n removeItem(item)\n {\n \tthis.removeItemAt( item.getIndex() );\n\t};\n\n removeItems(arr)\n {\n arr.forEach( item => {\n\t \tthis.removeItemAt( item.getIndex() );\n\t\t});\n };\n\n\tdropup()\n\t{\n\t\tthis.parentElement.classList.add('dropup');\n\t};\n\n\ttoggle()\n\t{\n\t\tconst instance = bootstrap.Dropdown.getOrCreateInstance( this.previousElementSibling );\n\t\tif (instance) instance.toggle();\n\t};\n\n\tshow()\n\t{\n\t\tconst instance = bootstrap.Dropdown.getOrCreateInstance( this.previousElementSibling );\n\t\tif (instance) instance.show();\n\t};\n\n\thide()\n\t{\n\t\tconst instance = bootstrap.Dropdown.getOrCreateInstance( this.previousElementSibling );\n\t\tif (instance) instance.hide();\n\t};\n\t \n};\n\ncustomElements.define('wd-dropdown',WDDropdown);\n\n", "\nimport WDElement from '../wd-element.js'\nimport WDDropdown from '../dropdown/wd-dropdown';\n\nexport default class WDButton extends WDElement {\n\n\t#label = null;\n\t#loading = null;\n\t#icon = null;\n\t#suffix = null;\n\t#curState = {};\n\t#wrapper = null;\n\t#dropdown = null;\n\t#caret = null;\n\t#config = {\n\t\tindicator: 'fa-solid fa-caret-down',\n\t\tsplit: false\n\t};\n\t\n\tconstructor(params)\n\t{\n\t\tsuper().setAttribute('type','button');\n\n\t\tthis.classList.add('btn');\n\t\tthis.addEventListener('click', this.checkDisabled);\n\n\t\tif (params !== undefined)\n\t\t{\n\t\t\tif (typeof(params) == 'string')\n\t\t\t{\n\t\t\t\tthis.#config.label = params;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t//some custom types for quick-setting button configs\n\t\t\t\tif (params?.type)\n\t\t\t\t{\n\t\t\t\t\tif (params.type == 'save')\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.#config.label = 'Save';\n\t\t\t\t\t\tthis.#config.fasIcon = 'save';\n\t\t\t\t\t\tthis.#config.theme = 'success';\n\t\t\t\t\t}\n\n\t\t\t\t\tif (params.type == 'delete')\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.#config.label = 'Delete';\n\t\t\t\t\t\tthis.#config.fasIcon = 'trash';\n\t\t\t\t\t\tthis.#config.theme = 'danger';\n\t\t\t\t\t}\n\n\t\t\t\t\tif (params.type == 'add')\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.#config.label = 'Add';\n\t\t\t\t\t\tthis.#config.fasIcon = 'add';\n\t\t\t\t\t\tthis.#config.theme = 'primary';\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t//this allows for specific overwriting of the above types\n\t\t\t\tObject.assign(this.#config,params);\n\t\t\t}\n\t\t}\n\t};\n\t\n\tconnectedCallback()\n\t{\n\t\tsuper.connectedCallback();\n\n\t\tconst cfg = this.#config;\n\t\t\n\t\tif (cfg?.label && !cfg?.title) cfg.title = cfg.label;\n\t\tif (cfg?.fasIcon) cfg.icon = `fa-solid fa-${cfg.fasIcon}`;\n\t\tif (cfg?.farIcon) cfg.icon = `fa-solid fa-${cfg.farIcon}`;\n\n\t\t//setup the button\t\t\n\t\tif (cfg?.title) this.title(cfg.title);\n\t\tif (cfg?.label) this.label(cfg.label);\n\t\tif (cfg?.icon) this.icon(cfg.icon);\n\t\tif (cfg?.theme) this.theme(cfg.theme);\n\t\tif (cfg?.size) this.size(cfg.size);\n\t\tif (cfg?.click) this.on('click',cfg.click);\n\t};\n\t\n\tlabel(str)\n\t{\n\t\tif (!this.#label)\n\t\t{\n\t\t\tthis.#label = wdc('');\n\n\t\t\tif (this.#icon) this.#icon.after(this.#label);\n\t\t\telse this.prepend(this.#label);\n\t\t}\n\n\t\tif (str !== undefined)\n\t\t{\n\t\t\tthis.#label.replaceChildren();\n\t\t\tif (str !== null) this.#label.append(str);\n\t\t}\n\t\t\n\t\treturn this.#label;\n\t};\n\n\ttitle(val)\n\t{\n\t\tif (val !== 'undefined')\n\t\t{\n\t\t\tthis.setAttribute('title',val);\n\t\t\treturn this;\n\t\t}\n\t\telse\n\t\t{\n\t\t\treturn this.getAttribute('title');\n\t\t}\n\t};\n\t\n\ttext = this.label;\n\t\n\ticon(str)\n\t{\n\t\tif (!this.#icon)\n\t\t{\n\t\t\tthis.#icon = wdc('');\n\t\t\tthis.prepend(this.#icon);\n\t\t}\n\n\t\tif (typeof(str) == 'undefined') return this.#icon;\n\t\telse\n\t\t{\n\t\t\tconst iconClasses = Array.isArray(str) ? str : str.split(' ');\n\t\t\ticonClasses.push('icon');\n\t\t\t\n\t\t\tthis.#icon.setAttribute('class','');\n\t\t\tthis.#icon.classList.add(...iconClasses);\n\n\t\t\treturn this;\n\t\t}\n\n\t};\n\n\tfasIcon(iconName)\n\t{\n\t\treturn this.icon(`fa-solid fa-${iconName}`);\n\t};\n\n\tfarIcon(iconName)\n\t{\n\t\treturn this.icon(`fa-regular fa-${iconName}`);\n\t};\n\n\ttheme(str)\n\t{\n\t\tthis.classList.remove('btn-clear','btn-primary','btn-secondary','btn-success','btn-warning','btn-danger');\n\t\tthis.classList.add(`btn-${str}`);\n\n\t\treturn this;\n\t};\n\n\tsize(str)\n\t{\n\t\tthis.classList.remove('btn-sm','btn-md','btn-lg');\n\t\tthis.classList.add(`btn-${str}`);\n\n\t\treturn this;\n\t};\n\n\tsuffix(str)\n\t{\n\t\tif (!this.#suffix)\n\t\t{\n\t\t\tthis.#suffix = wdc('');\n\t\t\tthis.append(this.#suffix);\n\t\t}\n\n\t\tif (typeof(str) == 'undefined') return this.#suffix;\n\t\telse\n\t\t{\n\t\t\tthis.#suffix.setAttribute('class',str);\n\t\t\treturn this;\n\t\t}\n\n\t};\n\n\n\tbusy(mode)\n\t{\n\t\tif (mode === true && !this.disabled )\n\t\t{\n\t\t\t//disable the button\n\t\t\tthis.disabled = true;\n\t\t\n\t\t\tif (this.#icon)\n\t\t\t{\n\t\t\t\tthis.#curState.icon = this.#icon.getAttribute('class');\n\t\t\t\tthis.icon('fa-solid fa-circle-notch fa-spin');\n\t\t\t}\t\n\t\t}\n\t\telse if ( this.disabled )\n\t\t{\n\t\t\tthis.disabled = false;\n\t\t\tif (this.#icon) this.icon(this.#curState.icon);\n\t\t\tthis.#curState = {};\n\t\t}\n\t\n\t};\n\t\n // Property getter\n get disabled() {\n return this.hasAttribute('disabled');\n }\n\n // Property setter\n set disabled(value) {\n if (value) {\n this.setAttribute('disabled', '');\n } else {\n this.removeAttribute('disabled');\n }\n }\n\n\tcheckDisabled(event) \n\t{\n if (this.disabled) {\n // Prevent default action and stop propagation if disabled\n event.preventDefault();\n event.stopPropagation();\n }\n }\n\n /**\n \t* dropdown-related methods\n \t*/\n\twrapper() { return this.#wrapper; };\n\tdropdown() { \n\t\tif (!this.#dropdown) this.addDropdown();\n\t\treturn this.#dropdown; \n\t};\n\t\n\taddDropdown()\n\t{\n\t\t//insert the wrapper before our button, then add the button to the wrapper\n\t\tthis.#wrapper = wdc('
');\n\n\t\t//make the dropdown\n\t\tthis.#dropdown = new WDDropdown();\n\n\t\t//put the wrapper after the button, then move the button into the wrapper\n\t\tthis.after(this.#wrapper);\n\t\tthis.#wrapper.append(this);\n\t\tthis.#wrapper.append(this.#dropdown);\n\n\t\tconst icons = Array.isArray(this.#config?.indicator) ? this.#config.indicator : this.#config.indicator.split(' ');\n\t\tconst ddIcon = wdc('');\n\t\tddIcon.classList.add(...icons);\n\n\t\t//if it's a split button, make a new button to manage the dropdown\n\t\tif (this.#config.split == true)\n\t\t{\n\t\t\tconst splitBtn = new WDButton(ddIcon).classList.add( this.getAttribute('class') );\n\t\t\tsplitBtn.classList.add('dropdown-toggle','dropdown-toggle-split');\n\t\t\tsplitBtn.setAttribute(\"data-bs-toggle\",\"dropdown\");\n\t\t\tthis.#wrapper.append(splitBtn);\n\t\t}\n\t\telse\n\t\t{\n\t\t\t//setup the element to activate us when clicked\n\t\t\tthis.append(ddIcon);\n\t\t\tthis.classList.add(\"dropdown-toggle\");\n\t\t\tthis.setAttribute(\"data-bs-toggle\",\"dropdown\");\n\t\t}\n\n\t\tthis.on('show.bs.dropdown',ev => this.trigger('dropdown:show') );\n\t\tthis.on('shown.bs.dropdown',ev => this.trigger('dropdown:shown') );\n\t\tthis.on('hide.bs.dropdown',ev => this.trigger('dropdown:hide') );\n\t\tthis.on('hidden.bs.dropdown',ev => this.trigger('dropdown:hidden') );\n\n\t};\n\n};\n\ncustomElements.define('wd-button',WDButton);\n", "\nimport WDButton from './wd-button'\n\nexport default class WDToggleButton extends WDButton {\n\n constructor(initText)\n {\n super(initText).classList.add('wd-toggle-button');\n \n this.icon('fa-regular fa-square'); \n this.on('click',() => this.toggle() );\n };\n \n toggle()\n {\n if (this.icon().classList.contains('fa-check-square'))\n {\n this.icon('fa-regular fa-square');\n }\n else\n {\n this.icon('fa-regular fa-check-square');\n }\n };\n \n checked(check)\n {\n if (typeof(check) == 'undefined')\n {\n return this.icon().classList.contains('fa-check-square');\n }\n else\n {\n if (check === true) this.icon('fa-regular fa-check-square');\n else this.icon('fa-regular fa-square');\n }\n \n };\n \n};\n\ncustomElements.define('wd-toggle-button',WDToggleButton);\n", "\nimport WDElement from '../wd-element'\nimport { WDProxyArray } from '../../core/wd-proxy'\nimport WDUtil from '../../core/wd-util'\n\nexport default class WDChecklist extends WDElement {\n\n #options = [];\n #items = [];\n #values = [];\n #valueLabels = [];\n \n /**\n * default config\n */\n\t#config = { \n\t options: [], \n\t multiple: false,\n\t allow_clear: true,\n\t separator: ', ',\n\t templates: {\n\t \tlabel: '${label}',\n\t \tvalue: '${value}'\n\t\t},\n\t icons: {\n\t unchecked: 'fa-solid',\n\t checked: 'fa-solid fa-check',\n\t //unchecked: 'fa-regular fa-square',\n\t //checked: 'fa-regular fa-square-check'\n }\n };\n \n constructor(cfg)\n {\n super('
    ').classList.add('wd-checklist');\n\n if (cfg !== undefined) WDUtil.merge(this.#config,cfg);\n\n //convert legacy map config setting to templates\n if (cfg?.map?.label !== undefined) this.#config.templates.label = '${' + cfg.map.label + '}';\n if (cfg?.map?.value !== undefined) this.#config.templates.value = '${' + cfg.map.value + '}';\n\n //setup our base proxy for our option storage\n this.#options = new WDProxyArray([]);\n this.#values = new WDProxyArray([]);\n\n //setup an event watcher for proxy set if we have a content handler\n this.#options.on('set', evtData => this.handleSet(evtData) );\n this.#options.on('delete',evtData => this.handleDelete(evtData) );\n\n this.#values.on('set', evtData => this.handleValueSet(evtData) );\n this.#values.on('delete', evtData => this.handleValueDelete(evtData) );\n\n //passed some options, add to our proxy array\n if ( this.#config.options.length > 0)\n {\n \tthis.remap(this.#config.options);\n \tthis.#options.replace(this.#config.options);\n\t\t}\n\n\t\t//if passed a fetch\n\t\tif (this.#config.fetch) this.fetchOptions();\n\t\telse setTimeout( () => this.trigger('options:set'), 1);\n\t\t\n //\t\t\t\t\n\t\tif ( this.#config.value !== undefined ) this.val(this.#config.value);\n\t};\n\n\toptions() { return this.#options; };\t\n items() { return this.#items; };\n values() { return this.#values; };\n \n\tfetch(f) \n\t{ \n\t if ( f === undefined) return this.#config?.fetch; \n\t else\n\t {\n\t this.#config.fetch = f;\n\t return this;\n }\n };\n \t\n\tfetchOptions()\n\t{\n\t //make sure we have a request uri set\n\t if ( this.#config.fetch.getURI().includes('${') )\n\t {\n\t return new Promise( (resolve,reject) => {\n\n\t\t\t\tthis.setOptions([]);\n resolve(true);\n\t });\n\n\t }\n\t else\n\t {\n \t\treturn this.#config.fetch.req().then( results => {\n if (results instanceof Array) this.setOptions(results);\n });\n }\n };\n\n remap(options)\n {\n \toptions.forEach( item => {\n \t\n \t\tif (item?.value === undefined)\n \t\t{\n\t\t\t\tconst stringVal = this.#config.templates.value.interpolate(item);\n\t\t \t\tconst numVal = parseFloat(stringVal);\n\t\t item.value = !isNaN(numVal) && isFinite(stringVal) ? numVal : stringVal;\n\t\t\t}\n\t\t\t\n\t\t\tif (item?.label === undefined)\n\t\t\t{\n\t\t\t\titem.label = this.#config.templates.label.interpolate(item);\n\t\t\t}\n\t\t});\n\t};\n\n setOptions(newOptions)\n {\n \tthis.remap(newOptions);\n this.#options.replace(newOptions);\n this.trigger('options:set');\n };\n\n\tremoveItemAt(idx)\n\t{\n\t\tthis.#items[idx].remove();\n\t\tthis.#items.splice(idx,1);\n\t};\n\n replaceItemAt(item,idx)\n { \t\n if (idx < this.#items.length)\n { \t\n const r = this.#items[idx];\n \t\n item.insertBefore(r);\n r.remove();\n\n this.#items[idx] = item;\n } \t\n else\t\n { \t\n this.append(item);\n this.#items.push(item);\n } \t\n \t\t\n return item;\n }; \t\n\n handleSet(evtData)\n {\n /**\n create/replace a row with this index\n */\n const idx = evtData.property;\n const data = evtData.value; \n\n //if the property isn't numeric, then it's an internal array property we don't want\n if (parseInt(idx) != idx) return;\n\n //if data is undefined, the item is being removed\n if ( data === undefined )\n {\n this.removeItemAt(idx);\n }\n //not sure this handles replacing a row\n else\n {\n //create the new item\n\t\t\tconst item = new WDChecklistItem(data,{\n\t\t\t\ticons: this.#config.icons, \n\t\t\t});\n\n\t\t\tthis.replaceItemAt(item,idx);\n\n\t\t\t//check it if set\n\t\t\tif (this.#values.indexOf(data.value) != -1) \n\t\t\t{\n\t\t\t item.checked(true);\n }\n \n\t\t\t//watch for changes\n\t\t\titem.on('click',(evt) => {\n\t\t\t this.handleItemClick(item,evt);\n\t\t\t});\n }\n }; \n\n handleDelete(evtData)\n {\n const idx = evtData.property;\n\n //if the property isn't numeric, then it's an internal array property we don't want\n if (parseInt(idx) != idx) return;\n\n this.removeItemAt(idx);\n };\n\n\n\thandleValueSet(evtData)\n\t{\n\t if (evtData.property == 'length') return;\n\n\t this.checkValueRows();\n this.trigger('value:set');\n };\n\n handleValueDelete(evtData)\n {\n this.checkValueRows();\n this.trigger('value:set');\n };\n\n checkValueRows()\n {\n\t const values = this.#values.toArray();\n\n this.#items.forEach( item => {\n \n let isSet = false;\n \n values.some( v => {\n if (v == item.value())\n {\n isSet = true;\n return true;\n }\n });\n\n item.checked( isSet );\n });\n };\n\n handleItemClick(item,evt)\n {\n const itemVal = item.value();\n\n if (this.#config.multiple == true)\n {\n const idx = this.#values.indexOf(itemVal);\n\n if (idx == -1) this.#values.push(itemVal);\n else this.#values.splice(idx,1);\n }\n else\n {\n let newVal = [];\n \n //if allow clear is set, allow clearing the value or updating\n if (this.#config.allow_clear == true)\n {\n const idx = this.#values.indexOf(itemVal);\n newVal = (idx == -1) ? [itemVal] : [];\n }\n else\n {\n newVal = [itemVal];\n }\n\n this.#values.replace(newVal);\n }\n\n if (evt)\n {\n this.trigger('change');\n this.trigger('input');\t\t\t\t//not sure we need this one\n }\n \n };\n\n\tval(val)\n\t{\n\t if ( val === undefined )\n\t {\n return this.getVal();\t \n\t }\n\t else\n\t {\n\t this.setVal(val);\n\t return this;\n }\n\t};\n \n\tgetVal()\n\t{\n\t if (this.#config.multiple == true)\n {\n return this.#values;\n }\n else\n {\n return (this.#values.length > 0) ? this.#values[0] : null;\n }\n\t};\n\n\tsetVal(val,triggerChange)\n\t{\n\t if (val === null) val = [];\n\t else if ( !(val instanceof Array) ) val = [val];\n\n this.#values.replace(val);\n this.trigger('value:set');\n\t};\n\n\tsetValuesFromLabels(lbls)\n\t{\n\t\tconst vals = [];\n\t\n\t\tlbls.forEach( lbl => {\n\t\t\tconst opt = this.#options.find( x => x.label == lbl);\n\n\t\t\tif (opt) vals.push(opt.value);\n\t\t});\n\n\t\tif (vals.length > 0) this.setVal(vals);\n\t};\n\t\n\tsetValFromLabels(lbls)\n\t{\n\t\tif (!Array.isArray(lbls)) lbls = [lbls];\n\n\t\treturn new Promise( (resolve,reject) => {\n\t\t\n\t\t\tif (this.#options.length > 0)\n\t\t\t{\n\t\t\t\tthis.setValuesFromLabels(lbls);\n\t\t\t\tresolve(true);\n\t\t\t}\n\t\t\telse \n\t\t\t{\n\t\t\t\tthis.one('options:set',() => {\n\t\t\t\t\tthis.setValuesFromLabels(lbls);\n\t\t\t\t\tresolve(true);\n\t\t\t\t});\n\t\t\t}\n\t\t\n\t\t});\n\n\t};\n\n\t/**\n\t\t* generates a label based on all the selected values in the list\n\t\t*/\n\tgetValueLabel()\n\t{\n\t\tconst labels = [];\n\n\t\tthis.#values.forEach( v => {\n\t\t\n\t\t\tthis.#options.some( opt => {\n\n\t\t\t\tif ( String(opt.value) == String(v)) \n\t\t\t\t{\n\t\t\t\t\tlabels.push(opt.label);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t});\n\t\t\n\t\t});\n\n\t\t//if our labels are an object, then we used a dom object as a label.\tJust add them to a container\n\t\tif (labels.length > 0)\n\t\t{\n\t\t\tif (typeof(labels[0]) == 'object')\n\t\t\t{\n\t\t\t\tconst container = wdc('
    ');\n\t\t\t\tlabels.forEach( label => container.append( label.clone() ) );\n\t\t\t\treturn container;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\treturn labels.join( this.#config.separator );\n\t\t\t}\n\t\t}\n\t\telse\n\t\t{\n\t\t\treturn null;\n\t\t}\n\t};\n\t\n};\n\ncustomElements.define('wd-checklist',WDChecklist);\n\nclass WDChecklistItem extends WDElement {\n\n\t#icon;\n\t#data;\n\t#icons;\n\t#checked = false;\n\t\n\tconstructor(data,cfg)\n\t{\n\t\tsuper('
  • ').classList.add('wd-checklist-item');\n\n\t\tthis.#icon = wdc('');\n\t\t\n\t\t\n\t\tif (cfg?.icons?.unchecked) \n\t\t{\n\t\t\tconst icons = Array.isArray(cfg.icons.unchecked) ? cfg.icons.unchecked : cfg.icons.unchecked.split(' ');\n\t\t\tthis.#icon.classList.add(...icons);\n\t\t}\n\n\t\tthis.#data = data;\n\t\tthis.#icons = cfg?.icons;\n\n\t\tthis.append( this.#icon );\n\t\tthis.append( this.label() );\n\t};\n\n\tvalue() { return this.#data?.value; };\n\tlabel() { return this.#data?.label; };\n\n\tchecked(val)\n\t{\n\t\tif (val === undefined)\n\t\t{\n\t\t\treturn this.#checked;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthis.#checked = val;\n\t\t\tconst icons = this.#icons;\n\t\t\t\n\t\t\tif (val === true) \n\t\t\t{\n\t\t\t\tthis.#icon.classList.remove( ...icons.unchecked.split(' ') );\n\t\t\t\tthis.#icon.classList.add( ...icons.checked.split(' ') );\n\t\t\t}\n\t\t\telse \n\t\t\t{\n\t\t\t\tthis.#icon.classList.remove( ...icons.checked.split(' ') );\n\t\t\t\tthis.#icon.classList.add( ...icons.unchecked.split(' ') );\n\t\t\t}\n\t\t\t\n\t\t\treturn this;\n\t\t}\t\n\t};\n\n};\n\ncustomElements.define('wd-checklist-item',WDChecklistItem);\n", "\nimport WDElement from '../wd-element';\nimport WDChecklist from './wd-checklist';\nimport WDButton from '../button/wd-button';\nimport WDUtil from '../../core/wd-util';\n\n/**\n\tNote:\n\tThis form can store objects as values, but it uses the labels to search. This\n\tway it matches what a user would type. what they see is the value expected\n\t*/\nexport default class WDDropdownForm extends WDElement {\n\n\t#search = null;\n\t#timeout = null;\n\t#button = null;\n\t#dropdown = null;\n\t#optionsBtn = null;\n\t#checklist = null;\n\t\n\t#config = {\t\t\t\n\t\toptions: [],\n\t\tfetch: null,\n\t\tallow_search: false,\n\t\tallow_clear: true,\n\t\tvalue: null,\n\t\ttemplates: {\n\t\t\tlabel: '${label}',\n\t\t\tvalue: '${value}'\n\t\t},\n\t\tbutton: { \n\t\t\ttext: 'Not Selected',\n\t\t\tprefix: null,\n\t\t\ttheme: '', \n\t\t\tsize: 'md',\n\t\t\ticon: null,\n\t\t\tupdate_text: true,\n\t\t},\n\t\tedit: {\n\t\t\ttext: 'Edit Options',\n\t\t\thandler: null,\n\t\t\ticon: 'fa-solid fa-ellipsis-v'\n\t\t}\n\t};\n\t\t\t\t\n\tconstructor(opts)\n\t{\n\t\t//init\n\t\tsuper().classList.add('wd-dropdown-form','btn-group');\n\n\t\t//merge any passed parameters in\n\t\tif (opts) WDUtil.merge(this.#config,opts);\n\n //setup the button and base dropdown\n \tthis.button();\n\n \t//allows us to update the button text\n\t\tthis.#checklist.on('value:set',() => this.handleValueSet() );\n\t\tthis.#checklist.on('options:set',() => this.handleOptionsSet() );\n\t\t\n\t\tthis.val( this.#config.value );\n\t};\n\n\t\n /** accessors **/\n config() { return this.#config; };\n options() { return this.#checklist.options(); };\n values() { return this.#checklist.values(); };\n items() { return this.#checklist.items(); };\n fetch(f) { return this.#checklist.fetch(f); };\n \n button()\n {\n \tif (!this.#button)\n \t{\n\t \t\tthis.#button = new WDButton();\n\t\n\t \t\t//setup the button aesthetic\n\t \t\tif (this.#config.button.text) this.#button.label(this.#config.button.text);\n\t \t\tif (this.#config.button.title) this.#button.title(this.#config.button.title);\n\t \t\tif (this.#config.button.label) this.#button.label(this.#config.button.label);\n\t\t\tif (this.#config.button.icon) this.#button.icon(this.#config.button.icon);\n\t\t\tif (this.#config.button.theme) this.#button.theme(this.#config.button.theme);\n\t\t\tif (this.#config.button.size) this.#button.size(this.#config.button.size);\n\n\t\t\tif (this.#config?.readonly || this.#config?.button?.disabled) this.#button.disabled = true;\n\n\t\t\tthis.append(this.#button);\n\t\n\t\t\t//setup the dropdown\n\t\t\tthis.dropdown();\n\t\t\tthis.checklist();\n\t\t\t\t\n\t\t\t//setup our other options\n\t\t\tif (this.#config?.edit?.handler) this.setupEdit();\n\t\t\tif (this.#config?.allow_search) this.setupSearch();\n\t\t}\n\n\t\treturn this.#button;\n\t};\n\n\treadonly(mode)\n\t{\n\t\tif (mode == true) this.disable();\n\t\telse this.enable();\n\t};\n\n\tdisable() { this.#button.disabled = true; };\n\tenable() { this.#button.disabled = false; };\n\n /**\n \tsetup dropdown for results\n \t*/\n\tdropdown()\n\t{\n if (!this.#dropdown) \n {\n this.#button.append( wdc('') );\n this.#button.classList.add(\"dropdown-toggle\");\n this.#button.setAttribute(\"data-bs-toggle\",\"dropdown\");\n \n const autoClose = this.#config.multiple == true ? 'outside' : 'true';\n this.#button.setAttribute(\"data-bs-auto-close\",autoClose);\n \n this.#dropdown = wdc('
    ');\n this.append(this.#dropdown);\n\t\t}\n\t\t\n\t\treturn this.#dropdown;\n\t};\n\t\n\tchecklist()\n\t{\n\t\tif (!this.#checklist)\n\t\t{\n\t\t\t//pass relevant options to checklist\n\t\t\tconst cfg = {\n\t\t\t\toptions: this.#config.options,\n\t\t\t\tfetch: this.#config?.fetch,\n\t\t\t\tvalue: this.#config?.value,\n\t\t\t\ticons: this.#config?.icons,\n\t\t\t\ttemplates: this.#config?.templates,\n\t\t\t\tmap: this.#config?.map,\n\t\t\t\tseparator: this.#config?.separator,\n\t\t\t\tallow_clear: this.#config.allow_clear,\n\t\t\t\tmultiple: this.#config.multiple,\n\t\t\t};\n\t\t\t\n\t\t\tthis.#checklist = new WDChecklist(cfg);\n\t\t\tthis.#dropdown.append(this.#checklist);\n\n\t\t\tthis.#checklist.on('value:set',(evt) => this.trigger('value:set',evt) );\n\t\t\tthis.#checklist.on('change',(evt) => this.trigger('change',evt) );\n\t\t\tthis.#checklist.on('input',(evt) => this.trigger('input',evt) );\n\t\t}\n \n return this.#checklist;\n };\n\n\tsetupSearch()\n\t{\n\t\t//setup the dropdown and the search\n\t\tthis.#search = wdc('');\n\t\tthis.dropdown().prepend(this.#search);\n\t\tthis.dropdown().classList.add('search-form-item');\n\n\t\t//keep the change event from propagating up and triggering an onchange event in our form\n\t\tthis.#search.addEventListener('change',(evt) => { evt.stopPropagation(); } );\n\n \t//call our handler, and pass our response fmethod to it\n\t\tthis.#search.addEventListener('input',(evt) => {\n\t\t\tevt.stopPropagation();\n\t\t\tthis.handleKeyUp(evt);\n\t\t} );\n\t};\n\n\t/**\n\t\t*\n\t\t*/\t\n\toptionsBtn()\n\t{\n\t\tif (!this._optionsBtn)\n\t\t{\n\t\t\tthis._optionsBtn = new WDButton().fasIcon('ellipsis-v').theme('clear');\n\t\t\tthis.append(this._optionsBtn);\n\t\t\t\n\t\t\tthis._optionsBtn.dropdown().classList.add('dropdown-menu-end');\n\t\t\tthis.classList.add('dropdown-form-options-group');\n\t\t}\n\t\t\n\t\treturn this._optionsBtn;\n\t};\n\n\t/**\n\t\t*/\t\n\tsetupEdit()\n\t{\n\t\tconst editCfg = this.#config.edit;\n\t\t\n\t\tconst editBtn = this.optionsBtn().dropdown().add(editCfg.text);\n\t\teditBtn.icon(editCfg.icon).classList.add('edit-btn');\n\t\teditBtn.on('click',() => {\n\n\t\t\t//if allowsearch, pass the contents of the search box so the handler can\n\t\t\t//automatically create a new option if they wish\n\t\t\tconst passVal = (this.#config.allow_search == true) ? this.#search.val() : null;\n\n\t\t\t//whatever we spawn, if it triggers an item:updated event then refresh our list\n\t\t\tconst ref = this.#config.edit.handler(passVal); \n\n\t\t\tif (ref) \n\t\t\t{\n\t\t\t\tref.on('items:deleted',(evt) => {\n\n\t\t\t\t\tif (evt?.detail?.item) \n\t\t\t\t\t{\n\t\t\t\t\t\tthis.#checklist.options().some( (opt,idx) => {\n\t\t\t\t\t\t\n\t\t\t\t\t\t\tconst stringVal = this.#config.templates.value.interpolate(opt);\n\t\t\t\t\t\t\tconst numVal = parseFloat(stringVal);\n\t\t\t\t\t\t\tconst optval = !isNaN(numVal) && isFinite(stringVal) ? numVal : stringVal;\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tif ( optVal == evt.detail.item.value() )\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif ( this.val() == evt.detail.item.value() )\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tthis.val('0',true);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tthis.#checklist.options().remove(idx);\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\t\t\t\t\n\t\n\t\t\t\t\t}\n\n\t\t\t\t\tthis.trigger('options:updated');\n\n\t\t\t\t});\n\n\t\t\t\t//refresh our list if we get an items:updated event\n\t\t\t\tref.on('items:updated',(evt) => {\n\n\t\t\t\t\t//update the list then set it as a new value\t\t\t\t\t\n\t\t\t\t\tif (this.#config.fetch)\n\t\t\t\t\t{\n\t\t\t\t\t\tthis.#checklist.fetchOptions().then( () => {\n\n\t\t\t\t\t\t\tthis.trigger('options:updated');\n\n\t\t\t\t\t\t\tif (evt?.detail?.item?.value() !== undefined)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tthis.val(evt.detail.item.value(),true);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\t//otherwise just push the new value onto the end of the list\n\t\t\t\t\telse if (evt?.detail?.item?.value !== undefined) \n\t\t\t\t\t{\n\t\t\t\t\t\t\tthis.#checklist.options().unshift( evt.detail.item );\n\t\t\t\t\t\t\tthis.trigger('options:updated');\n\n\t\t\t\t\t\t\tsetTimeout( () => {\n\t\t\t\t\t\t\t\tthis.val(evt.detail.item.value(),true);\n\t\t\t\t\t\t\t},10);\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t});\n\n\t\t\t\t//select the newly created value\n\t\t\t\tref.on('item:selected',(evt) => {\n\t\t\t\t\tconst newItem = evt.detail.item;\n\t\t\t\t\tthis.val( evt.detail.item, true );\n\t\t\t\t});\n\t\t\t}\n\n\t\t});\n\n\t};\n\n\t/**\n\t\thandle keup responses from text box\n\t\t*/\n handleKeyUp()\n {\n \t//if passed a uri, process it\n \tif (this.#timeout) clearTimeout(this.#timeout);\n \tthis.#timeout = setTimeout( () => this.filterOptions() ,250);\n\t};\n\n /**\n \t*\n \t*/\n fetchOptions() \n { \n \treturn this.#checklist.fetchOptions();\n\t};\n\n\tsetFetchURI(uri)\n\t{\n\t\tthis.fetch().setBaseURI(uri);\n\t\tthis.fetchOptions();\n\t};\n\n\tsetOptions(newOptions)\n\t{\n\t\tthis.#checklist.setOptions(newOptions);\n\t};\n\n /**\n \t*\n \t*/\n\tfilterOptions()\n\t{\n\t\tconst ss = this.#search.value.toLowerCase();\n\n\t\tthis.#checklist.items().forEach( (item,idx) => {\n\n\t\t\tif (ss.length == '0' || item.label().toLowerCase().indexOf(ss) != -1)\n\t\t\t{\n \titem.style.display = 'flex';\n\t\t\t} \n\t\t\telse\n\t\t\t{\t\n\t\t\t\titem.style.display = 'none';\n\t\t\t}\t\n \t\t\n\t\t});\t\n\t};\n\t\n /**\n \t*\n \t*/\n\tval(val)\n\t{\n\t\treturn this.#checklist.val(val);\n\t};\n\n\tsetValFromLabels(lbls)\n\t{\n\t\treturn this.#checklist.setValFromLabels(lbls);\n\t};\n\t\n /**\n \t*\n \t*/\n\thandleValueSet()\n\t{\n\t\tthis.updateButtonText();\n\t};\n\n\thandleOptionsSet()\n\t{\n\t\tthis.trigger('options:set');\n\t\tthis.updateButtonText();\n\t};\n\t\n /**\n \t*\n \t*/\n\tupdateButtonText()\n\t{\n\t\tconst btnCfg = this.#config.button;\n\n\t\t//update the button text\n\t\tif ( btnCfg?.update_text === true)\n\t\t{\n\t\t\tconst valLabel = this.#checklist.getValueLabel();\n\t\t\tconst lbl = valLabel || btnCfg.text;\n\t\t\tconst btnLabel = this.button().label();\n\t\t\tbtnLabel.replaceChildren();\n\n\t\t\tif (this.#config?.button.prefix) \n\t\t\t{\n\t\t\t\tconst lblPrefix = wdc('');\n\t\t\t\tlblPrefix.append(this.#config.button.prefix);\n\t\t\t\tlblPrefix.classList.add('label-prefix');\n\n\t\t\t\tbtnLabel.append(lblPrefix);\n\t\t\t\tbtnLabel.append(lbl);\n\t\t\t}\n\t\t\telse\n\t\t\t{\t\t\t\t\n\t\t\t\tbtnLabel.append(lbl);\n\t\t\t}\n\n\t\t}\n\t\t//use the function to udpate the button text if given one\n\t\telse if (typeof(btnCfg.update_text) == 'function')\n\t\t{\n\t\t\tthis.button().label( btnCfg.update_text( this.val() ) );\n\t\t}\n\t};\n\n};\n\ncustomElements.define('wd-dropdown-form',WDDropdownForm);\n", "\nimport WDDropdownForm from './wd-dropdown-form';\n\n/**\n\tNote:\n\tThis form can store objects as values, but it uses the labels to search. This\n\tway it matches what a user would type. what they see is the value expected\n\t*/\nexport default class WDDropdownMultiForm extends WDDropdownForm {\n\n\tconstructor(cfg)\n\t{\n\t\tif (cfg) cfg.multiple = true;\n\t\tsuper(cfg);\n\t};\n\n};\n\ncustomElements.define('wd-dropdown-multi-form',WDDropdownMultiForm);\n", "\nimport WDDropdownForm from './wd-dropdown-form';\n\n/**\n\t* just like WDDropdownForm except the value is stored as an object\n\t*/\nexport default class WDDropdownObjectForm extends WDDropdownForm {\n\n\n\tgetObjectFromValue()\n\t{\n\t\tlet retObj = null;\n\n\t\tif (this.values().length > 0)\n\t\t{\n\t\t\tthis.values().forEach( val => {\n\t\t\t\n\t\t\t\tthis.options().some( opt => {\n\t\t\t\t\n\t\t\t\t\tif (opt.value == val)\n\t\t\t\t\t{\n\t\t\t\t\t\tretObj = opt;\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t\n\t\t\t\t});\n\t\t\t});\n\t\t}\n\t\t\n\t\treturn retObj;\n\t\t\n\t};\n\n /**\n \t*\n \t*/\n\tval(val,evt)\n\t{\n\t\tif (typeof(val) == 'undefined') \n\t\t{\n\t\t\treturn this.getObjectFromValue();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tsuper.val( val.value );\n\t\t\treturn this;\n\t\t}\n\n\t};\n\n};\n\ncustomElements.define('wd-dropdown-object-form',WDDropdownObjectForm);\n", "\nimport WDDropdownMultiForm from './wd-dropdown-multi-form';\n\n/**\n\tNote:\n\tThis form can store objects as values, but it uses the labels to search. This\n\tway it matches what a user would type. what they see is the value expected\n\t*/\nexport default class WDDropdownObjectMultiForm extends WDDropdownMultiForm {\n\n getObjectsFromValue()\n {\n let retObj = [];\n\n if (this.values().length > 0)\n {\n this.values().forEach( val => {\n \n \tthis.options().some( opt => {\n\t\n if (opt.value == val)\n {\n \tretObj.push( opt );\n return true;\n }\n \n });\n });\n }\n \n return retObj;\n };\n\n\tval(val)\n\t{\n\t\tif (typeof(val) == 'undefined') \n\t\t{\n\t\t\treturn this.getObjectsFromValue();\n\t\t}\n\t\telse\n\t\t{\n\t\t\tconst vals = [];\n\t\t\tval.forEach( v => vals.push( v.value ) );\n\t\t\t\n\t\t\tsuper.val(vals);\n\t\t\t\n\t\t\treturn this;\n\t\t}\n\t};\n\n};\n\ncustomElements.define('wd-dropdown-object-multi-form',WDDropdownObjectMultiForm);\n", "\nimport WDElement from '../wd-element';\nimport WDButton from '../button/wd-button';\nimport WDUtil from '../../core/wd-util';\n\nexport default class WDNavbarMenu extends WDElement {\n\n\t#toggleButton;\n\t#wrapper;\n\t#list;\n\t#config = { \n\t\ttoggle_icon: 'fa-solid fa-bars',\n\t\tcollapse: { mode:false }\n\t};\n\t\n\tconstructor(cfg)\n\t{\n\t\tsuper().classList.add('wd-navbar-menu');\n\t\t\n\t\tthis.#wrapper = wdc('
    ');\n\t\tthis.#list = wdc('
      ');\n\t\t\n\t\tthis.#wrapper.append(this.#list);\n\t\tthis.append(this.#wrapper);\n\n\t\tif (cfg) WDUtil.merge(this.#config,cfg);\n\n\t\tif (this.#config.collapse?.mode) this.collapsible(this.#config.collapse);\n\n\t\treturn this;\n\t};\n\n\tlist() { return this.#list; };\n\titems() { return this.list().children; };\n\titemArray() { return Array.from(this.items()); };\n\t\n\tcollapsible(collapseCfg)\n\t{\n\t\tconst mode = collapseCfg?.mode || 'menu';\t\t\t\t\t\t\t//modes: 'hide', 'bar', 'icon', 'menu'\n\t\tconst breakpoint = collapseCfg?.breakpoint || 'md';\t\t//eventually look for a WDConfig default breakpoint\n\n\t\tconsole.log('Mode',mode,breakpoint);\n\n\t\tthis.classList.add('collapsible');\t\t\n\t\tthis.setAttribute('data-collapse-mode',mode);\n\t\tthis.setAttribute('data-collapse-breakpoint',breakpoint);\n\t\t\n\t\tthis.toggleButton();\n\t\t\n\t\treturn this;\n\t};\n\n\ttoggleButton()\n\t{\n\t\tif (!this.#toggleButton)\n\t\t{\n\t\t\tthis.#toggleButton = new WDButton({icon:this.#config.toggle_icon});\n\t\t\tthis.#toggleButton.classList.add('wd-navbar-menu-toggler');\n\t\t\tthis.#toggleButton.on('click',() => this.classList.toggle('is-open') );\n\n\t\t\tthis.prepend(this.#toggleButton);\n\t\t}\n\t\t\n\t\treturn this.#toggleButton;\n\t};\n\n\t//\t\n\tadd(cfg)\n\t{\n\t\tconst btn = new WDButton(cfg);\n\n\t\tconsole.log('Navbar Menu Add',cfg,btn);\n\n\t\t//add to our common container\n\t\tthis.addItem(btn);\n\t\t\n\t\treturn btn;\n\t};\n\n\taddAtIndex(title,cfg,idx)\n\t{\n\t\tconst btn = new WDButton(title,cfg);\n\t\tconst item = this.addItem(btn);\n\t\t\n\t\t//add to our common container\n\t\tthis.addItemAtIndex(item,idx);\n\t\t\n\t\treturn btn;\n\t};\n\n\taddDivider()\n\t{\n\t\tconst item = wdc('
    • ');\n\t\tthis.#list.append(item);\n\t};\n\t\n\taddItem(obj)\n\t{\n\t\tconst item = wdc('
    • ');\n\t\tif (obj) item.append(obj);\n\n\t\t//add to object\n\t\tthis.#list.append(item);\n\t\t\n\t\treturn item;\n\t};\n\t\n\taddText(str)\n\t{\n\t\tconst item = this.addItem(str);\n\t\titem.classList.add('wd-navbar-menu-text');\n\n\t\treturn item;\n\t};\n\n\tempty()\n\t{\n\t\tthis.#list.replaceChildren();\n\t\treturn this;\n\t};\n\n\taddItemAtIndex(item,idx)\n\t{\n\t\tconst items = this.#list.querySelectorAll('.wd-navbar-menu-item');\n\n\t\tif (idx == 0) this.#list.prepend(item);\n\t\telse if (idx == items.length - 1) this.#list.append(item);\n\t\telse items[idx].before(item);\n \n\t\treturn item;\n\t};\n\n\tremoveItem(item)\n\t{\n\t\titem.remove();\n\t};\n\n\t/** remove menu item at specified index **/\n\tremoveAtIndex(idx)\n\t{\n\t\tconst items = this.#list.querySelectorAll('.wd-navbar-menu-item');\n\t\twdc(items[idx]).remove();\n\t};\n\n\t/** remove all items after the passed element **/\n\tremoveAfterItem(item)\n\t{\n\t\tconst items = this.#list.querySelectorAll('.wd-navbar-menu-item');\n\t\tconst idx = items.indexOf( item.element() );\n\t\tthis.removeAfterIndex(idx);\n\t};\n\n /** remove all items after the passed index **/\n removeAfterIndex(idx)\n {\n idx = parseInt(idx) + 1;\n\t\tconst items = this.#list.querySelectorAll('.wd-navbar-menu-item');\n\n\t\tfor (const itemIdx in items)\n\t\t{\n\t\t\tif (itemIdx > idx) items[itemIdx].remove();\n\t\t}\n };\n\n};\n\ncustomElements.define('wd-navbar-menu',WDNavbarMenu);\n", "\nimport WDNavbarMenu from './wd-navbar-menu';\n\nexport default class WDNavbarMenuPager extends WDNavbarMenu\n{\n\t#page = 0;\n\t#count = 0;\n\t#max = -1;\n\t#pages = [];\n\t#previous = null;\n\t#next = null;\n\t#first = null;\n\t#last = null;\n\t#start = null;\n\t#end = null;\n\n\tconstructor(cfg) {\n\t\n\t\tsuper(cfg).classList.add('wd-navbar-pager');\n\t\treturn this;\t\t\n\t};\n\t\n\t//opts {total,limit,max#pages}\n\tload(opts) \n\t{\n\t\t//this.empty();\n\t\tthis.#pages = [];\n\n\t\t//figure out how many pages we have\n\t\tthis.#count = parseFloat(opts.count) / parseFloat(opts.limit);\n if (parseInt(this.#count) != this.#count) this.#count = parseInt(this.#count) + 1;\n\n\t\tif (opts.max_pages) this.#max = opts.max_pages;\n\n\t\tthis.draw();\n\t};\n\n\tdraw()\n\t{\n\t\tthis.empty();\n\t\tthis.setupEndpoints();\n\n\t\tthis.#first = this.add().fasIcon('fast-backward');\n\t\tthis.#first.on('click',(evt) => this.firstPage(evt) );\n\t\tthis.#first.disabled = true;\n\n\t\tthis.#previous = this.add().fasIcon('backward');\n\t\tthis.#previous.on('click',(evt) => this.previousPage(evt) );\n\t\tthis.#previous.disabled = true;\n\n\t\tfor (let i=this.#start; i this.select(i) );\n\t\t\tthis.#pages.push(btn);\n\t\t}\n\n\t\tthis.#next = this.add().fasIcon('forward');\n\t\tthis.#next.on('click',(evt) => this.nextPage(evt) );\n\t\tthis.#next.disabled = true;\n\t\t\n\t\tthis.#last = this.add().fasIcon('fast-forward');\n\t\tthis.#last.on('click',(evt) => this.lastPage(evt) );\n\t\tthis.#last.disabled = true;\n\t\t\n\t\t//hide first/last if not needed\n\t\tif (this.#max == -1)\n\t\t{\n\t\t\tthis.#first.hide();\n\t\t\tthis.#last.hide();\n\t\t}\n\n\t\tif (this.#pages.length > 0) this.updateActive();\n\n\t};\n\n\tsetupEndpoints()\n\t{\n\t\t//no limit on how many pages we can show\n\t\tif (this.#max==-1 || this.#max >= this.#count)\n\t\t{\n\t\t\tthis.#start = 0;\n\t\t\tthis.#end = this.#count;\n\t\t}\n\t\t//we have a limit on how manage pages we can show\n\t\telse\n\t\t{\n\t\t\t//407 399 408\n\t\t\tif (this.#page > 0 && this.#page >= this.#end && this.#page < this.#count)\n\t\t\t{\n\t\t\t\tthis.#start = this.#page - this.#max + 1;\n\t\t\t\tthis.#end = this.#page + 1;\n\t\t\t}\n\t\t\telse if (this.#page <= this.#start && this.#page >= 0) \n\t\t\t{\n\t\t\t\tthis.#start = this.#page;\n\t\t\t\tthis.#end = this.#page + this.#max - 1;\n\t\t\t}\n\t\t\telse if (!this.#start || !this.#end)\n\t\t\t{\n\t\t\t\tthis.#start = 0;\n\t\t\t\tthis.#end = Math.min(this.#max,this.#count);\n\t\t\t}\n\t\t}\n\t};\n\n\tpage(idx)\n\t{\n\t\tif (typeof(idx) == 'undefined') return this.#page;\n\t\telse\n\t\t{\n\t\t\tthis.#page = 0;\n\t\t\tthis.updateActive();\n\t\t}\n\t};\n\n\tselect(idx) \n\t{\n\t\tthis.#page = idx;\n\n\t\t//if switching to a page outside our range, redraw\n\t\tif (this.#page >= this.#end || this.#page <= this.#start) this.draw();\n\n\t\t//otherwise just change the page\n\t\telse this.updateActive();\n\n\t\tthis.trigger('page:selected',{page:idx});\n\t};\n\n\tupdateActive()\n\t{\n\t\t//not initialized yet\n\t\tif (!this.#next || !this.#previous || !this.#first || !this.#last) return;\n\t\t\n\t\t//update the selected page\n\t\tconst activePage = this.querySelector('.current-page');\n\t\tif (activePage) activePage.classList.remove('current-page');\n\t\t\n\t\tthis.querySelector(`[data-page=\"${this.#page}\"]`).classList.add('current-page');\n\n\t\t//disable buttons we don't need based on current page\n\t\tif (this.#page==0) \n\t\t{\n\t\t\tthis.#previous.disabled = true;\n\t\t\tthis.#first.disabled = true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthis.#previous.disabled = false;\n\t\t\tthis.#first.disabled = false;\n\t\t}\n\n\t\tif (this.#page==this.#count-1) \n\t\t{\n\t\t\tthis.#next.disabled = true;\n\t\t\tthis.#last.disabled = true;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tthis.#next.disabled = false;\n\t\t\tthis.#last.disabled = false;\n\t\t}\n\n\t};\n\n\tnextPage(evt)\n\t{\n\t\tevt.preventDefault();\n\n\t\tif (this.#page == this.#count-1) return;\n\t\tthis.select(this.#page + 1);\n\t};\n\n\tpreviousPage(evt)\n\t{\n\t\tevt.preventDefault();\n\n\t\tif (this.#page == '0') return;\n\t\tthis.select(this.#page - 1);\n\t};\n\n\tfirstPage(evt)\n\t{\n\t\tevt.preventDefault();\n\t\t\n\t\tif (this.#page == '0') return;\n\t\tthis.select('0');\n\t};\n\n\tlastPage(evt)\n\t{\n\t\tevt.preventDefault();\n\n\t\tif (this.#page == this.#count-1) return;\n\t\tthis.select(this.#count-1);\n\t};\t\t\n\n};\n\ncustomElements.define('wd-navbar-menu-pager',WDNavbarMenuPager);\n", "\nimport WDElement from '../wd-element'\n\nexport class WDFormBasic extends WDElement {\n\n\t/** convenience methods **/\n\t#config = {};\n\t#label = null;\n\t#input = null;\n\t#prefix = null;\n\t#suffix = null;\n\t#inputGroup = null;\n\n\tconstructor(input,config)\n\t{\n\t\tsuper();\n\t\tif (config) Object.assign(this.#config,config);\n\n\t\tthis.#input = input;\n\t\tthis.append(input);\n\t\t\t\t\t\t\n\t\t/** now setup the attributes and properties we can handle at init time **/\n\t\tthis.addAttributes();\n\t\tthis.addProperties();\n\n\t\t/** add some passed event handlers if in the config **/\n\t\tfor (let key in this.#config) \n\t\t{\n\t\t\tif (key.indexOf('on') == '0') \n\t\t\t{\n\t\t\t\tlet evt = key.replace('on','');\n\t\t\t\tthis.#input.addEventListener(evt,this.#config[key]);\n\t\t\t}\n\t\t}\n\n //add validation handler if passed\n if (this.#config.required) this.feedback();\n\n\t\treturn this;\n\t};\n\t\t\n\t//\n\tconfig(val)\n\t{\n\t\tif (typeof(val) == 'undefined') return this.#config;\n\t\telse\n\t\t{\n\t\t\tthis.#config = val;\n\t\t\treturn this;\n\t\t}\n\t};\n\n\tinput() { return this.#input; };\n\tname() { return this.config().name; };\n\ttype() { return this.config().name; };\n\tform() { return this.closest('.wd-form'); };\n\t\n\tlabel()\n\t{\n\t\tif (!this.#label)\n\t\t{\n\t\t\tthis.#label = wdc(`