diff --git a/.gitignore b/.gitignore index ad2722efe..4f5f7ad7a 100644 --- a/.gitignore +++ b/.gitignore @@ -60,6 +60,7 @@ wp-content/themes/* wp-content/plugins/* !wp-content/themes/core !wp-content/plugins/core +!wp-content/plugins/facetwp !wp-config.php !wp-config-environment.php diff --git a/wp-content/plugins/facetwp/.gitignore b/wp-content/plugins/facetwp/.gitignore new file mode 100644 index 000000000..d588c26e6 --- /dev/null +++ b/wp-content/plugins/facetwp/.gitignore @@ -0,0 +1,5 @@ +.DS_Store +.idea/ +node_modules/ +yarn.lock +package-lock.json diff --git a/wp-content/plugins/facetwp/assets/css/admin.css b/wp-content/plugins/facetwp/assets/css/admin.css new file mode 100755 index 000000000..d368b82a5 --- /dev/null +++ b/wp-content/plugins/facetwp/assets/css/admin.css @@ -0,0 +1,1705 @@ +:root { + --red: #e5424c; + --red-dark: #c6212c; + --orange: #f49530; + --purple: #6233ca; + --purple-darker: #5124b3; + --green: #52c852; + --green-darker: #42b845; + --current: #d93f7c; + --hover: #cc52ab; + --blue: #50b7ff; + --blue-medium: #209cee; + --blue-light: #b4ddff; + --blue-ultra-light: #e1f2fd; + --violet-light: #e8a7ec; + --grey-medium: #aaa; + --grey-dark: #222; + --grey: #ddd; + --bg-grey-ultra-ultra-light: #faf9fb; + --bg-grey-ultra-light: #f3f2f5; + --bg-grey-light: #f6f5f8; + --bg-grey-medium: #efeff4; + --border-grey: #e7e4ed; + --border-grey-medium: #d3d1da; + --text-purple-grey-ultra-ultra-light: #dddbe3; + --text-purple-grey-ultra-light: #c1bdcc; + --text-purple-grey-light: #aaa1c3; + --text-purple-grey-medium: #9991b0; + --bg-purple-dark: #4c22ad; + --bg-purple-ultra-dark: #201346; + --input-border: #8c8f94; + --white: #fff; +} + +/* Header area */ +body.settings_page_facetwp { + background: var(--white); + font-size: 14px; +} + +body.settings_page_facetwp .wrap { + margin: 24px 20px 0 0; +} + +body.settings_page_facetwp #screen-meta, +body.settings_page_facetwp #screen-meta-links { + display: none; +} + +a { + color: var(--blue); + transition: none; +} + +a:hover { + color: var(--blue-medium); +} + +#icon-facetwp { + float: left; + padding: 8px 10px 0 0; +} + +.facetwp-header { + margin: 0 0 0 -20px; + padding: 10px 20px; + background-color: var(--purple-darker); + font-size: 14px; + line-height: 1.4285714285714286; /* to get full px heights with 14px font, for absolute positioning of dropdown */ +} + +.facetwp-header a:focus { + box-shadow: none; +} + +.facetwp-logo { + line-height: 20px; + background: url('../images/logo.svg?v=4.2') no-repeat; + background-size: contain; + display: inline-block; + width: 26px; + position: relative; + top: 1px; + text-decoration: none; +} + +.facetwp-version { + margin: 0 24px 0 4px; + color: var(--text-purple-grey-ultra-light); + font-size: 12px; + text-decoration: none; +} + +/* override WP */ +.facetwp-version:active, +.facetwp-version:focus { + color: var(--text-purple-grey-ultra-light); +} + +.facetwp-version:hover { + color: var(--violet-light); +} + +.facetwp-tab { + cursor: pointer; + display: inline-block; + border-radius: 3px; + padding: 5px 15px 6px 15px; + color: var(--text-purple-grey-ultra-ultra-light); +} + +.facetwp-tab.active { + background-color: rgba(255, 255, 255, 0.2); + color: var(--white); +} + +.facetwp-tab:hover { + color: var(--violet-light); +} + +.facetwp-tab.active:hover { + color: var(--white); +} + +.facetwp-actions { + float: right; +} + +.facetwp-response-wrap { + position: absolute; + right: 20px; + margin-top: 10px; + padding: 2px 8px 4px 8px; + line-height: 1; + color: var(--white); + background-color: var(--purple-darker); + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + cursor: pointer; +} + +.facetwp-response { + display: none; +} + +.facetwp-response.visible { + display: inline-block; + padding: 2px 5px 2px 4px; +} + +.facetwp-response-icon { + display: inline-block; + vertical-align: middle; +} + +.facetwp-response-icon span { + display: inline-block; + position: relative; + width: 20px; + height: 20px; +} + +.facetwp-response-icon span::before { + display: block; + font-family: dashicons; + font-size: 20px; + position: absolute; + top: 3px; + left: 3px; + line-height: 14px; + width: 14px; + height: 14px; + border-radius: 50%; + text-align: center; + text-indent: -3px; + background: #fff; +} + +.facetwp-response-icon[data-status="ok"] span::before { + content: "\f12a"; + color: var(--green); +} + +.facetwp-response-icon[data-status="load"] span::before { + content: "\f463"; + background: none; +} + +.facetwp-response-icon[data-status="error"] span::before { + content: "\f534"; + color: var(--orange); +} + +.facetwp-response-icon[data-status="load"] span { + animation: spin 1s infinite linear; +} + +.facetwp-region input::placeholder { + color: #b4b4b4; +} + +[v-cloak] { + display: none; +} + +.btn-split, +.btn-normal { + position: relative; + display: inline-block; + background-color: var(--red); + border-radius: 3px; + vertical-align: top; + cursor: pointer; +} + +.btn-split { + margin-right: 4px; +} + +.btn-normal { + padding: 6px 13px 6px 13px; + background-color: var(--bg-purple-dark); + color: var(--white); + line-height: 1.4285714285714286; +} + +.btn-normal.export-submit { + float: left; + clear: left; + margin-top: 12px; +} + +.facetwp-header .btn-normal { + border: none; + padding: 5px 13px 6px 13px; + background-color: var(--white); + color: var(--purple); +} + +.btn-normal:hover { + background-color: var(--green-darker); +} + +.btn-normal.import-submit { + margin-top: 7px; +} + +.facetwp-header .btn-normal:hover { + border: none; + color: var(--white); + background: var(--green-darker); +} + +.btn-split .btn-label, +.btn-split .btn-caret { + display: inline-block; + padding: 5px 13px 6px 13px; + color: var(--white); +} + +.btn-split .btn-label { + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; +} + +.btn-split .btn-caret { + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; + padding: 5px 10px 6px 10px; + border-left: 1px solid var(--red-dark); +} + +.btn-split .btn-label:hover, +.btn-split .btn-caret:hover { + background-color: var(--red-dark); +} + +.btn-split .fa-caret-down { + pointer-events: none; + position: relative; + top: 0px; +} + +.btn-split .btn-dropdown { + position: absolute; + border-radius: 0px 0px 3px 3px; + box-shadow: 0 4px 4px 0 rgba(0, 0, 0, .1), 0 0 0 1px rgba(0, 0, 0, .1); + background-color: var(--white); + font-size: 13px; + width: 190px; + top: 41px; + right: 0; + z-index: 10; +} + +.btn-split .dropdown-inner div { + padding: 10px; +} + +.btn-split .dropdown-inner div:hover { + background-color: var(--bg-grey-ultra-ultra-light); + color: var(--blue-medium); +} + +/* Loading animation */ +.facetwp-loading { + margin-top: 40px; + text-align: center; +} + +@keyframes spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} + +/* Content boxes */ +.facetwp-content { + padding-top: 18px; +} + +.facetwp-region { + display: none; + padding-bottom: 30px; +} + +.facetwp-region.active { + display: block; +} + +.facetwp-region iframe { + width: 100%; + height: 600px; +} + +.facetwp-subnav { + padding-bottom: 20px; +} + +.facetwp-btn { + display: inline-block; + cursor: pointer; + padding: 0px 8px; + background-color: var(--white); + border: 1px solid var(--red); + border-radius: 3px; + font-size: 12px; + font-weight: normal; + text-decoration: none; + line-height: 2.1em; + color: var(--red); +} + +h3 .facetwp-btn { + margin-left: 6px; + position: relative; + top: -1px; +} + +.facetwp-btn:hover { + background: var(--red); + color: var(--white); +} + +.facetwp-region h3 { + font-weight: normal; + font-size: 18px; + margin: 0 0 20px 0; +} + +.facetwp-region > h3 a { + display: inline-block; + border-bottom: 2px solid var(--blue); + color: var(--blue); + cursor: pointer; +} + +.facetwp-region > h3 a:hover { + border-bottom: 2px solid var(--blue-medium); + color: var(--blue-medium); +} + +/* override 1px wp margin */ +.facetwp-region input, +.facetwp-region select { + margin-left: 0; + margin-right: 0; +} + +.facetwp-region input[type='text'], +.facetwp-region input[type='number'], +.facetwp-region select { + width: 220px; + border-radius: 3px; + margin-right: 4px; + line-height: 1.4285714285714286; + padding: 4px 8px 6px 8px; + min-height: auto; +} + +.facetwp-region .fs-wrap { + margin-right: 4px; +} + +.facet-fields input.facet-step, +.facet-fields input.facet-count, +.facet-fields input.facet-soft-limit { + width: 50px; +} + +.facet-fields input.facet-count-text-plural { + width: 300px; +} + +.facetwp-content .qb-wrap .facetwp-row { + grid-template-columns: 160px 1fr; + padding: 0; +} + +.facetwp-region textarea { + width: 100%; + height: 320px; + padding: 10px; + color: var(--blue-light); + background-color: var(--grey-dark); + font-family: monospace; + white-space: pre; + overflow: auto; +} + +.facetwp-region textarea.facet-modifier-values { + width: 360px; + height: 120px; + height: 120px; +} + +.facetwp-region .item-name { + display: inline-block; + margin: 0 12px 0 8px; + line-height: 1.4; + padding: 6px 8px 6px 8px; + border-radius: 3px; + position: relative; + top: 0px; + background: var(--bg-grey-light); + border: 1px solid var(--border-grey-medium); +} + +.facetwp-region .item-name + .facetwp-btn { + position: relative; + top: 0px; +} + +.facetwp-region .facet-fields .qb-row .item-name { + top: -1px; +} + +.table-row { + margin-bottom: 15px; +} + +.hidden { + display: none; +} + +.facetwp-dev-mode { + display: inline-block; + position: absolute; + color: var(--purple); + right: 20px; + margin-top: 60px; +} + +.facetwp-region .open-builder:before { + content: '\f107'; + font: 400 20px/1 dashicons; + vertical-align: top; + padding-right: 4px; +} + +.facetwp-region .fs-dropdown { + max-width: 400px; + width: 218px; +} + +.facetwp-region .fs-dropdown .fs-options { + max-height: none; + height: 250px; + min-height: 250px; + min-width: 218px; + resize: both; + overflow-x: hidden; + overflow-y: auto; +} + +.facetwp-region .fs-dropdown .fs-option { + line-height: 1.15em; + padding: 5px 24px 5px 8px; + word-break: break-word; +} + +.facetwp-region .fs-dropdown .fs-option:hover { + background-color: var(--bg-grey-ultra-ultra-light); + color: var(--blue-medium); +} + +.facetwp-region .fs-dropdown .fs-optgroup-label { + background-color: var(--blue-ultra-light); + text-align: left; + font-size: 12px; + text-transform: uppercase; + padding: 9px 24px 8px 8px; + margin: 1px 0; +} + +.facetwp-region .fs-dropdown .fs-optgroup-label:first-child { + margin-top: 0; +} + +.facetwp-region .fs-wrap.fs-open .fs-dropdown { + max-width: none; + min-width: 218px; + width: auto; + border: 1px solid var(--input-border); + margin-top: -1px; + border-radius: 0px 0px 3px 3px; +} + +.fs-wrap.multiple .fs-option.selected .fs-checkbox i { + background-color: var(--blue-medium) !important; +} + +.facetwp-region .item-locked { + padding: 10px; + margin-bottom: 20px; + background-color: var(--red); + border-radius: 3px; + color: var(--white); +} + +.facetwp-region .item-locked span { + padding-left: 5px; + cursor: pointer; +} + +.facetwp-region .facetwp-content.locked { + position: relative; +} + +/* Unclickable locked overlay */ +.facetwp-region .facetwp-content.locked:after { + content: " "; + opacity: 0.6; + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + background: #fff; + z-index: 10000; +} + +.facetwp-region .fs-label-wrap { + border: 1px solid var(--input-border); + border-radius: 3px; +} + +.facetwp-region .fs-open .fs-label-wrap { + border-radius: 3px 3px 0px 0px; +} + +.facetwp-region .fs-label-wrap .fs-label { + padding: 5px 24px 5px 8px; + line-height: 1.4285714285714286; +} + +.facetwp-region .facetwp-row .fs-wrap { + display: block; + float: left; +} + +.facetwp-region .fs-wrap .fs-search { + border-bottom: 1px solid #ddd; + background-color: var(--bg-grey-ultra-ultra-light); +} + +.facetwp-region .fs-wrap .fs-search input { + min-height: auto; + line-height: 1.5em; +} + +.facetwp-region .fs-label-wrap .fs-arrow { + top: 6px; + right: 5px; + bottom: initial; + width: 16px; + height: 16px; + background: url('data:image/svg+xml;charset=US-ASCII,%3Csvg%20width%3D%2220%22%20height%3D%2220%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M5%206l5%205%205-5%202%201-7%207-7-7%202-1z%22%20fill%3D%22%23555%22%2F%3E%3C%2Fsvg%3E') no-repeat; + background-size: 16px 16px; + border: none; +} + +.facetwp-region .fs-open .fs-arrow { + transform: rotate(-180deg); +} + +/* Data Source dropdown */ +.name-source .fs-dropdown { + width: 300px; +} + +/* Cards UI */ +.facetwp-card { + cursor: pointer; +} + +.facetwp-card:nth-child(odd) { + background-color: var(--bg-grey-ultra-ultra-light); +} + +.facetwp-card, +.facetwp-table-header { + display: grid; + grid-template-columns: 30px 1fr 1fr 1fr 1fr 80px 80px; + line-height: 22px; + padding: 8px; +} + +.facetwp-region-templates .facetwp-card, +.facetwp-region-templates .facetwp-table-header { + grid-template-columns: 30px 1fr 1fr 1fr 1fr 80px; +} + +.facetwp-table-header { + font-weight: bold; +} + +.facetwp-card .card-drag { + cursor: move; + color: var(--grey); + font-size: 18px; + line-height: 1; +} + +.facetwp-card > div { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.facetwp-card > div:hover { + white-space: normal; +} + +.facetwp-card .card-label .label-text + span { + margin-left: 0.4em; /* locked icon */ +} + +.facetwp-card:hover .card-label { + color: var(--blue-medium); +} + +/* Actions button */ +.facetwp-card .card-actions { + text-align: right; +} + +.facetwp-card .actions-wrap { + display: inline-block; +} + +.facetwp-card .actions-wrap .actions-btn { + padding: 2px 8px; + color: var(--grey); +} + +.facetwp-card:hover .actions-wrap .actions-btn { + color: var(--text-purple-grey-medium); +} + +.facetwp-card .actions-wrap .actions-modal { + display: none; + position: absolute; + right: 28px; + font-size: 13px; + background-color: var(--white); + border-radius: 4px; + box-shadow: 0 3px 3px 0 rgba(0, 0, 0, .1), 0 0 0 1px rgba(0, 0, 0, .1); + margin-top: -1px; + z-index: 1; +} + +.facetwp-card .actions-wrap .actions-modal div { + padding: 4px 8px; +} + +.facetwp-card .actions-wrap .actions-modal div:hover { + background-color: var(--bg-grey-ultra-ultra-light); + color: var(--blue-medium); +} + +.facetwp-card .actions-wrap:hover .actions-modal { + display: block; +} + +/* Settings page */ +.facetwp-region .import-code { + height: 160px; + margin-bottom: 8px; +} + +.field-notes { + color: var(--grey-medium); + font-size: 13px; + margin-top: 2px; + line-height: 1.5em; +} + +.facetwp-toggle-status.field-notes { + display: inline-block; + margin: 0 0 0 15px; + padding-left: 26px; + position: absolute; +} + +.facetwp-toggle-status.field-notes:before { + content: "\f534"; + color: var(--red); + display: block; + font-family: dashicons; + font-size: 20px; + position: absolute; + left: 0; + top: 1px; +} + +.facetwp-region .facetwp-subnav a { + display: inline-block; + font-size: 18px; + margin: 0 20px 0 0; + border-bottom: 3px solid transparent; + cursor: pointer; + color: var(--grey-dark); +} + +.facetwp-region-facets .facetwp-subnav a, +.facetwp-region-templates .facetwp-subnav a { + margin: 0; +} + +.facetwp-subnav a.active, +.facetwp-subnav a.active:hover { + border-color: var(--blue); + color: var(--blue); + cursor: default; +} + +.facetwp-subnav a:hover { + color: var(--blue); + border-color: var(--blue); +} + +.facetwp-settings-section { + display: none; +} + +.facetwp-settings-section.active { + display: block; + margin-top: 17px; +} + +.facetwp-setting.slim { + width: 100px; +} + +.facetwp-settings-icon.active { + display: inline-block; + line-height: 1; + position: relative; + width: 15px; + height: 1em; + margin-left: 8px; + top: -1px; +} + +.facetwp-settings-icon.active::before { + content: "\f534"; + color: var(--red); + display: block; + font-family: dashicons; + font-size: 20px; + position: absolute; + top: 3px; + left: 3px; + line-height: 14px; + width: 14px; + height: 14px; + border-radius: 50%; + text-align: center; + text-indent: -3px; + background: #fff; +} + +.facetwp-settings-icon.facetwp-tooltip::after { + content: none; +} + +.select-all-none { + display: inline-block; + margin: 6px 0 0 10px; + line-height: 1.4285714285714286; +} + +.select-all-none span { + color: var(--blue); + cursor: pointer; +} + +.select-all-none span:hover { + color: var(--blue-medium); + text-decoration: underline; +} + +/* Tooltips */ +.facetwp-tooltip { + display: inline-block; + cursor: help; +} + +.facetwp-tooltip:after { + display: inline-block; + content: '?'; + line-height: 1; + font-size: 12px; + border: 1px solid var(--text-purple-grey-ultra-light); + border-radius: 50%; + margin-left: 5px; + padding: 0 3px; + color: var(--text-purple-grey-medium); +} + +.ftip-wrap { + border-radius: 5px !important; + background-color: var(--bg-purple-ultra-dark) !important; + padding: 7px 10px 8px 10px !important; +} + +.ftip-wrap a, +.ftip-wrap a:focus { + color: var(--blue); +} + +.ftip-wrap.active { + opacity: 1; +} + +.facetwp-tooltip-content { + display: none; +} + +/* Edit screens */ +.facetwp-row { + display: grid; + grid-template-columns: 220px 1fr; + padding-bottom: 20px; +} + +.facetwp-content.type-search .field-data-source, +.facetwp-content.type-pager .field-data-source, +.facetwp-content.type-reset .field-data-source, +.facetwp-content.type-sort .field-data-source { + display: none; +} + +/* Query builder */ +.qb-wrap .qb-condition { + padding-bottom: 15px; +} + +.qb-wrap .qb-actions { + padding-top: 15px; +} + +.qb-wrap .qb-actions .facetwp-btn { + margin-right: 4px; +} + +.qb-wrap .qb-compare, +.qb-wrap .qb-orderby, +.qb-wrap .qb-type, +.qb-wrap .qb-order { + width: 120px; +} + +.facet-fields .qb-row { + display: grid; + grid-template-columns: 1fr 50px; + background-color: var(--bg-grey-ultra-ultra-light); + border: 1px solid var(--border-grey-medium); + border-radius: 3px; + margin-bottom: 20px; + padding: 15px; +} + +.facet-fields .qb-row .qb-order-row { + margin-top: 10px; +} + +.qb-wrap input.qb-posts-per-page { + width: 70px; + margin-right: 0; +} + +.qb-wrap .align-right { + text-align: right; +} + +.qb-add, +.qb-move, +.qb-remove { + cursor: pointer; + font-size: 18px; + vertical-align: middle; +} + +.qb-remove { + color: var(--red); +} + +.qb-wrap .fs-wrap { + vertical-align: middle; +} + +.qb-wrap .v-select { + display: inline-block; + vertical-align: middle; + min-width: 160px; + max-width: 360px; +} + +.qb-wrap .v-select.vs--searchable { + min-width: 200px; +} + +.qb-wrap .v-select.vs--unsearchable { + vertical-align: top; +} + +.qb-wrap .v-select .vs__search { + background: none; + line-height: 1.4285714285714286; + padding: 4px 0 6px 0; + min-height: auto; + margin: 4px 2px 0; +} + +.qb-wrap .v-select .vs__search, +.qb-wrap .v-select .vs__search:focus { + font-size: 14px; + border: 1px solid var(--grey); + padding: 0 8px; +} + +.qb-wrap .v-select .vs__dropdown-toggle { + border-color: var(--input-border); + border-radius: 3px; +} + +.qb-wrap .v-select .vs__selected { + height: 20px; +} + +.qb-wrap .v-select .vs__actions { + display: none; +} + +/* Boolean toggle */ +.facetwp-switch { + position: relative; + display: inline-block; + width: 48px; + height: 24px; +} + +.facetwp-switch input { + display: none; +} + +.facetwp-slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: var(--grey-medium); + border-radius: 24px; + -webkit-transition: .4s; + transition: .4s; +} + +.facetwp-slider:before { + position: absolute; + content: ''; + height: 16px; + width: 16px; + left: 4px; + bottom: 4px; + background-color: white; + border-radius: 50%; + -webkit-transition: .4s; + transition: .4s; +} + +.facetwp-switch input:checked + .facetwp-slider { + background-color: var(--bg-purple-dark); +} + +.facetwp-switch input:checked + .facetwp-slider:before { + -webkit-transform: translateX(24px); + -ms-transform: translateX(24px); + transform: translateX(24px); +} + +/* Template content */ +.template-tabs { + margin-bottom: 20px; + border-bottom: 1px solid var(--grey); +} + +.template-tabs.top-level { + margin: 30px 0 45px 0; +} + +.template-tabs span { + display: inline-block; + padding: 5px 15px; + border: 1px solid var(--grey); + border-top-left-radius: 3px; + border-top-right-radius: 3px; + background-color: var(--bg-grey-ultra-ultra-light); + color: var(--purple); + margin-left: 5px; + margin-bottom: -1px; + cursor: pointer; +} + +.template-tabs span.active { + border-bottom: 1px solid var(--white); + background-color: var(--white); + color: var(--current); +} + +/* Layout builder */ +.builder-wrap { + display: grid; + grid-template-columns: 1fr 320px; + grid-column-gap: 20px; + min-height: 600px; + user-select: none; +} + +.builder-canvas { + position: relative; + outline: 1px solid var(--grey-medium); + border-radius: 4px; + background-color: var(--bg-grey-ultra-ultra-light); +} + +.builder-buttons div { + display: inline-block; + padding: 5px; + margin: 0 5px 5px 0; + border: 1px solid var(--grey-medium); + border-radius: 3px; + background: var(--white); + cursor: move; +} + +.builder-row, +.builder-col, +.builder-item { + position: relative; +} + +.builder-row-inner { + display: grid; + grid-template-columns: 1fr; + grid-column-gap: 6px; + position: relative; + min-height: 30px; +} + +.builder-row-actions, +.builder-col-actions, +.builder-item-actions { + display: none; + position: absolute; + height: 20px; + line-height: 20px; + color: var(--white); + cursor: pointer; + z-index: 10; +} + +.builder-row-actions span, +.builder-col-actions span, +.builder-item-actions span { + display: inline-block; + width: 20px; +} + +.builder-col-actions:hover { + width: auto; +} + +.builder-row-actions { + top: -20px; + left: 50%; + transform: translateX(-50%); + background-color: var(--purple); + border-top-left-radius: 3px; + border-top-right-radius: 3px; + /* fixes finnicky hovering to actions buttons */ + box-shadow: inset 0 -1px var(--bg-grey-ultra-ultra-light); + height: 21px; +} + +.builder-col-actions { + top: 0; + left: 0; + width: 20px; + overflow: hidden; + background-color: var(--purple); +} + +.builder-item-actions { + top: 0; + right: 0; + width: 20px; + overflow: hidden; + background-color: var(--purple); +} + +.builder-row:hover > .builder-row-actions, +.builder-col:hover > .builder-col-actions, +.builder-item:hover > .builder-item-actions { + display: inline-block; + line-height: 20px; + text-align: center; +} + +.builder-row:hover { + outline: 1px solid var(--purple); +} + +.builder-col .resizer { + position: absolute; + top: 0; + right: -6px; + width: 5px; + height: 100%; + cursor: col-resize; +} + +.builder-row:hover .builder-col .resizer { + background-color: var(--grey); +} + +.builder-col-inner { + position: relative; + min-height: 30px; + margin: 20px; +} + +.builder-col-inner.empty-col { + height: calc(100% - 20px); +} + +.builder-item { + background-color: var(--white); + outline: 1px dashed var(--grey-medium); + margin-bottom: 10px; + padding: 2px 10px; +} + +.builder-item:hover { + outline: 1px solid var(--purple); +} + +.builder-item-inner { + height: 30px; + line-height: 30px; + padding-right: 20px; + word-break: break-all; + overflow: hidden; + cursor: default; +} + +.builder-item-inner.is-hidden { + opacity: 0.4; +} + +.builder-item-inner .item-drag { + cursor: move; +} + +.builder-first-add { + display: flex; + align-items: center; + justify-content: center; + outline: 1px dashed var(--grey-medium); + font-size: 20px; + color: var(--grey-medium); + height: 30px; + cursor: pointer; +} + +.builder-first-add:hover { + outline: 1px dashed var(--purple); + background-color: var(--white); + color: var(--purple); +} + +.popover { + position: absolute; + top: 20px; + left: 20px; + background-color: var(--white); + outline: 1px solid var(--purple); + z-index: 10; + margin-right: 20px; + word-break: break-word; +} + +.popover-search { + border-bottom: 1px solid var(--grey); +} + +.popover-search input[type='text'] { + width: 100%; + min-width: 236px; + box-shadow: none; + outline-style: none; + border-color: transparent; + line-height: 1.5em; + padding: 0px 8px; + background-color: var(--bg-grey-ultra-ultra-light); +} + +.popover-choices { + height: 250px; + min-height: 250px; + min-width: 240px; + width: 240px; + padding: 0; + overflow-x: hidden; + overflow-y: auto; + resize: both; +} + +.popover-choices div { + line-height: 1.15em; + padding: 5px 8px; + cursor: pointer; +} + +.popover-choices div:hover { + background-color: var(--bg-grey-ultra-ultra-light); + color: var(--blue-medium); +} + +.builder-crumb { + display: inline-block; +} + +.builder-setting { + margin-bottom: 20px; +} + +.builder-setting .setting-title { + margin-bottom: 5px; +} + +.builder-setting input[type='number'] { + width: 60px; +} + +.builder-setting .utrbl { + display: inline-block; + width: 50px; +} + +.builder-setting .utrbl-unit { + display: none; +} + +.builder-setting .utrbl input[type='text'], +.builder-setting .utrbl input[type='number'] { + width: 46px; + height: auto; +} + +.builder-setting textarea { + height: 100px; +} + +.builder-setting .utrbl span { + display: block; + font-size: 9px; + text-transform: uppercase; + color: var(--grey-medium); +} + +.builder-setting .text-style-icons span { + display: inline-block; + padding: 4px 8px; + margin: 4px 2px 0 0; + border: 1px solid var(--grey); + border-radius: 3px; + cursor: pointer; +} + +.builder-setting .text-style-icons span.active { + background-color: var(--grey-dark); + color: var(--white); +} + +/* Color picker */ +.color-wrap { + position: relative; +} + +.color-wrap .color-canvas { + display: inline-block; +} + +.color-wrap .color-preview { + position: absolute; + top: 1px; + left: 1px; + height: 30px; + width: 28px; + border-top-left-radius: 2px; + border-bottom-left-radius: 2px; + z-index: 1; +} + +.color-wrap input.color-input { + padding-left: 36px; +} + +.color-wrap .color-clear { + display: inline-block; + position: relative; + right: 30px; + width: 20px; + text-align: center; + cursor: pointer; +} + +@media (min-width: 783px) { + .facetwp-header { + position: sticky; + z-index: 100; + top: 32px; + } +} + +/* FontAwesome */ +svg:not(:root).svg-inline--fa { + overflow: visible; +} + +.svg-inline--fa { + display: inline-block; + font-size: inherit; + height: 1em; + overflow: visible; + vertical-align: -.125em; +} + +.svg-inline--fa.fa-w-10 { + width: .625em; +} + +.svg-inline--fa.fa-w-11 { + width: .6875em; +} + +.svg-inline--fa.fa-w-12 { + width: .75em; +} + +.svg-inline--fa.fa-w-14 { + width: .875em; +} + +.svg-inline--fa.fa-w-16 { + width: 1em; +} + +.svg-inline--fa.fa-w-18 { + width: 1.125em; +} + +.svg-inline--fa.fa-w-20 { + width: 1.25em; +} + +/* Dark color theme for Listing Builder code editors */ + +:root { + --bg-code: #222831; + --text-light-code: #adbac7; + --bg-code-line-numbers: #617084; + --bg-code-lines-active: 229, 208, 122; + --bg-code-lines-selected: #50769c; + --bg-code-search: #383e46; + --bg-code-search-active: #41474f; +} + +.cm-s-default.CodeMirror { + border-radius: 6px !important; + padding-bottom: 64px; + background-color: var(--bg-code); + min-height: 230px; /* 10 lines */ + height: 40vh; + resize: vertical; +} + +.facet-marker-content + .cm-s-default.CodeMirror { + min-height: 180px; /* 8 lines */ + height: 25vh; +} + +.cm-s-default .CodeMirror-scroll { + color: var(--text-light-code); + font-family: ui-monospace, Courier, Cascadia Mono, Segoe UI Mono, Liberation Mono, monospace, monospace !important; + font-size: 15.5px !important; + line-height: 1.55em; + padding-top: 20px; + padding-bottom: 20px; + margin-bottom: 0 !important; + overflow-x: hidden !important; +} + +.cm-s-default .CodeMirror-gutters { + padding-left: 9px; + padding-right: 14px; + background-color: transparent; + border: none; +} + +.cm-s-default .CodeMirror-gutter-background { + border-left: 5px solid transparent !important; + box-sizing: border-box; +} + +.wrap .CodeMirror .CodeMirror-linenumber { + background-color: transparent; + color: var(--bg-code-line-numbers); +} + +.CodeMirror-dialog { + border: none !important; + background: var(--bg-code-search-active); + -webkit-box-shadow: -6px 7px 5px -4px rgba(34, 40, 49, 0.5); + box-shadow: -6px 7px 5px -4px rgba(34, 40, 49, 0.5); + border-bottom: 1px solid rgba(34, 40, 49, 0.5); + padding: 8px 20px; + font-size: 14px !important; +} + +.CodeMirror-dialog .CodeMirror-search-label { + color: rgba(var(--bg-code-lines-active), 1); +} + +.CodeMirror-dialog .CodeMirror-search-field { + background: #666; + color: #efefef; +} + +.CodeMirror-dialog .CodeMirror-search-field:focus { + outline: none; + border: none; + box-shadow: none; + background: #777; +} + +.cm-s-default .CodeMirror-lines { + background-color: transparent; + color: #abb2bf !important +} + +.cm-s-default .CodeMirror-cursor { + border-left: 2px solid #56b6c2 !important; +} + +.cm-s-default .CodeMirror-matchingbracket, .cm-s-default .CodeMirror-matchingtag { + background-color: transparent; + border-bottom: 2px solid #56b6c2; + color: #abb2bf !important; +} + +.cm-s-default .CodeMirror-nonmatchingbracket { + background-color: transparent; + border-bottom: 2px solid #e06c75; + color: #abb2bf !important; +} + +.cm-s-default .CodeMirror-foldgutter, .cm-s-default .CodeMirror-foldgutter-folded, .cm-s-default .CodeMirror-foldgutter-open, .cm-s-default .CodeMirror-foldmarker { + background-color: transparent; + border: none; + color: #5c6370 !important; + text-shadow: none; +} + +.CodeMirror-activeline-background { + background: none; +} + +.cm-s-default.CodeMirror-focused .CodeMirror-activeline-background, +.cm-s-default.CodeMirror-focused .CodeMirror-activeline-gutter { + background-color: rgba(var(--bg-code-lines-active), 0.13) !important; +} + +.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { + background-color: var(--bg-code-lines-selected) !important; +} + +.cm-s-default.CodeMirror-focused .CodeMirror-activeline .CodeMirror-linenumber { + color: rgba(var(--bg-code-lines-active), 1); +} + +.cm-s-default.CodeMirror-focused .CodeMirror-activeline .CodeMirror-gutter-background { + border-color: rgba(var(--bg-code-lines-active), 1) !important; +} + +.cm-s-default .cm-builtin, .cm-s-default .cm-error, .cm-s-default .cm-header, .cm-s-default .cm-negative, .cm-s-default .cm-positive, .cm-s-default .cm-string-2, .cm-s-default, .cm-s-default .cm-variable { + color: #dcbdfb; +} + +.cm-s-default .cm-tag { + color: #8ddb8c; +} + +.cm-s-default .cm-comment, .cm-s-default .cm-quote { + color: #768390; +} + +.cm-s-default .cm-header .cm-strong, .cm-s-default .cm-strong { + font-weight: 700; +} + +.cm-s-default .cm-em, .cm-s-default .cm-header .cm-em { + color: #c678dd; + font-style: italic; +} + +.cm-s-default .cm-atom, .cm-s-default .cm-attribute, .cm-s-default .cm-qualifier { + color: #d19a66; +} + +.cm-s-default .cm-number { + color: #6cb6ff; +} + +.cm-s-default .cm-link { + border-bottom: 1px solid #98c379; + color: #98c379; +} + +.cm-s-default .cm-keyword { + color: #f47067; +} + +.cm-s-default .cm-def, +.cm-s-default .cm-variable-2 { + color: var(--text-light-code); +} + +.cm-s-default .cm-m-css.cm-atom, .cm-s-default .cm-m-css.cm-builtin, .cm-s-default .cm-m-lua.cm-variable, .cm-s-default .cm-property { + color: #56b6c2; +} + +.cm-s-default .cm-operator { + color: var(--text-light-code); +} + +.cm-s-default .cm-bracket, .cm-s-default .cm-m-css.cm-property, .cm-s-default .cm-meta, .cm-s-default .cm-punctuation { + color: #6cb6ff; +} + +.cm-s-default .cm-tag.cm-bracket { + color: var(--text-light-code); +} + +.cm-s-default .cm-string { + color: #96d0ff; +} + +.cm-s-default .cm-m-css.cm-variable { + color: #828997; +} + +.cm-s-default .cm-searching { + background-color: rgba(var(--bg-code-lines-active), 0.3) !important; + padding: 0.2em 0.3em; + border-radius: 2px; +} + +.cm-s-default .CodeMirror-search-match { + border: none; + right: 20px !important; + margin-top: 20px; + background: rgba(var(--bg-code-lines-active), 1); +} + +p.note { + color: #9991b0; + margin-left: 15px; + font-size: 13px; + line-height: 1.55em; + margin: 15px; +} + +/* End dark color theme for Listing Builder code editors */ + +/* Google API check status */ + +.apitestcheck.btn-normal, +.apitestreset.btn-normal { + margin-right: 4px; + margin-bottom: 8px; +} + +.facetwp-api-status { + margin-top: -8px; +} + +.facetwp-api-status .action-testing { + margin: 0 0 4px 0px; + display: block; + float: left; + clear: left; +} + +.facetwp-api-status .action-testing.test-status { + font-weight: bold; + margin: 0 0 5px 0; + padding-top: 13px; +} + +.facetwp-api-status .action-testing > span:first-child { + margin-right: 3px; +} + +.facetwp-api-status span.status { + position: relative; + margin-left: 22px; +} + +.facetwp-api-status span.status:before { + display: block; + font-family: dashicons; + font-size: 16px; + font-weight: normal; + position: absolute; + top: 2px; + left: -20px; + line-height: 16px; + width: 16px; + height: 16px; + border-radius: 50%; + text-align: center; + background: #fff; +} + +.facetwp-api-status span.status { + font-weight: bold; +} + +.facetwp-api-status .status-pass span { + color: var(--green); +} + +.facetwp-api-status .status-fail span { + color: var(--red); +} + +.facetwp-api-status .status-pass > span.status:before { + content: "\f12a"; +} + +.facetwp-api-status .status-fail > span.status:before { + content: "\f107"; +} + +/* End Google API check status */ \ No newline at end of file diff --git a/wp-content/plugins/facetwp/assets/css/front.css b/wp-content/plugins/facetwp/assets/css/front.css new file mode 100644 index 000000000..9f64dc848 --- /dev/null +++ b/wp-content/plugins/facetwp/assets/css/front.css @@ -0,0 +1,345 @@ +.facetwp-facet { + margin-bottom: 40px; +} + +.facetwp-facet.is-loading { + opacity: 0.6; + /** prevent clicks during refresh **/ + pointer-events: none; +} + +.facetwp-overlay { + position: absolute; +} + +.facetwp-pager-label { + display: inline-block; + margin-right: 12px; +} + +.facetwp-page { + display: inline-block; + padding: 0px 4px; + margin-right: 6px; + cursor: pointer; +} + +.facetwp-page.dots { + cursor: default; +} + +.facetwp-page.active { + font-weight: bold; + cursor: default; +} + +/* Checkboxes */ + +.facetwp-type-checkboxes .facetwp-depth { + display: none; +} + +.facetwp-type-checkboxes .facetwp-depth.visible { + display: inherit; +} + +.facetwp-checkbox { + background: url('../images/checkbox.png') 0 50% no-repeat; + background-size: 14px 14px; + margin-bottom: 4px; + padding-left: 20px; + cursor: pointer; +} + +.facetwp-checkbox.checked { + background-image: url('../images/checkbox-on.png'); +} + +.facetwp-checkbox.disabled, +.facetwp-radio.disabled { + opacity: 0.4; + cursor: default; +} + +.facetwp-checkbox .facetwp-expand { + float: right; +} + +.facetwp-display-value { + padding-right: 5px; +} + +/* Radio */ + +.facetwp-radio { + background: url('../images/radio.png') 0 50% no-repeat; + background-size: 14px 14px; + margin-bottom: 4px; + padding-left: 20px; + cursor: pointer; +} + +.facetwp-radio.checked { + background-image: url('../images/radio-on.png'); +} + +/* fSelect */ + +.facetwp-type-fselect.is-loading { + opacity: 1; /* prevent stack order issues */ +} + +.facetwp-type-fselect.is-loading .fs-label-wrap, +.facetwp-type-fselect.is-loading .fs-search, +.facetwp-type-fselect.is-loading .fs-no-results, +.facetwp-type-fselect.is-loading .fs-options { + opacity: 0.6; +} + +.facetwp-type-fselect.is-loading .fs-option { + cursor: wait; +} + +.facetwp-type-fselect .fs-wrap.fs-disabled .fs-option { + opacity: 0.4; + cursor: wait; +} + +.facetwp-type-fselect .fs-option .fs-option-label { + white-space: nowrap; +} + +.facetwp-type-fselect .fs-option.d1 .fs-option-label { + padding-left: 20px; +} + +.facetwp-type-fselect .fs-option.d2 .fs-option-label { + padding-left: 40px; +} + +.facetwp-type-fselect .fs-option.d3 .fs-option-label { + padding-left: 60px; +} + +.facetwp-type-fselect .fs-option.d4 .fs-option-label { + padding-left: 80px; +} + +.facetwp-type-fselect .fs-option.d5 .fs-option-label { + padding-left: 100px; +} + +.facetwp-type-fselect .fs-option.d6 .fs-option-label { + padding-left: 120px; +} + +/* Hierarchy */ + +.facetwp-depth { + margin-left: 12px; +} + +.facetwp-link { + cursor: pointer; +} + +.facetwp-link.checked { + font-weight: bold; + cursor: default; +} + +.facetwp-toggle { + cursor: pointer; +} + +.facetwp-hidden { + display: none; +} + +/* Slider */ + +.facetwp-slider-wrap { + padding-bottom: 15px; +} + +.facetwp-slider-reset { + border: 1px solid #d9d9d9; + border-radius: 3px; + background: #fff; + box-shadow: inset 0 0 1px #fff, inset 0 1px 7px #ebebeb, 0 3px 6px -3px #bbb; + padding: 4px 8px; + cursor: pointer; +} + +.facetwp-slider[data-disabled="true"] { + opacity: 0.6; + cursor: not-allowed; +} + +.facetwp-slider[data-disabled="true"] .noUi-handle { + cursor: not-allowed; +} + +/* Search */ + +.facetwp-input-wrap { + display: inline-block; + position: relative; +} + +.facetwp-facet input.facetwp-search, +.facetwp-facet input.facetwp-location { + margin: 0; + padding-right: 30px; + min-width: 240px; +} + +.facetwp-icon { + right: 0; + height: 100%; + line-height: 1; + position: absolute; + cursor: pointer; + opacity: 0.5; +} + +.facetwp-icon:before { + display: inline-block; + content: ''; + width: 30px; + height: 100%; + background: url('../images/icon-search.png') no-repeat; + background-position: 5px 50%; + background-size: 20px 20px; +} + +/* Proximity */ + +.location-results { + position: absolute; + background: #fff; + border-left: 1px solid #ddd; + border-right: 1px solid #ddd; + overflow: hidden; + width: 100%; +} + +.location-result { + font-size: 11px; + border-bottom: 1px solid #ddd; + padding: 5px; + cursor: pointer; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + color: #888; +} + +.location-result:hover { + background-color: #f8f8f8; +} + +.location-result.active { + background-color: #EBF2FE; +} + +.location-result .result-main { + font-size: 13px; + color: #222; +} + +.facetwp-icon.locate-me:before { + background-image: url('../images/icon-locate.png'); +} + +.facetwp-icon.f-reset:before { + background-image: url('../images/icon-close.png'); +} + +.facetwp-icon.f-loading:before { + background-image: url('../images/loading.png'); + animation: spin 700ms infinite linear; +} + +.location-attribution { + border-bottom: 1px solid #ddd; + padding: 5px; +} + +.powered-by-google { + height: 15px; + background: url('../images/powered-by-google.png') top right no-repeat; + background-size: auto 15px; +} + +/* Rating */ + +.facetwp-stars { + display: inline-block; + line-height: 1; + padding-right: 4px; + user-select: none; + unicode-bidi: bidi-override; + direction: rtl; +} + +.facetwp-star { + cursor: pointer; + font-size: 20px; + color: #ccc; +} + +.facetwp-star:not(.disabled):hover, +.facetwp-star:not(.disabled):hover ~ .facetwp-star, +.facetwp-star.selected, +.facetwp-star.selected ~ .facetwp-star { + color: #000; +} + +.facetwp-star.selected:hover, +.facetwp-star.selected:hover ~ .facetwp-star { + color: red; +} + +.facetwp-star.disabled, +.facetwp-star.disabled:hover { + cursor: auto; + color: #eeeeee; +} + +/* Date Range */ +.facetwp-date.disabled, +.facetwp-date.disabled::placeholder { + color: #b4b4b4; + opacity: 1; +} + +/* CSS animations */ + +@keyframes spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +/* Selections shortcode */ + +.facetwp-selections li { + display: inline-block; + line-height: 1; +} + +.facetwp-selections .facetwp-selection-value { + display: inline-block; + margin-right: 10px; + cursor: pointer; + + padding-right: 16px; + background-image: url('../images/icon-close.png'); + background-size: 12px 12px; + background-repeat: no-repeat; + background-position: right center; +} \ No newline at end of file diff --git a/wp-content/plugins/facetwp/assets/images/checkbox-on.png b/wp-content/plugins/facetwp/assets/images/checkbox-on.png new file mode 100644 index 000000000..f5bf39f54 Binary files /dev/null and b/wp-content/plugins/facetwp/assets/images/checkbox-on.png differ diff --git a/wp-content/plugins/facetwp/assets/images/checkbox.png b/wp-content/plugins/facetwp/assets/images/checkbox.png new file mode 100644 index 000000000..ac29efb5b Binary files /dev/null and b/wp-content/plugins/facetwp/assets/images/checkbox.png differ diff --git a/wp-content/plugins/facetwp/assets/images/icon-close.png b/wp-content/plugins/facetwp/assets/images/icon-close.png new file mode 100755 index 000000000..f8a96b2a8 Binary files /dev/null and b/wp-content/plugins/facetwp/assets/images/icon-close.png differ diff --git a/wp-content/plugins/facetwp/assets/images/icon-locate.png b/wp-content/plugins/facetwp/assets/images/icon-locate.png new file mode 100755 index 000000000..5288a280c Binary files /dev/null and b/wp-content/plugins/facetwp/assets/images/icon-locate.png differ diff --git a/wp-content/plugins/facetwp/assets/images/icon-search.png b/wp-content/plugins/facetwp/assets/images/icon-search.png new file mode 100644 index 000000000..148264d86 Binary files /dev/null and b/wp-content/plugins/facetwp/assets/images/icon-search.png differ diff --git a/wp-content/plugins/facetwp/assets/images/loading.png b/wp-content/plugins/facetwp/assets/images/loading.png new file mode 100755 index 000000000..39a59963e Binary files /dev/null and b/wp-content/plugins/facetwp/assets/images/loading.png differ diff --git a/wp-content/plugins/facetwp/assets/images/logo.svg b/wp-content/plugins/facetwp/assets/images/logo.svg new file mode 100644 index 000000000..673dfd54a --- /dev/null +++ b/wp-content/plugins/facetwp/assets/images/logo.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/wp-content/plugins/facetwp/assets/images/powered-by-google.png b/wp-content/plugins/facetwp/assets/images/powered-by-google.png new file mode 100644 index 000000000..64b2cab5a Binary files /dev/null and b/wp-content/plugins/facetwp/assets/images/powered-by-google.png differ diff --git a/wp-content/plugins/facetwp/assets/images/radio-on.png b/wp-content/plugins/facetwp/assets/images/radio-on.png new file mode 100644 index 000000000..d1e74ce88 Binary files /dev/null and b/wp-content/plugins/facetwp/assets/images/radio-on.png differ diff --git a/wp-content/plugins/facetwp/assets/images/radio.png b/wp-content/plugins/facetwp/assets/images/radio.png new file mode 100644 index 000000000..ae6223836 Binary files /dev/null and b/wp-content/plugins/facetwp/assets/images/radio.png differ diff --git a/wp-content/plugins/facetwp/assets/img/m1.png b/wp-content/plugins/facetwp/assets/img/m1.png new file mode 100644 index 000000000..329ff524c Binary files /dev/null and b/wp-content/plugins/facetwp/assets/img/m1.png differ diff --git a/wp-content/plugins/facetwp/assets/img/m2.png b/wp-content/plugins/facetwp/assets/img/m2.png new file mode 100644 index 000000000..b999cbcf6 Binary files /dev/null and b/wp-content/plugins/facetwp/assets/img/m2.png differ diff --git a/wp-content/plugins/facetwp/assets/img/m3.png b/wp-content/plugins/facetwp/assets/img/m3.png new file mode 100644 index 000000000..9f30b3092 Binary files /dev/null and b/wp-content/plugins/facetwp/assets/img/m3.png differ diff --git a/wp-content/plugins/facetwp/assets/img/m4.png b/wp-content/plugins/facetwp/assets/img/m4.png new file mode 100644 index 000000000..0d3f8263b Binary files /dev/null and b/wp-content/plugins/facetwp/assets/img/m4.png differ diff --git a/wp-content/plugins/facetwp/assets/img/m5.png b/wp-content/plugins/facetwp/assets/img/m5.png new file mode 100644 index 000000000..61387d2ab Binary files /dev/null and b/wp-content/plugins/facetwp/assets/img/m5.png differ diff --git a/wp-content/plugins/facetwp/assets/js/dist/admin.min.js b/wp-content/plugins/facetwp/assets/js/dist/admin.min.js new file mode 100644 index 000000000..210149ff5 --- /dev/null +++ b/wp-content/plugins/facetwp/assets/js/dist/admin.min.js @@ -0,0 +1,2092 @@ +(function () { + 'use strict'; + + (function ($) { + + $(function () { + init_vue(); + init_custom_js(); + }); + + function init_vue() { + + Vue.config.devtools = true; + + Vue.component('v-select', VueSelect.VueSelect); + + Vue.filter('i18n', function (str) { return FWP.__(str); }); + + // Defaults mixin + var builder_defaults = { + methods: { + defaultLayout: function defaultLayout() { + return { + items: [this.defaultRow()], + settings: this.getDefaultSettings('layout') + }; + }, + defaultRow: function defaultRow() { + return { + type: 'row', + items: [this.defaultCol()], + settings: this.getDefaultSettings('row') + }; + }, + defaultCol: function defaultCol() { + return { + type: 'col', + items: [], + settings: this.getDefaultSettings('col') + }; + }, + defaultItem: function defaultItem(source) { + return { + type: 'item', + source: source, + settings: this.getDefaultSettings('item', source) + }; + }, + mergeSettings: function mergeSettings(settings, type, source) { + var defaults = this.getDefaultSettings(type, source); + var default_keys = Object.keys(defaults); + var setting_keys = Object.keys(settings); + + // Automatically inject new settings + var missing_keys = default_keys.filter(function (name) { return !setting_keys.includes(name); }); + + missing_keys.forEach(function (name, index) { + Vue.set(settings, name, defaults[name]); + }); + + return settings; + }, + getSettingsMeta: function getSettingsMeta() { + var settings = { + num_columns: { + type: 'number', + title: FWP.__('Number of grid columns '), + defaultValue: 1 + }, + grid_gap: { + type: 'number', + title: FWP.__('Spacing between results'), + defaultValue: 10 + }, + no_results_text: { + type: 'textarea', + title: FWP.__('No results text') + }, + text_style: { + type: 'text-style', + title: FWP.__('Text style'), + tab: 'style', + defaultValue: { + align: '', + bold: false, + italic: false + } + }, + text_color: { + type: 'color', + title: FWP.__('Text color'), + tab: 'style' + }, + font_size: { + type: 'slider', + title: FWP.__('Font size'), + tab: 'style', + defaultValue: { + unit: 'px', + size: 0 + } + }, + background_color: { + type: 'color', + title: FWP.__('Background color'), + tab: 'style' + }, + border: { + type: 'border', + title: FWP.__('Border'), + tab: 'style', + defaultValue: { + style: 'none', + color: '', + width: { + unit: 'px', + top: 0, + right: 0, + bottom: 0, + left: 0 + } + }, + children: { + style: { + type: 'select', + title: FWP.__('Border style'), + choices: { + 'none': FWP.__('None'), + 'solid': FWP.__('Solid'), + 'dashed': FWP.__('Dashed'), + 'dotted': FWP.__('Dotted'), + 'double': FWP.__('Double') + } + }, + color: { + type: 'color', + title: FWP.__('Border color') + }, + width: { + type: 'utrbl', + title: FWP.__('Border width') + } + } + }, + button_text: { + type: 'text', + title: FWP.__('Button text') + }, + button_text_color: { + type: 'color', + title: FWP.__('Button text color') + }, + button_color: { + type: 'color', + title: FWP.__('Button color') + }, + button_padding: { + type: 'utrbl', + title: FWP.__('Button padding'), + defaultValue: { + unit: 'px', + top: 0, + right: 0, + bottom: 0, + left: 0 + } + }, + separator: { + type: 'text', + title: FWP.__('Separator'), + defaultValue: ', ' + }, + custom_css: { + type: 'textarea', + title: FWP.__('Custom CSS'), + tab: 'style' + }, + grid_template_columns: { + type: 'text', + title: FWP.__('Column widths'), + defaultValue: '1fr' + }, + content: { + type: 'textarea', + title: FWP.__('Content') + }, + image_size: { + type: 'select', + title: FWP.__('Image size'), + defaultValue: 'thumbnail', + choices: FWP.image_sizes, + v_show: [ + { type: 'source', value: 'featured_image' } + ] + }, + author_field: { + type: 'select', + title: FWP.__('Author field'), + defaultValue: 'display_name', + choices: { + 'display_name': FWP.__('Display name'), + 'user_login': FWP.__('User login'), + 'ID': FWP.__('User ID') + } + }, + field_type: { + type: 'select', + title: FWP.__('Field type'), + defaultValue: 'text', + choices: { + 'text': 'Text', + 'date': 'Date', + 'number': 'Number' + } + }, + date_format: { + type: 'text', + title: FWP.__('Date format'), + defaultValue: 'F j, Y', + v_show: [ + { type: 'field_type', value: 'date' }, + { type: 'source', value: 'post_date' }, + { type: 'source', value: 'post_modified' } + ] + }, + input_format: { + type: 'text', + title: FWP.__('Input format'), + defaultValue: 'Y-m-d', + v_show: [ + { type: 'field_type', value: 'date' }, + { type: 'source', value: 'post_date' }, + { type: 'source', value: 'post_modified' } + ] + }, + number_format: { + type: 'select', + title: FWP.__('Number format'), + choices: { + '': FWP.__('None'), + 'n': '1234', + 'n.n': '1234.5', + 'n.nn': '1234.56', + 'n,n': '1,234', + 'n,n.n': '1,234.5', + 'n,n.nn': '1,234.56' + }, + v_show: [ + { type: 'field_type', value: 'number' } + ] + }, + link: { + type: 'link', + title: FWP.__('Link'), + defaultValue: { + type: 'none', + href: '', + target: '' + }, + children: { + type: { + type: 'select', + title: FWP.__('Link type'), + choices: { + 'none': FWP.__('None'), + 'post': FWP.__('Post URL'), + 'custom': FWP.__('Custom URL') + } + } + } + }, + prefix: { + type: 'text', + title: FWP.__('Prefix') + }, + suffix: { + type: 'text', + title: FWP.__('Suffix') + }, + is_hidden: { + type: 'checkbox', + defaultValue: false, + suffix: FWP.__('Hide item?') + }, + padding: { + type: 'utrbl', + title: FWP.__('Padding'), + defaultValue: { + unit: 'px', + top: 0, + right: 0, + bottom: 0, + left: 0 + }, + tab: 'style' + }, + name: { + type: 'text', + title: FWP.__('Unique name'), + notes: '(Required) unique element name, without spaces' + }, + css_class: { + type: 'text', + title: FWP.__('CSS class'), + tab: 'style' + } + }; + + settings.button_border = this.$root.cloneObj(settings.border); + settings.button_border.title = FWP.__('Button border'); + settings.button_border.tab = 'basic'; + + settings.term_link = this.$root.cloneObj(settings.link); + settings.term_link.children.type.choices = { + 'none': FWP.__('None'), + 'term': FWP.__('Term URL'), + 'custom': FWP.__('Custom URL') + }; + + return settings; + }, + getDefaultFields: function getDefaultFields(type, source) { + var fields = []; + + if ('layout' == type) { + fields.push('num_columns', 'grid_gap', 'no_results_text'); + } + + if ('row' == type) { + fields.push('grid_template_columns'); + } + + if ('item' == type) { + if ('html' == source) { + fields.push('content'); + } + if ('featured_image' == source) { + fields.push('image_size', 'link'); + } + if ('button' == source) { + fields.push('button_text', 'button_text_color', 'button_color', 'button_padding', 'button_border', 'link'); + } + if ('post_date' == source || 'post_modified' == source) { + fields.push('date_format'); + } + if ('post_title' == source) { + fields.push('link'); + } + if ('post_author' == source) { + fields.push('author_field'); + } + if (0 === source.indexOf('cf/')) { + fields.push('field_type', 'date_format', 'input_format', 'number_format', 'link'); + } + if (0 === source.indexOf('woo/')) { + fields.push('field_type', 'date_format', 'input_format', 'number_format'); + } + if (0 === source.indexOf('tax/')) { + fields.push('separator', 'term_link'); + } + if (!['html', 'button', 'featured_image'].includes(source)) { + fields.push('prefix', 'suffix'); + } + } + + fields.push('border', 'background_color', 'padding', 'text_color', 'text_style', 'font_size', 'name', 'css_class'); + + if ('layout' == type) { + fields.push('custom_css'); + } + + if ('item' == type) { + fields.push('is_hidden'); + } + + return fields; + }, + getDefaultSettings: function getDefaultSettings(type, source) { + var settings = {}; + var settings_meta = this.getSettingsMeta(); + var fields = this.getDefaultFields(type, source); + + fields.forEach(function (name) { + var defaultValue = settings_meta[name].defaultValue || ''; + + if ('name' == name) { + defaultValue = 'el-' + Math.random().toString(36).substring(7); + } + + settings[name] = defaultValue; + }); + + return settings; + } + } + }; + + /* ================ query builder ================ */ + + Vue.component('query-builder', { + props: { + query_obj: { + type: Object, + required: true + }, + template: { + type: Object, + required: true + } + }, + template: "\n
\n

Which results should be in the listing?

\n\n
\n {{ 'Fetch' | i18n }}\n \n \n\n {{ 'and show' | i18n }}\n \n {{ 'per page' | i18n }}\n
\n\n
\n {{ 'Sort by' | i18n }}\n
\n\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n\n
\n {{ 'Narrow results by' | i18n }}\n
\n\n
\n \n \n \n \n \n\n \n\n \n\n \n
\n Type a value, then press \"Enter\" to add it\n
\n \n\n \n
\n\n
\n {{ 'Add query sort' | i18n }}\n {{ 'Add query filter' | i18n }}\n {{ 'Convert to query args' | i18n }}\n
\n
\n ", + methods: { + addTag: function addTag(newTag, value) { + value.push(newTag); + }, + getPlaceholder: function getPlaceholder(ref) { + var key = ref.key; + + return ('tax/' == key.substr(0, 4)) ? FWP.__('Enter term slugs') : FWP.__('Enter values'); + }, + maybeShowValue: function maybeShowValue(compare) { + return !['EXISTS', 'NOT EXISTS', 'EMPTY', 'NOT EMPTY'].includes(compare); + }, + showCompare: function showCompare(option, ref) { + var key = ref.key; + var type = ref.type; + + if ('tax/' == key.substr(0, 4)) { + if (!['IN', 'NOT IN', 'EXISTS', 'NOT EXISTS'].includes(option)) { + return false; + } + } + else if (['ID', 'post_author', 'post_status', 'post_name'].includes(key)) { + if (option != 'IN' && option != 'NOT IN') { + return false; + } + } + else if ('DATE' == type || 'post_date' == key || 'post_modified' == key) { + if (!['>', '>=', '<', '<='].includes(option)) { + return false; + } + } + else if ('CHAR' == type) { + if (['>', '>=', '<', '<='].includes(option)) { + return false; + } + } + return true; + }, + addSortCriteria: function addSortCriteria() { + this.query_obj.orderby.push({ + key: 'title', + order: 'ASC', + type: 'CHAR' + }); + }, + addFilterCriteria: function addFilterCriteria() { + this.query_obj.filters.push({ + key: 'ID', + value: [], + compare: 'IN', + type: 'CHAR' + }); + }, + deleteSortCriteria: function deleteSortCriteria(index) { + Vue.delete(this.query_obj.orderby, index); + }, + deleteFilterCriteria: function deleteFilterCriteria(index) { + Vue.delete(this.query_obj.filters, index); + } + } + }); + + Vue.component('fselect', { + data: function data() { + return { + prev_key: '' + }; + }, + props: ['row'], + template: "\n \n ", + mounted: function mounted() { + fSelect(this.$el); + }, + /** + * fSelects won't refresh when deleting, so we need to + * manually reload() the changed elements + */ + beforeUpdate: function beforeUpdate() { + this.prev_key = this.$el.getAttribute('data-key'); + }, + updated: function updated() { + if (this.row.key != this.prev_key) { + this.$el.fselect.reload(); + } + } + }); + + /* ================ layout builder ================ */ + + + Vue.component('builder', { + props: { + layout: Object + }, + template: "\n
\n
\n

How should an individual result appear?

\n
\n \n \n \n \n
\n
\n \n
\n " + }); + + Vue.component('setting-wrap', { + mixins: [builder_defaults], + props: ['settings', 'name', 'source', 'tab'], + template: "\n
\n
\n {{ title }}\n
\n
\n
\n
\n
\n ", + computed: { + getSettingComponent: function getSettingComponent() { + return 'setting-' + this.type; + }, + isVisible: function isVisible() { + var ret = true; + var self = this; + + if ('undefined' === typeof this.meta.tab) { + this.meta.tab = 'basic'; + } + + if (this.meta.tab !== this.tab) { + ret = false; + } + else if ('undefined' !== typeof this.meta.v_show) { + ret = false; + this.meta.v_show.forEach(function (cond, index) { + var type = cond.type; + var setting_val = ('source' == type) ? self[type] : self.settings[type]; + var cond_value = cond.value || ''; + var cond_compare = cond.compare || '=='; + var is_match = ('==' == cond_compare) + ? setting_val == cond_value + : setting_val != cond_value; + + if (is_match) { + ret = true; + } + }); + } + + return ret; + } + }, + created: function created() { + this.settings_meta = this.getSettingsMeta(); + this.meta = this.settings_meta[this.name]; + this.type = this.meta.type; + this.title = this.meta.title; + } + }); + + Vue.component('setting-text', { + props: ['settings', 'name', 'meta'], + template: '' + }); + + Vue.component('setting-number', { + props: ['settings', 'name', 'meta'], + template: '' + }); + + Vue.component('setting-textarea', { + props: ['settings', 'name', 'meta'], + template: '' + }); + + Vue.component('setting-slider', { + props: ['settings', 'name', 'meta'], + template: "\n
\n \n \n
\n ", + computed: { + fontSizeLabel: function fontSizeLabel() { + var val = this.settings[this.name]; + return (0 === val.size) ? 'none' : val.size + val.unit; + } + } + }); + + Vue.component('setting-color', { + props: ['settings', 'name', 'meta'], + template: "\n
\n
\n \n \n
\n X\n
", + mounted: function mounted() { + var self = this; + var $canvas = self.$el.getElementsByClassName('color-canvas')[0]; + var $preview = self.$el.getElementsByClassName('color-preview')[0]; + var $input = self.$el.getElementsByClassName('color-input')[0]; + var $clear = self.$el.getElementsByClassName('color-clear')[0]; + $preview.style.backgroundColor = $input.value; + + var picker = new Picker({ + parent: $canvas, + popup: 'left', + alpha: false, + onDone: function onDone(color) { + var hex = color.hex().substr(0, 7); + self.settings[self.name] = hex; + $preview.style.backgroundColor = hex; + } + }); + + picker.onOpen = function(color) { + picker.setColor($input.value); + }; + + $clear.addEventListener('click', function() { + self.settings[self.name] = ''; + $preview.style.backgroundColor = ''; + }); + } + }); + + Vue.component('setting-link', { + props: ['settings', 'name', 'meta'], + template: "\n
\n \n \n\n
\n \n
\n
\n \n {{ 'Open in new tab?' | i18n }}\n
\n
\n " + }); + + Vue.component('setting-border', { + props: ['settings', 'name', 'meta'], + template: "\n
\n \n \n\n
\n
\n\n \n \n\n
\n\n \n \n
\n
\n " + }); + + Vue.component('setting-checkbox', { + props: ['settings', 'name', 'meta'], + template: "\n
\n {{ meta.suffix }}\n
\n " + }); + + Vue.component('setting-select', { + props: ['settings', 'name', 'meta'], + template: "\n \n " + }); + + Vue.component('setting-utrbl', { + props: ['settings', 'name', 'meta'], + template: "\n
\n
unit
\n
top
\n
right
\n
bottom
\n
left
\n
\n " + }); + + Vue.component('setting-text-style', { + props: ['settings', 'name', 'meta'], + template: "\n
\n \n \n \n \n \n
\n ", + methods: { + toggleChoice: function toggleChoice(opt, val) { + var old_val = this.settings[this.name][opt]; + + if ('undefined' !== typeof val) { + this.settings[this.name][opt] = (val !== old_val) ? val : ''; + } + else { + this.settings[this.name][opt] = ! old_val; + } + }, + isActive: function isActive(opt, val) { + var new_val = ('undefined' !== typeof val) ? val : true; + return this.settings[this.name][opt] === new_val; + } + } + }); + + Vue.component('builder-settings', { + mixins: [builder_defaults], + props: { + layout: Object + }, + data: function data() { + return { + title: '', + type: 'layout', + settings: this.layout.settings, + source: '', + active_tab: 'basic' + } + }, + template: "\n
\n

\n \n {{ settingTitle }}\n

\n
\n
\n {{ 'Basic' | i18n }}\n {{ 'Style' | i18n }}\n
\n \n \n
\n
\n ", + computed: { + settingTitle: function settingTitle() { + return ('' === this.title) ? FWP.__('Settings') : this.title; + }, + settingsFields: function settingsFields() { + return this.getDefaultFields(this.type, this.source); + } + }, + methods: { + uniqueKey: function uniqueKey() { + // method to prevent caching + return Math.floor(Math.random() * 999999); + }, + isActiveTab: function isActiveTab(which) { + return (this.active_tab === which) ? 'active' : ''; + }, + setActiveTab: function setActiveTab(which) { + this.active_tab = which; + } + }, + created: function created() { + var self = this; + + this.$root.$on('edit-layout', function () { + self.title = ''; + self.type = 'layout'; + self.settings = self.mergeSettings(self.layout.settings, self.type); + self.source = ''; + }); + + this.$root.$on('edit-row', function (ref, num) { + var settings = ref.settings; + + self.title = FWP.__('Row') + ' ' + num; + self.type = 'row'; + self.settings = self.mergeSettings(settings, self.type); + self.source = ''; + }); + + this.$root.$on('edit-col', function (ref, num) { + var settings = ref.settings; + + self.title = FWP.__('Column') + ' ' + num; + self.type = 'col'; + self.settings = self.mergeSettings(settings, self.type); + self.source = ''; + }); + + this.$root.$on('edit-item', function (ref) { + var source = ref.source; + var settings = ref.settings; + + self.title = FWP.layout_data[source]; + self.type = 'item'; + self.settings = self.mergeSettings(settings, self.type, source); + self.source = source; + }); + } + }); + + Vue.component('builder-row', { + mixins: [builder_defaults], + props: { + row: Object, + rows: Array, + index: Number, + is_child: Boolean + }, + template: "\n
\n
\n \n \n \n \n
\n
\n \n \n
\n
\n ", + computed: { + classIsChild: function classIsChild() { + return this.is_child ? 'is-child' : 'not-child'; + } + }, + methods: { + addRow: function addRow() { + this.rows.splice(this.index + 1, 0, this.defaultRow()); + + if (1 < this.rows.length) { + this.$root.$emit('edit-row', this.rows[this.index + 1], this.index + 2); + } + else { + this.$root.$emit('edit-layout'); + } + }, + addCol: function addCol() { + var len = this.row.items.push(this.defaultCol()); + this.$root.$emit('edit-col', this.row.items[len - 1], len); + + var grid_str = '1fr '.repeat(this.row.items.length).trim(); + this.row.settings.grid_template_columns = grid_str; + }, + editRow: function editRow() { + this.$root.$emit('edit-row', this.row, this.index + 1); + }, + deleteRow: function deleteRow() { + Vue.delete(this.rows, this.index); + this.$root.$emit('edit-layout'); + + // Add default row + if (this.rows.length < 1) { + if (! this.is_child) { + this.addRow(); + } + } + } + } + }); + + Vue.component('builder-col', { + mixins: [builder_defaults], + props: { + col: Object, + cols: Array, + index: Number + }, + data: function data() { + return { + adding_item: false + } + }, + template: "\n
\n \n \n
\n \n \n
\n
\n \n
\n \n \n \n \n
\n
\n
+
\n
\n
\n
\n
\n ", + methods: { + addItem: function addItem() { + this.adding_item = ! this.adding_item; + }, + editCol: function editCol() { + this.$root.$emit('edit-col', this.col, this.index + 1); + this.adding_item = false; + }, + deleteCol: function deleteCol() { + // Remove the column + this.cols.splice(this.index, 1); + + // Show the "Layout" settings + this.$root.$emit('edit-layout'); + + // Add default column + if (this.cols.length < 1) { + this.cols.push(this.defaultCol()); + } + + // Adjust the row's `grid_template_columns` string + var grid_str = '1fr '.repeat(this.cols.length).trim(); + this.$parent.row.settings.grid_template_columns = grid_str; + }, + away: function away() { + this.adding_item = false; + } + } + }); + + Vue.component('col-resizer', { + props: { + cols: Array, + index: Number + }, + data: function data() { + return { + isResizing: false + } + }, + template: '
', + computed: { + classNames: function classNames() { + return [ + 'resizer', + this.isResizing ? 'is-resizing' : '' + ]; + } + }, + methods: { + onMouseDown: function onMouseDown(ref) { + var this$1$1 = this; + var resizer = ref.target; + var initialPageX = ref.pageX; + ref.pageY; + + if (! resizer.classList.contains('resizer')) { + return; + } + + var self = this; + var pane = resizer.parentElement; + var row_inner = pane.parentElement; + var initialPaneWidth = pane.offsetWidth; + + var resize = function (initialSize, offset) { + if ( offset === void 0 ) offset = 0; + + var containerWidth = row_inner.clientWidth; + var paneWidth = initialSize + offset; + var width = ((paneWidth / containerWidth) * 100).toFixed(1) + '%'; + var gridColumns = this$1$1.$parent.$parent.row.settings.grid_template_columns.split(' '); + + gridColumns[this$1$1.index] = width; + + this$1$1.$parent.$parent.row.settings.grid_template_columns = gridColumns.join(' '); + }; + + // This adds is-resizing class to container + self.isResizing = true; + + var onMouseMove = function (ref) { + var pageX = ref.pageX; + ref.pageY; + + resize(initialPaneWidth, pageX - initialPageX); + }; + + var onMouseUp = function () { + // Run resize one more time to set computed width/height. + resize(pane.clientWidth); + + // This removes is-resizing class to container + self.isResizing = false; + + window.removeEventListener('mousemove', onMouseMove); + window.removeEventListener('mouseup', onMouseUp); + }; + + window.addEventListener('mousemove', onMouseMove); + window.addEventListener('mouseup', onMouseUp); + } + } + }); + + Vue.component('builder-item', { + props: { + item: Object, + items: Array, + index: Number + }, + template: "\n
\n
\n \n
\n
\n \n \n
\n
\n ", + methods: { + editItem: function editItem() { + this.$root.$emit('edit-item', this.item); + }, + deleteItem: function deleteItem() { + this.items.splice(this.index, 1); + this.$root.$emit('edit-layout'); + } + } + }); + + Vue.component('popover', { + mixins: [builder_defaults], + props: { + col: Object + }, + data: function data() { + return { + keywords: '' + } + }, + template: "\n
\n
\n \n
\n
\n \n
\n
\n \n ", + methods: { + handleBlur: function handleBlur(e) { + if (!e.currentTarget.contains(e.relatedTarget)) { + this.$parent.adding_item = false; + } + }, + isMatch: function isMatch(label) { + var bool = ('' == this.keywords) ? true : false; + + if (false === bool) { + var needle = this.keywords.toLowerCase(); + var haystack = label.toLowerCase(); + if (haystack.includes(needle)) { + bool = true; + } + } + + return bool; + }, + saveItem: function saveItem(source) { + if ('row' == source) { + var len = this.col.items.push(this.defaultRow()); + this.$root.$emit('edit-row', this.col.items[len - 1], len); + } + else { + var len$1 = this.col.items.push(this.defaultItem(source)); + this.$root.$emit('edit-item', this.col.items[len$1 - 1]); + } + + this.$parent.adding_item = false; + } + }, + mounted: function mounted() { + this.$refs.keywords.focus(); + } + }); + + + /* ================ facets / templates ================ */ + + + Vue.component('facets', { + props: ['facets'], + template: "\n \n \n
\n
\n {{ facet.label }}\n \n
\n
{{ facet.name }}
\n
{{ facet.type }}
\n
\n
{{ getRowCount(facet.name) }}
\n
\n
\n
\n
\n
Copy shortcode
\n
Duplicate
\n
Delete
\n
\n
\n
\n \n
\n ", + methods: { + getSource: function getSource(source) { + return FWP.layout_data[source] || '-'; + }, + getRowCount: function getRowCount(facet_name) { + if (this.$root.is_indexing) { + return '...'; + } + return this.$root.row_counts[facet_name] || '-'; + } + } + }); + + Vue.component('templates', { + props: ['templates'], + template: "\n \n \n
\n
\n {{ template.label }}\n \n
\n
{{ template.name }}
\n
{{ getDisplayMode(index) }}
\n
{{ getPostTypes(index) }}
\n
\n
\n
\n
\n
Copy shortcode
\n
Duplicate
\n
Delete
\n
\n
\n
\n \n
\n ", + methods: { + getDisplayMode: function getDisplayMode(index) { + var template = this.templates[index]; + return ('undefined' !== typeof template.modes) ? template.modes.display : 'advanced'; + }, + getPostTypes: function getPostTypes(index) { + var template = this.templates[index]; + if ('undefined' !== typeof template.modes) { + if ('visual' == template.modes.query) { + var post_types = template.query_obj.post_type; + if (0 === post_types.length) { + return ''; + } + else { + return post_types.map(function (type) { return type.label; }).join(', '); + } + } + } + return ''; + } + } + }); + + Vue.component('facet-edit', { + data: function data() { + return { + facet: {}, + codeEditors: { + marker_content: null + } + } + }, + created: function created() { + this.facet = this.$root.editing; + }, + mounted: function mounted() { + if ('map' === this.facet.type) { + + // Init template code editor + this.initCodeMirror('marker-content-editor', 'marker_content', false); + } + }, + watch: { + 'facet.ui_type': function(val) { + switch (val) { + case 'radio': + Vue.delete(this.facet, 'multiple'); + Vue.delete(this.facet, 'show_expanded'); + Vue.delete(this.facet, 'hierarchical'); + break; + case 'dropdown': + Vue.delete(this.facet, 'multiple'); + Vue.delete(this.facet, 'show_expanded'); + break; + case 'checkboxes': + Vue.delete(this.facet, 'multiple'); + break; + case 'fselect': + Vue.delete(this.facet, 'show_expanded'); + break; + } + } + }, + methods: { + setName: function setName(e) { + this.facet.name = this.$root.sanitizeName(e.target.innerHTML); + }, + unlock: function unlock() { + Vue.delete(this.facet, '_code'); + }, + initCodeMirror: function initCodeMirror(textareaId, type, reset) { + try { + var textarea = document.getElementById(textareaId); + + // Reset editor and start over when switching facet type and back to Map + // Same behavior as other facet settings when switching facet type + if (reset) { + this.codeEditors[type] = null; + } + var editor = wp.codeEditor.initialize(jQuery(textarea), fwp_editor_settings); + + // Update when editor content changed + this.updateCodeMirrorContent(editor, type); + + // Store a reference to the CodeMirror instance + this.codeEditors[type] = editor; + + } catch (error) { + console.error('Error initializing CodeMirror:', error); + } + }, + updateCodeMirrorContent: function updateCodeMirrorContent(editor, type) { + var this$1$1 = this; + + + // Process editor changes so they can be saved + try { + if (editor !== null) { + editor.codemirror.on('change', function () { + var newContent = editor.codemirror.getValue(); + newContent = JSON.parse(JSON.stringify(newContent)); + Vue.set(this$1$1.facet, type, newContent); + }); + } + } catch (error) { + console.error('Error updating CodeMirror content:', error); + } + } + }, + template: "\n
\n
\n This facet is registered in code. Click to allow edits:\n \n
\n
\n
\n
{{ 'Label' | i18n }}
\n
\n \n \n \n {{ 'Copy shortcode' | i18n }}\n \n
\n
\n
\n
{{ 'Facet type' | i18n }}
\n
\n \n \n
\n
\n
\n
{{ 'Data source' | i18n }}
\n
\n \n
\n
\n \n
\n
\n " + }); + + Vue.component('template-edit', { + mixins: [builder_defaults], + data: function data() { + return { + template: {}, + tab: 'display', + codeEditors: { + template: null, + query: null + } + } + }, + created: function created() { + this.template = this.$root.editing; + + // Set defaults for the layout builder + if (! this.template.layout) { + Vue.set(this.template, 'layout', this.defaultLayout()); + } + + // Set defaults for the query builder + if (! this.template.query_obj) { + Vue.set(this.template, 'query_obj', { + post_type: [], + posts_per_page: 10, + orderby: [], + filters: [] + }); + } + + // Set the modes + if (! this.template.modes) { + Vue.set(this.template, 'modes', { + display: ('' !== this.template.template) ? 'advanced' : 'visual', + query: ('' !== this.template.query) ? 'advanced' : 'visual' + }); + } + }, + mounted: function mounted() { + var this$1$1 = this; + + + // Init template code editor + this.initCodeMirror('template-editor', 'template'); + + // Update query editor when 'Convert to query args' button is clicked + this.$root.$on('query-updated', function () { + this$1$1.codeEditors['query'].codemirror.setValue(this$1$1.template.query); + }); + }, + watch: { + tab: function tab(newTab, oldTab) { + + // Init query code editor + if (newTab !== oldTab) { + if (newTab === 'query') { + this.initCodeMirror('query-editor', 'query'); + this.refreshCodeMirror(this.codeEditors['query']); + } + } + } + }, + methods: { + setName: function setName(e) { + this.template.name = this.$root.sanitizeName(e.target.innerHTML); + }, + isMode: function isMode(mode) { + return this.template.modes[this.tab] === mode; + }, + switchMode: function switchMode() { + var now = this.template.modes[this.tab]; + this.template.modes[this.tab] = ('visual' === now) ? 'advanced' : 'visual'; + + var editor = null; + + // Refresh editor when switching to advanced on both tabs + if ('visual' === now) { + if (this.tab === 'display') { + editor = this.codeEditors['template']; + } else { + editor = this.codeEditors['query']; + } + } + this.refreshCodeMirror(editor); + + }, + unlock: function unlock() { + Vue.delete(this.template, '_code'); + }, + initCodeMirror: function initCodeMirror(textareaId, type) { + try { + var textarea = document.getElementById(textareaId); + + if (this.codeEditors[type] === null) { + var editor = wp.codeEditor.initialize(jQuery(textarea), fwp_editor_settings); + + // Update when editor content changed + this.updateCodeMirrorContent(editor, type); + + // Store a reference to the CodeMirror instance + this.codeEditors[type] = editor; + } + } catch (error) { + console.error('Error initializing CodeMirror:', error); + } + }, + refreshCodeMirror: function refreshCodeMirror(editor) { + if ( editor !== null ) { + this.$nextTick(function () { + editor.codemirror.refresh(); + }); + } + }, + updateCodeMirrorContent: function updateCodeMirrorContent(editor, type) { + var this$1$1 = this; + + + // Process editor changes so they can be saved + try { + if (editor !== null) { + editor.codemirror.on('change', function () { + var newContent = editor.codemirror.getValue(); + newContent = JSON.parse(JSON.stringify(newContent)); + Vue.set(this$1$1.template, type, newContent); + }); + } + } catch (error) { + console.error('Error updating CodeMirror content:', error); + } + } + }, + template: "\n
\n
\n This template is registered in code. Click to allow edits:\n \n
\n
\n
\n \n \n \n {{ 'Copy shortcode' | i18n }}\n \n
\n\n \n\n
\n {{ 'Display' | i18n }}\n {{ 'Query' | i18n }}\n
\n\n
\n
\n \n
\n
\n

{{ 'Display Code' | i18n }} {{ 'Help' | i18n }}

\n \n

{{ 'To search your code, click in the editor, then use Ctrl+F (Windows/Linux) or Cmd+F (Mac).' | i18n }}

\n
\n
\n\n
\n
\n \n
\n
\n

{{ 'Query Arguments' | i18n }} {{ 'Help' | i18n }}

\n \n

{{ 'To search your code, click in the editor, then use Ctrl+F (Windows/Linux) or Cmd+F (Mac).' | i18n }}

\n
\n
\n
\n
\n " + }); + + Vue.component('facet-types', { + props: ['facet', 'selected', 'types'], + template: "\n \n " + }); + + Vue.component('facet-settings', { + props: ['facet','initCodeMirror'], + template: '', + methods: { + getFields: function getFields(aliases) { + var output = []; + $.each(aliases, function(name) { + output = output.concat(FWP.facet_fields[name].names); + }); + return output; + } + }, + computed: { + // dynamic component so the data bindings (e.g. v-model) get compiled + dynComponent: function dynComponent() { + return { + template: '
' + this.settingsHtml + '
', + props: ['facet'] + } + }, + settingsHtml: function settingsHtml() { + var self = this; + var facet_obj = FWP.facet_types[self.facet.type]; + var aliases = facet_obj.fields; + + // Support for settings_html() in < 3.9 + if ('undefined' === typeof aliases) { + if ('undefined' !== typeof FWP.clone[self.facet.type]) { + FWP.facet_fields[self.facet.type + '_fields'] = { + names: [], + html: FWP.clone[self.facet.type] + }; + + var $html = $(FWP.clone[self.facet.type]); + $.each($html.nodes[0].children, function(chunk) { + $(chunk).find('input, textarea, select, [setting-name]').each(function() { + var $el = $(this); + var setting_name = $el.attr('setting-name'); + + if (null === setting_name) { + setting_name = $el.attr('class').split(' ')[0].replace(/-/g, '_').substr(6); + } + + FWP.facet_fields[self.facet.type + '_fields'].names.push(setting_name); + }); + }); + + aliases = [self.facet.type + '_fields']; + } + } + + // Get the actual fields by parsing the aliases (groups) + var fields = self.getFields(aliases); + var html = ''; + + var combined = ['label', 'name', 'type', 'source', '_code'].concat(fields); + + // Remove irrelevant settings + $.each(Object.keys(self.facet), function(setting_name) { + if (-1 == combined.indexOf(setting_name)) { + Vue.delete(self.facet, setting_name); + } + }); + + // Add new settings + $.each(aliases, function(alias_name) { + var $parsed = $(FWP.facet_fields[alias_name].html); + + $.each(FWP.facet_fields[alias_name].names, function(setting_name) { + var name_dashed = setting_name.replace(/_/g, '-'); + var $input = $parsed.find('.facet-' + name_dashed); + var val = $input.val(); + + if (0 < $input.len()) { + $input.attr('v-model', 'facet.' + setting_name); + + if ('undefined' === typeof self.facet[setting_name]) { + if ($input.is('[type=checkbox]')) { + val = $input.nodes[0].checked ? 'yes' : 'no'; + } + if ('[]' === val) { + val = []; + } + } + else { + val = self.facet[setting_name]; + Vue.delete(self.facet, setting_name); + } + + Vue.set(self.facet, setting_name, val); + } + }); + + // Update the documentFragment HTML to include the "v-model" + $.each($parsed.nodes[0].children, function(el) { + html += el.outerHTML; + }); + }); + + return html; + } + }, + watch: { + 'facet.type': function(val,oldVal) { + var this$1$1 = this; + + if ('search' == val || 'pager' == val || 'reset' == val || 'sort' == val) { + Vue.delete(this.facet, 'source'); + } + if ( val !== oldVal && 'undefined' != typeof this.facet.ui_type ) { + // reset ui_type when changing facet type + Vue.delete(this.facet, 'ui_type'); + } + if ('map' == val) { + this.$nextTick(function () { + + // Init template code editor + this$1$1.initCodeMirror('marker-content-editor', 'marker_content', true); + }); + } + }, + } + }); + + Vue.component('data-sources', { + props: { + facet: Object, + settingName: { + type: String, + default: 'source' + } + }, + template: "\n \n ", + computed: { + className: function className() { + return 'facet-' + this.settingName.replace(/_/g, '-'); + } + }, + mounted: function mounted() { + fSelect(this.$el); + } + }); + + Vue.component('facet-names', { + props: { + facet: Object, + setting: String + }, + template: "\n \n ", + computed: { + className: function className() { + return 'facet-' + this.setting.replace(/_/g, '-'); + } + }, + methods: { + bindSelectedClass: function bindSelectedClass(name) { + return this.facet[this.setting].includes(name) ? 'selected' : ''; + } + }, + created: function created() { + if ('undefined' === typeof this.facet[this.setting]) { + this.facet[this.setting] = []; + } + }, + mounted: function mounted() { + fSelect(this.$el, { 'placeholder': 'Choose facets' }); + } + }); + + Vue.component('sort-options', { + props: { + facet: Object + }, + template: "\n
\n
\n
\n \n \n\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n 1\">\n
\n
\n
\n 0\">\n \n
\n
\n\n
\n {{ 'Add sort' | i18n }}\n
\n
\n ", + methods: { + addSort: function addSort() { + this.facet.sort_options.push({ + label: 'New option', + name: 'new_option', + orderby: [{ + key: 'title', + order: 'ASC', + type: 'CHAR' + }] + }); + }, + addSortField: function addSortField(opts, index) { + opts.splice(index + 1, 0, { + key: 'title', + order: 'ASC', + type: 'CHAR' + }); + }, + moveUp: function moveUp(opts, index) { + opts.splice(index -1, 0, opts.splice(index, 1)[0]); + }, + removeItem: function removeItem(row, index) { + Vue.delete(row, index); + }, + setName: function setName(row, e) { + row.name = this.$root.sanitizeName(e.target.innerHTML); + } + } + }); + + Vue.component('color-picker', { + props: { + facet: Object, + settingName: { + type: String, + default: 'color' + }, + defaultColor: { + type: String, + default: '#000' + }, + }, + template: "\n
\n
\n \n \n
\n X\n
", + created: function created() { + if ('undefined' === typeof this.facet[this.settingName]) { + this.facet[this.settingName] = this.defaultColor; + } + }, + computed: { + className: function className() { + return 'facet-' + this.settingName.replace(/_/g, '-') + ' color-input'; + }, + colorName: function colorName() { + return this.defaultColor; + } + }, + mounted: function mounted() { + var self = this; + var $canvas = self.$el.getElementsByClassName('color-canvas')[0]; + var $preview = self.$el.getElementsByClassName('color-preview')[0]; + var $input = self.$el.getElementsByClassName('color-input')[0]; + var $clear = self.$el.getElementsByClassName('color-clear')[0]; + $preview.style.backgroundColor = $input.value; + + var picker = new Picker({ + parent: $canvas, + popup: 'right', + alpha: false, + onDone: function onDone(color) { + var hex = color.hex().substr(0, 7); + self.facet[self.settingName] = hex; + $input.value = hex; + $preview.style.backgroundColor = hex; + } + }); + + picker.onOpen = function(color) { + picker.setColor($input.value); + }; + + $clear.addEventListener('click', function() { + self.facet[self.settingName] = self.defaultColor; + $input.value = self.defaultColor; + $preview.style.backgroundColor = $input.value; + }); + } + }); + + // Vue instance + FWP.vue = new Vue({ + el: '#app', + data: { + app: FWP.data, + editing: {}, + editing_facet: false, + editing_template: false, + row_counts: {}, + active_tab: 'facets', + active_subnav: 'general', + is_support_loaded: false, + is_name_editable: false, + is_rebuild_open: false, + is_indexing: false, + timeout: null + }, + methods: { + addItem: function addItem(type) { + if ('facet' == type) { + var len = this.app.facets.push({ + 'name': 'new_facet', + 'label': 'New Facet', + 'type': 'checkboxes', + 'source': 'post_type' + }); + this.editItem('facet', this.app.facets[len-1]); + } + else { + var len$1 = this.app.templates.push({ + 'name': 'new_template', + 'label': 'New Template', + 'query': '', + 'template': '' + }); + this.editItem('template', this.app.templates[len$1-1]); + } + }, + duplicateItem: function duplicateItem(type, index) { + var facet = this.cloneObj(this.app[type + 's'][index]); + facet.label += ' (copy)'; + facet.name += '_copy'; + + this.app[type + 's'].splice(index+1, 0, facet); + this.editItem(type, facet); + }, + editItem: function editItem(type, data) { + this['editing_' + type] = true; + this.editing = data; + window.scrollTo(0, 0); + }, + doneEditing: function doneEditing() { + this.editing_template = false; + this.editing_facet = false; + this.editing = {}; + }, + tabClick: function tabClick(which) { + this.doneEditing(); + this.active_tab = which; + if ('support' === which) { + this.is_support_loaded = true; + } + var url = new URL(location); + url.searchParams.set("tab", which); + history.pushState({}, "", url); + }, + getItemLabel: function getItemLabel() { + return this.editing.label; + }, + deleteItem: function deleteItem(type, index) { + this.app[type + 's'].splice(index, 1); + }, + saveChanges: function saveChanges() { + window.setStatus('load', FWP.__('Saving') + '...'); + + var data = JSON.parse(JSON.stringify(FWP.data)); + + // Remove code-based facets and templates + data.facets = data.facets.filter(function (obj) { return 'undefined' === typeof obj['_code']; }); + data.templates = data.templates.filter(function (obj) { return 'undefined' === typeof obj['_code']; }); + + // Settings save hook + data = FWP.hooks.applyFilters('facetwp/save_settings', { + action: 'facetwp_save_settings', + nonce: FWP.nonce, + data: data + }); + + $.post(ajaxurl, data, { + done: function (ref) { + var code = ref.code; + var message = ref.message; + + var code = ('success' == code) ? 'ok' : code; + window.setStatus(code, message); + }, + fail: function (err) { + window.setStatus('error', err); + } + }); + }, + rebuildAction: function rebuildAction() { + this.is_indexing ? this.cancelReindex() : this.rebuildIndex(); + }, + rebuildIndex: function rebuildIndex() { + var self = this; + + if (this.is_indexing) { + return; + } + + this.is_indexing = true; + + $.post(ajaxurl, { action: 'facetwp_rebuild_index', nonce: FWP.nonce }); + window.setStatus('load', FWP.__('Indexing') + '... 0%'); + this.timeout = setTimeout(function () { + self.getProgress(); + }, 5000); + }, + cancelReindex: function cancelReindex() { + var self = this; + + $.post(ajaxurl, { + action: 'facetwp_get_info', + type: 'cancel_reindex', + nonce: FWP.nonce + }, { + done: function (ref) { + var message = ref.message; + + self.is_indexing = false; + clearTimeout(self.timeout); + window.setStatus('error', message); + } + }); + }, + getProgress: function getProgress() { + var self = this; + var isNumeric = function (obj) { return !Array.isArray(obj) && (obj - parseFloat(obj) + 1) >= 0; }; + + $.post(ajaxurl, { + action: 'facetwp_heartbeat', + nonce: FWP.nonce + }, { + done: function (data) { + if ('-1' == data.pct) { + self.is_indexing = false; + + if (data.rows.length < 1) { + window.setStatus('error', FWP.__('The index table is empty')); + } + else { + window.setStatus('ok', FWP.__('Indexing complete')); + + // Update the row counts + $.each(self.$root.app.facets, function(facet) { + Vue.set(self.row_counts, facet.name, data.rows[facet.name]); + }); + } + } + else if (isNumeric(data.pct)) { + window.setStatus('load', FWP.__('Indexing') + '... ' + data.pct + '%'); + self.is_indexing = true; + + self.timeout = setTimeout(function () { + self.getProgress(); + }, 5000); + } + else { + window.setStatus('error', data); + self.is_indexing = false; + } + } + }); + }, + getInfo: function getInfo(type, label) { + window.setStatus('load', FWP.__(label) + '...'); + + $.post(ajaxurl, { + action: 'facetwp_get_info', + type: type, + nonce: FWP.nonce + }, { + done: function (ref) { + var message = ref.message; + + window.setStatus('error', message); + } + }); + }, + getQueryArgs: function getQueryArgs(template) { + var this$1$1 = this; + + template.modes.query = 'advanced'; + template.query = FWP.__('Loading') + '...'; + + $.post(ajaxurl, { + action: 'facetwp_get_query_args', + query_obj: template.query_obj, + nonce: FWP.nonce + }, { + done: function (message) { + var json = JSON.stringify(message, null, 2); + json = "'); + template.query = json; + + // Emit an event when template.query is changed to trigger updating the editor + this$1$1.$root.$emit('query-updated'); + } + }); + }, + showIndexerStats: function showIndexerStats() { + this.getInfo('indexer_stats', 'Looking'); + }, + searchablePostTypes: function searchablePostTypes() { + this.getInfo('post_types', 'Looking'); + }, + purgeIndexTable: function purgeIndexTable() { + this.getInfo('purge_index_table', 'Purging'); + }, + copyToClipboard: function copyToClipboard(name, type, ref) { + var target = ref.target; + + var $this = $(target); + var $el = $('.facetwp-clipboard'); + var orig_text = $this.text(); + + try { + $el.removeClass('hidden'); + $el.val('[facetwp ' + type + '="' + name + '"]'); + $el.nodes[0].select(); + document.execCommand('copy'); + $el.addClass('hidden'); + $this.text(FWP.__('Copied!')); + } + catch(err) { + $this.text(FWP.__('Press CTRL+C to copy')); + } + + window.setTimeout(function () { + $this.text(orig_text); + }, 2000); + }, + activate: function activate() { + $('.facetwp-activation-status').html(FWP.__('Activating') + '...'); + + $.post(ajaxurl, { + action: 'facetwp_license', + nonce: FWP.nonce, + license: $('.facetwp-license').val() + }, { + done: function (ref) { + var message = ref.message; + + $('.facetwp-activation-status').html(message); + } + }); + }, + + checkApi: function checkApi(testTypes, apikey) { + var this$1$1 = this; + + + var statuses = {}; + var doTests = testTypes.split(","); + + var updateStatus = function ( test, message, status, action ) { + if ( status === void 0 ) status = 'none'; + if ( action === void 0 ) action = 'none'; + + var testEl = document.querySelector('.facetwp-api-status .test-' + test); + testEl.innerHTML = message; + testEl.classList = ''; + testEl.classList.add( 'test-'+test ); + testEl.classList.add( 'status-'+status); + testEl.classList.add( 'action-'+action); + statuses[test] = status; + }; + + if (!/AIza[0-9A-Za-z\-_]{35}/.test(apikey)) { + alert( FWP.__('Your Google Maps API key format is invalid. Please check your key before continuing.') ); + return false; // Invalid format + } + + var prompt = FWP.__('Each test will count as one billable API request in your Google Cloud account. Continue?'); + if (confirm(prompt)) { + updateStatus( 'status', FWP.__('Testing ... '), 'testing', 'testing' ); + } else { + return; + } + + var testFunctions = {}; + + testFunctions.checkPlaces = async function () { + updateStatus( 'places', FWP.__('Checking Places API (New) ...'), 'testing', 'testing' ); + var placeUrl = "https://places.googleapis.com/v1/places/ChIJj61dQgK6j4AR4GeTYWZsKWw?fields=id,displayName,formattedAddress&key=" + apikey; + + try { + var response = await fetch(placeUrl); + if (!response.ok) { + if ('403' == response.status) { + throw new Error('permission denied'); + } else { + throw new Error('unknown error'); + } + } else { + updateStatus('places', '' + FWP.__('Places API (New)') + ' ' + FWP.__('connected ') + '', 'pass', 'testing'); + } + } catch (error) { + updateStatus('places', '' + FWP.__('Places API (New)') + ' ' + FWP.__('connection failed: ') + error.message + '', 'fail', 'testing'); + } + }; + + testFunctions.checkPlaceslegacy = async function () { + updateStatus( 'placeslegacy', FWP.__('Checking Places API (Legacy) ...'), 'testing', 'testing' ); + + this$1$1.loadGmaps(apikey); + + var ref = await google.maps.importLibrary("places"); + var PlacesService = ref.PlacesService; + + var service = new PlacesService( + document.createElement('div') + ); + + service.getDetails({ + placeId: 'ChIJj61dQgK6j4AR4GeTYWZsKWw', + fields: ['geometry'] + }, function(place, status) { + if (status === google.maps.places.PlacesServiceStatus.OK) { + updateStatus('placeslegacy', '' + FWP.__('Places API (Legacy)') + ' ' + FWP.__('connected ') + '', 'pass', 'testing'); + } else { + updateStatus('placeslegacy', '' + FWP.__('Places API (Legacy)') + ' ' + FWP.__('connection failed: ') + status + '', 'fail', 'testing'); + } + }); + }; + + testFunctions.checkGeocoder = async function () { + updateStatus( 'geocoder', FWP.__('Checking Geocoding API ...'), 'testing', 'testing' ); + + this$1$1.loadGmaps(apikey); + + var ref = await google.maps.importLibrary("geocoding"); + var Geocoder = ref.Geocoder; + var latlng = { lat: 40.714224, lng: -73.961452 }; + var geocoding = new Geocoder; + + geocoding.geocode({ 'location': latlng }, function(results, status) { + if (status === google.maps.GeocoderStatus.OK) { + updateStatus('geocoder', '' + FWP.__('Geocoding API') + ' ' + FWP.__('connected ') + '', 'pass', 'testing'); + } else { + updateStatus('geocoder', '' + FWP.__('Geocoding API') + ' ' + FWP.__('connection failed: ') + status + '', 'fail', 'testing'); + } + }); + }; + + testFunctions.checkMaps = async function () { + + updateStatus( 'maps', FWP.__('Checking Maps JavaScript API ...'), 'testing', 'testing' ); + + this$1$1.loadGmaps(apikey); + + var ref = await google.maps.importLibrary("maps"); + var Map = ref.Map; + + var mapDiv = document.createElement("div"); + mapDiv.id = "map"; + mapDiv.style.width = "1px"; + mapDiv.style.height = "1px"; + document.getElementsByTagName('body')[0].appendChild(mapDiv); + var map = new Map(document.getElementById("map"), { + center: { lat: -34.397, lng: 150.644 }, + zoom: 8, + }); + map.addListener('tilesloaded', function() { + updateStatus( 'maps', '' + FWP.__('Maps JavaScript API') + ' ' + FWP.__('connected') + '', 'pass', 'testing' ); + }); + + }; + + var validTests = [ 'maps', 'places', 'placeslegacy', 'geocoder' ]; + validTests.forEach(function (t) { + if ( doTests.includes(t) ) { + var tn = "check"+t.charAt(0).toUpperCase()+t.slice(1); + testFunctions[tn](); + } + }); + + + setTimeout(function () { + updateStatus( 'status', FWP.__('Test results:'), 'done', 'testing' ); + 'testing' !== statuses.maps || updateStatus( 'maps', '' + FWP.__('Maps JavaScript API') + ' ' + FWP.__('connection failed') + '', 'fail', 'testing' ); + 'testing' !== statuses.geocoder || updateStatus( 'geocoder', '' + FWP.__('Geocoding API') + ' ' + FWP.__('connection failed') + '', 'fail', 'testing' ); + 'testing' !== statuses.places || updateStatus( 'places', '' + FWP.__('Places API (New)') + ' ' + FWP.__('connection failed') + '', 'fail', 'testing' ); + 'testing' !== statuses.placeslegacy || updateStatus( 'placeslegacy', '' + FWP.__('Places API (Legacy)') + ' ' + FWP.__('connection failed') + '', 'fail', 'testing' ); + + }, "2000"); + + }, + loadGmaps: function loadGmaps(apikey) { + /** we can only load the maps scripts once without reloading the page */ + if ( !this.$root.gmapsLoaded ) { + var scriptTag = document.createElement('script'); + scriptTag.id = "fwp-gmaps-api"; + scriptTag.innerText = '(g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=\`https://maps.${c}apis.com/maps/api/js?\`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})({ key: "'+apikey+'", v: "quarterly", });'; + document.getElementsByTagName('head')[0].appendChild(scriptTag); + this.$root.gmapsLoaded = true; + } + }, + resetCheckApi: function resetCheckApi() { + var prompt = FWP.__('FacetWP\'s settings page needs to reload to test a different Google Maps API key. Unsaved changes to other settings will be discarded. Continue?'); + if (confirm(prompt)) { + window.location.reload(); + } else { + return; + } + }, + isNameEditable: function isNameEditable(ref) { + var name = ref.name; + + this.is_name_editable = ('' == name || 'new_' == name.substr(0, 4)); + }, + maybeEditName: function maybeEditName(item) { + if (this.is_name_editable) { + item.name = this.sanitizeName(item.label); + } + }, + sanitizeName: function sanitizeName(name) { + var val = name.trim().toLowerCase(); + var res = [ 'pager', 'sort', 'labels', 'length', 'name', 'method', 'num_choices' ]; + val = val.replace(/[^\w- ]/g, ''); // strip invalid characters + val = val.replace(/[- ]/g, '_'); // replace space and hyphen with underscore + val = val.replace(/[_]{2,}/g, '_'); // strip consecutive underscores + val = res.includes(val) ? val + '_' : val; // reserved + return val; + }, + documentClick: function documentClick(ref) { + var target = ref.target; + + var el = target; + + if (! el.classList.contains('btn-caret')) { + this.is_rebuild_open = false; + } + }, + cloneObj: function cloneObj(obj) { + return JSON.parse(JSON.stringify(obj)); + } + }, + computed: { + isEditing: function isEditing() { + return this.editing_facet || this.editing_template; + }, + indexButtonLabel: function indexButtonLabel() { + return this.is_indexing ? FWP.__('Stop indexer') : FWP.__('Re-index'); + } + }, + created: function created() { + document.addEventListener('click', this.documentClick); + }, + mounted: function mounted() { + var params = new URLSearchParams(document.location.search); + var tab = params.get("tab"); + null == tab || !['facets', 'templates', 'settings', 'support'].includes(tab) || this.tabClick(tab); + this.getProgress(); + }, + watch: { + 'app.settings.gmaps_api_key': function(oldVal,newVal) { + if ( this.$root.gmapsLoaded && oldVal != newVal && '' != newVal) { + this.$root.gmapsChanged = true; + } + } + } + }); + } + + function init_custom_js() { + + window.setStatus = function (code, message) { + $('.facetwp-response').html(message); + $('.facetwp-response-icon').nodes[0].setAttribute('data-status', code); + + if ('error' == code) { + $('.facetwp-response').addClass('visible'); + } + }; + + $().on('click', '.facetwp-settings-section .facetwp-switch', function () { + window.setStatus('error', 'Press "Save changes" to apply'); + }); + + $().on('click', '.facetwp-response-wrap', function () { + $('.facetwp-response').toggleClass('visible'); + }); + + $().on('click', '.export-all', function (e) { + var $opts = $('.export-items').next('.fs-wrap'); + var selectedType = "undefined" != typeof e.target.dataset.value ? e.target.dataset.value+"-" : ""; + $opts.find('.fs-option:not(.selected)').each(function() { + var $opt = $(this); + if ( $opt.attr('data-value').includes(selectedType) ) { + $opt.trigger('click'); + } + }); + $opts.find('.fs-option.selected').each(function() { + var $opt = $(this); + if ( !$opt.attr('data-value').includes(selectedType) ) { + $opt.trigger('click'); + } + }); + $opts.nodes[0]._rel.fselect.open(); + e.stopImmediatePropagation(); + }); + + $().on('click', '.export-none', function (e) { + var $opts = $('.export-items').next('.fs-wrap'); + $opts.find('.fs-option.selected').each(function() { + var $opt = $(this); + $opt.trigger('click'); + }); + $opts.nodes[0]._rel.fselect.open(); + e.stopImmediatePropagation(); + }); + + // Export + $().on('click', '.export-submit', function () { + $('.import-code').val(FWP.__('Loading') + '...'); + + $.post(ajaxurl, { + action: 'facetwp_backup', + nonce: FWP.nonce, + action_type: 'export', + items: $('.export-items').val() + }, { + done: function (resp) { + $('.import-code').val(JSON.stringify(resp)); + } + }); + }); + + // Import + $().on('click', '.import-submit', function () { + window.setStatus('load', FWP.__('Importing') + '...'); + + try { + var code = JSON.parse($('.import-code').val()); + + $.post(ajaxurl, { + action: 'facetwp_backup', + nonce: FWP.nonce, + action_type: 'import', + import_code: code, + overwrite: $('.import-overwrite').nodes[0].checked ? 1 : 0 + }, { + dataType: 'text', + done: function (resp) { + window.setStatus('ok', resp); + setTimeout(function () { + window.location.reload(); + }, 1500); + } + }); + } + catch(err) { + window.setStatus('error', 'Invalid JSON'); + } + }); + + // Initialize tooltips + $().on('mouseover', '.facetwp-tooltip', function() { + if (!this.classList.contains('.ftip-enabled')) { + fTip(this, { + content: function (node) { return $(node).find('.facetwp-tooltip-content').html(); } + }).open(); + } + }); + + // fSelect + fSelect('.export-items'); + } + + })(fUtil); + +})(); diff --git a/wp-content/plugins/facetwp/assets/js/dist/front.min.js b/wp-content/plugins/facetwp/assets/js/dist/front.min.js new file mode 100644 index 000000000..d03e5ce82 --- /dev/null +++ b/wp-content/plugins/facetwp/assets/js/dist/front.min.js @@ -0,0 +1 @@ +!function(){"use strict";var e;window.fUtil=(()=>{class e{constructor(e){if("string"==typeof e||e instanceof String)if(""===(e=e.replace(":selected",":checked")))this.nodes=[];else if(this.isValidSelector(e))this.nodes=Array.from(document.querySelectorAll(e));else{var a=document.createElement("template");a.innerHTML=e,this.nodes=[a.content]}else Array.isArray(e)?this.nodes=e:"object"==typeof e&&e.nodeName?this.nodes=[e]:"function"==typeof e?this.ready(e):e===window?this.nodes=[window]:this.nodes=[document];t.each(t.fn,((e,t)=>{this[t]=e}))}static isset(e){return void 0!==e}static post(e,a,s){(s=Object.assign({},{dataType:"json",contentType:"application/json",headers:{},done:()=>{},fail:()=>{}},s)).headers["Content-Type"]=s.contentType,a="application/json"===s.contentType?JSON.stringify(a):t.toEncoded(a),fetch(e,{method:"POST",headers:s.headers,body:a}).then((e=>e.ok?e[s.dataType]():s.fail(e.status+" - "+e.statusText))).then((e=>s.done(e))).catch((e=>s.fail(e)))}static toEncoded(e,a,s){s=s||[],a=a||"";return Array.isArray(e)?e.length?e.forEach((e=>{t.toEncoded(e,a+"[]",s)})):t.toEncoded("",a,s):"object"==typeof e&&null!==e?Object.keys(e).forEach((o=>{var n=a?a+"["+o+"]":o;t.toEncoded(e[o],n,s)})):s.push(encodeURIComponent(a)+"="+encodeURIComponent(e)),s.join("&")}static forEach(e,t){return"object"==typeof e&&null!==e&&(Array.isArray(e)?e.forEach(((e,a)=>t.bind(e)(e,a))):Object.keys(e).forEach((a=>{var s=e[a];t.bind(s)(s,a)}))),e}isValidSelector(e){try{document.createDocumentFragment().querySelector(e)}catch(e){return!1}return!0}clone(){return t(this.nodes)}len(){return this.nodes.length}each(e){return this.nodes.forEach(((t,a)=>{e.bind(t)(t,a)})),this}ready(e){if("function"==typeof e)return"complete"===document.readyState?e():void document.addEventListener("DOMContentLoaded",e,!1)}addClass(e){return this.each((t=>t.classList.add(e))),this}removeClass(e){return this.each((t=>t.classList.remove(e))),this}hasClass(e){return t.isset(this.nodes.find((t=>t.classList.contains(e))))}toggleClass(e){return this.each((t=>t.classList.toggle(e))),this}is(e){for(let t=0;t{t=t.concat(Array.from(a.querySelectorAll(e)))})),a.nodes=t,a}first(){let e=this.clone();return e.len()&&(e.nodes=this.nodes.slice(0,1)),e}last(){let e=this.clone();return e.len()&&(e.nodes=this.nodes.slice(-1)),e}prev(e){let a=[],s=this.clone();return s.each((s=>{let o=s.previousElementSibling;for(;o&&t.isset(e)&&!o.matches(e);)o=o.previousElementSibling;o&&a.push(o)})),s.nodes=a,s}next(e){let a=[],s=this.clone();return s.each((s=>{let o=s.nextElementSibling;for(;o&&t.isset(e)&&!o.matches(e);)o=o.nextElementSibling;o&&a.push(o)})),s.nodes=a,s}prepend(e){return this.each((t=>t.insertAdjacentHTML("afterbegin",e))),this}append(e){return this.each((t=>t.insertAdjacentHTML("beforeend",e))),this}parents(e){let t=[],a=this.clone();return a.each((a=>{let s=a.parentNode;for(;s&&s!==document;)s.matches(e)&&t.push(s),s=s.parentNode})),a.nodes=[...new Set(t)],a}closest(e){let t=[],a=this.clone();return a.each((a=>{let s=a.closest(e);s&&t.push(s)})),a.nodes=t,a}remove(){return this.each((e=>e.remove())),this}on(e,a,s){if(t.isset(a)){if(!t.isset(s))s=a,a=null;var o=e=>{if(null===a||e.target.matches(a))s.bind(e.target)(e);else if(e.target.closest(a)){var t=e.target.closest(a);s.bind(t)(e)}};return this.each((n=>{t.isset(n._id)||(n._id=t.event.count,t.event.store[t.event.count]=n,t.event.count++);var c=n._id;o._str=s.toString(),t.isset(t.event.map[c])||(t.event.map[c]={}),t.isset(t.event.map[c][e])||(t.event.map[c][e]={}),t.isset(t.event.map[c][e][a])||(t.event.map[c][e][a]=[]);let r=t.event.map[c][e][a].push(o);n.addEventListener(e,t.event.map[c][e][a][r-1])})),this}}off(e,a,s){if(!t.isset(s))s=a,a=null;return this.each((o=>{var n=o._id;t.each(t.event.map[n],((c,r)=>{t.each(c,((c,i)=>{t.each(c,((c,l)=>{e&&r!==e||a&&i!==a||s&&c._str!==s.toString()||(o.removeEventListener(r,t.event.map[n][r][i][l]),delete t.event.map[n][r][i][l])}))}))}))})),this}trigger(e,t){return this.each((a=>a.dispatchEvent(new CustomEvent(e,{detail:t,bubbles:!0})))),this}attr(e,a){return t.isset(a)?(this.each((t=>t.setAttribute(e,a))),this):this.len()?this.nodes[0].getAttribute(e):null}data(e,a){return t.isset(a)?(this.each((t=>t._fdata[e]=a)),this):this.len()?this.nodes[0]._fdata[e]:null}html(e){return t.isset(e)?(this.each((t=>t.innerHTML=e)),this):this.len()?this.nodes[0].innerHTML:null}text(e){return t.isset(e)?(this.each((t=>t.textContent=e)),this):this.len()?this.nodes[0].textContent:null}val(e){if(t.isset(e))return this.each((t=>t.value=e)),this;if(this.len()){var a=this.nodes[0];return"select"===a.nodeName.toLowerCase()&&a.multiple?[...a.options].filter((e=>e.selected)).map((e=>e.value)):a.value}return null}}var t=t=>new e(t);return t.fn={},t.post=e.post,t.isset=e.isset,t.each=e.forEach,t.toEncoded=e.toEncoded,t.event={map:{},store:[],count:0},t})(),(e=window).FWP=e.FWP||{},e.FWP.hooks=e.FWP.hooks||new function(){function e(e,t,a,s){var o,c,r;if(n[e][t])if(a)if(o=n[e][t],s)for(r=o.length;r--;)(c=o[r]).callback===a&&c.context===s&&o.splice(r,1);else for(r=o.length;r--;)o[r].callback===a&&o.splice(r,1);else n[e][t]=[]}function t(e,t,a,s,o){var c={callback:a,priority:s,context:o},r=n[e][t];r?(r.push(c),r=function(e){for(var t,a,s,o=1,n=e.length;ot.priority;)e[a]=e[a-1],--a;e[a]=t}return e}(r)):r=[c],n[e][t]=r}function a(e,t,a){var s,o,c=n[e][t];if(!c)return"filters"===e&&a[0];if(o=c.length,"filters"===e)for(s=0;s{class t{constructor(){this.import(),this.bindEvents()}import(){"undefined"!=typeof FWP&&e.each(FWP,((e,t)=>this[t]=e))}init(){var t=this;if(this.setDefaults(),00)){var a=t.helper.detectLoop(document.body);if(!a)return void console.error("FacetWP has not detected a listing template");e(a).addClass("facetwp-template")}var s=e(".facetwp-template").first();t.template=s.attr("data-name")?s.attr("data-name"):"wp",s.find(".facetwp-facet").len()>0&&console.error('Facets should not be inside the "facetwp-template" container'),t.hooks.doAction("facetwp/ready"),t.extras.selections&&t.hooks.addAction("facetwp/loaded",(()=>{var a="",s=["pager","reset","sort"],o=t.loaded||t.is_bfcache||!e.isset(FWP_HTTP.url_vars)?t.facets:Object.assign(t.facets,FWP_HTTP.url_vars);e.each(o,((o,n)=>{if(o.length<1||!e.isset(t.settings.labels[n])||s.includes(t.facet_type[n]))return!0;var c=o,r=e(".facetwp-facet-"+n),i=r.attr("data-ui")||r.attr("data-type");(c=t.hooks.applyFilters("facetwp/selections/"+i,c,{el:r,selected_values:c})).length&&("string"==typeof c?c=[{value:"",label:c}]:e.isset(c[0].label)||(c=[{value:"",label:c[0]}]));var l="";e.each(c,(e=>{l+=''+t.helper.escapeHtml(e.label)+""})),""!==l&&(a+='
  • '+t.settings.labels[n]+": "+l+"
  • ")})),""!==a&&(a="
      "+a+"
    "),e(".facetwp-selections").html(a)})),t.refresh()}setDefaults(){let t={facets:{},template:null,settings:{},is_reset:!1,is_refresh:!1,is_bfcache:!1,is_hash_click:!1,is_load_more:!1,auto_refresh:!0,soft_refresh:!1,frozen_facets:{},active_facet:null,facet_type:{},loaded:!1,extras:{},paged:1};for(var a in t)e.isset(this[a])||(this[a]=t[a])}refresh(){FWP.is_refresh=!0,FWP.toggleOverlay("on"),FWP.is_reset||FWP.parseFacets(),FWP.loaded||FWP.loadFromHash(),e().trigger("facetwp-refresh"),!FWP.loaded||FWP.is_popstate||FWP.is_load_more||FWP.setHash(),FWP.loaded||FWP.is_bfcache||!e.isset(FWP_JSON.preload_data)?FWP.fetchData():FWP.render(FWP_JSON.preload_data),e.each(FWP.frozen_facets,((e,t)=>{"hard"!==e&&delete FWP.frozen_facets[t]})),FWP.paged=1,FWP.soft_refresh=!1,FWP.is_refresh=!1,FWP.is_reset=!1}autoload(){FWP.auto_refresh&&!FWP.is_refresh&&FWP.refresh()}parseFacets(){FWP.facets={},e(".facetwp-facet").each((function(){var t=e(this),a=t.attr("data-name"),s=t.attr("data-type"),o=t.hasClass("facetwp-ignore");null!==t.attr("data-ui")&&(s=t.attr("data-ui")),FWP.facet_type[a]=s,o||FWP.hooks.doAction("facetwp/refresh/"+s,t,a)}))}buildQueryString(){var t="",a=[],s=window.location.search.replace("?","").split("&");e.each(s,(e=>{0!==e.split("=")[0].indexOf(FWP_JSON.prefix)&&a.push(e)})),a=a.join("&");var o=Object.assign({},FWP.facets);return 1{var t=e.split("=");""!=t[0]&&(FWP_HTTP.get[t[0]]=t[1])}))}loadFromHash(){var t=[],a=window.location.search.replace("?","").split("&");e.each(a,(e=>{0===e.split("=")[0].indexOf(FWP_JSON.prefix)&&t.push(e.replace(FWP_JSON.prefix,""))})),t=t.join("&"),e.each(FWP.facets,((e,t)=>{FWP.facets[t]=[]})),FWP.paged=1,FWP.extras.sort="default",""!==t&&(t=t.split("&"),e.each(t,(t=>{var a=t.split("=")[0],s=t.split("=")[1];if("paged"===a)FWP.paged=s;else if("per_page"===a||"sort"===a)FWP.extras[a]=s;else if(""!==s){var o=e.isset(FWP.facet_type[a])?FWP.facet_type[a]:"";FWP.facets[a]="search"===o||"autocomplete"===o?decodeURIComponent(s):decodeURIComponent(s).split(",")}})))}buildPostData(){return{facets:FWP.facets,frozen_facets:FWP.frozen_facets,http_params:FWP_HTTP,template:FWP.template,extras:FWP.extras,soft_refresh:FWP.soft_refresh?1:0,is_bfcache:FWP.is_bfcache?1:0,first_load:FWP.loaded?0:1,paged:FWP.paged}}fetchData(){var t="wp"===FWP.template?document.URL:FWP_JSON.ajaxurl,a={action:"facetwp_refresh",data:FWP.buildPostData()},s={dataType:"text",done:t=>{try{var a=JSON.parse(t);FWP.render(a)}catch(o){var s=t.indexOf('{"facets');if(-1{console.log(e)}};s=FWP.hooks.applyFilters("facetwp/ajax_settings",s),e.post(t,a,s)}render(t){if(FWP.response=t,"wp"!==FWP.template&&""!==t.template||FWP.loaded||FWP.is_bfcache){n=t.template;if("wp"===FWP.template){var a=e(t.template),s=a.find(".facetwp-template");if(s.len()<1){var o=FWP.helper.detectLoop(a.nodes[0]);o&&(s=e(o).addClass("facetwp-template"))}if(s.len()>0)n=s.html();else n=FWP_JSON.no_results_text}}else var n=!1;!1!==n&&(FWP.hooks.applyFilters("facetwp/template_html",!1,{response:t,html:n})||e(".facetwp-template").html(n)),e.each(t.facets,((t,a)=>{e(".facetwp-facet-"+a).html(t)})),e.isset(t.counts)&&e(".facetwp-counts").html(t.counts),e.isset(t.pager)&&e(".facetwp-pager").html(t.pager),e.isset(t.per_page)&&(e(".facetwp-per-page").html(t.per_page),"default"!==FWP.extras.per_page&&e(".facetwp-per-page-select").val(FWP.extras.per_page)),e.isset(t.sort)&&(e(".facetwp-sort").html(t.sort),e(".facetwp-sort-select").val(FWP.extras.sort)),e.each(t.settings,((e,t)=>{FWP.settings[t]=e})),"function"==typeof WPPlaylistView&&e(".facetwp-template .wp-playlist").each((e=>new WPPlaylistView({el:e}))),e().trigger("facetwp-loaded"),FWP.hooks.doAction("facetwp/loaded"),FWP.toggleOverlay("off"),FWP.active_facet=null,FWP.is_bfcache=!0,FWP.loaded=!0}reset(t){FWP.parseFacets();var a={};"string"==typeof t?a[t]="":Array.isArray(t)?e.each(t,(e=>{a[e]=""})):"object"==typeof t&&t&&(a=t);var s=Object.keys(a).length<1;e.each(FWP.facets,((t,o)=>{var n=e.isset(a[o]),c=Array.isArray(t)?t:[t];if(n&&-1{FWP.is_bfcache&&(FWP.loaded=!1),!FWP.loaded&&!FWP.is_bfcache||FWP.is_refresh||FWP.is_hash_click||(FWP.is_popstate=!0,FWP.refresh(),FWP.is_popstate=!1),FWP.is_hash_click=!1})),e().on("click",'a[href^="#"]',(()=>{FWP.is_hash_click=!0})),e().on("click",".facetwp-selections .facetwp-selection-value",(function(){if(!FWP.is_refresh){var t=e(this).closest("li").attr("data-facet"),a=e(this).attr("data-value");if(""!=a){var s={};s[t]=a,FWP.reset(s)}else FWP.reset(t)}})),e().on("click",".facetwp-page[data-page]",(function(){e(".facetwp-page").removeClass("active"),e(this).addClass("active"),FWP.paged=e(this).attr("data-page"),FWP.soft_refresh=!0;let t=e(this).closest(".facetwp-type-pager").attr("data-name");FWP.scroll_target="string"==typeof t?FWP.settings[t].scroll_target:"",FWP.scroll_offset=""!=FWP.scroll_target&&"number"==typeof Number(FWP.settings[t].scroll_offset)?FWP.settings[t].scroll_offset:0,FWP.refresh()})),FWP.hooks.addAction("facetwp/loaded",(function(){try{if(!FWP.loaded&&1FWP.settings[e].hasOwnProperty("scroll_target")))[0];FWP.scroll_target="string"==typeof e?FWP.settings[e].scroll_target:"",FWP.scroll_offset=""!=FWP.scroll_target&&"number"==typeof Number(FWP.settings[e].scroll_offset)?FWP.settings[e].scroll_offset:0}let e=document.querySelector(FWP.scroll_target).getBoundingClientRect().top+Number(FWP.scroll_offset);window.scrollBy({top:e,behavior:"smooth"})}catch(e){}FWP.scroll_target="",FWP.scroll_offset=""}),100);var t="function"==typeof jQuery?jQuery:fUtil;t(document).on("change",".facetwp-per-page-select",(function(){FWP.extras.per_page=e(this).val(),FWP.soft_refresh=!0,FWP.autoload()})),t(document).on("change",".facetwp-sort-select",(function(){FWP.extras.sort=e(this).val(),FWP.soft_refresh=!0,FWP.autoload()})),t((()=>{this.init()}))}}return t.prototype.helper={getUrlVar:e=>{e=FWP_JSON.prefix+e;for(var t=window.location.search.replace("?","").split("&"),a=0;a{var a;return function(...s){var o=e.bind(this,...s);clearTimeout(a),a=setTimeout(o,t)}},serialize:(t,a)=>{var s=[];a=e.isset(a)?a:"";for(var o in t)""!=t[o]&&s.push(a+encodeURIComponent(o)+"="+encodeURIComponent(t[o]));return s.join("&")},escapeHtml:e=>{var t={"&":"&","<":"<",">":">",'"':""","'":"'"};return e.replace(/[&<>"']/g,(e=>t[e])).trim()},detectLoop:e=>{for(var t=null,a=document.createNodeIterator(e,NodeFilter.SHOW_COMMENT,(()=>NodeFilter.FILTER_ACCEPT),!1);t=a.nextNode();)if(8===t.nodeType&&"fwp-loop"===t.nodeValue)return t.parentNode;return!1}},new t})(fUtil),function(e){function t(t){let a=e(t);return a.val()===a.attr("placeholder")?"":a.val()}FWP.logic=FWP.logic||{},e(".facetwp-facet").each((function(){this.addEventListener("mousedown",(function(){var t=e(".facetwp-facet-"+e(this).attr("data-name"));1e.post(s,{action:"facetwp_autocomplete_load",facet_name:a,query:t.value,data:FWP.buildPostData()},{done:e=>{this.fcomplete.render(e)}}),o.onSelect=()=>FWP.autoload(),fComplete(t,o)}))})),e().on("keyup",".facetwp-autocomplete",(function(e){13!==e.which||FWP.is_refresh||FWP.autoload()})),e().on("click",".facetwp-autocomplete-update",(function(){FWP.autoload()})),FWP.hooks.addAction("facetwp/refresh/checkboxes",(function(t,a){var s=[];t.find(".facetwp-checkbox.checked").each((function(){s.push(e(this).attr("data-value"))})),FWP.facets[a]=s})),FWP.hooks.addFilter("facetwp/selections/checkboxes",(function(t,a){var s=[];return e.each(a.selected_values,(function(t){var o=a.el.find('.facetwp-checkbox[data-value="'+t+'"]');if(o.len()){var n=e(o.html());n.find(".facetwp-counter").remove(),n.find(".facetwp-expand").remove(),s.push({value:t,label:n.text()})}})),s})),e().on("click",".facetwp-type-checkboxes .facetwp-expand",(function(t){var a=e(this).closest(".facetwp-checkbox").next(".facetwp-depth");a.toggleClass("visible");var s=a.hasClass("visible")?FWP_JSON.collapse:FWP_JSON.expand;e(this).html(s),t.stopImmediatePropagation()})),e().on("click",".facetwp-type-checkboxes .facetwp-checkbox:not(.disabled)",(function(){var t=e(this),a=!t.hasClass("checked"),s=t.closest(".facetwp-depth").len()>0;t.next().hasClass("facetwp-depth")&&t.next(".facetwp-depth").find(".facetwp-checkbox").removeClass("checked"),s&&t.parents(".facetwp-depth").each((function(){e(this).prev(".facetwp-checkbox").removeClass("checked")})),t.toggleClass("checked",a),FWP.autoload()})),e().on("click",".facetwp-type-checkboxes .facetwp-toggle",(function(){var t=e(this).closest(".facetwp-facet");t.find(".facetwp-toggle").toggleClass("facetwp-hidden"),t.find(".facetwp-overflow").toggleClass("facetwp-hidden")})),e().on("facetwp-loaded",(function(){e(".facetwp-type-checkboxes .facetwp-overflow").each((function(){var t=e(this).find(".facetwp-checkbox").len(),a=e(this).next(".facetwp-toggle");a.text(a.text().replace("{num}",t)),0t.find(".facetwp-expand").len()&&(t.find(".facetwp-checkbox.checked").each((function(){e(this).parents(".facetwp-depth").addClass("visible")})),t.find(".facetwp-depth").each((function(){var t=e(this).hasClass("visible")?"collapse":"expand";e(this).prev(".facetwp-checkbox").append(' '+FWP_JSON[t]+"")}))))}))})),FWP.hooks.addAction("facetwp/refresh/radio",(function(t,a){var s=[];t.find(".facetwp-radio.checked").each((function(){var t=e(this).attr("data-value");""!==t&&s.push(t)})),FWP.facets[a]=s})),FWP.hooks.addFilter("facetwp/selections/radio",(function(t,a){var s=[];return e.each(a.selected_values,(function(t){var o=a.el.find('.facetwp-radio[data-value="'+t+'"]');if(o.len()){var n=e(o.html());n.find(".facetwp-counter").remove(),s.push({value:t,label:n.text()})}})),s})),e().on("click",".facetwp-type-radio .facetwp-radio:not(.disabled)",(function(){var t=e(this).hasClass("checked");e(this).closest(".facetwp-facet").find(".facetwp-radio").removeClass("checked"),t||e(this).addClass("checked"),FWP.autoload()})),FWP.hooks.addAction("facetwp/refresh/date_range",(function(e,a){var s=e.find(".facetwp-date-min"),o=e.find(".facetwp-date-max"),n=s.len()?t(s.nodes[0]):"",c=o.len()?t(o.nodes[0]):"";FWP.facets[a]=""!==n||""!==c?[n,c]:[]})),FWP.hooks.addFilter("facetwp/selections/date_range",(function(e,t){var a=t.el,s=t.selected_values,o=a.attr("data-name"),n=FWP.settings[o].fields,c="";return"exact"==n?""!==s[0]&&(c=s[0]):"start_date"==n?""!==s[0]&&(c="[>=] "+s[0]):"end_date"==n?""!==s[1]&&(c="[<=] "+s[1]):"both"==n&&(""===s[0]&&""===s[1]||(""!==s[0]&&""!==s[1]?c=s[0]+" - "+s[1]:""!==s[0]?c="[>=] "+s[0]:""!==s[1]&&(c="[<=] "+s[1]))),c})),e().on("facetwp-loaded",(function(){var t=e(".facetwp-type-date_range .facetwp-date:not(.ready)");0!==t.len()&&t.each((function(){var t=e(this),a=t.closest(".facetwp-facet").attr("data-name"),s=FWP.settings[a],o={onChange:function(e){FWP.autoload()}};if(""!==s.locale&&(o.i18n=s.locale),""!==s.format&&(o.altFormat=s.format),"both"==s.fields){var n=t.hasClass("facetwp-date-min")?"min":"max";o.minDate=s.range[n].minDate,o.maxDate=s.range[n].maxDate}else o.minDate=s.range.minDate,o.maxDate=s.range.maxDate;if(o=FWP.hooks.applyFilters("facetwp/set_options/date_range",o,{facet_name:a,element:t}),t.addClass("ready"),o.minDate.length&&o.maxDate.length)new fDate(this,o);else{this.value="";let e=t.attr("data-empty");t.attr("placeholder",e).attr("disabled","disabled").addClass("disabled")}}))})),FWP.hooks.addAction("facetwp/refresh/dropdown",(function(e,t){var a=e.find(".facetwp-dropdown").val();FWP.facets[t]=a?[a]:[]})),FWP.hooks.addFilter("facetwp/selections/dropdown",(function(e,t){var a=t.el.find(".facetwp-dropdown");if(a.len()){var s=a.nodes[0];return s.options[s.selectedIndex].text.replace(/\(\d+\)$/,"")}return""})),("function"==typeof jQuery?jQuery:fUtil)(document).on("change",".facetwp-type-dropdown select",(function(){var t=e(this).closest(".facetwp-facet").attr("data-name");""!==e(this).val()&&(FWP.frozen_facets[t]="soft"),FWP.autoload()})),FWP.hooks.addAction("facetwp/refresh/fselect",(function(e,t){var a=e.find("select").val();null!==a&&""!==a||(a=[]),FWP.facets[t]=Array.isArray(a)?a:[a]})),FWP.hooks.addFilter("facetwp/selections/fselect",(function(t,a){var s=[];return e.each(a.selected_values,(e=>{var t=a.el.find('option[value="'+e+'"]');t.len()&&s.push({value:e,label:t.text()})})),s})),FWP.hooks.addAction("facetwp/loaded",(function(){if(null!==FWP.active_facet){var e=FWP.active_facet;if("fselect"==e.attr("data-type")){var t=e.find(".facetwp-dropdown").nodes[0];t.fselect.settings.multiple&&t.fselect.open()}}})),e().on("facetwp-loaded",(function(){e(".facetwp-type-fselect select:not(.fs-hidden)").each((function(){var t=e(this).closest(".facetwp-facet").attr("data-name"),a=FWP.settings[t];a.optionFormatter=function(e,t){var a=t.getAttribute("data-counter");return a?e+" ("+a+")":e};var s=FWP.hooks.applyFilters("facetwp/set_options/fselect",a,{facet_name:t});fSelect(this,s)}))})),e().on("fs:changed",(function(t){var a=e(t.detail[0]).closest(".facetwp-type-fselect").len()>0;!FWP.is_refresh&&a&&FWP.autoload()})),e().on("fs:closed",(function(){FWP.active_facet=null})),FWP.hooks.addAction("facetwp/refresh/hierarchy",(function(t,a){var s=[];t.find(".facetwp-link.checked").each((function(){s.push(e(this).attr("data-value"))})),FWP.facets[a]=s})),FWP.hooks.addFilter("facetwp/selections/hierarchy",(function(e,t){var a=t.el.find(".facetwp-link.checked");return a.len()?a.text():""})),e().on("click",".facetwp-type-hierarchy .facetwp-link",(function(){e(this).closest(".facetwp-facet").find(".facetwp-link").removeClass("checked"),""!==e(this).attr("data-value")&&e(this).addClass("checked"),FWP.autoload()})),e().on("click",".facetwp-type-hierarchy .facetwp-toggle",(function(){var t=e(this).closest(".facetwp-facet");t.find(".facetwp-toggle").toggleClass("facetwp-hidden"),t.find(".facetwp-overflow").toggleClass("facetwp-hidden")})),e().on("facetwp-loaded",(function(){e(".facetwp-type-hierarchy .facetwp-overflow").each((function(){var t=e(this).find(".facetwp-link").len(),a=e(this).next(".facetwp-toggle");a.text(a.text().replace("{num}",t))}))})),FWP.hooks.addAction("facetwp/refresh/number_range",(function(e,t){var a=e.find(".facetwp-number-min").val()||"",s=e.find(".facetwp-number-max").val()||"";FWP.facets[t]=""!==a||""!==s?[a,s]:[]})),FWP.hooks.addFilter("facetwp/selections/number_range",(function(e,t){var a=t.el,s=t.selected_values,o=a.attr("data-name"),n=FWP.settings[o].fields,c="";return"exact"==n?""!==s[0]&&(c=s[0]):"min"==n?""!==s[0]&&(c="[>=] "+s[0]):"max"==n?""!==s[1]&&(c="[<=] "+s[1]):"both"==n&&(""===s[0]&&""===s[1]||(""!==s[0]&&""!==s[1]?c=s[0]+" - "+s[1]:""!==s[0]?c="[>=] "+s[0]:""!==s[1]&&(c="[<=] "+s[1]))),c})),e().on("keyup",".facetwp-type-number_range .facetwp-number",(function(e){13!==e.which||FWP.is_refresh||FWP.autoload()})),e().on("click",".facetwp-type-number_range .facetwp-submit",(function(){FWP.refresh()})),e().on("facetwp-loaded",(async function(){var t=e(".facetwp-location");if(!(t.nodes.length<1)){if(!FWP.loaded)if(window.FWP_MAP=window.FWP_MAP||{},"place-class"==FWP.settings.places)try{const{AutocompleteSessionToken:t,AutocompleteSuggestion:a}=await google.maps.importLibrary("places");e().on("input",".facetwp-location",FWP.helper.debounce((function(s){const o=e(s.target).val(),n=e(s.target).closest(".facetwp-facet");if(!o||o.length{if(e&&e.length>0){let t="";e.forEach(((e,a)=>{let s=e.placePrediction;t+='
    ',t+=''+(s.mainText?.toString()??"")+" ",t+=''+(s.secondaryText?.toString()??"")+"",t+=''+(s.text?.toString()??"")+"",t+="
    "})),t+='
    ',n.find(".location-results").html(t).removeClass("facetwp-hidden")}else n.find(".location-results").html("").addClass("facetwp-hidden")})).catch((e=>{console.error("Error fetching predictions:",e),n.find(".location-results").html('
    Error fetching results.
    ').removeClass("facetwp-hidden")}))}),FWP_JSON.proximity.queryDelay))}catch(e){console.error("Error loading Places library:",e)}else await google.maps.importLibrary("places"),FWP_MAP.sessionToken=new google.maps.places.AutocompleteSessionToken,FWP_MAP.autocompleteService=new google.maps.places.AutocompleteService,FWP_MAP.placesService=new google.maps.places.PlacesService(document.createElement("div")),e().on("input",".facetwp-location",FWP.helper.debounce((function(t){var a=e(t.target).val(),s=e(t.target).closest(".facetwp-facet");if(""==a||a.length',a+=''+e.structured_formatting.main_text+" ",a+=''+e.structured_formatting.secondary_text+"",a+=''+e.description+"",a+=""})),a+='
    ',s.find(".location-results").html(a).removeClass("facetwp-hidden")}}))}}),FWP_JSON.proximity.queryDelay));t.each((function(t,a){e(this).trigger("keyup")}))}})),e().on("click",".location-result",(async function(){if("place-class"==FWP.settings.places){const t=e(this).closest(".facetwp-facet"),a=e(this).attr("data-place-id"),{Place:s,AutocompleteSessionToken:o}=await google.maps.importLibrary("places");FWP_MAP.token||=new o;const n=["location"],c=new s({id:a,sessionToken:FWP_MAP.token});c.fetchFields({fields:n}).then((()=>{t.find(".facetwp-lat").val(c.location.lat()),t.find(".facetwp-lng").val(c.location.lng()),FWP.autoload()})).catch((e=>{console.error("Error fetching place details:",e)})),FWP_MAP.token=null}else{var t=e(this).closest(".facetwp-facet"),a=e(this).attr("data-id");FWP_MAP.placesService.getDetails({placeId:a,fields:["geometry"],sessionToken:FWP_MAP.sessionToken},(function(e,a){a===google.maps.places.PlacesServiceStatus.OK&&(t.find(".facetwp-lat").val(e.geometry.location.lat()),t.find(".facetwp-lng").val(e.geometry.location.lng()),FWP.autoload())}))}var s=e(this).find(".result-description").text();e(".facetwp-location").val(s),e(".location-results").addClass("facetwp-hidden")})),e().on("click",".facetwp-type-proximity .locate-me",(function(t){var a=e(this),s=a.closest(".facetwp-facet"),o=s.find(".facetwp-location"),n=s.find(".facetwp-lat"),c=s.find(".facetwp-lng");if(a.hasClass("f-reset"))return n.val(""),c.val(""),o.val(""),void FWP.autoload();a.addClass("f-loading"),navigator.geolocation?navigator.geolocation.getCurrentPosition((function(e){var t=e.coords.latitude,r=e.coords.longitude;n.val(t),c.val(r);var i=new google.maps.Geocoder,l={lat:parseFloat(t),lng:parseFloat(r)};i.geocode({location:l},(function(e,t){t===google.maps.GeocoderStatus.OK?o.val(e[0].formatted_address):(console.error("Geocoder failed due to: "+t),o.val("Your location"),alert("Could not determine your address.")),a.addClass("f-reset"),FWP.autoload()})),a.removeClass("f-loading"),FWP.hooks.doAction("facetwp/geolocation/success",{facet:s,position:e})}),(function(e){a.removeClass("f-loading");let t="Could not get your location.";switch(e.code){case e.PERMISSION_DENIED:t="User denied the request for Geolocation.";break;case e.POSITION_UNAVAILABLE:t="Location information is unavailable.";break;case e.TIMEOUT:t="The request to get user location timed out.";break;case e.UNKNOWN_ERROR:t="An unknown error occurred."}console.error("Geolocation error: "+t,e),alert(t),FWP.hooks.doAction("facetwp/geolocation/error",{facet:s,error:e})})):(a.removeClass("f-loading"),alert("Your browser doesn't support Geolocation."))})),e().on("keyup",".facetwp-location",(function(t){var a=e(this).closest(".facetwp-facet"),s=""!==e(this).val()?"addClass":"removeClass";if(a.find(".locate-me")[s]("f-reset"),38===t.which||40===t.which||13===t.which)var o=parseInt(a.find(".location-result.active").attr("data-index")),n=parseInt(a.find(".location-result").last().attr("data-index"));if(38===t.which){var c=0e.removeAttribute("disabled")))})),FWP.hooks.addAction("facetwp/set_label/slider",(function(e){var t=e.attr("data-name"),a=FWP.settings[t].lower,s=FWP.settings[t].upper,o=FWP.settings[t].format,n={decimal_separator:FWP.settings[t].decimal_separator,thousands_separator:FWP.settings[t].thousands_separator},c=FWP.settings[t].prefix,r=FWP.settings[t].suffix;if(a===s)var i=c+nummy(a).format(o,n)+r;else i=c+nummy(a).format(o,n)+r+" — "+c+nummy(s).format(o,n)+r;e.find(".facetwp-slider-label").html(i)})),FWP.hooks.addFilter("facetwp/selections/slider",(function(e,t){var a=t.el.find(".facetwp-slider-label");return a.len()?a.text():""})),e().on("facetwp-loaded",(function(){e(".facetwp-type-slider .facetwp-slider").each((function(){var t=e(this),a=t.closest(".facetwp-facet"),s=a.attr("data-name"),o=FWP.settings[s],n=FWP.hooks.applyFilters("facetwp/set_options/slider",{range:o.range,start:o.start,step:parseFloat(o.step),connect:!0,handleAttributes:o.handle_attributes},{facet_name:s});if(t.hasClass("ready"))t.nodes[0].noUiSlider.updateOptions({range:n.range},!1);else{if(void 0!==this.noUiSlider)return;if(null===n.start[0])return;if(parseFloat(o.range.min)>parseFloat(o.range.max))return FWP.settings[s].lower=o.range.min,FWP.settings[s].upper=o.range.max,void FWP.hooks.doAction("facetwp/set_label/slider",a);parseFloat(o.range.min)==parseFloat(o.range.max)&&t.attr("data-disabled","true");var c=this;noUiSlider.create(c,n),c.noUiSlider.on("slide",(function(){t.attr("data-has-moved","true")})),c.noUiSlider.on("update",(function(e,t){FWP.settings[s].lower=e[0],FWP.settings[s].upper=e[1],FWP.hooks.doAction("facetwp/set_label/slider",a)})),c.noUiSlider.on("set",(function(){"true"===t.attr("data-has-moved")&&(t.attr("data-has-moved",""),FWP.active_facet=t.closest(".facetwp-facet"),FWP.autoload())})),t.addClass("ready")}})),e(".facetwp-type-slider").each((function(){var t=e(this).attr("data-name");e(this).find(".facetwp-slider-reset")[FWP.facets[t].length?"removeClass":"addClass"]("facetwp-hidden")}))})),e().on("click",".facetwp-type-slider .facetwp-slider-reset",(function(){var t=e(this).closest(".facetwp-facet").attr("data-name");FWP.reset(t)})),FWP.hooks.addAction("facetwp/refresh/rating",(function(t,a){var s=[];t.find(".facetwp-star.selected").each((function(){var t=e(this).attr("data-value");""!=t&&s.push(t)})),FWP.facets[a]=s})),e().on("mouseover",".facetwp-star:not(.disabled)",(function(){var t=e(this).closest(".facetwp-facet");if(e(this).hasClass("selected"))t.find(".facetwp-star-label").text(FWP_JSON.rating.Undo);else{var a="5"==e(this).attr("data-value")?"":FWP_JSON.rating["& up"];t.find(".facetwp-star-label").text(a),t.find(".facetwp-counter").text("("+e(this).attr("data-counter")+")")}})),e().on("mouseout",".facetwp-star:not(.disabled)",(function(){var t=e(this).closest(".facetwp-facet");t.find(".facetwp-star-label").text(""),t.find(".facetwp-counter").text("")})),e().on("click",".facetwp-star:not(.disabled)",(function(){var t=e(this).closest(".facetwp-facet"),a=e(this).hasClass("selected");t.find(".facetwp-star").removeClass("selected"),a||e(this).addClass("selected"),FWP.autoload()})),FWP.hooks.addAction("facetwp/refresh/sort",(function(e,t){var a=e.find("select").val();FWP.facets[t]=a?[a]:[]})),e().on("change",".facetwp-type-sort select",(function(){var t=e(this).closest(".facetwp-facet").attr("data-name");""!==e(this).val()&&(FWP.frozen_facets[t]="hard"),FWP.autoload()})),FWP.hooks.addAction("facetwp/refresh/pager",(function(e,t){FWP.facets[t]=[]})),FWP.hooks.addFilter("facetwp/template_html",(function(t,a){if(FWP.is_load_more){if(FWP.is_load_more=!1,0!s.includes(e)))),t.nodes[0]._facets=s})),e(".facetwp-hide-empty").each((function(){let t=e(this),a=t.closest(".facetwp-facet");t.nodes[0]._facets.every((e=>void 0===FWP.facets[e]||FWP.facets[e].length<1))?a.addClass("facetwp-hidden"):a.removeClass("facetwp-hidden")}))}))}(fUtil)}(); diff --git a/wp-content/plugins/facetwp/assets/js/src/accessibility.js b/wp-content/plugins/facetwp/assets/js/src/accessibility.js new file mode 100644 index 000000000..979e2cb9a --- /dev/null +++ b/wp-content/plugins/facetwp/assets/js/src/accessibility.js @@ -0,0 +1,105 @@ +(function($) { + var last_checked = null; + + if ('undefined' !== typeof FWP.hooks) { + FWP.hooks.addAction('facetwp/loaded', function() { + + // checkbox, radio, fselect + $('.facetwp-checkbox, .facetwp-radio, .fs-option').each(function() { + let $el = $(this); + if (! $el.hasClass('disabled')) { + $el.attr('role', 'checkbox'); + $el.attr('aria-checked', $el.hasClass('checked') || $el.hasClass('selected') ? 'true' : 'false'); + $el.attr('aria-label', $el.text()); + $el.attr('tabindex', 0); + } + }); + + // pager, show more, user selections, hierarchy + $('.facetwp-page, .facetwp-toggle, .facetwp-selection-value, .facetwp-link').each(function() { + let $el = $(this); + let label = $el.text(); + + if ($el.hasClass('facetwp-page')) { + label = FWP_JSON.a11y.label_page + ' ' + label; + + if ($el.hasClass('next')) { + label = FWP_JSON.a11y.label_page_next; + } + else if ($el.hasClass('prev')) { + label = FWP_JSON.a11y.label_page_prev; + } + } + + $el.attr('role', 'link'); + $el.attr('aria-label', label); + $el.attr('tabindex', 0); + }); + + // dropdown, sort facet, old sort feature + $('.facetwp-type-dropdown select, .facetwp-type-sort select, .facetwp-sort-select select').each(function() { + $(this).attr('aria-label', $(this).find('option:selected').text()); + }); + + // search, date, number range + $('.facetwp-search, .facetwp-date, .facetwp-number').each(function() { + $(this).attr('aria-label', $(this).attr('placeholder')); + }); + + // checkbox group + $('.facetwp-type-checkboxes').each(function() { + let facet_name = $(this).attr('data-name'); + $(this).attr('aria-label', FWP.settings.labels[facet_name]); + $(this).attr('role', 'group'); + }); + + // fselect + $('.fs-wrap').each(function() { + $(this).attr('role', 'button'); + $(this).attr('aria-haspopup', 'true'); + $(this).attr('aria-expanded', $(this).hasClass('fs-open') ? 'true' : 'false'); + $(this).attr('aria-label', $(this).find('.fs-label').html()); + }); + + $('.facetwp-type-fselect .facetwp-dropdown').attr('aria-hidden', 'true'); + + // pager + $('.facetwp-pager').attr('role', 'navigation'); + $('.facetwp-page.active').attr('aria-current', 'true'); + + // focus on selection + if (null != last_checked) { + var $el = $('.facetwp-facet [data-value="' + last_checked + '"]'); + if ($el.len()) { + $el.nodes[0].focus(); + } + last_checked = null; + } + }, 999); + } + + // keyboard support + $().on('keydown', '.facetwp-checkbox, .facetwp-radio, .facetwp-link', function(e) { + if (32 == e.keyCode || 13 == e.keyCode) { + last_checked = $(this).attr('data-value'); + e.preventDefault(); + this.click(); + } + }); + + $().on('keydown', '.facetwp-page, .facetwp-toggle, .facetwp-selection-value', function(e) { + if (32 == e.keyCode || 13 == e.keyCode) { + e.preventDefault(); + this.click(); + } + }); + + // fselect - determine "aria-expanded" + function toggleExpanded(e) { + var $fs = $(e.detail[0]); + $fs.attr('aria-expanded', $fs.hasClass('fs-open') ? 'true' : 'false'); + } + + $().on('fs:opened', toggleExpanded); + $().on('fs:closed', toggleExpanded); +})(fUtil); \ No newline at end of file diff --git a/wp-content/plugins/facetwp/assets/js/src/admin.js b/wp-content/plugins/facetwp/assets/js/src/admin.js new file mode 100644 index 000000000..012dbb73c --- /dev/null +++ b/wp-content/plugins/facetwp/assets/js/src/admin.js @@ -0,0 +1,2584 @@ +(($) => { + + $(() => { + init_vue(); + init_custom_js(); + }); + + function init_vue() { + + Vue.config.devtools = true; + + Vue.component('v-select', VueSelect.VueSelect); + + Vue.filter('i18n', str => FWP.__(str)); + + // Defaults mixin + const builder_defaults = { + methods: { + defaultLayout() { + return { + items: [this.defaultRow()], + settings: this.getDefaultSettings('layout') + }; + }, + defaultRow() { + return { + type: 'row', + items: [this.defaultCol()], + settings: this.getDefaultSettings('row') + }; + }, + defaultCol() { + return { + type: 'col', + items: [], + settings: this.getDefaultSettings('col') + }; + }, + defaultItem(source) { + return { + type: 'item', + source, + settings: this.getDefaultSettings('item', source) + }; + }, + mergeSettings(settings, type, source) { + let defaults = this.getDefaultSettings(type, source); + let default_keys = Object.keys(defaults); + let setting_keys = Object.keys(settings); + + // Automatically inject new settings + let missing_keys = default_keys.filter(name => !setting_keys.includes(name)); + + missing_keys.forEach((name, index) => { + Vue.set(settings, name, defaults[name]); + }); + + return settings; + }, + getSettingsMeta() { + let settings = { + num_columns: { + type: 'number', + title: FWP.__('Number of grid columns '), + defaultValue: 1 + }, + grid_gap: { + type: 'number', + title: FWP.__('Spacing between results'), + defaultValue: 10 + }, + no_results_text: { + type: 'textarea', + title: FWP.__('No results text') + }, + text_style: { + type: 'text-style', + title: FWP.__('Text style'), + tab: 'style', + defaultValue: { + align: '', + bold: false, + italic: false + } + }, + text_color: { + type: 'color', + title: FWP.__('Text color'), + tab: 'style' + }, + font_size: { + type: 'slider', + title: FWP.__('Font size'), + tab: 'style', + defaultValue: { + unit: 'px', + size: 0 + } + }, + background_color: { + type: 'color', + title: FWP.__('Background color'), + tab: 'style' + }, + border: { + type: 'border', + title: FWP.__('Border'), + tab: 'style', + defaultValue: { + style: 'none', + color: '', + width: { + unit: 'px', + top: 0, + right: 0, + bottom: 0, + left: 0 + } + }, + children: { + style: { + type: 'select', + title: FWP.__('Border style'), + choices: { + 'none': FWP.__('None'), + 'solid': FWP.__('Solid'), + 'dashed': FWP.__('Dashed'), + 'dotted': FWP.__('Dotted'), + 'double': FWP.__('Double') + } + }, + color: { + type: 'color', + title: FWP.__('Border color') + }, + width: { + type: 'utrbl', + title: FWP.__('Border width') + } + } + }, + button_text: { + type: 'text', + title: FWP.__('Button text') + }, + button_text_color: { + type: 'color', + title: FWP.__('Button text color') + }, + button_color: { + type: 'color', + title: FWP.__('Button color') + }, + button_padding: { + type: 'utrbl', + title: FWP.__('Button padding'), + defaultValue: { + unit: 'px', + top: 0, + right: 0, + bottom: 0, + left: 0 + } + }, + separator: { + type: 'text', + title: FWP.__('Separator'), + defaultValue: ', ' + }, + custom_css: { + type: 'textarea', + title: FWP.__('Custom CSS'), + tab: 'style' + }, + grid_template_columns: { + type: 'text', + title: FWP.__('Column widths'), + defaultValue: '1fr' + }, + content: { + type: 'textarea', + title: FWP.__('Content') + }, + image_size: { + type: 'select', + title: FWP.__('Image size'), + defaultValue: 'thumbnail', + choices: FWP.image_sizes, + v_show: [ + { type: 'source', value: 'featured_image' } + ] + }, + author_field: { + type: 'select', + title: FWP.__('Author field'), + defaultValue: 'display_name', + choices: { + 'display_name': FWP.__('Display name'), + 'user_login': FWP.__('User login'), + 'ID': FWP.__('User ID') + } + }, + field_type: { + type: 'select', + title: FWP.__('Field type'), + defaultValue: 'text', + choices: { + 'text': 'Text', + 'date': 'Date', + 'number': 'Number' + } + }, + date_format: { + type: 'text', + title: FWP.__('Date format'), + defaultValue: 'F j, Y', + v_show: [ + { type: 'field_type', value: 'date' }, + { type: 'source', value: 'post_date' }, + { type: 'source', value: 'post_modified' } + ] + }, + input_format: { + type: 'text', + title: FWP.__('Input format'), + defaultValue: 'Y-m-d', + v_show: [ + { type: 'field_type', value: 'date' }, + { type: 'source', value: 'post_date' }, + { type: 'source', value: 'post_modified' } + ] + }, + number_format: { + type: 'select', + title: FWP.__('Number format'), + choices: { + '': FWP.__('None'), + 'n': '1234', + 'n.n': '1234.5', + 'n.nn': '1234.56', + 'n,n': '1,234', + 'n,n.n': '1,234.5', + 'n,n.nn': '1,234.56' + }, + v_show: [ + { type: 'field_type', value: 'number' } + ] + }, + link: { + type: 'link', + title: FWP.__('Link'), + defaultValue: { + type: 'none', + href: '', + target: '' + }, + children: { + type: { + type: 'select', + title: FWP.__('Link type'), + choices: { + 'none': FWP.__('None'), + 'post': FWP.__('Post URL'), + 'custom': FWP.__('Custom URL') + } + } + } + }, + prefix: { + type: 'text', + title: FWP.__('Prefix') + }, + suffix: { + type: 'text', + title: FWP.__('Suffix') + }, + is_hidden: { + type: 'checkbox', + defaultValue: false, + suffix: FWP.__('Hide item?') + }, + padding: { + type: 'utrbl', + title: FWP.__('Padding'), + defaultValue: { + unit: 'px', + top: 0, + right: 0, + bottom: 0, + left: 0 + }, + tab: 'style' + }, + name: { + type: 'text', + title: FWP.__('Unique name'), + notes: '(Required) unique element name, without spaces' + }, + css_class: { + type: 'text', + title: FWP.__('CSS class'), + tab: 'style' + } + }; + + settings.button_border = this.$root.cloneObj(settings.border); + settings.button_border.title = FWP.__('Button border'); + settings.button_border.tab = 'basic'; + + settings.term_link = this.$root.cloneObj(settings.link); + settings.term_link.children.type.choices = { + 'none': FWP.__('None'), + 'term': FWP.__('Term URL'), + 'custom': FWP.__('Custom URL') + }; + + return settings; + }, + getDefaultFields(type, source) { + let fields = []; + + if ('layout' == type) { + fields.push('num_columns', 'grid_gap', 'no_results_text'); + } + + if ('row' == type) { + fields.push('grid_template_columns'); + } + + if ('item' == type) { + if ('html' == source) { + fields.push('content'); + } + if ('featured_image' == source) { + fields.push('image_size', 'link'); + } + if ('button' == source) { + fields.push('button_text', 'button_text_color', 'button_color', 'button_padding', 'button_border', 'link'); + } + if ('post_date' == source || 'post_modified' == source) { + fields.push('date_format'); + } + if ('post_title' == source) { + fields.push('link'); + } + if ('post_author' == source) { + fields.push('author_field'); + } + if (0 === source.indexOf('cf/')) { + fields.push('field_type', 'date_format', 'input_format', 'number_format', 'link'); + } + if (0 === source.indexOf('woo/')) { + fields.push('field_type', 'date_format', 'input_format', 'number_format'); + } + if (0 === source.indexOf('tax/')) { + fields.push('separator', 'term_link'); + } + if (!['html', 'button', 'featured_image'].includes(source)) { + fields.push('prefix', 'suffix'); + } + } + + fields.push('border', 'background_color', 'padding', 'text_color', 'text_style', 'font_size', 'name', 'css_class'); + + if ('layout' == type) { + fields.push('custom_css'); + } + + if ('item' == type) { + fields.push('is_hidden'); + } + + return fields; + }, + getDefaultSettings(type, source) { + let settings = {}; + let settings_meta = this.getSettingsMeta(); + let fields = this.getDefaultFields(type, source); + + fields.forEach(name => { + let defaultValue = settings_meta[name].defaultValue || ''; + + if ('name' == name) { + defaultValue = 'el-' + Math.random().toString(36).substring(7); + } + + settings[name] = defaultValue; + }); + + return settings; + } + } + }; + + /* ================ query builder ================ */ + + Vue.component('query-builder', { + props: { + query_obj: { + type: Object, + required: true + }, + template: { + type: Object, + required: true + } + }, + template: ` +
    +

    Which results should be in the listing?

    + +
    + {{ 'Fetch' | i18n }} + + + + {{ 'and show' | i18n }} + + {{ 'per page' | i18n }} +
    + +
    + {{ 'Sort by' | i18n }} +
    + +
    + + + + + + + + + + + + + + + + + + + +
    + +
    + {{ 'Narrow results by' | i18n }} +
    + +
    + + + + + + + + + + + +
    + Type a value, then press "Enter" to add it +
    +
    + + +
    + +
    + {{ 'Add query sort' | i18n }} + {{ 'Add query filter' | i18n }} + {{ 'Convert to query args' | i18n }} +
    +
    + `, + methods: { + addTag(newTag, value) { + value.push(newTag); + }, + getPlaceholder({key}) { + return ('tax/' == key.substr(0, 4)) ? FWP.__('Enter term slugs') : FWP.__('Enter values'); + }, + maybeShowValue(compare) { + return !['EXISTS', 'NOT EXISTS', 'EMPTY', 'NOT EMPTY'].includes(compare); + }, + showCompare(option, {key, type}) { + if ('tax/' == key.substr(0, 4)) { + if (!['IN', 'NOT IN', 'EXISTS', 'NOT EXISTS'].includes(option)) { + return false; + } + } + else if (['ID', 'post_author', 'post_status', 'post_name'].includes(key)) { + if (option != 'IN' && option != 'NOT IN') { + return false; + } + } + else if ('DATE' == type || 'post_date' == key || 'post_modified' == key) { + if (!['>', '>=', '<', '<='].includes(option)) { + return false; + } + } + else if ('CHAR' == type) { + if (['>', '>=', '<', '<='].includes(option)) { + return false; + } + } + return true; + }, + addSortCriteria() { + this.query_obj.orderby.push({ + key: 'title', + order: 'ASC', + type: 'CHAR' + }); + }, + addFilterCriteria() { + this.query_obj.filters.push({ + key: 'ID', + value: [], + compare: 'IN', + type: 'CHAR' + }); + }, + deleteSortCriteria(index) { + Vue.delete(this.query_obj.orderby, index); + }, + deleteFilterCriteria(index) { + Vue.delete(this.query_obj.filters, index); + } + } + }); + + Vue.component('fselect', { + data() { + return { + prev_key: '' + }; + }, + props: ['row'], + template: ` + + `, + mounted() { + fSelect(this.$el); + }, + /** + * fSelects won't refresh when deleting, so we need to + * manually reload() the changed elements + */ + beforeUpdate() { + this.prev_key = this.$el.getAttribute('data-key'); + }, + updated() { + if (this.row.key != this.prev_key) { + this.$el.fselect.reload(); + } + } + }); + + /* ================ layout builder ================ */ + + + Vue.component('builder', { + props: { + layout: Object + }, + template: ` +
    +
    +

    How should an individual result appear?

    +
    + + + + +
    +
    + +
    + ` + }); + + Vue.component('setting-wrap', { + mixins: [builder_defaults], + props: ['settings', 'name', 'source', 'tab'], + template: ` +
    +
    + {{ title }} +
    +
    +
    +
    +
    + `, + computed: { + getSettingComponent() { + return 'setting-' + this.type; + }, + isVisible() { + let ret = true; + let self = this; + + if ('undefined' === typeof this.meta.tab) { + this.meta.tab = 'basic'; + } + + if (this.meta.tab !== this.tab) { + ret = false; + } + else if ('undefined' !== typeof this.meta.v_show) { + ret = false; + this.meta.v_show.forEach((cond, index) => { + let type = cond.type; + let setting_val = ('source' == type) ? self[type] : self.settings[type]; + let cond_value = cond.value || ''; + let cond_compare = cond.compare || '=='; + let is_match = ('==' == cond_compare) + ? setting_val == cond_value + : setting_val != cond_value; + + if (is_match) { + ret = true; + } + }); + } + + return ret; + } + }, + created() { + this.settings_meta = this.getSettingsMeta(); + this.meta = this.settings_meta[this.name]; + this.type = this.meta.type; + this.title = this.meta.title; + } + }); + + Vue.component('setting-text', { + props: ['settings', 'name', 'meta'], + template: '' + }); + + Vue.component('setting-number', { + props: ['settings', 'name', 'meta'], + template: '' + }); + + Vue.component('setting-textarea', { + props: ['settings', 'name', 'meta'], + template: '' + }); + + Vue.component('setting-slider', { + props: ['settings', 'name', 'meta'], + template: ` +
    + + +
    + `, + computed: { + fontSizeLabel() { + let val = this.settings[this.name]; + return (0 === val.size) ? 'none' : val.size + val.unit; + } + } + }); + + Vue.component('setting-color', { + props: ['settings', 'name', 'meta'], + template: ` +
    +
    + + +
    + X +
    `, + mounted() { + let self = this; + let $canvas = self.$el.getElementsByClassName('color-canvas')[0]; + let $preview = self.$el.getElementsByClassName('color-preview')[0]; + let $input = self.$el.getElementsByClassName('color-input')[0]; + let $clear = self.$el.getElementsByClassName('color-clear')[0]; + $preview.style.backgroundColor = $input.value; + + let picker = new Picker({ + parent: $canvas, + popup: 'left', + alpha: false, + onDone(color) { + let hex = color.hex().substr(0, 7); + self.settings[self.name] = hex; + $preview.style.backgroundColor = hex; + } + }); + + picker.onOpen = function(color) { + picker.setColor($input.value); + }; + + $clear.addEventListener('click', function() { + self.settings[self.name] = ''; + $preview.style.backgroundColor = ''; + }); + } + }); + + Vue.component('setting-link', { + props: ['settings', 'name', 'meta'], + template: ` +
    + + + +
    + +
    +
    + + {{ 'Open in new tab?' | i18n }} +
    +
    + ` + }); + + Vue.component('setting-border', { + props: ['settings', 'name', 'meta'], + template: ` +
    + + + +
    +
    + + + + +
    + + + +
    +
    + ` + }); + + Vue.component('setting-checkbox', { + props: ['settings', 'name', 'meta'], + template: ` +
    + {{ meta.suffix }} +
    + ` + }); + + Vue.component('setting-select', { + props: ['settings', 'name', 'meta'], + template: ` + + ` + }); + + Vue.component('setting-utrbl', { + props: ['settings', 'name', 'meta'], + template: ` +
    +
    unit
    +
    top
    +
    right
    +
    bottom
    +
    left
    +
    + ` + }); + + Vue.component('setting-text-style', { + props: ['settings', 'name', 'meta'], + template: ` +
    + + + + + +
    + `, + methods: { + toggleChoice(opt, val) { + let old_val = this.settings[this.name][opt]; + + if ('undefined' !== typeof val) { + this.settings[this.name][opt] = (val !== old_val) ? val : ''; + } + else { + this.settings[this.name][opt] = ! old_val; + } + }, + isActive(opt, val) { + let new_val = ('undefined' !== typeof val) ? val : true; + return this.settings[this.name][opt] === new_val; + } + } + }); + + Vue.component('builder-settings', { + mixins: [builder_defaults], + props: { + layout: Object + }, + data() { + return { + title: '', + type: 'layout', + settings: this.layout.settings, + source: '', + active_tab: 'basic' + } + }, + template: ` +
    +

    + + {{ settingTitle }} +

    +
    +
    + {{ 'Basic' | i18n }} + {{ 'Style' | i18n }} +
    + + +
    +
    + `, + computed: { + settingTitle() { + return ('' === this.title) ? FWP.__('Settings') : this.title; + }, + settingsFields() { + return this.getDefaultFields(this.type, this.source); + } + }, + methods: { + uniqueKey() { + // method to prevent caching + return Math.floor(Math.random() * 999999); + }, + isActiveTab(which) { + return (this.active_tab === which) ? 'active' : ''; + }, + setActiveTab(which) { + this.active_tab = which; + } + }, + created() { + let self = this; + + this.$root.$on('edit-layout', () => { + self.title = ''; + self.type = 'layout'; + self.settings = self.mergeSettings(self.layout.settings, self.type); + self.source = ''; + }); + + this.$root.$on('edit-row', ({settings}, num) => { + self.title = FWP.__('Row') + ' ' + num; + self.type = 'row'; + self.settings = self.mergeSettings(settings, self.type); + self.source = ''; + }); + + this.$root.$on('edit-col', ({settings}, num) => { + self.title = FWP.__('Column') + ' ' + num; + self.type = 'col'; + self.settings = self.mergeSettings(settings, self.type); + self.source = ''; + }); + + this.$root.$on('edit-item', ({source, settings}) => { + self.title = FWP.layout_data[source]; + self.type = 'item'; + self.settings = self.mergeSettings(settings, self.type, source); + self.source = source; + }); + } + }); + + Vue.component('builder-row', { + mixins: [builder_defaults], + props: { + row: Object, + rows: Array, + index: Number, + is_child: Boolean + }, + template: ` +
    +
    + + + + +
    +
    + + +
    +
    + `, + computed: { + classIsChild() { + return this.is_child ? 'is-child' : 'not-child'; + } + }, + methods: { + addRow() { + this.rows.splice(this.index + 1, 0, this.defaultRow()); + + if (1 < this.rows.length) { + this.$root.$emit('edit-row', this.rows[this.index + 1], this.index + 2); + } + else { + this.$root.$emit('edit-layout'); + } + }, + addCol() { + let len = this.row.items.push(this.defaultCol()); + this.$root.$emit('edit-col', this.row.items[len - 1], len); + + let grid_str = '1fr '.repeat(this.row.items.length).trim(); + this.row.settings.grid_template_columns = grid_str; + }, + editRow() { + this.$root.$emit('edit-row', this.row, this.index + 1); + }, + deleteRow() { + Vue.delete(this.rows, this.index); + this.$root.$emit('edit-layout'); + + // Add default row + if (this.rows.length < 1) { + if (! this.is_child) { + this.addRow(); + } + } + } + } + }); + + Vue.component('builder-col', { + mixins: [builder_defaults], + props: { + col: Object, + cols: Array, + index: Number + }, + data() { + return { + adding_item: false + } + }, + template: ` +
    + + +
    + + +
    +
    + +
    + + + + +
    +
    +
    +
    +
    +
    +
    +
    + `, + methods: { + addItem() { + this.adding_item = ! this.adding_item; + }, + editCol() { + this.$root.$emit('edit-col', this.col, this.index + 1); + this.adding_item = false; + }, + deleteCol() { + // Remove the column + this.cols.splice(this.index, 1); + + // Show the "Layout" settings + this.$root.$emit('edit-layout'); + + // Add default column + if (this.cols.length < 1) { + this.cols.push(this.defaultCol()); + } + + // Adjust the row's `grid_template_columns` string + let grid_str = '1fr '.repeat(this.cols.length).trim(); + this.$parent.row.settings.grid_template_columns = grid_str; + }, + away() { + this.adding_item = false; + } + } + }); + + Vue.component('col-resizer', { + props: { + cols: Array, + index: Number + }, + data() { + return { + isResizing: false + } + }, + template: '
    ', + computed: { + classNames() { + return [ + 'resizer', + this.isResizing ? 'is-resizing' : '' + ]; + } + }, + methods: { + onMouseDown({ target: resizer, pageX: initialPageX, pageY: initialPageY }) { + if (! resizer.classList.contains('resizer')) { + return; + } + + let self = this; + let pane = resizer.parentElement; + let row_inner = pane.parentElement; + let initialPaneWidth = pane.offsetWidth; + + const resize = (initialSize, offset = 0) => { + let containerWidth = row_inner.clientWidth; + let paneWidth = initialSize + offset; + let width = ((paneWidth / containerWidth) * 100).toFixed(1) + '%'; + let gridColumns = this.$parent.$parent.row.settings.grid_template_columns.split(' '); + + gridColumns[this.index] = width; + + this.$parent.$parent.row.settings.grid_template_columns = gridColumns.join(' '); + }; + + // This adds is-resizing class to container + self.isResizing = true; + + const onMouseMove = ({ pageX, pageY }) => { + resize(initialPaneWidth, pageX - initialPageX); + }; + + const onMouseUp = () => { + // Run resize one more time to set computed width/height. + resize(pane.clientWidth); + + // This removes is-resizing class to container + self.isResizing = false; + + window.removeEventListener('mousemove', onMouseMove); + window.removeEventListener('mouseup', onMouseUp); + }; + + window.addEventListener('mousemove', onMouseMove); + window.addEventListener('mouseup', onMouseUp); + } + } + }); + + Vue.component('builder-item', { + props: { + item: Object, + items: Array, + index: Number + }, + template: ` +
    +
    + +
    +
    + + +
    +
    + `, + methods: { + editItem() { + this.$root.$emit('edit-item', this.item); + }, + deleteItem() { + this.items.splice(this.index, 1); + this.$root.$emit('edit-layout'); + } + } + }); + + Vue.component('popover', { + mixins: [builder_defaults], + props: { + col: Object + }, + data() { + return { + keywords: '' + } + }, + template: ` +
    + +
    +
    +
    +
    +
    + `, + methods: { + handleBlur(e) { + if (!e.currentTarget.contains(e.relatedTarget)) { + this.$parent.adding_item = false; + } + }, + isMatch(label) { + let bool = ('' == this.keywords) ? true : false; + + if (false === bool) { + let needle = this.keywords.toLowerCase(); + let haystack = label.toLowerCase(); + if (haystack.includes(needle)) { + bool = true; + } + } + + return bool; + }, + saveItem(source) { + if ('row' == source) { + let len = this.col.items.push(this.defaultRow()); + this.$root.$emit('edit-row', this.col.items[len - 1], len); + } + else { + let len = this.col.items.push(this.defaultItem(source)); + this.$root.$emit('edit-item', this.col.items[len - 1]); + } + + this.$parent.adding_item = false; + } + }, + mounted() { + this.$refs.keywords.focus(); + } + }); + + + /* ================ facets / templates ================ */ + + + Vue.component('facets', { + props: ['facets'], + template: ` + +
    +
    +
    + {{ facet.label }} + +
    +
    {{ facet.name }}
    +
    {{ facet.type }}
    +
    +
    {{ getRowCount(facet.name) }}
    +
    +
    +
    +
    +
    Copy shortcode
    +
    Duplicate
    +
    Delete
    +
    +
    +
    +
    +
    + `, + methods: { + getSource(source) { + return FWP.layout_data[source] || '-'; + }, + getRowCount(facet_name) { + if (this.$root.is_indexing) { + return '...'; + } + return this.$root.row_counts[facet_name] || '-'; + } + } + }); + + Vue.component('templates', { + props: ['templates'], + template: ` + +
    +
    +
    + {{ template.label }} + +
    +
    {{ template.name }}
    +
    {{ getDisplayMode(index) }}
    +
    {{ getPostTypes(index) }}
    +
    +
    +
    +
    +
    Copy shortcode
    +
    Duplicate
    +
    Delete
    +
    +
    +
    +
    +
    + `, + methods: { + getDisplayMode(index) { + let template = this.templates[index]; + return ('undefined' !== typeof template.modes) ? template.modes.display : 'advanced'; + }, + getPostTypes(index) { + let template = this.templates[index]; + if ('undefined' !== typeof template.modes) { + if ('visual' == template.modes.query) { + let post_types = template.query_obj.post_type; + if (0 === post_types.length) { + return ''; + } + else { + return post_types.map(type => type.label).join(', '); + } + } + } + return ''; + } + } + }); + + Vue.component('facet-edit', { + data() { + return { + facet: {}, + codeEditors: { + marker_content: null + } + } + }, + created() { + this.facet = this.$root.editing; + }, + mounted() { + if ('map' === this.facet.type) { + + // Init template code editor + this.initCodeMirror('marker-content-editor', 'marker_content', false); + } + }, + watch: { + 'facet.ui_type': function(val) { + switch (val) { + case 'radio': + Vue.delete(this.facet, 'multiple'); + Vue.delete(this.facet, 'show_expanded'); + Vue.delete(this.facet, 'hierarchical'); + break; + case 'dropdown': + Vue.delete(this.facet, 'multiple'); + Vue.delete(this.facet, 'show_expanded'); + break; + case 'checkboxes': + Vue.delete(this.facet, 'multiple'); + break; + case 'fselect': + Vue.delete(this.facet, 'show_expanded'); + break; + } + } + }, + methods: { + setName(e) { + this.facet.name = this.$root.sanitizeName(e.target.innerHTML); + }, + unlock() { + Vue.delete(this.facet, '_code'); + }, + initCodeMirror(textareaId, type, reset) { + try { + let textarea = document.getElementById(textareaId); + + // Reset editor and start over when switching facet type and back to Map + // Same behavior as other facet settings when switching facet type + if (reset) { + this.codeEditors[type] = null; + } + let editor = wp.codeEditor.initialize(jQuery(textarea), fwp_editor_settings); + + // Update when editor content changed + this.updateCodeMirrorContent(editor, type); + + // Store a reference to the CodeMirror instance + this.codeEditors[type] = editor; + + } catch (error) { + console.error('Error initializing CodeMirror:', error); + } + }, + updateCodeMirrorContent(editor, type) { + + // Process editor changes so they can be saved + try { + if (editor !== null) { + editor.codemirror.on('change', () => { + let newContent = editor.codemirror.getValue(); + newContent = JSON.parse(JSON.stringify(newContent)); + Vue.set(this.facet, type, newContent); + }); + } + } catch (error) { + console.error('Error updating CodeMirror content:', error); + } + } + }, + template: ` +
    +
    + This facet is registered in code. Click to allow edits: + +
    +
    +
    +
    {{ 'Label' | i18n }}
    +
    + + + + {{ 'Copy shortcode' | i18n }} + +
    +
    +
    +
    {{ 'Facet type' | i18n }}
    +
    + + +
    +
    +
    +
    {{ 'Data source' | i18n }}
    +
    + +
    +
    + +
    +
    + ` + }); + + Vue.component('template-edit', { + mixins: [builder_defaults], + data() { + return { + template: {}, + tab: 'display', + codeEditors: { + template: null, + query: null + } + } + }, + created() { + this.template = this.$root.editing; + + // Set defaults for the layout builder + if (! this.template.layout) { + Vue.set(this.template, 'layout', this.defaultLayout()); + } + + // Set defaults for the query builder + if (! this.template.query_obj) { + Vue.set(this.template, 'query_obj', { + post_type: [], + posts_per_page: 10, + orderby: [], + filters: [] + }); + } + + // Set the modes + if (! this.template.modes) { + Vue.set(this.template, 'modes', { + display: ('' !== this.template.template) ? 'advanced' : 'visual', + query: ('' !== this.template.query) ? 'advanced' : 'visual' + }); + } + }, + mounted() { + + // Init template code editor + this.initCodeMirror('template-editor', 'template'); + + // Update query editor when 'Convert to query args' button is clicked + this.$root.$on('query-updated', () => { + this.codeEditors['query'].codemirror.setValue(this.template.query); + }); + }, + watch: { + tab(newTab, oldTab) { + + // Init query code editor + if (newTab !== oldTab) { + if (newTab === 'query') { + this.initCodeMirror('query-editor', 'query'); + this.refreshCodeMirror(this.codeEditors['query']); + } + } + } + }, + methods: { + setName(e) { + this.template.name = this.$root.sanitizeName(e.target.innerHTML); + }, + isMode(mode) { + return this.template.modes[this.tab] === mode; + }, + switchMode() { + const now = this.template.modes[this.tab]; + this.template.modes[this.tab] = ('visual' === now) ? 'advanced' : 'visual'; + + let editor = null; + + // Refresh editor when switching to advanced on both tabs + if ('visual' === now) { + if (this.tab === 'display') { + editor = this.codeEditors['template']; + } else { + editor = this.codeEditors['query']; + } + } + this.refreshCodeMirror(editor); + + }, + unlock() { + Vue.delete(this.template, '_code'); + }, + initCodeMirror(textareaId, type) { + try { + let textarea = document.getElementById(textareaId); + + if (this.codeEditors[type] === null) { + let editor = wp.codeEditor.initialize(jQuery(textarea), fwp_editor_settings); + + // Update when editor content changed + this.updateCodeMirrorContent(editor, type); + + // Store a reference to the CodeMirror instance + this.codeEditors[type] = editor; + } + } catch (error) { + console.error('Error initializing CodeMirror:', error); + } + }, + refreshCodeMirror(editor) { + if ( editor !== null ) { + this.$nextTick(() => { + editor.codemirror.refresh(); + }); + } + }, + updateCodeMirrorContent(editor, type) { + + // Process editor changes so they can be saved + try { + if (editor !== null) { + editor.codemirror.on('change', () => { + let newContent = editor.codemirror.getValue(); + newContent = JSON.parse(JSON.stringify(newContent)); + Vue.set(this.template, type, newContent); + }); + } + } catch (error) { + console.error('Error updating CodeMirror content:', error); + } + } + }, + template: ` +
    +
    + This template is registered in code. Click to allow edits: + +
    +
    +
    + + + + {{ 'Copy shortcode' | i18n }} + +
    + + + +
    + {{ 'Display' | i18n }} + {{ 'Query' | i18n }} +
    + +
    +
    + +
    +
    +

    {{ 'Display Code' | i18n }} {{ 'Help' | i18n }}

    + +

    {{ 'To search your code, click in the editor, then use Ctrl+F (Windows/Linux) or Cmd+F (Mac).' | i18n }}

    +
    +
    + +
    +
    + +
    +
    +

    {{ 'Query Arguments' | i18n }} {{ 'Help' | i18n }}

    + +

    {{ 'To search your code, click in the editor, then use Ctrl+F (Windows/Linux) or Cmd+F (Mac).' | i18n }}

    +
    +
    +
    +
    + ` + }); + + Vue.component('facet-types', { + props: ['facet', 'selected', 'types'], + template: ` + + ` + }); + + Vue.component('facet-settings', { + props: ['facet','initCodeMirror'], + template: '', + methods: { + getFields(aliases) { + let output = []; + $.each(aliases, function(name) { + output = output.concat(FWP.facet_fields[name].names); + }); + return output; + } + }, + computed: { + // dynamic component so the data bindings (e.g. v-model) get compiled + dynComponent() { + return { + template: '
    ' + this.settingsHtml + '
    ', + props: ['facet'] + } + }, + settingsHtml() { + let self = this; + let facet_obj = FWP.facet_types[self.facet.type]; + let aliases = facet_obj.fields; + + // Support for settings_html() in < 3.9 + if ('undefined' === typeof aliases) { + if ('undefined' !== typeof FWP.clone[self.facet.type]) { + FWP.facet_fields[self.facet.type + '_fields'] = { + names: [], + html: FWP.clone[self.facet.type] + }; + + var $html = $(FWP.clone[self.facet.type]); + $.each($html.nodes[0].children, function(chunk) { + $(chunk).find('input, textarea, select, [setting-name]').each(function() { + let $el = $(this); + let setting_name = $el.attr('setting-name'); + + if (null === setting_name) { + setting_name = $el.attr('class').split(' ')[0].replace(/-/g, '_').substr(6); + } + + FWP.facet_fields[self.facet.type + '_fields'].names.push(setting_name); + }); + }); + + aliases = [self.facet.type + '_fields']; + } + } + + // Get the actual fields by parsing the aliases (groups) + let fields = self.getFields(aliases); + let html = ''; + + let combined = ['label', 'name', 'type', 'source', '_code'].concat(fields); + + // Remove irrelevant settings + $.each(Object.keys(self.facet), function(setting_name) { + if (-1 == combined.indexOf(setting_name)) { + Vue.delete(self.facet, setting_name); + } + }); + + // Add new settings + $.each(aliases, function(alias_name) { + let $parsed = $(FWP.facet_fields[alias_name].html); + + $.each(FWP.facet_fields[alias_name].names, function(setting_name) { + let name_dashed = setting_name.replace(/_/g, '-'); + let $input = $parsed.find('.facet-' + name_dashed); + let val = $input.val(); + + if (0 < $input.len()) { + $input.attr('v-model', 'facet.' + setting_name); + + if ('undefined' === typeof self.facet[setting_name]) { + if ($input.is('[type=checkbox]')) { + val = $input.nodes[0].checked ? 'yes' : 'no'; + } + if ('[]' === val) { + val = []; + } + } + else { + val = self.facet[setting_name]; + Vue.delete(self.facet, setting_name); + } + + Vue.set(self.facet, setting_name, val); + } + }); + + // Update the documentFragment HTML to include the "v-model" + $.each($parsed.nodes[0].children, function(el) { + html += el.outerHTML; + }); + }); + + return html; + } + }, + watch: { + 'facet.type': function(val,oldVal) { + if ('search' == val || 'pager' == val || 'reset' == val || 'sort' == val) { + Vue.delete(this.facet, 'source'); + } + if ( val !== oldVal && 'undefined' != typeof this.facet.ui_type ) { + // reset ui_type when changing facet type + Vue.delete(this.facet, 'ui_type'); + } + if ('map' == val) { + this.$nextTick(() => { + + // Init template code editor + this.initCodeMirror('marker-content-editor', 'marker_content', true); + }); + } + }, + } + }); + + Vue.component('data-sources', { + props: { + facet: Object, + settingName: { + type: String, + default: 'source' + } + }, + template: ` + + `, + computed: { + className() { + return 'facet-' + this.settingName.replace(/_/g, '-'); + } + }, + mounted() { + fSelect(this.$el); + } + }); + + Vue.component('facet-names', { + props: { + facet: Object, + setting: String + }, + template: ` + + `, + computed: { + className() { + return 'facet-' + this.setting.replace(/_/g, '-'); + } + }, + methods: { + bindSelectedClass(name) { + return this.facet[this.setting].includes(name) ? 'selected' : ''; + } + }, + created() { + if ('undefined' === typeof this.facet[this.setting]) { + this.facet[this.setting] = []; + } + }, + mounted() { + fSelect(this.$el, { 'placeholder': 'Choose facets' }); + } + }); + + Vue.component('sort-options', { + props: { + facet: Object + }, + template: ` +
    +
    +
    + + + +
    + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + + +
    +
    + +
    + {{ 'Add sort' | i18n }} +
    +
    + `, + methods: { + addSort() { + this.facet.sort_options.push({ + label: 'New option', + name: 'new_option', + orderby: [{ + key: 'title', + order: 'ASC', + type: 'CHAR' + }] + }); + }, + addSortField(opts, index) { + opts.splice(index + 1, 0, { + key: 'title', + order: 'ASC', + type: 'CHAR' + }); + }, + moveUp(opts, index) { + opts.splice(index -1, 0, opts.splice(index, 1)[0]); + }, + removeItem(row, index) { + Vue.delete(row, index); + }, + setName(row, e) { + row.name = this.$root.sanitizeName(e.target.innerHTML); + } + } + }); + + Vue.component('color-picker', { + props: { + facet: Object, + settingName: { + type: String, + default: 'color' + }, + defaultColor: { + type: String, + default: '#000' + }, + }, + template: ` +
    +
    + + +
    + X +
    `, + created() { + if ('undefined' === typeof this.facet[this.settingName]) { + this.facet[this.settingName] = this.defaultColor; + } + }, + computed: { + className() { + return 'facet-' + this.settingName.replace(/_/g, '-') + ' color-input'; + }, + colorName() { + return this.defaultColor; + } + }, + mounted() { + let self = this; + let $canvas = self.$el.getElementsByClassName('color-canvas')[0]; + let $preview = self.$el.getElementsByClassName('color-preview')[0]; + let $input = self.$el.getElementsByClassName('color-input')[0]; + let $clear = self.$el.getElementsByClassName('color-clear')[0]; + $preview.style.backgroundColor = $input.value; + + let picker = new Picker({ + parent: $canvas, + popup: 'right', + alpha: false, + onDone(color) { + let hex = color.hex().substr(0, 7); + self.facet[self.settingName] = hex; + $input.value = hex; + $preview.style.backgroundColor = hex; + } + }); + + picker.onOpen = function(color) { + picker.setColor($input.value); + }; + + $clear.addEventListener('click', function() { + self.facet[self.settingName] = self.defaultColor; + $input.value = self.defaultColor; + $preview.style.backgroundColor = $input.value; + }); + } + }); + + // Vue instance + FWP.vue = new Vue({ + el: '#app', + data: { + app: FWP.data, + editing: {}, + editing_facet: false, + editing_template: false, + row_counts: {}, + active_tab: 'facets', + active_subnav: 'general', + is_support_loaded: false, + is_name_editable: false, + is_rebuild_open: false, + is_indexing: false, + timeout: null + }, + methods: { + addItem(type) { + if ('facet' == type) { + let len = this.app.facets.push({ + 'name': 'new_facet', + 'label': 'New Facet', + 'type': 'checkboxes', + 'source': 'post_type' + }); + this.editItem('facet', this.app.facets[len-1]); + } + else { + let len = this.app.templates.push({ + 'name': 'new_template', + 'label': 'New Template', + 'query': '', + 'template': '' + }); + this.editItem('template', this.app.templates[len-1]); + } + }, + duplicateItem(type, index) { + let facet = this.cloneObj(this.app[type + 's'][index]); + facet.label += ' (copy)'; + facet.name += '_copy'; + + this.app[type + 's'].splice(index+1, 0, facet) + this.editItem(type, facet); + }, + editItem(type, data) { + this['editing_' + type] = true; + this.editing = data; + window.scrollTo(0, 0); + }, + doneEditing() { + this.editing_template = false; + this.editing_facet = false; + this.editing = {}; + }, + tabClick(which) { + this.doneEditing(); + this.active_tab = which; + if ('support' === which) { + this.is_support_loaded = true; + } + const url = new URL(location); + url.searchParams.set("tab", which); + history.pushState({}, "", url); + }, + getItemLabel() { + return this.editing.label; + }, + deleteItem(type, index) { + this.app[type + 's'].splice(index, 1); + }, + saveChanges() { + window.setStatus('load', FWP.__('Saving') + '...'); + + let data = JSON.parse(JSON.stringify(FWP.data)); + + // Remove code-based facets and templates + data.facets = data.facets.filter(obj => 'undefined' === typeof obj['_code']); + data.templates = data.templates.filter(obj => 'undefined' === typeof obj['_code']); + + // Settings save hook + data = FWP.hooks.applyFilters('facetwp/save_settings', { + action: 'facetwp_save_settings', + nonce: FWP.nonce, + data: data + }); + + $.post(ajaxurl, data, { + done: ({code, message}) => { + var code = ('success' == code) ? 'ok' : code; + window.setStatus(code, message); + }, + fail: (err) => { + window.setStatus('error', err); + } + }); + }, + rebuildAction() { + this.is_indexing ? this.cancelReindex() : this.rebuildIndex(); + }, + rebuildIndex() { + let self = this; + + if (this.is_indexing) { + return; + } + + this.is_indexing = true; + + $.post(ajaxurl, { action: 'facetwp_rebuild_index', nonce: FWP.nonce }); + window.setStatus('load', FWP.__('Indexing') + '... 0%'); + this.timeout = setTimeout(() => { + self.getProgress(); + }, 5000); + }, + cancelReindex() { + let self = this; + + $.post(ajaxurl, { + action: 'facetwp_get_info', + type: 'cancel_reindex', + nonce: FWP.nonce + }, { + done: ({message}) => { + self.is_indexing = false; + clearTimeout(self.timeout); + window.setStatus('error', message); + } + }); + }, + getProgress() { + let self = this; + let isNumeric = (obj) => !Array.isArray(obj) && (obj - parseFloat(obj) + 1) >= 0; + + $.post(ajaxurl, { + action: 'facetwp_heartbeat', + nonce: FWP.nonce + }, { + done: (data) => { + if ('-1' == data.pct) { + self.is_indexing = false; + + if (data.rows.length < 1) { + window.setStatus('error', FWP.__('The index table is empty')); + } + else { + window.setStatus('ok', FWP.__('Indexing complete')); + + // Update the row counts + $.each(self.$root.app.facets, function(facet) { + Vue.set(self.row_counts, facet.name, data.rows[facet.name]); + }); + } + } + else if (isNumeric(data.pct)) { + window.setStatus('load', FWP.__('Indexing') + '... ' + data.pct + '%'); + self.is_indexing = true; + + self.timeout = setTimeout(() => { + self.getProgress(); + }, 5000); + } + else { + window.setStatus('error', data); + self.is_indexing = false; + } + } + }); + }, + getInfo(type, label) { + window.setStatus('load', FWP.__(label) + '...'); + + $.post(ajaxurl, { + action: 'facetwp_get_info', + type, + nonce: FWP.nonce + }, { + done: ({message}) => { + window.setStatus('error', message); + } + }); + }, + getQueryArgs(template) { + let self = this; + + template.modes.query = 'advanced'; + template.query = FWP.__('Loading') + '...'; + + $.post(ajaxurl, { + action: 'facetwp_get_query_args', + query_obj: template.query_obj, + nonce: FWP.nonce + }, { + done: (message) => { + var json = JSON.stringify(message, null, 2); + json = "'); + template.query = json; + + // Emit an event when template.query is changed to trigger updating the editor + this.$root.$emit('query-updated'); + } + }) + }, + showIndexerStats() { + this.getInfo('indexer_stats', 'Looking'); + }, + searchablePostTypes() { + this.getInfo('post_types', 'Looking'); + }, + purgeIndexTable() { + this.getInfo('purge_index_table', 'Purging'); + }, + copyToClipboard(name, type, {target}) { + const $this = $(target); + const $el = $('.facetwp-clipboard'); + const orig_text = $this.text(); + + try { + $el.removeClass('hidden'); + $el.val('[facetwp ' + type + '="' + name + '"]'); + $el.nodes[0].select(); + document.execCommand('copy'); + $el.addClass('hidden'); + $this.text(FWP.__('Copied!')); + } + catch(err) { + $this.text(FWP.__('Press CTRL+C to copy')); + } + + window.setTimeout(() => { + $this.text(orig_text); + }, 2000); + }, + activate() { + $('.facetwp-activation-status').html(FWP.__('Activating') + '...'); + + $.post(ajaxurl, { + action: 'facetwp_license', + nonce: FWP.nonce, + license: $('.facetwp-license').val() + }, { + done: ({message}) => { + $('.facetwp-activation-status').html(message); + } + }) + }, + + checkApi(testTypes, apikey) { + + const statuses = {}; + const doTests = testTypes.split(","); + + const updateStatus = ( test, message, status = 'none', action = 'none' ) => { + const testEl = document.querySelector('.facetwp-api-status .test-' + test); + testEl.innerHTML = message; + testEl.classList = ''; + testEl.classList.add( 'test-'+test ); + testEl.classList.add( 'status-'+status); + testEl.classList.add( 'action-'+action); + statuses[test] = status; + } + + if (!/AIza[0-9A-Za-z\-_]{35}/.test(apikey)) { + alert( FWP.__('Your Google Maps API key format is invalid. Please check your key before continuing.') ); + return false; // Invalid format + } + + const prompt = FWP.__('Each test will count as one billable API request in your Google Cloud account. Continue?'); + if (confirm(prompt)) { + updateStatus( 'status', FWP.__('Testing ... '), 'testing', 'testing' ); + } else { + return; + } + + const testFunctions = {}; + + testFunctions.checkPlaces = async () => { + updateStatus( 'places', FWP.__('Checking Places API (New) ...'), 'testing', 'testing' ); + const placeUrl = `https://places.googleapis.com/v1/places/ChIJj61dQgK6j4AR4GeTYWZsKWw?fields=id,displayName,formattedAddress&key=${apikey}`; + + try { + const response = await fetch(placeUrl); + if (!response.ok) { + if ('403' == response.status) { + throw new Error('permission denied'); + } else { + throw new Error('unknown error'); + } + } else { + updateStatus('places', '' + FWP.__('Places API (New)') + ' ' + FWP.__('connected ') + '', 'pass', 'testing'); + } + } catch (error) { + updateStatus('places', '' + FWP.__('Places API (New)') + ' ' + FWP.__('connection failed: ') + error.message + '', 'fail', 'testing'); + } + } + + testFunctions.checkPlaceslegacy = async () => { + updateStatus( 'placeslegacy', FWP.__('Checking Places API (Legacy) ...'), 'testing', 'testing' ); + + this.loadGmaps(apikey); + + const {PlacesService} = await google.maps.importLibrary("places"); + + const service = new PlacesService( + document.createElement('div') + ); + + service.getDetails({ + placeId: 'ChIJj61dQgK6j4AR4GeTYWZsKWw', + fields: ['geometry'] + }, function(place, status) { + if (status === google.maps.places.PlacesServiceStatus.OK) { + updateStatus('placeslegacy', '' + FWP.__('Places API (Legacy)') + ' ' + FWP.__('connected ') + '', 'pass', 'testing'); + } else { + updateStatus('placeslegacy', '' + FWP.__('Places API (Legacy)') + ' ' + FWP.__('connection failed: ') + status + '', 'fail', 'testing'); + } + }); + } + + testFunctions.checkGeocoder = async () => { + updateStatus( 'geocoder', FWP.__('Checking Geocoding API ...'), 'testing', 'testing' ); + + this.loadGmaps(apikey); + + const {Geocoder} = await google.maps.importLibrary("geocoding"); + const latlng = { lat: 40.714224, lng: -73.961452 }; + const geocoding = new Geocoder; + + geocoding.geocode({ 'location': latlng }, function(results, status) { + if (status === google.maps.GeocoderStatus.OK) { + updateStatus('geocoder', '' + FWP.__('Geocoding API') + ' ' + FWP.__('connected ') + '', 'pass', 'testing'); + } else { + updateStatus('geocoder', '' + FWP.__('Geocoding API') + ' ' + FWP.__('connection failed: ') + status + '', 'fail', 'testing'); + } + }); + } + + testFunctions.checkMaps = async () => { + + updateStatus( 'maps', FWP.__('Checking Maps JavaScript API ...'), 'testing', 'testing' ); + + this.loadGmaps(apikey); + + const { Map } = await google.maps.importLibrary("maps"); + + const mapDiv = document.createElement("div"); + mapDiv.id = "map"; + mapDiv.style.width = "1px"; + mapDiv.style.height = "1px"; + document.getElementsByTagName('body')[0].appendChild(mapDiv); + const map = new Map(document.getElementById("map"), { + center: { lat: -34.397, lng: 150.644 }, + zoom: 8, + }); + map.addListener('tilesloaded', function() { + updateStatus( 'maps', '' + FWP.__('Maps JavaScript API') + ' ' + FWP.__('connected') + '', 'pass', 'testing' ); + }); + + } + + const validTests = [ 'maps', 'places', 'placeslegacy', 'geocoder' ]; + validTests.forEach((t) => { + if ( doTests.includes(t) ) { + let tn = "check"+t.charAt(0).toUpperCase()+t.slice(1); + testFunctions[tn](); + } + }); + + + setTimeout(() => { + updateStatus( 'status', FWP.__('Test results:'), 'done', 'testing' ); + 'testing' !== statuses.maps || updateStatus( 'maps', '' + FWP.__('Maps JavaScript API') + ' ' + FWP.__('connection failed') + '', 'fail', 'testing' ); + 'testing' !== statuses.geocoder || updateStatus( 'geocoder', '' + FWP.__('Geocoding API') + ' ' + FWP.__('connection failed') + '', 'fail', 'testing' ); + 'testing' !== statuses.places || updateStatus( 'places', '' + FWP.__('Places API (New)') + ' ' + FWP.__('connection failed') + '', 'fail', 'testing' ); + 'testing' !== statuses.placeslegacy || updateStatus( 'placeslegacy', '' + FWP.__('Places API (Legacy)') + ' ' + FWP.__('connection failed') + '', 'fail', 'testing' ); + + }, "2000"); + + }, + loadGmaps(apikey) { + /** we can only load the maps scripts once without reloading the page */ + if ( !this.$root.gmapsLoaded ) { + let scriptTag = document.createElement('script'); + scriptTag.id = "fwp-gmaps-api"; + scriptTag.innerText = '(g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=\`https://maps.${c}apis.com/maps/api/js?\`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})({ key: "'+apikey+'", v: "quarterly", });'; + document.getElementsByTagName('head')[0].appendChild(scriptTag); + this.$root.gmapsLoaded = true; + } + }, + resetCheckApi() { + let prompt = FWP.__('FacetWP\'s settings page needs to reload to test a different Google Maps API key. Unsaved changes to other settings will be discarded. Continue?'); + if (confirm(prompt)) { + window.location.reload(); + } else { + return; + } + }, + isNameEditable({name}) { + this.is_name_editable = ('' == name || 'new_' == name.substr(0, 4)); + }, + maybeEditName(item) { + if (this.is_name_editable) { + item.name = this.sanitizeName(item.label); + } + }, + sanitizeName(name) { + let val = name.trim().toLowerCase(); + let res = [ 'pager', 'sort', 'labels', 'length', 'name', 'method', 'num_choices' ]; + val = val.replace(/[^\w- ]/g, ''); // strip invalid characters + val = val.replace(/[- ]/g, '_'); // replace space and hyphen with underscore + val = val.replace(/[_]{2,}/g, '_'); // strip consecutive underscores + val = res.includes(val) ? val + '_' : val; // reserved + return val; + }, + documentClick({target}) { + let el = target; + + if (! el.classList.contains('btn-caret')) { + this.is_rebuild_open = false; + } + }, + cloneObj(obj) { + return JSON.parse(JSON.stringify(obj)); + } + }, + computed: { + isEditing() { + return this.editing_facet || this.editing_template; + }, + indexButtonLabel() { + return this.is_indexing ? FWP.__('Stop indexer') : FWP.__('Re-index'); + } + }, + created() { + document.addEventListener('click', this.documentClick); + }, + mounted() { + let params = new URLSearchParams(document.location.search); + let tab = params.get("tab"); + null == tab || !['facets', 'templates', 'settings', 'support'].includes(tab) || this.tabClick(tab); + this.getProgress(); + }, + watch: { + 'app.settings.gmaps_api_key': function(oldVal,newVal) { + if ( this.$root.gmapsLoaded && oldVal != newVal && '' != newVal) { + this.$root.gmapsChanged = true; + } + } + } + }); + } + + function init_custom_js() { + + window.setStatus = (code, message) => { + $('.facetwp-response').html(message); + $('.facetwp-response-icon').nodes[0].setAttribute('data-status', code); + + if ('error' == code) { + $('.facetwp-response').addClass('visible'); + } + }; + + $().on('click', '.facetwp-settings-section .facetwp-switch', () => { + window.setStatus('error', 'Press "Save changes" to apply'); + }); + + $().on('click', '.facetwp-response-wrap', () => { + $('.facetwp-response').toggleClass('visible'); + }); + + $().on('click', '.export-all', (e) => { + let $opts = $('.export-items').next('.fs-wrap'); + let selectedType = "undefined" != typeof e.target.dataset.value ? e.target.dataset.value+"-" : ""; + $opts.find('.fs-option:not(.selected)').each(function() { + let $opt = $(this); + if ( $opt.attr('data-value').includes(selectedType) ) { + $opt.trigger('click'); + } + }); + $opts.find('.fs-option.selected').each(function() { + let $opt = $(this); + if ( !$opt.attr('data-value').includes(selectedType) ) { + $opt.trigger('click'); + } + }); + $opts.nodes[0]._rel.fselect.open(); + e.stopImmediatePropagation(); + }); + + $().on('click', '.export-none', (e) => { + let $opts = $('.export-items').next('.fs-wrap'); + $opts.find('.fs-option.selected').each(function() { + let $opt = $(this); + $opt.trigger('click'); + }); + $opts.nodes[0]._rel.fselect.open(); + e.stopImmediatePropagation(); + }); + + // Export + $().on('click', '.export-submit', () => { + $('.import-code').val(FWP.__('Loading') + '...'); + + $.post(ajaxurl, { + action: 'facetwp_backup', + nonce: FWP.nonce, + action_type: 'export', + items: $('.export-items').val() + }, { + done: (resp) => { + $('.import-code').val(JSON.stringify(resp)); + } + }) + }); + + // Import + $().on('click', '.import-submit', () => { + window.setStatus('load', FWP.__('Importing') + '...'); + + try { + var code = JSON.parse($('.import-code').val()); + + $.post(ajaxurl, { + action: 'facetwp_backup', + nonce: FWP.nonce, + action_type: 'import', + import_code: code, + overwrite: $('.import-overwrite').nodes[0].checked ? 1 : 0 + }, { + dataType: 'text', + done: (resp) => { + window.setStatus('ok', resp); + setTimeout(() => { + window.location.reload(); + }, 1500); + } + }); + } + catch(err) { + window.setStatus('error', 'Invalid JSON'); + } + }); + + // Initialize tooltips + $().on('mouseover', '.facetwp-tooltip', function() { + if (!this.classList.contains('.ftip-enabled')) { + fTip(this, { + content: (node) => $(node).find('.facetwp-tooltip-content').html() + }).open(); + } + }); + + // fSelect + fSelect('.export-items'); + } + +})(fUtil); diff --git a/wp-content/plugins/facetwp/assets/js/src/deprecated.js b/wp-content/plugins/facetwp/assets/js/src/deprecated.js new file mode 100644 index 000000000..30ae25caf --- /dev/null +++ b/wp-content/plugins/facetwp/assets/js/src/deprecated.js @@ -0,0 +1,10 @@ +FWP.deprecated = (old_method, new_method, ...args) => { + console.warn('FWP.' + old_method + '() has changed to FWP.' + new_method + '()'); + return FWP[new_method](...args); +}; +FWP.build_post_data = (...args) => FWP.deprecated('build_post_data', 'buildPostData', ...args); +FWP.build_query_string = (...args) => FWP.deprecated('build_query_string', 'buildQueryString', ...args); +FWP.fetch_data = (...args) => FWP.deprecated('fetch_data', 'fetchData', ...args); +FWP.load_from_hash = (...args) => FWP.deprecated('load_from_hash', 'loadFromHash', ...args); +FWP.parse_facets = (...args) => FWP.deprecated('parse_facets', 'parseFacets', ...args); +FWP.set_hash = (...args) => FWP.deprecated('set_hash', 'setHash', ...args); diff --git a/wp-content/plugins/facetwp/assets/js/src/event-manager.js b/wp-content/plugins/facetwp/assets/js/src/event-manager.js new file mode 100644 index 000000000..738ba6f3f --- /dev/null +++ b/wp-content/plugins/facetwp/assets/js/src/event-manager.js @@ -0,0 +1,6 @@ +/** + * WP-JS-Hooks + * @version 1.0.0 + * @author Carl Danley & 10up + */ +!function(t,n){"use strict";t.FWP=t.FWP||{},t.FWP.hooks=t.FWP.hooks||new function(){function t(t,n,r,i){var e,o,c;if(f[t][n])if(r)if(e=f[t][n],i)for(c=e.length;c--;)(o=e[c]).callback===r&&o.context===i&&e.splice(c,1);else for(c=e.length;c--;)e[c].callback===r&&e.splice(c,1);else f[t][n]=[]}function n(t,n,i,e,o){var c={callback:i,priority:e,context:o},l=f[t][n];l?(l.push(c),l=r(l)):l=[c],f[t][n]=l}function r(t){for(var n,r,i,e=1,o=t.length;en.priority;)t[r]=t[r-1],--r;t[r]=n}return t}function i(t,n,r){var i,e,o=f[t][n];if(!o)return"filters"===t&&r[0];if(e=o.length,"filters"===t)for(i=0;i { + return $.post(endpoint, { + action: 'facetwp_autocomplete_load', + facet_name: facet_name, + query: el.value, + data: FWP.buildPostData() + }, { + done: (resp) => { + this.fcomplete.render(resp); + } + }); + }; + options.onSelect = () => FWP.autoload(); + + fComplete(el, options); + }); + }); + + $().on('keyup', '.facetwp-autocomplete', function(e) { + if (13 === e.which && ! FWP.is_refresh) { + FWP.autoload(); + } + }); + + $().on('click', '.facetwp-autocomplete-update', function() { + FWP.autoload(); + }); + + /* ======== Checkboxes ======== */ + + FWP.hooks.addAction('facetwp/refresh/checkboxes', function($this, facet_name) { + var selected_values = []; + $this.find('.facetwp-checkbox.checked').each(function() { + selected_values.push( + $(this).attr('data-value') + ); + }); + FWP.facets[facet_name] = selected_values; + }); + + FWP.hooks.addFilter('facetwp/selections/checkboxes', function(output, params) { + var choices = []; + $.each(params.selected_values, function(val) { + var $item = params.el.find('.facetwp-checkbox[data-value="' + val + '"]'); + if ($item.len()) { + var choice = $($item.html()); + choice.find('.facetwp-counter').remove(); + choice.find('.facetwp-expand').remove(); + choices.push({ + value: val, + label: choice.text() + }); + } + }); + return choices; + }); + + $().on('click', '.facetwp-type-checkboxes .facetwp-expand', function(e) { + var $wrap = $(this).closest('.facetwp-checkbox').next('.facetwp-depth'); + $wrap.toggleClass('visible'); + var content = $wrap.hasClass('visible') ? FWP_JSON['collapse'] : FWP_JSON['expand']; + $(this).html(content); + e.stopImmediatePropagation(); + }); + + $().on('click', '.facetwp-type-checkboxes .facetwp-checkbox:not(.disabled)', function() { + var $cb = $(this); + var is_checked = ! $cb.hasClass('checked'); + var is_child = $cb.closest('.facetwp-depth').len() > 0; + var is_parent = $cb.next().hasClass('facetwp-depth'); + + // if a parent is clicked, deselect all of its children + if (is_parent) { + $cb.next('.facetwp-depth').find('.facetwp-checkbox').removeClass('checked'); + } + // if a child is clicked, deselects all of its parents + if (is_child) { + $cb.parents('.facetwp-depth').each(function() { + $(this).prev('.facetwp-checkbox').removeClass('checked'); + }); + } + + $cb.toggleClass('checked', is_checked); + FWP.autoload(); + }); + + $().on('click', '.facetwp-type-checkboxes .facetwp-toggle', function() { + var $parent = $(this).closest('.facetwp-facet'); + $parent.find('.facetwp-toggle').toggleClass('facetwp-hidden'); + $parent.find('.facetwp-overflow').toggleClass('facetwp-hidden'); + }); + + $().on('facetwp-loaded', function() { + $('.facetwp-type-checkboxes .facetwp-overflow').each(function() { + var num = $(this).find('.facetwp-checkbox').len(); + var $el = $(this).next('.facetwp-toggle'); + $el.text($el.text().replace('{num}', num)); + + // auto-expand if a checkbox within the overflow is checked + if (0 < $(this).find('.facetwp-checkbox.checked').len()) { + $el.trigger('click'); + } + }); + + // hierarchy expand / collapse buttons + $('.facetwp-type-checkboxes').each(function() { + var $facet = $(this); + var name = $facet.attr('data-name'); + + // error handling + if (Object.keys(FWP.settings).length < 1) { + return; + } + + // expand children + if ('yes' === FWP.settings[name]['show_expanded']) { + $facet.find('.facetwp-depth').addClass('visible'); + } + + if (1 > $facet.find('.facetwp-expand').len()) { + + // expand groups with selected items + $facet.find('.facetwp-checkbox.checked').each(function() { + $(this).parents('.facetwp-depth').addClass('visible'); + }); + + // add the toggle button + $facet.find('.facetwp-depth').each(function() { + var which = $(this).hasClass('visible') ? 'collapse' : 'expand'; + $(this).prev('.facetwp-checkbox').append(' ' + FWP_JSON[which] + ''); + }); + } + }); + }); + + /* ======== Radio ======== */ + + FWP.hooks.addAction('facetwp/refresh/radio', function($this, facet_name) { + var selected_values = []; + $this.find('.facetwp-radio.checked').each(function() { + var val = $(this).attr('data-value'); + if ('' !== val) { + selected_values.push(val); + } + }); + FWP.facets[facet_name] = selected_values; + }); + + FWP.hooks.addFilter('facetwp/selections/radio', function(output, params) { + var choices = []; + $.each(params.selected_values, function(val) { + var $item = params.el.find('.facetwp-radio[data-value="' + val + '"]'); + if ($item.len()) { + var choice = $($item.html()); + choice.find('.facetwp-counter').remove(); + choices.push({ + value: val, + label: choice.text() + }); + } + }); + return choices; + }); + + $().on('click', '.facetwp-type-radio .facetwp-radio:not(.disabled)', function() { + var is_checked = $(this).hasClass('checked'); + $(this).closest('.facetwp-facet').find('.facetwp-radio').removeClass('checked'); + if (! is_checked) { + $(this).addClass('checked'); + } + FWP.autoload(); + }); + + /* ======== Date Range ======== */ + + FWP.hooks.addAction('facetwp/refresh/date_range', function($this, facet_name) { + var minNode = $this.find('.facetwp-date-min'); + var maxNode = $this.find('.facetwp-date-max'); + var min = (minNode.len()) ? pVal(minNode.nodes[0]) : ''; + var max = (maxNode.len()) ? pVal(maxNode.nodes[0]) : ''; + FWP.facets[facet_name] = ('' !== min || '' !== max) ? [min, max] : []; + }); + + FWP.hooks.addFilter('facetwp/selections/date_range', function(output, params) { + var $el = params.el; + var vals = params.selected_values; + var facet_name = $el.attr('data-name'); + var fields = FWP.settings[facet_name].fields; + var out = ''; + + if ('exact' == fields) { + if ('' !== vals[0]) { + out = vals[0]; + } + } + else if ('start_date' == fields) { + if ('' !== vals[0]) { + out = '[>=] ' + vals[0]; + } + } + else if ('end_date' == fields) { + if ('' !== vals[1]) { + out = '[<=] ' + vals[1]; + } + } + else if ('both' == fields) { + if ('' !== vals[0] || '' !== vals[1]) { + if ('' !== vals[0] && '' !== vals[1]) { + out = vals[0] + ' - ' + vals[1]; + } + else if ('' !== vals[0]) { + out = '[>=] ' + vals[0]; + } + else if ('' !== vals[1]) { + out = '[<=] ' + vals[1]; + } + } + } + + return out; + }); + + $().on('facetwp-loaded', function() { + var $dates = $('.facetwp-type-date_range .facetwp-date:not(.ready)'); + + if (0 === $dates.len()) { + return; + } + + $dates.each(function() { + var $this = $(this); + var facet_name = $this.closest('.facetwp-facet').attr('data-name'); + var settings = FWP.settings[facet_name]; + var opts = { + onChange: function(obj) { + FWP.autoload(); + } + }; + + if ('' !== settings.locale) { + opts.i18n = settings.locale; + } + + if ('' !== settings.format) { + opts.altFormat = settings.format; + } + + if ('both' == settings.fields) { + var which = $this.hasClass('facetwp-date-min') ? 'min' : 'max'; + opts.minDate = settings.range[which].minDate; + opts.maxDate = settings.range[which].maxDate; + } + else { + opts.minDate = settings.range.minDate; + opts.maxDate = settings.range.maxDate; + } + + opts = FWP.hooks.applyFilters('facetwp/set_options/date_range', opts, { + 'facet_name': facet_name, + 'element': $this + }); + + $this.addClass('ready'); // add class before fDate() + + if (opts.minDate.length && opts.maxDate.length) { + new fDate(this, opts); + } else { + this.value = ''; + let emptyText = $this.attr('data-empty'); + $this.attr('placeholder', emptyText).attr('disabled','disabled').addClass('disabled'); + } + + }); + }); + + /* ======== Dropdown ======== */ + + FWP.hooks.addAction('facetwp/refresh/dropdown', function($this, facet_name) { + var val = $this.find('.facetwp-dropdown').val(); + FWP.facets[facet_name] = val ? [val] : []; + }); + + FWP.hooks.addFilter('facetwp/selections/dropdown', function(output, params) { + var $item = params.el.find('.facetwp-dropdown'); + if ($item.len()) { + var dd = $item.nodes[0]; + var text = dd.options[dd.selectedIndex].text; + return text.replace(/\(\d+\)$/, ''); + } + return ''; + }); + + // Use jQuery if available for select2 + var $f = ('function' === typeof jQuery) ? jQuery : fUtil; + + $f(document).on('change', '.facetwp-type-dropdown select', function() { + var $facet = $(this).closest('.facetwp-facet'); + var facet_name = $facet.attr('data-name'); + + if ('' !== $(this).val()) { + FWP.frozen_facets[facet_name] = 'soft'; + } + FWP.autoload(); + }); + + /* ======== fSelect ======== */ + + FWP.hooks.addAction('facetwp/refresh/fselect', function($this, facet_name) { + var val = $this.find('select').val(); + if (null === val || '' === val) { + val = []; + } + FWP.facets[facet_name] = Array.isArray(val) ? val : [val]; + }); + + FWP.hooks.addFilter('facetwp/selections/fselect', function(output, params) { + var choices = []; + $.each(params.selected_values, (val) => { + var $item = params.el.find('option[value="' + val + '"]'); + if ($item.len()) { + choices.push({ + value: val, + label: $item.text() + }); + } + }); + return choices; + }); + + FWP.hooks.addAction('facetwp/loaded', function() { + if (null !== FWP.active_facet) { + var facet = FWP.active_facet; + if ('fselect' == facet.attr('data-type')) { + var input = facet.find('.facetwp-dropdown').nodes[0]; + if (input.fselect.settings.multiple) { + input.fselect.open(); + } + } + } + }); + + $().on('facetwp-loaded', function() { + $('.facetwp-type-fselect select:not(.fs-hidden)').each(function() { + var facet_name = $(this).closest('.facetwp-facet').attr('data-name'); + var settings = FWP.settings[facet_name]; + + settings.optionFormatter = function(label, node) { + var counter = node.getAttribute('data-counter'); + return (counter) ? label + ' (' + counter + ')' : label; + }; + + var opts = FWP.hooks.applyFilters('facetwp/set_options/fselect', settings, { + 'facet_name': facet_name + }); + + fSelect(this, opts); + }); + }); + + $().on('fs:changed', function(e) { + var is_facet = $(e.detail[0]).closest('.facetwp-type-fselect').len() > 0; + if (! FWP.is_refresh && is_facet) { + FWP.autoload(); + } + }); + + $().on('fs:closed', function() { + FWP.active_facet = null; + }); + + /* ======== Hierarchy ======== */ + + FWP.hooks.addAction('facetwp/refresh/hierarchy', function($this, facet_name) { + var selected_values = []; + $this.find('.facetwp-link.checked').each(function() { + selected_values.push( + $(this).attr('data-value') + ); + }); + FWP.facets[facet_name] = selected_values; + }); + + FWP.hooks.addFilter('facetwp/selections/hierarchy', function(output, params) { + var $item = params.el.find('.facetwp-link.checked'); + return $item.len() ? $item.text() : ''; + }); + + $().on('click', '.facetwp-type-hierarchy .facetwp-link', function() { + $(this).closest('.facetwp-facet').find('.facetwp-link').removeClass('checked'); + if ('' !== $(this).attr('data-value')) { + $(this).addClass('checked'); + } + FWP.autoload(); + }); + + $().on('click', '.facetwp-type-hierarchy .facetwp-toggle', function() { + var $parent = $(this).closest('.facetwp-facet'); + $parent.find('.facetwp-toggle').toggleClass('facetwp-hidden'); + $parent.find('.facetwp-overflow').toggleClass('facetwp-hidden'); + }); + + $().on('facetwp-loaded', function() { + $('.facetwp-type-hierarchy .facetwp-overflow').each(function() { + var num = $(this).find('.facetwp-link').len(); + var $el = $(this).next('.facetwp-toggle'); + $el.text($el.text().replace('{num}', num)); + }); + }); + + /* ======== Number Range ======== */ + + FWP.hooks.addAction('facetwp/refresh/number_range', function($this, facet_name) { + var min = $this.find('.facetwp-number-min').val() || ''; + var max = $this.find('.facetwp-number-max').val() || ''; + FWP.facets[facet_name] = ('' !== min || '' !== max) ? [min, max] : []; + }); + + FWP.hooks.addFilter('facetwp/selections/number_range', function(output, params) { + var $el = params.el; + var vals = params.selected_values; + var facet_name = $el.attr('data-name'); + var fields = FWP.settings[facet_name].fields; + var out = ''; + + if ('exact' == fields) { + if ('' !== vals[0]) { + out = vals[0]; + } + } + else if ('min' == fields) { + if ('' !== vals[0]) { + out = '[>=] ' + vals[0]; + } + } + else if ('max' == fields) { + if ('' !== vals[1]) { + out = '[<=] ' + vals[1]; + } + } + else if ('both' == fields) { + if ('' !== vals[0] || '' !== vals[1]) { + if ('' !== vals[0] && '' !== vals[1]) { + out = vals[0] + ' - ' + vals[1]; + } + else if ('' !== vals[0]) { + out = '[>=] ' + vals[0]; + } + else if ('' !== vals[1]) { + out = '[<=] ' + vals[1]; + } + } + } + + return out; + }); + + $().on('keyup', '.facetwp-type-number_range .facetwp-number', function(e) { + if (13 === e.which && ! FWP.is_refresh) { + FWP.autoload(); + } + }); + + $().on('click', '.facetwp-type-number_range .facetwp-submit', function() { + FWP.refresh(); + }); + + /* ======== Proximity ======== */ + + $().on('facetwp-loaded', async function() { + var $locations = $('.facetwp-location'); + + if ($locations.nodes.length < 1) { + return; + } + + if (! FWP.loaded) { + window.FWP_MAP = window.FWP_MAP || {}; + + + if ( 'place-class' == FWP.settings.places ) { // check places place-class + + try { + const { AutocompleteSessionToken, AutocompleteSuggestion } = await google.maps.importLibrary("places"); + + $().on('input', '.facetwp-location', FWP.helper.debounce(function(e) { + const val = $(e.target).val(); + const $facet = $(e.target).closest('.facetwp-facet'); + + if (!val || val.length < FWP_JSON['proximity']['minLength']) { + $facet.find('.location-results').addClass('facetwp-hidden'); + return; + } + + FWP_MAP.token ||= new AutocompleteSessionToken(); + + const request = { + input: val, + sessionToken: FWP_MAP.token, + ...FWP_JSON['proximity']['autocomplete_options'] + }; + + AutocompleteSuggestion.fetchAutocompleteSuggestions(request) + .then(({ suggestions }) => { + if (suggestions && suggestions.length > 0) { + let html = ''; + suggestions.forEach((suggestion, index) => { + let result = suggestion.placePrediction; + const css = (0 === index) ? ' active' : ''; + html += '
    '; + html += '' + (result.mainText?.toString() ?? '') + ' '; + html += '' + (result.secondaryText?.toString() ?? '') + ''; + html += '' + (result.text?.toString() ?? '') + ''; + html += '
    '; + }); + + html += '
    '; + $facet.find('.location-results').html(html).removeClass('facetwp-hidden'); + } else { + $facet.find('.location-results').html('').addClass('facetwp-hidden'); // Clear results if no predictions + } + }) + .catch((error) => { + console.error("Error fetching predictions:", error); + $facet.find('.location-results').html('
    Error fetching results.
    ').removeClass('facetwp-hidden'); // Display error message + }); + + }, FWP_JSON['proximity']['queryDelay'])); + + } catch (error) { + console.error("Error loading Places library:", error); + } + + } else { // places places-service + + await google.maps.importLibrary("places"); + + FWP_MAP.sessionToken = new google.maps.places.AutocompleteSessionToken(); + FWP_MAP.autocompleteService = new google.maps.places.AutocompleteService(); + FWP_MAP.placesService = new google.maps.places.PlacesService( + document.createElement('div') + ); + + // We need FWP_JSON available to grab the queryDelay + $().on('input', '.facetwp-location', FWP.helper.debounce(function(e) { + var val = $(e.target).val(); + var $facet = $(e.target).closest('.facetwp-facet'); + + if ('' == val || val.length < FWP_JSON['proximity']['minLength']) { + $facet.find('.location-results').addClass('facetwp-hidden'); + return; + } + + var options = FWP_JSON['proximity']['autocomplete_options']; + options.sessionToken = FWP_MAP.sessionToken; + options.input = val; + + FWP_MAP.autocompleteService.getPredictions(options, function(results, status) { + if (status === google.maps.places.PlacesServiceStatus.OK) { + var html = ''; + + results.forEach(function(result, index) { + var css = (0 === index) ? ' active' : ''; + html += '
    '; + html += '' + result.structured_formatting.main_text + ' '; + html += '' + result.structured_formatting.secondary_text + ''; + html += '' + result.description + ''; + html += '
    '; + }); + + html += '
    '; + + $facet.find('.location-results').html(html).removeClass('facetwp-hidden'); + } + }); + }, FWP_JSON['proximity']['queryDelay'])); + } + } // end check places + + $locations.each(function(el, idx) { + $(this).trigger('keyup'); + }); + }); + + $().on('click', '.location-result', async function() { + + if ( 'place-class' == FWP.settings.places ) { // check places place-class + + const $facet = $(this).closest('.facetwp-facet'); + const placeId = $(this).attr('data-place-id'); + + const { Place, AutocompleteSessionToken } = await google.maps.importLibrary("places"); + + FWP_MAP.token ||= new AutocompleteSessionToken(); + + // Fields parameter for Place Details request + const fields = ['location']; // Request only the location + const place = new Place({ id: placeId, sessionToken: FWP_MAP.token }); + + place.fetchFields({ fields }) + .then(() => { + $facet.find('.facetwp-lat').val(place.location.lat()); + $facet.find('.facetwp-lng').val(place.location.lng()); + FWP.autoload(); + }) + .catch((error) => { + console.error("Error fetching place details:", error); + // Handle the error appropriately (e.g., display an error message to the user) + }); + + FWP_MAP.token = null; // clear session token + + } else { + + var $facet = $(this).closest('.facetwp-facet'); + var place_id = $(this).attr('data-id'); + + FWP_MAP.placesService.getDetails({ + placeId: place_id, + fields: ['geometry'], + sessionToken: FWP_MAP.sessionToken, + }, function(place, status) { + if (status === google.maps.places.PlacesServiceStatus.OK) { + $facet.find('.facetwp-lat').val(place.geometry.location.lat()); + $facet.find('.facetwp-lng').val(place.geometry.location.lng()); + FWP.autoload(); + } + }); + + } + + var description = $(this).find('.result-description').text(); + $('.facetwp-location').val(description); + $('.location-results').addClass('facetwp-hidden'); + }); + + /* ======== Proximity ======== */ + + $().on('click', '.facetwp-type-proximity .locate-me', function(e) { + var $this = $(this); + var $facet = $this.closest('.facetwp-facet'); + var $input = $facet.find('.facetwp-location'); + var $lat = $facet.find('.facetwp-lat'); + var $lng = $facet.find('.facetwp-lng'); + + // reset + if ($this.hasClass('f-reset')) { + $lat.val(''); + $lng.val(''); + $input.val(''); + FWP.autoload(); + return; + } + + $this.addClass('f-loading'); + + // HTML5 geolocation + if (navigator.geolocation) { + navigator.geolocation.getCurrentPosition( + function(position) { // Success callback + var lat = position.coords.latitude; + var lng = position.coords.longitude; + + $lat.val(lat); + $lng.val(lng); + + var geocoder = new google.maps.Geocoder(); + var latlng = { lat: parseFloat(lat), lng: parseFloat(lng) }; + geocoder.geocode({ 'location': latlng }, function(results, status) { + if (status === google.maps.GeocoderStatus.OK) { + $input.val(results[0].formatted_address); + } else { + console.error("Geocoder failed due to: " + status); + $input.val('Your location'); // Fallback + alert("Could not determine your address."); // Inform user + } + $this.addClass('f-reset'); + FWP.autoload(); + }); + + $this.removeClass('f-loading'); + + FWP.hooks.doAction('facetwp/geolocation/success', { + 'facet': $facet, + 'position': position + }); + }, + function(error) { // Error callback + $this.removeClass('f-loading'); + let errorMessage = "Could not get your location."; + + switch (error.code) { + case error.PERMISSION_DENIED: + errorMessage = "User denied the request for Geolocation."; + break; + case error.POSITION_UNAVAILABLE: + errorMessage = "Location information is unavailable."; + break; + case error.TIMEOUT: + errorMessage = "The request to get user location timed out."; + break; + case error.UNKNOWN_ERROR: + errorMessage = "An unknown error occurred."; + break; + } + + console.error("Geolocation error: " + errorMessage, error); + alert(errorMessage); // Inform user + FWP.hooks.doAction('facetwp/geolocation/error', { + 'facet': $facet, + 'error': error + }); + } + ); + } else { + // Browser doesn't support Geolocation + $this.removeClass('f-loading'); + alert("Your browser doesn't support Geolocation."); + } + }); + + $().on('keyup', '.facetwp-location', function(e) { + var $facet = $(this).closest('.facetwp-facet'); + var method = ('' !== $(this).val()) ? 'addClass' : 'removeClass'; + $facet.find('.locate-me')[method]('f-reset'); + + if (38 === e.which || 40 === e.which || 13 === e.which) { + var curr_index = parseInt($facet.find('.location-result.active').attr('data-index')); + var max_index = parseInt($facet.find('.location-result').last().attr('data-index')); + } + + if (38 === e.which) { // up + var new_index = (0 < curr_index) ? (curr_index - 1) : max_index; + $facet.find('.location-result.active').removeClass('active'); + $facet.find('.location-result[data-index="' + new_index + '"]').addClass('active'); + } + else if (40 === e.which) { // down + var new_index = (curr_index < max_index) ? (curr_index + 1) : 0; + $facet.find('.location-result.active').removeClass('active'); + $facet.find('.location-result[data-index="' + new_index + '"]').addClass('active'); + } + else if (13 === e.which) { // enter + $facet.find('.location-result.active').trigger('click'); + } + }); + + var hideDropdown = function(e) { + var $el = $(e.target); + var $wrap = $el.closest('.facetwp-input-wrap'); + + if ($wrap.len() < 1 || $el.hasClass('f-reset')) { + $('.location-results').addClass('facetwp-hidden'); + } + }; + + $().on('click', hideDropdown); + $().on('focusout', hideDropdown); + + $().on('focusin', '.facetwp-location', function() { + var $facet = $(this).closest('.facetwp-facet'); + if ('' != $(this).val()) { + $facet.find('.location-results').removeClass('facetwp-hidden'); + } + }); + + $().on('change', '.facetwp-radius', function() { + var $facet = $(this).closest('.facetwp-facet'); + if ('' !== $facet.find('.facetwp-location').val()) { + FWP.autoload(); + } + }); + + $().on('input', '.facetwp-radius-slider', function(e) { + var $facet = $(this).closest('.facetwp-facet'); + $facet.find('.facetwp-radius-dist').text(e.target.value); + }); + + FWP.hooks.addAction('facetwp/refresh/proximity', function($this, facet_name) { + var lat = $this.find('.facetwp-lat').val(); + var lng = $this.find('.facetwp-lng').val(); + var radius = $this.find('.facetwp-radius').val(); + var location = encodeURIComponent($this.find('.facetwp-location').val()); + FWP.frozen_facets[facet_name] = 'hard'; + FWP.facets[facet_name] = ('' !== lat && 'undefined' !== typeof lat) ? + [lat, lng, radius, location] : []; + }); + + FWP.hooks.addFilter('facetwp/selections/proximity', function(label, params) { + return FWP_JSON['proximity']['clearText']; + }); + + /* ======== Search ======== */ + + FWP.logic.search = { + delay_refresh: FWP.helper.debounce(function(facet_name) { + FWP.frozen_facets[facet_name] = 'soft'; + FWP.autoload(); + }, 500) + }; + + FWP.hooks.addAction('facetwp/refresh/search', function($this, facet_name) { + var $input = $this.find('.facetwp-search'); + FWP.facets[facet_name] = $input.val() || ''; + $this.find('.facetwp-icon').addClass('f-loading'); + }); + + FWP.hooks.addAction('facetwp/loaded', function() { + $('.facetwp-type-search .facetwp-icon').removeClass('f-loading'); + }); + + $().on('keyup', '.facetwp-type-search .facetwp-search', function(e) { + if (FWP.is_refresh) { + return; + } + + var $facet = $(this).closest('.facetwp-facet'); + var facet_name = $facet.attr('data-name'); + + if ('undefined' !== typeof FWP.settings[facet_name]) { + if ('yes' === FWP.settings[facet_name]['auto_refresh']) { + FWP.logic.search['delay_refresh'](facet_name); + } + else if (13 === e.keyCode) { + FWP.autoload(); + } + } + }); + + $().on('click', '.facetwp-type-search .facetwp-icon', function() { + if (! FWP.is_refresh) { + FWP.autoload(); + } + }); + + /* ======== Slider ======== */ + + FWP.hooks.addAction('facetwp/refresh/slider', function($this, facet_name) { + FWP.facets[facet_name] = []; + + var $active = FWP.active_facet; + var url_var = FWP.helper.getUrlVar(facet_name); + + if (null !== $active && facet_name === $active.attr('data-name')) { + var node = $active.find('.facetwp-slider').nodes[0]; + if ('undefined' !== typeof node.noUiSlider) { + FWP.facets[facet_name] = node.noUiSlider.get(); + } + } + else if (false !== url_var) { + FWP.facets[facet_name] = url_var.replace('%2C', ',').split(','); + } + + // prevent changes during loading + $this.find('.facetwp-slider').attr('disabled', true); + }); + + FWP.hooks.addAction('facetwp/loaded', function() { + $('.facetwp-type-slider .facetwp-slider').nodes.forEach(node => node.removeAttribute('disabled')); + }); + + FWP.hooks.addAction('facetwp/set_label/slider', function($this) { + var facet_name = $this.attr('data-name'); + var min = FWP.settings[facet_name]['lower']; + var max = FWP.settings[facet_name]['upper']; + var format = FWP.settings[facet_name]['format']; + var opts = { + decimal_separator: FWP.settings[facet_name]['decimal_separator'], + thousands_separator: FWP.settings[facet_name]['thousands_separator'] + }; + + var prefix = FWP.settings[facet_name]['prefix']; + var suffix = FWP.settings[facet_name]['suffix']; + + if ( min === max ) { + var label = prefix + nummy(min).format(format, opts) + suffix; + } + else { + var label = prefix + nummy(min).format(format, opts) + suffix + ' — ' + + prefix + nummy(max).format(format, opts) + suffix; + } + $this.find('.facetwp-slider-label').html(label); + }); + + FWP.hooks.addFilter('facetwp/selections/slider', function(output, params) { + var $item = params.el.find('.facetwp-slider-label'); + return $item.len() ? $item.text() : ''; + }); + + $().on('facetwp-loaded', function() { + $('.facetwp-type-slider .facetwp-slider').each(function() { + var $this = $(this); + var $parent = $this.closest('.facetwp-facet'); + var facet_name = $parent.attr('data-name'); + var opts = FWP.settings[facet_name]; + + // custom slider options + var slider_opts = FWP.hooks.applyFilters('facetwp/set_options/slider', { + range: opts.range, + start: opts.start, + step: parseFloat(opts.step), + connect: true, + handleAttributes: opts.handle_attributes + }, { 'facet_name': facet_name }); + + if ($this.hasClass('ready')) { + $this.nodes[0].noUiSlider.updateOptions({ + range: slider_opts.range + }, false); + } + else { + + // fail on slider already initialized + if ('undefined' !== typeof this.noUiSlider) { + return; + } + + // fail if start values are null + if (null === slider_opts.start[0]) { + return; + } + + // fail on invalid ranges + if (parseFloat(opts.range.min) > parseFloat(opts.range.max)) { + FWP.settings[facet_name]['lower'] = opts.range.min; + FWP.settings[facet_name]['upper'] = opts.range.max; + FWP.hooks.doAction('facetwp/set_label/slider', $parent); + return; + } + + // disable the UI if only 1 value + if (parseFloat(opts.range.min) == parseFloat(opts.range.max)) { + $this.attr('data-disabled', 'true'); + } + + var slider = this; + noUiSlider.create(slider, slider_opts); + + // Only trigger a refresh if slider handles have actually moved + slider.noUiSlider.on('slide', function () { + $this.attr('data-has-moved', 'true'); + }); + + slider.noUiSlider.on('update', function(values, handle) { + FWP.settings[facet_name]['lower'] = values[0]; + FWP.settings[facet_name]['upper'] = values[1]; + FWP.hooks.doAction('facetwp/set_label/slider', $parent); + }); + + // This runs after click or handle slide + slider.noUiSlider.on('set', function() { + if ('true' === $this.attr('data-has-moved')) { + $this.attr('data-has-moved', ''); + FWP.active_facet = $this.closest('.facetwp-facet'); + FWP.autoload(); + } + }); + + $this.addClass('ready'); + } + }); + + // hide reset buttons + $('.facetwp-type-slider').each(function() { + var name = $(this).attr('data-name'); + var $button = $(this).find('.facetwp-slider-reset'); + var method = FWP.facets[name].length ? 'removeClass' : 'addClass'; + $button[method]('facetwp-hidden'); + }); + }); + + $().on('click', '.facetwp-type-slider .facetwp-slider-reset', function() { + var facet_name = $(this).closest('.facetwp-facet').attr('data-name'); + FWP.reset(facet_name); + }); + + /* ======== Rating ======== */ + + FWP.hooks.addAction('facetwp/refresh/rating', function($this, facet_name) { + var selected_values = []; + $this.find('.facetwp-star.selected').each(function() { + var val = $(this).attr('data-value'); + if ('' != val) { + selected_values.push(val); + } + }); + FWP.facets[facet_name] = selected_values; + }); + + $().on('mouseover', '.facetwp-star:not(.disabled)', function() { + var $facet = $(this).closest('.facetwp-facet'); + + if ($(this).hasClass('selected')) { + $facet.find('.facetwp-star-label').text(FWP_JSON['rating']['Undo']); + } + else { + var label = ('5' == $(this).attr('data-value')) ? '' : FWP_JSON['rating']['& up']; + $facet.find('.facetwp-star-label').text(label); + $facet.find('.facetwp-counter').text('(' + $(this).attr('data-counter') + ')'); + } + }); + + $().on('mouseout', '.facetwp-star:not(.disabled)', function() { + var $facet = $(this).closest('.facetwp-facet'); + $facet.find('.facetwp-star-label').text(''); + $facet.find('.facetwp-counter').text(''); + }); + + $().on('click', '.facetwp-star:not(.disabled)', function() { + var $facet = $(this).closest('.facetwp-facet'); + var is_selected = $(this).hasClass('selected'); + $facet.find('.facetwp-star').removeClass('selected'); + if (! is_selected) { + $(this).addClass('selected'); + } + FWP.autoload(); + }); + + /* ======== Sort ======== */ + + FWP.hooks.addAction('facetwp/refresh/sort', function($this, facet_name) { + var val = $this.find('select').val(); + FWP.facets[facet_name] = val ? [val] : []; + }); + + $().on('change', '.facetwp-type-sort select', function() { + var $facet = $(this).closest('.facetwp-facet'); + var facet_name = $facet.attr('data-name'); + + if ('' !== $(this).val()) { + FWP.frozen_facets[facet_name] = 'hard'; + } + FWP.autoload(); + }); + + /* ======== Pager ======== */ + + FWP.hooks.addAction('facetwp/refresh/pager', function($this, facet_name) { + FWP.facets[facet_name] = []; + }); + + FWP.hooks.addFilter('facetwp/template_html', function(resp, params) { + if (FWP.is_load_more) { + FWP.is_load_more = false; + + // layout builder + if ( 0 < $('.fwpl-layout').len() ) { + var layout = $(params.html).find('.fwpl-layout').html(); + $('.facetwp-template .fwpl-layout').append(layout); + } + // other + else { + $('.facetwp-template').append(params.html); + } + return true; + } + return resp; + }); + + $().on('click', '.facetwp-load-more', function() { + var loading_text = $(this).attr('data-loading'); + $(this).html(loading_text); + + FWP.is_load_more = true; // set the flag + FWP.load_more_paged += 1; // next page + FWP.paged = FWP.load_more_paged; // grab the next page of results + FWP.soft_refresh = true; // don't process facets + FWP.refresh(); + }); + + $().on('facetwp-loaded', function() { + var is_visible = (FWP.settings.pager.page < FWP.settings.pager.total_pages); + var method = is_visible ? 'removeClass' : 'addClass'; + $('.facetwp-load-more')[method]('facetwp-hidden'); + }); + + $().on('facetwp-refresh', function() { + if (! FWP.loaded || ! FWP.is_load_more) { + FWP.load_more_paged = 1; + } + }); + + /* ======== Reset ======== */ + + $().on('click', '.facetwp-reset', function() { + let values = $(this).nodes[0]._facets; + FWP.reset(values); + }); + + $().on('facetwp-loaded', function() { + if (! FWP.loaded) { + $('.facetwp-reset').each(function() { + let $this = $(this); + let mode = $this.attr('data-mode'); + let values = $this.attr('data-values'); + + values = (null == values) ? Object.keys(FWP.facets) : values.split(','); + + if ('exclude' == mode) { + values = Object.keys(FWP.facets).filter(name => { + return !values.includes(name); + }); + } + + // store the target facets (array) within the DOM element + $this.nodes[0]._facets = values; + }); + } + + // hide the reset if its target facets are all empty + $('.facetwp-hide-empty').each(function() { + let $this = $(this); + let $wrap = $this.closest('.facetwp-facet'); + let facets = $this.nodes[0]._facets; + let all_empty = facets.every(val => 'undefined' === typeof FWP.facets[val] || FWP.facets[val].length < 1); + all_empty ? $wrap.addClass('facetwp-hidden') : $wrap.removeClass('facetwp-hidden'); + }); + }); + +})(fUtil); diff --git a/wp-content/plugins/facetwp/assets/js/src/front.js b/wp-content/plugins/facetwp/assets/js/src/front.js new file mode 100644 index 000000000..bc0674761 --- /dev/null +++ b/wp-content/plugins/facetwp/assets/js/src/front.js @@ -0,0 +1,686 @@ +window.FWP = (($) => { + + class FacetWP { + constructor() { + this.import(); + this.bindEvents(); + } + + import() { + if ('undefined' !== typeof FWP) { + $.each(FWP, (val, key) => this[key] = val); + } + } + + init() { + var FWP = this; + + this.setDefaults(); + + if (0 < $('.facetwp-sort').len()) { + FWP.extras.sort = 'default'; + } + + if (0 < $('.facetwp-pager').len()) { + FWP.extras.pager = true; + } + + if (0 < $('.facetwp-per-page').len()) { + FWP.extras.per_page = 'default'; + } + + if (0 < $('.facetwp-counts').len()) { + FWP.extras.counts = true; + } + + if (0 < $('.facetwp-selections').len()) { + FWP.extras.selections = true; + } + + // Make sure there's a template + var has_template = $('.facetwp-template').len() > 0; + + if (! has_template) { + var has_loop = FWP.helper.detectLoop(document.body); + + if (has_loop) { + $(has_loop).addClass('facetwp-template'); + } + else { + console.error('FacetWP has not detected a listing template'); + return; + } + } + + var $div = $('.facetwp-template').first(); + FWP.template = $div.attr('data-name') ? $div.attr('data-name') : 'wp'; + + // Facets inside the template? + if ($div.find('.facetwp-facet').len() > 0) { + console.error('Facets should not be inside the "facetwp-template" container'); + } + + FWP.hooks.doAction('facetwp/ready'); + + // Generate the user selections + if (FWP.extras.selections) { + FWP.hooks.addAction('facetwp/loaded', () => { + + var selections = ''; + var skipped = ['pager', 'reset', 'sort']; + var selected_facets = ! FWP.loaded && ! FWP.is_bfcache && $.isset(FWP_HTTP.url_vars) ? Object.assign(FWP.facets, FWP_HTTP.url_vars): FWP.facets; + + $.each(selected_facets, (val, key) => { + if (val.length < 1 || ! $.isset(FWP.settings.labels[key]) || skipped.includes(FWP.facet_type[key])) { + return true; // skip facet + } + + var choices = val; + var $el = $('.facetwp-facet-' + key); + var facet_type = $el.attr('data-ui') || $el.attr('data-type'); + choices = FWP.hooks.applyFilters('facetwp/selections/' + facet_type, choices, { + 'el': $el, + 'selected_values': choices + }); + + if (choices.length) { + if ('string' === typeof choices) { + choices = [{ value: '', label: choices }]; + } + else if (! $.isset(choices[0].label)) { + choices = [{ value: '', label: choices[0] }]; + } + } + + var values = ''; + $.each(choices, (choice) => { + values += '' + FWP.helper.escapeHtml(choice.label) + ''; + }); + + if ('' !== values) { + selections += '
  • ' + FWP.settings.labels[key] + ': ' + values + '
  • '; + } + }); + + if ('' !== selections) { + selections = '
      ' + selections + '
    '; + } + + $('.facetwp-selections').html(selections); + }); + } + + FWP.refresh(); + } + + setDefaults() { + let defaults = { + 'facets': {}, + 'template': null, + 'settings': {}, + 'is_reset': false, + 'is_refresh': false, + 'is_bfcache': false, + 'is_hash_click': false, + 'is_load_more': false, + 'auto_refresh': true, + 'soft_refresh': false, + 'frozen_facets': {}, + 'active_facet': null, + 'facet_type': {}, + 'loaded': false, + 'extras': {}, + 'paged': 1 + }; + + for (var prop in defaults) { + if (!$.isset(this[prop])) { + this[prop] = defaults[prop]; + } + } + } + + refresh() { + FWP.is_refresh = true; + + // Add the loading overlay + FWP.toggleOverlay('on'); + + // Load facet DOM values + if (! FWP.is_reset) { + FWP.parseFacets(); + } + + // Check the URL on pageload + if (! FWP.loaded) { + FWP.loadFromHash(); + } + + // Fire a notification event + $().trigger('facetwp-refresh'); + + // Trigger window.onpopstate + if (FWP.loaded && ! FWP.is_popstate && ! FWP.is_load_more) { + FWP.setHash(); + } + + // Preload? + if (! FWP.loaded && ! FWP.is_bfcache && $.isset(FWP_JSON.preload_data)) { + FWP.render(FWP_JSON.preload_data); + } + else { + FWP.fetchData(); + } + + // Unfreeze any soft-frozen facets + $.each(FWP.frozen_facets, (type, name) => { + if ('hard' !== type) { + delete FWP.frozen_facets[name]; + } + }); + + // Cleanup + FWP.paged = 1; + FWP.soft_refresh = false; + FWP.is_refresh = false; + FWP.is_reset = false; + } + + autoload() { + if (FWP.auto_refresh && ! FWP.is_refresh) { + FWP.refresh(); + } + } + + parseFacets() { + FWP.facets = {}; + + $('.facetwp-facet').each(function() { + var $this = $(this); + var facet_name = $this.attr('data-name'); + var facet_type = $this.attr('data-type'); + var is_ignored = $this.hasClass('facetwp-ignore'); + + if (null !== $this.attr('data-ui')) { + facet_type = $this.attr('data-ui'); + } + + // Store the facet type + FWP.facet_type[facet_name] = facet_type; + + // Plugin hook + if (! is_ignored) { + FWP.hooks.doAction('facetwp/refresh/' + facet_type, $this, facet_name); + } + }); + } + + buildQueryString() { + var query_string = ''; + + // Non-FacetWP URL variables + var hash = []; + var get_str = window.location.search.replace('?', '').split('&'); + $.each(get_str, (val) => { + var param_name = val.split('=')[0]; + if (0 !== param_name.indexOf(FWP_JSON.prefix)) { + hash.push(val); + } + }); + hash = hash.join('&'); + + // FacetWP URL variables + var fwp_vars = Object.assign({}, FWP.facets); + + // Add pagination to the URL hash + if (1 < FWP.paged) { + fwp_vars['paged'] = FWP.paged; + } + + // Add "per page" to the URL hash + if (FWP.extras.per_page && 'default' !== FWP.extras.per_page) { + fwp_vars['per_page'] = FWP.extras.per_page; + } + + // Add sorting to the URL hash + if (FWP.extras.sort && 'default' !== FWP.extras.sort) { + fwp_vars['sort'] = FWP.extras.sort; + } + + fwp_vars = FWP.helper.serialize(fwp_vars, FWP_JSON.prefix); + + if ('' !== hash) { + query_string += hash; + } + if ('' !== fwp_vars) { + query_string += ('' !== hash ? '&' : '') + fwp_vars; + } + + return query_string; + } + + setHash() { + var query_string = FWP.buildQueryString(); + + if ('' !== query_string) { + query_string = '?' + query_string; + } + + if (history.pushState) { + history.pushState(null, null, window.location.pathname + query_string); + } + + // Update FWP_HTTP.get + FWP_HTTP.get = {}; + window.location.search.replace('?', '').split('&').forEach((el) => { + var item = el.split('='); + + if ('' != item[0]) { + FWP_HTTP.get[item[0]] = item[1]; + } + }); + } + + loadFromHash() { + var hash = []; + var get_str = window.location.search.replace('?', '').split('&'); + $.each(get_str, (val) => { + var param_name = val.split('=')[0]; + if (0 === param_name.indexOf(FWP_JSON.prefix)) { + hash.push(val.replace(FWP_JSON.prefix, '')); + } + }); + hash = hash.join('&'); + + // Reset facet values + $.each(FWP.facets, (val, key) => { + FWP.facets[key] = []; + }); + + FWP.paged = 1; + FWP.extras.sort = 'default'; + + if ('' !== hash) { + hash = hash.split('&'); + $.each(hash, (chunk) => { + var obj = chunk.split('=')[0]; + var val = chunk.split('=')[1]; + + if ('paged' === obj) { + FWP.paged = val; + } + else if ('per_page' === obj || 'sort' === obj) { + FWP.extras[obj] = val; + } + else if ('' !== val) { + var type = $.isset(FWP.facet_type[obj]) ? FWP.facet_type[obj] : ''; + if ('search' === type || 'autocomplete' === type) { + FWP.facets[obj] = decodeURIComponent(val); + } + else { + FWP.facets[obj] = decodeURIComponent(val).split(','); + } + } + }); + } + } + + buildPostData() { + return { + 'facets': FWP.facets, + 'frozen_facets': FWP.frozen_facets, + 'http_params': FWP_HTTP, + 'template': FWP.template, + 'extras': FWP.extras, + 'soft_refresh': FWP.soft_refresh ? 1 : 0, + 'is_bfcache': FWP.is_bfcache ? 1 : 0, + 'first_load': FWP.loaded ? 0 : 1, + 'paged': FWP.paged + }; + } + + fetchData() { + var endpoint = ('wp' === FWP.template) ? document.URL : FWP_JSON.ajaxurl; + var data = { + action: 'facetwp_refresh', + data: FWP.buildPostData() + }; + + var settings = { + dataType: 'text', // better JSON error handling + done: (resp) => { + try { + var json = JSON.parse(resp); + FWP.render(json); + } + catch(e) { + var pos = resp.indexOf('{"facets'); + if (-1 < pos) { + var json = JSON.parse(resp.substr(pos)); + FWP.render(json); + } + else { + $('.facetwp-template').text('FacetWP was unable to auto-detect the post listing'); + console.log(resp); + } + } + }, + fail: (err) => { + console.log(err); + } + }; + + settings = FWP.hooks.applyFilters('facetwp/ajax_settings', settings); + + $.post(endpoint, data, settings); + } + + render(response) { + FWP.response = response; + + // Don't render CSS-based (or empty) templates on pageload + // The template has already been pre-loaded + if (('wp' === FWP.template || '' === response.template) && ! FWP.loaded && ! FWP.is_bfcache) { + var inject = false; + } + else { + var inject = response.template; + + if ('wp' === FWP.template) { + var obj = $(response.template); + var $tpl = obj.find('.facetwp-template'); + + if ($tpl.len() < 1) { + var loop = FWP.helper.detectLoop(obj.nodes[0]); + + if (loop) { + $tpl = $(loop).addClass('facetwp-template'); + } + } + + if ($tpl.len() > 0) { + var inject = $tpl.html(); + } + else { + // Fallback until "loop_no_results" action is added to WP core + var inject = FWP_JSON['no_results_text']; + } + } + } + + if (false !== inject) { + if (! FWP.hooks.applyFilters('facetwp/template_html', false, { 'response': response, 'html': inject })) { + $('.facetwp-template').html(inject); + } + } + + // Populate each facet box + $.each(response.facets, (val, name) => { + $('.facetwp-facet-' + name).html(val); + }); + + // Populate the counts + if ($.isset(response.counts)) { + $('.facetwp-counts').html(response.counts); + } + + // Populate the pager + if ($.isset(response.pager)) { + $('.facetwp-pager').html(response.pager); + } + + // Populate the "per page" box + if ($.isset(response.per_page)) { + $('.facetwp-per-page').html(response.per_page); + if ('default' !== FWP.extras.per_page) { + $('.facetwp-per-page-select').val(FWP.extras.per_page); + } + } + + // Populate the sort box + if ($.isset(response.sort)) { + $('.facetwp-sort').html(response.sort); + $('.facetwp-sort-select').val(FWP.extras.sort); + } + + // Populate the settings object (iterate to preserve static facet settings) + $.each(response.settings, (val, key) => { + FWP.settings[key] = val; + }); + + // WP Playlist support + if ('function' === typeof WPPlaylistView) { + $('.facetwp-template .wp-playlist').each((item) => { + return new WPPlaylistView({ el: item }); + }); + } + + // Fire a notification event + $().trigger('facetwp-loaded'); + + // Allow final actions + FWP.hooks.doAction('facetwp/loaded'); + + // Remove the loading overlay + FWP.toggleOverlay('off'); + + // Clear the active facet + FWP.active_facet = null; + + // Detect "back-forward" cache + FWP.is_bfcache = true; + + // Done loading? + FWP.loaded = true; + } + + reset(facets) { + FWP.parseFacets(); + + var opts = {}; + + if ('string' === typeof facets) { + opts[facets] = ''; + } + else if (Array.isArray(facets)) { + $.each(facets, (facet_name) => { + opts[facet_name] = ''; + }); + } + else if ('object' === typeof facets && !! facets) { + opts = facets; + } + + var reset_all = Object.keys(opts).length < 1; + + $.each(FWP.facets, (vals, facet_name) => { + var has_reset = $.isset(opts[facet_name]); + var selected_vals = Array.isArray(vals) ? vals : [vals]; + + if (has_reset && -1 < selected_vals.indexOf(opts[facet_name])) { + var pos = selected_vals.indexOf(opts[facet_name]); + selected_vals.splice(pos, 1); // splice() is mutable! + FWP.facets[facet_name] = selected_vals; + } + + if (has_reset && (selected_vals.length < 1 || '' === opts[facet_name])) { + delete FWP.frozen_facets[facet_name]; + } + + if (reset_all || (has_reset && '' === opts[facet_name])) { + FWP.facets[facet_name] = []; + } + }); + + if (reset_all) { + FWP.extras.per_page = 'default'; + FWP.extras.sort = 'default'; + FWP.frozen_facets = {}; + } + + FWP.hooks.doAction('facetwp/reset'); + FWP.is_reset = true; + FWP.refresh(); + } + + toggleOverlay(which) { + var method = ('on' === which) ? 'addClass' : 'removeClass'; + $('.facetwp-facet')[method]('is-loading'); + } + + bindEvents() { + window.addEventListener('popstate', () => { + + // Detect browser "back-foward" cache + if (FWP.is_bfcache) { + FWP.loaded = false; + } + + if ((FWP.loaded || FWP.is_bfcache) && ! FWP.is_refresh && ! FWP.is_hash_click) { + FWP.is_popstate = true; + FWP.refresh(); + FWP.is_popstate = false; + } + + FWP.is_hash_click = false; + }); + + // Prevent hash clicks from triggering a refresh + $().on('click', 'a[href^="#"]', () => { + FWP.is_hash_click = true; + }); + + // Click on a user selection + $().on('click', '.facetwp-selections .facetwp-selection-value', function() { + if (FWP.is_refresh) { + return; + } + + var facet_name = $(this).closest('li').attr('data-facet'); + var facet_value = $(this).attr('data-value'); + + if ('' != facet_value) { + var obj = {}; + obj[facet_name] = facet_value; + FWP.reset(obj); + } + else { + FWP.reset(facet_name); + } + }); + + // Pagination + $().on('click', '.facetwp-page[data-page]', function() { + $('.facetwp-page').removeClass('active'); + $(this).addClass('active'); + + FWP.paged = $(this).attr('data-page'); + FWP.soft_refresh = true; + + let facet_name = $(this).closest('.facetwp-type-pager').attr('data-name'); + FWP.scroll_target = ( 'string' == typeof facet_name) ? FWP.settings[facet_name].scroll_target : ''; + FWP.scroll_offset = ( '' != FWP.scroll_target && 'number' == typeof Number(FWP.settings[facet_name].scroll_offset) ) ? FWP.settings[facet_name].scroll_offset : 0; + FWP.refresh(); + }); + + FWP.hooks.addAction('facetwp/loaded', function() { + try { + if ( !FWP.loaded && 1 < FWP.settings.pager.page ) { + let numbers_pager = Object.keys(FWP.settings).filter(key => FWP.settings[key].hasOwnProperty('scroll_target')); + let facet_name = numbers_pager[0]; + FWP.scroll_target = ( 'string' == typeof facet_name) ? FWP.settings[facet_name].scroll_target : ''; + FWP.scroll_offset = ( '' != FWP.scroll_target && 'number' == typeof Number(FWP.settings[facet_name].scroll_offset) ) ? FWP.settings[facet_name].scroll_offset : 0; + } + let target = document.querySelector(FWP.scroll_target); + let targetPos = target.getBoundingClientRect().top + Number(FWP.scroll_offset); + window.scrollBy({ top: targetPos, behavior: 'smooth' }); + } + catch(e) { + // do nothing + } + FWP.scroll_target = ''; // remove scroll + FWP.scroll_offset = ''; // remove scroll + }, 100 ); + + // Use jQuery if available for select2 + var $f = ('function' === typeof jQuery) ? jQuery : fUtil; + + // Per page + $f(document).on('change', '.facetwp-per-page-select', function() { + FWP.extras.per_page = $(this).val(); + FWP.soft_refresh = true; + FWP.autoload(); + }); + + // Sorting + $f(document).on('change', '.facetwp-sort-select', function() { + FWP.extras.sort = $(this).val(); + FWP.soft_refresh = true; + FWP.autoload(); + }); + + $f(() => { + this.init(); + }); + } + } + + FacetWP.prototype.helper = { + getUrlVar: (name) => { + var name = FWP_JSON.prefix + name; + var url_vars = window.location.search.replace('?', '').split('&'); + for (var i = 0; i < url_vars.length; i++) { + var item = url_vars[i].split('='); + if (item[0] === name) { + return item[1]; + } + } + return false; + }, + debounce: (func, wait) => { + var timeout; + return function(...args) { + var boundFunc = func.bind(this, ...args); + clearTimeout(timeout); + timeout = setTimeout(boundFunc, wait); + }; + }, + serialize: (obj, prefix) => { + var str = []; + var prefix = $.isset(prefix) ? prefix : ''; + for (var p in obj) { + if ('' != obj[p]) { // Needs to be "!=" instead of "!==" + str.push(prefix + encodeURIComponent(p) + '=' + encodeURIComponent(obj[p])); + } + } + return str.join('&'); + }, + escapeHtml: (text) => { + var map = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' + }; + return text.replace(/[&<>"']/g,(m) => map[m]).trim(); + }, + detectLoop: (node) => { + var curNode = null; + var iterator = document.createNodeIterator(node, NodeFilter.SHOW_COMMENT, () => { + return NodeFilter.FILTER_ACCEPT; /* IE expects a function */ + }, false); + + while (curNode = iterator.nextNode()) { + if (8 === curNode.nodeType && 'fwp-loop' === curNode.nodeValue) { + return curNode.parentNode; + } + } + + return false; + } + }; + + return new FacetWP(); + +})(fUtil); diff --git a/wp-content/plugins/facetwp/assets/js/src/map.js b/wp-content/plugins/facetwp/assets/js/src/map.js new file mode 100644 index 000000000..ad038377d --- /dev/null +++ b/wp-content/plugins/facetwp/assets/js/src/map.js @@ -0,0 +1,503 @@ +var FWP_MAP = FWP_MAP || {}; + +(function($) { + + FWP_MAP.markersArray = []; + FWP_MAP.markerLookup = {}; + FWP_MAP.is_filtering = false; + FWP_MAP.is_zooming = false; + FWP_MAP.map_loaded = false; + + // Get markers for a given post ID + FWP_MAP.get_post_markers = function(post_id) { + var output = []; + if ('undefined' !== typeof FWP_MAP.markerLookup[post_id]) { + var arrayOfIndexes = FWP_MAP.markerLookup[post_id]; + for (var i = 0; i < arrayOfIndexes.length; i++) { + var index = FWP_MAP.markerLookup[post_id][i]; + output.push(FWP_MAP.markersArray[index]); + } + } + return output; + } + + FWP.hooks.addAction('facetwp/refresh/map', function($this, facet_name) { + + var selected_values = []; + + // Set URL values when Enable Map Filtering is enabled + if ( FWP_MAP.is_filtering && ! FWP.is_popstate && FWP_MAP.map_loaded ) { + var currentZoom = FWP_MAP.map.getZoom(); + selected_values = FWP_MAP.map.getBounds().toUrlValue().split(','); + selected_values.push(currentZoom.toString()); // Add zoom level to be used to correct zooms on back button + } + + // Using back button when Enable Map Filtering is enabled + else if ( FWP_MAP.is_filtering && FWP.is_popstate === true ) { + + // Get bounds and zoom from URL. + // Needed because FWP.facets[facet_name] is undefined. Not sure why, seems related to async loading. + FWP.loadFromHash(); + + // Load map from URL as long as there are URL values when using back button + if ( FWP.facets[facet_name] && FWP.facets[facet_name].length > 0 ) { + do_refresh_from_url(FWP.facets[facet_name]); + } + + // On the last back button refresh, when URL is empty, reset Enable Map Filtering button and is_popstate + // Prevents further complications when starting anew with zooming/panning while Enable Map Filtering is enabled + else { + FWP.is_popstate = undefined; + $('.facetwp-map-filtering').trigger('click'); + } + } + + FWP.facets[facet_name] = selected_values; + FWP.frozen_facets[facet_name] = 'hard'; + + }); + + FWP.hooks.addAction('facetwp/reset', function() { + $.each(FWP.facet_type, function(type, name) { + if ('map' === type) { + FWP.frozen_facets[name] = 'hard'; + } + }); + }); + + FWP.hooks.addFilter('facetwp/selections/map', function(label, params) { + return FWP_JSON['map']['resetText']; + }); + + function do_refresh() { + if (FWP_MAP.is_filtering && ! FWP_MAP.is_zooming) { + FWP.autoload(); + } + + FWP_MAP.is_zooming = false; + } + + function do_refresh_from_url( selected_values ) { + + const sw = { + lat: parseFloat(selected_values[0]), + lng: parseFloat(selected_values[1]) + }; + const ne = { + lat: parseFloat(selected_values[2]), + lng: parseFloat(selected_values[3]) + }; + + const popstate_bounds = new google.maps.LatLngBounds(sw, ne); + + FWP_MAP.map.fitBounds( popstate_bounds ); + + FWP_MAP.is_zooming = true; // Prevent do_refresh() + + // fitBounds should work by itself in theory, but zooms are off for some reason (Google related I think) + // So we get the zoom from the URL also and set it after the fitBounds + // setZoom does not work directly because of fitbounds triggers async zooming, so added in a listener + google.maps.event.addListenerOnce(FWP_MAP.map, 'bounds_changed', function() { + FWP_MAP.map.setZoom( Number(selected_values[4]) ); + }); + } + + $().on('click', '.facetwp-map-filtering', function() { + var $this = $(this); + + if ($this.hasClass('enabled')) { + $this.text(FWP_JSON['map']['filterText']); + FWP_MAP.is_filtering = false; + FWP.autoload(); + } + else { + $this.text(FWP_JSON['map']['resetText']); + FWP_MAP.is_filtering = true; + FWP.autoload(); + } + + $this.toggleClass('enabled'); + }); + + FWP_MAP.createIcon = function(icon) { // back compat for icon arg for marker pin + const imgTag = document.createElement("img"); + if ( 'string' == typeof icon ) { + imgTag.src = icon; + return imgTag; + } + if ( 'string' == typeof icon.url ) { + imgTag.src = icon.url; + if ( icon.scaledSize ) { + if ( icon.scaledSize.width ) imgTag.width = icon.scaledSize.width; + if ( icon.scaledSize.height ) imgTag.height = icon.scaledSize.height; + } + return imgTag; + } + return false; + } + + FWP_MAP.init = async function() { + if ('undefined' === typeof FWP.settings.map || '' === FWP.settings.map) { + return; + } + + if ('object' !== typeof google || 'object' !== typeof google.maps) { + return; + } + + var config = FWP.settings.map.config; + + const { Map, InfoWindow } = await google.maps.importLibrary("maps"); + const { AdvancedMarkerElement, PinElement } = await google.maps.importLibrary("marker"); + + // back compat for example + // https://facetwp.com/help-center/facets/facet-types/map/advanced-map-customizations/#example-4-change-the-marker-icon-when-an-infowindow-is-open + AdvancedMarkerElement.prototype.setIcon = function(icon) { + if ( !icon ) { + let pin = new PinElement(); + this.content = pin.element; + } else { + newIcon = FWP_MAP.createIcon(icon); + this.content = newIcon; + } + } + + if (! FWP_MAP.map_loaded ) { + + FWP_MAP.map = new Map(document.getElementById('facetwp-map'), FWP.settings.map.init); + FWP_MAP.infoWindow = new InfoWindow(); + + FWP_MAP.map.addListener('dragend', function() { + do_refresh(); + }); + + FWP_MAP.map.addListener('zoom_changed', function() { + do_refresh(); + }); + + window.addEventListener('resize', FWP.helper.debounce(function() { + var center = FWP_MAP.map.getCenter(); + google.maps.event.trigger(FWP_MAP.map, 'resize'); + FWP_MAP.map.setCenter(center); + }, 500)); + + google.maps.event.addListener(FWP_MAP.map, 'click', function() { + FWP_MAP.infoWindow.close(); + }); + + FWP_MAP.oms = new OverlappingMarkerSpiderfier(FWP_MAP.map, config.spiderfy); + + // If first page load has map URL values + var selected_values = FWP.facets[FWP_JSON.map.facet_name]; + if (selected_values && selected_values.length > 0) { + + FWP_MAP.is_filtering = true; // Button was already enabled with PHP + do_refresh_from_url( selected_values ); + } + + FWP_MAP.map_loaded = true; + + } else if ( 'undefined' !== FWP.is_popstate && false === FWP.is_popstate ) { + + // restore map + let mapHTML = FWP_MAP.map.getDiv(); + let mapContainer = document.getElementById( 'facetwp-map'); + let mapParent = mapContainer.parentElement; + mapParent.replaceChild(mapHTML,mapContainer); + clearOverlays(); // Prevent cluster count issue when using back button and reset + + } else { + clearOverlays(); + } + + // this needs to re-init on each refresh + FWP_MAP.bounds = new google.maps.LatLngBounds(); + FWP_MAP.contentCache = {}; + + $.each(FWP.settings.map.locations, function(obj, idx) { + + const args = { position: obj.position, zIndex : obj.zIndex, title : obj.title }; // advanced marker properties + args.gmpDraggable = obj.draggable ?? obj.gmpDraggable; // back compat for legacy draggable + + // setting content in facetwp_map/marker/content short circuits other settings for marker + let content = FWP.hooks.applyFilters('facetwp_map/marker/content', null, obj, PinElement); + + if ( !content && obj.markerHtml ) { // new argument for creating pin from custom html + + content = document.createElement("div"); + content.innerHTML = obj.markerHtml; + + } + + if ( !content && obj.icon ) { // back compat for icon setting for legacy markers + content = FWP_MAP.createIcon(obj.icon); + + } + + if ( !content ) { // create a new pinv + + const { label = '' } = obj; + const { glyphHtml = '', pinClass = '', ...pinOptions } = obj.pinOptions || {}; + + if ( label ) { // back compat for label setting for legacy markers + pinOptions.glyph = label.text; + if ( label.color ) pinOptions.glyphColor = label.color; + } + + if ( glyphHtml ) { // new argument for HTML in glyph + const glyph = document.createElement("div"); + glyph.innerHTML = glyphHtml; + pinOptions.glyph = glyph; + } + + let pin = new PinElement(pinOptions); + + if ( pinClass ) { // new argument for class name in pin + pin.element.classList.add(pinClass); + } + + content = pin.element; + + } + + args.content = content; + + const marker = new AdvancedMarkerElement(args); + + marker.post_id = obj.post_id; + marker.infoWindowContent = obj.infoWindowContent; + marker.clickable = 'undefined' !== typeof obj.clickable ? obj.clickable : true; + + var isProgrammaticClick = false; + + google.maps.event.addListener(marker, 'spider_click', function() { + + if ( false == marker.clickable ) { + // Custom click handler + FWP.hooks.doAction('facetwp_map/marker/click', marker); + return; + } + + if ('undefined' !== typeof marker.infoWindowContent) { + FWP_MAP.infoWindow.setContent(marker.infoWindowContent); + } + else if ('undefined' === typeof FWP_MAP.contentCache[marker.post_id]) { + FWP_MAP.infoWindow.setContent('Loading...'); + FWP_MAP.infoWindow.open(FWP_MAP.map, marker); + + return $.post(FWP_JSON.map.ajaxurl, { + action: 'facetwp_map_marker_content', + facet_name: FWP_JSON.map.facet_name, + post_id: marker.post_id + }, { + dataType: 'text', + contentType: 'application/x-www-form-urlencoded', + done: (resp) => { + FWP_MAP.contentCache[marker.post_id] = resp; + FWP_MAP.infoWindow.setContent(resp); + FWP.hooks.doAction('facetwp_map/marker/click', marker); + + // Trigger extra click to reposition marker/infowindow withint viewport + // Check if the click event was triggered programmatically, to prevent recursion. + if (!isProgrammaticClick) { + isProgrammaticClick = true; + // Trigger a click programmatically after the marker has been clicked + google.maps.event.trigger(marker, 'click'); + isProgrammaticClick = false; // Reset the flag after the programmatic click + } + } + }); + } + else { + var data = FWP_MAP.contentCache[marker.post_id]; + FWP_MAP.infoWindow.setContent(data); + } + + FWP_MAP.infoWindow.open(FWP_MAP.map, marker); + + // Custom click handler + FWP.hooks.doAction('facetwp_map/marker/click', marker); + }); + + // Custom mouseover handler + marker.content.addEventListener('mouseenter', function(e) { + FWP.hooks.doAction('facetwp_map/marker/mouseover', marker); + }); + + // Custom mouseout handler + marker.content.addEventListener('mouseleave', function(e) { + FWP.hooks.doAction('facetwp_map/marker/mouseout', marker); + }); + + FWP_MAP.oms.addMarker(marker); + FWP_MAP.markersArray.push(marker); + FWP_MAP.bounds.extend(marker.position); + + // Create an object to lookup markers based on post ID + if ( 'undefined' !== typeof FWP_MAP.markerLookup[obj.post_id]) { + FWP_MAP.markerLookup[obj.post_id].push(idx); + } + else { + FWP_MAP.markerLookup[obj.post_id] = [idx]; + } + + FWP.hooks.doAction('facetwp_map/marker/added', marker, obj); + }); + + var has_results = FWP.settings.map.locations.length > 0; + var fit_bounds = FWP.hooks.applyFilters('facetwp_map/fit_bounds', ! FWP_MAP.is_filtering); + + if (fit_bounds && has_results) { + FWP_MAP.map.fitBounds(FWP_MAP.bounds); + } + else if (! FWP_MAP.is_filtering && (0 !== config.default_lat || 0 !== config.default_lng)) { + FWP_MAP.map.setCenter({ + lat: parseFloat(config.default_lat), + lng: parseFloat(config.default_lng) + }); + FWP_MAP.is_zooming = true; + FWP_MAP.map.setZoom(config.default_zoom); + } + + if ( 'undefined' != typeof markerClusterer && 'undefined' !== typeof config.cluster ) { + // default args + var clusterargs = { + algorithmOptions: { + // backwards compatibility + maxZoom: config.cluster.maxZoom, + minPoints: config.cluster.minimumClusterSize + }, + }; + + if ( false == config.cluster.zoomOnClick ) { + clusterargs.onClusterClick = false; // backwards compatibility + } + + clusterargs = Object.assign( FWP.hooks.applyFilters('facetwp_map/clusterer', clusterargs, config.cluster ), { markers: FWP_MAP.markersArray, map: FWP_MAP.map } ); + + FWP_MAP.mc = new markerClusterer.MarkerClusterer( clusterargs ); + } + + $().trigger('facetwp-maps-loaded'); + }; + + FWP.hooks.addFilter('facetwp_map/clusterer', function (clusterargs, clusterconfig) { + + if (typeof clusterconfig.imagePath !== 'undefined' && clusterconfig.imagePath !== '' && typeof clusterconfig.imageExtension !== 'undefined' && clusterconfig.imageExtension !== '') { + + // Backwards compatible custom cluster icon set with config.cluster.imagePath and config.cluster.imageExtension. + var imageRenderer = { + + render({ count, position }, stats) { + + const sizes = [53, 56, 66, 78, 90]; + let i = 0; + var dv = count; + while (dv !== 0) { + dv = parseInt(dv / 10, 10); + i++; + } + i = Math.min(i, 5); + + const size = sizes[i - 1]; + + const imgsrc = clusterconfig.imagePath + i + '.' + clusterconfig.imageExtension; + + const title = `Cluster of ${count} markers`; + + const zIndex = 1000000 + count; // There is no equivalent of google.maps.Marker.MAX_ZINDEX. This is the legacy value. + + const pinImgString = ` +
    + clustericon + ${count} +
    `; + + const parser = new DOMParser(); + const pinImg = parser.parseFromString(pinImgString, 'text/html').body.firstChild; + + const clusterOptions = { + map : clusterconfig.map, + position, + zIndex, + title, + content: pinImg, + }; + + return new google.maps.marker.AdvancedMarkerElement(clusterOptions); + } + } + + clusterargs.renderer = imageRenderer; + + } else if (typeof clusterconfig.styles !== 'undefined' && clusterconfig.styles !== '') { + + // Backwards compatible custom cluster icon set with args['config']['cluster']['styles'][]. + var stylesRenderer = { + + render({ count, position }, stats) { + let i = 0; + var dv = count; + while (dv !== 0) { + dv = parseInt(dv / 10, 10); + i++; + } + i = Math.min(i, 5); + + const styles = clusterconfig.styles[i - 1]; + const width = styles.width; + const height = styles.height; + const textColor = styles.textColor ?? '#000000'; + const textSize = styles.textSize ?? 11; + + const imgsrc = styles.url; + + const title = `Cluster of ${count} markers`; + + const zIndex = 1000000 + count; // There is no equivalent of google.maps.Marker.MAX_ZINDEX. This is the legacy value. + + const pinImgString = ` +
    + clustericon + ${count} +
    `; + + const parser = new DOMParser(); + const pinImg = parser.parseFromString(pinImgString, 'text/html').body.firstChild; + + const clusterOptions = { + map : clusterconfig.map, + position, + zIndex, + title, + content: pinImg, + }; + + return new google.maps.marker.AdvancedMarkerElement(clusterOptions); + } + } + + clusterargs.renderer = stylesRenderer; + } + + return clusterargs; + + }); + + $().on('facetwp-loaded', function() { + FWP_MAP.init(); + }); + + // Clear markers + function clearOverlays() { + FWP_MAP.oms.removeAllMarkers(); + FWP_MAP.markersArray = []; + FWP_MAP.markerLookup = {}; + + // clear clusters + if ('undefined' !== typeof FWP_MAP.mc) { + FWP_MAP.mc.clearMarkers(); + } + } + +})(fUtil); diff --git a/wp-content/plugins/facetwp/assets/js/src/oms.min.js b/wp-content/plugins/facetwp/assets/js/src/oms.min.js new file mode 100644 index 000000000..c79997879 --- /dev/null +++ b/wp-content/plugins/facetwp/assets/js/src/oms.min.js @@ -0,0 +1,15 @@ +/* +OverlappingMarkerSpiderfier +Original repo: https://github.com/jawj/OverlappingMarkerSpiderfier +Used: this fork with Advanced Markers: https://github.com/copiri-six/AME_Spiderfier +Version: 21 May 2024 - Commit a9cf4b9 + +Adapted highlighting events to work with Advanced Markers: +Original: +https://github.com/copiri-six/AME_Spiderfier/blob/a9cf4b95f9bad330f056f2390ad627fe53ce2d04/oms.js#L539-L540 +Adapted: +highlight: marker.content.addEventListener('mouseenter', highlightListenerFuncs.highlight), +unhighlight: marker.content.addEventListener('mouseleave', highlightListenerFuncs.unhighlight) +This bug may be fixed in the future: https://github.com/jawj/OverlappingMarkerSpiderfier/issues/182 +*/ +var callbackName,callbackRegEx,ref,ref1,scriptTag,tag,hasProp={}.hasOwnProperty;this.OverlappingMarkerSpiderfier=(function(){var t,r,e,i,s,n,a,o,l,h;for(i=0,a=(t=class{constructor(t,i={}){var s,o,h,u;for(s in this.map=t,null==this.constructor.hasInitialized&&(this.constructor.hasInitialized=!0,r=(e=google.maps).event,n=e.MapTypeId,a.keepSpiderfied=!1,a.ignoreMapClick=!1,a.markersWontHide=!1,a.markersWontMove=!1,a.basicFormatEvents=!1,a.nearbyDistance=20,a.circleSpiralSwitchover=9,a.circleFootSeparation=23,a.circleStartAngle=l/12,a.spiralFootSeparation=26,a.spiralLengthStart=11,a.spiralLengthFactor=4,a.spiderfiedZIndex=e.Marker.MAX_ZINDEX+2e4,a.highlightedLegZIndex=e.Marker.MAX_ZINDEX+1e4,a.usualLegZIndex=e.Marker.MAX_ZINDEX+1,a.legWeight=1.5,a.legColors={usual:{},highlighted:{}},h=a.legColors.usual,o=a.legColors.highlighted,h[n.HYBRID]=h[n.SATELLITE]="#fff",o[n.HYBRID]=o[n.SATELLITE]="#f00",h[n.TERRAIN]=h[n.ROADMAP]="#444",o[n.TERRAIN]=o[n.ROADMAP]="#f00",this.constructor.ProjHelper=function(t){return this.setMap(t)},this.constructor.ProjHelper.prototype=new e.OverlayView,this.constructor.ProjHelper.prototype.draw=function(){}),i)hasProp.call(i,s)&&(u=i[s],this[s]=u);this.projHelper=new this.constructor.ProjHelper(this.map),this.initMarkerArrays(),this.listeners={},this.formatIdleListener=this.formatTimeoutId=null,this.addListener("click",function(t,e){return r.trigger(t,"spider_click",e)}),this.addListener("format",function(t,e){return r.trigger(t,"spider_format",e)}),this.ignoreMapClick||r.addListener(this.map,"click",()=>this.unspiderfy()),r.addListener(this.map,"maptypeid_changed",()=>this.unspiderfy()),r.addListener(this.map,"zoom_changed",()=>{if(this.unspiderfy(),!this.basicFormatEvents)return this.formatMarkers()})}}).prototype,s=(o=[t,a]).length;ithis.spiderListener(t,r))],this.markersWontHide||i.push(r.addListener(t,"visible_changed",()=>this.markerChangeListener(t,!1))),this.markersWontMove||i.push(r.addListener(t,"position_changed",()=>this.markerChangeListener(t,!0))),null!=e&&i.push(r.addListener(t,"spider_click",e)),this.markerListenerRefs.push(i),this.markers.push(t),this.basicFormatEvents?this.trigger("format",t,this.constructor.markerStatus.UNSPIDERFIED):(this.trigger("format",t,this.constructor.markerStatus.UNSPIDERFIABLE),this.formatMarkers())),this},a.markerChangeListener=function(t,r){if(!this.spiderfying&&!this.unspiderfying)return null!=t._omsData&&(r||!this.isVisibleMarker(t))&&this.unspiderfy(r?t:null),this.formatMarkers()},a.getMarkers=function(){return this.markers.slice(0)},a.removeMarker=function(t){return this.forgetMarker(t),t.setMap(null)},a.forgetMarker=function(t){var e,i,s,n,a;if(null!=t._omsData&&this.unspiderfy(),(e=this.arrIndexOf(this.markers,t))<0)return this;for(i=0,s=(a=this.markerListenerRefs.splice(e,1)[0]).length;iu;a=0<=u?++o:--o)i=this.circleStartAngle+a*s,p.push(new e.Point(r.x+h*Math.cos(i),r.y+h*Math.sin(i)));return p},a.generatePtsSpiral=function(t,r){var i,s,n,a,o,h,u;for(a=this.spiralLengthStart,i=0,u=[],s=n=0,h=t;0<=h?nh;s=0<=h?++n:--n)i+=this.spiralFootSeparation/a+5e-4*s,o=new e.Point(r.x+a*Math.cos(i),r.y+a*Math.sin(i)),a+=l*this.spiralLengthFactor/i,u.push(o);return u},a.spiderListener=function(t,r){var e,i,s,n,a,o,l,h,u,p,c;if((o=null!=t._omsData)&&this.keepSpiderfied||this.unspiderfy(),o||this.map.getStreetView().getVisible()||"GoogleEarthAPI"===this.map.getMapTypeId())return this.trigger("click",t,r);for(e=0,h=[],u=[],p=(l=this.nearbyDistance)*l,a=this.llToPt(t.position),i=(c=this.markers).length;e(this.formatTimeoutId=null,null!=this.projHelper.getProjection())?this._formatMarkers():null==this.formatIdleListener?this.formatIdleListener=r.addListenerOnce(this.map,"idle",()=>this._formatMarkers()):void 0)},a._formatMarkers=function(){var t,r,e,i,s,n,a,o,l,h,u;if(this.basicFormatEvents){for(r=0,l=[],e=markers.length;rt._omsData.leg.setOptions({strokeColor:this.legColors.highlighted[this.map.mapTypeId],zIndex:this.highlightedLegZIndex}),unhighlight:()=>t._omsData.leg.setOptions({strokeColor:this.legColors.usual[this.map.mapTypeId],zIndex:this.usualLegZIndex})}},a.spiderfy=function(t,i){var s,n,a,o,l,h,u,p,c,g,f;return this.spiderfying=!0,g=t.length,s=this.ptAverage(function(){var r,e,i;for(r=0,i=[],e=t.length;r=this.circleSpiralSwitchover?this.generatePtsSpiral(g,s).reverse():this.generatePtsCircle(g,s),f=(function(){var i,s,p;for(i=0,p=[],s=o.length;ithis.ptDistanceSq(t.markerPt,a))).marker,h=new e.Polyline({map:this.map,path:[u.position,n],strokeColor:this.legColors.usual[this.map.mapTypeId],strokeWeight:this.legWeight,zIndex:this.usualLegZIndex}),u._omsData={usualPosition:u.position,usualZIndex:u.zIndex,leg:h},this.legColors.highlighted[this.map.mapTypeId]!==this.legColors.usual[this.map.mapTypeId]&&(l=this.makeHighlightListenerFuncs(u),u._omsData.hightlightListeners={highlight:u.content.addEventListener('mouseenter',l.highlight),unhighlight:u.content.addEventListener('mouseleave',l.unhighlight)}),this.trigger("format",u,this.constructor.markerStatus.SPIDERFIED),u.position=n,u.zIndex=Math.round(this.spiderfiedZIndex+a.y),p.push(u);return p}).call(this),delete this.spiderfying,this.spiderfied=!0,this.trigger("spiderfy",f,i)},a.unspiderfy=function(t=null){var e,i,s,n,a,o,l,h;if(null==this.spiderfied)return this;for(e=0,this.unspiderfying=!0,h=[],a=[],i=(o=this.markers).length;e { + + class fComplete { + + constructor(selector, options) { + let that = this; + + var defaults = { + data: [], + minChars: 3, + maxResults: 10, + searchDelay: 200, + loadingText: 'Loading...', + minCharsText: 'Enter {n} or more characters', + noResultsText: 'No results', + beforeRender: null, + onSelect: null + }; + + that.settings = Object.assign({}, defaults, options); + that.settings.minChars = Math.max(1, that.settings.minChars); + that.settings.maxResults = Math.max(1, that.settings.maxResults); + that.settings.searchDelay = Math.max(0, that.settings.searchDelay); + + if ('string' === typeof selector) { + var nodes = Array.from(document.querySelectorAll(selector)); + } + else if (selector instanceof Node) { + var nodes = [selector]; + } + else if (Array.isArray(selector)) { + var nodes = selector; + } + else { + var nodes = []; + } + + if ('undefined' === typeof window.fCompleteInit) { + window.fCompleteInit = { + lastFocus: null, + eventsBound: true + }; + that.bindEvents(); + } + + nodes.forEach((input) => { + that.input = input; + input.fcomplete = that; + input.classList.add('fcomplete-enabled'); + that.create(); + }); + } + + create() { + var that = this; + + var html = ` +
    +
    +
    +
    + `; + + var rect = that.input.getBoundingClientRect(); + + var tpl = document.createElement('template'); + tpl.innerHTML = html; + var wrap = tpl.content.querySelector('.fcomplete-wrap'); + wrap.style.minWidth = rect.width + 'px'; + that.input.parentNode.insertBefore(wrap, that.input.nextSibling); + + // add a relationship link + that.input._rel = wrap; + wrap._rel = that.input; + } + + destroy() { + this.input._rel.remove(); + delete this.input._rel; + } + + reload() { + this.destroy(); + this.create(); + } + + open() { + this.input._rel.classList.remove('fcomplete-hidden'); + } + + close() { + window.fCompleteInit.lastFocus = null; + this.input._rel.classList.add('fcomplete-hidden'); + } + + setStatus(text) { + var text = text.replace('{n}', this.settings.minChars); + var node = this.input._rel.querySelector('.fcomplete-status'); + node.innerHTML = text; + + var method = (text) ? 'remove' : 'add'; + node.classList[method]('fcomplete-hidden'); + } + + render(data) { + var data = (this.settings.beforeRender) ? this.settings.beforeRender(data) : data; + var wrap = this.input._rel; + + if (data.length) { + var html = ''; + var len = Math.min(data.length, this.settings.maxResults); + + for (var i = 0; i < len; i++) { + html += `
    ${data[i].label}
    `; + } + + wrap.querySelector('.fcomplete-results').innerHTML = html; + this.setStatus(''); + } + else { + wrap.querySelector('.fcomplete-results').innerHTML = ''; + this.setStatus(this.settings.noResultsText); + } + + this.input.fcomplete.open(); + } + + getAdjacentSibling(which) { + var that = this; + var which = which || 'next'; + var sibling = window.fCompleteInit.lastFocus; + var selector = '.fcomplete-result'; + + if (sibling) { + sibling = sibling[which + 'ElementSibling']; + + while (sibling) { + if (sibling.matches(selector)) break; + sibling = sibling[which + 'ElementSibling']; + } + + return sibling; + } + else if ('next' == which) { + sibling = that.input._rel.querySelector(selector); + } + + return sibling; + } + + debounce(func, wait) { + var timeout; + return (...args) => { + var boundFunc = func.bind(this, ...args); + clearTimeout(timeout); + timeout = setTimeout(boundFunc, wait); + } + } + + trigger(eventName, ...args) { + document.dispatchEvent(new CustomEvent(eventName, {detail: [...args]})); + } + + on(eventName, elementSelector, handler) { + document.addEventListener(eventName, function(e) { + // loop parent nodes from the target to the delegation node + for (var target = e.target; target && target != this; target = target.parentNode) { + if (target.matches(elementSelector)) { + handler.call(target, e); + break; + } + } + }, false); + } + + bindEvents() { + let that = this; + + that.on('click', '*', function(e) { + var wrap = this.closest('.fcomplete-wrap'); + var isInput = this.classList.contains('fcomplete-enabled'); + + if (isInput) { + var input = this; + var settings = input.fcomplete.settings; + var status = (settings.minChars > input.value.length) ? settings.minCharsText : ''; + input.fcomplete.setStatus(status); + this.fcomplete.open(); + } + else if (!wrap && !isInput) { + document.querySelectorAll('.fcomplete-wrap').forEach((node) => node.classList.add('fcomplete-hidden')); + } + }); + + that.on('click', '.fcomplete-result', function(e) { + var wrap = e.target.closest('.fcomplete-wrap'); + var input = wrap._rel; + input.value = e.target.getAttribute('data-value'); + + if (typeof input.fcomplete.settings.onSelect === 'function') { + input.fcomplete.settings.onSelect(); + } + + input.fcomplete.close(); + }); + that.on('keydown', '*', function(e) { + var wrap = this.closest('.fcomplete-wrap'); + var isInput = this.classList.contains('fcomplete-enabled'); + + if (!wrap && !isInput) return; + + var input = (wrap) ? wrap._rel : this; + wrap = (wrap) ? wrap : input._rel; + + if (-1 < [13, 38, 40, 27].indexOf(e.which)) { + e.preventDefault(); + } + + if (13 == e.which) { // enter + if (this.classList.contains('fcomplete-result')) { + this.click(); + } + } + else if (38 == e.which) { // up + if (this.classList.contains('fcomplete-result')) { + var sibling = wrap._rel.fcomplete.getAdjacentSibling('previous'); + window.fCompleteInit.lastFocus = sibling; // stop at the search box + (sibling) ? sibling.focus() : input.focus(); + } + } + else if (40 == e.which) { // down + if (this === input) { + var firstResult = wrap.querySelector('.fcomplete-result'); + + if (firstResult) { + firstResult.focus(); + window.fCompleteInit.lastFocus = firstResult; + } + } + else if (this.classList.contains('fcomplete-result')) { + var sibling = wrap._rel.fcomplete.getAdjacentSibling('next'); + + if (sibling) { + sibling.focus(); + window.fCompleteInit.lastFocus = sibling; // stop at the bottom + } + } + } + else if (9 == e.which || 27 == e.which) { // tab, esc + wrap._rel.fcomplete.close(); + } + }); + + that.on('keyup', '.fcomplete-enabled', that.debounce(function(e) { + if (-1 < [13, 38, 40, 27].indexOf(e.which)) { + return; + } + + var input = e.target; + var settings = input.fcomplete.settings; + + if (settings.minChars <= input.value.length) { + if (Array.isArray(settings.data)) { + input.fcomplete.render(settings.data); + } + else if (typeof settings.data === 'function') { + input.fcomplete.setStatus(settings.loadingText); + settings.data(); + } + } + else { + input.fcomplete.render([]); + input.fcomplete.setStatus(settings.minCharsText); + } + }, that.settings.searchDelay)); + } + } + + var $ = (selector, options) => new fComplete(selector, options); + + return $; + +})(); \ No newline at end of file diff --git a/wp-content/plugins/facetwp/assets/vendor/fDate/LICENSE.md b/wp-content/plugins/facetwp/assets/vendor/fDate/LICENSE.md new file mode 100644 index 000000000..09116e9fd --- /dev/null +++ b/wp-content/plugins/facetwp/assets/vendor/fDate/LICENSE.md @@ -0,0 +1,7 @@ +Copyright 2021 FacetWP, LLC + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/wp-content/plugins/facetwp/assets/vendor/fDate/fDate.css b/wp-content/plugins/facetwp/assets/vendor/fDate/fDate.css new file mode 100644 index 000000000..48575cbf0 --- /dev/null +++ b/wp-content/plugins/facetwp/assets/vendor/fDate/fDate.css @@ -0,0 +1,87 @@ +.fdate-input { + outline: none; +} + +.fdate-wrap { + width: 300px; + display: none; + background: #fff; + border-radius: 5px; + border: 1px solid #ddd; + font-size: 14px; + user-select: none; + -webkit-user-select: none; + z-index: 10000; +} + +.fdate-wrap.opened { + display: block; +} + +.fdate-wrap .disabled { + opacity: 0.1; +} + +.fdate-nav { + display: grid; + grid-template-columns: 1fr 5fr 1fr; +} + +.fdate-nav > div, +.fdate-clear { + padding: 10px 0; + text-align: center; + cursor: pointer; +} + +.fdate-grid { + display: grid; + grid-template-columns: repeat(4, 1fr); + text-align: center; +} + +.fdate-grid.grid-day { + grid-template-columns: repeat(7, 1fr); +} + +.fdate-grid > div { + padding: 20px 0; + opacity: 0.3; +} + +.fdate-grid > div:hover { + background-color: #ddd; + cursor: pointer; +} + +.fdate-grid .fdate-day { + padding: 8px 0; +} + +.fdate-grid .weekday, +.fdate-grid .inner { + opacity: 1; +} + +.fdate-grid .today { + background-color: #F8F8F8; +} + +.fdate-grid .selected { + background-color: #DDD6FE; +} + +.fdate-day.weekday { + font-weight: bold; + padding-top: 0; +} + +.fdate-grid .weekday:hover, +.fdate-grid .disabled:hover { + background-color: transparent; + cursor: default; +} + +.fdate-wrap .disabled:hover { + cursor: not-allowed; +} diff --git a/wp-content/plugins/facetwp/assets/vendor/fDate/fDate.js b/wp-content/plugins/facetwp/assets/vendor/fDate/fDate.js new file mode 100644 index 000000000..182d77276 --- /dev/null +++ b/wp-content/plugins/facetwp/assets/vendor/fDate/fDate.js @@ -0,0 +1,679 @@ +window.fDate = (() => { + + var qs = (selector) => document.querySelector(selector); + var isset = (input) => 'undefined' !== typeof input; + var ymd = (...args) => { + var d = new Date(...args); + var zeroed = (num) => (num > 9) ? num : '0' + num; + // toJSON() produces unexpected results due to timezones + return d.getFullYear() + '-' + zeroed(d.getMonth() + 1) + '-' + zeroed(d.getDate()); + }; + + class fDate { + + constructor(selector, options) { + let that = this; + + var defaults = { + i18n: { + weekdays_short: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'], + months_short: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], + months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], + clearText: 'Clear', + firstDayOfWeek: 0 + }, + minDate: '', + maxDate: '', + altFormat: '', + onChange: null + }; + + that.settings = Object.assign({}, defaults, options); + + if ('string' === typeof selector) { + var inputs = document.querySelectorAll(selector); + } + else if (selector instanceof Node) { + var inputs = [selector]; + } + else { + var inputs = selector; + } + + if (inputs.length) { + inputs.forEach(function(input) { + input.setAttribute('readonly', 'readonly'); + + if ('' !== that.settings.altFormat) { + that.el = input; + + let altInput = input.cloneNode(); + altInput.classList.add('fdate-alt-input'); + altInput.value = that.getAltDate(); + altInput._input = input; + + input._altInput = altInput; + input.setAttribute('type', 'hidden'); + input.parentNode.insertBefore(altInput, input.nextSibling); // append() + } + + input.classList.add('fdate-input'); + input._input = input; + input.fdate = { + settings: that.settings, + refresh() { + input.click(); + }, + open() { + input.click(); + }, + close() { + that.setCalVisibility('hide'); + }, + clear() { + input.value = ''; + + if (isset(input._altInput)) { + input._altInput.value = ''; + } + + that.triggerEvent('onChange'); + }, + destroy() { + input.classList.remove('fdate-input'); + delete input._altInput; + delete input._input; + delete input.fdate; + } + }; + }); + } + + if (null === qs('.fdate-wrap')) { + this.initCalendar(); + this.bindEvents(); + } + } + + initCalendar() { + var html = ` +
    +
    +
    <
    +
    +
    >
    +
    +
    +
    ${this.settings.i18n.clearText}
    +
    + `; + + document.body.insertAdjacentHTML('beforeend', html); + } + + setInput(input) { + this.el = input; + this.mode = 'day'; + this.settings = input.fdate.settings; + + this.setDateBounds(); + + // valid YYYY-MM-DD? + if (null !== input.value.match(/^\d{4}-\d{2}-\d{2}$/)) { + var date_str = input.value; + } + // use the min date or today, whichever is higher + else { + var today = ymd(); + var date_str = (this.min.str < today) ? today : this.min.str; + } + + // rewind the calendar if beyond the maxDate + if (date_str < this.max.str) { + var temp_date = new Date(date_str + 'T00:00'); + this.year = temp_date.getFullYear(); + this.month = temp_date.getMonth(); + } + else { + this.year = this.max.year; + this.month = this.max.month; + } + } + + setDateBounds() { + let min = this.settings.minDate || '1000-01-01'; + let max = this.settings.maxDate || '3000-01-01'; + let minDate = new Date(min + 'T00:00'); + let maxDate = new Date(max + 'T00:00'); + + this.min = { + year: minDate.getFullYear(), + month: minDate.getMonth(), + str: min + }; + + this.max = { + year: maxDate.getFullYear(), + month: maxDate.getMonth(), + str: max + }; + } + + isInBounds(val) { + if ('year' == this.mode) { + let year = parseInt(val); + + if (year < this.min.year || year > this.max.year) { + return false; + } + } + else if ('month' == this.mode) { + let month = parseInt(val); + let valStr = ymd(this.year, month).substr(0, 7); + let monthMin = this.min.str.substr(0, 7); + let monthMax = this.max.str.substr(0, 7); + + if (valStr < monthMin || valStr > monthMax) { + return false; + } + } + else if ('day' == this.mode) { + if (val < this.min.str || val > this.max.str) { + return false; + } + } + + return true; + } + + isNavAllowed(type) { + if ('year' == this.mode) { + let decade = parseInt(this.year.toString().substr(0, 3) + '0'); + + return ('next' == type) ? + decade < parseInt(this.max.str.substr(0, 4)) : + decade > parseInt(this.min.str.substr(0, 4)); + } + else if ('month' == this.mode) { + return ('next' == type) ? + ymd(this.year + 1, 0, 0) < this.max.str : + ymd(this.year, 0) > this.min.str; + } + else if ('day' == this.mode) { + return ('next' == type) ? + ymd(this.year, this.month + 1, 0) < this.max.str : + ymd(this.year, this.month) > this.min.str; + } + } + + setDisplay(which) { + var that = this; + + this.mode = which; + + qs('.fdate-grid').classList.remove('grid-day'); + + // show or hide the nav arrows + qs('.fdate-nav-prev').classList.add('disabled'); + qs('.fdate-nav-next').classList.add('disabled'); + + if (that.isNavAllowed('prev')) { + qs('.fdate-nav-prev').classList.remove('disabled'); + } + + if ( that.isNavAllowed('next')) { + qs('.fdate-nav-next').classList.remove('disabled'); + } + + // month + if ('month' == which) { + var output = ''; + this.settings.i18n.months_short.forEach(function(item, index) { + var css = that.isInBounds(index) ? ' inner' : ' disabled'; + output += '
    ' + item + '
    '; + }); + + qs('.fdate-grid').innerHTML = output; + qs('.fdate-nav-label').innerHTML = this.year; + } + // year + else if ('year' == which) { + var output = ''; + var decade = parseInt(this.year.toString().substr(0, 3) + '0'); + for (var i = 0; i < 10; i++) { + var css = that.isInBounds(decade + i) ? ' inner' : ' disabled'; + output += '
    ' + (decade + i) + '
    '; + } + + qs('.fdate-grid').innerHTML = output; + + var prefix = this.year.toString().substr(0, 3); + var decade = prefix + '0 - ' + prefix + '9'; + qs('.fdate-nav-label').innerHTML = decade; + } + // day + else { + qs('.fdate-grid').classList.add('grid-day'); + + var output = ''; + var days = this.generateDays(this.year, this.month); + days.forEach(function(item) { + output += '
    ' + item.text + '
    '; + }); + + qs('.fdate-grid').innerHTML = output; + qs('.fdate-nav-label').innerHTML = this.settings.i18n.months[this.month] + ' ' + this.year; + } + } + + generateDays(year, month) { + let that = this; + var output = []; + let i18n = that.settings.i18n; + let weekdays = i18n.weekdays_short; + let firstDayOfWeek = i18n.firstDayOfWeek; // 0 = Sunday + let firstDayNum = new Date(year, month).getDay(); // between 0 and 6 + let offset = firstDayNum - firstDayOfWeek; + offset = (offset < 0) ? 7 + offset : offset; // negative offset (e.g. August 2021) + let num_days = new Date(year, month + 1, 0).getDate(); + let today = ymd(); + + // shift weekdays according to firstDayOfWeek + if (0 < firstDayOfWeek) { + let temp = JSON.parse(JSON.stringify(weekdays)); + let append = temp.splice(0, firstDayOfWeek); + weekdays = temp.concat(append); + } + + // get weekdays + weekdays.forEach(function(item) { + output.push({ + text: item, + value: '', + class: ' weekday' + }); + }); + + // get days from the previous month + if (0 < offset) { + let year_prev = (0 == month) ? year - 1 : year; + let month_prev = (0 == month) ? 11 : month - 1; + let num_days_prev = new Date(year_prev, month_prev + 1, 0).getDate(); + + for (var i = (num_days_prev - offset + 1); i <= num_days_prev; i++) { + var val = ymd(year_prev, month_prev, i); + var css = that.isInBounds(val) ? '' : ' disabled'; + output.push({ + text: i, + value: val, + class: css + }); + } + } + + // get days from the current month + for (var i = 1; i <= num_days; i++) { + var val = ymd(year, month, i); + + if ( that.isInBounds(val)) { + var css = ' inner'; + css += (val == today) ? ' today' : ''; + css += (val == this.el.value) ? ' selected' : ''; + } + else { + var css = ' disabled'; + } + + output.push({ + text: i, + value: val, + class: css + }); + } + + // get days from the next month + let year_next = (11 == month) ? year + 1 : year; + let month_next = (11 == month) ? 0 : month + 1; + let num_filler = 42 - num_days - offset; + + for (var i = 1; i <= num_filler; i++) { + var val = ymd(year_next, month_next, i); + var css = that.isInBounds(val) ? '' : ' disabled'; + output.push({ + text: i, + value: val, + class: css + }); + } + + return output; + } + + adjustDate(increment, unit) { + var temp_year = ('year' == unit) ? this.year + increment : this.year; + var temp_month = ('month' == unit) ? this.month + increment : this.month; + var temp_date = new Date(temp_year, temp_month); + + this.year = temp_date.getFullYear(); + this.month = temp_date.getMonth(); + } + + on(eventName, elementSelector, handler) { + document.addEventListener(eventName, function(e) { + // loop parent nodes from the target to the delegation node + for (var target = e.target; target && target != this; target = target.parentNode) { + if (target.matches(elementSelector)) { + handler.call(target, e); + break; + } + } + }, false); + } + + getAltDate() { + let that = this; + + if ('' === that.el.value) { + return ''; + } + + let date_array = that.el.value.split('-'); + let format_array = that.settings.altFormat.split(''); + let output = ''; + + let escaped = false; + format_array.forEach(function(token) { + if ('\\' === token) { + escaped = true; + return; + } + + output += escaped ? token : that.parseDateToken(token, date_array); + escaped = false; + }); + + return output; + } + + parseDateToken(token, date_array) { + let i18n = this.settings.i18n; + + let tokens = { + 'd': () => date_array[2], + 'j': () => parseInt(date_array[2]), + 'm': () => date_array[1], + 'n': () => parseInt(date_array[1]), + 'F': () => i18n.months[parseInt(date_array[1]) - 1], + 'M': () => i18n.months_short[parseInt(date_array[1]) - 1], + 'y': () => date_array[0].substring(2), + 'Y': () => date_array[0] + }; + + return isset(tokens[token]) ? tokens[token]() : token; + } + + setPosition(input) { + let wrap = qs('.fdate-wrap'); + let inputBounds = input.getBoundingClientRect(); + let calendarWidth = wrap.getBoundingClientRect().width; + let calendarHeight = wrap.getBoundingClientRect().height; + let distanceFromRight = document.body.clientWidth - inputBounds.left; + let distanceFromBottom = document.body.clientHeight - inputBounds.bottom; + let showOnTop = (distanceFromBottom < calendarHeight && inputBounds.top > calendarHeight); + let showOnLeft = (distanceFromRight < calendarWidth && inputBounds.left > calendarWidth); + + let top = window.pageYOffset + inputBounds.top + (!showOnTop ? input.offsetHeight + 2 : -calendarHeight - 2); + let left = window.pageXOffset + inputBounds.left; + let right = window.pageXOffset + inputBounds.right - calendarWidth; + let pixels = showOnLeft ? right : left; + + wrap.style.position = 'absolute'; + wrap.style.top = top + 'px'; + wrap.style.left = pixels + 'px'; + } + + setCalVisibility(which) { + var wrap = qs('.fdate-wrap'); + + if ('hide' === which) { + if (wrap.classList.contains('opened')) { + wrap.classList.remove('opened'); + } + } + else { + if (! wrap.classList.contains('opened')) { + wrap.classList.add('opened'); + } + } + } + + triggerEvent(name) { + if (typeof this.settings[name] === 'function') { + this.settings[name](this); + } + } + + bindEvents() { + var that = this; + + that.on('click', '.fdate-day:not(.disabled):not(.weekday)', function(e) { + that.el.value = e.target.getAttribute('data-value'); + + if (isset(that.el._altInput)) { + that.el._altInput.value = that.getAltDate(); + } + + that.triggerEvent('onChange'); + that.setCalVisibility('hide'); + e.stopImmediatePropagation(); // important + }); + + that.on('click', '.fdate-month:not(.disabled)', function(e) { + that.month = parseInt(e.target.getAttribute('data-value')); + that.setDisplay('day'); + e.stopImmediatePropagation(); // important + }); + + that.on('click', '.fdate-year:not(.disabled)', function(e) { + that.year = parseInt(e.target.getAttribute('data-value')); + that.setDisplay('month'); + e.stopImmediatePropagation(); // important + }); + + that.on('click', '.fdate-nav-prev:not(.disabled)', function() { + var incr = ('year' == that.mode) ? -10 : -1; + var unit = ('day' == that.mode) ? 'month' : 'year'; + + that.adjustDate(incr, unit); + that.setDisplay(that.mode); + }); + + that.on('click', '.fdate-nav-next:not(.disabled)', function() { + var incr = ('year' == that.mode) ? 10 : 1; + var unit = ('day' == that.mode) ? 'month' : 'year'; + + that.adjustDate(incr, unit); + that.setDisplay(that.mode); + }); + + that.on('click', '.fdate-nav-label', function() { + if ('day' == that.mode) { + that.setDisplay('month'); + } + else if ('month' == that.mode) { + that.setDisplay('year'); + } + else if ('year' == that.mode) { + that.setDisplay('day'); + } + }); + + that.on('click', '.fdate-clear', function() { + that.el.fdate.clear(); + }); + + that.on('click', '*', function(e) { + var is_input = e.target.classList.contains('fdate-input') || e.target.classList.contains('fdate-alt-input'); + var is_cal = (null !== e.target.closest('.fdate-wrap')); + var is_clear = e.target.classList.contains('fdate-clear'); + + if (is_input || (is_cal && ! is_clear)) { + that.setCalVisibility('show'); + + // set position and render calendar + if (is_input) { + let visibleInput = e.target._altInput || e.target; + that.setInput(e.target._input); + that.setDisplay('day'); + that.setPosition(visibleInput); + } + } + else { + that.setCalVisibility('hide'); + } + }); + + // a11y support + window.addEventListener('keyup', function(e) { + if ('Tab' === e.key) { + if (e.target.classList.contains('fdate-input') || e.target.classList.contains('fdate-alt-input')) { + e.target._input.click(); + } + else { + that.setCalVisibility('hide'); + } + } + }); + + window.addEventListener('keydown', function(e) { + if ('Enter' === e.key) { + if (e.target.closest('.fdate-grid')) { + qs('.fdate-nav-label').focus(); + } + if (e.target.closest('.fdate-wrap')) { + e.target.click(); + } + } + else if ('Escape' === e.key) { + if (e.target.closest('.fdate-wrap') || e.target.classList.contains('fdate-input') || e.target.classList.contains('fdate-alt-input')) { + that.el.fdate.close(); + } + } + else if ('ArrowUp' === e.key) { + if (e.target.classList.contains('fdate-input') || e.target.classList.contains('fdate-alt-input')) { // from input + qs('.fdate-clear').focus(); + e.preventDefault(); + } + else if (e.target.classList.contains('fdate-nav-label')) { + that.el.focus(); + e.preventDefault(); + } + else if (e.target.classList.contains('fdate-clear')) { + let days = document.querySelectorAll('.fdate-day.inner'); + let item = (days.length) ? days[days.length - 1] : qs('.fdate-nav-label'); + item.focus(); + + e.preventDefault(); + } + else if (e.target.closest('.fdate-grid')) { + let offset = ('day' === that.mode) ? -7 : -4; + let el = that.getSibling(e.target, offset); + + if (el) { + el.focus(); + } + else { + qs('.fdate-nav-label').focus(); + } + e.preventDefault(); + } + } + else if ('ArrowDown' === e.key) { + if (e.target.classList.contains('fdate-input') || e.target.classList.contains('fdate-alt-input')) { // from input + let selected = qs('.fdate-grid .selected'); + let today = qs('.fdate-grid .today'); + + if (selected) { + selected.focus(); + } + else if (today) { + today.focus(); + } + else { + qs('.fdate-nav-label').focus(); + } + + e.preventDefault(); + } + else if (e.target.classList.contains('fdate-nav-label')) { // from nav + qs('.fdate-grid .inner').focus(); + e.preventDefault(); + } + else if (e.target.classList.contains('fdate-clear')) { + that.el.focus(); + e.preventDefault(); + } + else if (e.target.closest('.fdate-grid')) { // from grid + let offset = ('day' === that.mode) ? 7 : 4; + let el = that.getSibling(e.target, offset); + + if (el) { + el.focus(); + } + else { + qs('.fdate-clear').focus(); + } + e.preventDefault(); + } + } + else if ('ArrowLeft' === e.key) { + if (e.target.classList.contains('fdate-nav-label')) { // into the past + qs('.fdate-nav-prev').click(); + e.preventDefault(); + } + if (e.target.closest('.fdate-grid')) { // previous grid item + let prev = e.target.previousElementSibling; + if (prev && prev.classList.contains('inner')) { + prev.focus(); + } + else { + let days = document.querySelectorAll('.fdate-day.inner'); + days[days.length - 1].focus(); // last valid day of month + } + e.preventDefault(); + } + } + else if ('ArrowRight' === e.key) { + if (e.target.classList.contains('fdate-nav-label')) { // into the future + qs('.fdate-nav-next').click(); + e.preventDefault(); + } + if (e.target.closest('.fdate-grid')) { // next grid item + let next = e.target.nextElementSibling; + if (next && next.classList.contains('inner')) { + next.focus(); + } + else { + qs('.fdate-day.inner').focus(); // first valid day of month + } + e.preventDefault(); + } + } + }); + } + + getSibling(orig, offset) { + let el = orig; + for (var i = 0; i < Math.abs(offset); i++) { + el = (0 < offset) ? el.nextElementSibling : el.previousElementSibling; + if (null === el || !el.classList.contains('inner')) { + return null; + } + } + + return el; + } + } + + return fDate; +})(); \ No newline at end of file diff --git a/wp-content/plugins/facetwp/assets/vendor/fDate/fDate.min.js b/wp-content/plugins/facetwp/assets/vendor/fDate/fDate.min.js new file mode 100644 index 000000000..cef20a4cd --- /dev/null +++ b/wp-content/plugins/facetwp/assets/vendor/fDate/fDate.min.js @@ -0,0 +1 @@ +!function(){"use strict";var t,e,a,n;window.fDate=(t=function(t){return document.querySelector(t)},e=function(t){return void 0!==t},a=function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];var a=new(Function.prototype.bind.apply(Date,[null].concat(t))),n=function(t){return t>9?t:"0"+t};return a.getFullYear()+"-"+n(a.getMonth()+1)+"-"+n(a.getDate())},(n=function(a,n){var s=this;if(s.settings=Object.assign({},{i18n:{weekdays_short:["Su","Mo","Tu","We","Th","Fr","Sa"],months_short:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],clearText:"Clear",firstDayOfWeek:0},minDate:"",maxDate:"",altFormat:"",onChange:null},n),"string"==typeof a)var i=document.querySelectorAll(a);else i=a instanceof Node?[a]:a;i.length&&i.forEach((function(t){if(t.setAttribute("readonly","readonly"),""!==s.settings.altFormat){s.el=t;var a=t.cloneNode();a.classList.add("fdate-alt-input"),a.value=s.getAltDate(),a._input=t,t._altInput=a,t.setAttribute("type","hidden"),t.parentNode.insertBefore(a,t.nextSibling)}t.classList.add("fdate-input"),t._input=t,t.fdate={settings:s.settings,refresh:function(){t.click()},open:function(){t.click()},close:function(){s.setCalVisibility("hide")},clear:function(){t.value="",e(t._altInput)&&(t._altInput.value=""),s.triggerEvent("onChange")},destroy:function(){t.classList.remove("fdate-input"),delete t._altInput,delete t._input,delete t.fdate}}})),null===t(".fdate-wrap")&&(this.initCalendar(),this.bindEvents())}).prototype.initCalendar=function(){var t='\n
    \n
    \n
    <
    \n
    \n
    >
    \n
    \n
    \n
    '+this.settings.i18n.clearText+"
    \n
    \n ";document.body.insertAdjacentHTML("beforeend",t)},n.prototype.setInput=function(t){if(this.el=t,this.mode="day",this.settings=t.fdate.settings,this.setDateBounds(),null!==t.value.match(/^\d{4}-\d{2}-\d{2}$/))var e=t.value;else{var n=a();e=this.min.strthis.max.year)return!1}else if("month"==this.mode){var n=parseInt(t),s=a(this.year,n).substr(0,7),i=this.min.str.substr(0,7),r=this.max.str.substr(0,7);if(sr)return!1}else if("day"==this.mode&&(tthis.max.str))return!1;return!0},n.prototype.isNavAllowed=function(t){if("year"==this.mode){var e=parseInt(this.year.toString().substr(0,3)+"0");return"next"==t?eparseInt(this.min.str.substr(0,4))}return"month"==this.mode?"next"==t?a(this.year+1,0,0)this.min.str:"day"==this.mode?"next"==t?a(this.year,this.month+1,0)this.min.str:void 0},n.prototype.setDisplay=function(e){var a=this;if(this.mode=e,t(".fdate-grid").classList.remove("grid-day"),t(".fdate-nav-prev").classList.add("disabled"),t(".fdate-nav-next").classList.add("disabled"),a.isNavAllowed("prev")&&t(".fdate-nav-prev").classList.remove("disabled"),a.isNavAllowed("next")&&t(".fdate-nav-next").classList.remove("disabled"),"month"==e){var n="";this.settings.i18n.months_short.forEach((function(t,e){var s=a.isInBounds(e)?" inner":" disabled";n+='
    '+t+"
    "})),t(".fdate-grid").innerHTML=n,t(".fdate-nav-label").innerHTML=this.year}else if("year"==e){n="";for(var s=parseInt(this.year.toString().substr(0,3)+"0"),i=0;i<10;i++){var r=a.isInBounds(s+i)?" inner":" disabled";n+='
    '+(s+i)+"
    "}t(".fdate-grid").innerHTML=n;var o=this.year.toString().substr(0,3);s=o+"0 - "+o+"9",t(".fdate-nav-label").innerHTML=s}else t(".fdate-grid").classList.add("grid-day"),n="",this.generateDays(this.year,this.month).forEach((function(t){n+='
    '+t.text+"
    "})),t(".fdate-grid").innerHTML=n,t(".fdate-nav-label").innerHTML=this.settings.i18n.months[this.month]+" "+this.year},n.prototype.generateDays=function(t,e){var n=this,s=[],i=n.settings.i18n,r=i.weekdays_short,o=i.firstDayOfWeek,l=new Date(t,e).getDay()-o;l=l<0?7+l:l;var d=new Date(t,e+1,0).getDate(),c=a();if(0i,l=rs,d=window.pageYOffset+n.top+(o?-i-2:e.offsetHeight+2),c=window.pageXOffset+n.left,f=window.pageXOffset+n.right-s,u=l?f:c;a.style.position="absolute",a.style.top=d+"px",a.style.left=u+"px"},n.prototype.setCalVisibility=function(e){var a=t(".fdate-wrap");"hide"===e?a.classList.contains("opened")&&a.classList.remove("opened"):a.classList.contains("opened")||a.classList.add("opened")},n.prototype.triggerEvent=function(t){"function"==typeof this.settings[t]&&this.settings[t](this)},n.prototype.bindEvents=function(){var a=this;a.on("click",".fdate-day:not(.disabled):not(.weekday)",(function(t){a.el.value=t.target.getAttribute("data-value"),e(a.el._altInput)&&(a.el._altInput.value=a.getAltDate()),a.triggerEvent("onChange"),a.setCalVisibility("hide"),t.stopImmediatePropagation()})),a.on("click",".fdate-month:not(.disabled)",(function(t){a.month=parseInt(t.target.getAttribute("data-value")),a.setDisplay("day"),t.stopImmediatePropagation()})),a.on("click",".fdate-year:not(.disabled)",(function(t){a.year=parseInt(t.target.getAttribute("data-value")),a.setDisplay("month"),t.stopImmediatePropagation()})),a.on("click",".fdate-nav-prev:not(.disabled)",(function(){var t="year"==a.mode?-10:-1,e="day"==a.mode?"month":"year";a.adjustDate(t,e),a.setDisplay(a.mode)})),a.on("click",".fdate-nav-next:not(.disabled)",(function(){var t="year"==a.mode?10:1,e="day"==a.mode?"month":"year";a.adjustDate(t,e),a.setDisplay(a.mode)})),a.on("click",".fdate-nav-label",(function(){"day"==a.mode?a.setDisplay("month"):"month"==a.mode?a.setDisplay("year"):"year"==a.mode&&a.setDisplay("day")})),a.on("click",".fdate-clear",(function(){a.el.fdate.clear()})),a.on("click","*",(function(t){var e=t.target.classList.contains("fdate-input")||t.target.classList.contains("fdate-alt-input"),n=null!==t.target.closest(".fdate-wrap"),s=t.target.classList.contains("fdate-clear");if(e||n&&!s){if(a.setCalVisibility("show"),e){var i=t.target._altInput||t.target;a.setInput(t.target._input),a.setDisplay("day"),a.setPosition(i)}}else a.setCalVisibility("hide")})),window.addEventListener("keyup",(function(t){"Tab"===t.key&&(t.target.classList.contains("fdate-input")||t.target.classList.contains("fdate-alt-input")?t.target._input.click():a.setCalVisibility("hide"))})),window.addEventListener("keydown",(function(e){if("Enter"===e.key)e.target.closest(".fdate-grid")&&t(".fdate-nav-label").focus(),e.target.closest(".fdate-wrap")&&e.target.click();else if("Escape"===e.key)(e.target.closest(".fdate-wrap")||e.target.classList.contains("fdate-input")||e.target.classList.contains("fdate-alt-input"))&&a.el.fdate.close();else if("ArrowUp"===e.key){if(e.target.classList.contains("fdate-input")||e.target.classList.contains("fdate-alt-input"))t(".fdate-clear").focus(),e.preventDefault();else if(e.target.classList.contains("fdate-nav-label"))a.el.focus(),e.preventDefault();else if(e.target.classList.contains("fdate-clear")){var n=document.querySelectorAll(".fdate-day.inner");(n.length?n[n.length-1]:t(".fdate-nav-label")).focus(),e.preventDefault()}else if(e.target.closest(".fdate-grid")){var s="day"===a.mode?-7:-4,i=a.getSibling(e.target,s);i?i.focus():t(".fdate-nav-label").focus(),e.preventDefault()}}else if("ArrowDown"===e.key){if(e.target.classList.contains("fdate-input")||e.target.classList.contains("fdate-alt-input")){var r=t(".fdate-grid .selected"),o=t(".fdate-grid .today");r?r.focus():o?o.focus():t(".fdate-nav-label").focus(),e.preventDefault()}else if(e.target.classList.contains("fdate-nav-label"))t(".fdate-grid .inner").focus(),e.preventDefault();else if(e.target.classList.contains("fdate-clear"))a.el.focus(),e.preventDefault();else if(e.target.closest(".fdate-grid")){var l="day"===a.mode?7:4,d=a.getSibling(e.target,l);d?d.focus():t(".fdate-clear").focus(),e.preventDefault()}}else if("ArrowLeft"===e.key){if(e.target.classList.contains("fdate-nav-label")&&(t(".fdate-nav-prev").click(),e.preventDefault()),e.target.closest(".fdate-grid")){var c=e.target.previousElementSibling;if(c&&c.classList.contains("inner"))c.focus();else{var f=document.querySelectorAll(".fdate-day.inner");f[f.length-1].focus()}e.preventDefault()}}else if("ArrowRight"===e.key&&(e.target.classList.contains("fdate-nav-label")&&(t(".fdate-nav-next").click(),e.preventDefault()),e.target.closest(".fdate-grid"))){var u=e.target.nextElementSibling;u&&u.classList.contains("inner")?u.focus():t(".fdate-day.inner").focus(),e.preventDefault()}}))},n.prototype.getSibling=function(t,e){for(var a=t,n=0;n { + + var build = {}; + + class fSelect { + + constructor(selector, options) { + let that = this; + + var defaults = { + placeholder: 'Select some options', + numDisplayed: 3, + overflowText: '{n} selected', + searchText: 'Search', + noResultsText: 'No results found', + showSearch: true, + optionFormatter: false + }; + + that.settings = Object.assign({}, defaults, options); + build = {output: '', optgroup: 0, idx: 0}; + + if ('string' === typeof selector) { + var nodes = Array.from(document.querySelectorAll(selector)); + } + else if (selector instanceof Node) { + var nodes = [selector]; + } + else if (Array.isArray(selector)) { + var nodes = selector; + } + else { + var nodes = []; + } + + if ('undefined' === typeof window.fSelectInit) { + window.fSelectInit = { + searchCache: '', + lastChoice: null, + lastFocus: null, + activeEl: null + }; + that.bindEvents(); + } + + nodes.forEach((input) => { + if (typeof input.fselect === 'object') { + input.fselect.destroy(); + } + + that.settings.multiple = input.matches('[multiple]'); + input.fselect = that; + that.input = input; + that.create(); + }); + } + + create() { + var that = this; + var options = that.buildOptions(); + var label = that.getDropdownLabel(); + var mode = (that.settings.multiple) ? 'multiple' : 'single'; + var searchClass = (that.settings.showSearch) ? '' : ' fs-hidden'; + var noResultsClass = (build.idx < 2) ? '' : ' fs-hidden'; + + var html = ` +
    +
    +
    ${label}
    + +
    +
    +
    + +
    +
    ${that.settings.noResultsText}
    +
    ${options}
    +
    +
    + `; + + var tpl = document.createElement('template'); + tpl.innerHTML = html; + var wrap = tpl.content.querySelector('.fs-wrap'); + that.input.parentNode.insertBefore(wrap, that.input.nextSibling); + that.input.classList.add('fs-hidden'); + + // add a relationship link + that.input._rel = wrap; + wrap._rel = that.input; + } + + destroy() { + this.input._rel.remove(); + this.input.classList.remove('fs-hidden'); + delete this.input._rel; + } + + reload() { + this.destroy(); + this.create(); + } + + open() { + var wrap = this.input._rel; + wrap.classList.add('fs-open'); + wrap.querySelector('.fs-dropdown').classList.remove('fs-hidden'); + + // don't auto-focus for touch devices + if (! window.matchMedia("(pointer: coarse)").matches) { + wrap.querySelector('.fs-search input').focus(); + } + + window.fSelectInit.lastChoice = this.getSelectedOptions('value'); + window.fSelectInit.activeEl = wrap; + + this.trigger('fs:opened', wrap); + } + + close() { + this.input._rel.classList.remove('fs-open'); + this.input._rel.querySelector('.fs-dropdown').classList.add('fs-hidden'); + + window.fSelectInit.searchCache = ''; + window.fSelectInit.lastChoice = null; + window.fSelectInit.lastFocus = null; + window.fSelectInit.activeEl = null; + + this.trigger('fs:closed', this.input._rel); + } + + buildOptions(parent) { + var that = this; + var parent = parent || that.input; + + Array.from(parent.children).forEach((node) => { + if ('optgroup' === node.nodeName.toLowerCase()) { + var opt = `
    ${node.label}
    `; + build.output += opt; + that.buildOptions(node); + build.optgroup++; + } + else { + var val = node.value; + + // skip the first choice in multi-select mode + if (0 === build.idx && '' === val && that.settings.multiple) { + build.idx++; + return; + } + + var classes = ['fs-option', 'g' + build.optgroup]; + + // append existing classes + node.className.split(' ').forEach((className) => { + if ('' !== className) { + classes.push(className); + } + }); + + if (node.matches('[disabled]')) classes.push('disabled'); + if (node.matches('[selected]')) classes.push('selected'); + classes = classes.join(' '); + + if ('function' === typeof that.settings.optionFormatter) { + node.label = that.settings.optionFormatter(node.label, node); + } + + var opt = `
    ${node.label}
    `; + + build.output += opt; + build.idx++; + } + }); + + return build.output; + } + + getSelectedOptions(field, context) { + var context = context || this.input; + return Array.from(context.selectedOptions).map((opt) => { + return (field) ? opt[field] : opt; + }); + } + + getAdjacentSibling(which) { + var that = this; + var which = which || 'next'; + var sibling = window.fSelectInit.lastFocus; + var selector = '.fs-option:not(.fs-hidden):not(.disabled)'; + + if (sibling) { + sibling = sibling[which + 'ElementSibling']; + + while (sibling) { + if (sibling.matches(selector)) break; + sibling = sibling[which + 'ElementSibling']; + } + + return sibling; + } + else if ('next' == which) { + sibling = that.input._rel.querySelector(selector); + } + + return sibling; + } + + getDropdownLabel() { + var that = this; + var labelText = that.getSelectedOptions('text'); + + if (labelText.length < 1) { + labelText = that.settings.placeholder; + } + else if (labelText.length > that.settings.numDisplayed) { + labelText = that.settings.overflowText.replace('{n}', labelText.length); + } + else { + labelText = labelText.join(', '); + } + + return labelText; + } + + debounce(func, wait) { + var timeout; + return (...args) => { + var boundFunc = func.bind(this, ...args); + clearTimeout(timeout); + timeout = setTimeout(boundFunc, wait); + } + } + + trigger(eventName, ...args) { + document.dispatchEvent(new CustomEvent(eventName, {detail: [...args]})); + } + + on(eventName, elementSelector, handler) { + document.addEventListener(eventName, function(e) { + // loop parent nodes from the target to the delegation node + for (var target = e.target; target && target != this; target = target.parentNode) { + if (target.matches(elementSelector)) { + handler.call(target, e); + break; + } + } + }, false); + } + + bindEvents() { + var that = this; + var optionSelector = '.fs-option:not(.fs-hidden):not(.disabled)'; + var unaccented = (str) => str.normalize('NFD').replace(/[\u0300-\u036f]/g, ''); + + // debounce search for better performance + that.on('keyup', '.fs-search input', that.debounce(function(e) { + var wrap = e.target.closest('.fs-wrap'); + var options = wrap._rel.options; + + var matchOperators = /[|\\{}()[\]^$+*?.]/g; + var keywords = e.target.value.replace(matchOperators, '\\$&'); + keywords = unaccented(keywords); + + // if the searchCache already has a prefixed version of this search + // then don't un-hide the existing exclusions + if (0 !== keywords.indexOf(window.fSelectInit.searchCache)) { + wrap.querySelectorAll('.fs-option, .fs-optgroup-label').forEach((node) => node.classList.remove('fs-hidden')); + } + + window.fSelectInit.searchCache = keywords; + + for (var i = 0; i < options.length; i++) { + if ('' === options[i].value) continue; + + var needle = new RegExp(keywords, 'gi'); + var haystack = unaccented(options[i].text); + + if (null === haystack.match(needle)) { + wrap.querySelector('.fs-option[data-idx="' + i + '"]').classList.add('fs-hidden'); + } + } + + // hide optgroups if no choices + wrap.querySelectorAll('.fs-optgroup-label').forEach((node) => { + var group = node.getAttribute('data-group'); + var container = node.closest('.fs-options'); + var count = container.querySelectorAll('.fs-option.g' + group + ':not(.fs-hidden)').length; + + if (count < 1) { + node.classList.add('fs-hidden'); + } + }); + + // toggle the noResultsText div + if (wrap.querySelectorAll('.fs-option:not(.fs-hidden').length) { + wrap.querySelector('.fs-no-results').classList.add('fs-hidden'); + } + else { + wrap.querySelector('.fs-no-results').classList.remove('fs-hidden'); + } + + }, 100)); + + that.on('click', optionSelector, function(e) { + var wrap = this.closest('.fs-wrap'); + var value = this.getAttribute('data-value'); + var input = wrap._rel; + var isMultiple = wrap.classList.contains('multiple'); + + if (!isMultiple) { + input.value = value; + wrap.querySelectorAll('.fs-option.selected').forEach((node) => node.classList.remove('selected')); + } + else { + var idx = parseInt(this.getAttribute('data-idx')); + input.options[idx].selected = !this.classList.contains('selected'); + } + + this.classList.toggle('selected'); + + var label = input.fselect.getDropdownLabel(); + wrap.querySelector('.fs-label').innerHTML = label; + + // fire a change event + input.dispatchEvent(new Event('change', { bubbles: true })); + input.fselect.trigger('fs:changed', wrap); + + if (!isMultiple) { + input.fselect.close(); + } + + e.stopImmediatePropagation(); + }); + + that.on('keydown', '*', function(e) { + var wrap = this.closest('.fs-wrap'); + + if (!wrap) return; + + if (-1 < [38, 40, 27].indexOf(e.which)) { + e.preventDefault(); + } + + if (32 == e.which || 13 == e.which) { // space, enter + if (e.target.closest('.fs-search')) { + // preserve spaces for search + } + else if (e.target.matches(optionSelector)) { + e.target.click(); + e.preventDefault(); + } + else { + wrap.querySelector('.fs-label').click(); + e.preventDefault(); + } + } + else if (38 == e.which) { // up + var sibling = wrap._rel.fselect.getAdjacentSibling('previous'); + window.fSelectInit.lastFocus = sibling; // stop at the search box + + if (sibling) { + sibling.focus(); + } + else { + wrap.querySelector('.fs-search input').focus(); + } + } + else if (40 == e.which) { // down + var sibling = wrap._rel.fselect.getAdjacentSibling('next'); + + if (sibling) { + sibling.focus(); + window.fSelectInit.lastFocus = sibling; // stop at the bottom + } + } + else if (9 == e.which || 27 == e.which) { // tab, esc + wrap._rel.fselect.close(); + } + }); + + that.on('click', '*', function(e) { + var wrap = this.closest('.fs-wrap'); + var lastActive = window.fSelectInit.activeEl; + + if (wrap) { + var labelWrap = this.closest('.fs-label-wrap'); + + if (labelWrap) { + if (lastActive) { + lastActive._rel.fselect.close(); + } + if (wrap !== lastActive) { + wrap._rel.fselect.open(); + } + } + } + else { + if (lastActive) { + lastActive._rel.fselect.close(); + } + } + }); + } + } + + var $ = (selector, options) => new fSelect(selector, options); + + return $; + +})(); + +if ('undefined' !== typeof fUtil) { + fUtil.fn.fSelect = function(opts) { + this.each(function() { // no arrow function to preserve "this" + fSelect(this, opts); + }); + return this; + }; +} diff --git a/wp-content/plugins/facetwp/assets/vendor/fTip/fTip.css b/wp-content/plugins/facetwp/assets/vendor/fTip/fTip.css new file mode 100644 index 000000000..0c127ca10 --- /dev/null +++ b/wp-content/plugins/facetwp/assets/vendor/fTip/fTip.css @@ -0,0 +1,19 @@ +.ftip-wrap { + position: absolute; + display: none; + max-width: 400px; + padding: 6px 8px; + background-color: #222; + border-radius: 6px; + opacity: 0; + color: #fff; + cursor: default; + transition: height 0s, opacity 1s; + z-index: 100; +} + +.ftip-wrap.active { + height: auto; + display: inline-block; + opacity: 0.9; +} \ No newline at end of file diff --git a/wp-content/plugins/facetwp/assets/vendor/fTip/fTip.js b/wp-content/plugins/facetwp/assets/vendor/fTip/fTip.js new file mode 100644 index 000000000..b9011e7a5 --- /dev/null +++ b/wp-content/plugins/facetwp/assets/vendor/fTip/fTip.js @@ -0,0 +1,100 @@ +window.fTip = (() => { + + var qs = (selector) => document.querySelector(selector); + + class fTip { + + constructor(selector, options) { + let that = this; + + var defaults = { + content: (node) => node.getAttribute('title') + }; + + that.settings = Object.assign({}, defaults, options); + + var inputs = []; + + if ('string' === typeof selector) { + var inputs = Array.from(document.querySelectorAll(selector)); + } + else if (selector instanceof Node) { + var inputs = [selector]; + } + else if (Array.isArray(selector)) { + var inputs = selector; + } + + if (null === qs('.ftip-wrap')) { + that.buildHtml(); + that.bindEvents(); + } + + inputs.forEach(function(input) { + that.input = input; + input.ftip = that; + input.classList.add('ftip-enabled'); + }); + } + + open() { + let input = this.input; + let wrap = qs('.ftip-wrap'); + + wrap.innerHTML = this.settings.content(input); + wrap.classList.add('active'); + + let wrapBounds = wrap.getBoundingClientRect(); + let inputBounds = input.getBoundingClientRect(); + let top = window.pageYOffset + inputBounds.top; + let left = window.pageXOffset + inputBounds.right; + let centered = ((inputBounds.height - wrapBounds.height) / 2); + + wrap.style.top = (top + centered) + 'px'; + wrap.style.left = (left + 10) + 'px'; + } + + buildHtml() { + let html = '
    '; + document.body.insertAdjacentHTML('beforeend', html); + } + + bindEvents() { + var that = this; + let wrap = qs('.ftip-wrap'); + + var delayHandler = () => { + that.delay = setTimeout(() => { + wrap.classList.remove('active'); + }, 250); + }; + + that.on('mouseover', '.ftip-enabled', function(e) { + clearTimeout(that.delay); + this.ftip.open(); + }); + + that.on('mouseout', '.ftip-enabled', delayHandler); + that.on('mouseout', '.ftip-wrap', delayHandler); + that.on('mouseover', '.ftip-wrap', () => { + clearTimeout(that.delay); + }); + } + + on(eventName, elementSelector, handler) { + document.addEventListener(eventName, function(e) { + // loop parent nodes from the target to the delegation node + for (var target = e.target; target && target != this; target = target.parentNode) { + if (target.matches(elementSelector)) { + handler.call(target, e); + break; + } + } + }, false); + } + } + + var $ = (selector, options) => new fTip(selector, options); + + return $; +})(); \ No newline at end of file diff --git a/wp-content/plugins/facetwp/assets/vendor/fUtil/LICENSE.md b/wp-content/plugins/facetwp/assets/vendor/fUtil/LICENSE.md new file mode 100644 index 000000000..09116e9fd --- /dev/null +++ b/wp-content/plugins/facetwp/assets/vendor/fUtil/LICENSE.md @@ -0,0 +1,7 @@ +Copyright 2021 FacetWP, LLC + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/wp-content/plugins/facetwp/assets/vendor/fUtil/fUtil.js b/wp-content/plugins/facetwp/assets/vendor/fUtil/fUtil.js new file mode 100644 index 000000000..8ee8cb7c3 --- /dev/null +++ b/wp-content/plugins/facetwp/assets/vendor/fUtil/fUtil.js @@ -0,0 +1,457 @@ +window.fUtil = (() => { + + class fUtil { + + constructor(selector) { + if (typeof selector === 'string' || selector instanceof String) { // string + var selector = selector.replace(':selected', ':checked'); + + if ('' === selector) { + this.nodes = []; + } + else if (this.isValidSelector(selector)) { + this.nodes = Array.from(document.querySelectorAll(selector)); + } + else { + var tpl = document.createElement('template'); + tpl.innerHTML = selector; + this.nodes = [tpl.content]; + } + } + else if (Array.isArray(selector)) { // array of nodes + this.nodes = selector; + } + else if (typeof selector === 'object' && selector.nodeName) { // node + this.nodes = [selector]; + } + else if (typeof selector === 'function') { // function + this.ready(selector); + } + else if (selector === window) { // window + this.nodes = [window]; + } + else { // document + this.nodes = [document]; + } + + // custom plugins + $.each($.fn, (handler, method) => { + this[method] = handler; + }); + } + + static isset(input) { + return typeof input !== 'undefined'; + } + + static post(url, data, settings) { + var settings = Object.assign({}, { + dataType: 'json', + contentType: 'application/json', + headers: {}, + done: () => {}, + fail: () => {} + }, settings); + + settings.headers['Content-Type'] = settings.contentType; + + data = ('application/json' === settings.contentType) ? JSON.stringify(data) : $.toEncoded(data); + + fetch(url, { + method: 'POST', + headers: settings.headers, + body: data + }) + .then(resp => { + if (resp.ok) { + return resp[settings.dataType](); + } + + return settings.fail(resp.status + ' - ' + resp.statusText); + }) + .then(result => settings.done(result)) + .catch(err => settings.fail(err)); + } + + static toEncoded(obj, prefix, out) { + var out = out || []; + var prefix = prefix || ''; + + if (Array.isArray(obj)) { + if (obj.length) { + obj.forEach((val) => { + $.toEncoded(val, prefix + '[]', out); + }); + } + else { + $.toEncoded('', prefix, out); + } + } + else if (typeof obj === 'object' && obj !== null) { + Object.keys(obj).forEach((key) => { + var new_prefix = prefix ? prefix + '[' + key + ']' : key; + $.toEncoded(obj[key], new_prefix, out); + }); + } + else { + out.push(encodeURIComponent(prefix) + '=' + encodeURIComponent(obj)); + } + + return out.join('&'); + } + + static forEach(items, callback) { + if (typeof items === 'object' && items !== null) { + if (Array.isArray(items)) { + items.forEach((val, key) => callback.bind(val)(val, key)); + } + else { + Object.keys(items).forEach(key => { + var val = items[key]; + callback.bind(val)(val, key); + }); + } + } + + return items; + } + + isValidSelector(string) { + try { + document.createDocumentFragment().querySelector(string); + } + catch(err) { + return false; + } + return true; + } + + clone() { + return $(this.nodes); + } + + len() { + return this.nodes.length; + } + + each(callback) { + this.nodes.forEach((node, key) => { + let func = callback.bind(node); // set "this" + func(node, key); + }); + + return this; + } + + ready(callback) { + if (typeof callback !== 'function') return; + + if (document.readyState === 'complete') { + return callback(); + } + + document.addEventListener('DOMContentLoaded', callback, false); + } + + addClass(className) { + this.each(node => node.classList.add(className)); + return this; + } + + removeClass(className) { + this.each(node => node.classList.remove(className)); + return this; + } + + hasClass(className) { + return $.isset(this.nodes.find(node => node.classList.contains(className))); + } + + toggleClass(className) { + this.each(node => node.classList.toggle(className)); + return this; + } + + is(selector) { + for (let i = 0; i < this.len(); i++) { // forEach prevents loop exiting + if (this.nodes[i].matches(selector)) { + return true; + } + } + return false; + } + + find(selector) { + var selector = selector.replace(':selected', ':checked'); + let nodes = []; + let clone = this.clone(); + + clone.each(node => { + nodes = nodes.concat(Array.from(node.querySelectorAll(selector))); + }); + + clone.nodes = nodes; + return clone; + } + + first() { + let clone = this.clone(); + if (clone.len()) { + clone.nodes = this.nodes.slice(0, 1); + } + return clone; + } + + last() { + let clone = this.clone(); + if (clone.len()) { + clone.nodes = this.nodes.slice(-1); + } + return clone; + } + + prev(selector) { + let nodes = []; + let clone = this.clone(); + + clone.each(node => { + let sibling = node.previousElementSibling; + + while (sibling) { + if (!$.isset(selector) || sibling.matches(selector)) break; + sibling = sibling.previousElementSibling; + } + + if (sibling) { + nodes.push(sibling); + } + }); + + clone.nodes = nodes; + return clone; + } + + next(selector) { + let nodes = []; + let clone = this.clone(); + + clone.each(node => { + let sibling = node.nextElementSibling; + + while (sibling) { + if (!$.isset(selector) || sibling.matches(selector)) break; + sibling = sibling.nextElementSibling; + } + + if (sibling) { + nodes.push(sibling); + } + }); + + clone.nodes = nodes; + return clone; + } + + prepend(html) { + this.each(node => node.insertAdjacentHTML('afterbegin', html)); + return this; + } + + append(html) { + this.each(node => node.insertAdjacentHTML('beforeend', html)); + return this; + } + + parents(selector) { + let parents = []; + let clone = this.clone(); + + clone.each(node => { + let parent = node.parentNode; + while (parent && parent !== document) { + if (parent.matches(selector)) parents.push(parent); + parent = parent.parentNode; + } + }); + + clone.nodes = [...new Set(parents)]; // remove dupes + return clone; + } + + closest(selector) { + let nodes = []; + let clone = this.clone(); + + clone.each(node => { + let closest = node.closest(selector); + + if (closest) { + nodes.push(closest); + } + }); + + clone.nodes = nodes; + return clone; + } + + remove() { + this.each(node => node.remove()); + return this; + } + + on(eventName, selector, callback) { + if (!$.isset(selector)) return; + if (!$.isset(callback)) { + var callback = selector; + var selector = null; + } + + // Reusable callback + var checkForMatch = (e) => { + if (null === selector || e.target.matches(selector)) { + callback.bind(e.target)(e); + } + else if (e.target.closest(selector)) { + var $this = e.target.closest(selector); + callback.bind($this)(e); + } + }; + + this.each(node => { + + // Attach a unique ID to each node + if (!$.isset(node._id)) { + node._id = $.event.count; + $.event.store[$.event.count] = node; + $.event.count++; + } + + var id = node._id; + + // Store the raw callback, needed for .off() + checkForMatch._str = callback.toString(); + + if (!$.isset($.event.map[id])) { + $.event.map[id] = {}; + } + if (!$.isset($.event.map[id][eventName])) { + $.event.map[id][eventName] = {}; + } + if (!$.isset($.event.map[id][eventName][selector])) { + $.event.map[id][eventName][selector] = []; + } + + // Use $.event.map to store event references + // removeEventListener needs named callbacks, so we're creating + // one for every handler + let length = $.event.map[id][eventName][selector].push(checkForMatch); + + node.addEventListener(eventName, $.event.map[id][eventName][selector][length - 1]); + }); + + return this; + } + + off(eventName, selector, callback) { + if (!$.isset(callback)) { + var callback = selector; + var selector = null; + } + + this.each(node => { + var id = node._id; + + $.each($.event.map[id], (selectors, theEventName) => { + $.each(selectors, (callbacks, theSelector) => { + $.each(callbacks, (theCallback, index) => { + if ( + (!eventName || theEventName === eventName) && + (!selector || theSelector === selector) && + (!callback || theCallback._str === callback.toString()) + ) { + node.removeEventListener(theEventName, $.event.map[id][theEventName][theSelector][index]); + delete $.event.map[id][theEventName][theSelector][index]; + } + }); + }); + }); + }); + + return this; + } + + trigger(eventName, extraData) { + this.each(node => node.dispatchEvent(new CustomEvent(eventName, { + detail: extraData, + bubbles: true + }))); + return this; + } + + attr(attributeName, value) { + if (!$.isset(value)) { + return (this.len()) ? this.nodes[0].getAttribute(attributeName) : null; + } + + this.each(node => node.setAttribute(attributeName, value)); + return this; + + } + + data(key, value) { + if (!$.isset(value)) { + return (this.len()) ? this.nodes[0]._fdata[key] : null; + } + + this.each(node => node._fdata[key] = value); + return this; + } + + html(htmlString) { + if (!$.isset(htmlString)) { + return (this.len()) ? this.nodes[0].innerHTML : null; + } + + this.each(node => node.innerHTML = htmlString); + return this; + } + + text(textString) { + if (!$.isset(textString)) { + return (this.len()) ? this.nodes[0].textContent : null; + } + else { + this.each(node => node.textContent = textString); + return this; + } + } + + val(value) { + if (!$.isset(value)) { + if (this.len()) { + var field = this.nodes[0]; + if (field.nodeName.toLowerCase() === 'select' && field.multiple) { + return [...field.options].filter((x) => x.selected).map((x) => x.value); + } + return field.value; + } + return null; + } + else { + this.each(node => node.value = value); + return this; + } + } + } + + var $ = selector => new fUtil(selector); + + // Set object methods + $.fn = {}; + $.post = fUtil.post; + $.isset = fUtil.isset; + $.each = fUtil.forEach; + $.toEncoded = fUtil.toEncoded; + $.event = {map: {}, store: [], count: 0}; + return $; +})(); diff --git a/wp-content/plugins/facetwp/assets/vendor/noUiSlider/nouislider.css b/wp-content/plugins/facetwp/assets/vendor/noUiSlider/nouislider.css new file mode 100644 index 000000000..9c2264127 --- /dev/null +++ b/wp-content/plugins/facetwp/assets/vendor/noUiSlider/nouislider.css @@ -0,0 +1,154 @@ +.noUi-target, +.noUi-target * { + touch-action: none; + user-select: none; + box-sizing: border-box; +} +.noUi-target { + position: relative; +} +.noUi-base, +.noUi-connects { + width: 100%; + height: 100%; + position: relative; + z-index: 1; +} +.noUi-connects { + overflow: hidden; + z-index: 0; +} +.noUi-connect, +.noUi-origin { + will-change: transform; + position: absolute; + z-index: 1; + top: 0; + right: 0; + height: 100%; + width: 100%; + transform-origin: 0 0; + transform-style: flat; +} +.noUi-txt-dir-rtl.noUi-horizontal .noUi-origin { + left: 0; + right: auto; +} +.noUi-horizontal .noUi-origin { + height: 0; +} +.noUi-handle { + backface-visibility: hidden; + position: absolute; +} +.noUi-touch-area { + height: 100%; + width: 100%; +} +.noUi-state-tap .noUi-connect, +.noUi-state-tap .noUi-origin { + transition: transform 0.3s; +} +.noUi-horizontal { + height: 14px; +} +.noUi-horizontal .noUi-handle { + width: 20px; + height: 20px; + right: -10px; + top: -4px; +} +.noUi-txt-dir-rtl.noUi-horizontal .noUi-handle { + left: -10px; + right: auto; +} +.noUi-target { + background: #FAFAFA; + border-radius: 4px; + border: 1px solid #D3D3D3; + padding: 0 8px; +} +.noUi-connects { + border-radius: 3px; +} +.noUi-connect { + background: #ddd; +} +.noUi-handle { + border: 1px solid #D9D9D9; + border-radius: 3px; + background: #FFF; + cursor: default; +} +.noUi-pips, +.noUi-pips * { + box-sizing: border-box; +} +.noUi-pips { + position: absolute; + color: #999; +} +.noUi-value { + position: absolute; + white-space: nowrap; + text-align: center; +} +.noUi-value-sub { + color: #ccc; + font-size: 10px; +} +.noUi-marker { + position: absolute; + background: #CCC; +} +.noUi-marker-sub { + background: #AAA; +} +.noUi-marker-large { + background: #AAA; +} +.noUi-pips-horizontal { + padding: 10px 0; + height: 80px; + top: 100%; + left: 0; + width: 100%; +} +.noUi-value-horizontal { + transform: translate(-50%, 50%); +} +.noUi-rtl .noUi-value-horizontal { + transform: translate(50%, 50%); +} +.noUi-marker-horizontal.noUi-marker { + margin-left: -1px; + width: 2px; + height: 5px; +} +.noUi-marker-horizontal.noUi-marker-sub { + height: 10px; +} +.noUi-marker-horizontal.noUi-marker-large { + height: 15px; +} +.noUi-tooltip { + display: block; + position: absolute; + border: 1px solid #D9D9D9; + border-radius: 3px; + background: #fff; + color: #000; + padding: 5px; + text-align: center; + white-space: nowrap; +} +.noUi-horizontal .noUi-tooltip { + transform: translate(-50%, 0); + left: 50%; + bottom: 120%; +} +.noUi-horizontal .noUi-origin > .noUi-tooltip { + transform: translate(50%, 0); + left: auto; + bottom: 10px; +} diff --git a/wp-content/plugins/facetwp/assets/vendor/noUiSlider/nouislider.min.js b/wp-content/plugins/facetwp/assets/vendor/noUiSlider/nouislider.min.js new file mode 100644 index 000000000..e175fb270 --- /dev/null +++ b/wp-content/plugins/facetwp/assets/vendor/noUiSlider/nouislider.min.js @@ -0,0 +1,2 @@ +/*! noUiSlider - 15.6.1 custom - 2022-09-06 */ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).noUiSlider={})}(this,function(ut){"use strict";function n(t){return"object"==typeof t&&"function"==typeof t.to}function ct(t){t.parentElement.removeChild(t)}function pt(t){return null!=t}function ft(t){t.preventDefault()}function i(t){return"number"==typeof t&&!isNaN(t)&&isFinite(t)}function dt(t,e,r){0=e[r];)r+=1;return r}function r(t,e,r){var n,i,o;return r>=t.slice(-1)[0]?100:(o=l(r,t),n=t[o-1],t=t[o],i=e[o-1],e=e[o],i+(o=r,a(r=[n,t],r[0]<0?o+Math.abs(r[0]):o-r[0],0)/s(i,e)))}function o(t,e,r,n){var i,o,s;return 100===n?n:(o=t[(i=l(n,t))-1],s=t[i],r?(s-o)/2this.xPct[n+1];)n++;else t===this.xPct[this.xPct.length-1]&&(n=this.xPct.length-2);r||t!==this.xPct[n+1]||n++;for(var i,o=1,s=(e=null===e?[]:e)[n],a=0,l=0,u=0,c=r?(t-this.xPct[n])/(this.xPct[n+1]-this.xPct[n]):(this.xPct[n+1]-t)/(this.xPct[n+1]-this.xPct[n]);0= 2) required for mode 'count'.");for(var e=t.values-1,r=100/e,n=[];e--;)n[e]=e*r;return n.push(100),q(n,t.stepped)}(d),m={},t=S.xVal[0],e=S.xVal[S.xVal.length-1],g=!1,v=!1,b=0;return(h=h.slice().sort(function(t,e){return t-e}).filter(function(t){return!this[t]&&(this[t]=!0)},{}))[0]!==t&&(h.unshift(t),g=!0),h[h.length-1]!==e&&(h.push(e),v=!0),h.forEach(function(t,e){var r,n,i,o,s,a,l,u,c=h[e+1],p=d.mode===ut.PipsMode.Steps,f=(f=p?S.xNumSteps[e]:f)||c-t;for(void 0===c&&(c=t),f=Math.max(f,1e-7),r=t;r<=c;r=Number((r+f).toFixed(7))){for(a=(s=(i=S.toStepping(r))-b)/(d.density||1),u=s/(l=Math.round(a)),n=1;n<=l;n+=1)m[(o=b+n*u).toFixed(5)]=[S.fromStepping(o),0];s=-1ut.PipsType.NoValue&&((n=y(a,!1)).className=p(t,f.cssClasses.value),n.setAttribute("data-value",String(r)),n.style[f.style]=e+"%",n.innerHTML=String(s.to(r))))}),a}function P(){n&&(ct(n),n=null)}function C(t){P();var e=X(t),r=t.filter,t=t.format||{to:function(t){return String(Math.round(t))}};return n=c.appendChild(Y(e,r,t))}function I(){var t=i.getBoundingClientRect(),e="offset"+["Width","Height"][f.ort];return 0===f.ort?t.width||i[e]:t.height||i[e]}function N(n,i,o,s){function e(t){var e,r;return!!(t=function(e,t,r){var n=0===e.type.indexOf("touch"),i=0===e.type.indexOf("mouse"),o=0===e.type.indexOf("pointer"),s=0,a=0;0===e.type.indexOf("MSPointer")&&(o=!0);if("mousedown"===e.type&&!e.buttons&&!e.touches)return!1;if(n){n=function(t){t=t.target;return t===r||r.contains(t)||e.composed&&e.composedPath().shift()===r};if("touchstart"===e.type){var l=Array.prototype.filter.call(e.touches,n);if(1r.stepAfter.startValue&&(n=r.stepAfter.startValue-t),i=t>r.thisStep.startValue?r.thisStep.step:!1!==r.stepBefore.step&&t-r.stepBefore.highestStep,100===e?n=null:0===e&&(i=null);t=S.countStepDecimals();return null!==n&&!1!==n&&(n=Number(n.toFixed(t))),[i=null!==i&&!1!==i?Number(i.toFixed(t)):i,n]}gt(t=c,f.cssClasses.target),0===f.dir?gt(t,f.cssClasses.ltr):gt(t,f.cssClasses.rtl),0===f.ort?gt(t,f.cssClasses.horizontal):gt(t,f.cssClasses.vertical),gt(t,"rtl"===getComputedStyle(t).direction?f.cssClasses.textDirectionRtl:f.cssClasses.textDirectionLtr),i=y(t,f.cssClasses.base);var O,st=f.connect,at=i,lt=y(at,f.cssClasses.connects);l=[],(a=[]).push(F(lt,st[0]));for(var L=0;L*{margin:.5em}.layout_default.picker_wrapper::before{content:'';display:block;width:100%;height:0;order:1}.layout_default .picker_slider,.layout_default .picker_selector{padding:1em}.layout_default .picker_hue{width:100%}.layout_default .picker_sl{flex:1 1 auto}.layout_default .picker_sl::before{content:'';display:block;padding-bottom:100%}.layout_default .picker_editor{order:1;width:6rem}.layout_default .picker_editor input{width:calc(100% + 2px);height:calc(100% + 2px)}.layout_default .picker_sample{order:1;flex:1 1 auto}.layout_default .picker_done{order:1}.picker_wrapper{box-sizing:border-box;background:#f2f2f2;box-shadow:0 0 0 1px silver;cursor:default;font-family:sans-serif;color:#444;pointer-events:auto}.picker_wrapper:focus{outline:none}.picker_wrapper button,.picker_wrapper input{margin:-1px}.picker_selector{position:absolute;z-index:1;display:block;transform:translate(-50%, -50%);border:2px solid white;border-radius:100%;box-shadow:0 0 3px 1px #67b9ff;background:currentColor;cursor:pointer}.picker_slider .picker_selector{border-radius:2px}.picker_hue{position:relative;background-image:linear-gradient(90deg, red, yellow, lime, cyan, blue, magenta, red);box-shadow:0 0 0 1px silver}.picker_sl{position:relative;box-shadow:0 0 0 1px silver;background-image:linear-gradient(180deg, white, rgba(255,255,255,0) 50%),linear-gradient(0deg, black, rgba(0,0,0,0) 50%),linear-gradient(90deg, gray, rgba(128,128,128,0))}.picker_alpha,.picker_sample{position:relative;background:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2' height='2'%3E%3Cpath d='M1,0H0V1H2V2H1' fill='lightgrey'/%3E%3C/svg%3E\") left top/contain white;box-shadow:0 0 0 1px silver}.picker_alpha .picker_selector,.picker_sample .picker_selector{background:none}.picker_editor input{box-sizing:border-box;font-family:monospace;padding:.1em .2em}.picker_sample::before{content:'';position:absolute;display:block;width:100%;height:100%;background:currentColor}.picker_done button{box-sizing:border-box;padding:.2em .5em;cursor:pointer}.picker_arrow{position:absolute;z-index:-1}.picker_wrapper.popup{position:absolute;z-index:2;margin:1.5em}.picker_wrapper.popup,.picker_wrapper.popup .picker_arrow::before,.picker_wrapper.popup .picker_arrow::after{background:#f2f2f2;box-shadow:0 0 10px 1px rgba(0,0,0,0.4)}.picker_wrapper.popup .picker_arrow{width:3em;height:3em;margin:0}.picker_wrapper.popup .picker_arrow::before,.picker_wrapper.popup .picker_arrow::after{content:\"\";display:block;position:absolute;top:0;left:0;z-index:-99}.picker_wrapper.popup .picker_arrow::before{width:100%;height:100%;transform:skew(45deg);transform-origin:0 100%}.picker_wrapper.popup .picker_arrow::after{width:150%;height:150%;box-shadow:none}.popup.popup_top{bottom:100%;left:0}.popup.popup_top .picker_arrow{bottom:0;left:0;transform:rotate(-90deg)}.popup.popup_bottom{top:100%;left:0}.popup.popup_bottom .picker_arrow{top:0;left:0;transform:rotate(90deg) scale(1, -1)}.popup.popup_left{top:0;right:100%}.popup.popup_left .picker_arrow{top:0;right:0;transform:scale(-1, 1)}.popup.popup_right{top:0;left:100%}.popup.popup_right .picker_arrow{top:0;left:0}";var e=t.prototype;return e.setOptions=function(t){if(t){var e=this.settings;if(t instanceof HTMLElement)e.parent=t;else{e.parent&&t.parent&&e.parent!==t.parent&&(e.parent.removeEventListener("click",this._openProxy,!1),this._popupInited=!1),function(t,e,i){for(var o in t)i&&0<=i.indexOf(o)||(e[o]=t[o])}(t,e),t.onChange&&(this.onChange=t.onChange),t.onDone&&(this.onDone=t.onDone),t.onOpen&&(this.onOpen=t.onOpen),t.onClose&&(this.onClose=t.onClose);var i=t.color;i&&this._setColor(i)}var o=e.parent;o&&e.popup&&!this._popupInited?(a(o,"click",this._openProxy),p(o,[" ","Spacebar","Enter"],this._openProxy),this._popupInited=!0):t.parent&&!e.popup&&this.show()}},e.openHandler=function(t){if(this.show()){t&&t.preventDefault(),this.settings.parent.style.pointerEvents="none";var e=t&&t.type===o?this._domEdit:this.domElement;setTimeout(function(){e.focus()},100),this.onOpen&&this.onOpen(this.color)}},e.closeHandler=function(t){var e,i=this.settings.parent,o=t&&t.type,r=!1;t?o===n||o===s?this.domElement.contains(t.target)||(r=!0):((e=t).preventDefault(),e.stopPropagation(),r=!0):r=!0,r&&this.hide()&&(i.style.pointerEvents="",o!==n&&i.focus(),this.onClose&&this.onClose(this.color))},e.movePopup=function(t,e){this.closeHandler(),this.setOptions(t),e&&this.openHandler()},e.setColor=function(t,e){this._setColor(t,{silent:e})},e._setColor=function(t,e){if("string"==typeof t&&(t=t.trim()),t){var i;e=e||{};try{i=new r(t)}catch(t){if(e.failSilently)return;throw t}if(!this.settings.alpha){var o=i.hsla();o[3]=1,i.hsla(o)}this.color=i,this._setHSLA(null,null,null,null,e)}},e.show=function(){var t=this.settings;if(!t.parent)return!1;if(this.domElement){var e=this._toggleDOM(!0);return this._setPosition(),e}var i,o,r=t.template||'
    ',n=(i=r,(o=document.createElement("div")).innerHTML=i,o.firstElementChild);return this.domElement=n,this._domH=w(".picker_hue",n),this._domSL=w(".picker_sl",n),this._domA=w(".picker_alpha",n),this._domEdit=w(".picker_editor input",n),this._domSample=w(".picker_sample",n),this._domOkay=w(".picker_done button",n),n.classList.add("layout_"+t.layout),t.alpha||n.classList.add("no_alpha"),t.editor||n.classList.add("no_editor"),this._ifPopup(function(){n.classList.add("popup")}),this._setPosition(),this.color?this._updateUI():this._setColor("#0cf"),this._bindEvents(),!0},e.hide=function(){return this._toggleDOM(!1)},e._bindEvents=function(){var i=this,t=this.domElement;a(t,"click",function(t){t.preventDefault()}),l(this._domH,function(t,e){i._setHSLA(t)}),l(this._domSL,function(t,e){i._setHSLA(null,t,1-e)}),this.settings.alpha&&l(this._domA,function(t,e){i._setHSLA(null,null,null,1-e)});var e=this._domEdit;function o(t){i._ifPopup(function(){i.closeHandler(t)})}function r(t){i._ifPopup(function(){i.closeHandler(t)}),i.onDone&&i.onDone(i.color)}a(e,"input",function(t){i._setColor(this.value,{fromEditor:!0,failSilently:!0})}),a(e,"focus",function(t){this.selectionStart===this.selectionEnd&&this.select()}),a(window,n,o),a(window,s,o),p(t,["Esc","Escape"],o),a(this._domOkay,"click",r),p(t,["Enter"],r)},e._setPosition=function(){var i=this.settings.parent,o=this.domElement;i!==o.parentNode&&i.appendChild(o),this._ifPopup(function(t){"static"===getComputedStyle(i).position&&(i.style.position="relative");var e=!0===t?"popup_right":"popup_"+t;["popup_top","popup_bottom","popup_left","popup_right"].forEach(function(t){t===e?o.classList.add(t):o.classList.remove(t)}),o.classList.add(e)})},e._setHSLA=function(t,e,i,o,r){r=r||{};var n=this.color,s=n.hsla();[t,e,i,o].forEach(function(t,e){(t||0===t)&&(s[e]=t)}),n.hsla(s),this._updateUI(r),this.onChange&&!r.silent&&this.onChange(n)},e._updateUI=function(t){if(this.domElement){t=t||{};var e=this.color,i=e.hsla(),o="hsl("+360*i[0]+", 100%, 50%)",r=e.hslString(),n=e.hslaString(),s=this._domH,p=this._domSL,a=this._domA,l=w(".picker_selector",s),c=w(".picker_selector",p),u=w(".picker_selector",a);g(0,l,i[0]),this._domSL.style.backgroundColor=this._domH.style.color=o,g(0,c,i[1]),m(0,c,1-i[2]),p.style.color=r,m(0,u,1-i[3]);var h=r,d=h.replace("hsl","hsla").replace(")",", 0)"),f="linear-gradient("+[h,d]+")";if(this._domA.style.backgroundImage=f+", url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='2' height='2'%3E%3Cpath d='M1,0H0V1H2V2H1' fill='lightgrey'/%3E%3C/svg%3E\")",!t.fromEditor){var _=e.hex();this._domEdit.value=this.settings.alpha?_:_.substr(0,7)}this._domSample.style.color=n}function g(t,e,i){e.style.left=100*i+"%"}function m(t,e,i){e.style.top=100*i+"%"}},e._ifPopup=function(t,e){var i=this.settings;i.parent&&i.popup?t&&t(i.popup):e&&e()},e._toggleDOM=function(t){var e=this.domElement;if(!e)return!1;var i=t?"":"none",o=e.style.display!==i;return o&&(e.style.display=i),o},t}()}); \ No newline at end of file diff --git a/wp-content/plugins/facetwp/assets/vendor/vue/Sortable.min.js b/wp-content/plugins/facetwp/assets/vendor/vue/Sortable.min.js new file mode 100644 index 000000000..17bb16c73 --- /dev/null +++ b/wp-content/plugins/facetwp/assets/vendor/vue/Sortable.min.js @@ -0,0 +1,2 @@ +/*! Sortable 1.15.0 - MIT | git://github.com/SortableJS/Sortable.git */ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t=t||self).Sortable=e()}(this,function(){"use strict";function e(e,t){var n,o=Object.keys(e);return Object.getOwnPropertySymbols&&(n=Object.getOwnPropertySymbols(e),t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),o.push.apply(o,n)),o}function M(o){for(var t=1;tt.length)&&(e=t.length);for(var n=0,o=new Array(e);n"===e[0]&&(e=e.substring(1)),t))try{if(t.matches)return t.matches(e);if(t.msMatchesSelector)return t.msMatchesSelector(e);if(t.webkitMatchesSelector)return t.webkitMatchesSelector(e)}catch(t){return}}function N(t,e,n,o){if(t){n=n||document;do{if(null!=e&&(">"!==e[0]||t.parentNode===n)&&p(t,e)||o&&t===n)return t}while(t!==n&&(t=(i=t).host&&i!==document&&i.host.nodeType?i.host:i.parentNode))}var i;return null}var g,m=/\s+/g;function I(t,e,n){var o;t&&e&&(t.classList?t.classList[n?"add":"remove"](e):(o=(" "+t.className+" ").replace(m," ").replace(" "+e+" "," "),t.className=(o+(n?" "+e:"")).replace(m," ")))}function P(t,e,n){var o=t&&t.style;if(o){if(void 0===n)return document.defaultView&&document.defaultView.getComputedStyle?n=document.defaultView.getComputedStyle(t,""):t.currentStyle&&(n=t.currentStyle),void 0===e?n:n[e];o[e=!(e in o||-1!==e.indexOf("webkit"))?"-webkit-"+e:e]=n+("string"==typeof n?"":"px")}}function v(t,e){var n="";if("string"==typeof t)n=t;else do{var o=P(t,"transform")}while(o&&"none"!==o&&(n=o+" "+n),!e&&(t=t.parentNode));var i=window.DOMMatrix||window.WebKitCSSMatrix||window.CSSMatrix||window.MSCSSMatrix;return i&&new i(n)}function b(t,e,n){if(t){var o=t.getElementsByTagName(e),i=0,r=o.length;if(n)for(;i=n.left-e&&i<=n.right+e,e=r>=n.top-e&&r<=n.bottom+e;return o&&e?a=t:void 0}}),a);if(e){var n,o={};for(n in t)t.hasOwnProperty(n)&&(o[n]=t[n]);o.target=o.rootEl=e,o.preventDefault=void 0,o.stopPropagation=void 0,e[j]._onDragOver(o)}}var i,r,a}function Yt(t){q&&q.parentNode[j]._isOutsideThisEl(t.target)}function Bt(t,e){if(!t||!t.nodeType||1!==t.nodeType)throw"Sortable: `el` must be an HTMLElement, not ".concat({}.toString.call(t));this.el=t,this.options=e=a({},e),t[j]=this;var n,o,i={group:null,sort:!0,disabled:!1,store:null,handle:null,draggable:/^[uo]l$/i.test(t.nodeName)?">li":">*",swapThreshold:1,invertSwap:!1,invertedSwapThreshold:null,removeCloneOnHide:!0,direction:function(){return It(t,this.options)},ghostClass:"sortable-ghost",chosenClass:"sortable-chosen",dragClass:"sortable-drag",ignore:"a, img",filter:null,preventOnFilter:!0,animation:0,easing:null,setData:function(t,e){t.setData("Text",e.textContent)},dropBubble:!1,dragoverBubble:!1,dataIdAttr:"data-id",delay:0,delayOnTouchOnly:!1,touchStartThreshold:(Number.parseInt?Number:window).parseInt(window.devicePixelRatio,10)||1,forceFallback:!1,fallbackClass:"sortable-fallback",fallbackOnBody:!1,fallbackTolerance:0,fallbackOffset:{x:0,y:0},supportPointer:!1!==Bt.supportPointer&&"PointerEvent"in window&&!u,emptyInsertThreshold:5};for(n in K.initializePlugins(this,t,i),i)n in e||(e[n]=i[n]);for(o in Pt(e),this)"_"===o.charAt(0)&&"function"==typeof this[o]&&(this[o]=this[o].bind(this));this.nativeDraggable=!e.forceFallback&&Mt,this.nativeDraggable&&(this.options.touchStartThreshold=1),e.supportPointer?h(t,"pointerdown",this._onTapStart):(h(t,"mousedown",this._onTapStart),h(t,"touchstart",this._onTapStart)),this.nativeDraggable&&(h(t,"dragover",this),h(t,"dragenter",this)),Et.push(this.el),e.store&&e.store.get&&this.sort(e.store.get(this)||[]),a(this,x())}function Ft(t,e,n,o,i,r,a,l){var s,c,u=t[j],d=u.options.onMove;return!window.CustomEvent||y||w?(s=document.createEvent("Event")).initEvent("move",!0,!0):s=new CustomEvent("move",{bubbles:!0,cancelable:!0}),s.to=e,s.from=t,s.dragged=n,s.draggedRect=o,s.related=i||e,s.relatedRect=r||k(e),s.willInsertAfter=l,s.originalEvent=a,t.dispatchEvent(s),c=d?d.call(u,s,a):c}function jt(t){t.draggable=!1}function Ht(){Ct=!1}function Lt(t){return setTimeout(t,0)}function Kt(t){return clearTimeout(t)}Bt.prototype={constructor:Bt,_isOutsideThisEl:function(t){this.el.contains(t)||t===this.el||(gt=null)},_getDirection:function(t,e){return"function"==typeof this.options.direction?this.options.direction.call(this,t,e,q):this.options.direction},_onTapStart:function(e){if(e.cancelable){var n=this,o=this.el,t=this.options,i=t.preventOnFilter,r=e.type,a=e.touches&&e.touches[0]||e.pointerType&&"touch"===e.pointerType&&e,l=(a||e).target,s=e.target.shadowRoot&&(e.path&&e.path[0]||e.composedPath&&e.composedPath()[0])||l,c=t.filter;if(!function(t){Tt.length=0;var e=t.getElementsByTagName("input"),n=e.length;for(;n--;){var o=e[n];o.checked&&Tt.push(o)}}(o),!q&&!(/mousedown|pointerdown/.test(r)&&0!==e.button||t.disabled)&&!s.isContentEditable&&(this.nativeDraggable||!u||!l||"SELECT"!==l.tagName.toUpperCase())&&!((l=N(l,t.draggable,o,!1))&&l.animated||J===l)){if(nt=B(l),it=B(l,t.draggable),"function"==typeof c){if(c.call(this,e,l,this))return U({sortable:n,rootEl:s,name:"filter",targetEl:l,toEl:o,fromEl:o}),z("filter",n,{evt:e}),void(i&&e.cancelable&&e.preventDefault())}else if(c=c&&c.split(",").some(function(t){if(t=N(s,t.trim(),o,!1))return U({sortable:n,rootEl:t,name:"filter",targetEl:l,fromEl:o,toEl:o}),z("filter",n,{evt:e}),!0}))return void(i&&e.cancelable&&e.preventDefault());t.handle&&!N(s,t.handle,o,!1)||this._prepareDragStart(e,a,l)}}},_prepareDragStart:function(t,e,n){var o,i=this,r=i.el,a=i.options,l=r.ownerDocument;n&&!q&&n.parentNode===r&&(o=k(n),$=r,V=(q=n).parentNode,Q=q.nextSibling,J=n,at=a.group,st={target:Bt.dragged=q,clientX:(e||t).clientX,clientY:(e||t).clientY},ht=st.clientX-o.left,ft=st.clientY-o.top,this._lastX=(e||t).clientX,this._lastY=(e||t).clientY,q.style["will-change"]="all",o=function(){z("delayEnded",i,{evt:t}),Bt.eventCanceled?i._onDrop():(i._disableDelayedDragEvents(),!s&&i.nativeDraggable&&(q.draggable=!0),i._triggerDragStart(t,e),U({sortable:i,name:"choose",originalEvent:t}),I(q,a.chosenClass,!0))},a.ignore.split(",").forEach(function(t){b(q,t.trim(),jt)}),h(l,"dragover",Xt),h(l,"mousemove",Xt),h(l,"touchmove",Xt),h(l,"mouseup",i._onDrop),h(l,"touchend",i._onDrop),h(l,"touchcancel",i._onDrop),s&&this.nativeDraggable&&(this.options.touchStartThreshold=4,q.draggable=!0),z("delayStart",this,{evt:t}),!a.delay||a.delayOnTouchOnly&&!e||this.nativeDraggable&&(w||y)?o():Bt.eventCanceled?this._onDrop():(h(l,"mouseup",i._disableDelayedDrag),h(l,"touchend",i._disableDelayedDrag),h(l,"touchcancel",i._disableDelayedDrag),h(l,"mousemove",i._delayedDragTouchMoveHandler),h(l,"touchmove",i._delayedDragTouchMoveHandler),a.supportPointer&&h(l,"pointermove",i._delayedDragTouchMoveHandler),i._dragStartTimer=setTimeout(o,a.delay)))},_delayedDragTouchMoveHandler:function(t){t=t.touches?t.touches[0]:t;Math.max(Math.abs(t.clientX-this._lastX),Math.abs(t.clientY-this._lastY))>=Math.floor(this.options.touchStartThreshold/(this.nativeDraggable&&window.devicePixelRatio||1))&&this._disableDelayedDrag()},_disableDelayedDrag:function(){q&&jt(q),clearTimeout(this._dragStartTimer),this._disableDelayedDragEvents()},_disableDelayedDragEvents:function(){var t=this.el.ownerDocument;f(t,"mouseup",this._disableDelayedDrag),f(t,"touchend",this._disableDelayedDrag),f(t,"touchcancel",this._disableDelayedDrag),f(t,"mousemove",this._delayedDragTouchMoveHandler),f(t,"touchmove",this._delayedDragTouchMoveHandler),f(t,"pointermove",this._delayedDragTouchMoveHandler)},_triggerDragStart:function(t,e){e=e||"touch"==t.pointerType&&t,!this.nativeDraggable||e?this.options.supportPointer?h(document,"pointermove",this._onTouchMove):h(document,e?"touchmove":"mousemove",this._onTouchMove):(h(q,"dragend",this),h($,"dragstart",this._onDragStart));try{document.selection?Lt(function(){document.selection.empty()}):window.getSelection().removeAllRanges()}catch(t){}},_dragStarted:function(t,e){var n;yt=!1,$&&q?(z("dragStarted",this,{evt:e}),this.nativeDraggable&&h(document,"dragover",Yt),n=this.options,t||I(q,n.dragClass,!1),I(q,n.ghostClass,!0),Bt.active=this,t&&this._appendGhost(),U({sortable:this,name:"start",originalEvent:e})):this._nulling()},_emulateDragOver:function(){if(ct){this._lastX=ct.clientX,this._lastY=ct.clientY,kt();for(var t=document.elementFromPoint(ct.clientX,ct.clientY),e=t;t&&t.shadowRoot&&(t=t.shadowRoot.elementFromPoint(ct.clientX,ct.clientY))!==e;)e=t;if(q.parentNode[j]._isOutsideThisEl(t),e)do{if(e[j])if(e[j]._onDragOver({clientX:ct.clientX,clientY:ct.clientY,target:t,rootEl:e})&&!this.options.dragoverBubble)break}while(e=(t=e).parentNode);Rt()}},_onTouchMove:function(t){if(st){var e=this.options,n=e.fallbackTolerance,o=e.fallbackOffset,i=t.touches?t.touches[0]:t,r=Z&&v(Z,!0),a=Z&&r&&r.a,l=Z&&r&&r.d,e=Ot&&bt&&E(bt),a=(i.clientX-st.clientX+o.x)/(a||1)+(e?e[0]-_t[0]:0)/(a||1),l=(i.clientY-st.clientY+o.y)/(l||1)+(e?e[1]-_t[1]:0)/(l||1);if(!Bt.active&&!yt){if(n&&Math.max(Math.abs(i.clientX-this._lastX),Math.abs(i.clientY-this._lastY))n.right+10||t.clientX<=n.right&&t.clientY>n.bottom&&t.clientX>=n.left:t.clientX>n.right&&t.clientY>n.top||t.clientX<=n.right&&t.clientY>n.bottom+10}(n,r,this)&&!g.animated){if(g===q)return O(!1);if((l=g&&a===n.target?g:l)&&(w=k(l)),!1!==Ft($,a,q,o,l,w,n,!!l))return x(),g&&g.nextSibling?a.insertBefore(q,g.nextSibling):a.appendChild(q),V=a,A(),O(!0)}else if(g&&function(t,e,n){n=k(X(n.el,0,n.options,!0));return e?t.clientX{t.exports=function(t){if(Array.isArray(t)){for(var e=0,n=new Array(t.length);e{t.exports=function(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}},860:t=>{t.exports=function(t){if(Symbol.iterator in Object(t)||"[object Arguments]"===Object.prototype.toString.call(t))return Array.from(t)}},206:t=>{t.exports=function(){throw new TypeError("Invalid attempt to spread non-iterable instance")}},319:(t,e,n)=>{var o=n(646),i=n(860),s=n(206);t.exports=function(t){return o(t)||i(t)||s()}},8:e=>{function n(t){return"function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?e.exports=n=function(t){return typeof t}:e.exports=n=function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},n(t)}e.exports=n}},o={},y.n=t=>{var e=t&&t.__esModule?()=>t.default:()=>t;return y.d(e,{a:e}),e},y.d=(t,e)=>{for(var n in e)y.o(e,n)&&!y.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:e[n]})},y.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),y.r=t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},b={},(()=>{"use strict";y.r(b),y.d(b,{VueSelect:()=>h,default:()=>f,mixins:()=>d});var t=y(319),o=y.n(t),t=y(8),i=y.n(t),t=y(713),s=y.n(t),t={props:{autoscroll:{type:Boolean,default:!0}},watch:{typeAheadPointer:function(){this.autoscroll&&this.maybeAdjustScroll()},open:function(t){var e=this;this.autoscroll&&t&&this.$nextTick(function(){return e.maybeAdjustScroll()})}},methods:{maybeAdjustScroll:function(){var t,e,n,o,i=(null==(i=this.$refs.dropdownMenu)?void 0:i.children[this.typeAheadPointer])||!1;if(i)return t=this.getDropdownViewport(),e=(o=i.getBoundingClientRect()).top,n=o.bottom,o=o.height,et.bottom?this.$refs.dropdownMenu.scrollTop=i.offsetTop-(t.height-o):void 0},getDropdownViewport:function(){return this.$refs.dropdownMenu?this.$refs.dropdownMenu.getBoundingClientRect():{height:0,top:0,bottom:0}}}},e={data:function(){return{typeAheadPointer:-1}},watch:{filteredOptions:function(){for(var t=0;t=0&&Math.floor(e)===e&&isFinite(t)}function f(t){return r(t)&&"function"==typeof t.then&&"function"==typeof t.catch}function d(t){return null==t?"":Array.isArray(t)||u(t)&&t.toString===c?JSON.stringify(t,null,2):String(t)}function p(t){var e=parseFloat(t);return isNaN(e)?t:e}function v(t,e){for(var n=Object.create(null),r=t.split(","),o=0;o-1)return t.splice(n,1)}}var y=Object.prototype.hasOwnProperty;function _(t,e){return y.call(t,e)}function b(t){var e=Object.create(null);return function(n){return e[n]||(e[n]=t(n))}}var $=/-(\w)/g,w=b((function(t){return t.replace($,(function(t,e){return e?e.toUpperCase():""}))})),x=b((function(t){return t.charAt(0).toUpperCase()+t.slice(1)})),C=/\B([A-Z])/g,k=b((function(t){return t.replace(C,"-$1").toLowerCase()}));var S=Function.prototype.bind?function(t,e){return t.bind(e)}:function(t,e){function n(n){var r=arguments.length;return r?r>1?t.apply(e,arguments):t.call(e,n):t.call(e)}return n._length=t.length,n};function O(t,e){e=e||0;for(var n=t.length-e,r=new Array(n);n--;)r[n]=t[n+e];return r}function T(t,e){for(var n in e)t[n]=e[n];return t}function A(t){for(var e={},n=0;n0,G=q&&q.indexOf("edge/")>0;q&&q.indexOf("android");var X=q&&/iphone|ipad|ipod|ios/.test(q);q&&/chrome\/\d+/.test(q),q&&/phantomjs/.test(q);var Y,Q=q&&q.match(/firefox\/(\d+)/),tt={}.watch,et=!1;if(J)try{var nt={};Object.defineProperty(nt,"passive",{get:function(){et=!0}}),window.addEventListener("test-passive",null,nt)}catch(t){}var rt=function(){return void 0===Y&&(Y=!J&&"undefined"!=typeof global&&(global.process&&"server"===global.process.env.VUE_ENV)),Y},ot=J&&window.__VUE_DEVTOOLS_GLOBAL_HOOK__;function it(t){return"function"==typeof t&&/native code/.test(t.toString())}var at,st="undefined"!=typeof Symbol&&it(Symbol)&&"undefined"!=typeof Reflect&&it(Reflect.ownKeys);at="undefined"!=typeof Set&&it(Set)?Set:function(){function t(){this.set=Object.create(null)}return t.prototype.has=function(t){return!0===this.set[t]},t.prototype.add=function(t){this.set[t]=!0},t.prototype.clear=function(){this.set=Object.create(null)},t}();var ct=null;function ut(t){void 0===t&&(t=null),t||ct&&ct._scope.off(),ct=t,t&&t._scope.on()}var lt=function(){function t(t,e,n,r,o,i,a,s){this.tag=t,this.data=e,this.children=n,this.text=r,this.elm=o,this.ns=void 0,this.context=i,this.fnContext=void 0,this.fnOptions=void 0,this.fnScopeId=void 0,this.key=e&&e.key,this.componentOptions=a,this.componentInstance=void 0,this.parent=void 0,this.raw=!1,this.isStatic=!1,this.isRootInsert=!0,this.isComment=!1,this.isCloned=!1,this.isOnce=!1,this.asyncFactory=s,this.asyncMeta=void 0,this.isAsyncPlaceholder=!1}return Object.defineProperty(t.prototype,"child",{get:function(){return this.componentInstance},enumerable:!1,configurable:!0}),t}(),ft=function(t){void 0===t&&(t="");var e=new lt;return e.text=t,e.isComment=!0,e};function dt(t){return new lt(void 0,void 0,void 0,String(t))}function pt(t){var e=new lt(t.tag,t.data,t.children&&t.children.slice(),t.text,t.elm,t.context,t.componentOptions,t.asyncFactory);return e.ns=t.ns,e.isStatic=t.isStatic,e.key=t.key,e.isComment=t.isComment,e.fnContext=t.fnContext,e.fnOptions=t.fnOptions,e.fnScopeId=t.fnScopeId,e.asyncMeta=t.asyncMeta,e.isCloned=!0,e}var vt=0,ht=function(){function t(){this.id=vt++,this.subs=[]}return t.prototype.addSub=function(t){this.subs.push(t)},t.prototype.removeSub=function(t){g(this.subs,t)},t.prototype.depend=function(e){t.target&&t.target.addDep(this)},t.prototype.notify=function(t){for(var e=this.subs.slice(),n=0,r=e.length;n0&&(Xt((c=Yt(c,"".concat(a||"","_").concat(s)))[0])&&Xt(l)&&(f[u]=dt(l.text+c[0].text),c.shift()),f.push.apply(f,c)):i(c)?Xt(l)?f[u]=dt(l.text+c):""!==c&&f.push(dt(c)):Xt(c)&&Xt(l)?f[u]=dt(l.text+c.text):(o(t._isVList)&&r(c.tag)&&n(c.key)&&r(a)&&(c.key="__vlist".concat(a,"_").concat(s,"__")),f.push(c)));return f}function Qt(t,n,c,u,l,f){return(e(c)||i(c))&&(l=u,u=c,c=void 0),o(f)&&(l=2),function(t,n,o,i,c){if(r(o)&&r(o.__ob__))return ft();r(o)&&r(o.is)&&(n=o.is);if(!n)return ft();e(i)&&a(i[0])&&((o=o||{}).scopedSlots={default:i[0]},i.length=0);2===c?i=Gt(i):1===c&&(i=function(t){for(var n=0;n0,s=n?!!n.$stable:!a,c=n&&n.$key;if(n){if(n._normalized)return n._normalized;if(s&&o&&o!==t&&c===o.$key&&!a&&!o.$hasNormal)return o;for(var u in i={},n)n[u]&&"$"!==u[0]&&(i[u]=be(e,r,u,n[u]))}else i={};for(var l in r)l in i||(i[l]=$e(r,l));return n&&Object.isExtensible(n)&&(n._normalized=i),z(i,"$stable",s),z(i,"$key",c),z(i,"$hasNormal",a),i}function be(t,n,r,o){var i=function(){var n=ct;ut(t);var r=arguments.length?o.apply(null,arguments):o({}),i=(r=r&&"object"==typeof r&&!e(r)?[r]:Gt(r))&&r[0];return ut(n),r&&(!i||1===r.length&&i.isComment&&!ye(i))?void 0:r};return o.proxy&&Object.defineProperty(n,r,{get:i,enumerable:!0,configurable:!0}),i}function $e(t,e){return function(){return t[e]}}function we(e){return{get attrs(){return function(e){if(!e._attrsProxy){var n=e._attrsProxy={};z(n,"_v_attr_proxy",!0),xe(n,e.$attrs,t,e)}return e._attrsProxy}(e)},get slots(){return function(t){t._slotsProxy||ke(t._slotsProxy={},t.$scopedSlots);return t._slotsProxy}(e)},emit:S(e.$emit,e),expose:function(t){t&&Object.keys(t).forEach((function(n){return Ht(e,t,n)}))}}}function xe(t,e,n,r){var o=!1;for(var i in e)i in t?e[i]!==n[i]&&(o=!0):(o=!0,Ce(t,i,r));for(var i in t)i in e||(o=!0,delete t[i]);return o}function Ce(t,e,n){Object.defineProperty(t,e,{enumerable:!0,configurable:!0,get:function(){return n.$attrs[e]}})}function ke(t,e){for(var n in e)t[n]=e[n];for(var n in t)n in e||delete t[n]}function Se(){var t=ct;return t._setupContext||(t._setupContext=we(t))}var Oe,Te=null;function Ae(t,e){return(t.__esModule||st&&"Module"===t[Symbol.toStringTag])&&(t=t.default),s(t)?e.extend(t):t}function je(t){if(e(t))for(var n=0;ndocument.createEvent("Event").timeStamp&&(We=function(){return Ze.now()})}var Ge=function(t,e){if(t.post){if(!e.post)return 1}else if(e.post)return-1;return t.id-e.id};function Xe(){var t,e;for(qe=We(),Ke=!0,Be.sort(Ge),Je=0;JeJe&&Be[n].id>t.id;)n--;Be.splice(n+1,0,t)}else Be.push(t);Ve||(Ve=!0,xn(Xe))}}var Qe="watcher",tn="".concat(Qe," callback"),en="".concat(Qe," getter"),nn="".concat(Qe," cleanup");function rn(t,e){return sn(t,null,{flush:"post"})}var on,an={};function sn(n,r,o){var i=void 0===o?t:o,s=i.immediate,c=i.deep,u=i.flush,l=void 0===u?"pre":u;i.onTrack,i.onTrigger;var f,d,p=ct,v=function(t,e,n){return void 0===n&&(n=null),fn(t,null,n,p,e)},h=!1,m=!1;if(Rt(n)?(f=function(){return n.value},h=Mt(n)):Pt(n)?(f=function(){return n.__ob__.dep.depend(),n},c=!0):e(n)?(m=!0,h=n.some((function(t){return Pt(t)||Mt(t)})),f=function(){return n.map((function(t){return Rt(t)?t.value:Pt(t)?Hn(t):a(t)?v(t,en):void 0}))}):f=a(n)?r?function(){return v(n,en)}:function(){if(!p||!p._isDestroyed)return d&&d(),v(n,Qe,[y])}:j,r&&c){var g=f;f=function(){return Hn(g())}}var y=function(t){d=_.onStop=function(){v(t,nn)}};if(rt())return y=j,r?s&&v(r,tn,[f(),m?[]:void 0,y]):f(),j;var _=new zn(ct,f,j,{lazy:!0});_.noRecurse=!r;var b=m?[]:an;return _.run=function(){if(_.active||"pre"===l&&p&&p._isBeingDestroyed)if(r){var t=_.get();(c||h||(m?t.some((function(t,e){return I(t,b[e])})):I(t,b)))&&(d&&d(),v(r,tn,[t,b===an?void 0:b,y]),b=t)}else _.get()},"sync"===l?_.update=_.run:"post"===l?(_.post=!0,_.update=function(){return Ye(_)}):_.update=function(){if(p&&p===ct&&!p._isMounted){var t=p._preWatchers||(p._preWatchers=[]);t.indexOf(_)<0&&t.push(_)}else Ye(_)},r?s?_.run():b=_.get():"post"===l&&p?p.$once("hook:mounted",(function(){return _.get()})):_.get(),function(){_.teardown()}}var cn=function(){function t(t){void 0===t&&(t=!1),this.active=!0,this.effects=[],this.cleanups=[],!t&&on&&(this.parent=on,this.index=(on.scopes||(on.scopes=[])).push(this)-1)}return t.prototype.run=function(t){if(this.active){var e=on;try{return on=this,t()}finally{on=e}}},t.prototype.on=function(){on=this},t.prototype.off=function(){on=this.parent},t.prototype.stop=function(t){if(this.active){var e=void 0,n=void 0;for(e=0,n=this.effects.length;e1)return n&&a(e)?e.call(r):e}},h:function(t,e,n){return Qt(ct,t,e,n,2,!0)},getCurrentInstance:function(){return ct&&{proxy:ct}},useSlots:function(){return Se().slots},useAttrs:function(){return Se().attrs},mergeDefaults:function(t,n){var r=e(t)?t.reduce((function(t,e){return t[e]={},t}),{}):t;for(var o in n){var i=r[o];i?e(i)||a(i)?r[o]={type:i,default:n[o]}:i.default=n[o]:null===i&&(r[o]={default:n[o]})}return r},nextTick:xn,set:At,del:jt,useCssModule:function(e){return t},useCssVars:function(t){if(J){var e=ct;e&&rn((function(){var n=e.$el,r=t(e,e._setupProxy);if(n&&1===n.nodeType){var o=n.style;for(var i in r)o.setProperty("--".concat(i),r[i])}}))}},defineAsyncComponent:function(t){a(t)&&(t={loader:t});var e=t.loader,n=t.loadingComponent,r=t.errorComponent,o=t.delay,i=void 0===o?200:o,s=t.timeout;t.suspensible;var c=t.onError,u=null,l=0,f=function(){var t;return u||(t=u=e().catch((function(t){if(t=t instanceof Error?t:new Error(String(t)),c)return new Promise((function(e,n){c(t,(function(){return e((l++,u=null,f()))}),(function(){return n(t)}),l+1)}));throw t})).then((function(e){return t!==u&&u?u:(e&&(e.__esModule||"Module"===e[Symbol.toStringTag])&&(e=e.default),e)})))};return function(){return{component:f(),delay:i,timeout:s,error:r,loading:n}}},onBeforeMount:kn,onMounted:Sn,onBeforeUpdate:On,onUpdated:Tn,onBeforeUnmount:An,onUnmounted:jn,onErrorCaptured:En,onActivated:Nn,onDeactivated:Dn,onServerPrefetch:Pn,onRenderTracked:Mn,onRenderTriggered:In}),Fn=new at;function Hn(t){return Bn(t,Fn),Fn.clear(),t}function Bn(t,n){var r,o,i=e(t);if(!(!i&&!s(t)||Object.isFrozen(t)||t instanceof lt)){if(t.__ob__){var a=t.__ob__.dep.id;if(n.has(a))return;n.add(a)}if(i)for(r=t.length;r--;)Bn(t[r],n);else if(Rt(t))Bn(t.value,n);else for(r=(o=Object.keys(t)).length;r--;)Bn(t[o[r]],n)}}var Un=0,zn=function(){function t(t,e,n,r,o){!function(t,e){void 0===e&&(e=on),e&&e.active&&e.effects.push(t)}(this,on||(t?t._scope:void 0)),(this.vm=t)&&o&&(t._watcher=this),r?(this.deep=!!r.deep,this.user=!!r.user,this.lazy=!!r.lazy,this.sync=!!r.sync,this.before=r.before):this.deep=this.user=this.lazy=this.sync=!1,this.cb=n,this.id=++Un,this.active=!0,this.post=!1,this.dirty=this.lazy,this.deps=[],this.newDeps=[],this.depIds=new at,this.newDepIds=new at,this.expression="",a(e)?this.getter=e:(this.getter=function(t){if(!V.test(t)){var e=t.split(".");return function(t){for(var n=0;n-1)if(i&&!_(o,"default"))s=!1;else if(""===s||s===k(t)){var u=wr(String,o.type);(u<0||c-1:"string"==typeof t?t.split(",").indexOf(n)>-1:(r=t,"[object RegExp]"===c.call(r)&&t.test(n));var r}function Or(t,e){var n=t.cache,r=t.keys,o=t._vnode;for(var i in n){var a=n[i];if(a){var s=a.name;s&&!e(s)&&Tr(n,i,r,o)}}}function Tr(t,e,n,r){var o=t[e];!o||r&&o.tag===r.tag||o.componentInstance.$destroy(),t[e]=null,g(n,e)}!function(e){e.prototype._init=function(e){var n=this;n._uid=Qn++,n._isVue=!0,n.__v_skip=!0,n._scope=new cn(!0),e&&e._isComponent?function(t,e){var n=t.$options=Object.create(t.constructor.options),r=e._parentVnode;n.parent=e.parent,n._parentVnode=r;var o=r.componentOptions;n.propsData=o.propsData,n._parentListeners=o.listeners,n._renderChildren=o.children,n._componentTag=o.tag,e.render&&(n.render=e.render,n.staticRenderFns=e.staticRenderFns)}(n,e):n.$options=mr(tr(n.constructor),e||{},n),n._renderProxy=n,n._self=n,function(t){var e=t.$options,n=e.parent;if(n&&!e.abstract){for(;n.$options.abstract&&n.$parent;)n=n.$parent;n.$children.push(t)}t.$parent=n,t.$root=n?n.$root:t,t.$children=[],t.$refs={},t._provided=n?n._provided:Object.create(null),t._watcher=null,t._inactive=null,t._directInactive=!1,t._isMounted=!1,t._isDestroyed=!1,t._isBeingDestroyed=!1}(n),function(t){t._events=Object.create(null),t._hasHookEvent=!1;var e=t.$options._parentListeners;e&&Pe(t,e)}(n),function(e){e._vnode=null,e._staticTrees=null;var n=e.$options,r=e.$vnode=n._parentVnode,o=r&&r.context;e.$slots=me(n._renderChildren,o),e.$scopedSlots=r?_e(e.$parent,r.data.scopedSlots,e.$slots):t,e._c=function(t,n,r,o){return Qt(e,t,n,r,o,!1)},e.$createElement=function(t,n,r,o){return Qt(e,t,n,r,o,!0)};var i=r&&r.data;Tt(e,"$attrs",i&&i.attrs||t,null,!0),Tt(e,"$listeners",n._parentListeners||t,null,!0)}(n),He(n,"beforeCreate",void 0,!1),function(t){var e=Yn(t.$options.inject,t);e&&(Ct(!1),Object.keys(e).forEach((function(n){Tt(t,n,e[n])})),Ct(!0))}(n),Jn(n),function(t){var e=t.$options.provide;if(e){var n=a(e)?e.call(t):e;if(!s(n))return;for(var r=un(t),o=st?Reflect.ownKeys(n):Object.keys(n),i=0;i1?O(n):n;for(var r=O(arguments,1),o='event handler for "'.concat(t,'"'),i=0,a=n.length;iparseInt(this.max)&&Tr(e,n[0],n,this._vnode),this.vnodeToCache=null}}},created:function(){this.cache=Object.create(null),this.keys=[]},destroyed:function(){for(var t in this.cache)Tr(this.cache,t,this.keys)},mounted:function(){var t=this;this.cacheVNode(),this.$watch("include",(function(e){Or(t,(function(t){return Sr(e,t)}))})),this.$watch("exclude",(function(e){Or(t,(function(t){return!Sr(e,t)}))}))},updated:function(){this.cacheVNode()},render:function(){var t=this.$slots.default,e=je(t),n=e&&e.componentOptions;if(n){var r=kr(n),o=this.include,i=this.exclude;if(o&&(!r||!Sr(o,r))||i&&r&&Sr(i,r))return e;var a=this.cache,s=this.keys,c=null==e.key?n.Ctor.cid+(n.tag?"::".concat(n.tag):""):e.key;a[c]?(e.componentInstance=a[c].componentInstance,g(s,c),s.push(c)):(this.vnodeToCache=e,this.keyToCache=c),e.data.keepAlive=!0}return e||t&&t[0]}},Er={KeepAlive:jr};!function(t){var e={get:function(){return H}};Object.defineProperty(t,"config",e),t.util={warn:ur,extend:T,mergeOptions:mr,defineReactive:Tt},t.set=At,t.delete=jt,t.nextTick=xn,t.observable=function(t){return Ot(t),t},t.options=Object.create(null),R.forEach((function(e){t.options[e+"s"]=Object.create(null)})),t.options._base=t,T(t.options.components,Er),function(t){t.use=function(t){var e=this._installedPlugins||(this._installedPlugins=[]);if(e.indexOf(t)>-1)return this;var n=O(arguments,1);return n.unshift(this),a(t.install)?t.install.apply(t,n):a(t)&&t.apply(null,n),e.push(t),this}}(t),function(t){t.mixin=function(t){return this.options=mr(this.options,t),this}}(t),Cr(t),function(t){R.forEach((function(e){t[e]=function(t,n){return n?("component"===e&&u(n)&&(n.name=n.name||t,n=this.options._base.extend(n)),"directive"===e&&a(n)&&(n={bind:n,update:n}),this.options[e+"s"][t]=n,n):this.options[e+"s"][t]}}))}(t)}(xr),Object.defineProperty(xr.prototype,"$isServer",{get:rt}),Object.defineProperty(xr.prototype,"$ssrContext",{get:function(){return this.$vnode&&this.$vnode.ssrContext}}),Object.defineProperty(xr,"FunctionalRenderContext",{value:er}),xr.version=Ln;var Nr=v("style,class"),Dr=v("input,textarea,option,select,progress"),Pr=function(t,e,n){return"value"===n&&Dr(t)&&"button"!==e||"selected"===n&&"option"===t||"checked"===n&&"input"===t||"muted"===n&&"video"===t},Mr=v("contenteditable,draggable,spellcheck"),Ir=v("events,caret,typing,plaintext-only"),Lr=v("allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,default,defaultchecked,defaultmuted,defaultselected,defer,disabled,enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,required,reversed,scoped,seamless,selected,sortable,truespeed,typemustmatch,visible"),Rr="http://www.w3.org/1999/xlink",Fr=function(t){return":"===t.charAt(5)&&"xlink"===t.slice(0,5)},Hr=function(t){return Fr(t)?t.slice(6,t.length):""},Br=function(t){return null==t||!1===t};function Ur(t){for(var e=t.data,n=t,o=t;r(o.componentInstance);)(o=o.componentInstance._vnode)&&o.data&&(e=zr(o.data,e));for(;r(n=n.parent);)n&&n.data&&(e=zr(e,n.data));return function(t,e){if(r(t)||r(e))return Vr(t,Kr(e));return""}(e.staticClass,e.class)}function zr(t,e){return{staticClass:Vr(t.staticClass,e.staticClass),class:r(t.class)?[t.class,e.class]:e.class}}function Vr(t,e){return t?e?t+" "+e:t:e||""}function Kr(t){return Array.isArray(t)?function(t){for(var e,n="",o=0,i=t.length;o-1?yo(t,e,n):Lr(e)?Br(n)?t.removeAttribute(e):(n="allowfullscreen"===e&&"EMBED"===t.tagName?"true":e,t.setAttribute(e,n)):Mr(e)?t.setAttribute(e,function(t,e){return Br(e)||"false"===e?"false":"contenteditable"===t&&Ir(e)?e:"true"}(e,n)):Fr(e)?Br(n)?t.removeAttributeNS(Rr,Hr(e)):t.setAttributeNS(Rr,e,n):yo(t,e,n)}function yo(t,e,n){if(Br(n))t.removeAttribute(e);else{if(W&&!Z&&"TEXTAREA"===t.tagName&&"placeholder"===e&&""!==n&&!t.__ieph){var r=function(e){e.stopImmediatePropagation(),t.removeEventListener("input",r)};t.addEventListener("input",r),t.__ieph=!0}t.setAttribute(e,n)}}var _o={create:mo,update:mo};function bo(t,e){var o=e.elm,i=e.data,a=t.data;if(!(n(i.staticClass)&&n(i.class)&&(n(a)||n(a.staticClass)&&n(a.class)))){var s=Ur(e),c=o._transitionClasses;r(c)&&(s=Vr(s,Kr(c))),s!==o._prevClass&&(o.setAttribute("class",s),o._prevClass=s)}}var $o,wo,xo,Co,ko,So,Oo={create:bo,update:bo},To=/[\w).+\-_$\]]/;function Ao(t){var e,n,r,o,i,a=!1,s=!1,c=!1,u=!1,l=0,f=0,d=0,p=0;for(r=0;r=0&&" "===(h=t.charAt(v));v--);h&&To.test(h)||(u=!0)}}else void 0===o?(p=r+1,o=t.slice(0,r).trim()):m();function m(){(i||(i=[])).push(t.slice(p,r).trim()),p=r+1}if(void 0===o?o=t.slice(0,r).trim():0!==p&&m(),i)for(r=0;r-1?{exp:t.slice(0,Co),key:'"'+t.slice(Co+1)+'"'}:{exp:t,key:null};wo=t,Co=ko=So=0;for(;!Jo();)qo(xo=Ko())?Zo(xo):91===xo&&Wo(xo);return{exp:t.slice(0,ko),key:t.slice(ko+1,So)}}(t);return null===n.key?"".concat(t,"=").concat(e):"$set(".concat(n.exp,", ").concat(n.key,", ").concat(e,")")}function Ko(){return wo.charCodeAt(++Co)}function Jo(){return Co>=$o}function qo(t){return 34===t||39===t}function Wo(t){var e=1;for(ko=Co;!Jo();)if(qo(t=Ko()))Zo(t);else if(91===t&&e++,93===t&&e--,0===e){So=Co;break}}function Zo(t){for(var e=t;!Jo()&&(t=Ko())!==e;);}var Go,Xo="__r";function Yo(t,e,n){var r=Go;return function o(){var i=e.apply(null,arguments);null!==i&&ei(t,o,n,r)}}var Qo=hn&&!(Q&&Number(Q[1])<=53);function ti(t,e,n,r){if(Qo){var o=qe,i=e;e=i._wrapper=function(t){if(t.target===t.currentTarget||t.timeStamp>=o||t.timeStamp<=0||t.target.ownerDocument!==document)return i.apply(this,arguments)}}Go.addEventListener(t,e,et?{capture:n,passive:r}:n)}function ei(t,e,n,r){(r||Go).removeEventListener(t,e._wrapper||e,n)}function ni(t,e){if(!n(t.data.on)||!n(e.data.on)){var o=e.data.on||{},i=t.data.on||{};Go=e.elm||t.elm,function(t){if(r(t.__r)){var e=W?"change":"input";t[e]=[].concat(t.__r,t[e]||[]),delete t.__r}r(t.__c)&&(t.change=[].concat(t.__c,t.change||[]),delete t.__c)}(o),qt(o,i,ti,ei,Yo,e.context),Go=void 0}}var ri,oi={create:ni,update:ni,destroy:function(t){return ni(t,oo)}};function ii(t,e){if(!n(t.data.domProps)||!n(e.data.domProps)){var i,a,s=e.elm,c=t.data.domProps||{},u=e.data.domProps||{};for(i in(r(u.__ob__)||o(u._v_attr_proxy))&&(u=e.data.domProps=T({},u)),c)i in u||(s[i]="");for(i in u){if(a=u[i],"textContent"===i||"innerHTML"===i){if(e.children&&(e.children.length=0),a===c[i])continue;1===s.childNodes.length&&s.removeChild(s.childNodes[0])}if("value"===i&&"PROGRESS"!==s.tagName){s._value=a;var l=n(a)?"":String(a);ai(s,l)&&(s.value=l)}else if("innerHTML"===i&&Wr(s.tagName)&&n(s.innerHTML)){(ri=ri||document.createElement("div")).innerHTML="".concat(a,"");for(var f=ri.firstChild;s.firstChild;)s.removeChild(s.firstChild);for(;f.firstChild;)s.appendChild(f.firstChild)}else if(a!==c[i])try{s[i]=a}catch(t){}}}}function ai(t,e){return!t.composing&&("OPTION"===t.tagName||function(t,e){var n=!0;try{n=document.activeElement!==t}catch(t){}return n&&t.value!==e}(t,e)||function(t,e){var n=t.value,o=t._vModifiers;if(r(o)){if(o.number)return p(n)!==p(e);if(o.trim)return n.trim()!==e.trim()}return n!==e}(t,e))}var si={create:ii,update:ii},ci=b((function(t){var e={},n=/:(.+)/;return t.split(/;(?![^(]*\))/g).forEach((function(t){if(t){var r=t.split(n);r.length>1&&(e[r[0].trim()]=r[1].trim())}})),e}));function ui(t){var e=li(t.style);return t.staticStyle?T(t.staticStyle,e):e}function li(t){return Array.isArray(t)?A(t):"string"==typeof t?ci(t):t}var fi,di=/^--/,pi=/\s*!important$/,vi=function(t,e,n){if(di.test(e))t.style.setProperty(e,n);else if(pi.test(n))t.style.setProperty(k(e),n.replace(pi,""),"important");else{var r=mi(e);if(Array.isArray(n))for(var o=0,i=n.length;o-1?e.split(_i).forEach((function(e){return t.classList.add(e)})):t.classList.add(e);else{var n=" ".concat(t.getAttribute("class")||""," ");n.indexOf(" "+e+" ")<0&&t.setAttribute("class",(n+e).trim())}}function $i(t,e){if(e&&(e=e.trim()))if(t.classList)e.indexOf(" ")>-1?e.split(_i).forEach((function(e){return t.classList.remove(e)})):t.classList.remove(e),t.classList.length||t.removeAttribute("class");else{for(var n=" ".concat(t.getAttribute("class")||""," "),r=" "+e+" ";n.indexOf(r)>=0;)n=n.replace(r," ");(n=n.trim())?t.setAttribute("class",n):t.removeAttribute("class")}}function wi(t){if(t){if("object"==typeof t){var e={};return!1!==t.css&&T(e,xi(t.name||"v")),T(e,t),e}return"string"==typeof t?xi(t):void 0}}var xi=b((function(t){return{enterClass:"".concat(t,"-enter"),enterToClass:"".concat(t,"-enter-to"),enterActiveClass:"".concat(t,"-enter-active"),leaveClass:"".concat(t,"-leave"),leaveToClass:"".concat(t,"-leave-to"),leaveActiveClass:"".concat(t,"-leave-active")}})),Ci=J&&!Z,ki="transition",Si="animation",Oi="transition",Ti="transitionend",Ai="animation",ji="animationend";Ci&&(void 0===window.ontransitionend&&void 0!==window.onwebkittransitionend&&(Oi="WebkitTransition",Ti="webkitTransitionEnd"),void 0===window.onanimationend&&void 0!==window.onwebkitanimationend&&(Ai="WebkitAnimation",ji="webkitAnimationEnd"));var Ei=J?window.requestAnimationFrame?window.requestAnimationFrame.bind(window):setTimeout:function(t){return t()};function Ni(t){Ei((function(){Ei(t)}))}function Di(t,e){var n=t._transitionClasses||(t._transitionClasses=[]);n.indexOf(e)<0&&(n.push(e),bi(t,e))}function Pi(t,e){t._transitionClasses&&g(t._transitionClasses,e),$i(t,e)}function Mi(t,e,n){var r=Li(t,e),o=r.type,i=r.timeout,a=r.propCount;if(!o)return n();var s=o===ki?Ti:ji,c=0,u=function(){t.removeEventListener(s,l),n()},l=function(e){e.target===t&&++c>=a&&u()};setTimeout((function(){c0&&(n=ki,l=a,f=i.length):e===Si?u>0&&(n=Si,l=u,f=c.length):f=(n=(l=Math.max(a,u))>0?a>u?ki:Si:null)?n===ki?i.length:c.length:0,{type:n,timeout:l,propCount:f,hasTransform:n===ki&&Ii.test(r[Oi+"Property"])}}function Ri(t,e){for(;t.length1}function Vi(t,e){!0!==e.data.show&&Hi(e)}var Ki=function(t){var a,s,c={},u=t.modules,l=t.nodeOps;for(a=0;av?b(t,n(o[g+1])?null:o[g+1].elm,o,p,g,i):p>g&&w(e,f,v)}(f,h,m,i,u):r(m)?(r(t.text)&&l.setTextContent(f,""),b(f,null,m,0,m.length-1,i)):r(h)?w(h,0,h.length-1):r(t.text)&&l.setTextContent(f,""):t.text!==e.text&&l.setTextContent(f,e.text),r(v)&&r(p=v.hook)&&r(p=p.postpatch)&&p(t,e)}}}function S(t,e,n){if(o(n)&&r(t.parent))t.parent.data.pendingInsert=e;else for(var i=0;i-1,a.selected!==i&&(a.selected=i);else if(D(Gi(a),r))return void(t.selectedIndex!==s&&(t.selectedIndex=s));o||(t.selectedIndex=-1)}}function Zi(t,e){return e.every((function(e){return!D(e,t)}))}function Gi(t){return"_value"in t?t._value:t.value}function Xi(t){t.target.composing=!0}function Yi(t){t.target.composing&&(t.target.composing=!1,Qi(t.target,"input"))}function Qi(t,e){var n=document.createEvent("HTMLEvents");n.initEvent(e,!0,!0),t.dispatchEvent(n)}function ta(t){return!t.componentInstance||t.data&&t.data.transition?t:ta(t.componentInstance._vnode)}var ea={bind:function(t,e,n){var r=e.value,o=(n=ta(n)).data&&n.data.transition,i=t.__vOriginalDisplay="none"===t.style.display?"":t.style.display;r&&o?(n.data.show=!0,Hi(n,(function(){t.style.display=i}))):t.style.display=r?i:"none"},update:function(t,e,n){var r=e.value;!r!=!e.oldValue&&((n=ta(n)).data&&n.data.transition?(n.data.show=!0,r?Hi(n,(function(){t.style.display=t.__vOriginalDisplay})):Bi(n,(function(){t.style.display="none"}))):t.style.display=r?t.__vOriginalDisplay:"none")},unbind:function(t,e,n,r,o){o||(t.style.display=t.__vOriginalDisplay)}},na={model:Ji,show:ea},ra={name:String,appear:Boolean,css:Boolean,mode:String,type:String,enterClass:String,leaveClass:String,enterToClass:String,leaveToClass:String,enterActiveClass:String,leaveActiveClass:String,appearClass:String,appearActiveClass:String,appearToClass:String,duration:[Number,String,Object]};function oa(t){var e=t&&t.componentOptions;return e&&e.Ctor.options.abstract?oa(je(e.children)):t}function ia(t){var e={},n=t.$options;for(var r in n.propsData)e[r]=t[r];var o=n._parentListeners;for(var r in o)e[w(r)]=o[r];return e}function aa(t,e){if(/\d-keep-alive$/.test(e.tag))return t("keep-alive",{props:e.componentOptions.propsData})}var sa=function(t){return t.tag||ye(t)},ca=function(t){return"show"===t.name},ua={name:"transition",props:ra,abstract:!0,render:function(t){var e=this,n=this.$slots.default;if(n&&(n=n.filter(sa)).length){var r=this.mode,o=n[0];if(function(t){for(;t=t.parent;)if(t.data.transition)return!0}(this.$vnode))return o;var a=oa(o);if(!a)return o;if(this._leaving)return aa(t,o);var s="__transition-".concat(this._uid,"-");a.key=null==a.key?a.isComment?s+"comment":s+a.tag:i(a.key)?0===String(a.key).indexOf(s)?a.key:s+a.key:a.key;var c=(a.data||(a.data={})).transition=ia(this),u=this._vnode,l=oa(u);if(a.data.directives&&a.data.directives.some(ca)&&(a.data.show=!0),l&&l.data&&!function(t,e){return e.key===t.key&&e.tag===t.tag}(a,l)&&!ye(l)&&(!l.componentInstance||!l.componentInstance._vnode.isComment)){var f=l.data.transition=T({},c);if("out-in"===r)return this._leaving=!0,Wt(f,"afterLeave",(function(){e._leaving=!1,e.$forceUpdate()})),aa(t,o);if("in-out"===r){if(ye(a))return u;var d,p=function(){d()};Wt(c,"afterEnter",p),Wt(c,"enterCancelled",p),Wt(f,"delayLeave",(function(t){d=t}))}}return o}}},la=T({tag:String,moveClass:String},ra);delete la.mode;var fa={props:la,beforeMount:function(){var t=this,e=this._update;this._update=function(n,r){var o=Ie(t);t.__patch__(t._vnode,t.kept,!1,!0),t._vnode=t.kept,o(),e.call(t,n,r)}},render:function(t){for(var e=this.tag||this.$vnode.data.tag||"span",n=Object.create(null),r=this.prevChildren=this.children,o=this.$slots.default||[],i=this.children=[],a=ia(this),s=0;s-1?Xr[t]=e.constructor===window.HTMLUnknownElement||e.constructor===window.HTMLElement:Xr[t]=/HTMLUnknownElement/.test(e.toString())},T(xr.options.directives,na),T(xr.options.components,ha),xr.prototype.__patch__=J?Ki:j,xr.prototype.$mount=function(t,e){return function(t,e,n){var r;t.$el=e,t.$options.render||(t.$options.render=ft),He(t,"beforeMount"),r=function(){t._update(t._render(),n)},new zn(t,r,j,{before:function(){t._isMounted&&!t._isDestroyed&&He(t,"beforeUpdate")}},!0),n=!1;var o=t._preWatchers;if(o)for(var i=0;i\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/,Oa=/^\s*((?:v-[\w-]+:|@|:|#)\[[^=]+?\][^\s"'<>\/=]*)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/,Ta="[a-zA-Z_][\\-\\.0-9_a-zA-Z".concat(B.source,"]*"),Aa="((?:".concat(Ta,"\\:)?").concat(Ta,")"),ja=new RegExp("^<".concat(Aa)),Ea=/^\s*(\/?)>/,Na=new RegExp("^<\\/".concat(Aa,"[^>]*>")),Da=/^]+>/i,Pa=/^",""":'"',"&":"&"," ":"\n"," ":"\t","'":"'"},Fa=/&(?:lt|gt|quot|amp|#39);/g,Ha=/&(?:lt|gt|quot|amp|#39|#10|#9);/g,Ba=v("pre,textarea",!0),Ua=function(t,e){return t&&Ba(t)&&"\n"===e[0]};function za(t,e){var n=e?Ha:Fa;return t.replace(n,(function(t){return Ra[t]}))}function Va(t,e){for(var n,r,o=[],i=e.expectHTML,a=e.isUnaryTag||E,s=e.canBeLeftOpenTag||E,c=0,u=function(){if(n=t,r&&Ia(r)){var u=0,d=r.toLowerCase(),p=La[d]||(La[d]=new RegExp("([\\s\\S]*?)(]*>)","i"));w=t.replace(p,(function(t,n,r){return u=r.length,Ia(d)||"noscript"===d||(n=n.replace(//g,"$1").replace(//g,"$1")),Ua(d,n)&&(n=n.slice(1)),e.chars&&e.chars(n),""}));c+=t.length-w.length,t=w,f(d,c-u,c)}else{var v=t.indexOf("<");if(0===v){if(Pa.test(t)){var h=t.indexOf("--\x3e");if(h>=0)return e.shouldKeepComment&&e.comment&&e.comment(t.substring(4,h),c,c+h+3),l(h+3),"continue"}if(Ma.test(t)){var m=t.indexOf("]>");if(m>=0)return l(m+2),"continue"}var g=t.match(Da);if(g)return l(g[0].length),"continue";var y=t.match(Na);if(y){var _=c;return l(y[0].length),f(y[1],_,c),"continue"}var b=function(){var e=t.match(ja);if(e){var n={tagName:e[1],attrs:[],start:c};l(e[0].length);for(var r=void 0,o=void 0;!(r=t.match(Ea))&&(o=t.match(Oa)||t.match(Sa));)o.start=c,l(o[0].length),o.end=c,n.attrs.push(o);if(r)return n.unarySlash=r[1],l(r[0].length),n.end=c,n}}();if(b)return function(t){var n=t.tagName,c=t.unarySlash;i&&("p"===r&&ka(n)&&f(r),s(n)&&r===n&&f(n));for(var u=a(n)||!!c,l=t.attrs.length,d=new Array(l),p=0;p=0){for(w=t.slice(v);!(Na.test(w)||ja.test(w)||Pa.test(w)||Ma.test(w)||(x=w.indexOf("<",1))<0);)v+=x,w=t.slice(v);$=t.substring(0,v)}v<0&&($=t),$&&l($.length),e.chars&&$&&e.chars($,c-$.length,c)}if(t===n)return e.chars&&e.chars(t),"break"};t;){if("break"===u())break}function l(e){c+=e,t=t.substring(e)}function f(t,n,i){var a,s;if(null==n&&(n=c),null==i&&(i=c),t)for(s=t.toLowerCase(),a=o.length-1;a>=0&&o[a].lowerCasedTag!==s;a--);else a=0;if(a>=0){for(var u=o.length-1;u>=a;u--)e.end&&e.end(o[u].tag,n,i);o.length=a,r=a&&o[a-1].tag}else"br"===s?e.start&&e.start(t,[],!0,n,i):"p"===s&&(e.start&&e.start(t,[],!1,n,i),e.end&&e.end(t,n,i))}f()}var Ka,Ja,qa,Wa,Za,Ga,Xa,Ya,Qa=/^@|^v-on:/,ts=/^v-|^@|^:|^#/,es=/([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/,ns=/,([^,\}\]]*)(?:,([^,\}\]]*))?$/,rs=/^\(|\)$/g,os=/^\[.*\]$/,is=/:(.*)$/,as=/^:|^\.|^v-bind:/,ss=/\.[^.\]]+(?=[^\]]*$)/g,cs=/^v-slot(:|$)|^#/,us=/[\r\n]/,ls=/[ \f\t\r\n]+/g,fs=b(wa),ds="_empty_";function ps(t,e,n){return{type:1,tag:t,attrsList:e,attrsMap:bs(e),rawAttrsMap:{},parent:n,children:[]}}function vs(t,e){Ka=e.warn||Eo,Ga=e.isPreTag||E,Xa=e.mustUseProp||E,Ya=e.getTagNamespace||E,e.isReservedTag,qa=No(e.modules,"transformNode"),Wa=No(e.modules,"preTransformNode"),Za=No(e.modules,"postTransformNode"),Ja=e.delimiters;var n,r,o=[],i=!1!==e.preserveWhitespace,a=e.whitespace,s=!1,c=!1;function u(t){if(l(t),s||t.processed||(t=hs(t,e)),o.length||t===n||n.if&&(t.elseif||t.else)&&gs(n,{exp:t.elseif,block:t}),r&&!t.forbidden)if(t.elseif||t.else)a=t,u=function(t){for(var e=t.length;e--;){if(1===t[e].type)return t[e];t.pop()}}(r.children),u&&u.if&&gs(u,{exp:a.elseif,block:a});else{if(t.slotScope){var i=t.slotTarget||'"default"';(r.scopedSlots||(r.scopedSlots={}))[i]=t}r.children.push(t),t.parent=r}var a,u;t.children=t.children.filter((function(t){return!t.slotScope})),l(t),t.pre&&(s=!1),Ga(t.tag)&&(c=!1);for(var f=0;fc&&(s.push(i=t.slice(c,o)),a.push(JSON.stringify(i)));var u=Ao(r[1].trim());a.push("_s(".concat(u,")")),s.push({"@binding":u}),c=o+r[0].length}return c-1")+("true"===i?":(".concat(e,")"):":_q(".concat(e,",").concat(i,")"))),Ro(t,"change","var $$a=".concat(e,",")+"$$el=$event.target,"+"$$c=$$el.checked?(".concat(i,"):(").concat(a,");")+"if(Array.isArray($$a)){"+"var $$v=".concat(r?"_n("+o+")":o,",")+"$$i=_i($$a,$$v);"+"if($$el.checked){$$i<0&&(".concat(Vo(e,"$$a.concat([$$v])"),")}")+"else{$$i>-1&&(".concat(Vo(e,"$$a.slice(0,$$i).concat($$a.slice($$i+1))"),")}")+"}else{".concat(Vo(e,"$$c"),"}"),null,!0)}(t,r,o);else if("input"===i&&"radio"===a)!function(t,e,n){var r=n&&n.number,o=Fo(t,"value")||"null";o=r?"_n(".concat(o,")"):o,Do(t,"checked","_q(".concat(e,",").concat(o,")")),Ro(t,"change",Vo(e,o),null,!0)}(t,r,o);else if("input"===i||"textarea"===i)!function(t,e,n){var r=t.attrsMap.type,o=n||{},i=o.lazy,a=o.number,s=o.trim,c=!i&&"range"!==r,u=i?"change":"range"===r?Xo:"input",l="$event.target.value";s&&(l="$event.target.value.trim()");a&&(l="_n(".concat(l,")"));var f=Vo(e,l);c&&(f="if($event.target.composing)return;".concat(f));Do(t,"value","(".concat(e,")")),Ro(t,u,f,null,!0),(s||a)&&Ro(t,"blur","$forceUpdate()")}(t,r,o);else if(!H.isReservedTag(i))return zo(t,r,o),!1;return!0},text:function(t,e){e.value&&Do(t,"textContent","_s(".concat(e.value,")"),e)},html:function(t,e){e.value&&Do(t,"innerHTML","_s(".concat(e.value,")"),e)}},Ts={expectHTML:!0,modules:Cs,directives:Os,isPreTag:function(t){return"pre"===t},isUnaryTag:xa,mustUseProp:Pr,canBeLeftOpenTag:Ca,isReservedTag:Zr,getTagNamespace:Gr,staticKeys:function(t){return t.reduce((function(t,e){return t.concat(e.staticKeys||[])}),[]).join(",")}(Cs)},As=b((function(t){return v("type,tag,attrsList,attrsMap,plain,parent,children,attrs,start,end,rawAttrsMap"+(t?","+t:""))}));function js(t,e){t&&(ks=As(e.staticKeys||""),Ss=e.isReservedTag||E,Es(t),Ns(t,!1))}function Es(t){if(t.static=function(t){if(2===t.type)return!1;if(3===t.type)return!0;return!(!t.pre&&(t.hasBindings||t.if||t.for||h(t.tag)||!Ss(t.tag)||function(t){for(;t.parent;){if("template"!==(t=t.parent).tag)return!1;if(t.for)return!0}return!1}(t)||!Object.keys(t).every(ks)))}(t),1===t.type){if(!Ss(t.tag)&&"slot"!==t.tag&&null==t.attrsMap["inline-template"])return;for(var e=0,n=t.children.length;e|^function(?:\s+[\w$]+)?\s*\(/,Ps=/\([^)]*?\);*$/,Ms=/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['[^']*?']|\["[^"]*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*$/,Is={esc:27,tab:9,enter:13,space:32,up:38,left:37,right:39,down:40,delete:[8,46]},Ls={esc:["Esc","Escape"],tab:"Tab",enter:"Enter",space:[" ","Spacebar"],up:["Up","ArrowUp"],left:["Left","ArrowLeft"],right:["Right","ArrowRight"],down:["Down","ArrowDown"],delete:["Backspace","Delete","Del"]},Rs=function(t){return"if(".concat(t,")return null;")},Fs={stop:"$event.stopPropagation();",prevent:"$event.preventDefault();",self:Rs("$event.target !== $event.currentTarget"),ctrl:Rs("!$event.ctrlKey"),shift:Rs("!$event.shiftKey"),alt:Rs("!$event.altKey"),meta:Rs("!$event.metaKey"),left:Rs("'button' in $event && $event.button !== 0"),middle:Rs("'button' in $event && $event.button !== 1"),right:Rs("'button' in $event && $event.button !== 2")};function Hs(t,e){var n=e?"nativeOn:":"on:",r="",o="";for(var i in t){var a=Bs(t[i]);t[i]&&t[i].dynamic?o+="".concat(i,",").concat(a,","):r+='"'.concat(i,'":').concat(a,",")}return r="{".concat(r.slice(0,-1),"}"),o?n+"_d(".concat(r,",[").concat(o.slice(0,-1),"])"):n+r}function Bs(t){if(!t)return"function(){}";if(Array.isArray(t))return"[".concat(t.map((function(t){return Bs(t)})).join(","),"]");var e=Ms.test(t.value),n=Ds.test(t.value),r=Ms.test(t.value.replace(Ps,""));if(t.modifiers){var o="",i="",a=[],s=function(e){if(Fs[e])i+=Fs[e],Is[e]&&a.push(e);else if("exact"===e){var n=t.modifiers;i+=Rs(["ctrl","shift","alt","meta"].filter((function(t){return!n[t]})).map((function(t){return"$event.".concat(t,"Key")})).join("||"))}else a.push(e)};for(var c in t.modifiers)s(c);a.length&&(o+=function(t){return"if(!$event.type.indexOf('key')&&"+"".concat(t.map(Us).join("&&"),")return null;")}(a)),i&&(o+=i);var u=e?"return ".concat(t.value,".apply(null, arguments)"):n?"return (".concat(t.value,").apply(null, arguments)"):r?"return ".concat(t.value):t.value;return"function($event){".concat(o).concat(u,"}")}return e||n?t.value:"function($event){".concat(r?"return ".concat(t.value):t.value,"}")}function Us(t){var e=parseInt(t,10);if(e)return"$event.keyCode!==".concat(e);var n=Is[t],r=Ls[t];return"_k($event.keyCode,"+"".concat(JSON.stringify(t),",")+"".concat(JSON.stringify(n),",")+"$event.key,"+"".concat(JSON.stringify(r))+")"}var zs={on:function(t,e){t.wrapListeners=function(t){return"_g(".concat(t,",").concat(e.value,")")}},bind:function(t,e){t.wrapData=function(n){return"_b(".concat(n,",'").concat(t.tag,"',").concat(e.value,",").concat(e.modifiers&&e.modifiers.prop?"true":"false").concat(e.modifiers&&e.modifiers.sync?",true":"",")")}},cloak:j},Vs=function(t){this.options=t,this.warn=t.warn||Eo,this.transforms=No(t.modules,"transformCode"),this.dataGenFns=No(t.modules,"genData"),this.directives=T(T({},zs),t.directives);var e=t.isReservedTag||E;this.maybeComponent=function(t){return!!t.component||!e(t.tag)},this.onceId=0,this.staticRenderFns=[],this.pre=!1};function Ks(t,e){var n=new Vs(e),r=t?"script"===t.tag?"null":Js(t,n):'_c("div")';return{render:"with(this){return ".concat(r,"}"),staticRenderFns:n.staticRenderFns}}function Js(t,e){if(t.parent&&(t.pre=t.pre||t.parent.pre),t.staticRoot&&!t.staticProcessed)return Ws(t,e);if(t.once&&!t.onceProcessed)return Zs(t,e);if(t.for&&!t.forProcessed)return Ys(t,e);if(t.if&&!t.ifProcessed)return Gs(t,e);if("template"!==t.tag||t.slotTarget||e.pre){if("slot"===t.tag)return function(t,e){var n=t.slotName||'"default"',r=nc(t,e),o="_t(".concat(n).concat(r?",function(){return ".concat(r,"}"):""),i=t.attrs||t.dynamicAttrs?ic((t.attrs||[]).concat(t.dynamicAttrs||[]).map((function(t){return{name:w(t.name),value:t.value,dynamic:t.dynamic}}))):null,a=t.attrsMap["v-bind"];!i&&!a||r||(o+=",null");i&&(o+=",".concat(i));a&&(o+="".concat(i?"":",null",",").concat(a));return o+")"}(t,e);var n=void 0;if(t.component)n=function(t,e,n){var r=e.inlineTemplate?null:nc(e,n,!0);return"_c(".concat(t,",").concat(Qs(e,n)).concat(r?",".concat(r):"",")")}(t.component,t,e);else{var r=void 0,o=e.maybeComponent(t);(!t.plain||t.pre&&o)&&(r=Qs(t,e));var i=void 0,a=e.options.bindings;o&&a&&!1!==a.__isScriptSetup&&(i=qs(a,t.tag)||qs(a,w(t.tag))||qs(a,x(w(t.tag)))),i||(i="'".concat(t.tag,"'"));var s=t.inlineTemplate?null:nc(t,e,!0);n="_c(".concat(i).concat(r?",".concat(r):"").concat(s?",".concat(s):"",")")}for(var c=0;c>>0}(a)):"",")")}(t,t.scopedSlots,e),",")),t.model&&(n+="model:{value:".concat(t.model.value,",callback:").concat(t.model.callback,",expression:").concat(t.model.expression,"},")),t.inlineTemplate){var i=function(t,e){var n=t.children[0];if(n&&1===n.type){var r=Ks(n,e.options);return"inlineTemplate:{render:function(){".concat(r.render,"},staticRenderFns:[").concat(r.staticRenderFns.map((function(t){return"function(){".concat(t,"}")})).join(","),"]}")}}(t,e);i&&(n+="".concat(i,","))}return n=n.replace(/,$/,"")+"}",t.dynamicAttrs&&(n="_b(".concat(n,',"').concat(t.tag,'",').concat(ic(t.dynamicAttrs),")")),t.wrapData&&(n=t.wrapData(n)),t.wrapListeners&&(n=t.wrapListeners(n)),n}function tc(t){return 1===t.type&&("slot"===t.tag||t.children.some(tc))}function ec(t,e){var n=t.attrsMap["slot-scope"];if(t.if&&!t.ifProcessed&&!n)return Gs(t,e,ec,"null");if(t.for&&!t.forProcessed)return Ys(t,e,ec);var r=t.slotScope===ds?"":String(t.slotScope),o="function(".concat(r,"){")+"return ".concat("template"===t.tag?t.if&&n?"(".concat(t.if,")?").concat(nc(t,e)||"undefined",":undefined"):nc(t,e)||"undefined":Js(t,e),"}"),i=r?"":",proxy:true";return"{key:".concat(t.slotTarget||'"default"',",fn:").concat(o).concat(i,"}")}function nc(t,e,n,r,o){var i=t.children;if(i.length){var a=i[0];if(1===i.length&&a.for&&"template"!==a.tag&&"slot"!==a.tag){var s=n?e.maybeComponent(a)?",1":",0":"";return"".concat((r||Js)(a,e)).concat(s)}var c=n?function(t,e){for(var n=0,r=0;r':'
    ',lc.innerHTML.indexOf(" ")>0}var vc=!!J&&pc(!1),hc=!!J&&pc(!0),mc=b((function(t){var e=Qr(t);return e&&e.innerHTML})),gc=xr.prototype.$mount;return xr.prototype.$mount=function(t,e){if((t=t&&Qr(t))===document.body||t===document.documentElement)return this;var n=this.$options;if(!n.render){var r=n.template;if(r)if("string"==typeof r)"#"===r.charAt(0)&&(r=mc(r));else{if(!r.nodeType)return this;r=r.innerHTML}else t&&(r=function(t){if(t.outerHTML)return t.outerHTML;var e=document.createElement("div");return e.appendChild(t.cloneNode(!0)),e.innerHTML}(t));if(r){var o=dc(r,{outputSourceRange:!1,shouldDecodeNewlines:vc,shouldDecodeNewlinesForHref:hc,delimiters:n.delimiters,comments:n.comments},this),i=o.render,a=o.staticRenderFns;n.render=i,n.staticRenderFns=a}}return gc.call(this,t,e)},xr.compile=dc,T(xr,Rn),xr.effect=function(t,e){var n=new zn(ct,t,j,{sync:!0});e&&(n.update=function(){e((function(){return n.run()}))})},xr})); \ No newline at end of file diff --git a/wp-content/plugins/facetwp/assets/vendor/vue/vuedraggable.min.js b/wp-content/plugins/facetwp/assets/vendor/vue/vuedraggable.min.js new file mode 100644 index 000000000..c863726e6 --- /dev/null +++ b/wp-content/plugins/facetwp/assets/vendor/vue/vuedraggable.min.js @@ -0,0 +1,2 @@ +/* Vue.Draggable 2.24.3 */ +(function(t,e){"object"===typeof exports&&"object"===typeof module?module.exports=e(require("sortablejs")):"function"===typeof define&&define.amd?define(["sortablejs"],e):"object"===typeof exports?exports["vuedraggable"]=e(require("sortablejs")):t["vuedraggable"]=e(t["Sortable"])})("undefined"!==typeof self?self:this,(function(t){return function(t){var e={};function n(r){if(e[r])return e[r].exports;var o=e[r]={i:r,l:!1,exports:{}};return t[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=t,n.c=e,n.d=function(t,e,r){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},n.r=function(t){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"===typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)n.d(r,o,function(e){return t[e]}.bind(null,o));return r},n.n=function(t){var e=t&&t.__esModule?function(){return t["default"]}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s="fb15")}({"01f9":function(t,e,n){"use strict";var r=n("2d00"),o=n("5ca1"),i=n("2aba"),c=n("32e9"),u=n("84f2"),a=n("41a0"),s=n("7f20"),f=n("38fd"),l=n("2b4c")("iterator"),d=!([].keys&&"next"in[].keys()),p="@@iterator",h="keys",v="values",g=function(){return this};t.exports=function(t,e,n,b,m,y,x){a(n,e,b);var O,S,w,j=function(t){if(!d&&t in _)return _[t];switch(t){case h:return function(){return new n(this,t)};case v:return function(){return new n(this,t)}}return function(){return new n(this,t)}},M=e+" Iterator",C=m==v,T=!1,_=t.prototype,L=_[l]||_[p]||m&&_[m],I=L||j(m),E=m?C?j("entries"):I:void 0,P="Array"==e&&_.entries||L;if(P&&(w=f(P.call(new t)),w!==Object.prototype&&w.next&&(s(w,M,!0),r||"function"==typeof w[l]||c(w,l,g))),C&&L&&L.name!==v&&(T=!0,I=function(){return L.call(this)}),r&&!x||!d&&!T&&_[l]||c(_,l,I),u[e]=I,u[M]=g,m)if(O={values:C?I:j(v),keys:y?I:j(h),entries:E},x)for(S in O)S in _||i(_,S,O[S]);else o(o.P+o.F*(d||T),e,O);return O}},"02f4":function(t,e,n){var r=n("4588"),o=n("be13");t.exports=function(t){return function(e,n){var i,c,u=String(o(e)),a=r(n),s=u.length;return a<0||a>=s?t?"":void 0:(i=u.charCodeAt(a),i<55296||i>56319||a+1===s||(c=u.charCodeAt(a+1))<56320||c>57343?t?u.charAt(a):i:t?u.slice(a,a+2):c-56320+(i-55296<<10)+65536)}}},"0390":function(t,e,n){"use strict";var r=n("02f4")(!0);t.exports=function(t,e,n){return e+(n?r(t,e).length:1)}},"0bfb":function(t,e,n){"use strict";var r=n("cb7c");t.exports=function(){var t=r(this),e="";return t.global&&(e+="g"),t.ignoreCase&&(e+="i"),t.multiline&&(e+="m"),t.unicode&&(e+="u"),t.sticky&&(e+="y"),e}},"0d58":function(t,e,n){var r=n("ce10"),o=n("e11e");t.exports=Object.keys||function(t){return r(t,o)}},1495:function(t,e,n){var r=n("86cc"),o=n("cb7c"),i=n("0d58");t.exports=n("9e1e")?Object.defineProperties:function(t,e){o(t);var n,c=i(e),u=c.length,a=0;while(u>a)r.f(t,n=c[a++],e[n]);return t}},"214f":function(t,e,n){"use strict";n("b0c5");var r=n("2aba"),o=n("32e9"),i=n("79e5"),c=n("be13"),u=n("2b4c"),a=n("520a"),s=u("species"),f=!i((function(){var t=/./;return t.exec=function(){var t=[];return t.groups={a:"7"},t},"7"!=="".replace(t,"$")})),l=function(){var t=/(?:)/,e=t.exec;t.exec=function(){return e.apply(this,arguments)};var n="ab".split(t);return 2===n.length&&"a"===n[0]&&"b"===n[1]}();t.exports=function(t,e,n){var d=u(t),p=!i((function(){var e={};return e[d]=function(){return 7},7!=""[t](e)})),h=p?!i((function(){var e=!1,n=/a/;return n.exec=function(){return e=!0,null},"split"===t&&(n.constructor={},n.constructor[s]=function(){return n}),n[d](""),!e})):void 0;if(!p||!h||"replace"===t&&!f||"split"===t&&!l){var v=/./[d],g=n(c,d,""[t],(function(t,e,n,r,o){return e.exec===a?p&&!o?{done:!0,value:v.call(e,n,r)}:{done:!0,value:t.call(n,e,r)}:{done:!1}})),b=g[0],m=g[1];r(String.prototype,t,b),o(RegExp.prototype,d,2==e?function(t,e){return m.call(t,this,e)}:function(t){return m.call(t,this)})}}},"230e":function(t,e,n){var r=n("d3f4"),o=n("7726").document,i=r(o)&&r(o.createElement);t.exports=function(t){return i?o.createElement(t):{}}},"23c6":function(t,e,n){var r=n("2d95"),o=n("2b4c")("toStringTag"),i="Arguments"==r(function(){return arguments}()),c=function(t,e){try{return t[e]}catch(n){}};t.exports=function(t){var e,n,u;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(n=c(e=Object(t),o))?n:i?r(e):"Object"==(u=r(e))&&"function"==typeof e.callee?"Arguments":u}},2621:function(t,e){e.f=Object.getOwnPropertySymbols},"2aba":function(t,e,n){var r=n("7726"),o=n("32e9"),i=n("69a8"),c=n("ca5a")("src"),u=n("fa5b"),a="toString",s=(""+u).split(a);n("8378").inspectSource=function(t){return u.call(t)},(t.exports=function(t,e,n,u){var a="function"==typeof n;a&&(i(n,"name")||o(n,"name",e)),t[e]!==n&&(a&&(i(n,c)||o(n,c,t[e]?""+t[e]:s.join(String(e)))),t===r?t[e]=n:u?t[e]?t[e]=n:o(t,e,n):(delete t[e],o(t,e,n)))})(Function.prototype,a,(function(){return"function"==typeof this&&this[c]||u.call(this)}))},"2aeb":function(t,e,n){var r=n("cb7c"),o=n("1495"),i=n("e11e"),c=n("613b")("IE_PROTO"),u=function(){},a="prototype",s=function(){var t,e=n("230e")("iframe"),r=i.length,o="<",c=">";e.style.display="none",n("fab2").appendChild(e),e.src="javascript:",t=e.contentWindow.document,t.open(),t.write(o+"script"+c+"document.F=Object"+o+"/script"+c),t.close(),s=t.F;while(r--)delete s[a][i[r]];return s()};t.exports=Object.create||function(t,e){var n;return null!==t?(u[a]=r(t),n=new u,u[a]=null,n[c]=t):n=s(),void 0===e?n:o(n,e)}},"2b4c":function(t,e,n){var r=n("5537")("wks"),o=n("ca5a"),i=n("7726").Symbol,c="function"==typeof i,u=t.exports=function(t){return r[t]||(r[t]=c&&i[t]||(c?i:o)("Symbol."+t))};u.store=r},"2d00":function(t,e){t.exports=!1},"2d95":function(t,e){var n={}.toString;t.exports=function(t){return n.call(t).slice(8,-1)}},"2fdb":function(t,e,n){"use strict";var r=n("5ca1"),o=n("d2c8"),i="includes";r(r.P+r.F*n("5147")(i),"String",{includes:function(t){return!!~o(this,t,i).indexOf(t,arguments.length>1?arguments[1]:void 0)}})},"32e9":function(t,e,n){var r=n("86cc"),o=n("4630");t.exports=n("9e1e")?function(t,e,n){return r.f(t,e,o(1,n))}:function(t,e,n){return t[e]=n,t}},"38fd":function(t,e,n){var r=n("69a8"),o=n("4bf8"),i=n("613b")("IE_PROTO"),c=Object.prototype;t.exports=Object.getPrototypeOf||function(t){return t=o(t),r(t,i)?t[i]:"function"==typeof t.constructor&&t instanceof t.constructor?t.constructor.prototype:t instanceof Object?c:null}},"41a0":function(t,e,n){"use strict";var r=n("2aeb"),o=n("4630"),i=n("7f20"),c={};n("32e9")(c,n("2b4c")("iterator"),(function(){return this})),t.exports=function(t,e,n){t.prototype=r(c,{next:o(1,n)}),i(t,e+" Iterator")}},"456d":function(t,e,n){var r=n("4bf8"),o=n("0d58");n("5eda")("keys",(function(){return function(t){return o(r(t))}}))},4588:function(t,e){var n=Math.ceil,r=Math.floor;t.exports=function(t){return isNaN(t=+t)?0:(t>0?r:n)(t)}},4630:function(t,e){t.exports=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}}},"4bf8":function(t,e,n){var r=n("be13");t.exports=function(t){return Object(r(t))}},5147:function(t,e,n){var r=n("2b4c")("match");t.exports=function(t){var e=/./;try{"/./"[t](e)}catch(n){try{return e[r]=!1,!"/./"[t](e)}catch(o){}}return!0}},"520a":function(t,e,n){"use strict";var r=n("0bfb"),o=RegExp.prototype.exec,i=String.prototype.replace,c=o,u="lastIndex",a=function(){var t=/a/,e=/b*/g;return o.call(t,"a"),o.call(e,"a"),0!==t[u]||0!==e[u]}(),s=void 0!==/()??/.exec("")[1],f=a||s;f&&(c=function(t){var e,n,c,f,l=this;return s&&(n=new RegExp("^"+l.source+"$(?!\\s)",r.call(l))),a&&(e=l[u]),c=o.call(l,t),a&&c&&(l[u]=l.global?c.index+c[0].length:e),s&&c&&c.length>1&&i.call(c[0],n,(function(){for(f=1;f1?arguments[1]:void 0)}}),n("9c6c")("includes")},6821:function(t,e,n){var r=n("626a"),o=n("be13");t.exports=function(t){return r(o(t))}},"69a8":function(t,e){var n={}.hasOwnProperty;t.exports=function(t,e){return n.call(t,e)}},"6a99":function(t,e,n){var r=n("d3f4");t.exports=function(t,e){if(!r(t))return t;var n,o;if(e&&"function"==typeof(n=t.toString)&&!r(o=n.call(t)))return o;if("function"==typeof(n=t.valueOf)&&!r(o=n.call(t)))return o;if(!e&&"function"==typeof(n=t.toString)&&!r(o=n.call(t)))return o;throw TypeError("Can't convert object to primitive value")}},7333:function(t,e,n){"use strict";var r=n("0d58"),o=n("2621"),i=n("52a7"),c=n("4bf8"),u=n("626a"),a=Object.assign;t.exports=!a||n("79e5")((function(){var t={},e={},n=Symbol(),r="abcdefghijklmnopqrst";return t[n]=7,r.split("").forEach((function(t){e[t]=t})),7!=a({},t)[n]||Object.keys(a({},e)).join("")!=r}))?function(t,e){var n=c(t),a=arguments.length,s=1,f=o.f,l=i.f;while(a>s){var d,p=u(arguments[s++]),h=f?r(p).concat(f(p)):r(p),v=h.length,g=0;while(v>g)l.call(p,d=h[g++])&&(n[d]=p[d])}return n}:a},7726:function(t,e){var n=t.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=n)},"77f1":function(t,e,n){var r=n("4588"),o=Math.max,i=Math.min;t.exports=function(t,e){return t=r(t),t<0?o(t+e,0):i(t,e)}},"79e5":function(t,e){t.exports=function(t){try{return!!t()}catch(e){return!0}}},"7f20":function(t,e,n){var r=n("86cc").f,o=n("69a8"),i=n("2b4c")("toStringTag");t.exports=function(t,e,n){t&&!o(t=n?t:t.prototype,i)&&r(t,i,{configurable:!0,value:e})}},8378:function(t,e){var n=t.exports={version:"2.6.5"};"number"==typeof __e&&(__e=n)},"84f2":function(t,e){t.exports={}},"86cc":function(t,e,n){var r=n("cb7c"),o=n("c69a"),i=n("6a99"),c=Object.defineProperty;e.f=n("9e1e")?Object.defineProperty:function(t,e,n){if(r(t),e=i(e,!0),r(n),o)try{return c(t,e,n)}catch(u){}if("get"in n||"set"in n)throw TypeError("Accessors not supported!");return"value"in n&&(t[e]=n.value),t}},"9b43":function(t,e,n){var r=n("d8e8");t.exports=function(t,e,n){if(r(t),void 0===e)return t;switch(n){case 1:return function(n){return t.call(e,n)};case 2:return function(n,r){return t.call(e,n,r)};case 3:return function(n,r,o){return t.call(e,n,r,o)}}return function(){return t.apply(e,arguments)}}},"9c6c":function(t,e,n){var r=n("2b4c")("unscopables"),o=Array.prototype;void 0==o[r]&&n("32e9")(o,r,{}),t.exports=function(t){o[r][t]=!0}},"9def":function(t,e,n){var r=n("4588"),o=Math.min;t.exports=function(t){return t>0?o(r(t),9007199254740991):0}},"9e1e":function(t,e,n){t.exports=!n("79e5")((function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a}))},a352:function(e,n){e.exports=t},a481:function(t,e,n){"use strict";var r=n("cb7c"),o=n("4bf8"),i=n("9def"),c=n("4588"),u=n("0390"),a=n("5f1b"),s=Math.max,f=Math.min,l=Math.floor,d=/\$([$&`']|\d\d?|<[^>]*>)/g,p=/\$([$&`']|\d\d?)/g,h=function(t){return void 0===t?t:String(t)};n("214f")("replace",2,(function(t,e,n,v){return[function(r,o){var i=t(this),c=void 0==r?void 0:r[e];return void 0!==c?c.call(r,i,o):n.call(String(i),r,o)},function(t,e){var o=v(n,t,this,e);if(o.done)return o.value;var l=r(t),d=String(this),p="function"===typeof e;p||(e=String(e));var b=l.global;if(b){var m=l.unicode;l.lastIndex=0}var y=[];while(1){var x=a(l,d);if(null===x)break;if(y.push(x),!b)break;var O=String(x[0]);""===O&&(l.lastIndex=u(d,i(l.lastIndex),m))}for(var S="",w=0,j=0;j=w&&(S+=d.slice(w,C)+E,w=C+M.length)}return S+d.slice(w)}];function g(t,e,r,i,c,u){var a=r+t.length,s=i.length,f=p;return void 0!==c&&(c=o(c),f=d),n.call(u,f,(function(n,o){var u;switch(o.charAt(0)){case"$":return"$";case"&":return t;case"`":return e.slice(0,r);case"'":return e.slice(a);case"<":u=c[o.slice(1,-1)];break;default:var f=+o;if(0===f)return n;if(f>s){var d=l(f/10);return 0===d?n:d<=s?void 0===i[d-1]?o.charAt(1):i[d-1]+o.charAt(1):n}u=i[f-1]}return void 0===u?"":u}))}}))},aae3:function(t,e,n){var r=n("d3f4"),o=n("2d95"),i=n("2b4c")("match");t.exports=function(t){var e;return r(t)&&(void 0!==(e=t[i])?!!e:"RegExp"==o(t))}},ac6a:function(t,e,n){for(var r=n("cadf"),o=n("0d58"),i=n("2aba"),c=n("7726"),u=n("32e9"),a=n("84f2"),s=n("2b4c"),f=s("iterator"),l=s("toStringTag"),d=a.Array,p={CSSRuleList:!0,CSSStyleDeclaration:!1,CSSValueList:!1,ClientRectList:!1,DOMRectList:!1,DOMStringList:!1,DOMTokenList:!0,DataTransferItemList:!1,FileList:!1,HTMLAllCollection:!1,HTMLCollection:!1,HTMLFormElement:!1,HTMLSelectElement:!1,MediaList:!0,MimeTypeArray:!1,NamedNodeMap:!1,NodeList:!0,PaintRequestList:!1,Plugin:!1,PluginArray:!1,SVGLengthList:!1,SVGNumberList:!1,SVGPathSegList:!1,SVGPointList:!1,SVGStringList:!1,SVGTransformList:!1,SourceBufferList:!1,StyleSheetList:!0,TextTrackCueList:!1,TextTrackList:!1,TouchList:!1},h=o(p),v=0;vf)if(u=a[f++],u!=u)return!0}else for(;s>f;f++)if((t||f in a)&&a[f]===n)return t||f||0;return!t&&-1}}},c649:function(t,e,n){"use strict";(function(t){n.d(e,"c",(function(){return s})),n.d(e,"a",(function(){return u})),n.d(e,"b",(function(){return o})),n.d(e,"d",(function(){return a}));n("a481");function r(){return"undefined"!==typeof window?window.console:t.console}var o=r();function i(t){var e=Object.create(null);return function(n){var r=e[n];return r||(e[n]=t(n))}}var c=/-(\w)/g,u=i((function(t){return t.replace(c,(function(t,e){return e?e.toUpperCase():""}))}));function a(t){null!==t.parentElement&&t.parentElement.removeChild(t)}function s(t,e,n){var r=0===n?t.children[0]:t.children[n-1].nextSibling;t.insertBefore(e,r)}}).call(this,n("c8ba"))},c69a:function(t,e,n){t.exports=!n("9e1e")&&!n("79e5")((function(){return 7!=Object.defineProperty(n("230e")("div"),"a",{get:function(){return 7}}).a}))},c8ba:function(t,e){var n;n=function(){return this}();try{n=n||new Function("return this")()}catch(r){"object"===typeof window&&(n=window)}t.exports=n},ca5a:function(t,e){var n=0,r=Math.random();t.exports=function(t){return"Symbol(".concat(void 0===t?"":t,")_",(++n+r).toString(36))}},cadf:function(t,e,n){"use strict";var r=n("9c6c"),o=n("d53b"),i=n("84f2"),c=n("6821");t.exports=n("01f9")(Array,"Array",(function(t,e){this._t=c(t),this._i=0,this._k=e}),(function(){var t=this._t,e=this._k,n=this._i++;return!t||n>=t.length?(this._t=void 0,o(1)):o(0,"keys"==e?n:"values"==e?t[n]:[n,t[n]])}),"values"),i.Arguments=i.Array,r("keys"),r("values"),r("entries")},cb7c:function(t,e,n){var r=n("d3f4");t.exports=function(t){if(!r(t))throw TypeError(t+" is not an object!");return t}},ce10:function(t,e,n){var r=n("69a8"),o=n("6821"),i=n("c366")(!1),c=n("613b")("IE_PROTO");t.exports=function(t,e){var n,u=o(t),a=0,s=[];for(n in u)n!=c&&r(u,n)&&s.push(n);while(e.length>a)r(u,n=e[a++])&&(~i(s,n)||s.push(n));return s}},d2c8:function(t,e,n){var r=n("aae3"),o=n("be13");t.exports=function(t,e,n){if(r(e))throw TypeError("String#"+n+" doesn't accept regex!");return String(o(t))}},d3f4:function(t,e){t.exports=function(t){return"object"===typeof t?null!==t:"function"===typeof t}},d53b:function(t,e){t.exports=function(t,e){return{value:e,done:!!t}}},d8e8:function(t,e){t.exports=function(t){if("function"!=typeof t)throw TypeError(t+" is not a function!");return t}},e11e:function(t,e){t.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},f559:function(t,e,n){"use strict";var r=n("5ca1"),o=n("9def"),i=n("d2c8"),c="startsWith",u=""[c];r(r.P+r.F*n("5147")(c),"String",{startsWith:function(t){var e=i(this,t,c),n=o(Math.min(arguments.length>1?arguments[1]:void 0,e.length)),r=String(t);return u?u.call(e,r,n):e.slice(n,n+r.length)===r}})},f6fd:function(t,e){(function(t){var e="currentScript",n=t.getElementsByTagName("script");e in t||Object.defineProperty(t,e,{get:function(){try{throw new Error}catch(r){var t,e=(/.*at [^\(]*\((.*):.+:.+\)$/gi.exec(r.stack)||[!1])[1];for(t in n)if(n[t].src==e||"interactive"==n[t].readyState)return n[t];return null}}})})(document)},f751:function(t,e,n){var r=n("5ca1");r(r.S+r.F,"Object",{assign:n("7333")})},fa5b:function(t,e,n){t.exports=n("5537")("native-function-to-string",Function.toString)},fab2:function(t,e,n){var r=n("7726").document;t.exports=r&&r.documentElement},fb15:function(t,e,n){"use strict";var r;(n.r(e),"undefined"!==typeof window)&&(n("f6fd"),(r=window.document.currentScript)&&(r=r.src.match(/(.+\/)[^/]+\.js(\?.*)?$/))&&(n.p=r[1]));n("f751"),n("f559"),n("ac6a"),n("cadf"),n("456d");function o(t){if(Array.isArray(t))return t}function i(t,e){if("undefined"!==typeof Symbol&&Symbol.iterator in Object(t)){var n=[],r=!0,o=!1,i=void 0;try{for(var c,u=t[Symbol.iterator]();!(r=(c=u.next()).done);r=!0)if(n.push(c.value),e&&n.length===e)break}catch(a){o=!0,i=a}finally{try{r||null==u["return"]||u["return"]()}finally{if(o)throw i}}return n}}function c(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,r=new Array(e);n=i?o.length:o.indexOf(t)}));return n?c.filter((function(t){return-1!==t})):c}function x(t,e){var n=this;this.$nextTick((function(){return n.$emit(t.toLowerCase(),e)}))}function O(t){var e=this;return function(n){null!==e.realList&&e["onDrag"+t](n),x.call(e,t,n)}}function S(t){return["transition-group","TransitionGroup"].includes(t)}function w(t){if(!t||1!==t.length)return!1;var e=s(t,1),n=e[0].componentOptions;return!!n&&S(n.tag)}function j(t,e,n){return t[n]||(e[n]?e[n]():void 0)}function M(t,e,n){var r=0,o=0,i=j(e,n,"header");i&&(r=i.length,t=t?[].concat(p(i),p(t)):p(i));var c=j(e,n,"footer");return c&&(o=c.length,t=t?[].concat(p(t),p(c)):p(c)),{children:t,headerOffset:r,footerOffset:o}}function C(t,e){var n=null,r=function(t,e){n=b(n,t,e)},o=Object.keys(t).filter((function(t){return"id"===t||t.startsWith("data-")})).reduce((function(e,n){return e[n]=t[n],e}),{});if(r("attrs",o),!e)return n;var i=e.on,c=e.props,u=e.attrs;return r("on",i),r("props",c),Object.assign(n.attrs,u),n}var T=["Start","Add","Remove","Update","End"],_=["Choose","Unchoose","Sort","Filter","Clone"],L=["Move"].concat(T,_).map((function(t){return"on"+t})),I=null,E={options:Object,list:{type:Array,required:!1,default:null},value:{type:Array,required:!1,default:null},noTransitionOnDrag:{type:Boolean,default:!1},clone:{type:Function,default:function(t){return t}},element:{type:String,default:"div"},tag:{type:String,default:null},move:{type:Function,default:null},componentData:{type:Object,required:!1,default:null}},P={name:"draggable",inheritAttrs:!1,props:E,data:function(){return{transitionMode:!1,noneFunctionalComponentMode:!1}},render:function(t){var e=this.$slots.default;this.transitionMode=w(e);var n=M(e,this.$slots,this.$scopedSlots),r=n.children,o=n.headerOffset,i=n.footerOffset;this.headerOffset=o,this.footerOffset=i;var c=C(this.$attrs,this.componentData);return t(this.getTag(),c,r)},created:function(){null!==this.list&&null!==this.value&&g["b"].error("Value and list props are mutually exclusive! Please set one or another."),"div"!==this.element&&g["b"].warn("Element props is deprecated please use tag props instead. See https://github.com/SortableJS/Vue.Draggable/blob/master/documentation/migrate.md#element-props"),void 0!==this.options&&g["b"].warn("Options props is deprecated, add sortable options directly as vue.draggable item, or use v-bind. See https://github.com/SortableJS/Vue.Draggable/blob/master/documentation/migrate.md#options-props")},mounted:function(){var t=this;if(this.noneFunctionalComponentMode=this.getTag().toLowerCase()!==this.$el.nodeName.toLowerCase()&&!this.getIsFunctional(),this.noneFunctionalComponentMode&&this.transitionMode)throw new Error("Transition-group inside component is not supported. Please alter tag value or remove transition-group. Current tag value: ".concat(this.getTag()));var e={};T.forEach((function(n){e["on"+n]=O.call(t,n)})),_.forEach((function(n){e["on"+n]=x.bind(t,n)}));var n=Object.keys(this.$attrs).reduce((function(e,n){return e[Object(g["a"])(n)]=t.$attrs[n],e}),{}),r=Object.assign({},this.options,n,e,{onMove:function(e,n){return t.onDragMove(e,n)}});!("draggable"in r)&&(r.draggable=">*"),this._sortable=new v.a(this.rootContainer,r),this.computeIndexes()},beforeDestroy:function(){void 0!==this._sortable&&this._sortable.destroy()},computed:{rootContainer:function(){return this.transitionMode?this.$el.children[0]:this.$el},realList:function(){return this.list?this.list:this.value}},watch:{options:{handler:function(t){this.updateOptions(t)},deep:!0},$attrs:{handler:function(t){this.updateOptions(t)},deep:!0},realList:function(){this.computeIndexes()}},methods:{getIsFunctional:function(){var t=this._vnode.fnOptions;return t&&t.functional},getTag:function(){return this.tag||this.element},updateOptions:function(t){for(var e in t){var n=Object(g["a"])(e);-1===L.indexOf(n)&&this._sortable.option(n,t[e])}},getChildrenNodes:function(){if(this.noneFunctionalComponentMode)return this.$children[0].$slots.default;var t=this.$slots.default;return this.transitionMode?t[0].child.$slots.default:t},computeIndexes:function(){var t=this;this.$nextTick((function(){t.visibleIndexes=y(t.getChildrenNodes(),t.rootContainer.children,t.transitionMode,t.footerOffset)}))},getUnderlyingVm:function(t){var e=m(this.getChildrenNodes()||[],t);if(-1===e)return null;var n=this.realList[e];return{index:e,element:n}},getUnderlyingPotencialDraggableComponent:function(t){var e=t.__vue__;return e&&e.$options&&S(e.$options._componentTag)?e.$parent:!("realList"in e)&&1===e.$children.length&&"realList"in e.$children[0]?e.$children[0]:e},emitChanges:function(t){var e=this;this.$nextTick((function(){e.$emit("change",t)}))},alterList:function(t){if(this.list)t(this.list);else{var e=p(this.value);t(e),this.$emit("input",e)}},spliceList:function(){var t=arguments,e=function(e){return e.splice.apply(e,p(t))};this.alterList(e)},updatePosition:function(t,e){var n=function(n){return n.splice(e,0,n.splice(t,1)[0])};this.alterList(n)},getRelatedContextFromMoveEvent:function(t){var e=t.to,n=t.related,r=this.getUnderlyingPotencialDraggableComponent(e);if(!r)return{component:r};var o=r.realList,i={list:o,component:r};if(e!==n&&o&&r.getUnderlyingVm){var c=r.getUnderlyingVm(n);if(c)return Object.assign(c,i)}return i},getVmIndex:function(t){var e=this.visibleIndexes,n=e.length;return t>n-1?n:e[t]},getComponent:function(){return this.$slots.default[0].componentInstance},resetTransitionData:function(t){if(this.noTransitionOnDrag&&this.transitionMode){var e=this.getChildrenNodes();e[t].data=null;var n=this.getComponent();n.children=[],n.kept=void 0}},onDragStart:function(t){this.context=this.getUnderlyingVm(t.item),t.item._underlying_vm_=this.clone(this.context.element),I=t.item},onDragAdd:function(t){var e=t.item._underlying_vm_;if(void 0!==e){Object(g["d"])(t.item);var n=this.getVmIndex(t.newIndex);this.spliceList(n,0,e),this.computeIndexes();var r={element:e,newIndex:n};this.emitChanges({added:r})}},onDragRemove:function(t){if(Object(g["c"])(this.rootContainer,t.item,t.oldIndex),"clone"!==t.pullMode){var e=this.context.index;this.spliceList(e,1);var n={element:this.context.element,oldIndex:e};this.resetTransitionData(e),this.emitChanges({removed:n})}else Object(g["d"])(t.clone)},onDragUpdate:function(t){Object(g["d"])(t.item),Object(g["c"])(t.from,t.item,t.oldIndex);var e=this.context.index,n=this.getVmIndex(t.newIndex);this.updatePosition(e,n);var r={element:this.context.element,oldIndex:e,newIndex:n};this.emitChanges({moved:r})},updateProperty:function(t,e){t.hasOwnProperty(e)&&(t[e]+=this.headerOffset)},computeFutureIndex:function(t,e){if(!t.element)return 0;var n=p(e.to.children).filter((function(t){return"none"!==t.style["display"]})),r=n.indexOf(e.related),o=t.component.getVmIndex(r),i=-1!==n.indexOf(I);return i||!e.willInsertAfter?o:o+1},onDragMove:function(t,e){var n=this.move;if(!n||!this.realList)return!0;var r=this.getRelatedContextFromMoveEvent(t),o=this.context,i=this.computeFutureIndex(r,t);Object.assign(o,{futureIndex:i});var c=Object.assign({},t,{relatedContext:r,draggedContext:o});return n(c,e)},onDragEnd:function(){this.computeIndexes(),I=null}}};"undefined"!==typeof window&&"Vue"in window&&window.Vue.component("draggable",P);var $=P;e["default"]=$}})["default"]})); diff --git a/wp-content/plugins/facetwp/includes/api/fetch.php b/wp-content/plugins/facetwp/includes/api/fetch.php new file mode 100644 index 000000000..07334663a --- /dev/null +++ b/wp-content/plugins/facetwp/includes/api/fetch.php @@ -0,0 +1,180 @@ + 'POST', + 'callback' => [ $this, 'callback' ], + 'permission_callback' => [ $this, 'permission_callback' ] + ] ); + } + + + // PHP < 5.3 + function callback( $request ) { + $data = $request->get_param( 'data' ); + + if ( ! $request->is_json_content_type()) { + $data = empty( $data ) ? [] : json_decode( $data, true ); + } + + return $this->process_request( $data ); + } + + + // PHP < 5.3 + function permission_callback( $request ) { + return apply_filters( 'facetwp_api_can_access', false, $request ); + } + + + function process_request( $params = [] ) { + global $wpdb; + + $defaults = [ + 'facets' => [ + // 'category' => [ 'acf' ] + ], + 'query_args' => [ + 'post_type' => 'post', + 'post_status' => 'publish', + 'posts_per_page' => 10, + 'paged' => 1, + ], + 'settings' => [ + 'first_load' => true + ] + ]; + + $params = array_merge( $defaults, $params ); + $facet_types = FWP()->helper->facet_types; + $valid_facets = []; + $facets = []; + + // Validate input + $page = (int) ( $params['query_args']['paged'] ?? 1 ); + $per_page = (int) ( $params['query_args']['posts_per_page'] ?? 10 ); + + $page = max( $page, 1 ); + $per_page = ( 0 === $per_page ) ? 10 : $per_page; + $per_page = ( -1 > $per_page ) ? absint( $per_page ) : $per_page; + + $params['query_args']['paged'] = $page; + $params['query_args']['posts_per_page'] = $per_page; + + // Generate FWP()->facet->facets + // Required by FWP()->helper->facet_setting_exists() + foreach ( $params['facets'] as $facet_name => $facet_value ) { + $facet = FWP()->helper->get_facet_by_name( $facet_name ); + if ( false !== $facet ) { + $facet['selected_values'] = (array) $facet_value; + $valid_facets[ $facet_name ] = $facet; + FWP()->facet->facets[ $facet_name ] = $facet; + } + } + + // Get bucket of post IDs + $query_args = $params['query_args']; + FWP()->facet->query_args = $query_args; + $post_ids = FWP()->facet->get_filtered_post_ids( $query_args ); + + // SQL WHERE used by facets + $where_clause = ' AND post_id IN (' . implode( ',', $post_ids ) . ')'; + + // Check if empty + if ( 0 === $post_ids[0] && 1 === count( $post_ids ) ) { + $post_ids = []; + } + + // get_where_clause() needs "found_posts" (keep this BELOW the empty check) + FWP()->facet->query = (object) [ 'found_posts' => count( $post_ids ) ]; + + // Get valid facets and their values + foreach ( $valid_facets as $facet_name => $facet ) { + $args = [ + 'facet' => $facet, + 'where_clause' => $where_clause, + 'selected_values' => $facet['selected_values'], + ]; + + $facet_data = [ + 'name' => $facet['name'], + 'label' => $facet['label'], + 'type' => $facet['type'], + 'selected' => $facet['selected_values'], + ]; + + // Load facet choices if available + if ( method_exists( $facet_types[ $facet['type'] ], 'load_values' ) ) { + $choices = $facet_types[ $facet['type'] ]->load_values( $args ); + foreach ( $choices as $key => $choice ) { + $row = [ + 'value' => $choice['facet_value'], + 'label' => $choice['facet_display_value'], + 'depth' => (int) $choice['depth'], + 'count' => (int) $choice['counter'], + ]; + + if ( isset( $choice['term_id'] ) ) { + $row['term_id'] = (int) $choice['term_id']; + } + + if ( isset( $choice['parent_id'] ) ) { + $row['parent_id'] = (int) $choice['parent_id']; + } + + $choices[ $key ] = $row; + } + + $facet_data['choices'] = $choices; + } + + // Load facet settings if available + if ( method_exists( $facet_types[ $facet['type'] ], 'settings_js' ) ) { + $facet_data['settings'] = $facet_types[ $facet['type'] ]->settings_js( $args ); + } + + $facets[ $facet_name ] = $facet_data; + } + + $total_rows = count( $post_ids ); + + // Paginate? + if ( 0 < $per_page ) { + $total_pages = ceil( $total_rows / $per_page ); + + if ( $page > $total_pages ) { + $post_ids = []; + } + else { + $offset = ( $per_page * ( $page - 1 ) ); + $post_ids = array_slice( $post_ids, $offset, $per_page ); + } + } + else { + $total_pages = ( 0 < $total_rows ) ? 1 : 0; + } + + // Generate the output + $output = [ + 'results' => $post_ids, + 'facets' => $facets, + 'pager' => [ + 'page' => $page, + 'per_page' => $per_page, + 'total_rows' => $total_rows, + 'total_pages' => $total_pages, + ] + ]; + + return apply_filters( 'facetwp_api_output', $output ); + } +} diff --git a/wp-content/plugins/facetwp/includes/api/refresh.php b/wp-content/plugins/facetwp/includes/api/refresh.php new file mode 100644 index 000000000..bbc08212d --- /dev/null +++ b/wp-content/plugins/facetwp/includes/api/refresh.php @@ -0,0 +1,27 @@ + 'POST', + 'callback' => 'facetwp_api_refresh', + 'permission_callback' => '__return_true' + ] ); +}); + +function facetwp_api_refresh( $request ) { + $params = $request->get_params(); + $action = $params['action'] ?? ''; + + $valid_actions = [ + 'facetwp_refresh', + 'facetwp_autocomplete_load' + ]; + + $valid_actions = apply_filters( 'facetwp_api_valid_actions', $valid_actions ); + + if ( in_array( $action, $valid_actions ) ) { + do_action( $action ); + } + + return []; +} diff --git a/wp-content/plugins/facetwp/includes/class-ajax.php b/wp-content/plugins/facetwp/includes/class-ajax.php new file mode 100755 index 000000000..f295bc0a8 --- /dev/null +++ b/wp-content/plugins/facetwp/includes/class-ajax.php @@ -0,0 +1,326 @@ +$action(); + } + + // Authenticated + elseif ( current_user_can( apply_filters( 'facetwp_admin_settings_capability', 'manage_options' ) ) ) { + if ( wp_verify_nonce( $_POST['nonce'], 'fwp_admin_nonce' ) ) { + $this->$action(); + } + } + } + + // Listen for API refresh call + add_action( 'facetwp_refresh', [ $this, 'refresh' ] ); + + // Backwards compatibility + $this->url_vars = FWP()->request->url_vars; + $this->is_preload = FWP()->request->is_preload; + } + + + /** + * Save admin settings + */ + function save_settings() { + $settings = $_POST['data']; + + if ( isset( $settings['settings'] ) ) { + update_option( 'facetwp_settings', json_encode( $settings ), 'no' ); + + if ( FWP()->diff->is_reindex_needed() ) { + $response = [ + 'code' => 'error', + 'message' => __( 'Settings saved, please re-index', 'fwp' ) + ]; + } + else { + $response = [ + 'code' => 'success', + 'message' => __( 'Settings saved', 'fwp' ) + ]; + } + } + else { + $response = [ + 'code' => 'error', + 'message' => __( 'Error: invalid JSON', 'fwp' ) + ]; + } + + wp_send_json( $response ); + } + + + /** + * Rebuild the index table + */ + function rebuild_index() { + update_option( 'facetwp_indexing_cancelled', 'no', 'no' ); + FWP()->indexer->index(); + exit; + } + + + function get_info() { + $type = $_POST['type']; + + if ( 'post_types' == $type ) { + + $types = FWP()->helper->get_indexable_types(); + + $response = [ + 'code' => 'success', + 'message' => implode( ', ', $types ) + ]; + } + elseif ( 'indexer_stats' == $type ) { + $last_indexed = get_option( 'facetwp_last_indexed' ); + $last_indexed = $last_indexed ? human_time_diff( $last_indexed ) . ' ago' : 'never'; + + $response = [ + 'code' => 'success', + 'message' => "Last indexed: $last_indexed" + ]; + } + elseif ( 'cancel_reindex' == $type ) { + update_option( 'facetwp_indexing_cancelled', 'yes' ); + + $response = [ + 'code' => 'success', + 'message' => 'Indexing cancelled' + ]; + } + elseif ( 'purge_index_table' == $type ) { + global $wpdb; + + $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}facetwp_index" ); + $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}facetwp_temp" ); + delete_option( 'facetwp_version' ); + delete_option( 'facetwp_indexing' ); + delete_option( 'facetwp_indexing_data' ); + + $response = [ + 'code' => 'success', + 'message' => __( 'Done, please re-index', 'fwp' ) + ]; + } + + wp_send_json( $response ); + } + + + /** + * Return query arguments based on a Query Builder object + */ + function get_query_args() { + $query_obj = $_POST['query_obj']; + + if ( is_array( $query_obj ) ) { + $query_args = FWP()->builder->parse_query_obj( $query_obj ); + } + + wp_send_json( $query_args ); + } + + + /** + * Keep track of indexing progress + */ + function heartbeat() { + $output = [ + 'pct' => FWP()->indexer->get_progress() + ]; + + if ( -1 == $output['pct'] ) { + $output['rows'] = FWP()->helper->get_row_counts(); + } + + wp_send_json( $output ); + } + + + /** + * License activation + */ + function license() { + $license = sanitize_key( $_POST['license'] ); + + $request = wp_remote_post( 'https://api.facetwp.com', [ + 'body' => [ + 'action' => 'activate', + 'slug' => 'facetwp', + 'license' => $license, + 'host' => FWP()->helper->get_http_host(), + ] + ] ); + + if ( ! is_wp_error( $request ) || 200 == wp_remote_retrieve_response_code( $request ) ) { + update_option( 'facetwp_license', $license ); + update_option( 'facetwp_activation', $request['body'] ); + update_option( 'facetwp_updater_last_checked', 0 ); + echo $request['body']; + } + else { + echo json_encode( [ + 'status' => 'error', + 'message' => __( 'Error', 'fwp' ) . ': ' . $request->get_error_message(), + ] ); + } + + exit; + } + + + /** + * Import / export functionality + */ + function backup() { + $action_type = $_POST['action_type']; + $output = []; + + if ( 'export' == $action_type ) { + $items = $_POST['items']; + + if ( ! empty( $items ) ) { + foreach ( $items as $item ) { + if ( 'facet' == substr( $item, 0, 5 ) ) { + $item_name = substr( $item, 6 ); + $output['facets'][] = FWP()->helper->get_facet_by_name( $item_name ); + } + elseif ( 'template' == substr( $item, 0, 8 ) ) { + $item_name = substr( $item, 9 ); + $output['templates'][] = FWP()->helper->get_template_by_name( $item_name ); + } + } + } + echo json_encode( $output ); + } + elseif ( 'import' == $action_type ) { + $settings = FWP()->helper->settings; + $import_code = $_POST['import_code']; + $overwrite = (int) $_POST['overwrite']; + + if ( empty( $import_code ) || ! is_array( $import_code ) ) { + _e( 'Nothing to import', 'fwp' ); + exit; + } + + $status = [ + 'imported' => [], + 'skipped' => [], + ]; + + foreach ( $import_code as $object_type => $object_items ) { + foreach ( $object_items as $object_item ) { + $is_match = false; + foreach ( $settings[$object_type] as $key => $settings_item ) { + if ( $object_item['name'] == $settings_item['name'] ) { + if ( $overwrite ) { + $settings[$object_type][$key] = $object_item; + $status['imported'][] = $object_item['label']; + } + else { + $status['skipped'][] = $object_item['label']; + } + $is_match = true; + break; + } + } + + if ( ! $is_match ) { + $settings[$object_type][] = $object_item; + $status['imported'][] = $object_item['label']; + } + } + } + + update_option( 'facetwp_settings', json_encode( $settings ) ); + + if ( ! empty( $status['imported'] ) ) { + echo ' [' . __( 'Imported', 'fwp' ) . '] ' . implode( ', ', $status['imported'] ); + } + if ( ! empty( $status['skipped'] ) ) { + echo ' [' . __( 'Skipped', 'fwp' ) . '] ' . implode( ', ', $status['skipped'] ); + } + } + + exit; + } + + + /** + * The AJAX facet refresh handler + */ + function refresh() { + global $wpdb; + + $params = FWP()->request->process_post_data(); + $output = FWP()->facet->render( $params ); + $data = stripslashes_deep( $_POST['data'] ); + + // Ignore invalid UTF-8 characters in PHP 7.2+ + if ( version_compare( phpversion(), '7.2', '<' ) ) { + $output = json_encode( $output ); + } + else { + $output = json_encode( $output, JSON_INVALID_UTF8_IGNORE ); + } + + echo apply_filters( 'facetwp_ajax_response', $output, [ + 'data' => $data + ] ); + + exit; + } + + + /** + * Resume stalled indexer + */ + function resume_index() { + $touch = (int) FWP()->indexer->get_transient( 'touch' ); + if ( 0 < $touch && $_POST['touch'] == $touch ) { + FWP()->indexer->index(); + } + exit; + } +} diff --git a/wp-content/plugins/facetwp/includes/class-builder.php b/wp-content/plugins/facetwp/includes/class-builder.php new file mode 100644 index 000000000..7a634cb11 --- /dev/null +++ b/wp-content/plugins/facetwp/includes/class-builder.php @@ -0,0 +1,899 @@ +custom_css = "@media (max-width: 480px) { \n body .facetwp-template .fwpl-layout, \n body .facetwp-template-static .fwpl-layout { grid-template-columns: 1fr; } \n} \n"; + + $this->custom_css .= $settings['custom_css']; + + $selector = '.fwpl-layout'; + $selector .= empty( $settings['name'] ) ? '' : '.' . $settings['name']; + + $this->css = [ + '.fwpl-layout, .fwpl-row' => [ + 'display' => 'grid' + ], + $selector => [ + 'grid-template-columns' => 'repeat(' . $settings['num_columns'] . ', 1fr)', + 'grid-gap' => $settings['grid_gap'] . 'px' + ], + '.fwpl-btn' => [ + 'text-decoration' => 'none' + ], + $selector . ' .fwpl-result' => $this->build_styles( $settings ) + ]; + + $classes = $this->get_classes( 'fwpl-layout', $settings ); + + $output = '
    '; + + if ( have_posts() ) { + while ( have_posts() ) : the_post(); + $counter++; + + // Default dynamic tags + $tags = [ + 'post:id' => $post->ID, + 'post:name' => $post->post_name, + 'post:type' => $post->post_type, + 'post:title' => $post->post_title, + 'post:url' => get_permalink() + ]; + + $params = [ + 'layout' => $layout, + 'post' => $post + ]; + + $this->data = apply_filters( 'facetwp_builder_dynamic_tags', $tags, $params ); + + $output .= '
    '; + + foreach ( $layout['items'] as $row ) { + $output .= $this->render_row( $row ); + } + + $output .= '
    '; + + $output = $this->parse_dynamic_tags( $output, $params ); + + endwhile; + } + else { + $no_results_text = $settings['no_results_text'] ?? ''; + $output .= do_shortcode( $no_results_text ); + } + + $output .= '
    '; + + $output .= $this->render_css(); + + return $output; + } + + + /** + * Generate the row HTML + * @since 3.2.0 + */ + function render_row( $row ) { + $settings = $row['settings']; + + $this->css['.fwpl-row.' . $settings['name'] ] = $this->build_styles( $settings ); + + $classes = $this->get_classes( 'fwpl-row', $settings ); + + $output = '
    '; + + foreach ( $row['items'] as $col ) { + $output .= $this->render_col( $col ); + } + + $output .= '
    '; + + return $output; + } + + + /** + * Generate the col HTML + * @since 3.2.0 + */ + function render_col( $col ) { + $settings = $col['settings']; + + $this->css['.fwpl-col.' . $settings['name'] ] = $this->build_styles( $settings ); + + $classes = $this->get_classes( 'fwpl-col', $settings ); + + $output = '
    '; + + foreach ( $col['items'] as $item ) { + if ( 'row' == $item['type'] ) { + $output .= $this->render_row( $item ); + } + elseif ( 'item' == $item['type'] ) { + $output .= $this->render_item( $item ); + } + } + + $output .= '
    '; + + return $output; + } + + + /** + * Generate the item HTML + * @since 3.2.0 + */ + function render_item( $item ) { + global $post; + + $settings = $item['settings']; + $name = $settings['name']; + $source = $item['source']; + $value = $source; + + $selector = '.fwpl-item.' . $name; + $selector = ( 'button' == $source ) ? $selector . ' .fwpl-btn' : $selector; + $this->css[ $selector ] = $this->build_styles( $settings ); + + if ( 0 === strpos( $source, 'post_' ) || 'ID' == $source ) { + if ( 'post_title' == $source ) { + $value = $this->linkify( $post->$source, $settings['link'] ); + } + elseif ( 'post_excerpt' == $source ) { + $value = get_the_excerpt( $post->ID ); + } + elseif ( 'post_content' == $source ) { + $value = apply_filters( 'the_content', $post->post_content ); + } + elseif ( 'post_author' == $source ) { + $field = $settings['author_field']; + $user = get_user_by( 'id', $post->$source ); + $value = $user->$field; + } + elseif ( 'post_type' == $source ) { + $pt_obj = get_post_type_object( $post->$source ); + $value = $pt_obj->labels->singular_name ?? $post->$source; + } + else { + $value = $post->$source; + } + } + elseif ( 0 === strpos( $source, 'cf/attribute_' ) && 'product' == get_post_type( $post->ID ) ) { + $value = ''; + $product = wc_get_product( $post->ID ); + $attr = substr( $source, 13 ); + $attributes = array_filter( $product->get_attributes(), 'wc_attributes_array_filter_visible' ); + if ( isset( $attributes[ $attr ] ) ) { + $attribute = $attributes[ $attr ]; + if ( $attribute->is_taxonomy() ) { + $attribute_taxonomy = $attribute->get_taxonomy_object(); + $attribute_values = wc_get_product_terms( $product->get_id(), $attribute->get_name(), [ 'fields' => 'all' ] ); + + foreach ( $attribute_values as $attribute_value ) { + $value_name = esc_html( $attribute_value->name ); + + if ( $attribute_taxonomy->attribute_public ) { + $values[] = '
    '; + } + else { + $values[] = $value_name; + } + } + } + else { + $values = $attribute->get_options(); + + foreach ( $values as &$value ) { + $value = make_clickable( esc_html( $value ) ); + } + } + + $value = implode( ", ", $values ); + } + } + elseif ( 0 === strpos( $source, 'cf/' ) ) { + $value = get_post_meta( $post->ID, substr( $source, 3 ), true ); + $value = $this->linkify( $value, $settings['link'] ); + } + elseif ( 0 === strpos( $source, 'tax/' ) ) { + $temp = []; + $taxonomy = substr( $source, 4 ); + $terms = get_the_terms( $post->ID, $taxonomy ); + + if ( is_array( $terms ) ) { + foreach ( $terms as $term_obj ) { + $term = $this->linkify( $term_obj->name, $settings['term_link'], [ + 'term_id' => $term_obj->term_id, + 'taxonomy' => $taxonomy + ] ); + + $temp[] = '' . $term . ''; + } + } + + $value = implode( $settings['separator'], $temp ); + } + elseif ( 0 === strpos( $source, 'woo/' ) ) { + $field = substr( $source, 4 ); + $product = wc_get_product( $post->ID ); + + // Invalid product + if ( ! is_object( $product ) ) { + $value = ''; + } + + // Price + elseif ( 'price' == $field || 'sale_price' == $field || 'regular_price' == $field ) { + if ( $product->is_type( 'variable' ) ) { + $method_name = "get_variation_$field"; + $value = $product->$method_name( 'min' ); // get_variation_price() + } + else { + $method_name = "get_$field"; + $value = $product->$method_name(); // get_price() + } + } + + // Average Rating + elseif ( 'average_rating' == $field ) { + $value = $product->get_average_rating(); + } + + // Stock Status + elseif ( 'stock_status' == $field ) { + $value = $product->is_in_stock() ? __( 'In Stock', 'fwp' ) : __( 'Out of Stock', 'fwp' ); + } + + // On Sale + elseif ( 'on_sale' == $field ) { + $value = $product->is_on_sale() ? __( 'On Sale', 'fwp' ) : ''; + } + + // Product Type + elseif ( 'product_type' == $field ) { + $value = $product->get_type(); + } + } + elseif ( 0 === strpos( $source, 'acf/' ) && isset( FWP()->acf ) ) { + $value = FWP()->acf->get_field( $source, $post->ID ); + } + elseif ( 'featured_image' == $source ) { + $value = get_the_post_thumbnail( $post->ID, $settings['image_size'] ); + $value = $this->linkify( $value, $settings['link'] ); + } + elseif ( 'button' == $source ) { + $settings['link']['class'] = 'fwpl-btn'; + $value = $this->linkify( facetwp_i18n( $settings['button_text'] ), $settings['link'] ); + } + elseif ( 'html' == $source ) { + $value = do_shortcode( $settings['content'] ); + } + + // Date format + if ( ! empty( $settings['date_format'] ) && ! empty( $value ) ) { + if ( ! empty( $settings['input_format'] ) ) { + $date = DateTime::createFromFormat( $settings['input_format'], $value ); + } + else { + $date = new DateTime( $value ); + } + + // Use wp_date() to support i18n + if ( $date ) { + $value = wp_date( $settings['date_format'], $date->getTimestamp(), new DateTimeZone( 'UTC' ) ); + } + } + + // Number format + if ( ! empty( $settings['number_format'] ) && ! empty( $value ) ) { + $decimals = 2; + $format = $settings['number_format']; + $decimal_sep = FWP()->helper->get_setting( 'decimal_separator' ); + $thousands_sep = FWP()->helper->get_setting( 'thousands_separator' ); + + // No thousands separator + if ( false === strpos( $format, ',' ) ) { + $thousands_sep = ''; + } + + // Handle decimals + if ( false === ( $pos = strpos( $format, '.' ) ) ) { + $decimals = 0; + } + else { + $decimals = strlen( $format ) - $pos - 1; + } + + $value = number_format( $value, $decimals, $decimal_sep, $thousands_sep ); + } + + $output = ''; + $prefix = $settings['prefix'] ?? ''; + $suffix = $settings['suffix'] ?? ''; + + // Allow value hooks + $value = apply_filters( 'facetwp_builder_item_value', $value, $item ); + + // Convert array to string + if ( is_array( $value ) ) { + $value = implode( ', ', $value ); + } + + // Store the RAW short-tag + $this->data[ "$name:raw" ] = $value; + + // Attach the prefix / suffix to the value + if ( '' != $value ) { + $value = $prefix . $value . $suffix; + } + + // Store the short-tag + $this->data[ $name ] = $value; + + // Build the list of CSS classes + $classes = $this->get_classes( 'fwpl-item', $settings ); + + if ( '' == $value ) { + $classes .= ' is-empty'; + } + + // Prevent output + if ( ! $settings['is_hidden'] ) { + $output = '
    ' . $value . '
    '; + } + + return $output; + } + + + /** + * Parse dynamic tags, e.g. {{ first_name }} + */ + function parse_dynamic_tags( $output, $params ) { + $pattern = '/({{[ ]?(.*?)[ ]?}})/s'; + + return preg_replace_callback( $pattern, function( $matches ) use( $params ) { + $tag_name = $matches[2]; + $tag_value = $this->data[ $tag_name ] ?? ''; + return apply_filters( 'facetwp_builder_dynamic_tag_value', $tag_value, $tag_name, $params ); + }, $output ); + } + + + /** + * Calculate some dynamic tag values on-the-fly, to prevent + * unnecessary queries and extra load time + */ + function dynamic_tag_value( $tag_value, $tag_name, $params ) { + if ( 'post:image' == $tag_name ) { + $tag_value = get_the_post_thumbnail_url( $params['post']->ID, 'full' ); + } + + return $tag_value; + } + + + /** + * Build the redundant styles (border, padding,etc) + * @since 3.2.0 + */ + function build_styles( $settings ) { + $styles = []; + + if ( isset( $settings['grid_template_columns'] ) ) { + $styles['grid-template-columns'] = $settings['grid_template_columns']; + } + if ( isset( $settings['border'] ) ) { + $styles['border-style'] = $settings['border']['style']; + $styles['border-color'] = $settings['border']['color']; + $styles['border-width'] = $this->get_widths( $settings['border']['width'] ); + } + if ( isset( $settings['background_color'] ) ) { + $styles['background-color'] = $settings['background_color']; + } + if ( isset( $settings['padding'] ) ) { + $styles['padding'] = $this->get_widths( $settings['padding'] ); + } + if ( isset( $settings['text_style'] ) ) { + $styles['text-align'] = $settings['text_style']['align']; + $styles['font-weight'] = $settings['text_style']['bold'] ? 'bold' : ''; + $styles['font-style'] = $settings['text_style']['italic'] ? 'italic' : ''; + } + if ( isset( $settings['font_size'] ) ) { + $styles['font-size'] = $settings['font_size']['size'] . $settings['font_size']['unit']; + } + if ( isset( $settings['text_color'] ) ) { + $styles['color'] = $settings['text_color']; + } + if ( isset( $settings['button_border'] ) ) { + $border = $settings['button_border']; + $width = $border['width']; + $unit = $width['unit']; + + $styles['color'] = $settings['button_text_color']; + $styles['background-color'] = $settings['button_color']; + $styles['padding'] = $this->get_widths( $settings['button_padding'] ); + $styles['border-style'] = $border['style']; + $styles['border-color'] = $border['color']; + $styles['border-top-width'] = $width['top'] . $unit; + $styles['border-right-width'] = $width['right'] . $unit; + $styles['border-bottom-width'] = $width['bottom'] . $unit; + $styles['border-left-width'] = $width['left'] . $unit; + } + + return $styles; + } + + + /** + * Build the CSS widths, e.g. for "padding" or "border-width" + * @since 3.2.0 + */ + function get_widths( $data ) { + $unit = $data['unit']; + $top = $data['top']; + $right = $data['right']; + $bottom = $data['bottom']; + $left = $data['left']; + + if ( $top == $right && $right == $bottom && $bottom == $left ) { + return "$top$unit"; + } + elseif ( $top == $bottom && $left == $right ) { + return "$top$unit $left$unit"; + } + + return "$top$unit $right$unit $bottom$unit $left$unit"; + } + + + /** + * Convert a value into a link + * @since 3.2.0 + */ + function linkify( $value, $link_data, $term_data = [] ) { + global $post; + + $type = $link_data['type'] ?? ''; + $href = $link_data['href'] ?? ''; + $class = $link_data['class'] ?? ''; + $target = $link_data['target'] ?? ''; + + if ( 'none' !== $type ) { + if ( 'post' == $type ) { + $href = get_permalink(); + } + if ( 'term' == $type ) { + $href = get_term_link( $term_data['term_id'], $term_data['taxonomy'] ); + } + + if ( ! empty( $target ) ) { + $target = ' target="' . $target . '"'; + } + + if ( ! empty( $class ) ) { + $class = ' class="' . $class . '"'; + } + + $value = '' . $value . ''; + } + + return $value; + } + + + /** + * Turn the CSS array into valid CSS + * @since 3.2.0 + */ + function render_css() { + $output = "\n\n"; + + return $output; + } + + + /** + * Filter out empty or invalid rules + * @since 3.2.0 + */ + function get_valid_css_rules( $props ) { + $rules = []; + + foreach ( $props as $prop => $value ) { + if ( $this->is_valid_css_rule( $prop, $value ) ) { + $rules[ $prop ] = $value; + } + } + + return $rules; + } + + + /** + * Optimize CSS rules + * @since 3.2.0 + */ + function is_valid_css_rule( $prop, $value ) { + $return = true; + + if ( empty( $value ) || 'px' === $value || '0px' === $value || 'none' === $value ) { + $return = false; + } + + if ( 'font-size' === $prop && '0px' === $value ) { + $return = false; + } + + if ( 'text-decoration' === $prop && 'none' === $value ) { + $return = true; + } + + return $return; + } + + + /** + * Make sure the query is valid + * @since 3.2.0 + */ + function parse_query_obj( $query_obj ) { + $output = []; + $tax_query = []; + $meta_query = []; + $date_query = []; + $post_type = 'any'; + $post_status = [ 'publish' ]; + $posts_per_page = 10; + $post_in = []; + $post_not_in = []; + $author_in = []; + $author_not_in = []; + $orderby = []; + + if ( ! empty( $query_obj['posts_per_page'] ) ) { + $posts_per_page = (int) $query_obj['posts_per_page']; + } + + if ( ! empty( $query_obj['post_type'] ) ) { + $post_type = array_column( $query_obj['post_type'], 'value' ); + } + + if ( empty( $query_obj['filters'] ) ) { + $query_obj['filters'] = []; + } + + if ( empty( $query_obj['orderby'] ) ) { + $query_obj['orderby'] = []; + } + + foreach ( $query_obj['filters'] as $filter ) { + $key = $filter['key']; + $value = $filter['value']; + $compare = $filter['compare']; + $type = $filter['type']; + + // Cast as decimal for more accuracy + $type = ( 'NUMERIC' == $type ) ? 'DECIMAL(16,4)' : $type; + $exists_bypass = false; + $value_bypass = false; + + // Clear the value for certain compare types + if ( in_array( $compare, [ 'EXISTS', 'NOT EXISTS', 'EMPTY', 'NOT EMPTY' ] ) ) { + $value_bypass = true; + $value = ''; + } + + if ( in_array( $compare, [ 'EXISTS', 'NOT EXISTS' ] ) ) { + $exists_bypass = true; + } + + // If "EMPTY", use "=" compare type w/ empty string value + if ( in_array( $compare, [ 'EMPTY', 'NOT EMPTY' ] ) ) { + $compare = ( 'EMPTY' == $compare ) ? '=' : '!='; + } + + // Handle multiple values + if ( is_array( $value ) ) { + if ( in_array( $compare, [ '=', '!=' ] ) ) { + $compare = ( '=' == $compare ) ? 'IN' : 'NOT IN'; + } + + if ( ! in_array( $compare, [ 'IN', 'NOT IN' ] ) ) { + $value = $value[0]; + } + } + + if ( empty( $value ) && ! $value_bypass ) { + continue; + } + + // Support dynamic URL vars + $value = $this->parse_uri_tags( $value ); + + // Prepend with "date|" so we can populate with hydrate_date_values() + if ( 'DATE' == $type ) { + $value = 'date|' . $value; + } + + if ( 'ID' == $key ) { + $arg_name = ( 'IN' == $compare ) ? 'post_in' : 'post_not_in'; + $$arg_name = $value; + } + elseif ( 'post_author' == $key ) { + $arg_name = ( 'IN' == $compare ) ? 'author_in' : 'author_not_in'; + $$arg_name = $value; + } + elseif ( 'post_status' == $key ) { + $post_status = $value; + } + elseif ( 'post_date' == $key || 'post_modified' == $key ) { + if ( '>' == $compare || '>=' == $compare ) { + $date_query[] = [ + 'after' => $value, + 'inclusive' => ( '>=' == $compare ) + ]; + } + if ( '<' == $compare || '<=' == $compare ) { + $date_query[] = [ + 'before' => $value, + 'inclusive' => ( '<=' == $compare ) + ]; + } + } + elseif ( 0 === strpos( $key, 'tax/' ) ) { + $temp = [ + 'taxonomy' => substr( $key, 4 ), + 'field' => 'slug', + 'operator' => $compare + ]; + + if ( ! $exists_bypass ) { + $temp['terms'] = $value; + } + + $tax_query[] = $temp; + } + else { + $temp = [ + 'key' => substr( $key, strpos( $key, '/' ) + 1 ), + 'compare' => $compare, + 'type' => $type + ]; + + if ( ! $exists_bypass ) { + $temp['value'] = $value; + } + + $meta_query[] = $temp; + } + } + + foreach ( $query_obj['orderby'] as $index => $data ) { + if ( 'cf/' == substr( $data['key'], 0, 3 ) ) { + $type = $data['type']; + + // Cast as decimal for more accuracy + $type = ( 'NUMERIC' == $type ) ? 'DECIMAL(16,4)' : $type; + + $meta_query['sort_' . $index] = [ + 'key' => substr( $data['key'], 3 ), + 'type' => $type + ]; + + $orderby['sort_' . $index] = $data['order']; + } + else { + $orderby[ $data['key'] ] = $data['order']; + } + } + + $temp = [ + 'post_type' => $post_type, + 'post_status' => $post_status, + 'meta_query' => $meta_query, + 'tax_query' => $tax_query, + 'date_query' => $date_query, + 'post__in' => $post_in, + 'post__not_in' => $post_not_in, + 'author__in' => $author_in, + 'author__not_in' => $author_not_in, + 'orderby' => $orderby, + 'posts_per_page' => $posts_per_page + ]; + + foreach ( $temp as $key => $val ) { + if ( ! empty( $val ) ) { + $output[ $key ] = $val; + } + } + + return $output; + } + + + /** + * Get necessary values for the layout builder + * @since 3.2.0 + */ + function get_layout_data() { + $sources = FWP()->helper->get_data_sources(); + unset( $sources['post'] ); + + // Static options + $output = [ + 'row' => 'Child Row', + 'html' => 'HTML', + 'button' => 'Button', + 'featured_image' => 'Featured Image', + 'ID' => 'Post ID', + 'post_title' => 'Post Title', + 'post_name' => 'Post Name', + 'post_content' => 'Post Content', + 'post_excerpt' => 'Post Excerpt', + 'post_date' => 'Post Date', + 'post_modified' => 'Post Modified', + 'post_author' => 'Post Author', + 'post_type' => 'Post Type' + ]; + + foreach ( $sources as $group ) { + foreach ( $group['choices'] as $name => $label ) { + $output[ $name ] = $label; + } + } + + return $output; + } + + + /** + * Get necessary data for the query builder + * @since 3.0.0 + */ + function get_query_data() { + $builder_post_types = []; + + $post_types = get_post_types( [ 'public' => true ], 'objects' ); + $data_sources = FWP()->helper->get_data_sources( 'builder' ); + + // Remove ACF choices + unset( $data_sources['acf'] ); + + foreach ( $post_types as $type ) { + $builder_post_types[] = [ + 'label' => $type->labels->name, + 'value' => $type->name + ]; + } + + $data_sources['posts']['choices'] = [ + 'ID' => 'ID', + 'post_author' => 'Post Author', + 'post_status' => 'Post Status', + 'post_date' => 'Post Date', + 'post_modified' => 'Post Modified' + ]; + + return apply_filters( 'facetwp_builder_query_data', [ + 'post_types' => $builder_post_types, + 'filter_by' => $data_sources + ] ); + } + + + /** + * Replace "date|" placeholders with actual dates + */ + function hydrate_date_values( $query_args ) { + if ( isset( $query_args['meta_query'] ) ) { + foreach ( $query_args['meta_query'] as $index => $row ) { + if ( isset( $row['value'] ) && is_string( $row['value'] ) && 0 === strpos( $row['value'], 'date|' ) ) { + $value = trim( substr( $row['value'], 5 ) ); + $value = date( 'Y-m-d', strtotime( $value ) ); + $query_args['meta_query'][ $index ]['value'] = $value; + } + } + } + + return $query_args; + } + + + /** + * Let users pull URI or GET params into the query builder + * E.g. "http:uri", "http:uri:0", or "http:get:year" + * @since 3.6.0 + */ + function parse_uri_tags( $values ) { + $temp = (array) $values; + + foreach ( $temp as $key => $value ) { + if ( 0 === strpos( $value, 'http:uri' ) ) { + $uri = FWP()->helper->get_uri(); + $uri_parts = explode( '/', $uri ); + $tag_parts = explode( ':', $value ); + if ( isset( $tag_parts[2] ) ) { + $index = (int) $tag_parts[2]; + $index = ( $index < 0 ) ? count( $uri_parts ) + $index : $index; + $temp[ $key ] = $uri_parts[ $index ] ?? ''; + } + else { + $temp[ $key ] = $uri; + } + } + elseif ( 0 === strpos( $value, 'http:get:' ) ) { + $tag_parts = explode( ':', $value ); + $temp[ $key ] = $_GET[ $tag_parts[2] ] ?? ''; + } + } + + return is_array( $values ) ? $temp : $temp[0]; + } + + /** + * Initialize CodeMirror for Listing Builder editors + * @since 4.3.2 + */ + function initialize_builder_editor( $hook ) { + if ( 'settings_page_facetwp' == $hook ) { + $fwp_editor_settings = wp_enqueue_code_editor( array( 'type' => 'php' ) ); + wp_localize_script( 'jquery', 'fwp_editor_settings', $fwp_editor_settings ); + } + } +} diff --git a/wp-content/plugins/facetwp/includes/class-diff.php b/wp-content/plugins/facetwp/includes/class-diff.php new file mode 100644 index 000000000..1f16795e1 --- /dev/null +++ b/wp-content/plugins/facetwp/includes/class-diff.php @@ -0,0 +1,73 @@ +helper->load_settings(); + $s2 = FWP()->helper->load_settings( true ); + + // Compare settings + $to_check = [ 'thousands_separator', 'decimal_separator', 'wc_enable_variations', 'wc_index_all' ]; + + foreach ( $to_check as $name ) { + $attr1 = $this->get_attr( $name, $s1['settings'] ); + $attr2 = $this->get_attr( $name, $s2['settings'] ); + if ( $attr1 !== $attr2 ) { + return true; + } + } + + // Get facets, removing non-indexable ones + $f1 = array_filter( $s1['facets'], [ $this, 'is_indexable' ] ); + $f2 = array_filter( $s2['facets'], [ $this, 'is_indexable' ] ); + + // The facet count is different + if ( count( $f1 ) !== count( $f2 ) ) { + return true; + } + + // Sort the facets alphabetically + usort( $f1, function( $a, $b ) { + return strcmp( $a['name'], $b['name'] ); + }); + + usort( $f2, function( $a, $b ) { + return strcmp( $a['name'], $b['name'] ); + }); + + // Compare facet properties + $to_check = [ 'name', 'type', 'source', 'source_other', 'parent_term', 'hierarchical', 'modifier_type', 'modifier_values' ]; + + foreach ( $f1 as $index => $facet ) { + foreach ( $to_check as $attr ) { + $attr1 = $this->get_attr( $attr, $facet ); + $attr2 = $this->get_attr( $attr, $f2[ $index ] ); + if ( $attr1 !== $attr2 ) { + return true; + } + } + } + + return false; + } + + + function is_indexable( $facet ) { + return ! in_array( $facet['type'], [ 'search', 'pager', 'reset', 'sort' ] ); + } + + + /** + * Get an array element + * @since 3.0.9 + */ + function get_attr( $name, $collection ) { + return $collection[ $name ] ?? false; + } +} diff --git a/wp-content/plugins/facetwp/includes/class-display.php b/wp-content/plugins/facetwp/includes/class-display.php new file mode 100644 index 000000000..e7f26f862 --- /dev/null +++ b/wp-content/plugins/facetwp/includes/class-display.php @@ -0,0 +1,304 @@ +get( 'facetwp' ) && did_action( 'wp_head' ) ) { + echo "\n"; + } + } + + + /** + * Set default values for atts + * + * Old: [facetwp template="foo" static] + * New: [facetwp template="foo" static="true"] + */ + function normalize_atts( $atts ) { + foreach ( $atts as $key => $val ) { + if ( is_int( $key ) ) { + $atts[ $val ] = true; + unset( $atts[ $key ] ); + } + } + return $atts; + } + + + /** + * Register shortcodes + */ + function shortcode( $atts ) { + $atts = $this->normalize_atts( $atts ); + $this->shortcode_atts[] = $atts; + + $output = ''; + if ( isset( $atts['facet'] ) ) { + $facet = FWP()->helper->get_facet_by_name( $atts['facet'] ); + + if ( $facet ) { + $ui = empty( $facet['ui_type'] ) ? $facet['type'] : $facet['ui_type']; + $ui_attr = empty( $facet['ui_type'] ) ? '' : ' data-ui="' . $ui . '"'; + $output = '
    '; + + // Build list of active facet types + $this->active_types[ $facet['type'] ] = $facet['type']; + $this->active_facets[ $facet['name'] ] = $facet['name']; + $this->load_assets = true; + } + } + elseif ( isset( $atts['template'] ) ) { + $template = FWP()->helper->get_template_by_name( $atts['template'] ); + + if ( $template ) { + $class_name = 'facetwp-template'; + + // Static template + if ( isset( $atts['static'] ) ) { + $renderer = new FacetWP_Renderer(); + $renderer->template = $template; + $renderer->query_args = $renderer->get_query_args(); + $renderer->query = new WP_Query( $renderer->query_args ); + $html = $renderer->get_template_html(); + $class_name .= '-static'; + } + // Preload template (search engine visible) + else { + global $wp_query; + + $temp_query = $wp_query; + $args = FWP()->request->process_preload_data( $template['name'] ); + $preload_data = FWP()->facet->render( $args ); + $html = $preload_data['template']; + $wp_query = $temp_query; + + $this->load_assets = true; + } + + $output = '
    {html}
    '; + $output = str_replace( '{class}', $class_name, $output ); + $output = str_replace( '{name}', $atts['template'], $output ); + $output = str_replace( '{html}', $html, $output ); + } + } + elseif ( isset( $atts['sort'] ) ) { + $this->active_extras['sort'] = true; + $output = '
    '; + } + elseif ( isset( $atts['selections'] ) ) { + $output = '
    '; + } + elseif ( isset( $atts['counts'] ) ) { + $this->active_extras['counts'] = true; + $output = '
    '; + } + elseif ( isset( $atts['pager'] ) ) { + $this->active_extras['pager'] = true; + $output = '
    '; + } + elseif ( isset( $atts['per_page'] ) ) { + $this->active_extras['per_page'] = true; + $output = '
    '; + } + + $output = apply_filters( 'facetwp_shortcode_html', $output, $atts ); + + return $output; + } + + /** + * get google api key from GMAPS_API_KEY, facetwp_gmaps_api_key filter, or gmaps_api_key setting + * @since 4.4 + * */ + function get_gmaps_api_key() { + + // hard-coded + $api_key = defined( 'GMAPS_API_KEY' ) ? GMAPS_API_KEY : ''; + + // admin ui + $tmp_key = FWP()->helper->get_setting( 'gmaps_api_key' ); + $api_key = empty( $tmp_key ) ? $api_key : $tmp_key; + + // API key hook + return apply_filters( 'facetwp_gmaps_api_key', $api_key ); + + } + + /** + * backwards compatibility for params added in facetwp_gmaps_url filter + * @since 4.4 + */ + function gmaps_params( $params ) { + if ( has_filter( 'facetwp_gmaps_url' ) ) { + $gmaps_url = apply_filters( 'facetwp_gmaps_url', '//maps.googleapis.com/maps/api/js?libraries=places' ); // old url + parse_str( parse_url( $gmaps_url, PHP_URL_QUERY ), $query_params ); // get array of params + $query_params = array_diff_key( $query_params, [ 'libraries' => '', 'key' => '', 'callback' => '' ] ); // remove unneeded params + $params = array_merge( $params, $query_params ); + } + return $params; + } + + + /** + * Output facet scripts + */ + function front_scripts() { + + // Not enqueued - front.js needs to load before front_scripts() + if ( apply_filters( 'facetwp_load_assets', $this->load_assets ) ) { + + // Load CSS? + if ( apply_filters( 'facetwp_load_css', true ) ) { + $this->assets['front.css'] = FACETWP_URL . '/assets/css/front.css'; + } + + // Load required JS + $this->assets['front.js'] = FACETWP_URL . '/assets/js/dist/front.min.js'; + + // Backwards compat? + if ( apply_filters( 'facetwp_load_deprecated', false ) ) { + $this->assets['front-deprecated.js'] = FACETWP_URL . '/assets/js/src/deprecated.js'; + } + + // Load a11y? + $a11y = FWP()->helper->get_setting( 'load_a11y', 'no' ); + $a11y_hook = apply_filters( 'facetwp_load_a11y', false ); + + if ( 'yes' == $a11y || $a11y_hook ) { + $this->assets['accessibility.js'] = FACETWP_URL . '/assets/js/src/accessibility.js'; + $this->json['a11y'] = [ + 'label_page' => __( 'Go to page', 'fwp-front' ), + 'label_page_next' => __( 'Go to next page', 'fwp-front' ), + 'label_page_prev' => __( 'Go to previous page', 'fwp-front' ) + ]; + } + + // Pass GET and URI params + $http_params = [ + 'get' => $_GET, + 'uri' => FWP()->helper->get_uri(), + 'url_vars' => FWP()->request->url_vars, + ]; + + // See FWP()->facet->get_query_args() + if ( ! empty( FWP()->facet->archive_args ) ) { + $http_params['archive_args'] = FWP()->facet->archive_args; + } + + // Populate the FWP_JSON object + $this->json['prefix'] = FWP()->helper->get_setting( 'prefix' ); + $this->json['no_results_text'] = __( 'No results found', 'fwp-front' ); + $this->json['ajaxurl'] = get_rest_url() . 'facetwp/v1/refresh'; + $this->json['nonce'] = wp_create_nonce( 'wp_rest' ); + + if ( apply_filters( 'facetwp_use_preloader', true ) ) { + $overrides = FWP()->request->process_preload_overrides([ + 'facets' => $this->active_facets, + 'extras' => $this->active_extras, + ]); + $args = FWP()->request->process_preload_data( false, $overrides ); + $this->json['preload_data'] = FWP()->facet->render( $args ); + } + + ob_start(); + + foreach ( $this->active_types as $type ) { + $facet_class = FWP()->helper->facet_types[ $type ]; + if ( method_exists( $facet_class, 'front_scripts' ) ) { + $facet_class->front_scripts(); + } + } + + $inline_scripts = ob_get_clean(); + + if ( apply_filters( 'facetwp_load_gmaps', false ) ) { + + // remove non-async gmaps + add_filter( 'facetwp_assets', function( $assets ) { + unset( $assets['gmaps'] ); + return $assets; + }); + + $params = [ + 'key' => $this->get_gmaps_api_key(), + 'v' => 'quarterly', + ]; + $params_string = ''; + foreach ( apply_filters( 'facetwp_gmaps_params', $params ) AS $param => $val ) { + $params_string .= $param . ': "' . esc_attr($val) . '",'; + } +?> + +assets ); + + foreach ( $assets as $slug => $data ) { + $data = (array) $data; + $is_css = ( 'css' == substr( $slug, -3 ) ); + $version = empty( $data[1] ) ? FACETWP_VERSION : $data[1]; + $url = $data[0]; + + if ( false !== strpos( $url, 'facetwp' ) ) { + $prefix = ( false !== strpos( $url, '?' ) ) ? '&' : '?'; + $url .= $prefix . 'ver=' . $version; + } + + $html = $is_css ? '' : ''; + $html = apply_filters( 'facetwp_asset_html', $html, $url ); + echo str_replace( '{url}', $url, $html ) . "\n"; + } + + echo $inline_scripts; + + do_action( 'facetwp_scripts' ); +?> + +facet_types = $this->get_facet_types(); + $this->settings = $this->load_settings(); + } + + + /** + * Parse the URL hostname + */ + function get_http_host() { + return parse_url( get_option( 'home' ), PHP_URL_HOST ); + } + + + /** + * Get the current page URI + */ + function get_uri() { + if ( isset( FWP()->facet->http_params ) ) { + return FWP()->facet->http_params['uri']; + } + + $uri = parse_url( $_SERVER['REQUEST_URI'] ); + return isset( $uri['path'] ) ? trim( $uri['path'], '/' ) : ''; + } + + + /** + * Get available facet types * + * @since 4.4 Added Map type facet + */ + function get_facet_types() { + if ( ! empty( $this->facet_types ) ) { + return $this->facet_types; + } + + include( FACETWP_DIR . '/includes/facets/base.php' ); + + $types = [ + 'checkboxes' => 'Facetwp_Facet_Checkboxes', + 'dropdown' => 'Facetwp_Facet_Dropdown', + 'radio' => 'Facetwp_Facet_Radio_Core', + 'fselect' => 'Facetwp_Facet_fSelect', + 'hierarchy' => 'Facetwp_Facet_Hierarchy', + 'slider' => 'Facetwp_Facet_Slider', + 'search' => 'Facetwp_Facet_Search', + 'autocomplete' => 'Facetwp_Facet_Autocomplete', + 'date_range' => 'Facetwp_Facet_Date_Range', + 'number_range' => 'Facetwp_Facet_Number_Range', + 'rating' => 'FacetWP_Facet_Rating', + 'proximity' => 'Facetwp_Facet_Proximity_Core', + 'map' => 'Facetwp_Facet_Map', + 'pager' => 'FacetWP_Facet_Pager', + 'reset' => 'FacetWP_Facet_Reset', + 'sort' => 'FacetWP_Facet_Sort' + ]; + + $facet_types = []; + + foreach ( $types as $slug => $class_name ) { + include( FACETWP_DIR . "/includes/facets/$slug.php" ); + $facet_types[ $slug ] = new $class_name(); + } + + return apply_filters( 'facetwp_facet_types', $facet_types ); + } + + + /** + * Get settings and allow for developer hooks + */ + function load_settings( $last_index = false ) { + $name = $last_index ? 'facetwp_settings_last_index' : 'facetwp_settings'; + $option = get_option( $name ); + + $defaults = [ + 'facets' => [], + 'templates' => [], + 'settings' => [ + 'thousands_separator' => ',', + 'decimal_separator' => '.', + 'prefix' => '_', + 'load_jquery' => 'no' + ] + ]; + + $settings = ( false !== $option ) ? json_decode( $option, true ) : []; + $settings = array_merge( $defaults, $settings ); + $settings['settings'] = array_merge( $defaults['settings'], $settings['settings'] ); + + // Store DB-based facet & template names + $db_names = []; + + foreach ( $settings['facets'] as $facet ) { + $db_names[ 'facet-' . $facet['name'] ] = true; + } + + foreach ( $settings['templates'] as $template ) { + $db_names[ 'template-' . $template['name'] ] = true; + } + + // Programmatically registered + $facets = apply_filters( 'facetwp_facets', $settings['facets'] ); + $templates = apply_filters( 'facetwp_templates', $settings['templates'] ); + + $tmp_facets = []; + $tmp_templates = []; + + // Merge DB + code-based facets + foreach ( $facets as $facet ) { + $name = $facet['name']; + $is_db_based = isset( $db_names[ "facet-$name" ] ); + + if ( ! $is_db_based ) { + $facet['_code'] = true; + } + + if ( ! $is_db_based || empty( $tmp_facets[ $name ] ) ) { + + // Valid facet type? + if ( in_array( $facet['type'], array_keys( $this->facet_types ) ) ) { + $defaults = $this->facet_types[ $facet['type'] ]->field_defaults ?? []; + $facet = array_merge( $defaults, $facet ); + $tmp_facets[ $name ] = $facet; + } + } + } + + // Merge DB + code-based templates + foreach ( $templates as $template ) { + $name = $template['name']; + $is_db_based = isset( $db_names[ "template-$name" ] ); + + if ( ! $is_db_based ) { + $template['_code'] = true; + } + + if ( ! $is_db_based || empty( $tmp_templates[ $name ] ) ) { + $tmp_templates[ $name ] = $template; + } + } + + // Convert back to numerical arrays + $settings['facets'] = array_values( $tmp_facets ); + $settings['templates'] = array_values( $tmp_templates ); + + // Filtered settings + return $settings; + } + + + /** + * Get a general setting value + * + * @param string $name The setting name + * @param mixed $default The default value + * @since 1.9 + */ + function get_setting( $name, $default = '' ) { + return $this->settings['settings'][ $name ] ?? $default; + } + + + /** + * Get an array of all facets + * @return array + */ + function get_facets() { + return $this->settings['facets']; + } + + + /** + * Get an array of all templates + * @return array + */ + function get_templates() { + return $this->settings['templates']; + } + + + /** + * Get all properties for a single facet + * @param string $facet_name + * @return mixed An array of facet info, or false + */ + function get_facet_by_name( $facet_name ) { + foreach ( $this->get_facets() as $facet ) { + if ( $facet_name == $facet['name'] ) { + return $facet; + } + } + + return false; + } + + + /** + * Get all properties for a single template + * + * @param string $template_name + * @return mixed An array of template info, or false + */ + function get_template_by_name( $template_name ) { + foreach ( $this->get_templates() as $template ) { + if ( $template_name == $template['name'] ) { + return $template; + } + } + + return false; + } + + + /** + * Fetch facets using one of its settings + * @param string $setting_name + * @param mixed $setting_value + * @return array + */ + function get_facets_by( $setting, $value ) { + $matches = []; + + foreach ( $this->get_facets() as $facet ) { + if ( isset( $facet[ $setting ] ) && $value === $facet[ $setting ] ) { + $matches[] = $facet; + } + } + + return $matches; + } + + + /** + * Fetch facets by data source type (prefix, ie tax, cf, acf ...) + * @param string $value + * @return array + * @since 4.4 + */ + function get_facets_by_datasource_type( $value ) { + $matches = []; + + foreach ( $this->get_facets() as $facet ) { + if ( isset( $facet[ 'source' ] ) && false !== strpos( $facet['source'], $value ) ) { + $matches[] = $facet; + } + } + + return $matches; + } + + + /** + * Get terms across all languages (thanks, WPML) + * @since 3.8.5 + */ + function get_terms( $taxonomy ) { + global $wpdb; + + $sql = " + SELECT t.term_id, t.name, t.slug, tt.parent FROM {$wpdb->term_taxonomy} tt + INNER JOIN {$wpdb->terms} t ON t.term_id = tt.term_id + WHERE tt.taxonomy = %s"; + + return $wpdb->get_results( $wpdb->prepare( $sql, $taxonomy ) ); + } + + + /** + * Get an array of term information, including depth + * @param string $taxonomy The taxonomy name + * @return array Term information + * @since 0.9.0 + */ + function get_term_depths( $taxonomy ) { + + if ( isset( $this->term_cache[ $taxonomy ] ) ) { + return $this->term_cache[ $taxonomy ]; + } + + $output = []; + $parents = []; + + $terms = $this->get_terms( $taxonomy ); + + // Get term parents + foreach ( $terms as $term ) { + $parents[ $term->term_id ] = $term->parent; + } + + // Build the term array + foreach ( $terms as $term ) { + $output[ $term->term_id ] = [ + 'term_id' => $term->term_id, + 'name' => $term->name, + 'slug' => $term->slug, + 'parent_id' => $term->parent, + 'depth' => 0 + ]; + + $current_parent = $term->parent; + while ( 0 < (int) $current_parent ) { + $current_parent = $parents[ $current_parent ]; + $output[ $term->term_id ]['depth']++; + + // Prevent an infinite loop + if ( 50 < $output[ $term->term_id ]['depth'] ) { + break; + } + } + } + + $this->term_cache[ $taxonomy ] = $output; + + return $output; + } + + + /** + * Finish sorting the facet values + * The results are already sorted by depth and (name OR count), we just need + * to move the children directly below their parents + */ + function sort_taxonomy_values( $values = [], $orderby = 'count' ) { + $final = []; + $cache = []; + + // Create an "order" sort value based on the top-level items + foreach ( $values as $key => $val ) { + if ( 0 == $val['depth'] ) { + $val['order'] = $key; + $cache[ $val['term_id'] ] = $key; + $final[] = $val; + } + elseif ( isset( $cache[ $val['parent_id'] ] ) ) { // skip orphans + $val['order'] = $cache[ $val['parent_id'] ] . ".$key"; // dot-separated hierarchy string + $cache[ $val['term_id'] ] = $val['order']; + $final[] = $val; + } + } + + // Sort the array based on the new "order" element + // Since this is a dot-separated hierarchy string, use version_compare + usort( $final, function( $a, $b ) { + return version_compare( $a['order'], $b['order'] ); + }); + + return $final; + } + + + /** + * Sanitize SQL data + * @return mixed The sanitized value(s) + * @since 3.0.7 + */ + function sanitize( $input ) { + global $wpdb; + + if ( is_array( $input ) ) { + $output = []; + + foreach ( $input as $key => $val ) { + $output[ $key ] = $this->sanitize( $val ); + } + } + else { + // Use mysqli_real_escape_string if WP 6.4+ or $wpdb->use_mysqli is true + // The $wpdb->use_mysqli property was removed in WP 6.4 + // Since the mysqli driver is now required + if ( ! isset( $wpdb->use_mysqli ) || ( $wpdb->dbh && $wpdb->use_mysqli ) ) { + $output = mysqli_real_escape_string( $wpdb->dbh, $input ); + } + else { + $output = addslashes( $input ); + } + } + + return $output; + } + + + /** + * Escape output data + * @return mixed the escaped value(s) + * @since 4.2.0 + */ + function escape( $input ) { + if ( is_array( $input ) ) { + $output = []; + + foreach ( $input as $key => $val ) { + $output[ $key ] = $this->escape( $val ); + } + } + else { + $output = htmlspecialchars( $input ); + } + + return $output; + } + + + /** + * Does an active facet with the specified setting exist? + * @return boolean + * @since 1.4.0 + */ + function facet_setting_exists( $setting_name, $setting_value ) { + foreach ( FWP()->facet->facets as $f ) { + if ( isset( $f[ $setting_name ] ) && $f[ $setting_name ] == $setting_value ) { + return true; + } + } + + return false; + } + + + /** + * Does this facet have a setting with the specified value? + * @return boolean + * @since 2.3.4 + */ + function facet_is( $facet, $setting_name, $setting_value ) { + if ( is_string( $facet ) ) { + $facet = $this->get_facet_by_name( $facet ); + } + + if ( isset( $facet[ $setting_name ] ) && $facet[ $setting_name ] == $setting_value ) { + return true; + } + + return false; + } + + + /** + * Hash a facet value if needed + * @return string + * @since 2.1 + */ + function safe_value( $value ) { + $value = remove_accents( $value ); + + if ( preg_match( '/[^a-z0-9_.\- ]/i', $value ) ) { + if ( ! preg_match( '/^\d{4}-(0[1-9]|1[012])-([012]\d|3[01])/', $value ) ) { + $value = md5( $value ); + } + } + + $value = str_replace( ' ', '-', strtolower( $value ) ); + $value = preg_replace( '/[-]{2,}/', '-', $value ); + $value = ( 50 < strlen( $value ) ) ? substr( $value, 0, 50 ) : $value; + return $value; + } + + + /** + * Properly format numbers, taking separators into account + * @return number + * @since 2.7.5 + */ + function format_number( $num ) { + $sep_decimal = $this->get_setting( 'decimal_separator' ); + $sep_thousands = $this->get_setting( 'thousands_separator' ); + + $num = str_replace( $sep_thousands, '', $num ); + $num = ( ',' == $sep_decimal ) ? str_replace( ',', '.', $num ) : $num; + $num = preg_replace( '/[^0-9-.]/', '', $num ); + + return $num; + } + + + /** + * Get facet data sources + * @return array + * @since 2.2.1 + */ + function get_data_sources( $context = 'default' ) { + global $wpdb; + + // Cached? + if ( ! empty( $this->data_sources ) ) { + $sources = $this->data_sources; + } + else { + + // Get excluded meta keys + $excluded_fields = apply_filters( 'facetwp_excluded_custom_fields', [ + '_edit_last', + '_edit_lock', + ] ); + + // Get taxonomies + $taxonomies = get_taxonomies( [], 'object' ); + + // Get custom fields + $meta_keys = $wpdb->get_col( "SELECT DISTINCT meta_key FROM {$wpdb->postmeta} ORDER BY meta_key" ); + $custom_fields = array_diff( $meta_keys, $excluded_fields ); + + $sources = [ + 'posts' => [ + 'label' => __( 'Posts', 'fwp' ), + 'choices' => [ + 'post_type' => __( 'Post Type', 'fwp' ), + 'post_date' => __( 'Post Date', 'fwp' ), + 'post_modified' => __( 'Post Modified', 'fwp' ), + 'post_title' => __( 'Post Title', 'fwp' ), + 'post_author' => __( 'Post Author', 'fwp' ) + ], + 'weight' => 10 + ], + 'taxonomies' => [ + 'label' => __( 'Taxonomies', 'fwp' ), + 'choices' => [], + 'weight' => 20 + ], + 'custom_fields' => [ + 'label' => __( 'Custom Fields', 'fwp' ), + 'choices' => [], + 'weight' => 30 + ] + ]; + + foreach ( $taxonomies as $tax ) { + $sources['taxonomies']['choices'][ 'tax/' . $tax->name ] = $tax->labels->name . ' (' . $tax->name . ')'; + } + + foreach ( $custom_fields as $cf ) { + if ( 0 !== strpos( $cf, '_oembed_' ) ) { + $sources['custom_fields']['choices'][ 'cf/' . $cf ] = $cf; + } + } + + $this->data_sources = $sources; + } + + $sources = apply_filters( 'facetwp_facet_sources', $sources, $context ); + + uasort( $sources, [ $this, 'sort_by_weight' ] ); + + return $sources; + } + + + /** + * Sort facetwp_facet_sources by weight + * @since 2.7.5 + */ + function sort_by_weight( $a, $b ) { + $a['weight'] = $a['weight'] ?? 10; + $b['weight'] = $b['weight'] ?? 10; + + if ( $a['weight'] == $b['weight'] ) { + return 0; + } + + return ( $a['weight'] < $b['weight'] ) ? -1 : 1; + } + + + /** + * Get row counts for all facets + * @since 3.3.4 + */ + function get_row_counts() { + global $wpdb; + + $output = []; + $results = $wpdb->get_results( "SELECT facet_name, COUNT(*) AS row_count FROM {$wpdb->prefix}facetwp_index GROUP BY facet_name" ); + + foreach ( $results as $result ) { + $output[ $result->facet_name ] = (int) $result->row_count; + } + + return $output; + } + + /** + * Get indexable post types + */ + function get_indexable_types() { + + $args = FWP()->indexer->get_query_args(); + $types = (array) ( $args['post_type'] ?? 'post' ); + $statuses = (array) ( $args['post_status'] ?? 'publish' ); + + if ( ! in_array( 'inherit', $statuses ) && in_array( 'attachment', $types ) ) { + $types = array_values( array_diff( $types, [ 'attachment' ] ) ); + } + + sort( $types ); + + return $types; + + } + + /** + * Grab the license key + * @since 3.0.3 + */ + function get_license_key() { + $license_key = defined( 'FACETWP_LICENSE_KEY' ) ? FACETWP_LICENSE_KEY : get_option( 'facetwp_license' ); + $license_key = apply_filters( 'facetwp_license_key', $license_key ); + return sanitize_key( trim( $license_key ) ); + } + + + /** + * Determine whether the license is active + * @since 3.3.0 + */ + function is_license_active() { + return ( 'success' == $this->get_license_meta( 'status' ) ); + } + + + /** + * Get a license meta value + * Possible keys: status, message, expiration, payment_id, price_id + * @since 3.5.3 + */ + function get_license_meta( $key = 'status' ) { + $activation = get_option( 'facetwp_activation' ); + + if ( ! empty( $activation ) ) { + $data = json_decode( $activation, true ); + + if ( isset( $data[ $key ] ) ) { + return $data[ $key ]; + } + } + + return false; + } +} diff --git a/wp-content/plugins/facetwp/includes/class-indexer.php b/wp-content/plugins/facetwp/includes/class-indexer.php new file mode 100644 index 000000000..44af6b12c --- /dev/null +++ b/wp-content/plugins/facetwp/includes/class-indexer.php @@ -0,0 +1,721 @@ +set_table( 'auto' ); + + add_action( 'facetwp_indexer_cron', [ $this, 'get_progress' ] ); + + $this->run_cron(); + + if ( apply_filters( 'facetwp_indexer_is_enabled', true ) ) { + $this->run_hooks(); + } + } + + + /** + * Event listeners + * @since 2.8.4 + */ + function run_hooks() { + add_action( 'save_post', [ $this, 'save_post' ] ); + add_action( 'delete_post', [ $this, 'delete_post' ] ); + add_action( 'edited_term', [ $this, 'edit_term' ], 10, 3 ); + add_action( 'delete_term', [ $this, 'delete_term' ], 10, 3 ); + add_action( 'set_object_terms', [ $this, 'set_object_terms' ] ); + add_filter( 'wp_insert_post_parent', [ $this, 'is_wp_insert_post' ] ); + } + + + /** + * Cron task + * @since 2.8.5 + */ + function run_cron() { + if ( ! wp_next_scheduled( 'facetwp_indexer_cron' ) ) { + wp_schedule_single_event( time() + 300, 'facetwp_indexer_cron' ); + } + } + + + /** + * Update the index when posts get saved + * @since 0.1.0 + */ + function save_post( $post_id ) { + if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { + return; + } + + if ( false !== wp_is_post_revision( $post_id ) ) { + return; + } + + if ( 'auto-draft' == get_post_status( $post_id ) ) { + return; + } + + // late check to disable indexing after __construct is already loaded + if ( !apply_filters( 'facetwp_indexer_is_enabled', true ) ) { + return; + } + + $this->index( $post_id ); + $this->is_saving = false; + } + + + /** + * Update the index when posts get deleted + * @since 0.6.0 + */ + function delete_post( $post_id ) { + global $wpdb; + + $wpdb->query( "DELETE FROM {$this->table} WHERE post_id = $post_id" ); + } + + + /** + * Update the index when terms get saved + * @since 0.6.0 + */ + function edit_term( $term_id, $tt_id, $taxonomy ) { + global $wpdb; + + $term = get_term( $term_id, $taxonomy ); + $slug = FWP()->helper->safe_value( $term->slug ); + $matches = FWP()->helper->get_facets_by( 'source', "tax/$taxonomy" ); + + if ( ! empty( $matches ) ) { + $facet_names = wp_list_pluck( $matches, 'name' ); + $facet_names = implode( "','", esc_sql( $facet_names ) ); + + $wpdb->query( $wpdb->prepare( " + UPDATE {$this->table} + SET facet_value = %s, facet_display_value = %s + WHERE facet_name IN ('$facet_names') AND term_id = %d", + $slug, $term->name, $term_id + ) ); + } + } + + + /** + * Update the index when terms get deleted + * @since 0.6.0 + */ + function delete_term( $term_id, $tt_id, $taxonomy ) { + global $wpdb; + + $matches = FWP()->helper->get_facets_by( 'source', "tax/$taxonomy" ); + + if ( ! empty( $matches ) ) { + $facet_names = wp_list_pluck( $matches, 'name' ); + $facet_names = implode( "','", esc_sql( $facet_names ) ); + + $wpdb->query( " + DELETE FROM {$this->table} + WHERE facet_name IN ('$facet_names') AND term_id = $term_id" + ); + } + } + + + /** + * We're hijacking wp_insert_post_parent + * Prevent our set_object_terms() hook from firing within wp_insert_post + * @since 2.2.2 + */ + function is_wp_insert_post( $post_parent ) { + $this->is_saving = true; + return $post_parent; + } + + + /** + * Support for manual taxonomy associations + * @since 0.8.0 + */ + function set_object_terms( $object_id ) { + // late check to disable indexing after __construct is already loaded + if ( ! $this->is_saving && apply_filters( 'facetwp_indexer_is_enabled', true ) ) { + $this->index( $object_id ); + } + } + + + /** + * Rebuild the facet index + * @param mixed $post_id The post ID (set to FALSE to re-index everything) + */ + function index( $post_id = false ) { + global $wpdb; + + $this->index_all = ( false === $post_id ); + + // Index everything + if ( $this->index_all ) { + + // Store the pre-index settings (see FacetWP_Diff) + update_option( 'facetwp_settings_last_index', get_option( 'facetwp_settings' ), 'no' ); + + // PHP sessions are blocking, so close if active + if ( PHP_SESSION_ACTIVE === session_status() ) { + session_write_close(); + } + + // Bypass the PHP timeout + ini_set( 'max_execution_time', 0 ); + + // Prevent multiple indexing processes + $touch = (int) $this->get_transient( 'touch' ); + + if ( 0 < $touch ) { + // Run only if the indexer is inactive or stalled + if ( ( time() - $touch ) < 60 ) { + exit; + } + } + else { + // Create temp table + $this->manage_temp_table( 'create' ); + } + } + // Index a single post + elseif ( is_int( $post_id ) ) { + + // Clear table values + $wpdb->query( "DELETE FROM {$this->table} WHERE post_id = $post_id" ); + } + // Exit + else { + return; + } + + // Resume indexing? + $offset = (int) ( $_POST['facetwp_offset'] ?? 0 ); + $attempt = (int) ( $_POST['facetwp_retries'] ?? 0 ); + + if ( 0 < $offset ) { + $post_ids = json_decode( get_option( 'facetwp_indexing' ), true ); + } + else { + $post_ids = $this->get_post_ids_to_index( $post_id ); + + // Store post IDs + if ( $this->index_all ) { + update_option( 'facetwp_indexing', json_encode( $post_ids ) ); + } + } + + // Count total posts + $num_total = count( $post_ids ); + + // Get all facet sources + $facets = FWP()->helper->get_facets(); + + // Populate an array of facet value modifiers + $this->load_value_modifiers( $facets ); + + foreach ( $post_ids as $counter => $post_id ) { + + // Advance until we reach the offset + if ( $counter < $offset ) { + continue; + } + + // Update the progress bar + if ( $this->index_all ) { + if ( 0 == ( $counter % $this->chunk_size ) ) { + $num_retries = (int) $this->get_transient( 'retries' ); + + // Exit if newer retries exist + if ( $attempt < $num_retries ) { + exit; + } + + // Exit if the indexer was cancelled + wp_cache_delete( 'facetwp_indexing_cancelled', 'options' ); + + if ( 'yes' === get_option( 'facetwp_indexing_cancelled', 'no' ) ) { + update_option( 'facetwp_indexing_data', '' ); + update_option( 'facetwp_indexing', '' ); + $this->manage_temp_table( 'delete' ); + exit; + } + + $transients = [ + 'num_indexed' => $counter, + 'num_total' => $num_total, + 'retries' => $attempt, + 'touch' => time(), + ]; + update_option( 'facetwp_indexing_data', json_encode( $transients ) ); + } + } + + // If the indexer stalled, start from the last valid chunk + if ( 0 < $offset && ( $counter - $offset < $this->chunk_size ) ) { + $wpdb->query( "DELETE FROM {$this->table} WHERE post_id = $post_id" ); + } + + $this->index_post( $post_id, $facets ); + } + + // Indexing complete + if ( $this->index_all ) { + update_option( 'facetwp_last_indexed', time(), 'no' ); + update_option( 'facetwp_indexing_data', '', 'no' ); + update_option( 'facetwp_indexing', '', 'no' ); + + $this->manage_temp_table( 'replace' ); + $this->manage_temp_table( 'delete' ); + } + + do_action( 'facetwp_indexer_complete' ); + } + + + /** + * Get the array of indexer query args + * @since 4.1.8 + */ + function get_query_args( $post_id = false ) { + $post_types = get_post_types( [ + 'exclude_from_search' => false + ] ); + + $args = [ + 'post_type' => $post_types, + 'post_status' => 'publish', + 'posts_per_page' => -1, + 'fields' => 'ids', + 'orderby' => 'ID', + 'cache_results' => false, + 'no_found_rows' => true, + 'ignore_custom_sort' => true, + 'suppress_filters' => true + ]; + + if ( is_int( $post_id ) ) { + $args['p'] = $post_id; + $args['posts_per_page'] = 1; + } + + return apply_filters( 'facetwp_indexer_query_args', $args ); + } + + + /** + * Get an array of post IDs to index + * @since 3.6.8 + */ + function get_post_ids_to_index( $post_id = false ) { + $args = $this->get_query_args( $post_id ); + $query = new WP_Query( $args ); + return (array) $query->posts; + } + + + /** + * Index an individual post + * @since 3.6.8 + */ + function index_post( $post_id, $facets ) { + + // Force WPML to change the language + do_action( 'facetwp_indexer_post', [ 'post_id' => $post_id ] ); + + // Loop through all facets + foreach ( $facets as $facet ) { + + // Do not index search facets + if ( 'search' == $facet['type'] ) { + continue; + } + + $this->facet = $facet; + $source = $facet['source'] ?? ''; + + // Set default index_row() params + $defaults = [ + 'post_id' => $post_id, + 'facet_name' => $facet['name'], + 'facet_source' => $source, + 'facet_value' => '', + 'facet_display_value' => '', + 'term_id' => 0, + 'parent_id' => 0, + 'depth' => 0, + 'variation_id' => 0, + ]; + + $defaults = apply_filters( 'facetwp_indexer_post_facet_defaults', $defaults, [ + 'facet' => $facet + ] ); + + // Set flag for custom handling + $this->is_overridden = true; + + // Bypass default indexing + $bypass = apply_filters( 'facetwp_indexer_post_facet', false, [ + 'defaults' => $defaults, + 'facet' => $facet + ] ); + + if ( $bypass ) { + continue; + } + + $this->is_overridden = false; + + // Get rows to insert + $rows = $this->get_row_data( $defaults ); + + foreach ( $rows as $row ) { + $this->index_row( $row ); + } + } + } + + + /** + * Get data for a table row + * @since 2.1.1 + */ + function get_row_data( $defaults ) { + $output = []; + + $facet = $this->facet; + $post_id = $defaults['post_id']; + $source = $defaults['facet_source']; + + if ( 'tax/' == substr( $source, 0, 4 ) ) { + $used_terms = []; + $taxonomy = substr( $source, 4 ); + $term_objects = wp_get_object_terms( $post_id, $taxonomy ); + if ( is_wp_error( $term_objects ) ) { + return $output; + } + + // Store the term depths + $hierarchy = FWP()->helper->get_term_depths( $taxonomy ); + + // Only index child terms + $children = false; + if ( ! empty( $facet['parent_term'] ) ) { + $children = get_term_children( $facet['parent_term'], $taxonomy ); + } + + foreach ( $term_objects as $term ) { + + // If "parent_term" is set, only index children + if ( false !== $children && ! in_array( $term->term_id, $children ) ) { + continue; + } + + // Prevent duplicate terms + if ( isset( $used_terms[ $term->term_id ] ) ) { + continue; + } + $used_terms[ $term->term_id ] = true; + + // Handle hierarchical taxonomies + $term_info = $hierarchy[ $term->term_id ]; + $depth = $term_info['depth']; + + // Adjust depth if parent_term is set + if ( ! empty( $facet['parent_term'] ) ) { + if ( isset( $hierarchy[ $facet['parent_term'] ] ) ) { + $anchor = (int) $hierarchy[ $facet['parent_term'] ]['depth'] + 1; + $depth = ( $depth - $anchor ); + } + } + + $params = $defaults; + $params['facet_value'] = $term->slug; + $params['facet_display_value'] = $term->name; + $params['term_id'] = $term->term_id; + $params['parent_id'] = $term_info['parent_id']; + $params['depth'] = $depth; + $output[] = $params; + + // Automatically index implicit parents + if ( 'hierarchy' == $facet['type'] || FWP()->helper->facet_is( $facet, 'hierarchical', 'yes' ) ) { + while ( $depth > 0 ) { + $term_id = $term_info['parent_id']; + $term_info = $hierarchy[ $term_id ]; + $depth = $depth - 1; + + if ( ! isset( $used_terms[ $term_id ] ) ) { + $used_terms[ $term_id ] = true; + + $params = $defaults; + $params['facet_value'] = $term_info['slug']; + $params['facet_display_value'] = $term_info['name']; + $params['term_id'] = $term_id; + $params['parent_id'] = $term_info['parent_id']; + $params['depth'] = $depth; + $output[] = $params; + } + } + } + } + } + elseif ( 'cf/' == substr( $source, 0, 3 ) ) { + $source_noprefix = substr( $source, 3 ); + $values = get_post_meta( $post_id, $source_noprefix, false ); + foreach ( $values as $value ) { + $params = $defaults; + $params['facet_value'] = $value; + $params['facet_display_value'] = $value; + $output[] = $params; + } + } + elseif ( 'post' == substr( $source, 0, 4 ) ) { + $post = get_post( $post_id ); + $value = $post->{$source}; + $display_value = $value; + if ( 'post_author' == $source ) { + $user = get_user_by( 'id', $value ); + $display_value = ( $user instanceof WP_User ) ? $user->display_name : $value; + } + elseif ( 'post_type' == $source ) { + $post_type = get_post_type_object( $value ); + if ( isset( $post_type->labels->name ) ) { + $display_value = $post_type->labels->name; + } + } + + $params = $defaults; + $params['facet_value'] = $value; + $params['facet_display_value'] = $display_value; + $output[] = $params; + } + + return apply_filters( 'facetwp_indexer_row_data', $output, [ + 'defaults' => $defaults, + 'facet' => $this->facet + ] ); + } + + + /** + * Index a facet value + * @since 0.6.0 + */ + function index_row( $params ) { + + // Allow for custom indexing + $params = apply_filters( 'facetwp_index_row', $params, $this ); + + // Allow hooks to bypass the row insertion + if ( is_array( $params ) ) { + $this->insert( $params ); + } + } + + + /** + * Save a facet value to DB + * This can be trigged by "facetwp_index_row" to handle multiple values + * @since 1.2.5 + */ + function insert( $params ) { + global $wpdb; + + $value = $params['facet_value']; + $display_value = $params['facet_display_value']; + + // Only accept scalar values + if ( '' === $value || ! is_scalar( $value ) ) { + return; + } + + // Apply UI-based modifiers + if ( isset( $this->modifiers[ $params['facet_name'] ] ) ) { + $mod = $this->modifiers[ $params['facet_name' ] ]; + $is_match = in_array( $display_value, $mod['values'] ); + + if ( ( 'exclude' == $mod['type'] && $is_match ) || ( 'include' == $mod['type'] && ! $is_match ) ) { + return; + } + } + + $wpdb->query( $wpdb->prepare( "INSERT INTO {$this->table} + (post_id, facet_name, facet_value, facet_display_value, term_id, parent_id, depth, variation_id) VALUES (%d, %s, %s, %s, %d, %d, %d, %d)", + $params['post_id'], + $params['facet_name'], + FWP()->helper->safe_value( $value ), + $display_value, + $params['term_id'], + $params['parent_id'], + $params['depth'], + $params['variation_id'] + ) ); + } + + + /** + * Get the indexing completion percentage + * @return mixed The decimal percentage, or -1 + * @since 0.1.0 + */ + function get_progress() { + $return = -1; + $num_indexed = (int) $this->get_transient( 'num_indexed' ); + $num_total = (int) $this->get_transient( 'num_total' ); + $retries = (int) $this->get_transient( 'retries' ); + $touch = (int) $this->get_transient( 'touch' ); + + if ( 0 < $num_total ) { + + // Resume a stalled indexer + if ( 60 < ( time() - $touch ) ) { + $post_data = [ + 'blocking' => false, + 'timeout' => 0.02, + 'body' => [ + 'action' => 'facetwp_resume_index', + 'facetwp_offset' => $num_indexed, + 'facetwp_retries' => $retries + 1, + 'touch' => $touch + ] + ]; + wp_remote_post( admin_url( 'admin-ajax.php' ), $post_data ); + } + + // Calculate the percent completion + if ( $num_indexed != $num_total ) { + $return = round( 100 * ( $num_indexed / $num_total ), 2 ); + } + } + + return $return; + } + + + /** + * Get indexer transient variables + * @since 1.7.8 + */ + function get_transient( $name = false ) { + $transients = get_option( 'facetwp_indexing_data' ); + + if ( ! empty( $transients ) ) { + $transients = json_decode( $transients, true ); + if ( $name ) { + return $transients[ $name ] ?? false; + } + + return $transients; + } + + return false; + } + + + /** + * Set either the index or temp table + * @param string $table 'auto', 'index', or 'temp' + * @since 4.1.4 + */ + function set_table( $table = 'auto' ) { + global $wpdb; + + if ( 'auto' == $table ) { + $table = ( '' == get_option( 'facetwp_indexing', '' ) ) ? 'index' : 'temp'; + } + + $this->table = $wpdb->prefix . 'facetwp_' . $table; + } + + + /** + * Index table management + * @since 3.5 + */ + function manage_temp_table( $action = 'create' ) { + global $wpdb; + + $table = $wpdb->prefix . 'facetwp_index'; + $temp_table = $wpdb->prefix . 'facetwp_temp'; + + if ( 'create' == $action ) { + $wpdb->query( "CREATE TABLE $temp_table LIKE $table" ); + $this->set_table( 'temp' ); + } + elseif ( 'replace' == $action ) { + $wpdb->query( "TRUNCATE TABLE $table" ); + $wpdb->query( "INSERT INTO $table SELECT * FROM $temp_table" ); + } + elseif ( 'delete' == $action ) { + $wpdb->query( "DROP TABLE IF EXISTS $temp_table" ); + $this->set_table( 'index' ); + } + } + + + /** + * Populate an array of facet value modifiers (defined in the admin UI) + * @since 3.5.6 + */ + function load_value_modifiers( $facets ) { + $output = []; + + foreach ( $facets as $facet ) { + $name = $facet['name']; + $type = empty( $facet['modifier_type'] ) ? 'off' : $facet['modifier_type']; + + if ( 'include' == $type || 'exclude' == $type ) { + $temp = preg_split( '/\r\n|\r|\n/', trim( $facet['modifier_values'] ) ); + $values = []; + + // Compare using both original and encoded values + foreach ( $temp as $val ) { + $val = trim( $val ); + if ( empty( $val ) ) { + continue; + } + $val_encoded = htmlentities( $val ); + $val_decoded = html_entity_decode( $val ); + $values[ $val ] = true; + $values[ $val_encoded ] = true; + $values[ $val_decoded ] = true; + } + + if ( ! empty( $values) ) { + $output[ $name ] = [ 'type' => $type, 'values' => array_keys( $values ) ]; + } + } + } + + $this->modifiers = $output; + } +} diff --git a/wp-content/plugins/facetwp/includes/class-init.php b/wp-content/plugins/facetwp/includes/class-init.php new file mode 100644 index 000000000..d3d41b76d --- /dev/null +++ b/wp-content/plugins/facetwp/includes/class-init.php @@ -0,0 +1,223 @@ +load_textdomain(); + + // is_plugin_active + include_once( ABSPATH . 'wp-admin/includes/plugin.php' ); + + $includes = [ + 'api/fetch', + 'api/refresh', + 'class-helper', + 'class-ajax', + 'class-request', + 'class-renderer', + 'class-diff', + 'class-indexer', + 'class-display', + 'class-builder', + 'class-overrides', + 'class-settings', + 'class-upgrade', + 'functions' + ]; + + foreach ( $includes as $inc ) { + include ( FACETWP_DIR . "/includes/$inc.php" ); + } + + new FacetWP_Upgrade(); + new FacetWP_Overrides(); + + FWP()->api = new FacetWP_API_Fetch(); + FWP()->helper = new FacetWP_Helper(); + FWP()->facet = new FacetWP_Renderer(); + FWP()->settings = new FacetWP_Settings(); + FWP()->diff = new FacetWP_Diff(); + FWP()->indexer = new FacetWP_Indexer(); + FWP()->display = new FacetWP_Display(); + FWP()->builder = new FacetWP_Builder(); + FWP()->request = new FacetWP_Request(); + FWP()->ajax = new FacetWP_Ajax(); + + // integrations + include( FACETWP_DIR . '/includes/integrations/searchwp/searchwp.php' ); + include( FACETWP_DIR . '/includes/integrations/woocommerce/woocommerce.php' ); + include( FACETWP_DIR . '/includes/integrations/edd/edd.php' ); + include( FACETWP_DIR . '/includes/integrations/acf/acf.php' ); + include( FACETWP_DIR . '/includes/integrations/wp-cli/wp-cli.php' ); + include( FACETWP_DIR . '/includes/integrations/wp-rocket/wp-rocket.php' ); + + // update checks + include( FACETWP_DIR . '/includes/class-updater.php' ); + + // hooks + add_action( 'admin_menu', [ $this, 'admin_menu' ] ); + add_action( 'wp_enqueue_scripts', [ $this, 'front_scripts' ] ); + add_filter( 'redirect_canonical', [ $this, 'redirect_canonical' ], 10, 2 ); + add_filter( 'plugin_action_links_facetwp/index.php', [ $this, 'plugin_action_links' ] ); + + do_action( 'facetwp_init' ); + } + + + /** + * i18n support + */ + function load_textdomain() { + + // admin-facing + load_plugin_textdomain( 'fwp' ); + + // front-facing + load_plugin_textdomain( 'fwp-front', false, basename( FACETWP_DIR ) . '/languages' ); + } + + + /** + * Register the FacetWP settings page + */ + function admin_menu() { + add_options_page( 'FacetWP', 'FacetWP', apply_filters( 'facetwp_admin_settings_capability', 'manage_options' ), 'facetwp', [ $this, 'settings_page' ] ); + } + + + /** + * Notify users to install necessary integrations + */ + function admin_notices() { + if ( apply_filters( 'facetwp_dismiss_notices', false ) ) { + return; + } + + $reqs = [ + 'Beaver Builder' => [ + 'is_active' => class_exists( 'FLBuilderLoader' ), + 'addon' => 'facetwp-beaver-builder/facetwp-beaver-builder.php', + 'slug' => 'beaver-builder' + ], + 'Bricks' => [ + 'is_active' => function_exists( 'bricks_is_builder' ), + 'addon' => 'facetwp-bricks/facetwp-bricks.php', + 'slug' => 'bricks' + ], + 'Elementor' => [ + 'is_active' => defined( 'ELEMENTOR_VERSION' ), + 'addon' => 'facetwp-elementor/facetwp-elementor.php', + 'slug' => 'elementor' + ], + 'Multilingual (Polylang)' => [ + 'is_active' => function_exists( 'pll_register_string' ), + 'addon' => 'facetwp-i18n/facetwp-i18n.php', + 'slug' => 'multilingual' + ], + 'Relevanssi' => [ + 'is_active' => function_exists( 'relevanssi_search' ), + 'addon' => 'facetwp-relevanssi/facetwp-relevanssi.php', + 'slug' => 'relevanssi' + ], + 'Recipes (Tasty Recipes)' => [ + 'is_active' => function_exists( 'Tasty_Recipes' ), + 'addon' => 'facetwp-recipes/facetwp-recipes.php', + 'slug' => 'recipes' + ], + 'Multilingual (WPML)' => [ + 'is_active' => defined( 'ICL_SITEPRESS_VERSION' ), + 'addon' => 'facetwp-i18n/facetwp-i18n.php', + 'slug' => 'multilingual' + ], + 'Recipes (WP Recipe Maker)' => [ + 'is_active' => function_exists( 'run_wp_recipe_maker' ), + 'addon' => 'facetwp-recipes/facetwp-recipes.php', + 'slug' => 'recipes' + ] + ]; + + $needed = []; + + foreach ( $reqs as $name => $req ) { + if ( $req['is_active'] && ! is_plugin_active( $req['addon'] ) ) { + $needed[] = sprintf( '%s', $req['slug'], $name ); + } + } + + if ( ! empty( $needed ) ) { + echo '

    Please install these FacetWP integration add-ons (then re-index): ' . implode( ', ', $needed ) . '

    '; + } + } + + + /** + * Enqueue jQuery + */ + function front_scripts() { + if ( 'yes' == FWP()->helper->get_setting( 'load_jquery', 'yes' ) ) { + wp_enqueue_script( 'jquery' ); + } + } + + + /** + * Route to the correct edit screen + */ + function settings_page() { + include( FACETWP_DIR . '/templates/page-settings.php' ); + } + + + /** + * Prevent WP from redirecting FWP pager to /page/X + */ + function redirect_canonical( $redirect_url, $requested_url ) { + if ( false !== strpos( $redirect_url, FWP()->helper->get_setting( 'prefix' ) . 'paged' ) ) { + return false; + } + return $redirect_url; + } + + + /** + * Add "Settings" link to plugin listing page + */ + function plugin_action_links( $links ) { + $settings_link = admin_url( 'options-general.php?page=facetwp' ); + $settings_link = '' . __( 'Settings', 'fwp' ) . ''; + array_unshift( $links, $settings_link ); + return $links; + } + + + /** + * WooCommerce 3.6+ doesn't load its frontend includes for REST API requests + * We need to force-load these includes for FacetWP refreshes + * See includes() within class-woocommerce.php + * + * This code isn't within /integrations/woocommerce/ because it runs *before* init + * + * @since 3.3.10 + */ + function is_rest_api_request( $request ) { + if ( false !== strpos( $_SERVER['REQUEST_URI'], 'facetwp' ) ) { + return false; + } + return $request; + } +} + +$this->init = new FacetWP_Init(); diff --git a/wp-content/plugins/facetwp/includes/class-overrides.php b/wp-content/plugins/facetwp/includes/class-overrides.php new file mode 100644 index 000000000..f97aa7066 --- /dev/null +++ b/wp-content/plugins/facetwp/includes/class-overrides.php @@ -0,0 +1,116 @@ +is_overridden ) { + return $params; + } + + $facet = FWP()->helper->get_facet_by_name( $params['facet_name'] ); + + // Store raw numbers to format later + if ( in_array( $facet['type'], [ 'number_range', 'slider' ] ) ) { + $this->raw = [ + 'value' => $params['facet_value'], + 'label' => $params['facet_display_value'] + ]; + } + + // Support "Other data source" values + if ( ! empty( $facet['source_other'] ) ) { + $other_params = $params; + $other_params['facet_source'] = $facet['source_other']; + $rows = $class->get_row_data( $other_params ); + $params['facet_display_value'] = apply_filters( 'facetwp_index_source_other_value', $rows[0]['facet_display_value'] ?? $params['facet_display_value'], $params ); + } + + return $params; + } + + + /** + * Make sure that numbers are properly formatted + */ + function format_numbers( $params, $class ) { + + if ( empty( $this->raw ) ) { + return $params; + } + + $value = $params['facet_value']; + $label = $params['facet_display_value']; + + // Only format if un-altered + if ( $this->raw['value'] === $value && $this->raw['label'] === $label ) { + $params['facet_value'] = FWP()->helper->format_number( $this->raw['value'] ); + $params['facet_display_value'] = FWP()->helper->format_number( $this->raw['label'] ); + } + + $this->raw = null; + + return $params; + } + + + /** + * Ignore certain post types + */ + function ignore_post_types( $is_main_query, $query ) { + $blacklist = [ + 'acf-field', + 'acf-field-group', + 'advanced_ads', + 'carts', + 'cookielawinfo', + 'edd_wish_list', + 'ms_relationship', + 'nav_menu_item', + 'wc_user_membership', + 'wp_block', + 'wp_global_styles', + 'wp_navigation', + 'wp_template', + 'wp_template_part' + ]; + $post_type = $query->get( 'post_type' ); + + if ( is_string( $post_type ) && in_array( $post_type, $blacklist ) ) { + $is_main_query = false; + } + + // Ignore the "WP GDPR Compliance" plugin + if ( '[wpgdprc_access_request_form]' == $query->get( 's' ) ) { + $is_main_query = false; + } + + return $is_main_query; + } + + /** + * Disable indexer + */ + function disable_indexer($enabled) { + + if ('no' == FWP()->helper->get_setting('enable_indexer', 'yes')) { + return false; // enable indexer setting + } + + return $enabled; + } +} diff --git a/wp-content/plugins/facetwp/includes/class-renderer.php b/wp-content/plugins/facetwp/includes/class-renderer.php new file mode 100644 index 000000000..141e2e1a3 --- /dev/null +++ b/wp-content/plugins/facetwp/includes/class-renderer.php @@ -0,0 +1,689 @@ +facet_types = FWP()->helper->facet_types; + } + + + /** + * Generate the facet output + * @param array $params An array of arrays (see the FacetWP->refresh() method) + * @return array + */ + function render( $params ) { + + $output = [ + 'facets' => [], + 'template' => '', + 'settings' => [], + ]; + + // Hook params + $params = apply_filters( 'facetwp_render_params', $params ); + + // First ajax refresh? + $first_load = (bool) $params['first_load']; + $is_bfcache = (bool) $params['is_bfcache']; + + // Initial pageload? + $this->is_preload = isset( $params['is_preload'] ); + + // Set the AJAX and HTTP params + $this->ajax_params = $params; + $this->http_params = $params['http_params']; + + // Validate facets + $this->facets = []; + foreach ( $params['facets'] as $f ) { + $name = $f['facet_name']; + $facet = FWP()->helper->get_facet_by_name( $name ); + if ( $facet ) { + + // Default to "OR" mode + $facet['operator'] = $facet['operator'] ?? 'or'; + + // Fix operator for non-multiselect + $ui_type = empty( $facet['ui_type'] ) ? $facet['type'] : $facet['ui_type']; + if ( 'fselect' == $ui_type ) { + $multi = $facet['multiple'] ?? 'no'; + $facet['operator'] = ( 'no' == $multi ) ? 'or' : $facet['operator']; + } else if ( in_array( $ui_type, [ 'radio', 'dropdown' ] ) ) { + $facet['operator'] = 'or'; + } + + // Support the "facetwp_preload_url_vars" hook + if ( $first_load && empty( $f['selected_values'] ) && ! empty( $this->http_params['url_vars'][ $name ] ) ) { + $f['selected_values'] = $this->http_params['url_vars'][ $name ]; + } + + // Support commas within search / autocomplete facets + if ( 'search' == $facet['type'] || 'autocomplete' == $facet['type'] ) { + $f['selected_values'] = implode( ',', (array) $f['selected_values'] ); + } + + $facet['selected_values'] = FWP()->helper->sanitize( $f['selected_values'] ); + + $this->facets[ $name ] = $facet; + } + } + + // Get the template from $helper->settings + if ( 'wp' == $params['template'] ) { + $this->template = [ 'name' => 'wp' ]; + $query_args = FWP()->request->query_vars ?? []; + } + else { + $this->template = FWP()->helper->get_template_by_name( $params['template'] ); + $query_args = $this->get_query_args(); + } + + // Detect search string + if ( ! empty( $query_args['s'] ) ) { + $this->is_search = true; + } + + // Run the query once (prevent duplicate queries when preloading) + if ( empty( $this->query_args ) ) { + + // Support "post__in" + if ( empty( $query_args['post__in'] ) ) { + $query_args['post__in'] = []; + } + + // Get the template "query" field + $query_args = apply_filters( 'facetwp_query_args', $query_args, $this ); + + // Pagination + $query_args['paged'] = empty( $params['paged'] ) ? 1 : (int) $params['paged']; + + // Preserve SQL_CALC_FOUND_ROWS + unset( $query_args['no_found_rows'] ); + + // Narrow posts based on facet selections + $post_ids = $this->get_filtered_post_ids( $query_args ); + + // Update the SQL query + if ( ! empty( $post_ids ) ) { + if ( FWP()->is_filtered || FWP()->is_modified ) { + $query_args['post__in'] = $post_ids; + } + + $this->where_clause = ' AND post_id IN (' . implode( ',', $post_ids ) . ')'; + } + + // Set the default limit + if ( empty( $query_args['posts_per_page'] ) ) { + $query_args['posts_per_page'] = (int) get_option( 'posts_per_page' ); + } + + // Adhere to the "per page" box + $per_page = isset( $params['extras']['per_page'] ) ? $params['extras']['per_page'] : ''; + if ( ! empty( $per_page ) && 'default' != $per_page ) { + $query_args['posts_per_page'] = (int) $per_page; + } + + $this->query_args = apply_filters( 'facetwp_filtered_query_args', $query_args, $this ); + + // Run the WP_Query + $this->query = new WP_Query( $this->query_args ); + } + + // Debug + if ( 'on' == FWP()->helper->get_setting( 'debug_mode', 'off' ) ) { + $output['settings']['debug'] = $this->get_debug_info(); + } else { + $output['settings']['debug'] = "Enable debug mode in [Settings > FacetWP > Settings]"; + } + + // Generate the template HTML + // For performance gains, skip the template on pageload + if ( 'wp' != $this->template['name'] ) { + if ( ! $first_load || $is_bfcache || apply_filters( 'facetwp_template_force_load', false ) ) { + $output['template'] = $this->get_template_html(); + } + } + + // Don't render these facets + $frozen_facets = $params['frozen_facets']; + + // Calculate pager args + $pager_args = [ + 'page' => (int) $this->query_args['paged'], + 'per_page' => (int) $this->query_args['posts_per_page'], + 'total_rows' => (int) $this->query->found_posts, + 'total_rows_unfiltered' => (int) count( FWP()->unfiltered_post_ids ), + 'total_pages' => 1, + ]; + + if ( 0 < $pager_args['per_page'] ) { + $pager_args['total_pages'] = ceil( $pager_args['total_rows'] / $pager_args['per_page'] ); + } + + $pager_args = apply_filters( 'facetwp_pager_args', $pager_args, $this ); + + $this->pager_args = $pager_args; + + // Stick the pager args into the JSON response + $output['settings']['pager'] = $pager_args; + + // Display the pagination HTML + if ( isset( $params['extras']['pager'] ) ) { + $output['pager'] = $this->paginate( $pager_args ); + } + + // Display the "per page" HTML + if ( isset( $params['extras']['per_page'] ) ) { + $output['per_page'] = $this->get_per_page_box(); + } + + // Display the counts HTML + if ( isset( $params['extras']['counts'] ) ) { + $output['counts'] = $this->get_result_count( $pager_args ); + } + + // Not paging + if ( 0 == $params['soft_refresh'] ) { + $output['settings']['num_choices'] = []; + } + + // Get facet data + foreach ( $this->facets as $facet_name => $the_facet ) { + $facet_type = $the_facet['type']; + $ui_type = empty( $the_facet['ui_type'] ) ? $facet_type : $the_facet['ui_type']; + + // Invalid facet type + if ( ! isset( $this->facet_types[ $facet_type ] ) ) { + continue; + } + + // Skip facets when paging + if ( 0 < $params['soft_refresh'] && 'pager' != $facet_type ) { + continue; + } + + // Get facet labels + if ( 0 == $params['soft_refresh'] ) { + $output['settings']['labels'][ $facet_name ] = facetwp_i18n( $the_facet['label'] ); + } + + // Load all facets on back / forward button press (first_load = true) + if ( ! $first_load ) { + + // Skip frozen facets + if ( isset( $frozen_facets[ $facet_name ] ) ) { + continue; + } + } + + $args = [ + 'facet' => $the_facet, + 'where_clause' => $this->where_clause, + 'selected_values' => $the_facet['selected_values'], + ]; + + // Load facet values if needed + if ( method_exists( $this->facet_types[ $facet_type ], 'load_values' ) ) { + + // Grab preloaded values if available + if ( isset( $this->preloaded_values[ $facet_name ] ) ) { + $args['values'] = $this->preloaded_values[ $facet_name ]; + } + else { + $args['values'] = $this->facet_types[ $facet_type ]->load_values( $args ); + + if ( $this->is_preload ) { + $this->preloaded_values[ $facet_name ] = $args['values']; + } + } + } + + // Filter the render args + $args = apply_filters( 'facetwp_facet_render_args', $args ); + + // Return the number of available choices + if ( isset( $args['values'] ) ) { + $num_choices = 0; + $is_ghost = FWP()->helper->facet_is( $the_facet, 'ghosts', 'yes' ); + + foreach ( $args['values'] as $choice ) { + if ( isset( $choice['counter'] ) && ( 0 < $choice['counter'] || $is_ghost ) ) { + $num_choices++; + } + } + + $output['settings']['num_choices'][ $facet_name ] = $num_choices; + } + + // Generate the facet HTML + $html = $this->facet_types[ $ui_type ]->render( $args ); + $output['facets'][ $facet_name ] = apply_filters( 'facetwp_facet_html', $html, $args ); + + // Return any JS settings + if ( method_exists( $this->facet_types[ $ui_type ], 'settings_js' ) ) { + $output['settings'][ $facet_name ] = $this->facet_types[ $ui_type ]->settings_js( $args ); + } + + // Grab num_choices for slider facets + if ( 'slider' == $the_facet['type'] ) { + $min = $output['settings'][ $facet_name ]['range']['min']; + $max = $output['settings'][ $facet_name ]['range']['max']; + $output['settings']['num_choices'][ $facet_name ] = ( $min == $max ) ? 0 : 1; + } + } + + return apply_filters( 'facetwp_render_output', $output, $params ); + } + + + /** + * Get WP_Query arguments by executing the template "query" field + * @return null + */ + function get_query_args() { + + $defaults = []; + + // Allow templates to piggyback archives + if ( apply_filters( 'facetwp_template_use_archive', false ) ) { + $main_query = $GLOBALS['wp_the_query']; + + // Initial pageload + if ( $main_query->is_archive || $main_query->is_search ) { + if ( $main_query->is_category ) { + $defaults['cat'] = $main_query->get( 'cat' ); + } + elseif ( $main_query->is_tag ) { + $defaults['tag_id'] = $main_query->get( 'tag_id' ); + } + elseif ( $main_query->is_tax ) { + $defaults['taxonomy'] = $main_query->get( 'taxonomy' ); + $defaults['term'] = $main_query->get( 'term' ); + } + elseif ( $main_query->is_search ) { + $defaults['s'] = $main_query->get( 's' ); + } + + $this->archive_args = $defaults; + } + // Subsequent ajax requests + elseif ( ! empty( $this->http_params['archive_args'] ) ) { + foreach ( $this->http_params['archive_args'] as $key => $val ) { + if ( in_array( $key, [ 'cat', 'tag_id', 'taxonomy', 'term', 's' ] ) ) { + $defaults[ $key ] = $val; + } + } + } + } + + // Use the query builder + if ( isset( $this->template['modes'] ) && 'visual' == $this->template['modes']['query'] ) { + $query_args = FWP()->builder->parse_query_obj( $this->template['query_obj'] ); + } + else { + + // remove UTF-8 non-breaking spaces + $query_args = preg_replace( "/\xC2\xA0/", ' ', $this->template['query'] ); + $query_args = (array) eval( '?>' . $query_args ); + } + + // Merge the two arrays + return array_merge( $defaults, $query_args ); + } + + + /** + * Get ALL post IDs for the matching query + * @return array An array of post IDs + */ + function get_filtered_post_ids( $query_args = [] ) { + + if ( empty( $query_args ) ) { + $query_args = $this->query_args; + } + + // Only get relevant post IDs + $args = array_merge( $query_args, [ + 'paged' => 1, + 'posts_per_page' => -1, + 'update_post_meta_cache' => false, + 'update_post_term_cache' => false, + 'cache_results' => false, + 'no_found_rows' => true, + 'nopaging' => true, // prevent "offset" issues + 'facetwp' => false, + 'fields' => 'ids', + ] ); + + $query = new WP_Query( $args ); + + // Allow hooks to modify the default post IDs + $post_ids = apply_filters( 'facetwp_pre_filtered_post_ids', $query->posts, $this ); + + // Store the original post IDs (before facet filtering is applied) + FWP()->unfiltered_post_ids = $post_ids; + + foreach ( $this->facets as $facet_name => $the_facet ) { + $facet_type = $the_facet['type']; + + // Stop looping + if ( empty( $post_ids ) ) { + break; + } + + $matches = []; + $selected_values = $the_facet['selected_values']; + + if ( empty( $selected_values ) ) { + continue; + } + + // Handle each facet + if ( isset( $this->facet_types[ $facet_type ] ) ) { + + $hook_params = [ + 'facet' => $the_facet, + 'selected_values' => $selected_values, + ]; + + // Hook to support custom filter_posts() handler + $matches = apply_filters( 'facetwp_facet_filter_posts', false, $hook_params ); + + if ( false === $matches ) { + $matches = $this->facet_types[ $facet_type ]->filter_posts( $hook_params ); + } + } + + // Skip this facet + if ( 'continue' == $matches ) { + continue; + } + + // Force array + $matches = (array) $matches; + + // Store post IDs per facet (needed for "OR" mode) + FWP()->or_values[ $facet_name ] = $matches; + + if ( 'search' == $facet_type ) { + $this->is_search = true; + } + + // For search facets, loop through $matches to set order + // For other facets, loop through $post_ids to preserve the existing order + $needles = ( 'search' == $facet_type ) ? $matches : $post_ids; + $haystack = ( 'search' == $facet_type ) ? $post_ids : $matches; + $haystack = array_flip( $haystack ); + $intersected_ids = []; + + foreach ( $needles as $post_id ) { + if ( isset( $haystack[ $post_id ] ) ) { + $intersected_ids[] = $post_id; + } + } + + $post_ids = $intersected_ids; + } + + $post_ids = apply_filters( 'facetwp_filtered_post_ids', array_values( $post_ids ), $this ); + + // Store the final post IDs (after facet filtering has been applied) + FWP()->filtered_post_ids = $post_ids; + + // Have any facets applied changes? + FWP()->is_filtered = ( FWP()->unfiltered_post_ids !== $post_ids ); + + // Have any hooks modified the unfiltered post IDs? + FWP()->is_modified = ( FWP()->unfiltered_post_ids !== $query->posts ); + + // Return a zero array if no matches + return empty( $post_ids ) ? [ 0 ] : $post_ids; + } + + + /** + * Run the template display code + * @return string (HTML) + */ + function get_template_html() { + global $post, $wp_query; + + $output = apply_filters( 'facetwp_template_html', false, $this ); + + if ( false === $output ) { + ob_start(); + + // Preserve globals + $temp_post = is_object( $post ) ? clone $post : $post; + $temp_wp_query = is_object( $wp_query ) ? clone $wp_query : $wp_query; + + $query = $this->query; + $wp_query = $query; // Make $query->blah() optional + + if ( isset( $this->template['modes'] ) && 'visual' == $this->template['modes']['display'] ) { + echo FWP()->builder->render_layout( $this->template['layout'] ); + } + else { + + // Remove UTF-8 non-breaking spaces + $display_code = $this->template['template']; + $display_code = preg_replace( "/\xC2\xA0/", ' ', $display_code ); + eval( '?>' . $display_code ); + } + + // Reset globals + $post = $temp_post; + $wp_query = $temp_wp_query; + + // Store buffered output + $output = ob_get_clean(); + } + + $output = preg_replace( "/\xC2\xA0/", ' ', $output ); + return $output; + } + + + /** + * Result count (1-10 of 234) + * @param array $params An array with "page", "per_page", and "total_rows" + * @return string + */ + function get_result_count( $params = [] ) { + $text_of = __( 'of', 'fwp-front' ); + + $page = (int) $params['page']; + $per_page = (int) $params['per_page']; + $total_rows = (int) $params['total_rows']; + + if ( $per_page < $total_rows ) { + $lower = ( 1 + ( ( $page - 1 ) * $per_page ) ); + $upper = ( $page * $per_page ); + $upper = ( $total_rows < $upper ) ? $total_rows : $upper; + $output = "$lower-$upper $text_of $total_rows"; + } + else { + $lower = ( 0 < $total_rows ) ? 1 : 0; + $upper = $total_rows; + $output = $total_rows; + } + + return apply_filters( 'facetwp_result_count', $output, [ + 'lower' => $lower, + 'upper' => $upper, + 'total' => $total_rows, + ] ); + } + + + /** + * Pagination + * @param array $params An array with "page", "per_page", and "total_rows" + * @return string + */ + function paginate( $params = [] ) { + $pager_class = FWP()->helper->facet_types['pager']; + $pager_class->pager_args = $params; + + $output = $pager_class->render_numbers([ + 'inner_size' => 2, + 'dots_label' => '…', + 'prev_label' => '<<', + 'next_label' => '>>', + ]); + + return apply_filters( 'facetwp_pager_html', $output, $params ); + } + + + /** + * "Per Page" dropdown box + * @return string + */ + function get_per_page_box() { + $pager_class = FWP()->helper->facet_types['pager']; + $pager_class->pager_args = $this->pager_args; + + $options = apply_filters( 'facetwp_per_page_options', [ 10, 25, 50, 100 ] ); + + $output = $pager_class->render_per_page([ + 'default_label' => __( 'Per page', 'fwp-front' ), + 'per_page_options' => implode( ',', $options ) + ]); + + return apply_filters( 'facetwp_per_page_html', $output, [ + 'options' => $options + ] ); + } + + + /** + * Get debug info for the browser console + * @since 3.5.7 + */ + function get_debug_info() { + $last_indexed = get_option( 'facetwp_last_indexed' ); + $last_indexed = $last_indexed ? human_time_diff( $last_indexed ) : 'never'; + + $debug = [ + 'query_args' => $this->query_args, + 'sql' => $this->query->request, + 'facets' => $this->facets, + 'template' => $this->template, + 'settings' => FWP()->helper->settings['settings'], + 'last_indexed' => $last_indexed, + 'row_counts' => FWP()->helper->get_row_counts(), + 'hooks_used' => $this->get_hooks_used(), + 'post_types' => FWP()->helper->get_indexable_types() + ]; + + // Reduce debug payload + if ( ! empty( $this->query_args['post__in'] ) ) { + $debug['query_args']['post__in_count'] = count( $this->query_args['post__in'] ); + $debug['query_args']['post__in'] = array_slice( $this->query_args['post__in'], 0, 10 ); + + $debug['sql'] = preg_replace_callback( '/posts.ID IN \((.*?)\)/s', function( $matches ) { + $count = substr_count( $matches[1], ',' ) + 1; + return ( $count <= 10 ) ? $matches[0] : "posts.ID IN (<$count IDs>)"; + }, $debug['sql'] ); + } + + return $debug; + } + + + /** + * Display the location of relevant hooks (for Debug Mode) + * @since 3.5.7 + */ + function get_hooks_used() { + $relevant_hooks = []; + + $match_tags = apply_filters( 'facetwp_debug_hooks', [ + 'pre_get_posts', + 'posts_results' + ]); + + foreach ( $GLOBALS['wp_filter'] as $tag => $hook_data ) { + if ( 0 === strpos( $tag, 'facetwp' ) || in_array( $tag, $match_tags ) ) { + foreach ( $hook_data->callbacks as $callbacks ) { + foreach ( $callbacks as $cb ) { + if ( is_string( $cb['function'] ) && false !== strpos( $cb['function'], '::' ) ) { + $cb['function'] = explode( '::', $cb['function'] ); + } + + if ( is_array( $cb['function'] ) ) { + $class = is_object( $cb['function'][0] ) ? get_class( $cb['function'][0] ) : $cb['function'][0]; + $ref = new ReflectionMethod( $class, $cb['function'][1] ); + } + elseif ( is_object( $cb['function'] ) ) { + if ( is_a( $cb['function'], 'Closure' ) ) { + $ref = new ReflectionFunction( $cb['function'] ); + } + else { + $class = get_class( $cb['function'] ); + $ref = new ReflectionMethod( $class, '__invoke' ); + } + } + else { + $ref = new ReflectionFunction( $cb['function'] ); + } + + $filename = str_replace( ABSPATH, '', $ref->getFileName() ); + + // ignore built-in hooks + if ( false === strpos( $filename, 'plugins/facetwp' ) ) { + if ( false !== strpos( $filename, 'wp-content' ) || false !== strpos( $tag, 'facetwp' ) ) { + $relevant_hooks[ $tag ][] = $filename . ':' . $ref->getStartLine(); + } + } + } + } + } + } + + return $relevant_hooks; + } +} diff --git a/wp-content/plugins/facetwp/includes/class-request.php b/wp-content/plugins/facetwp/includes/class-request.php new file mode 100644 index 000000000..0df9d08ff --- /dev/null +++ b/wp-content/plugins/facetwp/includes/class-request.php @@ -0,0 +1,387 @@ +process_json(); + $this->intercept_request(); + } + + + /** + * application/json requires processing the raw PHP input stream + */ + function process_json() { + $json = file_get_contents( 'php://input' ); + if ( 0 === strpos( $json, '{' ) ) { + $post_data = json_decode( $json, true ); + $action = $post_data['action'] ?? ''; + + if ( is_string( $action ) && 0 === strpos( $action, 'facetwp' ) ) { + $_POST = $post_data; + } + } + } + + + /** + * If AJAX and the template is "wp", return the buffered HTML + * Otherwise, store the GET variables for later use + */ + function intercept_request() { + $action = isset( $_POST['action'] ) ? sanitize_key( $_POST['action'] ) : ''; + + $valid_actions = [ + 'facetwp_refresh', + 'facetwp_autocomplete_load' + ]; + + $this->is_refresh = ( 'facetwp_refresh' == $action ); + $this->is_preload = ! in_array( $action, $valid_actions ); + $prefix = FWP()->helper->get_setting( 'prefix' ); + $is_css_tpl = isset( $_POST['data']['template'] ) && 'wp' == $_POST['data']['template']; + + // Disable the admin bar to prevent JSON issues + if ( $this->is_refresh ) { + add_filter( 'show_admin_bar', '__return_false' ); + } + + // Pageload + if ( $this->is_preload ) { + $features = [ 'paged', 'per_page', 'sort' ]; + $valid_names = wp_list_pluck( FWP()->helper->get_facets(), 'name' ); + $valid_names = array_merge( $valid_names, $features ); + + // Store GET variables + foreach ( $valid_names as $name ) { + if ( isset( $_GET[ $prefix . $name ] ) && '' !== $_GET[ $prefix . $name ] ) { + $new_val = stripslashes_deep( $_GET[ $prefix . $name ] ); + $new_val = in_array( $name, $features ) ? $new_val : explode( ',', $new_val ); + $this->url_vars[ $name ] = $new_val; + } + } + + $this->url_vars = apply_filters( 'facetwp_preload_url_vars', $this->url_vars ); + } + // Populate $_GET + else { + $data = stripslashes_deep( $_POST['data'] ); + + if ( ! empty( $data['http_params']['get'] ) ) { + foreach ( $data['http_params']['get'] as $key => $val ) { + if ( ! isset( $_GET[ $key ] ) ) { + $_GET[ $key ] = $val; + } + } + } + } + + if ( $this->is_preload || $is_css_tpl ) { + add_filter( 'posts_pre_query', [ $this, 'maybe_abort_query' ], 10, 2 ); + add_action( 'pre_get_posts', [ $this, 'sacrificial_lamb' ], 998 ); + add_action( 'pre_get_posts', [ $this, 'update_query_vars' ], 999 ); + } + + if ( ! $this->is_preload && $is_css_tpl && 'facetwp_autocomplete_load' != $action ) { + add_action( 'shutdown', [ $this, 'inject_template' ], 0 ); + ob_start(); + } + } + + + /** + * FacetWP runs the archive query before WP gets the chance. + * This hook prevents the query from running twice, by letting us inject the + * first query's posts (and counts) into the "main" query. + */ + function maybe_abort_query( $posts, $query ) { + $do_abort = apply_filters( 'facetwp_archive_abort_query', true, $query ); + $has_query_run = ( ! empty( FWP()->facet->query ) ); + + if ( $do_abort && $has_query_run && isset( $this->query_vars ) ) { + + // New var; any changes to $query will cause is_main_query() to return false + $query_vars = $query->query_vars; + + // If paged = 0, set to 1 or the compare will fail + if ( empty( $query_vars['paged'] ) ) { + $query_vars['paged'] = 1; + } + + // Only intercept the same query + // We're using "==", which doesn't care if assoc order is different + if ( $query_vars == $this->query_vars ) { + $posts = FWP()->facet->query->posts; + $query->found_posts = FWP()->facet->query->found_posts; + $query->max_num_pages = FWP()->facet->query->max_num_pages; + } + } + + return $posts; + } + + + /** + * Fixes https://core.trac.wordpress.org/ticket/40393 + */ + function sacrificial_lamb( $query ) { + } + + + /** + * Force FacetWP to use the default WP query + */ + function update_query_vars( $query ) { + + if ( isset( $this->query_vars ) // Ran already + || $this->is_shortcode // Skip shortcode template + || ( is_admin() && ! wp_doing_ajax() ) // Skip admin + || ( wp_doing_ajax() && ! $this->is_refresh ) // Skip other ajax + || ! $this->is_main_query( $query ) // Not the main query + ) { + return; + } + + // Set the flag + $query->set( 'facetwp', true ); + + // If "s" is an empty string and no post_type is set, WP sets + // post_type = "any". We want to prevent this except on the search page. + if ( '' == $query->get( 's' ) && ! isset( $_GET['s'] ) ) { + $query->set( 's', null ); + } + + // Set the initial query vars, needed for render() + $this->query_vars = $query->query_vars; + + // Notify + do_action( 'facetwp_found_main_query' ); + + // Generate the FWP output + $data = ( $this->is_preload ) ? $this->process_preload_data() : $this->process_post_data(); + $this->output = FWP()->facet->render( $data ); + + // Set the updated query vars, needed for maybe_abort_query() + $this->query_vars = FWP()->facet->query->query_vars; + + // Set the updated query vars + $force_query = apply_filters( 'facetwp_preload_force_query', false, $query ); + + if ( ! $this->is_preload || ! empty( $this->url_vars ) || $force_query ) { + $query->query_vars = FWP()->facet->query_args; + } + + if ( 'product_query' == $query->get( 'wc_query' ) ) { + wc_set_loop_prop( 'total', FWP()->facet->pager_args['total_rows'] ); + wc_set_loop_prop( 'per_page', FWP()->facet->pager_args['per_page'] ); + wc_set_loop_prop( 'total_pages', FWP()->facet->pager_args['total_pages'] ); + wc_set_loop_prop( 'current_page', FWP()->facet->pager_args['page'] ); + } + } + + + /** + * Is this the main query? + */ + function is_main_query( $query ) { + if ( 'yes' == FWP()->helper->get_setting( 'strict_query_detection', 'no' ) ) { + $is_main_query = ( $query->is_main_query() ); + } + else { + $is_main_query = ( $query->is_main_query() || $query->is_archive ); + } + + $is_main_query = ( $query->is_singular || $query->is_feed ) ? false : $is_main_query; + $is_main_query = ( $query->get( 'suppress_filters', false ) ) ? false : $is_main_query; // skip get_posts() + $is_main_query = ( '' !== $query->get( 'facetwp' ) ) ? (bool) $query->get( 'facetwp' ) : $is_main_query; // flag + $is_main_query = ( doing_filter( 'get_the_excerpt' ) ) ? false : $is_main_query; // skip for excerpts + return apply_filters( 'facetwp_is_main_query', $is_main_query, $query ); + } + + + /** + * Process the AJAX $_POST data + * This gets passed into FWP()->facet->render() + */ + function process_post_data() { + $data = stripslashes_deep( $_POST['data'] ); + $facets = $data['facets']; + $extras = $data['extras'] ?? []; + $frozen_facets = $data['frozen_facets'] ?? []; + + $params = [ + 'facets' => [], + 'template' => $data['template'], + 'frozen_facets' => $frozen_facets, + 'http_params' => $data['http_params'], + 'extras' => $extras, + 'soft_refresh' => (int) $data['soft_refresh'], + 'is_bfcache' => (int) $data['is_bfcache'], + 'first_load' => (int) $data['first_load'], // skip the template? + 'paged' => (int) $data['paged'], + ]; + + foreach ( $facets as $facet_name => $selected_values ) { + $params['facets'][] = [ + 'facet_name' => $facet_name, + 'selected_values' => $selected_values, + ]; + } + + return $params; + } + + + /** + * On initial pageload, preload the data + * + * This gets called twice; once in the template shortcode (to grab only the template) + * and again in FWP()->display->front_scripts() to grab everything else. + * + * Two calls are needed for timing purposes; the template shortcode often renders + * before some or all of the other FacetWP-related shortcodes. + */ + function process_preload_data( $template_name = false, $overrides = [] ) { + + if ( false === $template_name ) { + $template_name = $this->template_name ?? 'wp'; + } + + $this->template_name = $template_name; + + // Is this a template shortcode? + $this->is_shortcode = ( 'wp' != $template_name ); + + $params = [ + 'facets' => [], + 'template' => $template_name, + 'http_params' => [ + 'get' => $_GET, + 'uri' => FWP()->helper->get_uri(), + 'url_vars' => $this->url_vars, + ], + 'frozen_facets' => [], + 'soft_refresh' => 1, // skip the facets + 'is_preload' => 1, + 'is_bfcache' => 0, + 'first_load' => 0, // load the template + 'extras' => [], + 'paged' => 1, + ]; + + // Support "/page/X/" on preload + if ( ! empty( $this->query_vars['paged'] ) ) { + $params['paged'] = (int) $this->query_vars['paged']; + } + + foreach ( $this->url_vars as $key => $val ) { + if ( 'paged' == $key ) { + $params['paged'] = $val; + } + elseif ( 'per_page' == $key || 'sort' == $key ) { + $params['extras'][ $key ] = $val; + } + else { + $params['facets'][] = [ + 'facet_name' => $key, + 'selected_values' => $val, + ]; + } + } + + // Override the defaults + $params = array_merge( $params, $overrides ); + + return $params; + } + + + /** + * This gets called from FWP()->display->front_scripts(), when we finally + * know which shortcodes are on the page. + * + * Since we already got the template HTML on the first process_preload_data() call, + * this time we're grabbing everything but the template. + * + * The return value of this method gets passed into the 2nd argument of + * process_preload_data(). + */ + function process_preload_overrides( $items ) { + $overrides = []; + $url_vars = FWP()->request->url_vars; + + foreach ( $items['facets'] as $name ) { + $overrides['facets'][] = [ + 'facet_name' => $name, + 'selected_values' => $url_vars[ $name ] ?? [], + ]; + } + + if ( isset( $items['extras']['counts'] ) ) { + $overrides['extras']['counts'] = true; + } + if ( isset( $items['extras']['pager'] ) ) { + $overrides['extras']['pager'] = true; + } + if ( isset( $items['extras']['per_page'] ) ) { + $overrides['extras']['per_page'] = $url_vars['per_page'] ?? 'default'; + } + if ( isset( $items['extras']['sort'] ) ) { + $overrides['extras']['sort'] = $url_vars['sort'] ?? 'default'; + } + + $overrides['soft_refresh'] = 0; // load the facets + $overrides['first_load'] = 1; // skip the template + + return $overrides; + } + + + /** + * Inject the page HTML into the JSON response + * We'll cherry-pick the content from the HTML using front.js + */ + function inject_template() { + $html = ob_get_clean(); + + // Throw an error + if ( empty( $this->output['settings'] ) ) { + $html = __( 'FacetWP was unable to auto-detect the post listing', 'fwp' ); + } + // Grab the contents + else { + preg_match( "/(.*?)<\/body>/s", $html, $matches ); + + if ( ! empty( $matches ) ) { + $html = trim( $matches[2] ); + } + } + + $this->output['template'] = $html; + do_action( 'facetwp_inject_template', $this->output ); + wp_send_json( $this->output ); + } +} diff --git a/wp-content/plugins/facetwp/includes/class-settings.php b/wp-content/plugins/facetwp/includes/class-settings.php new file mode 100755 index 000000000..79350bcd9 --- /dev/null +++ b/wp-content/plugins/facetwp/includes/class-settings.php @@ -0,0 +1,742 @@ + [ + 'label' => __( 'General', 'fwp' ), + 'fields' => [ + 'license_key' => [ + 'label' => __( 'License key', 'fwp' ), + 'html' => $this->get_setting_html( 'license_key' ) + ], + 'gmaps_api_key' => [ + 'label' => __( 'Google Maps API key', 'fwp' ), + 'html' => $this->get_setting_html( 'gmaps_api_key' ) + ], + 'gmaps_api_key_test' => [ + 'label' => __( 'Test Google APIs', 'fwp' ), + 'notes' => 'Test if the required Google API\'s are connected:
    1. Maps JavaScript API: required for the Map / Proximity facets to work.
    2. Geocoding API: required for the Proximity facet\'s "Locate me" button.
    3. Places API (Legacy): required for the Legacy Proximity facet\'s autocomplete function (with the "Use Legacy Proximity facet" setting enabled).
    4. Places API (New): required for the new Proximity facet\'s autocomplete function (with the "Use Legacy Proximity facet" setting disabled).
    Note: changes in the Google Cloud Console settings may take up to five minutes to take effect. Clear your browser cache before testing changed settings.', + 'html' => $this->get_setting_html( 'gmaps_api_key_test' ) + ], + 'places_version' => [ + 'label' => __( 'Use Legacy Proximity facet', 'fwp' ), + 'notes' => 'Enable to use the Legacy Proximity facet type. This legacy facet type uses Google\'s legacy "Places" API, instead of the "Places (New)" API. Important: make sure the correct Places API is enabled in your Google Cloud Console for your project: enable "Places" API for the Legacy Proximity facet and "Places (New)" for the normal Proximity facet. ', + 'html' => $this->get_setting_html( 'places_version', 'toggle', [ + 'true_value' => 'places-service', + 'false_value' => 'place-class' + ]), + 'show' => '"" != app.settings.gmaps_api_key' + ], + 'separators' => [ + 'label' => __( 'Separators', 'fwp' ), + 'notes' => 'Enter the thousands and decimal separators, respectively', + 'html' => $this->get_setting_html( 'separators' ) + ], + 'prefix' => [ + 'label' => __( 'URL prefix', 'fwp' ), + 'html' => $this->get_setting_html( 'prefix', 'dropdown', [ + 'choices' => [ 'fwp_' => 'fwp_', '_' => '_' ] + ]) + ], + 'load_jquery' => [ + 'label' => __( 'Load jQuery', 'fwp' ), + 'notes' => 'FacetWP no longer requires jQuery, but enable if needed', + 'html' => $this->get_setting_html( 'load_jquery', 'toggle', [ + 'true_value' => 'yes', + 'false_value' => 'no' + ]) + ], + 'load_a11y' => [ + 'label' => __( 'Load a11y support', 'fwp' ), + 'notes' => 'Improved accessibility for users with disabilities', + 'html' => $this->get_setting_html( 'load_a11y', 'toggle', [ + 'true_value' => 'yes', + 'false_value' => 'no' + ]) + ], + 'strict_query_detection' => [ + 'label' => __( 'Strict query detection', 'fwp' ), + 'notes' => 'Enable if FacetWP auto-detects the wrong archive query', + 'html' => $this->get_setting_html( 'strict_query_detection', 'toggle', [ + 'true_value' => 'yes', + 'false_value' => 'no' + ]) + ], + 'debug_mode' => [ + 'label' => __( 'Debug mode', 'fwp' ), + 'notes' => 'After enabling, type "FWP.settings.debug" into the browser console on your front-end facet page', + 'html' => $this->get_setting_html( 'debug_mode', 'toggle', [ + 'true_value' => 'on', + 'false_value' => 'off', + 'message' => 'Debug Mode exposes some of your settings and can influence loading speeds.
    Make sure to disable it when not needed for troubleshooting.' + ]), + ], + 'enable_indexer' => [ + 'label' => __( 'Enable automatic indexing', 'fwp' ), + 'notes' => 'Disable to prevent editing posts and terms from updating the indexing table.', + 'html' => $this->get_setting_html( 'enable_indexer', 'toggle', [ + 'true_value' => 'yes', + 'false_value' => 'no' + ]) + ] + ] + ], + 'woocommerce' => [ + 'label' => __( 'WooCommerce', 'fwp' ), + 'fields' => [ + 'wc_enable_variations' => [ + 'label' => __( 'Support product variations?', 'fwp' ), + 'notes' => __( 'Enable if your store uses variable products.', 'fwp' ), + 'html' => $this->get_setting_html( 'wc_enable_variations', 'toggle' ) + ], + 'wc_index_all' => [ + 'label' => __( 'Index out-of-stock products?', 'fwp' ), + 'notes' => __( 'Show facet choices for out-of-stock products?', 'fwp' ), + 'html' => $this->get_setting_html( 'wc_index_all', 'toggle' ) + ] + ] + ], + 'backup' => [ + 'label' => __( 'Import / Export', 'fwp' ), + 'fields' => [ + 'export' => [ + 'label' => __( 'Export', 'fwp' ), + 'html' => $this->get_setting_html( 'export' ) + ], + 'import' => [ + 'label' => __( 'Import', 'fwp' ), + 'html' => $this->get_setting_html( 'import' ) + ] + ] + ] + ]; + + if ( $this-> get_gmaps_key_other() ) { + unset( $defaults['general']['fields']['places_version']['show'] ); + } + + if ( ! is_plugin_active( 'woocommerce/woocommerce.php' ) ) { + unset( $defaults['woocommerce'] ); + } + + if ( '_' == FWP()->helper->settings['settings']['prefix'] ) { + unset( $defaults['general']['fields']['prefix'] ); + } + + return apply_filters( 'facetwp_settings_admin', $defaults, $this ); + } + + + + /** + * Get gmaps api key if set from filter or constant + * @since 4.4 + */ + function get_gmaps_key_other() { + + $params = apply_filters( 'facetwp_gmaps_params', [] ); + if ( !empty( $params['key'] ) ) { + return $params['key']; + } + + return apply_filters( 'facetwp_gmaps_api_key', defined( 'GMAPS_API_KEY' ) ? GMAPS_API_KEY : '' ); + } + + + /** + * All facet admin fields + * @since 3.9 + */ + function get_registered_facet_fields() { + $settings = [ + 'label_any' => [ + 'label' => __( 'Default label', 'fwp' ), + 'notes' => 'Customize the "Any" label', + 'default' => __( 'Any', 'fwp' ), + 'show' => "facet.ui_type != 'checkboxes'" + ], + 'placeholder' => [ + 'label' => __( 'Placeholder text', 'fwp' ) + ], + 'parent_term' => [ + 'label' => __( 'Parent term', 'fwp' ), + 'notes' => 'To show only child terms, enter the parent term ID. Otherwise, leave blank.', + 'show' => "facet.source.substr(0, 3) == 'tax'" + ], + 'hierarchical' => [ + 'type' => 'toggle', + 'label' => __( 'Hierarchical', 'fwp' ), + 'notes' => 'Is this a hierarchical taxonomy?', + 'show' => "facet.source.substr(0, 3) == 'tax' && facet.ui_type != 'radio'" + ], + 'show_expanded' => [ + 'type' => 'toggle', + 'label' => __( 'Show expanded', 'fwp' ), + 'notes' => 'Should child terms be visible by default?', + 'show' => "facet.hierarchical == 'yes' && !['radio','fselect','dropdown'].includes(facet.ui_type)" + ], + 'multiple' => [ + 'type' => 'toggle', + 'label' => __( 'Multi-select', 'fwp' ), + 'notes' => 'Allow multiple selections?', + 'show' => "!['radio','checkboxes', 'dropdown'].includes(facet.ui_type)" + ], + 'ghosts' => [ + 'type' => 'alias', + 'items' => [ + 'ghosts' => [ + 'type' => 'toggle', + 'label' => __( 'Show ghosts', 'fwp' ), + 'notes' => 'Show choices that would return zero results?' + ], + 'preserve_ghosts' => [ + 'type' => 'toggle', + 'label' => __( 'Preserve ghost order', 'fwp' ), + 'notes' => 'Keep ghost choices in the same order?', + 'show' => "facet.ghosts == 'yes' && facet.orderby != 'count'" + ] + ] + ], + 'modifiers' => [ + 'type' => 'alias', + 'items' => [ + 'modifier_type' => [ + 'type' => 'select', + 'label' => __( 'Value modifiers', 'fwp' ), + 'notes' => 'Include or exclude certain values?', + 'choices' => [ + 'off' => __( 'Off', 'fwp' ), + 'exclude' => __( 'Exclude these values', 'fwp' ), + 'include' => __( 'Show only these values', 'fwp' ) + ] + ], + 'modifier_values' => [ + 'type' => 'textarea', + 'label' => '', + 'placeholder' => 'Add one value per line', + 'show' => "facet.modifier_type != 'off'" + ] + ] + ], + 'operator' => [ + 'type' => 'select', + 'label' => __( 'Facet logic', 'fwp' ), + 'notes' => 'How should multiple selections affect the results?', + 'choices' => [ + 'and' => __( 'AND (match all)', 'fwp' ), + 'or' => __( 'OR (match any)', 'fwp' ), + ], + 'show' => "facet.ui_type == 'checkboxes' || facet.multiple == 'yes' || facet.type == 'checkboxes' || facet.type == 'color'" + ], + 'orderby' => [ + 'type' => 'select', + 'label' => __( 'Sort by', 'fwp' ), + 'default' => 'count', + 'choices' => [ + 'count' => __( 'Highest count', 'fwp' ), + 'display_value' => __( 'Display value', 'fwp' ), + 'raw_value' => __( 'Raw value', 'fwp' ), + 'term_order' => __( 'Term order', 'fwp' ) + ] + ], + 'count' => [ + 'label' => __( 'Count', 'fwp' ), + 'notes' => 'The maximum number of choices to show (-1 for no limit)', + 'default' => 10 + ], + 'soft_limit' => [ + 'label' => __( 'Soft limit', 'fwp' ), + 'notes' => 'Show a toggle link after this many choices', + 'default' => 5, + 'show' => "facet.hierarchical != 'yes' && !['radio','fselect', 'dropdown'].includes(facet.ui_type)" + ], + 'source_other' => [ + 'label' => __( 'Other data source', 'fwp' ), + 'notes' => 'Use a separate value for the upper limit?', + 'html' => '' + ], + 'compare_type' => [ + 'type' => 'select', + 'label' => __( 'Compare type', 'fwp' ), + 'notes' => "Basic - entered range surrounds the post's range
    Enclose - entered range is fully inside the post's range
    Intersect - entered range overlaps the post's range

    When in doubt, choose Basic", + 'choices' => [ + '' => __( 'Basic', 'fwp' ), + 'enclose' => __( 'Enclose', 'fwp' ), + 'intersect' => __( 'Intersect', 'fwp' ) + ], + 'show' => "'undefined' != typeof facet.source_other && facet.source_other != ''" + ], + 'ui_type' => [ + 'label' => __( 'UI type', 'fwp' ), + 'type' => 'select', + 'choices' => [ + 'checkboxes' => __( 'Checkboxes', 'fwp' ), + 'radio' => __( 'Radio', 'fwp' ), + 'dropdown' => __( 'Dropdown', 'fwp' ), + 'fselect' => __( 'fSelect', 'fwp' ) + ] + ], + 'ui_ghosts' => [ + 'type' => 'toggle', + 'label' => __( 'Show ghosts', 'fwp' ), + 'notes' => 'Show choices that would return zero results?' + ], + 'reset_text' => [ + 'label' => __( 'Reset text', 'fwp' ), + 'default' => 'Reset' + ] + ]; + + foreach ( FWP()->helper->facet_types as $name => $obj ) { + if ( method_exists( $obj, 'register_fields' ) ) { + $settings = array_merge( $settings, $obj->register_fields() ); + } + } + + return $settings; + } + + + /** + * Return HTML for a single facet field (supports aliases) + * @since 3.9 + */ + function get_facet_field_html( $name ) { + ob_start(); + + $fields = FWP()->settings->get_registered_facet_fields(); + + if ( isset( $fields[ $name ] ) ) { + $field = $fields[ $name ]; + + if ( isset( $field['type'] ) && 'alias' == $field['type'] ) { + foreach ( $field['items'] as $k => $v ) { + $v['name'] = $k; + $this->render_facet_field( $v ); + } + } + else { + $field['name'] = $name; + $this->render_facet_field( $field ); + } + } + + return ob_get_clean(); + } + + + /** + * Render a facet field + * @since 3.9 + */ + function render_facet_field( $field ) { + $name = str_replace( '_', '-', $field['name'] ); + $type = $field['type'] ?? 'text'; + $placeholder = $field['placeholder'] ?? ''; + $show = isset( $field['show'] ) ? ' v-show="' . $field['show'] . '"' : ''; + $default = isset( $field['default'] ) ? ' value="' . $field['default'] . '"' : ''; + $label = empty( $field['label'] ) ? '' : $field['label']; + $checked = ( isset( $field['default'] ) && 'checked' == $field['default'] ) ? ' checked="checked"' : ''; + + if ( isset( $field['notes'] ) ) { + $label = '
    ' . $label . '
    ' . $field['notes'] . '
    '; + } + + ob_start(); + + if ( isset( $field['html'] ) ) { + echo $field['html']; + } + elseif ( 'text' == $type ) { +?> + /> + + + + + + + +
    > +
    +
    +
    + + + /> +
    +
    get_activation_status(); ?>
    + + + + get_gmaps_key_other() ? ' disabled placeholder="'.$this->get_gmaps_key_other().'"' : ''; ?> /> + + get_gmaps_key_other() ? '
    The API key is set with a hook or in wp-config.php
    ' : ''; ?> + + + + get_gmaps_key_other() ) ) : ?> + +
    +
    +
    +
    +
    + + + +
    + +
    +
    +
    +
    +
    + + + +
    + + + + + +
    + + + + Thousands: +   + Decimal: + + + + + +
    Select: | | |
    +
    + +
    + + + +
    +
    +
    +
    +
    + + + + + + + + + + +
    + + +helper->settings; + + foreach ( $settings['facets'] as $facet ) { + $export['facet-' . $facet['name']] = 'Facet - ' . $facet['label']; + } + + foreach ( $settings['templates'] as $template ) { + $export['template-' . $template['name']] = 'Listing - '. $template['label']; + } + + return $export; + } + + + /** + * Get the activation status + * @since 3.0.0 + */ + function get_activation_status() { + $message = __( 'Not yet activated', 'fwp' ); + $status = FWP()->helper->get_license_meta( 'status' ); + + if ( false !== $status ) { + if ( 'success' == $status ) { + $expiration = FWP()->helper->get_license_meta( 'expiration' ); + $expiration = date( 'M j, Y', strtotime( $expiration ) ); + $message = __( 'Valid until', 'fwp' ) . ' ' . $expiration; + } + else { + $message = FWP()->helper->get_license_meta( 'message' ); + } + } + + return $message; + } + + /** + * Load i18n admin strings + * @since 3.2.0 + */ + function get_i18n_strings() { + return [ + 'Number of grid columns' => __( 'Number of grid columns', 'fwp' ), + 'Spacing between results' => __( 'Spacing between results', 'fwp' ), + 'No results text' => __( 'No results text', 'fwp' ), + 'Text style' => __( 'Text style', 'fwp' ), + 'Text color' => __( 'Text color', 'fwp' ), + 'Font size' => __( 'Font size', 'fwp' ), + 'Background color' => __( 'Background color', 'fwp' ), + 'Border' => __( 'Border', 'fwp' ), + 'Border style' => __( 'Border style', 'fwp' ), + 'None' => __( 'None', 'fwp' ), + 'Solid' => __( 'Solid', 'fwp' ), + 'Dashed' => __( 'Dashed', 'fwp' ), + 'Dotted' => __( 'Dotted', 'fwp' ), + 'Double' => __( 'Double', 'fwp' ), + 'Border color' => __( 'Border color', 'fwp' ), + 'Border width' => __( 'Border width', 'fwp' ), + 'Button text' => __( 'Button text', 'fwp' ), + 'Button text color' => __( 'Button text color', 'fwp' ), + 'Button padding' => __( 'Button padding', 'fwp' ), + 'Separator' => __( 'Separator', 'fwp' ), + 'Custom CSS' => __( 'Custom CSS', 'fwp' ), + 'Column widths' => __( 'Column widths', 'fwp' ), + 'Content' => __( 'Content', 'fwp' ), + 'Image size' => __( 'Image size', 'fwp' ), + 'Author field' => __( 'Author field', 'fwp' ), + 'Display name' => __( 'Display name', 'fwp' ), + 'User login' => __( 'User login', 'fwp' ), + 'User ID' => __( 'User ID', 'fwp' ), + 'Field type' => __( 'Field type', 'fwp' ), + 'Text' => __( 'Text', 'fwp' ), + 'Date' => __( 'Date', 'fwp' ), + 'Number' => __( 'Number', 'fwp' ), + 'Date format' => __( 'Date format', 'fwp' ), + 'Input format' => __( 'Input format', 'fwp' ), + 'Number format' => __( 'Number format', 'fwp' ), + 'Link' => __( 'Link', 'fwp' ), + 'Link type' => __( 'Link type', 'fwp' ), + 'Post URL' => __( 'Post URL', 'fwp' ), + 'Custom URL' => __( 'Custom URL', 'fwp' ), + 'Open in new tab?' => __( 'Open in new tab?', 'fwp' ), + 'Prefix' => __( 'Prefix', 'fwp' ), + 'Suffix' => __( 'Suffix', 'fwp' ), + 'Hide item?' => __( 'Hide item?', 'fwp' ), + 'Padding' => __( 'Padding', 'fwp' ), + 'CSS class' => __( 'CSS class', 'fwp' ), + 'Button Border' => __( 'Button border', 'fwp' ), + 'Term URL' => __( 'Term URL', 'fwp' ), + 'Fetch' => __( 'Fetch', 'fwp' ), + 'All post types' => __( 'All post types', 'fwp' ), + 'and show' => __( 'and show', 'fwp' ), + 'per page' => __( 'per page', 'fwp' ), + 'Sort by' => __( 'Sort by', 'fwp' ), + 'Posts' => __( 'Posts', 'fwp' ), + 'Post Title' => __( 'Post Title', 'fwp' ), + 'Post Name' => __( 'Post Name', 'fwp' ), + 'Post Type' => __( 'Post Type', 'fwp' ), + 'Post Date' => __( 'Post Date', 'fwp' ), + 'Post Modified' => __( 'Post Modified', 'fwp' ), + 'Comment Count' => __( 'Comment Count', 'fwp' ), + 'Menu Order' => __( 'Menu Order', 'fwp' ), + 'Custom Fields' => __( 'Custom Fields', 'fwp' ), + 'Narrow results by' => __( 'Narrow results by', 'fwp' ), + 'Hit Enter' => __( 'Hit Enter', 'fwp' ), + 'Add query sort' => __( 'Add query sort', 'fwp' ), + 'Add query filter' => __( 'Add query filter', 'fwp' ), + 'Clear' => __( 'Clear', 'fwp' ), + 'Enter term slugs' => __( 'Enter term slugs', 'fwp' ), + 'Enter values' => __( 'Enter values', 'fwp' ), + 'Layout' => __( 'Layout', 'fwp' ), + 'Content' => __( 'Content', 'fwp' ), + 'Style' => __( 'Style', 'fwp' ), + 'Row' => __( 'Row', 'fwp' ), + 'Column' => __( 'Column', 'fwp' ), + 'Start typing' => __( 'Start typing', 'fwp' ), + 'Label' => __( 'Label', 'fwp' ), + 'Unique name' => __( 'Unique name', 'fwp' ), + 'Facet type' => __( 'Facet type', 'fwp' ), + 'Copy shortcode' => __( 'Copy shortcode', 'fwp' ), + 'Data source' => __( 'Data source', 'fwp' ), + 'Switch to advanced mode' => __( 'Switch to advanced mode', 'fwp' ), + 'Switch to visual mode' => __( 'Switch to visual mode', 'fwp' ), + 'Display' => __( 'Display', 'fwp' ), + 'Query' => __( 'Query', 'fwp' ), + 'Help' => __( 'Help', 'fwp' ), + 'Display Code' => __( 'Display Code', 'fwp' ), + 'Query Arguments' => __( 'Query Arguments', 'fwp' ), + 'Saving' => __( 'Saving', 'fwp' ), + 'Indexing' => __( 'Indexing', 'fwp' ), + 'The index table is empty' => __( 'The index table is empty', 'fwp' ), + 'Indexing complete' => __( 'Indexing complete', 'fwp' ), + 'Looking' => __( 'Looking', 'fwp' ), + 'Purging' => __( 'Purging', 'fwp' ), + 'Copied!' => __( 'Copied!', 'fwp' ), + 'Press CTRL+C to copy' => __( 'Press CTRL+C to copy', 'fwp' ), + 'Activating' => __( 'Activating', 'fwp' ), + 'Re-index' => __( 'Re-index', 'fwp' ), + 'Stop indexer' => __( 'Stop indexer', 'fwp' ), + 'Loading' => __( 'Loading', 'fwp' ), + 'Importing' => __( 'Importing', 'fwp' ), + 'Convert to query args' => __( 'Convert to query args', 'fwp' ) + ]; + } + + + /** + * Get available image sizes + * @since 3.2.7 + */ + function get_image_sizes() { + global $_wp_additional_image_sizes; + + $sizes = []; + + $default_sizes = [ 'thumbnail', 'medium', 'medium_large', 'large', 'full' ]; + + foreach ( get_intermediate_image_sizes() as $size ) { + if ( in_array( $size, $default_sizes ) ) { + $sizes[ $size ]['width'] = (int) get_option( "{$size}_size_w" ); + $sizes[ $size ]['height'] = (int) get_option( "{$size}_size_h" ); + } + elseif ( isset( $_wp_additional_image_sizes[ $size ] ) ) { + $sizes[ $size ] = $_wp_additional_image_sizes[ $size ]; + } + } + + return $sizes; + } + + + /** + * Return an array of formatted image sizes + * @since 3.2.7 + */ + function get_image_size_labels() { + $labels = []; + $sizes = $this->get_image_sizes(); + + foreach ( $sizes as $size => $data ) { + $height = ( 0 === $data['height'] ) ? 'w' : 'x' . $data['height']; + $label = $size . ' (' . $data['width'] . $height . ')'; + $labels[ $size ] = $label; + } + + $labels['full'] = __( 'full', 'fwp' ); + + return $labels; + } + + + /** + * Create SVG images (based on Font Awesome) + * @license https://fontawesome.com/license/free CC BY 4.0 + * @since 3.6.5 + */ + function get_svg() { + $output = []; + + $icons = [ + // [viewBox width, viewBox height, icon width, svg data] + "align-center" => [448, 512, 14, "M432 160H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0 256H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM108.1 96h231.81A12.09 12.09 0 0 0 352 83.9V44.09A12.09 12.09 0 0 0 339.91 32H108.1A12.09 12.09 0 0 0 96 44.09V83.9A12.1 12.1 0 0 0 108.1 96zm231.81 256A12.09 12.09 0 0 0 352 339.9v-39.81A12.09 12.09 0 0 0 339.91 288H108.1A12.09 12.09 0 0 0 96 300.09v39.81a12.1 12.1 0 0 0 12.1 12.1z"], + "align-left" => [448, 512, 14, "M12.83 352h262.34A12.82 12.82 0 0 0 288 339.17v-38.34A12.82 12.82 0 0 0 275.17 288H12.83A12.82 12.82 0 0 0 0 300.83v38.34A12.82 12.82 0 0 0 12.83 352zm0-256h262.34A12.82 12.82 0 0 0 288 83.17V44.83A12.82 12.82 0 0 0 275.17 32H12.83A12.82 12.82 0 0 0 0 44.83v38.34A12.82 12.82 0 0 0 12.83 96zM432 160H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0 256H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16z"], + "align-right" => [448, 512, 14, "M16 224h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16zm416 192H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm3.17-384H172.83A12.82 12.82 0 0 0 160 44.83v38.34A12.82 12.82 0 0 0 172.83 96h262.34A12.82 12.82 0 0 0 448 83.17V44.83A12.82 12.82 0 0 0 435.17 32zm0 256H172.83A12.82 12.82 0 0 0 160 300.83v38.34A12.82 12.82 0 0 0 172.83 352h262.34A12.82 12.82 0 0 0 448 339.17v-38.34A12.82 12.82 0 0 0 435.17 288z"], + "arrow-circle-up" => [512, 512, 16, "M8 256C8 119 119 8 256 8s248 111 248 248-111 248-248 248S8 393 8 256zm143.6 28.9l72.4-75.5V392c0 13.3 10.7 24 24 24h16c13.3 0 24-10.7 24-24V209.4l72.4 75.5c9.3 9.7 24.8 9.9 34.3.4l10.9-11c9.4-9.4 9.4-24.6 0-33.9L273 107.7c-9.4-9.4-24.6-9.4-33.9 0L106.3 240.4c-9.4 9.4-9.4 24.6 0 33.9l10.9 11c9.6 9.5 25.1 9.3 34.4-.4z"], + "bold" => [384, 512, 12, "M333.49 238a122 122 0 0 0 27-65.21C367.87 96.49 308 32 233.42 32H34a16 16 0 0 0-16 16v48a16 16 0 0 0 16 16h31.87v288H34a16 16 0 0 0-16 16v48a16 16 0 0 0 16 16h209.32c70.8 0 134.14-51.75 141-122.4 4.74-48.45-16.39-92.06-50.83-119.6zM145.66 112h87.76a48 48 0 0 1 0 96h-87.76zm87.76 288h-87.76V288h87.76a56 56 0 0 1 0 112z"], + "caret-down" => [320, 512, 10, "M31.3 192h257.3c17.8 0 26.7 21.5 14.1 34.1L174.1 354.8c-7.8 7.8-20.5 7.8-28.3 0L17.2 226.1C4.6 213.5 13.5 192 31.3 192z"], + "cog" => [512, 512, 16, "M487.4 315.7l-42.6-24.6c4.3-23.2 4.3-47 0-70.2l42.6-24.6c4.9-2.8 7.1-8.6 5.5-14-11.1-35.6-30-67.8-54.7-94.6-3.8-4.1-10-5.1-14.8-2.3L380.8 110c-17.9-15.4-38.5-27.3-60.8-35.1V25.8c0-5.6-3.9-10.5-9.4-11.7-36.7-8.2-74.3-7.8-109.2 0-5.5 1.2-9.4 6.1-9.4 11.7V75c-22.2 7.9-42.8 19.8-60.8 35.1L88.7 85.5c-4.9-2.8-11-1.9-14.8 2.3-24.7 26.7-43.6 58.9-54.7 94.6-1.7 5.4.6 11.2 5.5 14L67.3 221c-4.3 23.2-4.3 47 0 70.2l-42.6 24.6c-4.9 2.8-7.1 8.6-5.5 14 11.1 35.6 30 67.8 54.7 94.6 3.8 4.1 10 5.1 14.8 2.3l42.6-24.6c17.9 15.4 38.5 27.3 60.8 35.1v49.2c0 5.6 3.9 10.5 9.4 11.7 36.7 8.2 74.3 7.8 109.2 0 5.5-1.2 9.4-6.1 9.4-11.7v-49.2c22.2-7.9 42.8-19.8 60.8-35.1l42.6 24.6c4.9 2.8 11 1.9 14.8-2.3 24.7-26.7 43.6-58.9 54.7-94.6 1.5-5.5-.7-11.3-5.6-14.1zM256 336c-44.1 0-80-35.9-80-80s35.9-80 80-80 80 35.9 80 80-35.9 80-80 80z"], + "columns" => [512, 512, 16, "M464 32H48C21.49 32 0 53.49 0 80v352c0 26.51 21.49 48 48 48h416c26.51 0 48-21.49 48-48V80c0-26.51-21.49-48-48-48zM224 416H64V160h160v256zm224 0H288V160h160v256z"], + "eye-slash" => [640, 512, 20, "M320 400c-75.85 0-137.25-58.71-142.9-133.11L72.2 185.82c-13.79 17.3-26.48 35.59-36.72 55.59a32.35 32.35 0 0 0 0 29.19C89.71 376.41 197.07 448 320 448c26.91 0 52.87-4 77.89-10.46L346 397.39a144.13 144.13 0 0 1-26 2.61zm313.82 58.1l-110.55-85.44a331.25 331.25 0 0 0 81.25-102.07 32.35 32.35 0 0 0 0-29.19C550.29 135.59 442.93 64 320 64a308.15 308.15 0 0 0-147.32 37.7L45.46 3.37A16 16 0 0 0 23 6.18L3.37 31.45A16 16 0 0 0 6.18 53.9l588.36 454.73a16 16 0 0 0 22.46-2.81l19.64-25.27a16 16 0 0 0-2.82-22.45zm-183.72-142l-39.3-30.38A94.75 94.75 0 0 0 416 256a94.76 94.76 0 0 0-121.31-92.21A47.65 47.65 0 0 1 304 192a46.64 46.64 0 0 1-1.54 10l-73.61-56.89A142.31 142.31 0 0 1 320 112a143.92 143.92 0 0 1 144 144c0 21.63-5.29 41.79-13.9 60.11z"], + "italic" => [320, 512, 10, "M320 48v32a16 16 0 0 1-16 16h-62.76l-80 320H208a16 16 0 0 1 16 16v32a16 16 0 0 1-16 16H16a16 16 0 0 1-16-16v-32a16 16 0 0 1 16-16h62.76l80-320H112a16 16 0 0 1-16-16V48a16 16 0 0 1 16-16h192a16 16 0 0 1 16 16z"], + "lock" => [448, 512, 14, "M400 224h-24v-72C376 68.2 307.8 0 224 0S72 68.2 72 152v72H48c-26.5 0-48 21.5-48 48v192c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V272c0-26.5-21.5-48-48-48zm-104 0H152v-72c0-39.7 32.3-72 72-72s72 32.3 72 72v72z"], + "lock-open" => [576, 512, 18, "M423.5 0C339.5.3 272 69.5 272 153.5V224H48c-26.5 0-48 21.5-48 48v192c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V272c0-26.5-21.5-48-48-48h-48v-71.1c0-39.6 31.7-72.5 71.3-72.9 40-.4 72.7 32.1 72.7 72v80c0 13.3 10.7 24 24 24h32c13.3 0 24-10.7 24-24v-80C576 68 507.5-.3 423.5 0z"], + "minus-circle" => [512, 512, 16, "M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zM124 296c-6.6 0-12-5.4-12-12v-56c0-6.6 5.4-12 12-12h264c6.6 0 12 5.4 12 12v56c0 6.6-5.4 12-12 12H124z"], + "plus" => [448, 512, 14, "M416 208H272V64c0-17.67-14.33-32-32-32h-32c-17.67 0-32 14.33-32 32v144H32c-17.67 0-32 14.33-32 32v32c0 17.67 14.33 32 32 32h144v144c0 17.67 14.33 32 32 32h32c17.67 0 32-14.33 32-32V304h144c17.67 0 32-14.33 32-32v-32c0-17.67-14.33-32-32-32z"], + "plus-circle" => [512, 512, 16, "M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm144 276c0 6.6-5.4 12-12 12h-92v92c0 6.6-5.4 12-12 12h-56c-6.6 0-12-5.4-12-12v-92h-92c-6.6 0-12-5.4-12-12v-56c0-6.6 5.4-12 12-12h92v-92c0-6.6 5.4-12 12-12h56c6.6 0 12 5.4 12 12v92h92c6.6 0 12 5.4 12 12v56z"], + "times" => [352, 512, 11, "M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z"] + ]; + + foreach ( $icons as $name => $attr ) { + $svg = ''; + $search = [ '{name}', '{w}', '{h}', '{faw}', '{d}' ]; + $replace = [ $name, $attr[0], $attr[1], $attr[2], $attr[3] ]; + $output[ $name ] = str_replace( $search, $replace, $svg ); + } + + return $output; + } +} diff --git a/wp-content/plugins/facetwp/includes/class-updater.php b/wp-content/plugins/facetwp/includes/class-updater.php new file mode 100644 index 000000000..2121b8fca --- /dev/null +++ b/wp-content/plugins/facetwp/includes/class-updater.php @@ -0,0 +1,174 @@ + $info['Name'], + 'version' => $info['Version'], + 'description' => $info['Description'], + 'plugin_path' => $plugin_path, + ]; + } + } + } + + return $output; + } + + + /** + * Handle the "View Details" popup + * + * $args->slug = "facetwp-flyout" + * plugin_path = "facetwp-flyout/facetwp-flyout.php" + */ + function plugins_api( $result, $action, $args ) { + if ( 'plugin_information' == $action ) { + $slug = $args->slug; + $to_check = $this->get_plugins_to_check(); + + $response = get_option( 'facetwp_updater_response', '' ); + $response = json_decode( $response, true ); + + if ( isset( $to_check[ $slug ] ) && isset( $response[ $slug ] ) ) { + $local_data = $to_check[ $slug ]; + $remote_data = $response[ $slug ]; + + return (object) [ + 'name' => $local_data['name'], + 'slug' => $local_data['plugin_path'], + 'version' => $remote_data['version'], + 'last_updated' => $remote_data['last_updated'], + 'download_link' => $remote_data['package'], + 'sections' => [ + 'description' => $local_data['description'], + 'changelog' => $remote_data['sections']['changelog'] + ], + 'homepage' => 'https://facetwp.com/', + 'rating' => 100, + 'num_ratings' => 1 + ]; + } + } + + return $result; + } + + + /** + * Grab (and cache) plugin update data + */ + function check_update( $transient ) { + if ( empty( $transient->checked ) ) { + return $transient; + } + + $now = strtotime( 'now' ); + $response = get_option( 'facetwp_updater_response', '' ); + $ts = (int) get_option( 'facetwp_updater_last_checked' ); + $plugins = $this->get_plugins_to_check(); + $force_check = ! empty( $_GET['force-check'] ); // 'Check again' link on Dashboard > Updates page + + if ( empty( $response ) || $ts + 14400 < $now || $force_check ) { + + $request = wp_remote_post( 'https://api.facetwp.com', [ + 'body' => [ + 'action' => 'check_plugins', + 'slugs' => array_keys( $plugins ), + 'license' => FWP()->helper->get_license_key(), + 'host' => FWP()->helper->get_http_host(), + 'wp_v' => get_bloginfo( 'version' ), + 'fwp_v' => FACETWP_VERSION, + 'php_v' => phpversion(), + ] + ] ); + + if ( ! is_wp_error( $request ) || 200 == wp_remote_retrieve_response_code( $request ) ) { + $body = json_decode( $request['body'], true ); + $activation = json_encode( $body['activation'] ); + $response = json_encode( $body['slugs'] ); + } + + update_option( 'facetwp_activation', $activation ); + update_option( 'facetwp_updater_response', $response, 'no' ); + update_option( 'facetwp_updater_last_checked', $now, 'no' ); + } + + if ( ! empty( $response ) ) { + $response = json_decode( $response, true ); + + foreach ( $response as $slug => $info ) { + if ( isset( $plugins[ $slug ] ) ) { + $plugin_path = $plugins[ $slug ]['plugin_path']; + $response_obj = (object) [ + 'slug' => $slug, + 'plugin' => $plugin_path, + 'new_version' => $info['version'], + 'package' => $info['package'], + 'requires' => $info['requires'], + 'tested' => $info['tested'] + ]; + + if ( version_compare( $plugins[ $slug ]['version'], $info['version'], '<' ) ) { + $transient->response[ $plugin_path ] = $response_obj; + } + else { + $transient->no_update[ $plugin_path ] = $response_obj; + } + } + } + } + + return $transient; + } + + + /** + * Display a license reminder on the plugin list screen + */ + function in_plugin_update_message( $plugin_data, $response ) { + if ( ! FWP()->helper->is_license_active() ) { + $price_id = (int) FWP()->helper->get_license_meta( 'price_id' ); + $license = FWP()->helper->get_license_key(); + + if ( 0 < $price_id ) { + echo '
    ' . sprintf( + __( 'Please renew your license for automatic updates.', 'fwp' ), + esc_url( "https://facetwp.com/checkout/?edd_action=add_to_cart&download_id=24&edd_options[price_id]=$price_id&discount=$license" ) + ); + } + else { + echo '
    ' . __( 'Please activate your FacetWP license for automatic updates.', 'fwp' ); + } + } + } +} + +new FacetWP_Updater(); diff --git a/wp-content/plugins/facetwp/includes/class-upgrade.php b/wp-content/plugins/facetwp/includes/class-upgrade.php new file mode 100644 index 000000000..c2b4be98c --- /dev/null +++ b/wp-content/plugins/facetwp/includes/class-upgrade.php @@ -0,0 +1,142 @@ +version = FACETWP_VERSION; + $this->last_version = get_option( 'facetwp_version' ); + + if ( version_compare( $this->last_version, $this->version, '<' ) ) { + if ( version_compare( $this->last_version, '0.1.0', '<' ) ) { + require_once( ABSPATH . 'wp-admin/includes/upgrade.php' ); + $this->clean_install(); + } + else { + $this->run_upgrade(); + } + + update_option( 'facetwp_version', $this->version ); + } + } + + + private function clean_install() { + global $wpdb; + + $int = apply_filters( 'facetwp_use_bigint', false ) ? 'BIGINT' : 'INT'; + + $charset_collate = $wpdb->get_charset_collate(); + + $sql = " + CREATE TABLE IF NOT EXISTS {$wpdb->prefix}facetwp_index ( + id BIGINT unsigned not null auto_increment, + post_id $int unsigned, + facet_name VARCHAR(50), + facet_value VARCHAR(50), + facet_display_value VARCHAR(200), + term_id $int unsigned default '0', + parent_id $int unsigned default '0', + depth INT unsigned default '0', + variation_id $int unsigned default '0', + PRIMARY KEY (id), + INDEX post_id_idx (post_id), + INDEX facet_name_idx (facet_name), + INDEX facet_name_value_idx (facet_name, facet_value) + ) $charset_collate"; + dbDelta( $sql ); + + // Add default settings + $settings = file_get_contents( FACETWP_DIR . '/assets/js/src/sample.json' ); + add_option( 'facetwp_settings', $settings ); + } + + + private function run_upgrade() { + global $wpdb; + + $table = sanitize_key( $wpdb->prefix . 'facetwp_index' ); + + $changed = false; + $settings = get_option( 'facetwp_settings' ); + $settings = json_decode( $settings, true ); + + if ( version_compare( $this->last_version, '3.1.0', '<' ) ) { + $wpdb->query( "ALTER TABLE $table MODIFY facet_name VARCHAR(50)" ); + $wpdb->query( "ALTER TABLE $table MODIFY facet_value VARCHAR(50)" ); + $wpdb->query( "ALTER TABLE $table MODIFY facet_display_value VARCHAR(200)" ); + $wpdb->query( "CREATE INDEX facet_name_value_idx ON $table (facet_name, facet_value)" ); + } + + if ( version_compare( $this->last_version, '3.3.2', '<' ) ) { + $wpdb->query( "CREATE INDEX post_id_idx ON $table (post_id)" ); + $wpdb->query( "DROP INDEX facet_source_idx ON $table" ); + $wpdb->query( "ALTER TABLE $table DROP COLUMN facet_source" ); + } + + if ( version_compare( $this->last_version, '3.3.3', '<' ) ) { + if ( function_exists( 'SWP' ) ) { + $engines = array_keys( SWP()->settings['engines'] ); + + foreach ( $settings['facets'] as $key => $facet ) { + if ( 'search' == $facet['type'] && in_array( $facet['search_engine'], $engines ) ) { + $settings['facets'][ $key ]['search_engine'] = 'swp_' . $facet['search_engine']; + $changed = true; + } + } + } + } + + if ( version_compare( $this->last_version, '3.5.3', '<' ) ) { + update_option( 'facetwp_updater_response', '', 'no' ); + update_option( 'facetwp_updater_last_checked', '', 'no' ); + } + + if ( version_compare( $this->last_version, '3.5.4', '<' ) ) { + if ( ! isset( $settings['settings']['prefix'] ) ) { + $settings['settings']['prefix'] = 'fwp_'; + $changed = true; + } + } + + if ( version_compare( $this->last_version, '4.1.8', '<' ) ) { + foreach ( $settings['facets'] as $key => $facet ) { + if ( 'hierarchy' == $facet['type'] ) { + $settings['facets'][ $key ]['soft_limit'] = $settings['facets'][ $key ]['count']; + unset( $settings['facets'][ $key ]['count'] ); + $changed = true; + } + } + } + + if ( version_compare( $this->last_version, '4.3.4', '<' ) ) { + if ( ! isset( $settings['settings']['enable_indexer'] ) ) { + $settings['settings']['enable_indexer'] = 'yes'; + $changed = true; + } + } + + if ( version_compare( $this->last_version, '4.4', '<' ) ) { + if ( ! isset( $settings['settings']['gmaps_api_key'] ) ) { + $settings['settings']['gmaps_api_key'] = ''; + $changed = true; + } + + if ( ! isset( $settings['settings']['places_version'] ) ) { + $api_key = defined( 'GMAPS_API_KEY' ) ? GMAPS_API_KEY : $settings['settings']['gmaps_api_key']; + $is_legacy = apply_filters( 'facetwp_gmaps_api_key', $api_key ); + + $settings['settings']['places_version'] = $is_legacy ? 'places-service' : 'place-class'; + $changed = true; + } + } + + if ( $changed ) { + update_option( 'facetwp_settings', json_encode( $settings ) ); + } + } +} diff --git a/wp-content/plugins/facetwp/includes/facets/autocomplete.php b/wp-content/plugins/facetwp/includes/facets/autocomplete.php new file mode 100644 index 000000000..5f22975b2 --- /dev/null +++ b/wp-content/plugins/facetwp/includes/facets/autocomplete.php @@ -0,0 +1,170 @@ +label = __( 'Autocomplete', 'fwp' ); + $this->fields = [ 'placeholder' ]; + + // ajax + add_action( 'facetwp_autocomplete_load', [ $this, 'ajax_load' ] ); + + // css-based template + add_action( 'facetwp_init', [ $this, 'maybe_buffer_output' ] ); + add_action( 'facetwp_found_main_query', [ $this, 'template_handler' ] ); + + // result limit + $this->limit = (int) apply_filters( 'facetwp_facet_autocomplete_limit', 10 ); + } + + + /** + * For page templates with a custom WP_Query, we need to prevent the + * page header from being output with the autocomplete JSON + */ + function maybe_buffer_output() { + if ( isset( $_POST['action'] ) && 'facetwp_autocomplete_load' == $_POST['action'] ) { + $this->is_buffering = true; + ob_start(); + } + } + + + /** + * For CSS-based templates, the "facetwp_autocomplete_load" action isn't fired + * so we need to manually check the action + */ + function template_handler() { + if ( isset( $_POST['action'] ) && 'facetwp_autocomplete_load' == $_POST['action'] ) { + if ( $this->is_buffering ) { + while ( ob_get_level() ) { + ob_end_clean(); + } + } + $this->ajax_load(); + } + } + + + /** + * Generate the facet HTML + */ + function render( $params ) { + + $output = ''; + $facet = $params['facet']; + $value = (array) $params['selected_values']; + $value = empty( $value ) ? '' : stripslashes( $value[0] ); + $placeholder = empty( $facet['placeholder'] ) ? __( 'Start typing', 'fwp-front' ) : $facet['placeholder']; + $placeholder = facetwp_i18n( $placeholder ); + $output .= ''; + $output .= ''; + return $output; + } + + + /** + * Filter the query based on selected values + */ + function filter_posts( $params ) { + global $wpdb; + + $facet = $params['facet']; + $selected_values = $params['selected_values']; + $selected_values = is_array( $selected_values ) ? $selected_values[0] : $selected_values; + $selected_values = stripslashes( $selected_values ); + + if ( empty( $selected_values ) ) { + return 'continue'; + } + + $sql = " + SELECT DISTINCT post_id FROM {$wpdb->prefix}facetwp_index + WHERE facet_name = %s AND facet_display_value LIKE %s"; + + $sql = $wpdb->prepare( $sql, $facet['name'], '%' . $selected_values . '%' ); + return facetwp_sql( $sql, $facet ); + } + + + /** + * Output any front-end scripts + */ + function front_scripts() { + FWP()->display->json['no_results'] = __( 'No results', 'fwp-front' ); + FWP()->display->assets['fComplete.js'] = FACETWP_URL . '/assets/vendor/fComplete/fComplete.js'; + FWP()->display->assets['fComplete.css'] = FACETWP_URL . '/assets/vendor/fComplete/fComplete.css'; + } + + + /** + * Load facet values via AJAX + */ + function ajax_load() { + global $wpdb; + + // optimizations + $_POST['data']['soft_refresh'] = 1; + $_POST['data']['extras'] = []; + + $query = stripslashes( $_POST['query'] ); + $query = FWP()->helper->sanitize( $wpdb->esc_like( $query ) ); + $facet_name = FWP()->helper->sanitize( $_POST['facet_name'] ); + $output = []; + + // simulate a refresh + FWP()->facet->render( + FWP()->request->process_post_data() + ); + + // then grab the matching post IDs + $where_clause = $this->get_where_clause( [ 'name' => $facet_name ] ); + + if ( ! empty( $query ) && ! empty( $facet_name ) ) { + $sql = " + SELECT DISTINCT facet_value, facet_display_value + FROM {$wpdb->prefix}facetwp_index + WHERE + facet_name = '$facet_name' AND + facet_display_value LIKE '%$query%' + $where_clause + ORDER BY facet_display_value ASC + LIMIT $this->limit"; + + $results = $wpdb->get_results( $sql, ARRAY_A ); + + foreach ( $results as $row ) { + $label = $row['facet_display_value']; + + $output[] = [ + 'value' => $label, + 'label' => apply_filters( 'facetwp_facet_display_value', $label, [ + 'selected' => false, + 'facet' => FWP()->helper->get_facet_by_name( $facet_name ), + 'row' => $row + ]) + ]; + } + } + + wp_send_json( $output ); + } + + + /** + * (Front-end) Attach settings to the AJAX response + */ + function settings_js( $params ) { + return [ + 'loadingText' => __( 'Loading', 'fwp-front' ) . '...', + 'minCharsText' => __( 'Enter {n} or more characters', 'fwp-front' ), + 'noResultsText' => __( 'No results', 'fwp-front' ), + 'maxResults' => $this->limit + ]; + } +} diff --git a/wp-content/plugins/facetwp/includes/facets/base.php b/wp-content/plugins/facetwp/includes/facets/base.php new file mode 100644 index 000000000..91e0e6a40 --- /dev/null +++ b/wp-content/plugins/facetwp/includes/facets/base.php @@ -0,0 +1,110 @@ + str_replace( 'tax/', '', $facet['source'] ), + 'term_order' => true, // Custom flag + 'fields' => 'ids', + ] ); + + if ( ! empty( $term_ids ) && ! is_wp_error( $term_ids ) ) { + $term_ids = implode( ',', $term_ids ); + $orderby = "FIELD(f.term_id, $term_ids)"; + } + } + + // Sort by depth + if ( FWP()->helper->facet_is( $facet, 'hierarchical', 'yes' ) ) { + $orderby = "f.depth, $orderby"; + } + + return $orderby; + } + + + /** + * Grab the limit, and support -1 + * @since 3.5.4 + */ + function get_limit( $facet, $default = 10, $field = 'count' ) { + $count = $facet[ $field ] ?? $default; + + if ( '-1' == $count ) { + return 1000; + } + elseif ( ctype_digit( "$count" ) ) { // ctype_digit expects input string + return $count; + } + + return $default; + } + + + /** + * Adjust the $where_clause for facets in "OR" mode + * + * FWP()->or_values contains EVERY facet and their matching post IDs + * FWP()->unfiltered_post_ids contains original post IDs + * + * @since 3.2.0 + */ + function get_where_clause( $facet ) { + + // Ignore the current facet's selections + if ( isset( FWP()->or_values ) && ( 1 < count( FWP()->or_values ) || ! isset( FWP()->or_values[ $facet['name'] ] ) ) ) { + $post_ids = []; + $or_values = FWP()->or_values; // Preserve original + unset( $or_values[ $facet['name'] ] ); + + $counter = 0; + foreach ( $or_values as $name => $vals ) { + $post_ids = ( 0 == $counter ) ? $vals : array_intersect( $post_ids, $vals ); + $counter++; + } + + $post_ids = array_intersect( $post_ids, FWP()->unfiltered_post_ids ); + } + else { + $post_ids = FWP()->unfiltered_post_ids; + } + + $post_ids = empty( $post_ids ) ? [ 0 ] : $post_ids; + return ' AND post_id IN (' . implode( ',', $post_ids ) . ')'; + } + + + /** + * Render some commonly used admin settings + * @since 3.5.6 + * @deprecated 3.9 + */ + function render_setting( $name ) { + echo FWP()->settings->get_facet_field_html( $name ); + } +} diff --git a/wp-content/plugins/facetwp/includes/facets/checkboxes.php b/wp-content/plugins/facetwp/includes/facets/checkboxes.php new file mode 100644 index 000000000..d50451f1b --- /dev/null +++ b/wp-content/plugins/facetwp/includes/facets/checkboxes.php @@ -0,0 +1,258 @@ +label = __( 'Checkboxes', 'fwp' ); + $this->fields = [ 'parent_term', 'modifiers', 'hierarchical', 'show_expanded', + 'ghosts', 'operator', 'orderby', 'count', 'soft_limit' ]; + } + + + /** + * Load the available choices + */ + function load_values( $params ) { + global $wpdb; + + $facet = $params['facet']; + $from_clause = $wpdb->prefix . 'facetwp_index f'; + $where_clause = $params['where_clause']; + + // Orderby + $orderby = $this->get_orderby( $facet ); + + // Limit + $limit = $this->get_limit( $facet ); + + // Use "OR" mode when necessary + $is_single = FWP()->helper->facet_is( $facet, 'multiple', 'no' ); + $using_or = FWP()->helper->facet_is( $facet, 'operator', 'or' ); + + // Facet in "OR" mode + if ( $is_single || $using_or ) { + $where_clause = $this->get_where_clause( $facet ); + } + + $orderby = apply_filters( 'facetwp_facet_orderby', $orderby, $facet ); + $from_clause = apply_filters( 'facetwp_facet_from', $from_clause, $facet ); + $where_clause = apply_filters( 'facetwp_facet_where', $where_clause, $facet ); + + $sql = " + SELECT f.facet_value, f.facet_display_value, f.term_id, f.parent_id, f.depth, COUNT(DISTINCT f.post_id) AS counter + FROM $from_clause + WHERE f.facet_name = '{$facet['name']}' $where_clause + GROUP BY f.facet_value + ORDER BY $orderby + LIMIT $limit"; + + $output = $wpdb->get_results( $sql, ARRAY_A ); + + // Show "ghost" facet choices + // For performance gains, only run if facets are in use + $show_ghosts = FWP()->helper->facet_is( $facet, 'ghosts', 'yes' ); + + if ( $show_ghosts && FWP()->is_filtered ) { + $raw_post_ids = implode( ',', FWP()->unfiltered_post_ids ); + + $sql = " + SELECT f.facet_value, f.facet_display_value, f.term_id, f.parent_id, f.depth, 0 AS counter + FROM $from_clause + WHERE f.facet_name = '{$facet['name']}' AND post_id IN ($raw_post_ids) + GROUP BY f.facet_value + ORDER BY $orderby + LIMIT $limit"; + + $ghost_output = $wpdb->get_results( $sql, ARRAY_A ); + $tmp = []; + + $preserve_ghosts = FWP()->helper->facet_is( $facet, 'preserve_ghosts', 'yes' ); + $orderby_count = FWP()->helper->facet_is( $facet, 'orderby', 'count' ); + + // Keep the facet placement intact + if ( $preserve_ghosts && ! $orderby_count ) { + foreach ( $ghost_output as $row ) { + $tmp[ $row['facet_value'] . ' ' ] = $row; + } + + foreach ( $output as $row ) { + $tmp[ $row['facet_value'] . ' ' ] = $row; + } + + $output = $tmp; + } + else { + // Make the array key equal to the facet_value (for easy lookup) + foreach ( $output as $row ) { + $tmp[ $row['facet_value'] . ' ' ] = $row; // Force a string array key + } + + $output = $tmp; + + foreach ( $ghost_output as $row ) { + $facet_value = $row['facet_value']; + if ( ! isset( $output[ "$facet_value " ] ) ) { + $output[ "$facet_value " ] = $row; + } + } + } + + $output = array_splice( $output, 0, $limit ); + $output = array_values( $output ); + } + + return $output; + } + + + /** + * Generate the facet HTML + */ + function render( $params ) { + $facet = $params['facet']; + + if ( FWP()->helper->facet_is( $facet, 'hierarchical', 'yes' ) ) { + return $this->render_hierarchy( $params ); + } + + $output = ''; + $values = (array) $params['values']; + $soft_limit = empty( $facet['soft_limit'] ) ? 0 : (int) $facet['soft_limit']; + + $key = 0; + foreach ( $values as $key => $row ) { + if ( 0 < $soft_limit && $key == $soft_limit ) { + $output .= '
    '; + } + $output .= $this->render_choice( $row, $params ); + } + + if ( 0 < $soft_limit && $soft_limit <= $key ) { + $output .= '
    '; + $output .= '' . facetwp_i18n( __( 'See {num} more', 'fwp-front' ) ) . ''; + $output .= '' . facetwp_i18n( __( 'See less', 'fwp-front' ) ) . ''; + } + + return $output; + } + + + /** + * Generate the facet HTML (hierarchical taxonomies) + */ + function render_hierarchy( $params ) { + + $output = ''; + $facet = $params['facet']; + $values = FWP()->helper->sort_taxonomy_values( $params['values'], $facet['orderby'] ); + + $init_depth = -1; + $last_depth = -1; + + foreach ( $values as $row ) { + $depth = (int) $row['depth']; + + if ( -1 == $last_depth ) { + $init_depth = $depth; + } + elseif ( $depth > $last_depth ) { + $output .= '
    '; + } + elseif ( $depth < $last_depth ) { + for ( $i = $last_depth; $i > $depth; $i-- ) { + $output .= '
    '; + } + } + + $output .= $this->render_choice( $row, $params ); + + $last_depth = $depth; + } + + for ( $i = $last_depth; $i > $init_depth; $i-- ) { + $output .= '
    '; + } + + return $output; + } + + + /** + * Render a single facet choice + */ + function render_choice( $row, $params ) { + $label = esc_html( $row['facet_display_value'] ); + + $output = ''; + $selected_values = (array) $params['selected_values']; + $selected = in_array( $row['facet_value'], $selected_values ) ? ' checked' : ''; + $selected .= ( '' != $row['counter'] && 0 == $row['counter'] && '' == $selected ) ? ' disabled' : ''; + $output .= '
    '; + $output .= ''; + $output .= apply_filters( 'facetwp_facet_display_value', $label, [ + 'selected' => ( '' !== $selected ), + 'facet' => $params['facet'], + 'row' => $row + ]); + $output .= ''; + $output .= '(' . $row['counter'] . ')'; + $output .= '
    '; + return $output; + } + + + /** + * Filter the query based on selected values + */ + function filter_posts( $params ) { + global $wpdb; + + $output = []; + $facet = $params['facet']; + $selected_values = $params['selected_values']; + + $sql = $wpdb->prepare( "SELECT DISTINCT post_id + FROM {$wpdb->prefix}facetwp_index + WHERE facet_name = %s", + $facet['name'] + ); + + // Match ALL values + if ( 'and' == $facet['operator'] ) { + foreach ( $selected_values as $key => $value ) { + $results = facetwp_sql( $sql . " AND facet_value IN ('$value')", $facet ); + $output = ( $key > 0 ) ? array_intersect( $output, $results ) : $results; + + if ( empty( $output ) ) { + break; + } + } + } + // Match ANY value + else { + $selected_values = implode( "','", $selected_values ); + $output = facetwp_sql( $sql . " AND facet_value IN ('$selected_values')", $facet ); + } + + return $output; + } + + + /** + * Output any front-end scripts + */ + function front_scripts() { + FWP()->display->json['expand'] = '[+]'; + FWP()->display->json['collapse'] = '[-]'; + } + + + /** + * (Front-end) Attach settings to the AJAX response + */ + function settings_js( $params ) { + $expand = empty( $params['facet']['show_expanded'] ) ? 'no' : $params['facet']['show_expanded']; + return [ 'show_expanded' => $expand ]; + } +} diff --git a/wp-content/plugins/facetwp/includes/facets/date_range.php b/wp-content/plugins/facetwp/includes/facets/date_range.php new file mode 100644 index 000000000..01f3bcc66 --- /dev/null +++ b/wp-content/plugins/facetwp/includes/facets/date_range.php @@ -0,0 +1,306 @@ +label = __( 'Date Range', 'fwp' ); + $this->fields = [ 'source_other', 'compare_type', 'date_fields', 'date_format' ]; + } + + + /** + * Generate the facet HTML + */ + function render( $params ) { + + $output = ''; + $value = (array) $params['selected_values']; + $fields = empty( $params['facet']['fields'] ) ? 'both' : $params['facet']['fields']; + + $text_date = facetwp_i18n( __( 'Date', 'fwp-front' ) ); + $text_start_date = facetwp_i18n( __( 'Start date', 'fwp-front' ) ); + $text_end_date = facetwp_i18n( __( 'End date', 'fwp-front' ) ); + + $text_date_empty = facetwp_i18n( __( 'No dates', 'fwp-front' ) ); + $text_start_date_empty = facetwp_i18n( __( 'No start dates', 'fwp-front' ) ); + $text_end_date_empty = facetwp_i18n( __( 'No end dates', 'fwp-front' ) ); + + + if ( 'exact' == $fields ) { + $output .= ''; + } + if ( 'both' == $fields || 'start_date' == $fields ) { + $output .= ''; + } + if ( 'both' == $fields || 'end_date' == $fields ) { + $output .= ''; + } + + return $output; + } + + + /** + * Filter the query based on selected values + */ + function filter_posts( $params ) { + global $wpdb; + + $facet = $params['facet']; + $values = $params['selected_values']; + $where = ''; + + $min = empty( $values[0] ) ? false : $values[0]; + $max = empty( $values[1] ) ? false : $values[1]; + + $fields = $facet['fields'] ?? 'both'; + $compare_type = empty( $facet['compare_type'] ) ? 'basic' : $facet['compare_type']; + $is_dual = ! empty( $facet['source_other'] ); + + if ( $is_dual && 'basic' != $compare_type ) { + if ( 'exact' == $fields ) { + $max = $min; + } + + /** + * Enclose type defaults + * default min or max to + * opposite value + **/ + if ( 'enclose' == $compare_type ) { + $min = ( false !== $min ) ? $min : $max; + $max = ( false !== $max ) ? $max : $min; + } + + $min = ( false !== $min ) ? $min : '0000-00-00'; + $max = ( false !== $max ) ? $max : '3000-12-31'; + + /** + * Enclose compare + * The post's range must surround the user-defined range + */ + if ( 'enclose' == $compare_type ) { + $where .= " AND LEFT(facet_value, 10) <= '$min'"; + $where .= " AND LEFT(facet_display_value, 10) >= '$max'"; + } + + /** + * Intersect compare + * @link http://stackoverflow.com/a/325964 + */ + if ( 'intersect' == $compare_type ) { + $where .= " AND LEFT(facet_value, 10) <= '$max'"; + $where .= " AND LEFT(facet_display_value, 10) >= '$min'"; + } + } + + /** + * Basic compare + * The user-defined range must surround the post's range + */ + else { + if ( 'exact' == $fields ) { + $max = $min; + } + if ( false !== $min ) { + $where .= " AND LEFT(facet_value, 10) >= '$min'"; + } + if ( false !== $max ) { + $where .= " AND LEFT(facet_display_value, 10) <= '$max'"; + } + } + + $sql = " + SELECT DISTINCT post_id FROM {$wpdb->prefix}facetwp_index + WHERE facet_name = '{$facet['name']}' $where"; + return facetwp_sql( $sql, $facet ); + } + + + /** + * Output any front-end scripts + */ + function front_scripts() { + FWP()->display->assets['fDate.css'] = FACETWP_URL . '/assets/vendor/fDate/fDate.css'; + FWP()->display->assets['fDate.js'] = FACETWP_URL . '/assets/vendor/fDate/fDate.min.js'; + } + + + function register_fields() { + return [ + 'date_fields' => [ + 'type' => 'alias', + 'items' => [ + 'fields' => [ + 'type' => 'select', + 'label' => __( 'Fields to show', 'fwp' ), + 'choices' => [ + 'both' => __( 'Start + End Dates', 'fwp' ), + 'exact' => __( 'Exact Date', 'fwp' ), + 'start_date' => __( 'Start Date', 'fwp' ), + 'end_date' => __( 'End Date', 'fwp' ) + ] + ] + ] + ], + 'date_format' => [ + 'type' => 'alias', + 'items' => [ + 'format' => [ + 'label' => __( 'Display format', 'fwp' ), + 'notes' => 'See available formatting tokens', + 'placeholder' => 'Y-m-d' + ] + ] + ] + ]; + } + + + /** + * (Front-end) Attach settings to the AJAX response + */ + function settings_js( $params ) { + global $wpdb; + + $facet = $params['facet']; + $selected_values = $params['selected_values']; + $fields = empty( $facet['fields'] ) ? 'both' : $facet['fields']; + $format = empty( $facet['format'] ) ? '' : $facet['format']; + + // Use "OR" mode by excluding the facet's own selection + $where_clause = $this->get_where_clause( $facet ); + + $sql = " + SELECT MIN(facet_value) AS `minDate`, MAX(facet_display_value) AS `maxDate` FROM {$wpdb->prefix}facetwp_index + WHERE facet_name = '{$facet['name']}' AND facet_display_value != '' $where_clause"; + $row = $wpdb->get_row( $sql ); + + $min = substr( $row->minDate ?? '', 0, 10 ); + $max = substr( $row->maxDate ?? '', 0, 10 ); + + if ( 'both' == $fields ) { + $min_upper = ! empty( $selected_values[1] ) ? $selected_values[1] : $max; + $max_lower = ! empty( $selected_values[0] ) ? $selected_values[0] : $min; + + $range = [ + 'min' => [ + 'minDate' => $min, + 'maxDate' => $min_upper + ], + 'max' => [ + 'minDate' => $max_lower, + 'maxDate' => $max + ] + ]; + } + else { + $range = [ + 'minDate' => $min, + 'maxDate' => $max + ]; + } + + return [ + 'locale' => $this->get_i18n_labels(), + 'format' => $format, + 'fields' => $fields, + 'range' => $range + ]; + } + + + function get_i18n_labels() { + $locale = get_locale(); + $locale = empty( $locale ) ? 'en' : substr( $locale, 0, 2 ); + + $locales = [ + 'ca' => [ + 'weekdays_short' => ['Dg', 'Dl', 'Dt', 'Dc', 'Dj', 'Dv', 'Ds'], + 'months_short' => ['Gen', 'Febr', 'Març', 'Abr', 'Maig', 'Juny', 'Jul', 'Ag', 'Set', 'Oct', 'Nov', 'Des'], + 'months' => ['Gener', 'Febrer', 'Març', 'Abril', 'Maig', 'Juny', 'Juliol', 'Agost', 'Setembre', 'Octubre', 'Novembre', 'Desembre'], + 'firstDayOfWeek' => 1 + ], + 'da' => [ + 'weekdays_short' => ['søn', 'man', 'tir', 'ons', 'tors', 'fre', 'lør'], + 'months_short' => ['jan', 'feb', 'mar', 'apr', 'maj', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'dec'], + 'months' => ['januar', 'februar', 'marts', 'april', 'maj', 'juni', 'juli', 'august', 'september', 'oktober', 'november', 'december'], + 'firstDayOfWeek' => 1 + ], + 'de' => [ + 'weekdays_short' => ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'], + 'months_short' => ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'], + 'months' => ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'], + 'firstDayOfWeek' => 1 + ], + 'es' => [ + 'weekdays_short' => ['Dom', 'Lun', 'Mar', 'Mié', 'Jue', 'Vie', 'Sáb'], + 'months_short' => ['Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'], + 'months' => ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'], + 'firstDayOfWeek' => 1 + ], + 'fr' => [ + 'weekdays_short' => ['dim', 'lun', 'mar', 'mer', 'jeu', 'ven', 'sam'], + 'months_short' => ['janv', 'févr', 'mars', 'avr', 'mai', 'juin', 'juil', 'août', 'sept', 'oct', 'nov', 'déc'], + 'months' => ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'], + 'firstDayOfWeek' => 1 + ], + 'it' => [ + 'weekdays_short' => ['Dom', 'Lun', 'Mar', 'Mer', 'Gio', 'Ven', 'Sab'], + 'months_short' => ['Gen', 'Feb', 'Mar', 'Apr', 'Mag', 'Giu', 'Lug', 'Ago', 'Set', 'Ott', 'Nov', 'Dic'], + 'months' => ['Gennaio', 'Febbraio', 'Marzo', 'Aprile', 'Maggio', 'Giugno', 'Luglio', 'Agosto', 'Settembre', 'Ottobre', 'Novembre', 'Dicembre'], + 'firstDayOfWeek' => 1 + ], + 'nb' => [ + 'weekdays_short' => ['Søn', 'Man', 'Tir', 'Ons', 'Tor', 'Fre', 'Lør'], + 'months_short' => ['Jan', 'Feb', 'Mar', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Des'], + 'months' => ['Januar', 'Februar', 'Mars', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Desember'], + 'firstDayOfWeek' => 1 + ], + 'nl' => [ + 'weekdays_short' => ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'], + 'months_short' => ['jan', 'feb', 'mrt', 'apr', 'mei', 'jun', 'jul', 'aug', 'sept', 'okt', 'nov', 'dec'], + 'months' => ['januari', 'februari', 'maart', 'april', 'mei', 'juni', 'juli', 'augustus', 'september', 'oktober', 'november', 'december'], + 'firstDayOfWeek' => 1 + ], + 'pl' => [ + 'weekdays_short' => ['Nd', 'Pn', 'Wt', 'Śr', 'Cz', 'Pt', 'So'], + 'months_short' => ['Sty', 'Lut', 'Mar', 'Kwi', 'Maj', 'Cze', 'Lip', 'Sie', 'Wrz', 'Paź', 'Lis', 'Gru'], + 'months' => ['Styczeń', 'Luty', 'Marzec', 'Kwiecień', 'Maj', 'Czerwiec', 'Lipiec', 'Sierpień', 'Wrzesień', 'Październik', 'Listopad', 'Grudzień'], + 'firstDayOfWeek' => 1 + ], + 'pt' => [ + 'weekdays_short' => ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb'], + 'months_short' => ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Out', 'Nov', 'Dez'], + 'months' => ['Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho', 'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro'], + 'firstDayOfWeek' => 0 + ], + 'ro' => [ + 'weekdays_short' => ['Dum', 'Lun', 'Mar', 'Mie', 'Joi', 'Vin', 'Sâm'], + 'months_short' => ['Ian', 'Feb', 'Mar', 'Apr', 'Mai', 'Iun', 'Iul', 'Aug', 'Sep', 'Oct', 'Noi', 'Dec'], + 'months' => ['Ianuarie', 'Februarie', 'Martie', 'Aprilie', 'Mai', 'Iunie', 'Iulie', 'August', 'Septembrie', 'Octombrie', 'Noiembrie', 'Decembrie'], + 'firstDayOfWeek' => 1 + ], + 'ru' => [ + 'weekdays_short' => ['Вс', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб'], + 'months_short' => ['Янв', 'Фев', 'Март', 'Апр', 'Май', 'Июнь', 'Июль', 'Авг', 'Сен', 'Окт', 'Ноя', 'Дек'], + 'months' => ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'], + 'firstDayOfWeek' => 1 + ], + 'sv' => [ + 'weekdays_short' => ['Sön', 'Mån', 'Tis', 'Ons', 'Tor', 'Fre', 'Lör'], + 'months_short' => ['Jan', 'Feb', 'Mar', 'Apr', 'Maj', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dec'], + 'months' => ['Januari', 'Februari', 'Mars', 'April', 'Maj', 'Juni', 'Juli', 'Augusti', 'September', 'Oktober', 'November', 'December'], + 'firstDayOfWeek' => 1 + ] + ]; + + if ( isset( $locales[ $locale ] ) ) { + $locales[ $locale ]['clearText'] = __( 'Clear', 'fwp-front' ); + return $locales[ $locale ]; + } + + return ''; + } +} diff --git a/wp-content/plugins/facetwp/includes/facets/dropdown.php b/wp-content/plugins/facetwp/includes/facets/dropdown.php new file mode 100644 index 000000000..be99a9e09 --- /dev/null +++ b/wp-content/plugins/facetwp/includes/facets/dropdown.php @@ -0,0 +1,74 @@ +label = __( 'Dropdown', 'fwp' ); + $this->fields = [ 'label_any', 'parent_term', 'modifiers', 'hierarchical', 'ghosts', 'orderby', 'count' ]; + } + + + /** + * Load the available choices + */ + function load_values( $params ) { + return FWP()->helper->facet_types['checkboxes']->load_values( $params ); + } + + + /** + * Generate the facet HTML + */ + function render( $params ) { + + $output = ''; + $facet = $params['facet']; + $values = (array) $params['values']; + $selected_values = (array) $params['selected_values']; + $is_hierarchical = FWP()->helper->facet_is( $facet, 'hierarchical', 'yes' ); + + if ( $is_hierarchical ) { + $values = FWP()->helper->sort_taxonomy_values( $params['values'], $facet['orderby'] ); + } + + $label_any = empty( $facet['label_any'] ) ? __( 'Any', 'fwp-front' ) : $facet['label_any']; + $label_any = facetwp_i18n( $label_any ); + + $output .= ''; + return $output; + } + + + /** + * Filter the query based on selected values + */ + function filter_posts( $params ) { + return FWP()->helper->facet_types['checkboxes']->filter_posts( $params ); + } +} diff --git a/wp-content/plugins/facetwp/includes/facets/fselect.php b/wp-content/plugins/facetwp/includes/facets/fselect.php new file mode 100644 index 000000000..fd5308033 --- /dev/null +++ b/wp-content/plugins/facetwp/includes/facets/fselect.php @@ -0,0 +1,100 @@ +label = __( 'fSelect', 'fwp' ); + $this->fields = [ 'label_any', 'parent_term', 'modifiers', 'hierarchical', 'multiple', + 'ghosts', 'operator', 'orderby', 'count' ]; + } + + + /** + * Load the available choices + */ + function load_values( $params ) { + return FWP()->helper->facet_types['checkboxes']->load_values( $params ); + } + + + /** + * Generate the facet HTML + */ + function render( $params ) { + + $output = ''; + $facet = $params['facet']; + $values = (array) $params['values']; + $selected_values = (array) $params['selected_values']; + $is_hierarchical = FWP()->helper->facet_is( $facet, 'hierarchical', 'yes' ); + + if ( $is_hierarchical ) { + $values = FWP()->helper->sort_taxonomy_values( $params['values'], $facet['orderby'] ); + } + + $multiple = FWP()->helper->facet_is( $facet, 'multiple', 'yes' ) ? ' multiple="multiple"' : ''; + $label_any = empty( $facet['label_any'] ) ? __( 'Any', 'fwp-front' ) : $facet['label_any']; + $label_any = facetwp_i18n( $label_any ); + + $output .= ''; + return $output; + } + + + /** + * Filter the query based on selected values + */ + function filter_posts( $params ) { + return FWP()->helper->facet_types['checkboxes']->filter_posts( $params ); + } + + + /** + * (Front-end) Attach settings to the AJAX response + */ + function settings_js( $params ) { + $facet = $params['facet']; + + $label_any = empty( $facet['label_any'] ) ? __( 'Any', 'fwp-front' ) : $facet['label_any']; + $label_any = facetwp_i18n( $label_any ); + + return [ + 'placeholder' => $label_any, + 'overflowText' => __( '{n} selected', 'fwp-front' ), + 'searchText' => __( 'Search', 'fwp-front' ), + 'noResultsText' => __( 'No results found', 'fwp-front' ), + 'operator' => $facet['operator'] + ]; + } + + + /** + * Output any front-end scripts + */ + function front_scripts() { + FWP()->display->assets['fSelect.css'] = FACETWP_URL . '/assets/vendor/fSelect/fSelect.css'; + FWP()->display->assets['fSelect.js'] = FACETWP_URL . '/assets/vendor/fSelect/fSelect.js'; + } +} diff --git a/wp-content/plugins/facetwp/includes/facets/hierarchy.php b/wp-content/plugins/facetwp/includes/facets/hierarchy.php new file mode 100644 index 000000000..5e6bdf957 --- /dev/null +++ b/wp-content/plugins/facetwp/includes/facets/hierarchy.php @@ -0,0 +1,182 @@ +label = __( 'Hierarchy', 'fwp' ); + $this->fields = [ 'label_any', 'modifiers', 'orderby', 'count', 'soft_limit' ]; + } + + + /** + * Load the available choices + */ + function load_values( $params ) { + global $wpdb; + + $facet = $params['facet']; + $from_clause = $wpdb->prefix . 'facetwp_index f'; + $where_clause = $params['where_clause']; + + $selected_values = (array) $params['selected_values']; + $facet_parent_id = 0; + $output = []; + + $label_any = empty( $facet['label_any'] ) ? __( 'Any', 'fwp-front' ) : $facet['label_any']; + $label_any = facetwp_i18n( $label_any ); + + // Orderby + $orderby = $this->get_orderby( $facet ); + + // Determine the parent_id and depth + if ( ! empty( $selected_values[0] ) ) { + + // Get term ID from slug + $sql = " + SELECT t.term_id + FROM {$wpdb->terms} t + INNER JOIN {$wpdb->term_taxonomy} tt ON tt.term_id = t.term_id AND tt.taxonomy = %s + WHERE t.slug = %s + LIMIT 1"; + + $value = $selected_values[0]; + $taxonomy = str_replace( 'tax/', '', $facet['source'] ); + $facet_parent_id = (int) $wpdb->get_var( $wpdb->prepare( $sql, $taxonomy, $value ) ); + + // Invalid term + if ( $facet_parent_id < 1 ) { + return []; + } + + // Create term lookup array + $depths = FWP()->helper->get_term_depths( $taxonomy ); + $max_depth = (int) $depths[ $facet_parent_id ]['depth']; + $last_parent_id = $facet_parent_id; + + // Loop backwards + for ( $i = 0; $i <= $max_depth; $i++ ) { + $output[] = [ + 'facet_value' => $depths[ $last_parent_id ]['slug'], + 'facet_display_value' => $depths[ $last_parent_id ]['name'], + 'depth' => $depths[ $last_parent_id ]['depth'] + 1, + 'counter' => 1, // FWP.settings.num_choices + ]; + + $last_parent_id = (int) $depths[ $last_parent_id ]['parent_id']; + } + + $output[] = [ + 'facet_value' => '', + 'facet_display_value' => $label_any, + 'depth' => 0, + 'counter' => 1, + ]; + + // Reverse it + $output = array_reverse( $output ); + } + + $limit = $this->get_limit( $facet ); + + // Update the WHERE clause + $where_clause .= " AND parent_id = '$facet_parent_id'"; + + $orderby = apply_filters( 'facetwp_facet_orderby', $orderby, $facet ); + $from_clause = apply_filters( 'facetwp_facet_from', $from_clause, $facet ); + $where_clause = apply_filters( 'facetwp_facet_where', $where_clause, $facet ); + + $sql = " + SELECT f.facet_value, f.facet_display_value, COUNT(DISTINCT f.post_id) AS counter + FROM $from_clause + WHERE f.facet_name = '{$facet['name']}' $where_clause + GROUP BY f.facet_value + ORDER BY $orderby + LIMIT $limit"; + + $results = $wpdb->get_results( $sql, ARRAY_A ); + $new_depth = empty( $output ) ? 0 : $output[ count( $output ) - 1 ]['depth'] + 1; + + foreach ( $results as $result ) { + $result['depth'] = $new_depth; + $result['is_choice'] = true; + $output[] = $result; + } + + return $output; + } + + + /** + * Generate the facet HTML + */ + function render( $params ) { + $facet = $params['facet']; + $values = (array) $params['values']; + $selected_values = (array) $params['selected_values']; + + $output = ''; + $soft_limit = empty( $facet['soft_limit'] ) ? 0 : (int) $facet['soft_limit']; + + $num = 0; + if ( ! empty( $values ) ) { + foreach ( $values as $row ) { + $last_depth = $last_depth ?? $row['depth']; + $selected = ( ! empty( $selected_values ) && $row['facet_value'] == $selected_values[0] ); + + $label = esc_html( $row['facet_display_value'] ); + $label = apply_filters( 'facetwp_facet_display_value', $label, [ + 'selected' => $selected, + 'facet' => $facet, + 'row' => $row + ]); + + if ( $row['depth'] > $last_depth ) { + $output .= '
    '; + } + + if ( 0 < $soft_limit && $num == $soft_limit ) { + $output .= '
    '; + } + + if ( ! $selected ) { + if ( isset( $row['is_choice'] ) ) { + $label .= ' (' . $row['counter'] . ')'; + } + else { + $arrow = apply_filters( 'facetwp_facet_hierarchy_arrow', '‹ ' ); + $label = $arrow . $label; + } + } + + $output .= '
    ' . $label . '
    '; + + if ( isset( $row['is_choice'] ) ) { + $num++; + } + + $last_depth = $row['depth']; + } + + if ( 0 < $soft_limit && $soft_limit < $num ) { + $output .= '
    '; + $output .= '' . facetwp_i18n( __( 'See {num} more', 'fwp-front' ) ) . ''; + $output .= '' . facetwp_i18n( __( 'See less', 'fwp-front' ) ) . ''; + } + + for ( $i = 0; $i <= $last_depth; $i++ ) { + $output .= '
    '; + } + } + + return $output; + } + + + /** + * Filter the query based on selected values + */ + function filter_posts( $params ) { + return FWP()->helper->facet_types['checkboxes']->filter_posts( $params ); + } +} diff --git a/wp-content/plugins/facetwp/includes/facets/map.php b/wp-content/plugins/facetwp/includes/facets/map.php new file mode 100644 index 000000000..86bd906aa --- /dev/null +++ b/wp-content/plugins/facetwp/includes/facets/map.php @@ -0,0 +1,548 @@ +label = __( 'Map', 'fwp' ); + $this->fields = [ 'longitude', 'mapid', 'map_design', 'btn_label', 'reset_label', 'cluster', 'limit', 'map_width','map_height', 'min_zoom', 'max_zoom', 'default_lat', 'default_lng', 'default_zoom', 'ajax_markers', 'marker_content' ]; + $this->field_defaults = [ + 'mapid' => 'DEMO_MAP_ID' + ]; + + add_filter( 'facetwp_index_row', [ $this, 'index_latlng' ], 1, 2 ); + add_filter( 'facetwp_render_output', [ $this, 'add_marker_data' ], 10, 2 ); + + // ajax load of marker content + add_action( 'facetwp_init', function() { + if ( isset( $_POST['action'] ) && 'facetwp_map_marker_content' == $_POST['action'] ) { + $post_id = (int) $_POST['post_id']; + $facet_name = $_POST['facet_name']; + + echo $this->get_marker_content( $post_id, $facet_name ); + wp_die(); + } + }); + } + + /** + * Generate the facet HTML + */ + function render( $params ) { + + $width = $params['facet']['map_width']; + $width = empty( $width ) ? 600 : $width; + $width = is_numeric( $width ) ? $width . 'px' : $width; + + $height = $params['facet']['map_height']; + $height = empty( $height ) ? 300 : $height; + $height = is_numeric( $height ) ? $height . 'px' : $height; + + $class = ''; + $btn_label = ( isset( $params['facet']['btn_label'] ) && '' != $params['facet']['btn_label'] ) ? $params['facet']['btn_label'] : __( 'Enable map filtering', 'facetwp-map-facet' ); + + if ( $this->is_map_filtering_enabled() ) { + $class = ' enabled'; + $btn_label = ( isset( $params['facet']['reset_label'] ) && '' != $params['facet']['reset_label'] ) ? $params['facet']['reset_label'] : __( 'Reset', 'facetwp-map-facet' ) ; + } + + $output = '
    '; + $output .= '
    '; + return $output; + } + + + /** + * Is filtering turned on for the map? + * @return bool + */ + function is_map_filtering_enabled() { + foreach ( FWP()->facet->facets as $facet ) { + if ( 'map' == $facet['type'] && ! empty( $facet['selected_values'] ) ) { + return true; + } + } + + return false; + } + + + /** + * Is a proximity facet in use? If so, return a lat/lng array + * @return mixed array of coordinates, or FALSE + */ + function is_proximity_in_use() { + foreach ( FWP()->facet->facets as $facet ) { + if ( 'proximity' == $facet['type'] && ! empty( $facet['selected_values'] ) ) { + $this->proximity_facet = $facet; + + return [ + 'lat' => (float) $facet['selected_values'][0], + 'lng' => (float) $facet['selected_values'][1], + 'radius' => (int) $facet['selected_values'][2] + ]; + } + } + + return false; + } + + + function add_marker_data( $output, $params ) { + if ( ! $this->is_map_active() ) { + return $output; + } + + // Exit if paging and limit = "all" + if ( 0 < (bool) FWP()->facet->ajax_params['soft_refresh'] ) { + if ( 'all' == FWP()->helper->facet_is( $this->map_facet, 'limit', 'all' ) ) { + $output['settings']['map'] = ''; + return $output; + } + } + + $settings = [ + 'locations' => [] + ]; + + $settings['config'] = [ + 'default_lat' => (float) $this->map_facet['default_lat'], + 'default_lng' => (float) $this->map_facet['default_lng'], + 'default_zoom' => (int) $this->map_facet['default_zoom'], + 'spiderfy' => [ + 'markersWontMove' => true, + 'markersWontHide' => true, + 'basicFormatEvents' => true, + 'keepSpiderfied' => true + ] + ]; + + if ( 'yes' == $this->map_facet['cluster'] ) { + $settings['config']['cluster'] = [ + 'zoomOnClick' => true, + 'maxZoom' => 15, + 'minimumClusterSize' => 2 + ]; + } + + $settings['init'] = [ + 'mapId' => ( '' != ( $this->map_facet['mapid'] ?? '' ) ) ? $this->map_facet['mapid'] : 'DEMO_MAP_ID', + 'gestureHandling' => 'auto', + 'zoom' => (int) $this->map_facet['default_zoom'] ?: 5, + 'minZoom' => (int) $this->map_facet['min_zoom'] ?: 1, + 'maxZoom' => (int) $this->map_facet['max_zoom'] ?: 20, + 'center' => [ + 'lat' => (float) $this->map_facet['default_lat'], + 'lng' => (float) $this->map_facet['default_lng'], + ], + ]; + + $settings = apply_filters( 'facetwp_map_init_args', $settings ); + + // Get the proximity facet's coordinates (if available) + $this->proximity_coords = $this->is_proximity_in_use(); + + if ( false !== $this->proximity_coords ) { + $marker_args = [ + 'title' => __( 'Your location', 'facetwp-map-facet' ), + 'position' => $this->proximity_coords, + 'pinOptions' => [ 'background' => '#FFD700', 'borderColor' => '#DAA520', 'scale' => 0.85, 'glyphColor' => '#DAA520'], + 'infoWindowContent' => '

    '.__( 'Your location', 'facetwp-map-facet' ).'

    ', + ]; + + $marker_args = apply_filters( 'facetwp_map_proximity_marker_args', $marker_args ); + + if ( ! empty( $marker_args ) ) { + $settings['locations'][] = $marker_args; + } + } + + // get all post IDs + if ( isset( $this->map_facet['limit'] ) && 'all' == $this->map_facet['limit'] ) { + $post_ids = isset( FWP()->filtered_post_ids ) ? + FWP()->filtered_post_ids : + FWP()->facet->query_args['post__in']; + } + // get paginated post IDs + else { + $post_ids = (array) wp_list_pluck( FWP()->facet->query->posts, 'ID' ); + } + + // remove duplicates + $post_ids = array_unique( $post_ids ); + + $all_coords = $this->get_coordinates( $post_ids, $this->map_facet ); + + foreach ( $post_ids as $post_id ) { + if ( isset( $all_coords[ $post_id ] ) ) { + foreach ( $all_coords[ $post_id ] as $coords ) { + $args = [ + 'position' => $coords, + 'post_id' => $post_id, + ]; + + if ( 'yes' !== $this->map_facet['ajax_markers'] ) { + $args['infoWindowContent'] = $this->get_marker_content( $post_id ); + } + + $args = apply_filters( 'facetwp_map_marker_args', $args, $post_id ); + + // back compat for legacy content arg + // change content to infoWindowContent (if it exists*) + // because content is now the pin arg in markers + // *for ajax markers, infoWindowContent should not be set to '' + if ( isset( $args['content'] ) ) $args['infoWindowContent'] = $args['content']; + + if ( false !== $args ) { + $settings['locations'][] = $args; + } + } + } + } + + $output['settings']['map'] = $settings; + + return $output; + } + + + /** + * Grab all coordinates from the index table + */ + function get_coordinates( $post_ids, $facet ) { + global $wpdb; + + $output = []; + + if ( ! empty( $post_ids ) ) { + $post_ids = implode( ',', $post_ids ); + + $sql = " + SELECT post_id, facet_value AS lat, facet_display_value AS lng + FROM {$wpdb->prefix}facetwp_index + WHERE facet_name = '{$facet['name']}' AND post_id IN ($post_ids)"; + + $result = $wpdb->get_results( $sql ); + + foreach ( $result as $row ) { + $output[ $row->post_id ][] = [ + 'lat' => (float) $row->lat, + 'lng' => (float) $row->lng, + ]; + } + + // Support ACF repeaters + if ( false !== $this->proximity_coords) { + foreach ( $output as $post_id => $coords ) { + if ( 1 < count( $coords ) ) { + $output[ $post_id ] = []; + + foreach ( $coords as $latlng ) { + if ( $this->is_within_bounds( $latlng ) ) { + $output[ $post_id ][] = $latlng; + } + } + } + } + } + } + + return $output; + } + + + /** + * Is the current point within the proximity bounds? + */ + function is_within_bounds( $latlng ) { + $lat1 = $latlng['lat']; + $lng1 = $latlng['lng']; + $lat2 = $this->proximity_coords['lat']; + $lng2 = $this->proximity_coords['lng']; + + $radius = $this->proximity_coords['radius']; + $unit = $this->proximity_facet['unit']; + + if ( $lat1 == $lat2 && $lng1 == $lng2 ) { + return true; + } + + $dist = sin( deg2rad( $lat1 ) ) * sin( deg2rad( $lat2 ) ) + cos( deg2rad( $lat1 ) ) * cos( deg2rad( $lat2 ) ) * cos( deg2rad( $lng1 - $lng2 ) ); + $dist = min( max( $dist, -1 ), 1 ); // force value between -1 and 1 + $dist = rad2deg( acos( $dist ) ); + + $miles = $dist * 60 * 1.1515; + $needle = ( 'km' == $unit ) ? $miles * 1.609344 : $miles; + return $needle <= $radius; + } + + + /** + * Is this page using a map facet? + */ + function is_map_active() { + foreach ( FWP()->facet->facets as $name => $facet ) { + if ( 'map' == $facet['type'] ) { + $this->map_facet = $facet; // save the facet + return true; + } + } + + return false; + } + + + /** + * Get marker content (pulled via ajax) + */ + function get_marker_content( $post_id, $facet_name = false ) { + if ( false !== $facet_name ) { + $facet = FWP()->helper->get_facet_by_name( $facet_name ); + $content = $facet['marker_content']; + } + else { + $content = $this->map_facet['marker_content']; + } + + if ( empty( $content ) ) { + return ''; + } + + global $post; + + ob_start(); + + // Set the main $post object + $post = get_post( $post_id ); + + setup_postdata( $post ); + + // Remove UTF-8 non-breaking spaces + $html = preg_replace( "/\xC2\xA0/", ' ', $content ); + + eval( '?>' . $html ); + + // Reset globals + wp_reset_postdata(); + + // Store buffered output + return ob_get_clean(); + } + + + /** + * Filter the query based on the map bounds + */ + function filter_posts( $params ) { + global $wpdb; + + $facet = $params['facet']; + $selected_values = (array) $params['selected_values']; + + $swlat = (float) $selected_values[0]; + $swlng = (float) $selected_values[1]; + $nelat = (float) $selected_values[2]; + $nelng = (float) $selected_values[3]; + + // @url https://stackoverflow.com/a/35944747 + if ( $swlng < $nelng ) { + $compare_lng = "facet_display_value BETWEEN $swlng AND $nelng"; + } + else { + $compare_lng = "facet_display_value BETWEEN $swlng AND 180 OR "; + $compare_lng .= "facet_display_value BETWEEN -180 AND $nelng"; + } + + $sql = " + SELECT DISTINCT post_id FROM {$wpdb->prefix}facetwp_index + WHERE facet_name = '{$facet['name']}' AND + facet_value BETWEEN $swlat AND $nelat AND ($compare_lng)"; + + return $wpdb->get_col( $sql ); + } + + + /** + * Output any front-end scripts + */ + function front_scripts() { + + add_filter( 'facetwp_load_gmaps', '__return_true', 4 ); + + FWP()->display->assets['oms'] = [ FACETWP_URL . '/assets/js/src/oms.min.js', FACETWP_MAP_FACET_VERSION ]; + + if ( 'yes' == $this->map_facet['cluster'] ) { + FWP()->display->assets['markerclusterer'] = [ 'https://unpkg.com/@googlemaps/markerclusterer/dist/index.min.js', FACETWP_MAP_FACET_VERSION ]; + } + + FWP()->display->assets['facetwp-map'] = [ FACETWP_URL . '/assets/js/src/map.js', FACETWP_MAP_FACET_VERSION ]; + + $btn_label = ( isset( $this->map_facet['btn_label'] ) && '' != $this->map_facet['btn_label'] ) ? $this->map_facet['btn_label'] : __( 'Enable map filtering', 'fwp-front' ) ; + FWP()->display->json['map']['filterText'] = facetwp_i18n( $btn_label ); + + $reset_label = ( isset( $this->map_facet['reset_label'] ) && '' != $this->map_facet['reset_label'] ) ? $this->map_facet['reset_label'] : __( 'Reset', 'fwp-front'); + FWP()->display->json['map']['resetText'] = facetwp_i18n( $reset_label ); + FWP()->display->json['map']['facet_name'] = $this->map_facet['name']; + FWP()->display->json['map']['ajaxurl'] = admin_url( 'admin-ajax.php' ); + } + + + function register_fields() { + return [ + 'longitude' => [ + 'type' => 'alias', + 'items' => [ + 'source_other' => [ + 'label' => __( 'Longitude', 'fwp' ), + 'notes' => __( '(Optional) use a separate longitude field.', 'fwp' ), + 'html' => '' + ] + ] + ], + 'mapid' => [ + 'label' => __( 'Map ID', 'fwp' ), + 'placeholder' => 'DEMO_MAP_ID', + 'notes' => __( 'In the Google Cloud Console\'s Map Management section, create a Map ID and enter it here. Choose a map rendering type (raster or vector) in the Map ID settings. To change your map styling, create a Map Style in the Map Styles section and attach it to the Map ID.', 'fwp' ) + ], + 'map_design' => [ + 'label' => __( 'Map design', 'fwp' ), + 'notes' => __( 'In the Google Cloud Console\'s Map Management section, create a Map ID and enter it in the above Map ID setting. Then create a Map Style in the Map Styles section and attach it to the Map ID.', 'fwp' ), + 'html' => 'This setting has been replaced with
    + cloud-based map styling. + ' + ], + 'btn_label' => [ + 'label' => __( 'Enable filtering button label', 'fwp' ), + 'placeholder' => facetwp_i18n( __( 'Enable map filtering', 'fwp' ) ) + ], + 'reset_label' => [ + 'label' => __( 'Reset button label', 'fwp', 'fwp' ), + 'placeholder' => facetwp_i18n( __( 'Reset', 'fwp' ) ) + ], + 'cluster' => [ + 'type' => 'toggle', + 'label' => __( 'Marker clustering', 'fwp' ), + 'notes' => __( 'Group markers into clusters?', 'fwp' ) + ], + 'limit' => [ + 'type' => 'select', + 'label' => __( 'Marker limit', 'fwp' ), + 'choices' => [ + 'all' => __( 'Show all results', 'fwp' ), + 'paged' => __( 'Show current page results', 'fwp' ) + ] + ], + 'map_width' => [ + 'type' => 'text', + 'html' => '', + 'label' => __( 'Map width / height', 'fwp' ), + 'notes' => __( 'Set width and height of the map. Without units, px is assumed. Use other CSS units if needed, e.g. 100% for responsive full width of the parent container. Don\'t use 100% for the height if the map\'s container does not have a fixed height, else the map will have no height and will not show.', 'fwp' ) + ], + 'map_height' => [ + 'show' => '0==1' + ], + 'min_zoom' => [ + 'type' => 'text', + 'html' => ' + ', + 'label' => __( 'Zoom min / max', 'fwp' ), + 'notes' => __( 'Set zoom bounds (between 1 and 20).', 'fwp' ) + ], + 'max_zoom' => [ + 'show' => '0==1' + ], + 'default_lat' => [ + 'type' => 'text', + 'html' => ' + + ', + 'label' => __( 'Fallback lat / lng / zoom', 'fwp' ), + 'notes' => __( 'Center the map here, and set a custom zoom level, if there are no results.', 'fwp' ) + ], + 'default_lng' => [ + 'show' => '0==1' + ], + 'default_zoom' => [ + 'show' => '0==1' + ], + 'ajax_markers' => [ + 'type' => 'toggle', + 'label' => __( 'Info window ajax loading', 'fwp' ), + 'notes' => __( 'Dynamically load marker info window content on click of markers, which could improve load times for pages with many markers.', 'fwp' ) + ], + 'marker_content' => [ + 'type' => 'textarea', + 'label' => __( 'Info window content', 'fwp' ), + 'html' => ' + +

    ' . __( 'To search your code, click in the editor, then use Ctrl+F (Windows/Linux) or Cmd+F (Mac).', 'fwp' ) . '

    + ' + ], + ]; + } + + + /** + * Index the coordinates + * We expect a comma-separated "latitude, longitude" + */ + function index_latlng( $params, $class ) { + + $facet = FWP()->helper->get_facet_by_name( $params['facet_name'] ); + + if ( false !== $facet && 'map' == $facet['type'] ) { + $latlng = $params['facet_value']; + + // Only handle "lat, lng" strings + if ( is_string( $latlng ) ) { + $latlng = preg_replace( '/[^0-9.,-]/', '', $latlng ); + + if ( ! empty( $facet['source_other'] ) ) { + $other_params = $params; + $other_params['facet_source'] = $facet['source_other']; + $rows = $class->get_row_data( $other_params ); + + if ( false === strpos( $latlng, ',' ) && ! empty( $rows ) ) { + $lng = $rows[0]['facet_display_value']; + $lng = preg_replace( '/[^0-9.,-]/', '', $lng ); + $latlng .= ',' . $lng; + } + } + + if ( preg_match( "/^([\d.-]+),([\d.-]+)$/", $latlng ) ) { + $latlng = explode( ',', $latlng ); + $params['facet_value'] = $latlng[0]; + $params['facet_display_value'] = $latlng[1]; + } + + /** make sure lat and lng are valid floats **/ + $params['facet_value'] = $params['facet_value'] == (float)$params['facet_value'] ? (float)$params['facet_value'] : ''; + $params['facet_display_value'] = $params['facet_display_value'] == (float)$params['facet_display_value'] ? (float)$params['facet_display_value'] : ''; + + /** check for valid range of lat and lng */ + if ( '' == $params['facet_value'] || '' == $params['facet_display_value'] || 90 < abs( $params['facet_value'] ) || 180 < abs( $params['facet_display_value'] ) ) { + $params['facet_value'] = ''; // don't index + } + } + } + + return $params; + } +} diff --git a/wp-content/plugins/facetwp/includes/facets/number_range.php b/wp-content/plugins/facetwp/includes/facets/number_range.php new file mode 100644 index 000000000..889c22338 --- /dev/null +++ b/wp-content/plugins/facetwp/includes/facets/number_range.php @@ -0,0 +1,137 @@ +label = __( 'Number Range', 'fwp' ); + $this->fields = [ 'source_other', 'compare_type', 'number_fields' ]; + } + + + /** + * Generate the facet HTML + */ + function render( $params ) { + + $output = ''; + $value = $params['selected_values']; + $value = empty( $value ) ? [ '', '', ] : $value; + $fields = empty( $params['facet']['fields'] ) ? 'both' : $params['facet']['fields']; + + if ( 'exact' == $fields ) { + $output .= ''; + } + if ( 'both' == $fields || 'min' == $fields ) { + $output .= ''; + } + if ( 'both' == $fields || 'max' == $fields ) { + $output .= ''; + } + + $output .= ''; + + return $output; + } + + + /** + * Filter the query based on selected values + */ + function filter_posts( $params ) { + global $wpdb; + + $facet = $params['facet']; + $values = $params['selected_values']; + $where = ''; + + $min = ( '' == $values[0] ) ? false : $values[0]; + $max = ( '' == $values[1] ) ? false : $values[1]; + + $fields = $facet['fields'] ?? 'both'; + $compare_type = empty( $facet['compare_type'] ) ? 'basic' : $facet['compare_type']; + $is_dual = ! empty( $facet['source_other'] ); + + if ( $is_dual && 'basic' != $compare_type ) { + if ( 'exact' == $fields ) { + $max = $min; + } + + $min = ( false !== $min ) ? $min : -999999999999; + $max = ( false !== $max ) ? $max : 999999999999; + + /** + * Enclose compare + * The post's range must surround the user-defined range + */ + if ( 'enclose' == $compare_type ) { + $where .= " AND (facet_value + 0) <= '$min'"; + $where .= " AND (facet_display_value + 0) >= '$max'"; + } + + /** + * Intersect compare + * @link http://stackoverflow.com/a/325964 + */ + if ( 'intersect' == $compare_type ) { + $where .= " AND (facet_value + 0) <= '$max'"; + $where .= " AND (facet_display_value + 0) >= '$min'"; + } + } + + /** + * Basic compare + * The user-defined range must surround the post's range + */ + else { + if ( 'exact' == $fields ) { + $max = $min; + } + if ( false !== $min ) { + $where .= " AND (facet_value + 0) >= '$min'"; + } + if ( false !== $max ) { + $where .= " AND (facet_display_value + 0) <= '$max'"; + } + } + + $sql = " + SELECT DISTINCT post_id FROM {$wpdb->prefix}facetwp_index + WHERE facet_name = '{$facet['name']}' $where"; + return facetwp_sql( $sql, $facet ); + } + + + function register_fields() { + return [ + 'number_fields' => [ + 'type' => 'alias', + 'items' => [ + 'fields' => [ + 'type' => 'select', + 'label' => __( 'Fields to show', 'fwp' ), + 'choices' => [ + 'both' => __( 'Min + Max', 'fwp' ), + 'exact' => __( 'Exact', 'fwp' ), + 'min' => __( 'Min', 'fwp' ), + 'max' => __( 'Max', 'fwp' ) + ] + ] + ] + ] + ]; + } + + + /** + * (Front-end) Attach settings to the AJAX response + */ + function settings_js( $params ) { + $facet = $params['facet']; + $fields = empty( $facet['fields'] ) ? 'both' : $facet['fields']; + + return [ + 'fields' => $fields + ]; + } +} diff --git a/wp-content/plugins/facetwp/includes/facets/pager.php b/wp-content/plugins/facetwp/includes/facets/pager.php new file mode 100644 index 000000000..b0c663832 --- /dev/null +++ b/wp-content/plugins/facetwp/includes/facets/pager.php @@ -0,0 +1,297 @@ +label = __( 'Pager', 'fwp' ); + $this->fields = [ 'pager_type', 'inner_size', 'dots_label', 'prev_label', 'next_label', + 'count_text_plural', 'count_text_singular', 'count_text_none', 'scroll_target', 'scroll_offset', + 'load_more_text', 'loading_text', 'default_label', 'per_page_options' + ]; + } + + + /** + * Generate the facet HTML + */ + function render( $params ) { + $facet = $params['facet']; + $pager_type = $facet['pager_type']; + $this->pager_args = FWP()->facet->pager_args; + + $method = 'render_' . $pager_type; + if ( method_exists( $this, $method ) ) { + return $this->$method( $facet ); + } + } + + + function render_numbers( $facet ) { + $inner_size = (int) $facet['inner_size']; + $dots_label = facetwp_i18n( $facet['dots_label'] ); + $prev_label = facetwp_i18n( $facet['prev_label'] ); + $next_label = facetwp_i18n( $facet['next_label'] ); + + $output = ''; + $page = (int) $this->pager_args['page']; + $total_pages = (int) $this->pager_args['total_pages']; + $inner_first = max( $page - $inner_size, 2 ); + $inner_last = min( $page + $inner_size, $total_pages - 1 ); + + if ( 1 < $total_pages ) { + + // Prev button + if ( 1 < $page && '' != $prev_label ) { + $output .= $this->render_page( $page - 1, $prev_label, 'prev' ); + } + + // First page + $output .= $this->render_page( 1, false, 'first' ); + + // Dots + if ( 2 < $inner_first && '' != $dots_label ) { + $output .= $this->render_page( '', $dots_label, 'dots' ); + } + + for ( $i = $inner_first; $i <= $inner_last; $i++ ) { + $output .= $this->render_page( $i ); + } + + // Dots + if ( $inner_last < $total_pages - 1 && '' != $dots_label ) { + $output .= $this->render_page( '', $dots_label, 'dots' ); + } + + // Last page + $output .= $this->render_page( $total_pages, false, 'last' ); + + // Next button + if ( $page < $total_pages && '' != $next_label ) { + $output .= $this->render_page( $page + 1, $next_label, 'next' ); + } + } + + return '
    ' . $output . '
    '; + } + + + function render_page( $page, $label = false, $extra_class = false ) { + $label = ( false === $label ) ? $page : $label; + $class = 'facetwp-page'; + + if ( ! empty( $extra_class ) ) { + $class .= ' ' . $extra_class; + } + + if ( $page == $this->pager_args['page'] ) { + $class .= ' active'; + } + + $data = empty( $page ) ? '' : ' data-page="' . $page . '"'; + $html = '' . $label . ''; + + return apply_filters( 'facetwp_facet_pager_link', $html, [ + 'page' => $page, + 'label' => $label, + 'extra_class' => $extra_class + ]); + } + + + function render_counts( $facet ) { + $text_singular = facetwp_i18n( $facet['count_text_singular'] ); + $text_plural = facetwp_i18n( $facet['count_text_plural'] ); + $text_none = facetwp_i18n( $facet['count_text_none'] ); + + $page = $this->pager_args['page']; + $per_page = $this->pager_args['per_page']; + $total_rows = $this->pager_args['total_rows']; + $total_rows_unfiltered = $this->pager_args['total_rows_unfiltered']; + $total_pages = $this->pager_args['total_pages']; + + if ( -1 == $per_page ) { + $per_page = $total_rows; + } + + if ( 1 < $total_rows ) { + $lower = ( 1 + ( ( $page - 1 ) * $per_page ) ); + $upper = ( $page * $per_page ); + $upper = ( $total_rows < $upper ) ? $total_rows : $upper; + + // If a load_more pager is in use, force $lower = 1 + if ( FWP()->helper->facet_setting_exists( 'pager_type', 'load_more' ) ) { + $lower = 1; + } + + $output = $text_plural; + $output = str_replace( '[lower]', $lower, $output ); + $output = str_replace( '[upper]', $upper, $output ); + $output = str_replace( '[total]', $total_rows, $output ); + $output = str_replace( '[total_unfiltered]', $total_rows_unfiltered, $output ); + $output = str_replace( '[page]', $page, $output ); + $output = str_replace( '[per_page]', $per_page, $output ); + $output = str_replace( '[total_pages]', $total_pages, $output ); + } + else { + $output = ( 0 < $total_rows ) ? $text_singular : $text_none; + } + + return $output; + } + + + function render_load_more( $facet ) { + $text = facetwp_i18n( $facet['load_more_text'] ); + $loading_text = facetwp_i18n( $facet['loading_text'] ); + + return ''; + } + + + function render_per_page( $facet ) { + $default = facetwp_i18n( $facet['default_label'] ); + $options = explode( ',', $facet['per_page_options'] ); + $options = array_map( 'trim', $options ); + $output = ''; + + if ( ! empty( $default ) ) { + $output .= ''; + } + + $per_page = $this->pager_args['per_page']; + $var_exists = isset( FWP()->request->url_vars['per_page'] ); + + foreach ( $options as $option ) { + $val = $label = $option; + + // Support "All" option + if ( ! ctype_digit( $val ) ) { + $val = -1; + $label = facetwp_i18n( $label ); + } + + $selected = ( $var_exists && $val == $per_page ) ? ' selected' : ''; + $output .= ''; + } + + return ''; + } + + + /** + * Filter the query based on selected values + */ + function filter_posts( $params ) { + return 'continue'; + } + + + /** + * (Front-end) Attach settings to the AJAX response + */ + function settings_js( $params ) { + $facet = $params['facet']; + $settings[ 'pager_type' ] = $facet['pager_type']; + + if ( 'numbers' == $facet['pager_type'] ) { + $settings[ 'scroll_target' ] = $facet['scroll_target'] ?? ''; + $settings[ 'scroll_offset' ] = (int) ( $facet['scroll_offset'] ?? '' ); + } + + return $settings; + } + + + function register_fields() { + return [ + 'pager_type' => [ + 'type' => 'select', + 'label' => __( 'Pager type', 'fwp' ), + 'choices' => [ + 'numbers' => __( 'Page numbers', 'fwp' ), + 'counts' => __( 'Result counts', 'fwp' ), + 'load_more' => __( 'Load more', 'fwp' ), + 'per_page' => __( 'Per page', 'fwp' ) + ] + ], + 'inner_size' => [ + 'label' => __( 'Inner size', 'fwp' ), + 'notes' => 'Number of pages to show on each side of the current page', + 'default' => 2, + 'show' => "facet.pager_type == 'numbers'" + ], + 'dots_label' => [ + 'label' => __( 'Dots label', 'fwp' ), + 'notes' => 'The filler between the inner and outer pages', + 'default' => '…', + 'show' => "facet.pager_type == 'numbers'" + ], + 'prev_label' => [ + 'label' => __( 'Prev button label', 'fwp' ), + 'notes' => 'Leave blank to hide', + 'default' => '« Prev', + 'show' => "facet.pager_type == 'numbers'" + ], + 'next_label' => [ + 'label' => __( 'Next button label', 'fwp' ), + 'notes' => 'Leave blank to hide', + 'default' => 'Next »', + 'show' => "facet.pager_type == 'numbers'" + ], + 'scroll_target' => [ + 'label' => __( 'Scroll target', 'fwp' ), + 'notes' => 'Add a class or ID to target for scroll up on paging. Use "body" for top of page or ".facetwp-template" for top of results. Leave blank for no scrolling.', + 'default' => '', + 'placeholder' => 'e.g. .facetwp-template', + 'show' => "facet.pager_type == 'numbers'" + ], + 'scroll_offset' => [ + 'label' => __( 'Scroll offset', 'fwp' ), + 'notes' => 'Number of px to modify scroll target position, for example 100 will be 100px below target, -100 will be 100px above target', + 'default' => '', + 'show' => "facet.scroll_target != '' && facet.pager_type == 'numbers'" + ], + 'count_text_plural' => [ + 'label' => __( 'Count text (plural)', 'fwp' ), + 'notes' => 'Available tags: [lower], [upper], [total], [page], [per_page], [total_pages]', + 'default' => '[lower] - [upper] of [total] results', + 'show' => "facet.pager_type == 'counts'" + ], + 'count_text_singular' => [ + 'label' => __( 'Count text (singular)', 'fwp' ), + 'default' => '1 result', + 'show' => "facet.pager_type == 'counts'" + ], + 'count_text_none' => [ + 'label' => __( 'Count text (no results)', 'fwp' ), + 'default' => 'No results', + 'show' => "facet.pager_type == 'counts'" + ], + 'load_more_text' => [ + 'label' => __( 'Load more text', 'fwp' ), + 'default' => 'Load more', + 'show' => "facet.pager_type == 'load_more'" + ], + 'loading_text' => [ + 'label' => __( 'Loading text', 'fwp' ), + 'default' => 'Loading...', + 'show' => "facet.pager_type == 'load_more'" + ], + 'default_label' => [ + 'label' => __( 'Default label', 'fwp' ), + 'default' => 'Per page', + 'show' => "facet.pager_type == 'per_page'" + ], + 'per_page_options' => [ + 'label' => __( 'Per page options', 'fwp' ), + 'notes' => 'A comma-separated list of choices. Optionally add a non-numeric choice to be used as a "Show all" option.', + 'default' => '10, 25, 50, 100', + 'show' => "facet.pager_type == 'per_page'" + ] + ]; + } +} diff --git a/wp-content/plugins/facetwp/includes/facets/proximity.php b/wp-content/plugins/facetwp/includes/facets/proximity.php new file mode 100644 index 000000000..5e49bb2af --- /dev/null +++ b/wp-content/plugins/facetwp/includes/facets/proximity.php @@ -0,0 +1,415 @@ + distance */ + public $distance = []; + + /* (array) Associative array containing post_id => [lat, lng] */ + public $post_latlng = []; + + function __construct() { + $this->label = __( 'Proximity', 'fwp' ); + $this->fields = [ 'longitude', 'unit', 'radius_ui', 'radius_options', 'radius_min', 'radius_max', 'radius_default', 'placeholder' ]; + + add_filter( 'facetwp_index_row', [ $this, 'index_latlng' ], 1, 2 ); + add_filter( 'facetwp_sort_options', [ $this, 'sort_options' ], 1, 2 ); + add_filter( 'facetwp_filtered_post_ids', [ $this, 'sort_by_distance' ], 10, 2 ); + add_filter( 'facetwp_render_output', [ $this, 'add_places_version' ], 10, 2 ); + } + + /** + * Get places version from setting, default to legacy version - places-service + * Also can be set with filter + * add_filter( 'facetwp_proximity_places_version', function($version) { return 'place-class'; } ); + * @since 4.4 + */ + function get_places_version() { + $places_version = FWP()->helper->get_setting( 'places_version', 'places-service' ); + return apply_filters( 'facetwp_proximity_places_version', $places_version ); + } + + /** + * Add a places version as FWP.settings.places + * @since 4.4 + */ + function add_places_version( $output, $params ) { + $output['settings']['places'] = $this->get_places_version(); + return $output; + } + + /** + * Generate the facet HTML + */ + function render( $params ) { + + $output = ''; + $facet = $params['facet']; + $value = $params['selected_values']; + $unit = empty( $facet['unit'] ) ? 'mi' : $facet['unit']; + $unit_display = 'km' == $facet['unit'] ? __( 'km', 'fwp-front' ) : ( 'mi' == $facet['unit'] ? __( 'mi', 'fwp-front' ) : $facet['unit'] ); + $placeholder = empty( $facet['placeholder'] ) ? __( 'Enter location', 'fwp-front' ) : $facet['placeholder']; + $placeholder = facetwp_i18n( $placeholder ); + + $lat = empty( $value[0] ) ? '' : $value[0]; + $lng = empty( $value[1] ) ? '' : $value[1]; + $chosen_radius = empty( $value[2] ) ? '' : (float) $value[2]; + $location_name = empty( $value[3] ) ? '' : urldecode( $value[3] ); + + $radius_options = [ 10, 25, 50, 100, 250 ]; + + // Grab the radius UI + $radius_ui = empty( $facet['radius_ui'] ) ? 'dropdown' : $facet['radius_ui']; + + // Grab radius options from the UI + if ( ! empty( $facet['radius_options'] ) ) { + $radius_options = explode( ',', preg_replace( '/\s+/', '', $facet['radius_options'] ) ); + } + + // Grab default radius from the UI + if ( empty( $chosen_radius ) && ! empty( $facet['radius_default'] ) ) { + $chosen_radius = (float) $facet['radius_default']; + } + + // Support dynamic radius + if ( ! empty( $chosen_radius ) && 0 < $chosen_radius ) { + if ( ! in_array( $chosen_radius, $radius_options ) ) { + $radius_options[] = $chosen_radius; + } + } + + $radius_options = apply_filters( 'facetwp_proximity_radius_options', $radius_options ); + + ob_start(); +?> + + + + +
    +
    + + + + + + + +
    + +
    + + +
    +
    + + + + + + + + + +prefix}facetwp_index + WHERE facet_name = '{$facet['name']}'"; + + $results = $wpdb->get_results( $sql ); + + foreach ( $results as $row ) { + $lat2 = (float) $row->lat; + $lng2 = (float) $row->lng; + + if ( ( $lat1 == $lat2 ) && ( $lng1 == $lng2 ) ) { + $dist = 0; + } + else { + $calc = sin( $lat1 * $rad ) * sin( $lat2 * $rad ) + + cos( $lat1 * $rad ) * cos( $lat2 * $rad ) * + cos( $lng2 * $rad - $lng1 * $rad ); + + // acos() must be between -1 and 1 + $dist = acos( max( -1, min( 1, $calc ) ) ) * $earth_radius; + } + + if ( $dist <= $radius ) { + $existing = $this->distance[ $row->post_id ] ?? -1; + + if ( -1 == $existing || $dist < $existing ) { + $this->distance[ $row->post_id ] = $dist; + + if ( apply_filters( 'facetwp_proximity_store_latlng', false ) ) { + $this->post_latlng[ $row->post_id ] = [ $lat2, $lng2 ]; + } + } + } + } + + asort( $this->distance, SORT_NUMERIC ); + + return array_keys( $this->distance ); + } + + + /** + * Output front-end scripts + */ + function front_scripts() { + if ( apply_filters( 'facetwp_proximity_load_js', true ) ) { // back compatibility for filter + + if ( version_compare( FACETWP_MAP_FACET_VERSION, '2.0', '<' ) || 'places-service' == $this->get_places_version() ) { + + // hard-coded + $api_key = defined( 'GMAPS_API_KEY' ) ? GMAPS_API_KEY : ''; + + // admin ui + $tmp_key = FWP()->helper->get_setting( 'gmaps_api_key' ); + $api_key = empty( $tmp_key ) ? $api_key : $tmp_key; + + // hook + $api_key = apply_filters( 'facetwp_gmaps_api_key', $api_key ); + + FWP()->display->assets['gmaps'] = '//maps.googleapis.com/maps/api/js?libraries=places&key=' . trim( esc_attr( $api_key ) ) . '&callback=Function.prototype'; + + } else { + + add_filter( 'facetwp_load_gmaps', '__return_true', 5 ); + + } + } + + // Pass extra options into Places Autocomplete + $options = apply_filters( 'facetwp_proximity_autocomplete_options', [] ); + + if ( 'place-class' == $this->get_places_version() ) { + + // Backward compatibility with documented legacy Places API options that don't exist in Places (New) Autocomplete + if ( isset( $options['componentRestrictions'] ) ) { + if ( isset( $options['componentRestrictions']['country'] ) ) { + $options['includedRegionCodes'] = $options['componentRestrictions']['country']; + } + unset( $options['componentRestrictions'] ); + } + if ( isset( $options['types'] ) ) { + $options['includedPrimaryTypes'] = $options['types']; + unset( $options['types'] ); + } + if ( isset( $options['bounds'] ) ) { + $options['locationBias'] = $options['bounds']; + unset( $options['bounds'] ); + } + + } + + + FWP()->display->json['proximity']['autocomplete_options'] = $options; + FWP()->display->json['proximity']['clearText'] = __( 'Clear location', 'fwp-front' ); + FWP()->display->json['proximity']['queryDelay'] = 250; + FWP()->display->json['proximity']['minLength'] = 3; + } + + + function register_fields() { + return [ + 'longitude' => [ + 'type' => 'alias', + 'items' => [ + 'source_other' => [ + 'label' => __( 'Longitude', 'fwp' ), + 'notes' => '(Optional) use a separate longitude field', + 'html' => '' + ] + ] + ], + 'unit' => [ + 'type' => 'select', + 'label' => __( 'Unit of measurement', 'fwp' ), + 'choices' => [ + 'mi' => __( 'Miles', 'fwp' ), + 'km' => __( 'Kilometers', 'fwp' ) + ] + ], + 'radius_ui' => [ + 'type' => 'select', + 'label' => __( 'Radius UI', 'fwp' ), + 'choices' => [ + 'dropdown' => __( 'Dropdown', 'fwp' ), + 'slider' => __( 'Slider', 'fwp' ), + 'none' => __( 'None', 'fwp' ) + ] + ], + 'radius_options' => [ + 'label' => __( 'Radius options', 'fwp' ), + 'notes' => 'A comma-separated list of radius choices', + 'default' => '10, 25, 50, 100, 250', + 'show' => "facet.radius_ui == 'dropdown'" + ], + 'radius_min' => [ + 'label' => __( 'Range (min)', 'fwp' ), + 'default' => 1, + 'show' => "facet.radius_ui == 'slider'" + ], + 'radius_max' => [ + 'label' => __( 'Range (max)', 'fwp' ), + 'default' => 50, + 'show' => "facet.radius_ui == 'slider'" + ], + 'radius_default' => [ + 'label' => __( 'Default radius', 'fwp' ), + 'default' => 25 + ] + ]; + } + + + /** + * Index the coordinates + * We expect a comma-separated "latitude, longitude" + */ + function index_latlng( $params, $class ) { + + $facet = FWP()->helper->get_facet_by_name( $params['facet_name'] ); + + if ( false !== $facet && 'proximity' == $facet['type'] ) { + $latlng = $params['facet_value']; + + // Only handle "lat, lng" strings + if ( is_string( $latlng ) ) { + $latlng = preg_replace( '/[^0-9.,-]/', '', $latlng ); + + if ( ! empty( $facet['source_other'] ) ) { + $other_params = $params; + $other_params['facet_source'] = $facet['source_other']; + $rows = $class->get_row_data( $other_params ); + + if ( false === strpos( $latlng, ',' ) && ! empty( $rows ) ) { + $lng = $rows[0]['facet_display_value']; + $lng = preg_replace( '/[^0-9.,-]/', '', $lng ); + $latlng .= ',' . $lng; + } + } + + if ( preg_match( "/^([\d.-]+),([\d.-]+)$/", $latlng ) ) { + $latlng = explode( ',', $latlng ); + $params['facet_value'] = $latlng[0]; + $params['facet_display_value'] = $latlng[1]; + } + + /** make sure lat and lng are valid floats **/ + $params['facet_value'] = $params['facet_value'] == (float)$params['facet_value'] ? (float)$params['facet_value'] : ''; + $params['facet_display_value'] = $params['facet_display_value'] == (float)$params['facet_display_value'] ? (float)$params['facet_display_value'] : ''; + + /** check for valid range of lat and lng */ + if ( '' == $params['facet_value'] || '' == $params['facet_display_value'] || 90 < abs( $params['facet_value'] ) || 180 < abs( $params['facet_display_value'] ) ) { + $params['facet_value'] = ''; // don't index + } + } + } + + return $params; + } + + + /** + * Add "Distance" to the sort box + */ + function sort_options( $options, $params ) { + + if ( FWP()->helper->facet_setting_exists( 'type', 'proximity' ) ) { + $options['distance'] = [ + 'label' => __( 'Distance', 'fwp-front' ), + 'query_args' => [ + 'orderby' => 'post__in', + 'order' => 'ASC', + ], + ]; + } + + return $options; + } + + + /** + * Sort the final (filtered) post IDs by distance + */ + function sort_by_distance( $post_ids, $class ) { + + $distance = FWP()->helper->facet_types['proximity']->distance; + + if ( ! empty( $distance ) ) { + $ordered_posts = array_keys( $distance ); + $filtered_posts = array_flip( $post_ids ); + $intersected_ids = []; + + foreach ( $ordered_posts as $p ) { + if ( isset( $filtered_posts[ $p ] ) ) { + $intersected_ids[] = $p; + } + } + + $post_ids = $intersected_ids; + } + + return $post_ids; + } +} + + +/** + * Get a post's distance + */ +function facetwp_get_distance( $post_id = false ) { + global $post; + + // Get the post ID + $post_id = ( false === $post_id ) ? $post->ID : $post_id; + + // Get the proximity class + $facet_type = FWP()->helper->facet_types['proximity']; + + // Get the distance + $distance = $facet_type->distance[ $post_id ] ?? -1; + + if ( -1 < $distance ) { + return apply_filters( 'facetwp_proximity_distance_output', $distance ); + } + + return false; +} diff --git a/wp-content/plugins/facetwp/includes/facets/radio.php b/wp-content/plugins/facetwp/includes/facets/radio.php new file mode 100644 index 000000000..9407ecb68 --- /dev/null +++ b/wp-content/plugins/facetwp/includes/facets/radio.php @@ -0,0 +1,64 @@ +label = __( 'Radio', 'fwp' ); + $this->fields = [ 'label_any', 'parent_term', 'modifiers', 'ghosts', 'orderby', 'count' ]; + } + + + /** + * Load the available choices + */ + function load_values( $params ) { + $params['facet']['operator'] = 'or'; + return FWP()->helper->facet_types['checkboxes']->load_values( $params ); + } + + + /** + * Generate the facet HTML + */ + function render( $params ) { + + $output = ''; + $facet = $params['facet']; + $values = (array) $params['values']; + $selected_values = (array) $params['selected_values']; + $label_any = empty( $facet['label_any'] ) ? false : facetwp_i18n( $facet['label_any'] ); + + if ( $label_any ) { + $selected = empty( $selected_values ) ? ' checked' : ''; + $output .= '
    ' . esc_attr( $label_any ) . '
    '; + } + + foreach ( $values as $row ) { + $label = esc_html( $row['facet_display_value'] ); + $selected = in_array( $row['facet_value'], $selected_values ) ? ' checked' : ''; + $selected .= ( 0 == $row['counter'] && '' == $selected ) ? ' disabled' : ''; + $output .= '
    '; + $output .= ''; + $output .= apply_filters( 'facetwp_facet_display_value', $label, [ + 'selected' => ( '' !== $selected ), + 'facet' => $facet, + 'row' => $row + ]); + $output .= ''; + $output .= '(' . $row['counter'] . ')'; + $output .= '
    '; + } + + return $output; + } + + + /** + * Filter the query based on selected values + */ + function filter_posts( $params ) { + $params['facet']['operator'] = 'or'; + return FWP()->helper->facet_types['checkboxes']->filter_posts( $params ); + } +} diff --git a/wp-content/plugins/facetwp/includes/facets/rating.php b/wp-content/plugins/facetwp/includes/facets/rating.php new file mode 100644 index 000000000..481f6b16f --- /dev/null +++ b/wp-content/plugins/facetwp/includes/facets/rating.php @@ -0,0 +1,191 @@ +label = __( 'Star Rating', 'fwp' ); + $this->fields = [ 'ratings_icon', 'ghost_ratings', 'color', 'color_selected', 'color_undo', 'color_ghosts' ]; + } + + + /** + * Load the available choices + */ + function load_values( $params ) { + global $wpdb; + + $facet = $params['facet']; + $from_clause = $wpdb->prefix . 'facetwp_index f'; + + // Facet in "OR" mode + $where_clause = $this->get_where_clause( $facet ); + + $output = [ + 1 => [ 'counter' => 0 ], + 2 => [ 'counter' => 0 ], + 3 => [ 'counter' => 0 ], + 4 => [ 'counter' => 0 ], + 5 => [ 'counter' => 0 ] + ]; + + $sql = " + SELECT COUNT(*) AS `count`, FLOOR(f.facet_value) AS `rating` + FROM $from_clause + WHERE f.facet_name = '{$facet['name']}' AND FLOOR(f.facet_value) >= 1 $where_clause + GROUP BY rating"; + + $results = $wpdb->get_results( $sql ); + + foreach ( $results as $result ) { + $output[ $result->rating ]['counter'] = $result->count; + } + + $total = 0; + + // The lower rating should include higher rating counts + for ( $i = 5; $i > 0; $i-- ) { + $output[ $i ]['counter'] += $total; + $total = $output[ $i ]['counter']; + } + + return $output; + } + + + /** + * Generate the facet HTML + */ + function render( $params ) { + + $output = ''; + $facet = $params['facet']; + $values = (array) $params['values']; + $selected_values = (array) $params['selected_values']; + $show_ghosts = FWP()->helper->facet_is( $facet, 'ghost_ratings', 'yes' ); + $ratings_icon = ( isset( $facet[ 'ratings_icon' ] ) && '' != $facet[ 'ratings_icon' ] ) ? $facet[ 'ratings_icon' ] : '★'; + + $num_stars = 0; + foreach ( $values as $val ) { + if ( 0 < $val['counter'] ) { + $num_stars++; + } + } + + $num_stars = $show_ghosts ? 5 : $num_stars; + + if ( 0 < $num_stars ) { + $output .= ''; + + for ( $i = $num_stars; $i >= 1; $i-- ) { + $class = in_array( $i, $selected_values ) ? ' selected' : ''; + $is_disabled = ! ( 0 < $values[ $i ]['counter'] ) ? true : false; + $class = $is_disabled ? $class . ' disabled' : $class; + $output .= '' . apply_filters( 'facetwp_ratings_icon', $ratings_icon, $is_disabled, $facet ) . ''; + } + + $output .= ''; + $output .= ' '; + $output .= ' '; + } + + return $output; + } + + + /** + * Filter the query based on selected values + */ + function filter_posts( $params ) { + global $wpdb; + + $facet = $params['facet']; + $selected_values = $params['selected_values']; + $selected_values = is_array( $selected_values ) ? $selected_values[0] : $selected_values; + + $sql = " + SELECT DISTINCT post_id FROM {$wpdb->prefix}facetwp_index + WHERE facet_name = '{$facet['name']}' AND facet_value >= '$selected_values'"; + return $wpdb->get_col( $sql ); + } + + + function register_fields() { + + return [ + 'ratings_icon' => [ + 'type' => 'select', + 'label' => __( 'Rating icon', 'fwp' ), + 'notes' => 'Select icon for ratings.', + 'choices' => [ + '★' => __( 'Stars', 'fwp' ) . ' ★★★★★', + '☆' => __( 'Star Outlines', 'fwp' ) . ' ☆☆☆☆☆', + '♥' => __( 'Hearts', 'fwp' ) . ' ♥♥♥♥♥', + ] + ], + 'ghost_ratings' => [ + 'type' => 'toggle', + 'label' => __( 'Show ghost ratings', 'fwp' ), + 'notes' => 'Always show 5 icons even when there are no matches.' + ], + 'color' => [ + 'type' => 'color-picker', + 'label' => __( 'Color', 'fwp' ), + 'notes' => 'Set icon color.', + 'html' => '', + 'default' => '#cccccc' + ], + 'color_selected' => [ + 'type' => 'color-picker', + 'label' => __( 'Selected color', 'fwp' ), + 'notes' => 'Set icon hover and selected color.', + 'html' => '', + ], + 'color_undo' => [ + 'type' => 'color-picker', + 'label' => __( 'Undo color', 'fwp' ), + 'notes' => 'Set icon undo color.', + 'html' => '', + ], + 'color_ghosts' => [ + 'type' => 'color-picker', + 'label' => __( 'Ghost color', 'fwp' ), + 'notes' => 'Set icon ghost color.', + 'html' => '', + 'show' => "facet.ghost_ratings != 'no'" + ] + ]; + } + + + /** + * Output front-end scripts + */ + function front_scripts() { + FWP()->display->json['rating']['& up'] = facetwp_i18n( __( '& up', 'fwp-front' ) ); + FWP()->display->json['rating']['Undo'] = facetwp_i18n( __( 'Undo', 'fwp-front' ) ); + + $facets = FWP()->helper->get_facets_by( 'type', 'rating' ); + + $styles = ''; + + foreach ( $facets AS $facet ) { + + $color = ( isset( $facet[ 'color' ] ) ) ? $facet[ 'color' ] : '#cccccc'; + $selected = ( isset( $facet[ 'color_selected' ] ) ) ? $facet[ 'color_selected' ] : '#000000'; + $undo = ( isset( $facet[ 'color_undo' ] ) ) ? $facet[ 'color_undo' ] : '#ff0000'; + $ghosts = ( isset( $facet[ 'color_ghosts' ] ) ) ? $facet[ 'color_ghosts' ] : '#eeeeee'; + + $styles .= ' + .facetwp-facet-' . $facet['name'] . ' .facetwp-star { color: ' . esc_attr( $color ) . ' } + .facetwp-facet-' . $facet['name'] . ' .facetwp-star:not(.disabled):hover, .facetwp-star:not(.disabled):hover ~ .facetwp-star, .facetwp-star.selected, .facetwp-star.selected ~ .facetwp-star { color: ' . esc_attr( $selected ) . '; } + .facetwp-facet-' . $facet['name'] . ' .facetwp-star.selected:hover, .facetwp-star.selected:hover ~ .facetwp-star { color: ' . esc_attr( $undo ) . '; } + .facetwp-facet-' . $facet['name'] . ' .facetwp-star.disabled, .facetwp-facet-' . $facet['name'] . ' .facetwp-star.disabled:hover { color: ' . esc_attr( $ghosts ) . '; } + '; + } + + if ( !empty( $styles ) ) { + echo ''; + } + } +} diff --git a/wp-content/plugins/facetwp/includes/facets/reset.php b/wp-content/plugins/facetwp/includes/facets/reset.php new file mode 100644 index 000000000..75048d85c --- /dev/null +++ b/wp-content/plugins/facetwp/includes/facets/reset.php @@ -0,0 +1,85 @@ +label = __( 'Reset', 'fwp' ); + $this->fields = [ 'reset_ui', 'reset_text', 'reset_mode', 'reset_facets', 'auto_hide' ]; + } + + + function render( $params ) { + $facet = $params['facet']; + $reset_ui = $facet['reset_ui']; + $reset_text = empty( $facet['reset_text'] ) ? __( 'Reset', 'fwp-front' ) : $facet['reset_text']; + $reset_text = facetwp_i18n( $reset_text ); + + $classes = [ 'facetwp-reset' ]; + $attrs = ''; + + if ( ! FWP()->helper->facet_is( $facet, 'reset_mode', 'off' ) ) { + if ( ! empty( $facet['reset_facets'] ) ) { + $vals = implode( ',', $facet['reset_facets'] ); + $attrs = ' data-mode="{mode}" data-values="{vals}"'; + $attrs = str_replace( '{mode}', $facet['reset_mode'], $attrs ); + $attrs = str_replace( '{vals}', esc_attr( $vals ), $attrs ); + } + } + + if ( FWP()->helper->facet_is( $facet, 'auto_hide', 'yes' ) ) { + $classes[] = 'facetwp-hide-empty'; + } + + if ( 'button' == $reset_ui ) { + $output = ''; + } + else { + $output = '{label}'; + } + + $output = str_replace( '{classes}', implode( ' ', $classes ), $output ); + $output = str_replace( '{label}', esc_attr( $reset_text ), $output ); + $output = str_replace( '{attrs}', $attrs, $output ); + return $output; + } + + + function filter_posts( $params ) { + return 'continue'; + } + + + function register_fields() { + return [ + 'reset_ui' => [ + 'type' => 'select', + 'label' => __( 'Reset UI', 'fwp' ), + 'choices' => [ + 'button' => __( 'Button', 'fwp' ), + 'link' => __( 'Link', 'fwp' ) + ] + ], + 'reset_mode' => [ + 'type' => 'select', + 'label' => __( 'Include / exclude', 'fwp' ), + 'notes' => 'Include or exclude certain facets?', + 'choices' => [ + 'off' => __( 'Reset everything', 'fwp' ), + 'include' => __( 'Reset only these facets', 'fwp' ), + 'exclude' => __( 'Reset all except these facets', 'fwp' ) + ] + ], + 'reset_facets' => [ + 'label' => '', + 'html' => '', + 'show' => "facet.reset_mode != 'off'" + ], + 'auto_hide' => [ + 'type' => 'toggle', + 'label' => __( 'Auto-hide', 'fwp' ), + 'notes' => 'Hide when no facets have selected values' + ] + ]; + } +} diff --git a/wp-content/plugins/facetwp/includes/facets/search.php b/wp-content/plugins/facetwp/includes/facets/search.php new file mode 100644 index 000000000..d582f0168 --- /dev/null +++ b/wp-content/plugins/facetwp/includes/facets/search.php @@ -0,0 +1,102 @@ +label = __( 'Search', 'fwp' ); + $this->fields = [ 'search_engine', 'placeholder', 'auto_refresh', 'enable_relevance' ]; + $this->field_defaults = [ 'enable_relevance' => 'yes' ]; + } + + + /** + * Generate the facet HTML + */ + function render( $params ) { + + $output = ''; + $facet = $params['facet']; + $value = (array) $params['selected_values']; + $value = empty( $value ) ? '' : stripslashes( $value[0] ); + $placeholder = empty( $facet['placeholder'] ) ? __( 'Enter keywords', 'fwp-front' ) : $facet['placeholder']; + $placeholder = facetwp_i18n( $placeholder ); + $output .= ''; + $output .= ''; + $output .= ''; + $output .= ''; + return $output; + } + + + /** + * Filter the query based on selected values + */ + function filter_posts( $params ) { + + $facet = $params['facet']; + + if ( 'no' == $facet['enable_relevance'] ) { + add_filter( 'facetwp_use_search_relevancy', '__return_false' ); + } + + $selected_values = $params['selected_values']; + $selected_values = is_array( $selected_values ) ? $selected_values[0] : $selected_values; + + if ( empty( $selected_values ) ) { + return 'continue'; + } + + // Default WP search + $search_args = [ + 's' => $selected_values, + 'posts_per_page' => 200, + 'fields' => 'ids', + ]; + + $search_args = apply_filters( 'facetwp_search_query_args', $search_args, $params ); + + $query = new WP_Query( $search_args ); + + return (array) $query->posts; + } + + + function register_fields() { + $engines = apply_filters( 'facetwp_facet_search_engines', [] ); + $choices = [ '' => __( 'WP Default', 'fwp' ) ]; + + foreach ( $engines as $key => $label ) { + $choices[ $key ] = $label; + } + + return [ + 'search_engine' => [ + 'type' => 'select', + 'label' => __( 'Search engine', 'fwp' ), + 'choices' => $choices + ], + 'auto_refresh' => [ + 'type' => 'toggle', + 'label' => __( 'Auto refresh', 'fwp' ), + 'notes' => 'Automatically refresh the results while typing?' + ], + 'enable_relevance' => [ + 'type' => 'toggle', + 'label' => __( 'Order by relevance', 'fwp' ), + 'notes' => 'By default, search results are ordered by relevance. Disable to use the original listing query order.', + 'default' => 'checked' + ] + ]; + } + + + /** + * (Front-end) Attach settings to the AJAX response + */ + function settings_js( $params ) { + $auto_refresh = empty( $params['facet']['auto_refresh'] ) ? 'no' : $params['facet']['auto_refresh']; + return [ 'auto_refresh' => $auto_refresh ]; + } +} diff --git a/wp-content/plugins/facetwp/includes/facets/slider.php b/wp-content/plugins/facetwp/includes/facets/slider.php new file mode 100644 index 000000000..6b3e4e4c7 --- /dev/null +++ b/wp-content/plugins/facetwp/includes/facets/slider.php @@ -0,0 +1,164 @@ +label = __( 'Slider', 'fwp' ); + $this->fields = [ 'source_other', 'compare_type', 'prefix', 'suffix', + 'reset_text', 'slider_format', 'step' ]; + + add_filter( 'facetwp_render_output', [ $this, 'maybe_prevent_facet_html' ], 10, 2 ); + } + + + /** + * Generate the facet HTML + */ + function render( $params ) { + $facet = $params['facet']; + $reset_text = __( 'Reset', 'fwp-front' ); + + if ( ! empty( $facet['reset_text'] ) ) { + $reset_text = facetwp_i18n( $facet['reset_text'] ); + } + + $output = '
    '; + $output .= '
    '; + $output .= '
    '; + $output .= ''; + $output .= '
    '; + return $output; + } + + + /** + * Filter the query based on selected values + */ + function filter_posts( $params ) { + return FWP()->helper->facet_types['number_range']->filter_posts( $params ); + } + + + /** + * (Front-end) Attach settings to the AJAX response + */ + function settings_js( $params ) { + global $wpdb; + + $facet = $params['facet']; + $where_clause = $this->get_where_clause( $facet ); + $selected_values = $params['selected_values']; + + // Set default slider values + $defaults = [ + 'format' => '', + 'prefix' => '', + 'suffix' => '', + 'step' => 1, + ]; + $facet = array_merge( $defaults, $facet ); + + $sql = " + SELECT MIN(facet_value + 0) AS `min`, MAX(facet_display_value + 0) AS `max` FROM {$wpdb->prefix}facetwp_index + WHERE facet_name = '{$facet['name']}' AND facet_display_value != '' $where_clause"; + $row = $wpdb->get_row( $sql ); + + $range_min = (float) $row->min; + $range_max = (float) $row->max; + + $selected_min = (float) ( $selected_values[0] ?? $range_min ); + $selected_max = (float) ( $selected_values[1] ?? $range_max ); + + return [ + 'range' => [ // outer (bar) + 'min' => min( $range_min, $selected_min ), + 'max' => max( $range_max, $selected_max ) + ], + 'decimal_separator' => FWP()->helper->get_setting( 'decimal_separator' ), + 'thousands_separator' => FWP()->helper->get_setting( 'thousands_separator' ), + 'start' => [ $selected_min, $selected_max ], // inner (handles) + 'format' => $facet['format'], + 'prefix' => facetwp_i18n( $facet['prefix'] ), + 'suffix' => facetwp_i18n( $facet['suffix'] ), + 'step' => $facet['step'], + 'handle_attributes' => [ + [ 'aria-label' => facetwp_i18n('lower') ], + [ 'aria-label' => facetwp_i18n('upper') ] + ] + ]; + } + + + /** + * Prevent the slider HTML from refreshing when active + * @since 3.8.11 + */ + function maybe_prevent_facet_html( $output, $params ) { + if ( ! empty( $output['facets'] && 0 === $params['first_load' ] ) ) { + foreach ( FWP()->facet->facets as $name => $facet ) { + if ( 'slider' == $facet['type'] && ! empty( $facet['selected_values'] ) ) { + unset( $output['facets'][ $name ] ); + } + } + } + return $output; + } + + + /** + * Output any front-end scripts + */ + function front_scripts() { + FWP()->display->assets['nouislider.css'] = FACETWP_URL . '/assets/vendor/noUiSlider/nouislider.css'; + FWP()->display->assets['nouislider.js'] = FACETWP_URL . '/assets/vendor/noUiSlider/nouislider.min.js'; + FWP()->display->assets['nummy.js'] = FACETWP_URL . '/assets/vendor/nummy/nummy.min.js'; + } + + + function register_fields() { + $thousands = FWP()->helper->get_setting( 'thousands_separator' ); + $decimal = FWP()->helper->get_setting( 'decimal_separator' ); + $choices = []; + + if ( '' != $thousands ) { + $choices['0,0'] = "5{$thousands}280"; + $choices['0,0.0'] = "5{$thousands}280{$decimal}4"; + $choices['0,0.00'] = "5{$thousands}280{$decimal}42"; + } + + $choices['0'] = '5280'; + $choices['0.0'] = "5280{$decimal}4"; + $choices['0.00'] = "5280{$decimal}42"; + $choices['0a'] = '5k'; + $choices['0.0a'] = "5{$decimal}3k"; + $choices['0.00a'] = "5{$decimal}28k"; + + return [ + 'prefix' => [ + 'label' => __( 'Prefix', 'fwp' ), + 'notes' => 'Text that appears before each slider value', + ], + 'suffix' => [ + 'label' => __( 'Suffix', 'fwp' ), + 'notes' => 'Text that appears after each slider value', + ], + 'slider_format' => [ + 'type' => 'alias', + 'items' => [ + 'format' => [ + 'type' => 'select', + 'label' => __( 'Format', 'fwp' ), + 'notes' => 'If the number separators are wrong, change the [Separators] setting in the Settings tab, then save and reload the page', + 'choices' => $choices + ] + ] + ], + 'step' => [ + 'label' => __( 'Step', 'fwp' ), + 'notes' => 'The amount of increase between intervals', + 'default' => 1 + ] + ]; + } +} diff --git a/wp-content/plugins/facetwp/includes/facets/sort.php b/wp-content/plugins/facetwp/includes/facets/sort.php new file mode 100644 index 000000000..6c56c98e0 --- /dev/null +++ b/wp-content/plugins/facetwp/includes/facets/sort.php @@ -0,0 +1,226 @@ +label = __( 'Sort', 'fwp' ); + $this->fields = [ 'sort_default_label', 'sort_options' ]; + + add_filter( 'facetwp_filtered_query_args', [ $this, 'apply_sort' ], 1, 2 ); + add_filter( 'facetwp_render_output', [ $this, 'render_sort_feature' ], 1, 2 ); + } + + + /** + * Render the sort facet + */ + function render( $params ) { + $facet = $this->parse_sort_facet( $params['facet'] ); + $selected_values = (array) $params['selected_values']; + + $label = facetwp_i18n( $facet['default_label'] ); + $output = ''; + + foreach ( $facet['sort_options'] as $key => $choice ) { + $label = facetwp_i18n( $choice['label'] ); + $selected = in_array( $key, $selected_values ) ? ' selected' : ''; + $output .= ''; + } + + return ''; + } + + + /** + * Sort facets don't narrow results + */ + function filter_posts( $params ) { + return 'continue'; + } + + + /** + * Register admin settings + */ + function register_fields() { + return [ + 'sort_default_label' => [ + 'type' => 'alias', + 'items' => [ + 'default_label' => [ + 'label' => __( 'Default label', 'fwp' ), + 'notes' => 'The sort box placeholder text', + 'default' => __( 'Sort by', 'fwp' ) + ] + ] + ], + 'sort_options' => [ + 'label' => __( 'Sort options', 'fwp' ), + 'notes' => 'Define the choices that appear in the sort box', + 'html' => '' + ] + ]; + } + + + /** + * Convert a sort facet's sort options into WP_Query arguments + * @since 4.0.8 + */ + function parse_sort_facet( $facet ) { + $sort_options = []; + + foreach ( $facet['sort_options'] as $row ) { + $parsed = FWP()->builder->parse_query_obj([ 'orderby' => $row['orderby'] ]); + + $sort_options[ $row['name'] ] = [ + 'label' => $row['label'], + 'query_args' => array_intersect_key( $parsed, [ + 'meta_query' => true, + 'orderby' => true + ]) + ]; + } + + $sort_options = apply_filters( 'facetwp_facet_sort_options', $sort_options, [ + 'facet' => $facet, + 'template_name' => FWP()->facet->template['name'] + ]); + + $facet['sort_options'] = $sort_options; + + return $facet; + } + + + /** + * Handle both sort facets and the (old) sort feature + * @since 4.0.6 + */ + function apply_sort( $query_args, $class ) { + + foreach ( $class->facets as $facet ) { + if ( 'sort' == $facet['type'] ) { + $sort_facet = $this->parse_sort_facet( $facet ); + break; + } + } + + // Support the (old) sort feature + $sort_value = 'default'; + $this->sort_options = $this->get_sort_options(); + + if ( ! empty( $class->ajax_params['extras']['sort'] ) ) { + $sort_value = $class->ajax_params['extras']['sort']; + + if ( ! empty( $this->sort_options[ $sort_value ] ) ) { + $args = $this->sort_options[ $sort_value ]['query_args']; + $query_args = array_merge( $query_args, $args ); + } + } + + // Preserve relevancy sort + $use_relevancy = apply_filters( 'facetwp_use_search_relevancy', true, $class ); + $is_default_sort = ( 'default' == $sort_value && empty( $class->http_params['get']['orderby'] ) ); + if ( $class->is_search && $use_relevancy && $is_default_sort && FWP()->is_filtered ) { + $query_args['orderby'] = 'post__in'; + } + + // Support the (new) sort facet + if ( ! empty( $sort_facet['selected_values'] ) ) { + $chosen = $sort_facet['selected_values'][0]; + $sort_options = $sort_facet['sort_options']; + + if ( isset( $sort_options[ $chosen ] ) ) { + $qa = $sort_options[ $chosen ]['query_args']; + + if ( isset( $qa['meta_query'] ) ) { + $meta_query = $query_args['meta_query'] ?? []; + $query_args['meta_query'] = array_merge( $meta_query, $qa['meta_query'] ); + } + + $query_args['orderby'] = $qa['orderby']; + } + } + + return $query_args; + } + + + /** + * Generate choices for the (old) sort feature + * @since 4.0.6 + */ + function get_sort_options() { + + $options = [ + 'default' => [ + 'label' => __( 'Sort by', 'fwp-front' ), + 'query_args' => [] + ], + 'title_asc' => [ + 'label' => __( 'Title (A-Z)', 'fwp-front' ), + 'query_args' => [ + 'orderby' => 'title', + 'order' => 'ASC', + ] + ], + 'title_desc' => [ + 'label' => __( 'Title (Z-A)', 'fwp-front' ), + 'query_args' => [ + 'orderby' => 'title', + 'order' => 'DESC', + ] + ], + 'date_desc' => [ + 'label' => __( 'Date (Newest)', 'fwp-front' ), + 'query_args' => [ + 'orderby' => 'date', + 'order' => 'DESC', + ] + ], + 'date_asc' => [ + 'label' => __( 'Date (Oldest)', 'fwp-front' ), + 'query_args' => [ + 'orderby' => 'date', + 'order' => 'ASC', + ] + ] + ]; + + return apply_filters( 'facetwp_sort_options', $options, [ + 'template_name' => FWP()->facet->template['name'], + ] ); + } + + + /** + * Render the (old) sort feature + * @since 4.0.6 + */ + function render_sort_feature( $output, $params ) { + $has_sort = isset( $params['extras']['sort'] ); + $has_choices = isset( $this->sort_options ); + + if ( 0 == $params['soft_refresh'] && $has_sort && $has_choices ) { + $html = ''; + + foreach ( $this->sort_options as $key => $atts ) { + $html .= ''; + } + + $html = ''; + + $output['sort'] = apply_filters( 'facetwp_sort_html', $html, [ + 'sort_options' => $this->sort_options, + 'template_name' => FWP()->facet->template['name'], + ]); + } + + return $output; + } +} diff --git a/wp-content/plugins/facetwp/includes/functions.php b/wp-content/plugins/facetwp/includes/functions.php new file mode 100644 index 000000000..25ea10dc3 --- /dev/null +++ b/wp-content/plugins/facetwp/includes/functions.php @@ -0,0 +1,43 @@ + true ] ); + * + * @since 1.7.5 + */ +function facetwp_display() { + $args = array_replace( [ 'pager', true, [] ], func_get_args() ); + + $atts = (array) $args[2]; + $atts[ $args[0] ] = $args[1]; + + return FWP()->display->shortcode( $atts ); +} + + +/** + * Allow for translation of dynamic strings + * @since 2.1 + */ +function facetwp_i18n( $string ) { + return apply_filters( 'facetwp_i18n', $string ); +} + + +/** + * Support SQL modifications + * @since 2.7 + */ +function facetwp_sql( $sql, $facet ) { + global $wpdb; + + $sql = apply_filters( 'facetwp_wpdb_sql', $sql, $facet ); + return apply_filters( 'facetwp_wpdb_get_col', $wpdb->get_col( $sql ), $sql, $facet ); +} diff --git a/wp-content/plugins/facetwp/includes/integrations/acf/acf.php b/wp-content/plugins/facetwp/includes/integrations/acf/acf.php new file mode 100644 index 000000000..5eba7361d --- /dev/null +++ b/wp-content/plugins/facetwp/includes/integrations/acf/acf.php @@ -0,0 +1,644 @@ +get_fields(); + $choices = []; + + foreach ( $fields as $field ) { + $field_id = $field['hierarchy']; + $field_name = $field['name']; + $field_label = '[' . $field['group_title'] . '] ' . $field['parents'] . $field['label']; + $choices[ "acf/$field_id" ] = $field_label; + + // remove "hidden" ACF fields + unset( $sources['custom_fields']['choices'][ "cf/_$field_name" ] ); + } + + if ( ! empty( $choices ) ) { + $sources['acf'] = [ + 'label' => 'ACF', + 'choices' => $choices, + 'weight' => 5 + ]; + } + + return $sources; + } + + + /** + * If the facet "Sort by" value is "Term order", then preserve + * the custom order of certain ACF fields (checkboxes, radio, etc.) + */ + function facet_orderby( $orderby, $facet ) { + if ( isset( $facet['source'] ) && isset( $facet['orderby'] ) ) { + if ( 0 === strpos( $facet['source'], 'acf/' ) && 'term_order' == $facet['orderby'] ) { + $source_parts = explode( '/', $facet['source'] ); + $field_id = array_pop( $source_parts ); + $field_object = get_field_object( $field_id ); + if ( ! empty( $field_object['choices'] ) ) { + $choices = $field_object['choices']; + $choices = implode( "','", esc_sql( $choices ) ); + $orderby = "FIELD(f.facet_display_value, '$choices')"; + } + } + } + + return $orderby; + } + + + /** + * Index ACF field data + */ + function index_acf_values( $return, $params ) { + $defaults = $params['defaults']; + $facet = $params['facet']; + $post_id = (int) $defaults['post_id']; + $post_type = get_post_type( $post_id ); + + // Index out of stock products? + $index_all = ( 'yes' === FWP()->helper->get_setting( 'wc_index_all', 'no' ) ); + $index_all = apply_filters( 'facetwp_index_all_products', $index_all ); + + if ( function_exists( 'wc_get_product' ) && ( 'product' == $post_type || 'product_variation' == $post_type ) ) { + $product = wc_get_product( $post_id ); + + if ( ! $product || ( ! $index_all && ! $product->is_in_stock() ) ) { + return true; // skip + } + } + + if ( isset( $facet['source'] ) && 'acf/' == substr( $facet['source'], 0, 4 ) ) { + $hierarchy = explode( '/', substr( $facet['source'], 4 ) ); + + // support "User Post Type" plugin + $object_id = apply_filters( 'facetwp_acf_object_id', $defaults['post_id'] ); + + // get values (for sub-fields, use the parent repeater) + $value = get_field( $hierarchy[0], $object_id, false ); + + // prevent null values from being run through format_date() + if ( $value === null ) { + return true; // skip + } + + // handle repeater values + if ( 1 < count( $hierarchy ) ) { + + $parent_field_key = array_shift( $hierarchy ); + $value = $this->process_field_value( $value, $hierarchy, $parent_field_key ); + + // get the sub-field properties + $sub_field = get_field_object( end($hierarchy), $object_id, false, false ); + + foreach ( $value as $key => $val ) { + $this->repeater_row = $key; + $rows = $this->get_values_to_index( $val, $sub_field, $defaults ); + $this->index_field_values( $rows ); + } + } + else { + + // get the field properties + $field = get_field_object( $hierarchy[0], $object_id, false, false ); + + // index values + $rows = $this->get_values_to_index( $value, $field, $defaults ); + $this->index_field_values( $rows ); + } + + return true; + } + + return $return; + } + + + /** + * Hijack the "facetwp_indexer_query_args" hook to lookup the fields once + */ + function lookup_acf_fields( $args ) { + $this->get_fields(); + return $args; + } + + + /** + * Grab all ACF fields + */ + function get_fields() { + + add_action( 'pre_get_posts', [ $this, 'disable_wpml' ] ); + $field_groups = acf_get_field_groups(); + remove_action( 'pre_get_posts', [ $this, 'disable_wpml' ] ); + + foreach ( $field_groups as $field_group ) { + $fields = acf_get_fields( $field_group ); + + if ( ! empty( $fields ) ) { + $this->flatten_fields( $fields, $field_group ); + } + } + + return $this->fields; + } + + + /** + * We need to get field groups in ALL languages + */ + function disable_wpml( $query ) { + $query->set( 'suppress_filters', true ); + $query->set( 'lang', '' ); + } + + + /** + * Extract field values from the repeater array + */ + function process_field_value( $value, $hierarchy, $parent_field_key ) { + $temp_val = []; + + // prevent PHP8 fatal error on invalid lookup field + $parent_field_type = $this->parent_type_lookup[ $parent_field_key ] ?? 'none'; + + if ( ! is_array( $value ) || 'none' == $parent_field_type ) { + return $temp_val; + } + + // reduce the hierarchy array + $field_key = array_shift( $hierarchy ); + + // group + if ( 'group' == $parent_field_type ) { + if ( 0 == count( $hierarchy ) ) { + $temp_val[] = $value[ $field_key ]; + } + else { + return $this->process_field_value( $value[ $field_key ], $hierarchy, $field_key ); + } + } + // repeater + else { + if ( 0 == count( $hierarchy ) ) { + foreach ( $value as $val ) { + $temp_val[] = $val[ $field_key ]; + } + } + else { + foreach ( $value as $outer ) { + if ( isset( $outer[ $field_key ] ) ) { + foreach ( $outer[ $field_key ] as $inner ) { + $temp_val[] = $inner; + } + } + } + + return $this->process_field_value( $temp_val, $hierarchy, $field_key ); + } + } + + return $temp_val; + } + + + /** + * Get an array of $params arrays + * Useful for indexing and grabbing values for the Layout Builder + * @since 3.4.0 + */ + function get_values_to_index( $value, $field, $params ) { + $value = maybe_unserialize( $value ); + $type = $field['type']; + $output = []; + + // checkboxes + if ( 'checkbox' == $type || 'select' == $type || 'radio' == $type || 'button_group' == $type ) { + if ( false !== $value ) { + foreach ( (array) $value as $val ) { + $display_value = isset( $field['choices'][ $val ] ) ? + $field['choices'][ $val ] : + $val; + + $params['facet_value'] = $val; + $params['facet_display_value'] = $display_value; + $output[] = $params; + } + } + } + + // relationship + elseif ( 'relationship' == $type || 'post_object' == $type || 'page_link' == $type ) { + if ( false !== $value ) { + foreach ( (array) $value as $val ) { + + // does the post exist? + if ( false !== get_post_type( $val ) ) { + $params['facet_value'] = $val; + $params['facet_display_value'] = get_the_title( $val ); + $output[] = $params; + } + } + } + } + + // user + elseif ( 'user' == $type ) { + if ( false !== $value ) { + foreach ( (array) $value as $val ) { + $user = get_user_by( 'id', $val ); + + // does the user exist? + if ( false !== $user ) { + $params['facet_value'] = $val; + $params['facet_display_value'] = $user->display_name; + $output[] = $params; + } + } + } + } + + // taxonomy + elseif ( 'taxonomy' == $type ) { + if ( ! empty( $value ) ) { + foreach ( (array) $value as $val ) { + global $wpdb; + + $term_id = (int) $val; + $term = $wpdb->get_row( "SELECT name, slug FROM {$wpdb->terms} WHERE term_id = '$term_id' LIMIT 1" ); + + // does the term exist? + if ( null !== $term ) { + $params['facet_value'] = $term->slug; + $params['facet_display_value'] = $term->name; + $params['term_id'] = $term_id; + $output[] = $params; + } + } + } + } + + // date_picker + elseif ( 'date_picker' == $type ) { + $formatted = $this->format_date( $value ); + $params['facet_value'] = $formatted; + $params['facet_display_value'] = apply_filters( 'facetwp_acf_display_value', $formatted, $params ); + $output[] = $params; + } + + // true_false + elseif ( 'true_false' == $type ) { + + // Optionally index 'false' value as default for unsaved posts (for which $value is int(0)) + $default_false = apply_filters( 'facetwp_index_acf_truefalse_default_false', false ); + + // Skip indexing if no explicit default (true) is set in the field settings, unless enabled with the hook. + if ($value === 0 && !$default_false) { + $value = ''; + } + + $display_value = ( 0 < (int) $value ) ? __( 'Yes', 'fwp-front' ) : __( 'No', 'fwp-front' ); + $params['facet_value'] = $value; + $params['facet_display_value'] = $display_value; + $output[] = $params; + } + + // google_map + elseif ( 'google_map' == $type ) { + if ( isset( $value['lat'] ) && isset( $value['lng'] ) ) { + $params['facet_value'] = $value['lat']; + $params['facet_display_value'] = $value['lng']; + $params['place_details'] = $value; + $output[] = $params; + } + } + + // text + else { + $params['facet_value'] = $value; + $params['facet_display_value'] = apply_filters( 'facetwp_acf_display_value', $value, $params ); + $output[] = $params; + } + + return $output; + } + + + /** + * Index values + */ + function index_field_values( $rows ) { + foreach ( $rows as $params ) { + FWP()->indexer->index_row( $params ); + } + } + + + /** + * Handle "source_other" setting + */ + function index_source_other( $value, $params ) { + if ( ! empty( $params['facet_name'] ) ) { + $facet = FWP()->helper->get_facet_by_name( $params['facet_name'] ); + + if ( ! empty( $facet['source_other'] ) ) { + + if ( 0 === strpos( $facet['source_other'], 'acf/' ) ) { + $hierarchy = explode( '/', substr( $facet['source_other'], 4 ) ); + + // support "User Post Type" plugin + $object_id = apply_filters( 'facetwp_acf_object_id', $params['post_id'] ); + + // get the value + $value = get_field( $hierarchy[0], $object_id, false ); + // handle repeater values + if ( 1 < count( $hierarchy ) ) { + $parent_field_key = array_shift( $hierarchy ); + $value = $this->process_field_value( $value, $hierarchy, $parent_field_key ); + $value = $value[ $this->repeater_row ]; + } + + } else { + + $other_params = $params; + $other_params['facet_source'] = $facet['source_other']; + $rows = FWP()->indexer->get_row_data( $other_params ); + $value = $rows[0]['facet_display_value'] ?? $params['facet_display_value']; + } + } + + if ( 'date_range' == $facet['type'] ) { + + // prevent null values from being run through format_date() + if ( $value === null ) { + return ''; // skip + } + + $value = $this->format_date( $value ); + } + } + + return $value; + } + + /** + * Format dates in YYYY-MM-DD + */ + function format_date( $str ) { + + if ( $str === null ) { + return ''; + } + + if ( 8 == strlen( $str ) && ctype_digit( $str ) ) { + $str = substr( $str, 0, 4 ) . '-' . substr( $str, 4, 2 ) . '-' . substr( $str, 6, 2 ); + } + + return $str; + } + + + /** + * Generates a flat array of fields within a specific field group + */ + function flatten_fields( $fields, $field_group, $hierarchy = '', $parents = '' ) { + if ( !empty( $fields ) ) { + foreach ( $fields as $field ) { + + // append the hierarchy string + $new_hierarchy = $hierarchy . '/' . $field['key']; + + // loop again for repeater or group fields + if ( ( 'repeater' == $field['type'] || 'group' == $field['type'] ) && !empty( $field['sub_fields'] ) ) { + $new_parents = $parents . $field['label'] . ' → '; + + $this->parent_type_lookup[ $field['key'] ] = $field['type']; + $this->flatten_fields( $field['sub_fields'], $field_group, $new_hierarchy, $new_parents ); + } + else { + $this->fields[] = [ + 'key' => $field['key'], + 'name' => $field['name'], + 'label' => $field['label'], + 'hierarchy' => trim( $new_hierarchy, '/' ), + 'parents' => $parents, + 'group_title' => $field_group['title'], + ]; + } + } + } + } + + + /** + * Get the field value (support User Post Type) + * @since 3.4.1 + */ + function get_field( $source, $post_id ) { + $hierarchy = explode( '/', substr( $source, 4 ) ); + $object_id = apply_filters( 'facetwp_acf_object_id', $post_id ); + return get_field( $hierarchy[0], $object_id ); + } + + + /** + * Fallback values for the layout builder + * @since 3.4.0 + * + * ACF return formats: + * [image, file] = array, url, id + * [select, checkbox, radio, button_group] = value, label, array (both) + * [post_object, relationship, taxonomy] = object, id + * [user] = array, object, id + * [link] = array, url + */ + function layout_builder_values( $value, $item ) { + global $post; + + // exit if not an object or array + if ( is_scalar( $value ) || is_null( $value ) ) { + return $value; + } + + $hierarchy = explode( '/', substr( $item['source'], 4 ) ); + + // support "User Post Type" plugin + $object_id = apply_filters( 'facetwp_acf_object_id', $post->ID ); + + // get the field properties + $field = get_field_object( $hierarchy[0], $object_id, false, false ); + + $type = $field['type']; + $format = $field['return_format'] ?? ''; + $is_multiple = (bool) ( $field['multiple'] ?? false ); + + if ( ( 'post_object' == $type || 'relationship' == $type ) && 'object' == $format ) { + $output = []; + + $value = is_array( $value ) ? $value : [ $value ]; + + foreach ( $value as $val ) { + $output[] = '' . esc_html( $val->post_title ) . ''; + } + + $value = $output; + } + + if ( 'taxonomy' == $type && 'object' == $format ) { + $output = []; + + foreach ( $value as $val ) { + $output[] = $val->name; + } + + $value = $output; + } + + if ( ( 'select' == $type || 'checkbox' == $type || 'radio' == $type || 'button_group' == $type ) && 'array' == $format ) { + $value = $value['label'] ?? wp_list_pluck( $value, 'label' ); + } + + if ( ( 'image' == $type || 'gallery' == $type ) && 'array' == $format ) { + $value = ( 'image' == $type ) ? [ $value ] : $value; + + foreach ( $value as $val ) { + $value = '' . esc_attr( $val['alt'] ) . ''; + } + } + + if ( 'file' == $type && 'array' == $format ) { + $value = '' . esc_html( $value['filename'] ) . ' (' . size_format( $value['filesize'], 1 ) . ')'; + } + + if ( 'link' == $type && 'array' == $format ) { + $value = '' . esc_html( $value['title'] ) . ''; + } + + if ( 'google_map' == $type ) { + $value = '' . esc_html( $value['address'] ) . ''; + } + + if ( 'user' == $type && ( 'object' == $format || 'array' == $format ) ) { + $output = []; + + $value = $is_multiple ? $value : [ $value ]; + + foreach ( $value as $val ) { + if ( 'object' == $format ) { + $output[] = $val->display_name; + } + elseif ( 'array' == $format ) { + $output[] = $val['display_name']; + } + } + $value = $output; + } + + return $value; + } + + /** + * Update the index when terms get saved + * @since 4.4 + */ + function edit_term( $term_id, $tt_id, $taxonomy ) { + global $wpdb; + + $term = get_term( $term_id, $taxonomy ); + $slug = FWP()->helper->safe_value( $term->slug ); + $matches = FWP()->helper->get_facets_by_datasource_type( 'acf' ); + + if ( ! empty( $matches ) ) { + + $facet_names = []; + + foreach ( $matches AS $facet ) { + $source_parts = explode( '/', $facet['source'] ); + $field_id = array_pop( $source_parts ); + $field_object = get_field_object( $field_id ); + if ( !empty( $field_object ) && 'taxonomy' == $field_object['type'] ) { + $facet_names[] = $facet['name']; + } + } + + if ( !empty( $facet_names ) ) { + + $facet_names = implode( "','", esc_sql( $facet_names ) ); + + $wpdb->query( $wpdb->prepare( " + UPDATE {$wpdb->prefix}facetwp_index + SET facet_value = %s, facet_display_value = %s + WHERE facet_name IN ('$facet_names') AND term_id = %d", + $slug, $term->name, $term_id + ) ); + + } + } + } + + + /** + * Update the index when terms get deleted + * @since 4.4 + */ + function delete_term( $term_id, $tt_id, $taxonomy ) { + global $wpdb; + + $matches = FWP()->helper->get_facets_by_datasource_type( 'acf' ); + + if ( ! empty( $matches ) ) { + + $facet_names = []; + + foreach ( $matches AS $facet ) { + $source_parts = explode( '/', $facet['source'] ); + $field_id = array_pop( $source_parts ); + $field_object = get_field_object( $field_id ); + if ( !empty( $field_object ) && 'taxonomy' == $field_object['type'] ) { + $facet_names[] = $facet['name']; + } + } + + if ( !empty( $facet_names ) ) { + + $facet_names = implode( "','", esc_sql( $facet_names ) ); + + $wpdb->query( " + DELETE FROM {$wpdb->prefix}facetwp_index + WHERE facet_name IN ('$facet_names') AND term_id = $term_id" + ); + + } + } + } +} + + +if ( function_exists( 'acf' ) && version_compare( acf()->settings['version'], '5.0', '>=' ) ) { + FWP()->acf = new FacetWP_Integration_ACF(); +} diff --git a/wp-content/plugins/facetwp/includes/integrations/edd/edd.js b/wp-content/plugins/facetwp/includes/integrations/edd/edd.js new file mode 100644 index 000000000..f7745467a --- /dev/null +++ b/wp-content/plugins/facetwp/includes/integrations/edd/edd.js @@ -0,0 +1,6 @@ +(function($) { + $().on('facetwp-loaded', function() { + $('.edd-no-js').addClass('facetwp-hidden'); + $('a.edd-add-to-cart').addClass('edd-has-js'); + }); +})(fUtil); \ No newline at end of file diff --git a/wp-content/plugins/facetwp/includes/integrations/edd/edd.php b/wp-content/plugins/facetwp/includes/integrations/edd/edd.php new file mode 100644 index 000000000..77d0caa36 --- /dev/null +++ b/wp-content/plugins/facetwp/includes/integrations/edd/edd.php @@ -0,0 +1,51 @@ + $val ) { + if ( 0 === strpos( $val, '_edd_' ) ) { + unset( $sources['custom_fields']['choices'][ $key ] ); + } + } + + return $sources; + } +} + + +if ( is_plugin_active( 'easy-digital-downloads/easy-digital-downloads.php' ) ) { + new FacetWP_Integration_EDD(); +} diff --git a/wp-content/plugins/facetwp/includes/integrations/searchwp/searchwp.php b/wp-content/plugins/facetwp/includes/integrations/searchwp/searchwp.php new file mode 100644 index 000000000..3fe995fd1 --- /dev/null +++ b/wp-content/plugins/facetwp/includes/integrations/searchwp/searchwp.php @@ -0,0 +1,239 @@ +is_main_query() && $query->is_search() ) { + $args = stripslashes_deep( $this->get_valid_args( $query ) ); + $this->keywords = $args['s']; + $this->swp_query = $this->run_query( $args ); + $query->set( 'using_searchwp', true ); + $query->set( 'searchwp', false ); + } + + return $is_main_query; + } + + + /** + * Whitelist supported SWP_Query arguments + * + * @link https://searchwp.com/documentation/classes/swp_query/#arguments + */ + function get_valid_args( $query ) { + $output = []; + + $valid = [ + 's', 'engine', 'post__in', 'post__not_in', 'post_type', 'post_status', + 'tax_query', 'meta_query', 'date_query', 'order', 'orderby' + ]; + + foreach ( $valid as $arg ) { + $val = $query->get( $arg ); + if ( ! empty( $val ) || 's' == $arg ) { + $output[ $arg ] = $val; + } + } + + return $output; + } + + + /** + * Modify FacetWP's render() query to use SearchWP's results while bypassing + * WP core search. We're using this additional query to support custom query + * modifications, such as for FacetWP's sort box. + * + * The hook priority (1000) is important because this needs to run after + * FacetWP_Request->update_query_vars() + */ + function pre_get_posts( $query ) { + if ( true === $query->get( 'using_searchwp' ) ) { + if ( true === $query->get( 'facetwp' ) && ! $this->first_run ) { + $query->set( 's', '' ); + + $post_ids = FWP()->filtered_post_ids; + $post_ids = empty( $post_ids ) ? [ 0 ] : $post_ids; + $query->set( 'post__in', $post_ids ); + + if ( '' === $query->get( 'post_type' ) ) { + $query->set( 'post_type', 'any' ); + $query->set( 'post_status', 'any' ); + } + + if ( '' === $query->get( 'orderby' ) ) { + $query->set( 'orderby', 'post__in' ); + } + } + } + } + + + /** + * If [facetwp => false] then it's the get_filtered_post_ids() query. Return + * the raw SearchWP results to prevent the additional query. + * + * If [facetwp => true] and [first_run => true] then it's the main WP query. Return + * a non-null value to kill the query, since we don't use the results. + * + * If [facetwp => true] and [first_run => false] then it's the FacetWP render() query. + */ + function posts_pre_query( $posts, $query ) { + if ( true === $query->get( 'using_searchwp' ) ) { + if ( true === $query->get( 'facetwp' ) ) { + $query->set( 's', $this->keywords ); + + // kill the main WP query + if ( $this->first_run ) { + $this->first_run = false; + + $page = max( $query->get( 'paged' ), 1 ); + $per_page = (int) $query->get( 'posts_per_page', get_option( 'posts_per_page' ) ); + $query->found_posts = count( FWP()->filtered_post_ids ); + $query->max_num_pages = ( 0 < $per_page ) ? ceil( $query->found_posts / $per_page ) : 0; + + return []; + } + } + else { + return $this->swp_query->posts; + } + } + + return $posts; + } + + + /** + * Apply highlighting if available + */ + function posts_results( $posts, $query ) { + if ( true === $query->get( 'using_searchwp' ) ) { + if ( true === $query->get( 'facetwp' ) && ! $this->first_run ) { + + // SearchWP 4.1+ + if ( isset( $this->swp_query->query ) ) { + foreach ( $posts as $index => $post ) { + $source = \SearchWP\Utils::get_post_type_source_name( $post->post_type ); + $entry = new \SearchWP\Entry( $source, $post->ID, false ); + $posts[ $index ] = $entry->native( $this->swp_query->query ); + } + } + } + } + + return $posts; + } + + + /** + * For search facets, run \SWP_Query and return matching post IDs + */ + function search_facet( $return, $params ) { + $facet = $params['facet']; + $selected_values = $params['selected_values']; + $selected_values = is_array( $selected_values ) ? $selected_values[0] : $selected_values; + $engine = $facet['search_engine'] ?? ''; + + if ( 'search' == $facet['type'] && 0 === strpos( $engine, 'swp_' ) ) { + $return = []; + + if ( empty( $selected_values ) ) { + $return = 'continue'; + } + elseif ( ! empty( FWP()->unfiltered_post_ids ) ) { + + if ( 'no' == $facet['enable_relevance'] ) { + add_filter( 'facetwp_use_search_relevancy', '__return_false' ); + } + + $swp_query = $this->run_query([ + 's' => $selected_values, + 'engine' => substr( $engine, 4 ), + 'post__in' => FWP()->unfiltered_post_ids + ]); + $return = $swp_query->posts; + } + } + + return $return; + } + + + /** + * Run a search and return the \SWP_Query object + */ + function run_query( $args ) { + $overrides = [ 'posts_per_page' => 200, 'fields' => 'ids', 'facetwp' => true ]; + $args = array_merge( $args, $overrides ); + return new \SWP_Query( $args ); + } + + + /** + * Add engines to the search facet + */ + function search_engines( $engines ) { + + if ( version_compare( SEARCHWP_VERSION, '4.0', '>=' ) ) { + $settings = get_option( SEARCHWP_PREFIX . 'engines' ); + + foreach ( $settings as $key => $info ) { + $engines[ 'swp_' . $key ] = 'SearchWP - ' . $info['label']; + } + } + else { + $settings = get_option( SEARCHWP_PREFIX . 'settings' ); + + foreach ( $settings['engines'] as $key => $info ) { + $label = $info['searchwp_engine_label'] ?? __( 'Default', 'fwp' ); + $engines[ 'swp_' . $key ] = 'SearchWP - ' . $label; + } + } + + return $engines; + } + + + /** + * Short-circuit SearchWP when "s" is empty and a search facet is in use + * @since 4.2.6 + */ + function short_circuit( $bool, $query ) { + if ( $query->is_search() && '' == $query->get( 's' ) ) { + $facets = FWP()->facet->facets ?? FWP()->ajax->url_vars; + foreach ( $facets AS $name => $value ) { + if ( FWP()->helper->facet_is( $name, 'type', 'search' ) ) { + return true; + } + } + } + + return $bool; + } +} + + +if ( defined( 'SEARCHWP_VERSION' ) ) { + new FacetWP_Integration_SearchWP(); +} diff --git a/wp-content/plugins/facetwp/includes/integrations/woocommerce/taxonomy.php b/wp-content/plugins/facetwp/includes/integrations/woocommerce/taxonomy.php new file mode 100644 index 000000000..173eca16c --- /dev/null +++ b/wp-content/plugins/facetwp/includes/integrations/woocommerce/taxonomy.php @@ -0,0 +1,86 @@ +\n"; + } + } + + + /** + * Adjust the category listing counts when facets are selected + * @since 3.3.10 + */ + function adjust_term_counts( $terms, $taxonomy, $query_vars ) { + if ( FWP()->request->is_refresh || ! empty( FWP()->request->url_vars ) ) { + $fields = $query_vars['fields'] ?? 'all'; + if ( 'product_cat' == reset( $taxonomy ) && 'all' == $fields ) { + global $wpdb, $wp_query; + + $sql = $wp_query->request; + if ( false !== ( $pos = strpos( $sql, 'ORDER BY' ) ) ) { + $sql = substr( $sql, 0, $pos ); + } + + $post_ids = $wpdb->get_col( $sql ); + + if ( ! empty( $post_ids ) ) { + $term_counts = []; + $post_ids_str = implode( ',', $post_ids ); + + $query = " + SELECT term_id, COUNT(DISTINCT post_id) AS term_count + FROM {$wpdb->prefix}facetwp_index + WHERE post_id IN ($post_ids_str) + GROUP BY term_id"; + + $results = $wpdb->get_results( $query ); + + foreach ( $results as $row ) { + $term_counts[ $row->term_id ] = (int) $row->term_count; + } + + foreach ( $terms as $term ) { + $term->count = $term_counts[ $term->term_id ] ?? 0; + } + } + } + } + + return $terms; + } + + + /** + * Append facet URL variables to the category archive links + * @since 3.3.10 + */ + function append_url_vars( $term_link, $term, $taxonomy ) { + if ( 'product_cat' == $taxonomy && did_action( 'woocommerce_shop_loop_header' ) && !did_action( 'woocommerce_after_shop_loop' ) ) { + $query_string = filter_var( $_SERVER['QUERY_STRING'], FILTER_SANITIZE_URL ); + + if ( ! empty( $query_string ) ) { + $prefix = ( false !== strpos( $query_string, '?' ) ) ? '&' : '?'; + $term_link .= $prefix . $query_string; + } + } + + return $term_link; + } +} + +new FacetWP_Integration_WooCommerce_Taxonomy(); diff --git a/wp-content/plugins/facetwp/includes/integrations/woocommerce/woocommerce.js b/wp-content/plugins/facetwp/includes/integrations/woocommerce/woocommerce.js new file mode 100644 index 000000000..cf8b1dfc5 --- /dev/null +++ b/wp-content/plugins/facetwp/includes/integrations/woocommerce/woocommerce.js @@ -0,0 +1,34 @@ +(function($) { + + $().on('facetwp-refresh', function() { + if (! FWP.loaded) { + setup_woocommerce(); + } + }); + + function setup_woocommerce() { + + // Intercept WooCommerce pagination + $().on('click', '.woocommerce-pagination a', function(e) { + e.preventDefault(); + var matches = $(this).attr('href').match(/\/page\/(\d+)/); + if (null !== matches) { + FWP.paged = parseInt(matches[1]); + FWP.soft_refresh = true; + FWP.refresh(); + } + }); + + // Disable sort handler + $('.woocommerce-ordering').attr('onsubmit', 'event.preventDefault()'); + + // Intercept WooCommerce sorting + $().on('change', '.woocommerce-ordering .orderby', function(e) { + var qs = new URLSearchParams(window.location.search); + qs.set('orderby', $(this).val()); + history.pushState(null, null, window.location.pathname + '?' + qs.toString()); + FWP.soft_refresh = true; + FWP.refresh(); + }); + } +})(fUtil); \ No newline at end of file diff --git a/wp-content/plugins/facetwp/includes/integrations/woocommerce/woocommerce.php b/wp-content/plugins/facetwp/includes/integrations/woocommerce/woocommerce.php new file mode 100644 index 000000000..32f20f05a --- /dev/null +++ b/wp-content/plugins/facetwp/includes/integrations/woocommerce/woocommerce.php @@ -0,0 +1,617 @@ +helper->get_setting( 'wc_enable_variations', 'no' ) ); + + if ( apply_filters( 'facetwp_enable_product_variations', $is_enabled ) ) { + add_filter( 'facetwp_indexer_post_facet_defaults', [ $this, 'force_taxonomy' ], 10, 2 ); + add_filter( 'facetwp_indexer_query_args', [ $this, 'index_variations' ] ); + add_filter( 'facetwp_index_row', [ $this, 'attribute_variations' ], 1 ); + add_filter( 'facetwp_wpdb_sql', [ $this, 'wpdb_sql' ], 10, 2 ); + add_filter( 'facetwp_wpdb_get_col', [ $this, 'wpdb_get_col' ], 10, 3 ); + add_filter( 'facetwp_filtered_post_ids', [ $this, 'process_variations' ] ); + add_filter( 'facetwp_facet_where', [ $this, 'facet_where' ], 10, 2 ); + } + + // Preserve the WooCommerce sort + add_filter( 'posts_clauses', [ $this, 'preserve_sort' ], 20, 2 ); + + // Prevent WooCommerce from redirecting to a single result page + add_filter( 'woocommerce_redirect_single_search_result', [ $this, 'redirect_single_search_result' ] ); + + // Prevent WooCommerce sort (posts_clauses) when doing FacetWP sort + add_filter( 'woocommerce_default_catalog_orderby', [ $this, 'default_catalog_orderby' ] ); + + // Dynamic counts when Shop Page Display = "Categories" or "Both" + if ( apply_filters( 'facetwp_woocommerce_support_categories_display', false ) ) { + include( FACETWP_DIR . '/includes/integrations/woocommerce/taxonomy.php' ); + } + + // Prevent WooCommerce breadcrumb from showing "Page X" on page 1 and higher when there are facet selections or pre-selected facets + add_filter( 'woocommerce_get_breadcrumb', [ $this, 'prevent_wc_breadcrumb_paged' ], 9999, 2 ); + + } + + + /** + * Run WooCommerce handlers on facetwp-refresh + * @since 2.0.9 + */ + function assets( $assets ) { + $assets['woocommerce.js'] = FACETWP_URL . '/includes/integrations/woocommerce/woocommerce.js'; + return $assets; + } + + + /** + * Add WooCommerce-specific data sources + * @since 2.1.4 + */ + function facet_sources( $sources ) { + $sources['woocommerce'] = [ + 'label' => __( 'WooCommerce', 'fwp' ), + 'choices' => [ + 'woo/price' => __( 'Price' ), + 'woo/sale_price' => __( 'Sale Price' ), + 'woo/regular_price' => __( 'Regular Price' ), + 'woo/average_rating' => __( 'Average Rating' ), + 'woo/stock_status' => __( 'Stock Status' ), + 'woo/on_sale' => __( 'On Sale' ), + 'woo/featured' => __( 'Featured' ), + 'woo/product_type' => __( 'Product Type' ), + ], + 'weight' => 5 + ]; + + // Move WC taxonomy choices + foreach ( $sources['taxonomies']['choices'] as $key => $label ) { + if ( 'tax/product_cat' == $key || 'tax/product_tag' == $key || 0 === strpos( $key, 'tax/pa_' ) ) { + $sources['woocommerce']['choices'][ $key ] = $label; + unset( $sources['taxonomies']['choices'][ $key ] ); + } + } + + return $sources; + } + + + /** + * Attributes for WC product variations are stored in postmeta + * @since 2.7.2 + */ + function force_taxonomy( $defaults, $params ) { + if ( 0 === strpos( $defaults['facet_source'], 'tax/pa_' ) ) { + $post_id = (int) $defaults['post_id']; + + if ( 'product_variation' == get_post_type( $post_id ) ) { + $defaults['facet_source'] = str_replace( 'tax/', 'cf/attribute_', $defaults['facet_source'] ); + } + } + + return $defaults; + } + + + /** + * Index product variations + * @since 2.7 + */ + function index_variations( $args ) { + + // Saving a single product + if ( ! empty( $args['p'] ) ) { + $post_id = (int) $args['p']; + if ( 'product' == get_post_type( $post_id ) ) { + if ( 'variable' == $this->get_product_type( $post_id ) ) { + $product = wc_get_product( $post_id ); + + if ( false !== $product ) { + $children = $product->get_children(); + $args['post_type'] = [ 'product', 'product_variation' ]; + $args['post__in'] = $children; + $args['post__in'][] = $post_id; + $args['posts_per_page'] = -1; + unset( $args['p'] ); + } + } + } + } + // Force product variations to piggyback products + else { + $pt = (array) $args['post_type']; + + if ( in_array( 'product', $pt ) ) { + $pt[] = 'product_variation'; + } + + $args['post_type'] = $pt; + } + + return $args; + } + + + /** + * When indexing product variations, attribute its parent product + * @since 2.7 + */ + function attribute_variations( $params ) { + $post_id = (int) $params['post_id']; + + // Set variation_id for all posts + $params['variation_id'] = $post_id; + + if ( 'product_variation' == get_post_type( $post_id ) ) { + $params['post_id'] = wp_get_post_parent_id( $post_id ); + + // Lookup the term name for variation values + if ( 0 === strpos( $params['facet_source'], 'cf/attribute_pa_' ) ) { + $taxonomy = str_replace( 'cf/attribute_', '', $params['facet_source'] ); + $term = get_term_by( 'slug', $params['facet_value'], $taxonomy ); + + if ( false !== $term ) { + $params['term_id'] = $term->term_id; + $params['parent_id'] = $term->parent; + $params['facet_display_value'] = $term->name; + + $params['depth'] = count( get_ancestors( $term->term_id, $taxonomy, 'taxonomy' ) ); + + $facet = FWP()->helper->get_facet_by_name( $params['facet_name'] ); + + // handle parent_term setting + if ( isset( $facet['parent_term'] ) && 0 < $facet['parent_term'] && !term_is_ancestor_of( $facet['parent_term'], $params['term_id'], $taxonomy ) ) { + $params['facet_value'] = ''; // don't index + } + } + } + } + + return $params; + } + + + /** + * Hijack filter_posts() to grab variation IDs + * @since 2.7 + */ + function wpdb_sql( $sql, $facet ) { + $sql = str_replace( + 'DISTINCT post_id', + 'DISTINCT post_id, GROUP_CONCAT(variation_id) AS variation_ids', + $sql + ); + + $sql .= ' GROUP BY post_id'; + + return $sql; + } + + + /** + * Store a facet's variation IDs + * @since 2.7 + */ + function wpdb_get_col( $result, $sql, $facet ) { + global $wpdb; + + $facet_name = $facet['name']; + $post_ids = $wpdb->get_col( $sql, 0 ); // arrays of product IDs + $variations = $wpdb->get_col( $sql, 1 ); // variation IDs as arrays of comma-separated strings + + foreach ( $post_ids as $index => $post_id ) { + $variations_array = explode( ',', $variations[ $index ] ); + $type = in_array( $post_id, $variations_array ) ? 'products' : 'variations'; + + if ( isset( $this->cache[ $facet_name ][ $type ] ) ) { + foreach ( $variations_array as $id ) { + $this->cache[ $facet_name ][ $type ][] = $id; + } + } + else { + $this->cache[ $facet_name ][ $type ] = $variations_array; + } + } + + return $result; + } + + + /** + * We need lookup arrays for both products and variations + * @since 2.7.1 + */ + function generate_lookup_array( $post_ids ) { + global $wpdb; + + $output = []; + + if ( ! empty( $post_ids ) ) { + $sql = " + SELECT DISTINCT post_id, variation_id + FROM {$wpdb->prefix}facetwp_index + WHERE post_id IN (" . implode( ',', $post_ids ) . ")"; + $results = $wpdb->get_results( $sql ); + + foreach ( $results as $result ) { + $output['get_variations'][ $result->post_id ][] = $result->variation_id; + $output['get_product'][ $result->variation_id ] = $result->post_id; + } + } + + return $output; + } + + + /** + * Determine valid variation IDs + * @since 2.7 + */ + function process_variations( $post_ids ) { + if ( empty( $this->cache ) ) { + return $post_ids; + } + + $this->lookup = $this->generate_lookup_array( FWP()->unfiltered_post_ids ); + + // Loop through each facet's data + foreach ( $this->cache as $facet_name => $groups ) { + $this->storage[ $facet_name ] = []; + + // Create an array of variation IDs + foreach ( $groups as $type => $ids ) { // products or variations + foreach ( $ids as $id ) { + $this->storage[ $facet_name ][] = $id; + + // Lookup variation IDs for each product + if ( 'products' == $type ) { + if ( ! empty( $this->lookup['get_variations'][ $id ] ) ) { + foreach ( $this->lookup['get_variations'][ $id ] as $variation_id ) { + $this->storage[ $facet_name ][] = $variation_id; + } + } + } + } + } + } + + $result = $this->calculate_variations(); + $this->variations = $result['variations']; + $post_ids = array_intersect( $post_ids, $result['products'] ); + $post_ids = empty( $post_ids ) ? [ 0 ] : $post_ids; + return $post_ids; + } + + + /** + * Calculate variation IDs + * @param mixed $facet_name Facet name to ignore, or FALSE + * @return array Associative array of product IDs + variation IDs + * @since 2.8 + */ + function calculate_variations( $facet_name = false ) { + + $new = true; + $final_products = []; + $final_variations = []; + + // Intersect product + variation IDs across facets + foreach ( $this->storage as $name => $variation_ids ) { + + // Skip facets in "OR" mode + if ( $facet_name === $name ) { + continue; + } + + $final_variations = ( $new ) ? $variation_ids : array_intersect( $final_variations, $variation_ids ); + $new = false; + } + + // Lookup each variation's product ID + foreach ( $final_variations as $variation_id ) { + if ( isset( $this->lookup['get_product'][ $variation_id ] ) ) { + $final_products[ $this->lookup['get_product'][ $variation_id ] ] = true; // prevent duplicates + } + } + + // Append product IDs to the variations array + $final_products = array_keys( $final_products ); + + foreach ( $final_products as $id ) { + $final_variations[] = $id; + } + + return [ + 'products' => $final_products, + 'variations' => array_unique( $final_variations ) + ]; + } + + + /** + * Apply variation IDs to load_values() method + * @since 2.7 + */ + function facet_where( $where_clause, $facet ) { + + // Support facets in "OR" mode + if ( FWP()->helper->facet_is( $facet, 'operator', 'or' ) ) { + $result = $this->calculate_variations( $facet['name'] ); + $variations = $result['variations']; + } + else { + $variations = $this->variations; + } + + if ( ! empty( $variations ) ) { + $where_clause .= ' AND variation_id IN (' . implode( ',', $variations ) . ')'; + } + + return $where_clause; + } + + + /** + * Efficiently grab the product type without wc_get_product() + * @since 3.3.8 + */ + function get_product_type( $post_id ) { + global $wpdb; + + $sql = " + SELECT t.name + FROM $wpdb->terms t + INNER JOIN $wpdb->term_taxonomy tt ON tt.term_id = t.term_id AND tt.taxonomy = 'product_type' + INNER JOIN $wpdb->term_relationships tr ON tr.term_taxonomy_id = tt.term_taxonomy_id AND tr.object_id = %d"; + + $type = $wpdb->get_var( + $wpdb->prepare( $sql, $post_id ) + ); + + return ( null !== $type ) ? $type : 'simple'; + } + + + /** + * Index WooCommerce-specific values + * @since 2.1.4 + */ + function index_woo_values( $return, $params ) { + $facet = $params['facet']; + $defaults = $params['defaults']; + $post_id = (int) $defaults['post_id']; + $post_type = get_post_type( $post_id ); + + // Index out of stock products? + $index_all = ( 'yes' === FWP()->helper->get_setting( 'wc_index_all', 'no' ) ); + $index_all = apply_filters( 'facetwp_index_all_products', $index_all ); + + if ( 'product' == $post_type || 'product_variation' == $post_type ) { + $product = wc_get_product( $post_id ); + + if ( ! $product || ( ! $index_all && ! $product->is_in_stock() ) ) { + return true; // skip + } + } + + // Default handling + if ( 'product' != $post_type || empty( $facet['source'] ) ) { + return $return; + } + + // Ignore product attributes with "Used for variations" ticked + if ( 0 === strpos( $facet['source'], 'tax/pa_' ) ) { + if ( 'variable' == $this->get_product_type( $post_id ) ) { + $attrs = $product->get_attributes(); + $attr_name = str_replace( 'tax/', '', $facet['source'] ); + if ( isset( $attrs[ $attr_name ] ) && 1 === $attrs[ $attr_name ]['is_variation'] ) { + return true; // skip + } + } + } + + // Custom woo fields + if ( 0 === strpos( $facet['source'], 'woo/' ) ) { + $source = substr( $facet['source'], 4 ); + + // Price + if ( 'price' == $source || 'sale_price' == $source || 'regular_price' == $source ) { + if ( $product->is_type( 'variable' ) ) { + $method_name = "get_variation_$source"; + $price_min = $product->$method_name( 'min' ); // get_variation_price() + $price_max = $product->$method_name( 'max' ); + } + else { + $method_name = "get_$source"; + $price_min = $price_max = $product->$method_name(); // get_price() + } + + $defaults['facet_value'] = $price_min; + $defaults['facet_display_value'] = $price_max; + FWP()->indexer->index_row( $defaults ); + } + + // Average Rating + elseif ( 'average_rating' == $source ) { + $rating = $product->get_average_rating(); + $defaults['facet_value'] = $rating; + $defaults['facet_display_value'] = $rating; + FWP()->indexer->index_row( $defaults ); + } + + // Stock Status + elseif ( 'stock_status' == $source ) { + $in_stock = $product->is_in_stock(); + $defaults['facet_value'] = (int) $in_stock; + $defaults['facet_display_value'] = $in_stock ? 'In Stock' : 'Out of Stock'; + FWP()->indexer->index_row( $defaults ); + } + + // On Sale + elseif ( 'on_sale' == $source ) { + if ( $product->is_on_sale() ) { + $defaults['facet_value'] = 1; + $defaults['facet_display_value'] = 'On Sale'; + FWP()->indexer->index_row( $defaults ); + } + } + + // Featured + elseif ( 'featured' == $source ) { + if ( $product->is_featured() ) { + $defaults['facet_value'] = 1; + $defaults['facet_display_value'] = 'Featured'; + FWP()->indexer->index_row( $defaults ); + } + } + + // Product Type + elseif ( 'product_type' == $source ) { + $type = $product->get_type(); + $defaults['facet_value'] = $type; + $defaults['facet_display_value'] = $type; + FWP()->indexer->index_row( $defaults ); + } + + return true; // skip + } + + return $return; + } + + + /** + * Exclude specific WC custom fields + * @since 4.2.3 + */ + function exclude_data_sources( $sources ) { + unset( $sources['custom_fields']['choices']['cf/_product_attributes'] ); + return $sources; + } + + + /** + * Allow certain hard-coded choices to be translated dynamically + * instead of stored as translated in the index table + * @since 3.9.6 + */ + function translate_hardcoded_choices( $label, $params ) { + $source = $params['facet']['source']; + + if ( 'woo/stock_status' == $source ) { + + // We aren't using a ternary here in case the user + // assigned a custom stock status label + if ( 'In Stock' == $label ) { + $label = __( 'In Stock', 'fwp-front' ); + } + elseif ( 'Out of Stock' == $label ) { + $label = __( 'Out of Stock', 'fwp-front' ); + } + } + elseif ( 'woo/on_sale' == $source ) { + $label = __( 'On Sale', 'fwp-front' ); + } + elseif ( 'woo/featured' == $source ) { + $label = __( 'Featured', 'fwp-front' ); + } + + return $label; + } + + + /** + * WooCommerce removes its sort hooks after the main product_query runs + * We need to preserve the sort for FacetWP to work + * + * @since 3.2.8 + */ + function preserve_sort( $clauses, $query ) { + + if ( ! apply_filters( 'facetwp_woocommerce_preserve_sort', true ) ) { + return $clauses; + } + + $prefix = FWP()->helper->get_setting( 'prefix' ); + $using_sort = isset( FWP()->facet->http_params['get'][ $prefix . 'sort' ] ); + + if ( 'product_query' == $query->get( 'wc_query' ) && true === $query->get( 'facetwp' ) && ! $using_sort ) { + if ( false === $this->post_clauses ) { + $this->post_clauses = $clauses; + } + else { + $clauses['join'] = $this->post_clauses['join']; + $clauses['where'] = $this->post_clauses['where']; + $clauses['orderby'] = $this->post_clauses['orderby']; + + // Narrow the main query results + $where_clause = FWP()->facet->where_clause; + + if ( ! empty( $where_clause ) ) { + $column = $GLOBALS['wpdb']->posts; + $clauses['where'] .= str_replace( 'post_id', "$column.ID", $where_clause ); + } + } + } + + return $clauses; + } + + + /** + * Prevent WooCommerce from redirecting to single result page + * @since 3.3.7 + */ + function redirect_single_search_result( $bool ) { + $using_facetwp = ( FWP()->request->is_refresh || ! empty( FWP()->request->url_vars ) ); + return $using_facetwp ? false : $bool; + } + + + /** + * Prevent WooCommerce sort when a FacetWP sort is active + * @since 3.6.8 + */ + function default_catalog_orderby( $orderby ) { + $sort = FWP()->helper->get_setting( 'prefix' ) . 'sort'; + return isset( $_GET[ $sort ] ) ? 'menu_order' : $orderby; + } + + + /** + * Prevent WooCommerce breadcrumb from showing "Page X" on page 1 and higher when there are facet selections or pre-selected facets + * @since 4.3.6 + */ + function prevent_wc_breadcrumb_paged( $crumbs, $breadcrumb ) { + + if ( function_exists( 'FWP' ) && !empty( FWP()->request->url_vars ) ) { + if ( get_query_var( 'paged' ) >= 1 && 'subcategories' !== woocommerce_get_loop_display_mode() ) { + array_pop( $crumbs ); + } + } + + return $crumbs; + } + +} + + +if ( is_plugin_active( 'woocommerce/woocommerce.php' ) ) { + new FacetWP_Integration_WooCommerce(); +} diff --git a/wp-content/plugins/facetwp/includes/integrations/wp-cli/wp-cli.php b/wp-content/plugins/facetwp/includes/integrations/wp-cli/wp-cli.php new file mode 100644 index 000000000..9cf36073f --- /dev/null +++ b/wp-content/plugins/facetwp/includes/integrations/wp-cli/wp-cli.php @@ -0,0 +1,150 @@ +] + * : Index specific post IDs (comma-separated). + * + * [--facets=] + * : Index specific facet names (comma-separated). + */ + function index( $args, $assoc_args ) { + $index_all = true; + + if ( isset( $assoc_args['ids'] ) ) { + if ( empty( $assoc_args['ids'] ) ) { + WP_CLI::error( 'IDs empty.' ); + } + + $ids = preg_replace( '/\s+/', '', $assoc_args['ids'] ); + $ids = explode( ',', $ids ); + $post_ids = array_filter( $ids, 'ctype_digit' ); + $index_all = false; + } + else { + $post_ids = FWP()->indexer->get_post_ids_to_index(); + } + + if ( isset( $assoc_args['facets'] ) ) { + if ( empty( $assoc_args['facets'] ) ) { + WP_CLI::error( 'Facets empty.' ); + } + + $facets = []; + $facet_names = preg_replace( '/\s+/', '', $assoc_args['facets'] ); + $facet_names = explode( ',', $facet_names ); + foreach ( $facet_names as $name ) { + $facet = FWP()->helper->get_facet_by_name( $name ); + if ( false !== $facet ) { + $facets[] = $facet; + } + } + + $index_all = false; + } + else { + $facets = FWP()->helper->get_facets(); + } + + $progress = WP_CLI\Utils\make_progress_bar( 'Indexing:', count( $post_ids ) ); + + // prep + if ( $index_all ) { + FWP()->indexer->manage_temp_table( 'create' ); + } + else { + $assoc_args['pre_index'] = true; + $this->purge( $args, $assoc_args ); + } + + // manually load value modifiers + FWP()->indexer->load_value_modifiers( $facets ); + + // index + foreach ( $post_ids as $post_id ) { + FWP()->indexer->index_post( $post_id, $facets ); + $progress->tick(); + } + + // cleanup + if ( $index_all ) { + update_option( 'facetwp_last_indexed', time(), 'no' ); + FWP()->indexer->manage_temp_table( 'replace' ); + FWP()->indexer->manage_temp_table( 'delete' ); + } + + $progress->finish(); + + WP_CLI::success( 'Indexing complete.' ); + } + + + /** + * Purge facet data. + * + * ## OPTIONS + * + * [--ids=] + * : Purge specific post IDs (comma-separated). + * + * [--facets=] + * : Purge specific facet names (comma-separated). + */ + function purge( $args, $assoc_args ) { + global $wpdb; + + $table = FWP()->indexer->table; + + if ( ! isset( $assoc_args['ids'] ) && ! isset( $assoc_args['facets'] ) ) { + $sql = "TRUNCATE TABLE $table"; + } + else { + $where = []; + + if ( isset( $assoc_args['ids'] ) ) { + if ( empty( $assoc_args['ids'] ) ) { + WP_CLI::error( 'IDs empty.' ); + } + + $ids = preg_replace( '/\s+/', '', ',' . $assoc_args['ids'] ); + $ids = explode( ',', $ids ); + $post_ids = array_filter( $ids, 'ctype_digit' ); + $post_ids = implode( "','", $post_ids ); + $where[] = "post_id IN ('$post_ids')"; + } + + if ( isset( $assoc_args['facets'] ) ) { + if ( empty( $assoc_args['facets'] ) ) { + WP_CLI::error( 'Facets empty.' ); + } + + $facet_names = preg_replace( '/\s+/', '', $assoc_args['facets'] ); + $facet_names = explode( ',', $facet_names ); + $facet_names = array_map( 'esc_sql', $facet_names ); + $facet_names = implode( "','", $facet_names ); + $where[] = "facet_name IN ('$facet_names')"; + } + + $sql = "DELETE FROM $table WHERE " . implode( ' AND ', $where ); + } + + $wpdb->query( $sql ); + + if ( ! isset( $assoc_args['pre_index'] ) ) { + WP_CLI::success( 'Purge complete.' ); + } + } +} + +if ( defined( 'WP_CLI' ) && WP_CLI ) { + WP_CLI::add_command( 'facetwp', 'FacetWP_Integration_WP_CLI' ); +} diff --git a/wp-content/plugins/facetwp/includes/integrations/wp-rocket/wp-rocket.php b/wp-content/plugins/facetwp/includes/integrations/wp-rocket/wp-rocket.php new file mode 100644 index 000000000..b0799aaee --- /dev/null +++ b/wp-content/plugins/facetwp/includes/integrations/wp-rocket/wp-rocket.php @@ -0,0 +1,24 @@ +. +*/ + +defined( 'ABSPATH' ) or exit; + +class FacetWP +{ + + private static $instance; + + public $filtered_post_ids; + public $unfiltered_post_ids; + public $is_filtered; + public $is_modified; + public $or_values; + + public $init; + public $api; + public $helper; + public $facet; + public $settings; + public $diff; + public $indexer; + public $display; + public $builder; + public $request; + public $ajax; + public $acf; + + + function __construct() { + + // php check + if ( version_compare( phpversion(), '7.0', '<' ) ) { + add_action( 'admin_notices', array( $this, 'upgrade_notice' ) ); + return; + } + + // setup variables + define( 'FACETWP_VERSION', '4.4.1' ); + define( 'FACETWP_DIR', dirname( __FILE__ ) ); + define( 'FACETWP_URL', plugins_url( '', __FILE__ ) ); + define( 'FACETWP_BASENAME', plugin_basename( __FILE__ ) ); + + // get the gears turning + include( FACETWP_DIR . '/includes/class-init.php' ); + } + + + /** + * Singleton + */ + public static function instance() { + if ( ! isset( self::$instance ) ) { + self::$instance = new self; + } + return self::$instance; + } + + + /** + * Require PHP 7.0+ + */ + function upgrade_notice() { + $message = __( 'FacetWP requires PHP %s or above. Please contact your host and request a PHP upgrade.', 'fwp' ); + echo '

    ' . sprintf( $message, '7.0' ) . '

    '; + } +} + + +function FWP() { + return FacetWP::instance(); +} + + +FWP(); diff --git a/wp-content/plugins/facetwp/languages/fwp-front-ca.mo b/wp-content/plugins/facetwp/languages/fwp-front-ca.mo new file mode 100644 index 000000000..f2315316e Binary files /dev/null and b/wp-content/plugins/facetwp/languages/fwp-front-ca.mo differ diff --git a/wp-content/plugins/facetwp/languages/fwp-front-ca.po b/wp-content/plugins/facetwp/languages/fwp-front-ca.po new file mode 100644 index 000000000..99f0afccb --- /dev/null +++ b/wp-content/plugins/facetwp/languages/fwp-front-ca.po @@ -0,0 +1,226 @@ +msgid "" +msgstr "" +"Project-Id-Version: FacetWP (front)\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-02-21 14:01+0000\n" +"PO-Revision-Date: 2025-04-18 10:44+0000\n" +"Last-Translator: \n" +"Language-Team: Catalan\n" +"Language: ca\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Loco https://localise.biz/\n" +"X-Loco-Version: 2.7.1; wp-6.7.1\n" +"X-Poedit-SourceCharset: UTF-8\n" + +#: includes/facets/rating.php:165 +msgid "& up" +msgstr "& up" + +#. Description of the plugin +#: index.php +msgid "Advanced Filtering for WordPress" +msgstr "" + +#: includes/facets/dropdown.php:35 includes/facets/fselect.php:37 +#: includes/facets/fselect.php:80 includes/facets/hierarchy.php:26 +msgid "Any" +msgstr "Qualsevol" + +#: includes/facets/date_range.php:300 +msgid "Clear" +msgstr "Esborrar" + +#: includes/facets/proximity.php:243 +msgid "Clear location" +msgstr "Localització de clara" + +#: includes/facets/date_range.php:21 +msgid "Date" +msgstr "Data" + +#: includes/facets/sort.php:180 +msgid "Date (Newest)" +msgstr "Data (més recent)" + +#: includes/facets/sort.php:187 +msgid "Date (Oldest)" +msgstr "Data (més antic)" + +#: includes/facets/proximity.php:357 +msgid "Distance" +msgstr "Distància" + +#: includes/facets/map.php:397 +msgid "Enable map filtering" +msgstr "" + +#: includes/facets/date_range.php:23 +msgid "End date" +msgstr "Data final" + +#: includes/facets/search.php:23 +msgid "Enter keywords" +msgstr "Introduïu les paraules clau" + +#: includes/facets/proximity.php:52 +msgid "Enter location" +msgstr "Introduïu la ubicació" + +#: includes/facets/autocomplete.php:165 +msgid "Enter {n} or more characters" +msgstr "" + +#. Plugin Name of the plugin +#: index.php +msgid "FacetWP" +msgstr "" + +#. Author of the plugin +#: index.php +msgid "FacetWP, LLC" +msgstr "" + +#: includes/integrations/woocommerce/woocommerce.php:532 +msgid "Featured" +msgstr "" + +#: includes/facets/autocomplete.php:66 includes/facets/number_range.php:32 +msgid "Go" +msgstr "Cercar" + +#: includes/class-display.php:208 +msgid "Go to next page" +msgstr "" + +#: includes/class-display.php:207 +msgid "Go to page" +msgstr "" + +#: includes/class-display.php:209 +msgid "Go to previous page" +msgstr "" + +#. Author URI of the plugin +#: index.php +msgid "https://facetwp.com/" +msgstr "" + +#: includes/integrations/woocommerce/woocommerce.php:522 +msgid "In Stock" +msgstr "En Stock" + +#: includes/facets/proximity.php:51 +msgid "km" +msgstr "" + +#: includes/facets/autocomplete.php:164 +msgid "Loading" +msgstr "" + +#: includes/facets/number_range.php:29 +msgid "Max" +msgstr "Màx" + +#: includes/facets/proximity.php:51 +msgid "mi" +msgstr "" + +#: includes/facets/number_range.php:26 +msgid "Min" +msgstr "Min" + +#: includes/integrations/acf/acf.php:325 +msgid "No" +msgstr "No" + +#: includes/facets/date_range.php:25 +msgid "No dates" +msgstr "" + +#: includes/facets/date_range.php:27 +msgid "No end dates" +msgstr "" + +#: includes/facets/autocomplete.php:99 includes/facets/autocomplete.php:166 +msgid "No results" +msgstr "No hi ha resultats" + +#: includes/class-display.php:227 includes/facets/fselect.php:87 +msgid "No results found" +msgstr "No s'han trobat resultats" + +#: includes/facets/date_range.php:26 +msgid "No start dates" +msgstr "" + +#: includes/facets/number_range.php:23 +msgid "Number" +msgstr "Número" + +#: includes/class-renderer.php:535 +msgid "of" +msgstr "de" + +#: includes/integrations/woocommerce/woocommerce.php:529 +msgid "On Sale" +msgstr "Oferta" + +#: includes/integrations/woocommerce/woocommerce.php:525 +msgid "Out of Stock" +msgstr "Sense Stock" + +#: includes/class-renderer.php:592 +msgid "Per page" +msgstr "Per pàgina" + +#: includes/facets/map.php:400 includes/facets/reset.php:15 +#: includes/facets/slider.php:20 +msgid "Reset" +msgstr "Restablir" + +#: includes/facets/fselect.php:86 +msgid "Search" +msgstr "Cercar" + +#: includes/facets/checkboxes.php:134 includes/facets/hierarchy.php:164 +msgid "See less" +msgstr "Veure menys" + +#: includes/facets/checkboxes.php:133 includes/facets/hierarchy.php:163 +msgid "See {num} more" +msgstr "Veure més {num}" + +#: includes/facets/sort.php:162 +msgid "Sort by" +msgstr "Ordena per" + +#: includes/facets/date_range.php:22 +msgid "Start date" +msgstr "Data inici" + +#: includes/facets/autocomplete.php:63 +msgid "Start typing" +msgstr "Comenceu a escriure" + +#: includes/facets/sort.php:166 +msgid "Title (A-Z)" +msgstr "Títol (A-Z)" + +#: includes/facets/sort.php:173 +msgid "Title (Z-A)" +msgstr "Títol (Z-A)" + +#: includes/facets/rating.php:166 +msgid "Undo" +msgstr "Desfer" + +#: includes/integrations/acf/acf.php:325 +msgid "Yes" +msgstr "Sí" + +#: includes/facets/fselect.php:85 +msgid "{n} selected" +msgstr "{n} seleccionats" diff --git a/wp-content/plugins/facetwp/languages/fwp-front-da_DK.mo b/wp-content/plugins/facetwp/languages/fwp-front-da_DK.mo new file mode 100644 index 000000000..bfc0777ef Binary files /dev/null and b/wp-content/plugins/facetwp/languages/fwp-front-da_DK.mo differ diff --git a/wp-content/plugins/facetwp/languages/fwp-front-da_DK.po b/wp-content/plugins/facetwp/languages/fwp-front-da_DK.po new file mode 100644 index 000000000..227a0ce9f --- /dev/null +++ b/wp-content/plugins/facetwp/languages/fwp-front-da_DK.po @@ -0,0 +1,226 @@ +msgid "" +msgstr "" +"Project-Id-Version: FacetWP (front)\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-02-21 14:01+0000\n" +"PO-Revision-Date: 2025-04-18 10:45+0000\n" +"Last-Translator: \n" +"Language-Team: Danish\n" +"Language: da-DK\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Loco https://localise.biz/\n" +"X-Loco-Version: 2.7.1; wp-6.7.1\n" +"X-Poedit-SourceCharset: UTF-8\n" + +#: includes/facets/rating.php:165 +msgid "& up" +msgstr "& op" + +#. Description of the plugin +#: index.php +msgid "Advanced Filtering for WordPress" +msgstr "" + +#: includes/facets/dropdown.php:35 includes/facets/fselect.php:37 +#: includes/facets/fselect.php:80 includes/facets/hierarchy.php:26 +msgid "Any" +msgstr "Enhver" + +#: includes/facets/date_range.php:300 +msgid "Clear" +msgstr "Ryd" + +#: includes/facets/proximity.php:243 +msgid "Clear location" +msgstr "Rud beliggenhed" + +#: includes/facets/date_range.php:21 +msgid "Date" +msgstr "Dato" + +#: includes/facets/sort.php:180 +msgid "Date (Newest)" +msgstr "Dato (nyeste)" + +#: includes/facets/sort.php:187 +msgid "Date (Oldest)" +msgstr "Dato (ældste)" + +#: includes/facets/proximity.php:357 +msgid "Distance" +msgstr "Afstand" + +#: includes/facets/map.php:397 +msgid "Enable map filtering" +msgstr "" + +#: includes/facets/date_range.php:23 +msgid "End date" +msgstr "Slut dato" + +#: includes/facets/search.php:23 +msgid "Enter keywords" +msgstr "Indtast søgeord" + +#: includes/facets/proximity.php:52 +msgid "Enter location" +msgstr "Indtast lokation" + +#: includes/facets/autocomplete.php:165 +msgid "Enter {n} or more characters" +msgstr "" + +#. Plugin Name of the plugin +#: index.php +msgid "FacetWP" +msgstr "" + +#. Author of the plugin +#: index.php +msgid "FacetWP, LLC" +msgstr "" + +#: includes/integrations/woocommerce/woocommerce.php:532 +msgid "Featured" +msgstr "" + +#: includes/facets/autocomplete.php:66 includes/facets/number_range.php:32 +msgid "Go" +msgstr "Gå" + +#: includes/class-display.php:208 +msgid "Go to next page" +msgstr "" + +#: includes/class-display.php:207 +msgid "Go to page" +msgstr "" + +#: includes/class-display.php:209 +msgid "Go to previous page" +msgstr "" + +#. Author URI of the plugin +#: index.php +msgid "https://facetwp.com/" +msgstr "" + +#: includes/integrations/woocommerce/woocommerce.php:522 +msgid "In Stock" +msgstr "På lager" + +#: includes/facets/proximity.php:51 +msgid "km" +msgstr "" + +#: includes/facets/autocomplete.php:164 +msgid "Loading" +msgstr "" + +#: includes/facets/number_range.php:29 +msgid "Max" +msgstr "Maks" + +#: includes/facets/proximity.php:51 +msgid "mi" +msgstr "" + +#: includes/facets/number_range.php:26 +msgid "Min" +msgstr "Min" + +#: includes/integrations/acf/acf.php:325 +msgid "No" +msgstr "Nej" + +#: includes/facets/date_range.php:25 +msgid "No dates" +msgstr "" + +#: includes/facets/date_range.php:27 +msgid "No end dates" +msgstr "" + +#: includes/facets/autocomplete.php:99 includes/facets/autocomplete.php:166 +msgid "No results" +msgstr "Ingen resultater" + +#: includes/class-display.php:227 includes/facets/fselect.php:87 +msgid "No results found" +msgstr "Ingen resultater fundet" + +#: includes/facets/date_range.php:26 +msgid "No start dates" +msgstr "" + +#: includes/facets/number_range.php:23 +msgid "Number" +msgstr "Nummer" + +#: includes/class-renderer.php:535 +msgid "of" +msgstr "af" + +#: includes/integrations/woocommerce/woocommerce.php:529 +msgid "On Sale" +msgstr "Udsalg" + +#: includes/integrations/woocommerce/woocommerce.php:525 +msgid "Out of Stock" +msgstr "Ikke på lager" + +#: includes/class-renderer.php:592 +msgid "Per page" +msgstr "Per side" + +#: includes/facets/map.php:400 includes/facets/reset.php:15 +#: includes/facets/slider.php:20 +msgid "Reset" +msgstr "Nulstil" + +#: includes/facets/fselect.php:86 +msgid "Search" +msgstr "Søg" + +#: includes/facets/checkboxes.php:134 includes/facets/hierarchy.php:164 +msgid "See less" +msgstr "Se mindre" + +#: includes/facets/checkboxes.php:133 includes/facets/hierarchy.php:163 +msgid "See {num} more" +msgstr "Se {num} mere" + +#: includes/facets/sort.php:162 +msgid "Sort by" +msgstr "Sorter efter" + +#: includes/facets/date_range.php:22 +msgid "Start date" +msgstr "Start dato" + +#: includes/facets/autocomplete.php:63 +msgid "Start typing" +msgstr "Begynd at skrive" + +#: includes/facets/sort.php:166 +msgid "Title (A-Z)" +msgstr "Titel (A - Å)" + +#: includes/facets/sort.php:173 +msgid "Title (Z-A)" +msgstr "Titel (Å-A)" + +#: includes/facets/rating.php:166 +msgid "Undo" +msgstr "Fortryd" + +#: includes/integrations/acf/acf.php:325 +msgid "Yes" +msgstr "Ja" + +#: includes/facets/fselect.php:85 +msgid "{n} selected" +msgstr "{n} valgt" diff --git a/wp-content/plugins/facetwp/languages/fwp-front-de_DE.mo b/wp-content/plugins/facetwp/languages/fwp-front-de_DE.mo new file mode 100644 index 000000000..3d97bf780 Binary files /dev/null and b/wp-content/plugins/facetwp/languages/fwp-front-de_DE.mo differ diff --git a/wp-content/plugins/facetwp/languages/fwp-front-de_DE.po b/wp-content/plugins/facetwp/languages/fwp-front-de_DE.po new file mode 100644 index 000000000..90c4383c6 --- /dev/null +++ b/wp-content/plugins/facetwp/languages/fwp-front-de_DE.po @@ -0,0 +1,226 @@ +msgid "" +msgstr "" +"Project-Id-Version: FacetWP (front)\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-02-21 14:01+0000\n" +"PO-Revision-Date: 2025-04-18 10:28+0000\n" +"Last-Translator: \n" +"Language-Team: German\n" +"Language: de-DE\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Loco https://localise.biz/\n" +"X-Loco-Version: 2.7.1; wp-6.7.1\n" +"X-Poedit-SourceCharset: UTF-8\n" + +#: includes/facets/rating.php:165 +msgid "& up" +msgstr "& up" + +#. Description of the plugin +#: index.php +msgid "Advanced Filtering for WordPress" +msgstr "" + +#: includes/facets/dropdown.php:35 includes/facets/fselect.php:37 +#: includes/facets/fselect.php:80 includes/facets/hierarchy.php:26 +msgid "Any" +msgstr "Alle" + +#: includes/facets/date_range.php:300 +msgid "Clear" +msgstr "Zurücksetzen" + +#: includes/facets/proximity.php:243 +msgid "Clear location" +msgstr "Location löschen" + +#: includes/facets/date_range.php:21 +msgid "Date" +msgstr "Datum" + +#: includes/facets/sort.php:180 +msgid "Date (Newest)" +msgstr "Datum (Neueste)" + +#: includes/facets/sort.php:187 +msgid "Date (Oldest)" +msgstr "Datum (älteste)" + +#: includes/facets/proximity.php:357 +msgid "Distance" +msgstr "Entfernung" + +#: includes/facets/map.php:397 +msgid "Enable map filtering" +msgstr "" + +#: includes/facets/date_range.php:23 +msgid "End date" +msgstr "Enddatum" + +#: includes/facets/search.php:23 +msgid "Enter keywords" +msgstr "Suchbegriff eingeben" + +#: includes/facets/proximity.php:52 +msgid "Enter location" +msgstr "Geben Sie den Ort an" + +#: includes/facets/autocomplete.php:165 +msgid "Enter {n} or more characters" +msgstr "" + +#. Plugin Name of the plugin +#: index.php +msgid "FacetWP" +msgstr "" + +#. Author of the plugin +#: index.php +msgid "FacetWP, LLC" +msgstr "" + +#: includes/integrations/woocommerce/woocommerce.php:532 +msgid "Featured" +msgstr "" + +#: includes/facets/autocomplete.php:66 includes/facets/number_range.php:32 +msgid "Go" +msgstr "Los" + +#: includes/class-display.php:208 +msgid "Go to next page" +msgstr "" + +#: includes/class-display.php:207 +msgid "Go to page" +msgstr "" + +#: includes/class-display.php:209 +msgid "Go to previous page" +msgstr "" + +#. Author URI of the plugin +#: index.php +msgid "https://facetwp.com/" +msgstr "" + +#: includes/integrations/woocommerce/woocommerce.php:522 +msgid "In Stock" +msgstr "Auf Lager" + +#: includes/facets/proximity.php:51 +msgid "km" +msgstr "km" + +#: includes/facets/autocomplete.php:164 +msgid "Loading" +msgstr "" + +#: includes/facets/number_range.php:29 +msgid "Max" +msgstr "Max" + +#: includes/facets/proximity.php:51 +msgid "mi" +msgstr "mi" + +#: includes/facets/number_range.php:26 +msgid "Min" +msgstr "Min" + +#: includes/integrations/acf/acf.php:325 +msgid "No" +msgstr "Nein" + +#: includes/facets/date_range.php:25 +msgid "No dates" +msgstr "" + +#: includes/facets/date_range.php:27 +msgid "No end dates" +msgstr "" + +#: includes/facets/autocomplete.php:99 includes/facets/autocomplete.php:166 +msgid "No results" +msgstr "Keine Ergebnisse" + +#: includes/class-display.php:227 includes/facets/fselect.php:87 +msgid "No results found" +msgstr "Keine Ergebnisse gefunden" + +#: includes/facets/date_range.php:26 +msgid "No start dates" +msgstr "" + +#: includes/facets/number_range.php:23 +msgid "Number" +msgstr "Nummer" + +#: includes/class-renderer.php:535 +msgid "of" +msgstr "von" + +#: includes/integrations/woocommerce/woocommerce.php:529 +msgid "On Sale" +msgstr "Im Angebot" + +#: includes/integrations/woocommerce/woocommerce.php:525 +msgid "Out of Stock" +msgstr "Ausverkauft" + +#: includes/class-renderer.php:592 +msgid "Per page" +msgstr "Pro Seite" + +#: includes/facets/map.php:400 includes/facets/reset.php:15 +#: includes/facets/slider.php:20 +msgid "Reset" +msgstr "Zurücksetzen" + +#: includes/facets/fselect.php:86 +msgid "Search" +msgstr "Suche" + +#: includes/facets/checkboxes.php:134 includes/facets/hierarchy.php:164 +msgid "See less" +msgstr "Weniger sehen" + +#: includes/facets/checkboxes.php:133 includes/facets/hierarchy.php:163 +msgid "See {num} more" +msgstr "{num} weitere anzeigen" + +#: includes/facets/sort.php:162 +msgid "Sort by" +msgstr "Sortieren nach" + +#: includes/facets/date_range.php:22 +msgid "Start date" +msgstr "Startdatum" + +#: includes/facets/autocomplete.php:63 +msgid "Start typing" +msgstr "Adresse eingeben" + +#: includes/facets/sort.php:166 +msgid "Title (A-Z)" +msgstr "Titel (A-Z)" + +#: includes/facets/sort.php:173 +msgid "Title (Z-A)" +msgstr "Titel (Z-A)" + +#: includes/facets/rating.php:166 +msgid "Undo" +msgstr "Rückgängig" + +#: includes/integrations/acf/acf.php:325 +msgid "Yes" +msgstr "Ja" + +#: includes/facets/fselect.php:85 +msgid "{n} selected" +msgstr "{n} ausgewählt" diff --git a/wp-content/plugins/facetwp/languages/fwp-front-es_AR.mo b/wp-content/plugins/facetwp/languages/fwp-front-es_AR.mo new file mode 100644 index 000000000..6773f8380 Binary files /dev/null and b/wp-content/plugins/facetwp/languages/fwp-front-es_AR.mo differ diff --git a/wp-content/plugins/facetwp/languages/fwp-front-es_AR.po b/wp-content/plugins/facetwp/languages/fwp-front-es_AR.po new file mode 100644 index 000000000..c94937a5c --- /dev/null +++ b/wp-content/plugins/facetwp/languages/fwp-front-es_AR.po @@ -0,0 +1,226 @@ +msgid "" +msgstr "" +"Project-Id-Version: FacetWP (front)\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-02-21 14:01+0000\n" +"PO-Revision-Date: 2025-04-18 10:46+0000\n" +"Last-Translator: \n" +"Language-Team: Spanish (Argentina)\n" +"Language: es-AR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Loco https://localise.biz/\n" +"X-Loco-Version: 2.7.1; wp-6.7.1\n" +"X-Poedit-SourceCharset: UTF-8\n" + +#: includes/facets/rating.php:165 +msgid "& up" +msgstr "& up" + +#. Description of the plugin +#: index.php +msgid "Advanced Filtering for WordPress" +msgstr "" + +#: includes/facets/dropdown.php:35 includes/facets/fselect.php:37 +#: includes/facets/fselect.php:80 includes/facets/hierarchy.php:26 +msgid "Any" +msgstr "Cualquier" + +#: includes/facets/date_range.php:300 +msgid "Clear" +msgstr "Limpiar" + +#: includes/facets/proximity.php:243 +msgid "Clear location" +msgstr "Borrar Ubicación" + +#: includes/facets/date_range.php:21 +msgid "Date" +msgstr "Fecha" + +#: includes/facets/sort.php:180 +msgid "Date (Newest)" +msgstr "Fecha (más reciente)" + +#: includes/facets/sort.php:187 +msgid "Date (Oldest)" +msgstr "Fecha (más antigua)" + +#: includes/facets/proximity.php:357 +msgid "Distance" +msgstr "Distancia" + +#: includes/facets/map.php:397 +msgid "Enable map filtering" +msgstr "" + +#: includes/facets/date_range.php:23 +msgid "End date" +msgstr "Último día" + +#: includes/facets/search.php:23 +msgid "Enter keywords" +msgstr "Ingrese palabras claves" + +#: includes/facets/proximity.php:52 +msgid "Enter location" +msgstr "Introducir ubicación" + +#: includes/facets/autocomplete.php:165 +msgid "Enter {n} or more characters" +msgstr "" + +#. Plugin Name of the plugin +#: index.php +msgid "FacetWP" +msgstr "" + +#. Author of the plugin +#: index.php +msgid "FacetWP, LLC" +msgstr "" + +#: includes/integrations/woocommerce/woocommerce.php:532 +msgid "Featured" +msgstr "" + +#: includes/facets/autocomplete.php:66 includes/facets/number_range.php:32 +msgid "Go" +msgstr "Ir" + +#: includes/class-display.php:208 +msgid "Go to next page" +msgstr "" + +#: includes/class-display.php:207 +msgid "Go to page" +msgstr "" + +#: includes/class-display.php:209 +msgid "Go to previous page" +msgstr "" + +#. Author URI of the plugin +#: index.php +msgid "https://facetwp.com/" +msgstr "" + +#: includes/integrations/woocommerce/woocommerce.php:522 +msgid "In Stock" +msgstr "En Stock" + +#: includes/facets/proximity.php:51 +msgid "km" +msgstr "" + +#: includes/facets/autocomplete.php:164 +msgid "Loading" +msgstr "" + +#: includes/facets/number_range.php:29 +msgid "Max" +msgstr "Max" + +#: includes/facets/proximity.php:51 +msgid "mi" +msgstr "" + +#: includes/facets/number_range.php:26 +msgid "Min" +msgstr "Minutos" + +#: includes/integrations/acf/acf.php:325 +msgid "No" +msgstr "No" + +#: includes/facets/date_range.php:25 +msgid "No dates" +msgstr "" + +#: includes/facets/date_range.php:27 +msgid "No end dates" +msgstr "" + +#: includes/facets/autocomplete.php:99 includes/facets/autocomplete.php:166 +msgid "No results" +msgstr "No hay resultados" + +#: includes/class-display.php:227 includes/facets/fselect.php:87 +msgid "No results found" +msgstr "No se encontraron resultados" + +#: includes/facets/date_range.php:26 +msgid "No start dates" +msgstr "" + +#: includes/facets/number_range.php:23 +msgid "Number" +msgstr "Número" + +#: includes/class-renderer.php:535 +msgid "of" +msgstr "de" + +#: includes/integrations/woocommerce/woocommerce.php:529 +msgid "On Sale" +msgstr "A la venta" + +#: includes/integrations/woocommerce/woocommerce.php:525 +msgid "Out of Stock" +msgstr "Fuera de Stock" + +#: includes/class-renderer.php:592 +msgid "Per page" +msgstr "Por página" + +#: includes/facets/map.php:400 includes/facets/reset.php:15 +#: includes/facets/slider.php:20 +msgid "Reset" +msgstr "Reiniciar" + +#: includes/facets/fselect.php:86 +msgid "Search" +msgstr "Buscar" + +#: includes/facets/checkboxes.php:134 includes/facets/hierarchy.php:164 +msgid "See less" +msgstr "Ver menos" + +#: includes/facets/checkboxes.php:133 includes/facets/hierarchy.php:163 +msgid "See {num} more" +msgstr "Ver {num} más" + +#: includes/facets/sort.php:162 +msgid "Sort by" +msgstr "Ordenar por" + +#: includes/facets/date_range.php:22 +msgid "Start date" +msgstr "Fecha de Comienzo" + +#: includes/facets/autocomplete.php:63 +msgid "Start typing" +msgstr "Comience a escribir" + +#: includes/facets/sort.php:166 +msgid "Title (A-Z)" +msgstr "Título (A-Z)" + +#: includes/facets/sort.php:173 +msgid "Title (Z-A)" +msgstr "Título (Z-A)" + +#: includes/facets/rating.php:166 +msgid "Undo" +msgstr "Deshacer" + +#: includes/integrations/acf/acf.php:325 +msgid "Yes" +msgstr "Sí" + +#: includes/facets/fselect.php:85 +msgid "{n} selected" +msgstr "{n} selecciones" diff --git a/wp-content/plugins/facetwp/languages/fwp-front-es_ES.mo b/wp-content/plugins/facetwp/languages/fwp-front-es_ES.mo new file mode 100644 index 000000000..8b509b618 Binary files /dev/null and b/wp-content/plugins/facetwp/languages/fwp-front-es_ES.mo differ diff --git a/wp-content/plugins/facetwp/languages/fwp-front-es_ES.po b/wp-content/plugins/facetwp/languages/fwp-front-es_ES.po new file mode 100644 index 000000000..2616aa6fe --- /dev/null +++ b/wp-content/plugins/facetwp/languages/fwp-front-es_ES.po @@ -0,0 +1,226 @@ +msgid "" +msgstr "" +"Project-Id-Version: FacetWP (front)\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-02-21 14:01+0000\n" +"PO-Revision-Date: 2025-04-18 10:47+0000\n" +"Last-Translator: \n" +"Language-Team: Spanish (Spain)\n" +"Language: es-ES\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Loco https://localise.biz/\n" +"X-Loco-Version: 2.7.1; wp-6.7.1\n" +"X-Poedit-SourceCharset: UTF-8\n" + +#: includes/facets/rating.php:165 +msgid "& up" +msgstr "& up" + +#. Description of the plugin +#: index.php +msgid "Advanced Filtering for WordPress" +msgstr "" + +#: includes/facets/dropdown.php:35 includes/facets/fselect.php:37 +#: includes/facets/fselect.php:80 includes/facets/hierarchy.php:26 +msgid "Any" +msgstr "Cualquiera" + +#: includes/facets/date_range.php:300 +msgid "Clear" +msgstr "Limpiar" + +#: includes/facets/proximity.php:243 +msgid "Clear location" +msgstr "Borrar ubicación" + +#: includes/facets/date_range.php:21 +msgid "Date" +msgstr "Fecha" + +#: includes/facets/sort.php:180 +msgid "Date (Newest)" +msgstr "Fecha (más reciente)" + +#: includes/facets/sort.php:187 +msgid "Date (Oldest)" +msgstr "Fecha (más antigua)" + +#: includes/facets/proximity.php:357 +msgid "Distance" +msgstr "Distancia" + +#: includes/facets/map.php:397 +msgid "Enable map filtering" +msgstr "" + +#: includes/facets/date_range.php:23 +msgid "End date" +msgstr "Fecha de finalización" + +#: includes/facets/search.php:23 +msgid "Enter keywords" +msgstr "¿Qué estás buscando?" + +#: includes/facets/proximity.php:52 +msgid "Enter location" +msgstr "Introducir ubicación" + +#: includes/facets/autocomplete.php:165 +msgid "Enter {n} or more characters" +msgstr "" + +#. Plugin Name of the plugin +#: index.php +msgid "FacetWP" +msgstr "" + +#. Author of the plugin +#: index.php +msgid "FacetWP, LLC" +msgstr "" + +#: includes/integrations/woocommerce/woocommerce.php:532 +msgid "Featured" +msgstr "" + +#: includes/facets/autocomplete.php:66 includes/facets/number_range.php:32 +msgid "Go" +msgstr "Ir" + +#: includes/class-display.php:208 +msgid "Go to next page" +msgstr "" + +#: includes/class-display.php:207 +msgid "Go to page" +msgstr "" + +#: includes/class-display.php:209 +msgid "Go to previous page" +msgstr "" + +#. Author URI of the plugin +#: index.php +msgid "https://facetwp.com/" +msgstr "" + +#: includes/integrations/woocommerce/woocommerce.php:522 +msgid "In Stock" +msgstr "En Stock" + +#: includes/facets/proximity.php:51 +msgid "km" +msgstr "" + +#: includes/facets/autocomplete.php:164 +msgid "Loading" +msgstr "" + +#: includes/facets/number_range.php:29 +msgid "Max" +msgstr "Máx" + +#: includes/facets/proximity.php:51 +msgid "mi" +msgstr "" + +#: includes/facets/number_range.php:26 +msgid "Min" +msgstr "Mín" + +#: includes/integrations/acf/acf.php:325 +msgid "No" +msgstr "No" + +#: includes/facets/date_range.php:25 +msgid "No dates" +msgstr "" + +#: includes/facets/date_range.php:27 +msgid "No end dates" +msgstr "" + +#: includes/facets/autocomplete.php:99 includes/facets/autocomplete.php:166 +msgid "No results" +msgstr "No hay resultados" + +#: includes/class-display.php:227 includes/facets/fselect.php:87 +msgid "No results found" +msgstr "No se han encontrado resultados" + +#: includes/facets/date_range.php:26 +msgid "No start dates" +msgstr "" + +#: includes/facets/number_range.php:23 +msgid "Number" +msgstr "Número" + +#: includes/class-renderer.php:535 +msgid "of" +msgstr "de" + +#: includes/integrations/woocommerce/woocommerce.php:529 +msgid "On Sale" +msgstr "En oferta" + +#: includes/integrations/woocommerce/woocommerce.php:525 +msgid "Out of Stock" +msgstr "Agotado" + +#: includes/class-renderer.php:592 +msgid "Per page" +msgstr "Por página" + +#: includes/facets/map.php:400 includes/facets/reset.php:15 +#: includes/facets/slider.php:20 +msgid "Reset" +msgstr "Reiniciar" + +#: includes/facets/fselect.php:86 +msgid "Search" +msgstr "Buscar" + +#: includes/facets/checkboxes.php:134 includes/facets/hierarchy.php:164 +msgid "See less" +msgstr "Ver menos" + +#: includes/facets/checkboxes.php:133 includes/facets/hierarchy.php:163 +msgid "See {num} more" +msgstr "Ver {num} más" + +#: includes/facets/sort.php:162 +msgid "Sort by" +msgstr "Ordenar por" + +#: includes/facets/date_range.php:22 +msgid "Start date" +msgstr "Fecha de inicio" + +#: includes/facets/autocomplete.php:63 +msgid "Start typing" +msgstr "Escribe algo" + +#: includes/facets/sort.php:166 +msgid "Title (A-Z)" +msgstr "Título (A-Z)" + +#: includes/facets/sort.php:173 +msgid "Title (Z-A)" +msgstr "Título (Z-A)" + +#: includes/facets/rating.php:166 +msgid "Undo" +msgstr "Deshacer" + +#: includes/integrations/acf/acf.php:325 +msgid "Yes" +msgstr "Sí" + +#: includes/facets/fselect.php:85 +msgid "{n} selected" +msgstr "{n} selecciones" diff --git a/wp-content/plugins/facetwp/languages/fwp-front-fr_FR.mo b/wp-content/plugins/facetwp/languages/fwp-front-fr_FR.mo new file mode 100644 index 000000000..41f266dbf Binary files /dev/null and b/wp-content/plugins/facetwp/languages/fwp-front-fr_FR.mo differ diff --git a/wp-content/plugins/facetwp/languages/fwp-front-fr_FR.po b/wp-content/plugins/facetwp/languages/fwp-front-fr_FR.po new file mode 100644 index 000000000..62af9f58c --- /dev/null +++ b/wp-content/plugins/facetwp/languages/fwp-front-fr_FR.po @@ -0,0 +1,226 @@ +msgid "" +msgstr "" +"Project-Id-Version: FacetWP (front)\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-02-21 14:01+0000\n" +"PO-Revision-Date: 2025-04-18 10:47+0000\n" +"Last-Translator: \n" +"Language-Team: French (France)\n" +"Language: fr-FR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"X-Generator: Loco https://localise.biz/\n" +"X-Loco-Version: 2.7.1; wp-6.7.1\n" +"X-Poedit-SourceCharset: UTF-8\n" + +#: includes/facets/rating.php:165 +msgid "& up" +msgstr "& up" + +#. Description of the plugin +#: index.php +msgid "Advanced Filtering for WordPress" +msgstr "" + +#: includes/facets/dropdown.php:35 includes/facets/fselect.php:37 +#: includes/facets/fselect.php:80 includes/facets/hierarchy.php:26 +msgid "Any" +msgstr "Tous" + +#: includes/facets/date_range.php:300 +msgid "Clear" +msgstr "Effacer" + +#: includes/facets/proximity.php:243 +msgid "Clear location" +msgstr "Effacer l’emplacement" + +#: includes/facets/date_range.php:21 +msgid "Date" +msgstr "Date" + +#: includes/facets/sort.php:180 +msgid "Date (Newest)" +msgstr "Date (plus récent)" + +#: includes/facets/sort.php:187 +msgid "Date (Oldest)" +msgstr "Date (la plus ancienne)" + +#: includes/facets/proximity.php:357 +msgid "Distance" +msgstr "Distance" + +#: includes/facets/map.php:397 +msgid "Enable map filtering" +msgstr "" + +#: includes/facets/date_range.php:23 +msgid "End date" +msgstr "Date de fin" + +#: includes/facets/search.php:23 +msgid "Enter keywords" +msgstr "Rechercher par nom" + +#: includes/facets/proximity.php:52 +msgid "Enter location" +msgstr "Entrez la localisation" + +#: includes/facets/autocomplete.php:165 +msgid "Enter {n} or more characters" +msgstr "" + +#. Plugin Name of the plugin +#: index.php +msgid "FacetWP" +msgstr "" + +#. Author of the plugin +#: index.php +msgid "FacetWP, LLC" +msgstr "" + +#: includes/integrations/woocommerce/woocommerce.php:532 +msgid "Featured" +msgstr "" + +#: includes/facets/autocomplete.php:66 includes/facets/number_range.php:32 +msgid "Go" +msgstr "Aller" + +#: includes/class-display.php:208 +msgid "Go to next page" +msgstr "" + +#: includes/class-display.php:207 +msgid "Go to page" +msgstr "" + +#: includes/class-display.php:209 +msgid "Go to previous page" +msgstr "" + +#. Author URI of the plugin +#: index.php +msgid "https://facetwp.com/" +msgstr "" + +#: includes/integrations/woocommerce/woocommerce.php:522 +msgid "In Stock" +msgstr "En stock" + +#: includes/facets/proximity.php:51 +msgid "km" +msgstr "" + +#: includes/facets/autocomplete.php:164 +msgid "Loading" +msgstr "" + +#: includes/facets/number_range.php:29 +msgid "Max" +msgstr "Max" + +#: includes/facets/proximity.php:51 +msgid "mi" +msgstr "" + +#: includes/facets/number_range.php:26 +msgid "Min" +msgstr "Min" + +#: includes/integrations/acf/acf.php:325 +msgid "No" +msgstr "Non" + +#: includes/facets/date_range.php:25 +msgid "No dates" +msgstr "" + +#: includes/facets/date_range.php:27 +msgid "No end dates" +msgstr "" + +#: includes/facets/autocomplete.php:99 includes/facets/autocomplete.php:166 +msgid "No results" +msgstr "Aucun résultat" + +#: includes/class-display.php:227 includes/facets/fselect.php:87 +msgid "No results found" +msgstr "Aucun résultat trouvé" + +#: includes/facets/date_range.php:26 +msgid "No start dates" +msgstr "" + +#: includes/facets/number_range.php:23 +msgid "Number" +msgstr "Nombre" + +#: includes/class-renderer.php:535 +msgid "of" +msgstr "de" + +#: includes/integrations/woocommerce/woocommerce.php:529 +msgid "On Sale" +msgstr "Promo" + +#: includes/integrations/woocommerce/woocommerce.php:525 +msgid "Out of Stock" +msgstr "Rupture de stock" + +#: includes/class-renderer.php:592 +msgid "Per page" +msgstr "Par page" + +#: includes/facets/map.php:400 includes/facets/reset.php:15 +#: includes/facets/slider.php:20 +msgid "Reset" +msgstr "Réinitialiser" + +#: includes/facets/fselect.php:86 +msgid "Search" +msgstr "Rechercher" + +#: includes/facets/checkboxes.php:134 includes/facets/hierarchy.php:164 +msgid "See less" +msgstr "Voir moins" + +#: includes/facets/checkboxes.php:133 includes/facets/hierarchy.php:163 +msgid "See {num} more" +msgstr "Voir {num} plus" + +#: includes/facets/sort.php:162 +msgid "Sort by" +msgstr "Trier par" + +#: includes/facets/date_range.php:22 +msgid "Start date" +msgstr "Date de début" + +#: includes/facets/autocomplete.php:63 +msgid "Start typing" +msgstr "Commencer à taper" + +#: includes/facets/sort.php:166 +msgid "Title (A-Z)" +msgstr "Titre (A-Z)" + +#: includes/facets/sort.php:173 +msgid "Title (Z-A)" +msgstr "Titre (Z-A)" + +#: includes/facets/rating.php:166 +msgid "Undo" +msgstr "Annuler" + +#: includes/integrations/acf/acf.php:325 +msgid "Yes" +msgstr "Oui" + +#: includes/facets/fselect.php:85 +msgid "{n} selected" +msgstr "{n} sélectionné" diff --git a/wp-content/plugins/facetwp/languages/fwp-front-it_IT.mo b/wp-content/plugins/facetwp/languages/fwp-front-it_IT.mo new file mode 100644 index 000000000..7907c7398 Binary files /dev/null and b/wp-content/plugins/facetwp/languages/fwp-front-it_IT.mo differ diff --git a/wp-content/plugins/facetwp/languages/fwp-front-it_IT.po b/wp-content/plugins/facetwp/languages/fwp-front-it_IT.po new file mode 100644 index 000000000..23e3d83a3 --- /dev/null +++ b/wp-content/plugins/facetwp/languages/fwp-front-it_IT.po @@ -0,0 +1,226 @@ +msgid "" +msgstr "" +"Project-Id-Version: FacetWP (front)\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-02-21 14:01+0000\n" +"PO-Revision-Date: 2025-04-18 10:48+0000\n" +"Last-Translator: \n" +"Language-Team: Italian\n" +"Language: it-IT\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Loco https://localise.biz/\n" +"X-Loco-Version: 2.7.1; wp-6.7.1\n" +"X-Poedit-SourceCharset: UTF-8\n" + +#: includes/facets/rating.php:165 +msgid "& up" +msgstr "& up" + +#. Description of the plugin +#: index.php +msgid "Advanced Filtering for WordPress" +msgstr "" + +#: includes/facets/dropdown.php:35 includes/facets/fselect.php:37 +#: includes/facets/fselect.php:80 includes/facets/hierarchy.php:26 +msgid "Any" +msgstr "Qualsiasi" + +#: includes/facets/date_range.php:300 +msgid "Clear" +msgstr "Cancella" + +#: includes/facets/proximity.php:243 +msgid "Clear location" +msgstr "Cancella località" + +#: includes/facets/date_range.php:21 +msgid "Date" +msgstr "Data" + +#: includes/facets/sort.php:180 +msgid "Date (Newest)" +msgstr "Data (più recente)" + +#: includes/facets/sort.php:187 +msgid "Date (Oldest)" +msgstr "Data (più vecchia)" + +#: includes/facets/proximity.php:357 +msgid "Distance" +msgstr "Distanza" + +#: includes/facets/map.php:397 +msgid "Enable map filtering" +msgstr "" + +#: includes/facets/date_range.php:23 +msgid "End date" +msgstr "Data Fine" + +#: includes/facets/search.php:23 +msgid "Enter keywords" +msgstr "Inserisci parole chiave" + +#: includes/facets/proximity.php:52 +msgid "Enter location" +msgstr "Inserisci posizione" + +#: includes/facets/autocomplete.php:165 +msgid "Enter {n} or more characters" +msgstr "" + +#. Plugin Name of the plugin +#: index.php +msgid "FacetWP" +msgstr "" + +#. Author of the plugin +#: index.php +msgid "FacetWP, LLC" +msgstr "" + +#: includes/integrations/woocommerce/woocommerce.php:532 +msgid "Featured" +msgstr "" + +#: includes/facets/autocomplete.php:66 includes/facets/number_range.php:32 +msgid "Go" +msgstr "Vai" + +#: includes/class-display.php:208 +msgid "Go to next page" +msgstr "" + +#: includes/class-display.php:207 +msgid "Go to page" +msgstr "" + +#: includes/class-display.php:209 +msgid "Go to previous page" +msgstr "" + +#. Author URI of the plugin +#: index.php +msgid "https://facetwp.com/" +msgstr "" + +#: includes/integrations/woocommerce/woocommerce.php:522 +msgid "In Stock" +msgstr "Disponibile" + +#: includes/facets/proximity.php:51 +msgid "km" +msgstr "" + +#: includes/facets/autocomplete.php:164 +msgid "Loading" +msgstr "" + +#: includes/facets/number_range.php:29 +msgid "Max" +msgstr "Max" + +#: includes/facets/proximity.php:51 +msgid "mi" +msgstr "" + +#: includes/facets/number_range.php:26 +msgid "Min" +msgstr "Min" + +#: includes/integrations/acf/acf.php:325 +msgid "No" +msgstr "No" + +#: includes/facets/date_range.php:25 +msgid "No dates" +msgstr "" + +#: includes/facets/date_range.php:27 +msgid "No end dates" +msgstr "" + +#: includes/facets/autocomplete.php:99 includes/facets/autocomplete.php:166 +msgid "No results" +msgstr "Nessun risultato" + +#: includes/class-display.php:227 includes/facets/fselect.php:87 +msgid "No results found" +msgstr "Nessun risultato trovato" + +#: includes/facets/date_range.php:26 +msgid "No start dates" +msgstr "" + +#: includes/facets/number_range.php:23 +msgid "Number" +msgstr "Numero" + +#: includes/class-renderer.php:535 +msgid "of" +msgstr "di" + +#: includes/integrations/woocommerce/woocommerce.php:529 +msgid "On Sale" +msgstr "Offerta" + +#: includes/integrations/woocommerce/woocommerce.php:525 +msgid "Out of Stock" +msgstr "Esaurito" + +#: includes/class-renderer.php:592 +msgid "Per page" +msgstr "Per pagina" + +#: includes/facets/map.php:400 includes/facets/reset.php:15 +#: includes/facets/slider.php:20 +msgid "Reset" +msgstr "Reset" + +#: includes/facets/fselect.php:86 +msgid "Search" +msgstr "Cerca" + +#: includes/facets/checkboxes.php:134 includes/facets/hierarchy.php:164 +msgid "See less" +msgstr "Vedi di meno" + +#: includes/facets/checkboxes.php:133 includes/facets/hierarchy.php:163 +msgid "See {num} more" +msgstr "Vedi altri {num}" + +#: includes/facets/sort.php:162 +msgid "Sort by" +msgstr "Ordina per" + +#: includes/facets/date_range.php:22 +msgid "Start date" +msgstr "Data Inizio" + +#: includes/facets/autocomplete.php:63 +msgid "Start typing" +msgstr "Scrivi l'indirizzo o la città qui" + +#: includes/facets/sort.php:166 +msgid "Title (A-Z)" +msgstr "Titoli (A-Z)" + +#: includes/facets/sort.php:173 +msgid "Title (Z-A)" +msgstr "Titoli (Z-A)" + +#: includes/facets/rating.php:166 +msgid "Undo" +msgstr "Annulla" + +#: includes/integrations/acf/acf.php:325 +msgid "Yes" +msgstr "Sì" + +#: includes/facets/fselect.php:85 +msgid "{n} selected" +msgstr "{n} selezionato" diff --git a/wp-content/plugins/facetwp/languages/fwp-front-nb_NO.mo b/wp-content/plugins/facetwp/languages/fwp-front-nb_NO.mo new file mode 100644 index 000000000..9b99ea848 Binary files /dev/null and b/wp-content/plugins/facetwp/languages/fwp-front-nb_NO.mo differ diff --git a/wp-content/plugins/facetwp/languages/fwp-front-nb_NO.po b/wp-content/plugins/facetwp/languages/fwp-front-nb_NO.po new file mode 100644 index 000000000..7bcde227e --- /dev/null +++ b/wp-content/plugins/facetwp/languages/fwp-front-nb_NO.po @@ -0,0 +1,227 @@ +# Translation of Plugin: FacetWP - FacetWP frontend in Norwegian (Bokmål) +# This file is distributed under the same license as the Plugin: FacetWP - FacetWP frontend package. +msgid "" +msgstr "" +"PO-Revision-Date: 2025-04-18 10:49+0000\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Loco https://localise.biz/\n" +"Language: nb-NO\n" +"Project-Id-Version: Plugin: FacetWP - FacetWP frontend\n" +"Language-Team: Norwegian (Bokmål)\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-04-18 10:49+0000\n" +"Last-Translator: \n" +"X-Loco-Version: 2.7.1; wp-6.7.1" + +#: includes/facets/rating.php:165 +msgid "& up" +msgstr "& opp" + +#. Description of the plugin +#: index.php +msgid "Advanced Filtering for WordPress" +msgstr "" + +#: includes/facets/dropdown.php:35 includes/facets/fselect.php:37 +#: includes/facets/fselect.php:80 includes/facets/hierarchy.php:26 +msgid "Any" +msgstr "Noen" + +#: includes/facets/date_range.php:300 +msgid "Clear" +msgstr "Tøm" + +#: includes/facets/proximity.php:243 +msgid "Clear location" +msgstr "Tøm sted" + +#: includes/facets/date_range.php:21 +msgid "Date" +msgstr "Dato" + +#: includes/facets/sort.php:180 +msgid "Date (Newest)" +msgstr "Dato (nyeste)" + +#: includes/facets/sort.php:187 +msgid "Date (Oldest)" +msgstr "Dato (eldste)" + +#: includes/facets/proximity.php:357 +msgid "Distance" +msgstr "Avstand" + +#: includes/facets/map.php:397 +msgid "Enable map filtering" +msgstr "" + +#: includes/facets/date_range.php:23 +msgid "End date" +msgstr "Sluttdato" + +#: includes/facets/search.php:23 +msgid "Enter keywords" +msgstr "Skriv inn søkeord" + +#: includes/facets/proximity.php:52 +msgid "Enter location" +msgstr "Angi plassering" + +#: includes/facets/autocomplete.php:165 +msgid "Enter {n} or more characters" +msgstr "Skriv inn {n} eller flere tegn" + +#. Plugin Name of the plugin +#: index.php +msgid "FacetWP" +msgstr "" + +#. Author of the plugin +#: index.php +msgid "FacetWP, LLC" +msgstr "" + +#: includes/integrations/woocommerce/woocommerce.php:532 +msgid "Featured" +msgstr "Utvalgte" + +#: includes/facets/autocomplete.php:66 includes/facets/number_range.php:32 +msgid "Go" +msgstr "Gå" + +#: includes/class-display.php:208 +msgid "Go to next page" +msgstr "" + +#: includes/class-display.php:207 +msgid "Go to page" +msgstr "" + +#: includes/class-display.php:209 +msgid "Go to previous page" +msgstr "" + +#. Author URI of the plugin +#: index.php +msgid "https://facetwp.com/" +msgstr "" + +#: includes/integrations/woocommerce/woocommerce.php:522 +msgid "In Stock" +msgstr "På lager" + +#: includes/facets/proximity.php:51 +msgid "km" +msgstr "" + +#: includes/facets/autocomplete.php:164 +msgid "Loading" +msgstr "Laster" + +#: includes/facets/number_range.php:29 +msgid "Max" +msgstr "Maks" + +#: includes/facets/proximity.php:51 +msgid "mi" +msgstr "" + +#: includes/facets/number_range.php:26 +msgid "Min" +msgstr "Min" + +#: includes/integrations/acf/acf.php:325 +msgid "No" +msgstr "Nei" + +#: includes/facets/date_range.php:25 +msgid "No dates" +msgstr "" + +#: includes/facets/date_range.php:27 +msgid "No end dates" +msgstr "" + +#: includes/facets/autocomplete.php:99 includes/facets/autocomplete.php:166 +msgid "No results" +msgstr "Ingen resultater" + +#: includes/class-display.php:227 includes/facets/fselect.php:87 +msgid "No results found" +msgstr "Ingen resultater funnet" + +#: includes/facets/date_range.php:26 +msgid "No start dates" +msgstr "" + +#: includes/facets/number_range.php:23 +msgid "Number" +msgstr "Nummer" + +#: includes/class-renderer.php:535 +msgid "of" +msgstr "av" + +#: includes/integrations/woocommerce/woocommerce.php:529 +msgid "On Sale" +msgstr "På salg" + +#: includes/integrations/woocommerce/woocommerce.php:525 +msgid "Out of Stock" +msgstr "Ikke på lager" + +#: includes/class-renderer.php:592 +msgid "Per page" +msgstr "Per side" + +#: includes/facets/map.php:400 includes/facets/reset.php:15 +#: includes/facets/slider.php:20 +msgid "Reset" +msgstr "Tilbakestill" + +#: includes/facets/fselect.php:86 +msgid "Search" +msgstr "Søk" + +#: includes/facets/checkboxes.php:134 includes/facets/hierarchy.php:164 +msgid "See less" +msgstr "Se mindre" + +#: includes/facets/checkboxes.php:133 includes/facets/hierarchy.php:163 +msgid "See {num} more" +msgstr "Se {num} mer" + +#: includes/facets/sort.php:162 +msgid "Sort by" +msgstr "Sorter etter" + +#: includes/facets/date_range.php:22 +msgid "Start date" +msgstr "Startdato" + +#: includes/facets/autocomplete.php:63 +msgid "Start typing" +msgstr "Begynner å skrive" + +#: includes/facets/sort.php:166 +msgid "Title (A-Z)" +msgstr "Tittel (A-Z)" + +#: includes/facets/sort.php:173 +msgid "Title (Z-A)" +msgstr "Tittel (Z-A)" + +#: includes/facets/rating.php:166 +msgid "Undo" +msgstr "Angre" + +#: includes/integrations/acf/acf.php:325 +msgid "Yes" +msgstr "Ja" + +#: includes/facets/fselect.php:85 +msgid "{n} selected" +msgstr "{n} valgt" diff --git a/wp-content/plugins/facetwp/languages/fwp-front-nl_NL.mo b/wp-content/plugins/facetwp/languages/fwp-front-nl_NL.mo new file mode 100644 index 000000000..812dd4f11 Binary files /dev/null and b/wp-content/plugins/facetwp/languages/fwp-front-nl_NL.mo differ diff --git a/wp-content/plugins/facetwp/languages/fwp-front-nl_NL.po b/wp-content/plugins/facetwp/languages/fwp-front-nl_NL.po new file mode 100644 index 000000000..3c85b6e06 --- /dev/null +++ b/wp-content/plugins/facetwp/languages/fwp-front-nl_NL.po @@ -0,0 +1,226 @@ +msgid "" +msgstr "" +"Project-Id-Version: FacetWP (front)\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-02-21 14:01+0000\n" +"PO-Revision-Date: 2025-04-18 10:22+0000\n" +"Last-Translator: \n" +"Language-Team: Dutch\n" +"Language: nl-NL\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Loco https://localise.biz/\n" +"X-Loco-Version: 2.7.1; wp-6.7.1\n" +"X-Poedit-SourceCharset: UTF-8\n" + +#: includes/facets/rating.php:165 +msgid "& up" +msgstr "& hoger" + +#. Description of the plugin +#: index.php +msgid "Advanced Filtering for WordPress" +msgstr "" + +#: includes/facets/dropdown.php:35 includes/facets/fselect.php:37 +#: includes/facets/fselect.php:80 includes/facets/hierarchy.php:26 +msgid "Any" +msgstr "Alle" + +#: includes/facets/date_range.php:300 +msgid "Clear" +msgstr "Wissen" + +#: includes/facets/proximity.php:243 +msgid "Clear location" +msgstr "Verwijder locatie" + +#: includes/facets/date_range.php:21 +msgid "Date" +msgstr "Datum" + +#: includes/facets/sort.php:180 +msgid "Date (Newest)" +msgstr "Datum (nieuwste)" + +#: includes/facets/sort.php:187 +msgid "Date (Oldest)" +msgstr "Datum (oudste)" + +#: includes/facets/proximity.php:357 +msgid "Distance" +msgstr "Afstand" + +#: includes/facets/map.php:397 +msgid "Enable map filtering" +msgstr "Filter met kaart" + +#: includes/facets/date_range.php:23 +msgid "End date" +msgstr "Einddatum" + +#: includes/facets/search.php:23 +msgid "Enter keywords" +msgstr "Voer zoektermen in" + +#: includes/facets/proximity.php:52 +msgid "Enter location" +msgstr "Vul locatie in" + +#: includes/facets/autocomplete.php:165 +msgid "Enter {n} or more characters" +msgstr "Vul {n} of meer letters in" + +#. Plugin Name of the plugin +#: index.php +msgid "FacetWP" +msgstr "" + +#. Author of the plugin +#: index.php +msgid "FacetWP, LLC" +msgstr "" + +#: includes/integrations/woocommerce/woocommerce.php:532 +msgid "Featured" +msgstr "" + +#: includes/facets/autocomplete.php:66 includes/facets/number_range.php:32 +msgid "Go" +msgstr "Ga" + +#: includes/class-display.php:208 +msgid "Go to next page" +msgstr "Naar volgende pagina" + +#: includes/class-display.php:207 +msgid "Go to page" +msgstr "Naar pagina" + +#: includes/class-display.php:209 +msgid "Go to previous page" +msgstr "Naar vorige pagina" + +#. Author URI of the plugin +#: index.php +msgid "https://facetwp.com/" +msgstr "" + +#: includes/integrations/woocommerce/woocommerce.php:522 +msgid "In Stock" +msgstr "Op voorraad" + +#: includes/facets/proximity.php:51 +msgid "km" +msgstr "km" + +#: includes/facets/autocomplete.php:164 +msgid "Loading" +msgstr "Laden" + +#: includes/facets/number_range.php:29 +msgid "Max" +msgstr "Max" + +#: includes/facets/proximity.php:51 +msgid "mi" +msgstr "mi" + +#: includes/facets/number_range.php:26 +msgid "Min" +msgstr "Min" + +#: includes/integrations/acf/acf.php:325 +msgid "No" +msgstr "Nee" + +#: includes/facets/date_range.php:25 +msgid "No dates" +msgstr "Geen data" + +#: includes/facets/date_range.php:27 +msgid "No end dates" +msgstr "Geen einddata" + +#: includes/facets/autocomplete.php:99 includes/facets/autocomplete.php:166 +msgid "No results" +msgstr "Niets gevonden" + +#: includes/class-display.php:227 includes/facets/fselect.php:87 +msgid "No results found" +msgstr "Niets gevonden" + +#: includes/facets/date_range.php:26 +msgid "No start dates" +msgstr "Geen start data" + +#: includes/facets/number_range.php:23 +msgid "Number" +msgstr "Nummer" + +#: includes/class-renderer.php:535 +msgid "of" +msgstr "van" + +#: includes/integrations/woocommerce/woocommerce.php:529 +msgid "On Sale" +msgstr "Aanbieding" + +#: includes/integrations/woocommerce/woocommerce.php:525 +msgid "Out of Stock" +msgstr "Niet op voorraad" + +#: includes/class-renderer.php:592 +msgid "Per page" +msgstr "Per pagina" + +#: includes/facets/map.php:400 includes/facets/reset.php:15 +#: includes/facets/slider.php:20 +msgid "Reset" +msgstr "Reset" + +#: includes/facets/fselect.php:86 +msgid "Search" +msgstr "Zoeken" + +#: includes/facets/checkboxes.php:134 includes/facets/hierarchy.php:164 +msgid "See less" +msgstr "Toon minder" + +#: includes/facets/checkboxes.php:133 includes/facets/hierarchy.php:163 +msgid "See {num} more" +msgstr "Toon {num} meer" + +#: includes/facets/sort.php:162 +msgid "Sort by" +msgstr "Sorteer op" + +#: includes/facets/date_range.php:22 +msgid "Start date" +msgstr "Start datum" + +#: includes/facets/autocomplete.php:63 +msgid "Start typing" +msgstr "Start met typen" + +#: includes/facets/sort.php:166 +msgid "Title (A-Z)" +msgstr "Titel (A-Z)" + +#: includes/facets/sort.php:173 +msgid "Title (Z-A)" +msgstr "Titel (Z-A)" + +#: includes/facets/rating.php:166 +msgid "Undo" +msgstr "Undo" + +#: includes/integrations/acf/acf.php:325 +msgid "Yes" +msgstr "Ja" + +#: includes/facets/fselect.php:85 +msgid "{n} selected" +msgstr "{n} geselecteerd" diff --git a/wp-content/plugins/facetwp/languages/fwp-front-pl_PL.mo b/wp-content/plugins/facetwp/languages/fwp-front-pl_PL.mo new file mode 100644 index 000000000..494a55e08 Binary files /dev/null and b/wp-content/plugins/facetwp/languages/fwp-front-pl_PL.mo differ diff --git a/wp-content/plugins/facetwp/languages/fwp-front-pl_PL.po b/wp-content/plugins/facetwp/languages/fwp-front-pl_PL.po new file mode 100644 index 000000000..c699452c9 --- /dev/null +++ b/wp-content/plugins/facetwp/languages/fwp-front-pl_PL.po @@ -0,0 +1,227 @@ +msgid "" +msgstr "" +"Project-Id-Version: FacetWP (front)\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-02-21 14:01+0000\n" +"PO-Revision-Date: 2025-04-18 10:50+0000\n" +"Last-Translator: \n" +"Language-Team: Polish\n" +"Language: pl-PL\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 " +"|| n%100>14) ? 1 : 2);\n" +"X-Generator: Loco https://localise.biz/\n" +"X-Loco-Version: 2.7.1; wp-6.7.1\n" +"X-Poedit-SourceCharset: UTF-8\n" + +#: includes/facets/rating.php:165 +msgid "& up" +msgstr "& się" + +#. Description of the plugin +#: index.php +msgid "Advanced Filtering for WordPress" +msgstr "" + +#: includes/facets/dropdown.php:35 includes/facets/fselect.php:37 +#: includes/facets/fselect.php:80 includes/facets/hierarchy.php:26 +msgid "Any" +msgstr "Dowolny" + +#: includes/facets/date_range.php:300 +msgid "Clear" +msgstr "Wyczyść" + +#: includes/facets/proximity.php:243 +msgid "Clear location" +msgstr "Wyczyść lokalizację" + +#: includes/facets/date_range.php:21 +msgid "Date" +msgstr "Data" + +#: includes/facets/sort.php:180 +msgid "Date (Newest)" +msgstr "Daty (Najnowsze)" + +#: includes/facets/sort.php:187 +msgid "Date (Oldest)" +msgstr "Daty (Najstarsze)" + +#: includes/facets/proximity.php:357 +msgid "Distance" +msgstr "Odległość" + +#: includes/facets/map.php:397 +msgid "Enable map filtering" +msgstr "" + +#: includes/facets/date_range.php:23 +msgid "End date" +msgstr "Data zakończenia" + +#: includes/facets/search.php:23 +msgid "Enter keywords" +msgstr "Wprowadź słowa kluczowe" + +#: includes/facets/proximity.php:52 +msgid "Enter location" +msgstr "Wprowadź lokalizację" + +#: includes/facets/autocomplete.php:165 +msgid "Enter {n} or more characters" +msgstr "" + +#. Plugin Name of the plugin +#: index.php +msgid "FacetWP" +msgstr "" + +#. Author of the plugin +#: index.php +msgid "FacetWP, LLC" +msgstr "" + +#: includes/integrations/woocommerce/woocommerce.php:532 +msgid "Featured" +msgstr "" + +#: includes/facets/autocomplete.php:66 includes/facets/number_range.php:32 +msgid "Go" +msgstr "Idź" + +#: includes/class-display.php:208 +msgid "Go to next page" +msgstr "" + +#: includes/class-display.php:207 +msgid "Go to page" +msgstr "" + +#: includes/class-display.php:209 +msgid "Go to previous page" +msgstr "" + +#. Author URI of the plugin +#: index.php +msgid "https://facetwp.com/" +msgstr "" + +#: includes/integrations/woocommerce/woocommerce.php:522 +msgid "In Stock" +msgstr "W magazynie" + +#: includes/facets/proximity.php:51 +msgid "km" +msgstr "" + +#: includes/facets/autocomplete.php:164 +msgid "Loading" +msgstr "" + +#: includes/facets/number_range.php:29 +msgid "Max" +msgstr "Max" + +#: includes/facets/proximity.php:51 +msgid "mi" +msgstr "" + +#: includes/facets/number_range.php:26 +msgid "Min" +msgstr "Min" + +#: includes/integrations/acf/acf.php:325 +msgid "No" +msgstr "Nie" + +#: includes/facets/date_range.php:25 +msgid "No dates" +msgstr "" + +#: includes/facets/date_range.php:27 +msgid "No end dates" +msgstr "" + +#: includes/facets/autocomplete.php:99 includes/facets/autocomplete.php:166 +msgid "No results" +msgstr "Brak wyników" + +#: includes/class-display.php:227 includes/facets/fselect.php:87 +msgid "No results found" +msgstr "Nie znaleziono żadnych wyników" + +#: includes/facets/date_range.php:26 +msgid "No start dates" +msgstr "" + +#: includes/facets/number_range.php:23 +msgid "Number" +msgstr "Numer" + +#: includes/class-renderer.php:535 +msgid "of" +msgstr "z" + +#: includes/integrations/woocommerce/woocommerce.php:529 +msgid "On Sale" +msgstr "W sprzedaży" + +#: includes/integrations/woocommerce/woocommerce.php:525 +msgid "Out of Stock" +msgstr "Brak w magazynie" + +#: includes/class-renderer.php:592 +msgid "Per page" +msgstr "Na stronę" + +#: includes/facets/map.php:400 includes/facets/reset.php:15 +#: includes/facets/slider.php:20 +msgid "Reset" +msgstr "Resetuj" + +#: includes/facets/fselect.php:86 +msgid "Search" +msgstr "Szukaj" + +#: includes/facets/checkboxes.php:134 includes/facets/hierarchy.php:164 +msgid "See less" +msgstr "Zobacz mniej" + +#: includes/facets/checkboxes.php:133 includes/facets/hierarchy.php:163 +msgid "See {num} more" +msgstr "Zobacz {num} więcej" + +#: includes/facets/sort.php:162 +msgid "Sort by" +msgstr "Sortuj wg" + +#: includes/facets/date_range.php:22 +msgid "Start date" +msgstr "Data rozpoczęcia" + +#: includes/facets/autocomplete.php:63 +msgid "Start typing" +msgstr "Zacznij wpisywać" + +#: includes/facets/sort.php:166 +msgid "Title (A-Z)" +msgstr "Nazwy (A-Z)" + +#: includes/facets/sort.php:173 +msgid "Title (Z-A)" +msgstr "Nazwy (Z-A)" + +#: includes/facets/rating.php:166 +msgid "Undo" +msgstr "Cofnij" + +#: includes/integrations/acf/acf.php:325 +msgid "Yes" +msgstr "Tak" + +#: includes/facets/fselect.php:85 +msgid "{n} selected" +msgstr "wybrano {n}" diff --git a/wp-content/plugins/facetwp/languages/fwp-front-pt_BR.mo b/wp-content/plugins/facetwp/languages/fwp-front-pt_BR.mo new file mode 100644 index 000000000..b4134fc86 Binary files /dev/null and b/wp-content/plugins/facetwp/languages/fwp-front-pt_BR.mo differ diff --git a/wp-content/plugins/facetwp/languages/fwp-front-pt_BR.po b/wp-content/plugins/facetwp/languages/fwp-front-pt_BR.po new file mode 100644 index 000000000..329496b6c --- /dev/null +++ b/wp-content/plugins/facetwp/languages/fwp-front-pt_BR.po @@ -0,0 +1,226 @@ +msgid "" +msgstr "" +"Project-Id-Version: FacetWP (front)\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-02-21 14:01+0000\n" +"PO-Revision-Date: 2025-04-18 10:50+0000\n" +"Last-Translator: \n" +"Language-Team: Portuguese (Brazil)\n" +"Language: pt-BR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"X-Generator: Loco https://localise.biz/\n" +"X-Loco-Version: 2.7.1; wp-6.7.1\n" +"X-Poedit-SourceCharset: UTF-8\n" + +#: includes/facets/rating.php:165 +msgid "& up" +msgstr "& up" + +#. Description of the plugin +#: index.php +msgid "Advanced Filtering for WordPress" +msgstr "" + +#: includes/facets/dropdown.php:35 includes/facets/fselect.php:37 +#: includes/facets/fselect.php:80 includes/facets/hierarchy.php:26 +msgid "Any" +msgstr "Qualquer" + +#: includes/facets/date_range.php:300 +msgid "Clear" +msgstr "Limpar" + +#: includes/facets/proximity.php:243 +msgid "Clear location" +msgstr "Limpar localização" + +#: includes/facets/date_range.php:21 +msgid "Date" +msgstr "Data" + +#: includes/facets/sort.php:180 +msgid "Date (Newest)" +msgstr "Data (mais recente)" + +#: includes/facets/sort.php:187 +msgid "Date (Oldest)" +msgstr "Data (mais antiga)" + +#: includes/facets/proximity.php:357 +msgid "Distance" +msgstr "Distância" + +#: includes/facets/map.php:397 +msgid "Enable map filtering" +msgstr "" + +#: includes/facets/date_range.php:23 +msgid "End date" +msgstr "Data Final" + +#: includes/facets/search.php:23 +msgid "Enter keywords" +msgstr "Digitar palavras-chave" + +#: includes/facets/proximity.php:52 +msgid "Enter location" +msgstr "Digitar local" + +#: includes/facets/autocomplete.php:165 +msgid "Enter {n} or more characters" +msgstr "" + +#. Plugin Name of the plugin +#: index.php +msgid "FacetWP" +msgstr "" + +#. Author of the plugin +#: index.php +msgid "FacetWP, LLC" +msgstr "" + +#: includes/integrations/woocommerce/woocommerce.php:532 +msgid "Featured" +msgstr "" + +#: includes/facets/autocomplete.php:66 includes/facets/number_range.php:32 +msgid "Go" +msgstr "Ir" + +#: includes/class-display.php:208 +msgid "Go to next page" +msgstr "" + +#: includes/class-display.php:207 +msgid "Go to page" +msgstr "" + +#: includes/class-display.php:209 +msgid "Go to previous page" +msgstr "" + +#. Author URI of the plugin +#: index.php +msgid "https://facetwp.com/" +msgstr "" + +#: includes/integrations/woocommerce/woocommerce.php:522 +msgid "In Stock" +msgstr "Em estoque" + +#: includes/facets/proximity.php:51 +msgid "km" +msgstr "" + +#: includes/facets/autocomplete.php:164 +msgid "Loading" +msgstr "" + +#: includes/facets/number_range.php:29 +msgid "Max" +msgstr "Máx" + +#: includes/facets/proximity.php:51 +msgid "mi" +msgstr "" + +#: includes/facets/number_range.php:26 +msgid "Min" +msgstr "Min" + +#: includes/integrations/acf/acf.php:325 +msgid "No" +msgstr "Não" + +#: includes/facets/date_range.php:25 +msgid "No dates" +msgstr "" + +#: includes/facets/date_range.php:27 +msgid "No end dates" +msgstr "" + +#: includes/facets/autocomplete.php:99 includes/facets/autocomplete.php:166 +msgid "No results" +msgstr "Sem resultados" + +#: includes/class-display.php:227 includes/facets/fselect.php:87 +msgid "No results found" +msgstr "Nenhum resultado encontrado" + +#: includes/facets/date_range.php:26 +msgid "No start dates" +msgstr "" + +#: includes/facets/number_range.php:23 +msgid "Number" +msgstr "Número" + +#: includes/class-renderer.php:535 +msgid "of" +msgstr "de" + +#: includes/integrations/woocommerce/woocommerce.php:529 +msgid "On Sale" +msgstr "À venda" + +#: includes/integrations/woocommerce/woocommerce.php:525 +msgid "Out of Stock" +msgstr "Fora de Estoque" + +#: includes/class-renderer.php:592 +msgid "Per page" +msgstr "Por página" + +#: includes/facets/map.php:400 includes/facets/reset.php:15 +#: includes/facets/slider.php:20 +msgid "Reset" +msgstr "Redefinir" + +#: includes/facets/fselect.php:86 +msgid "Search" +msgstr "Pesquisar" + +#: includes/facets/checkboxes.php:134 includes/facets/hierarchy.php:164 +msgid "See less" +msgstr "Ver menos" + +#: includes/facets/checkboxes.php:133 includes/facets/hierarchy.php:163 +msgid "See {num} more" +msgstr "Ver {num} mais" + +#: includes/facets/sort.php:162 +msgid "Sort by" +msgstr "Ordenar por" + +#: includes/facets/date_range.php:22 +msgid "Start date" +msgstr "Data de Início" + +#: includes/facets/autocomplete.php:63 +msgid "Start typing" +msgstr "Comece a digitar" + +#: includes/facets/sort.php:166 +msgid "Title (A-Z)" +msgstr "Título (A-Z)" + +#: includes/facets/sort.php:173 +msgid "Title (Z-A)" +msgstr "Título (Z-A)" + +#: includes/facets/rating.php:166 +msgid "Undo" +msgstr "Desfazer" + +#: includes/integrations/acf/acf.php:325 +msgid "Yes" +msgstr "Sim" + +#: includes/facets/fselect.php:85 +msgid "{n} selected" +msgstr "{n} seleccionado" diff --git a/wp-content/plugins/facetwp/languages/fwp-front-ro_RO.mo b/wp-content/plugins/facetwp/languages/fwp-front-ro_RO.mo new file mode 100644 index 000000000..93392b23c Binary files /dev/null and b/wp-content/plugins/facetwp/languages/fwp-front-ro_RO.mo differ diff --git a/wp-content/plugins/facetwp/languages/fwp-front-ro_RO.po b/wp-content/plugins/facetwp/languages/fwp-front-ro_RO.po new file mode 100644 index 000000000..36af0af55 --- /dev/null +++ b/wp-content/plugins/facetwp/languages/fwp-front-ro_RO.po @@ -0,0 +1,227 @@ +msgid "" +msgstr "" +"Project-Id-Version: FacetWP (front)\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-02-21 14:01+0000\n" +"PO-Revision-Date: 2025-04-18 10:51+0000\n" +"Last-Translator: \n" +"Language-Team: Romanian\n" +"Language: ro-RO\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n==0 || (n!=1 && n%100>=1 && " +"n%100<=19) ? 1 : 2);\n" +"X-Generator: Loco https://localise.biz/\n" +"X-Loco-Version: 2.7.1; wp-6.7.1\n" +"X-Poedit-SourceCharset: UTF-8\n" + +#: includes/facets/rating.php:165 +msgid "& up" +msgstr "& up" + +#. Description of the plugin +#: index.php +msgid "Advanced Filtering for WordPress" +msgstr "" + +#: includes/facets/dropdown.php:35 includes/facets/fselect.php:37 +#: includes/facets/fselect.php:80 includes/facets/hierarchy.php:26 +msgid "Any" +msgstr "Orice" + +#: includes/facets/date_range.php:300 +msgid "Clear" +msgstr "Sterge" + +#: includes/facets/proximity.php:243 +msgid "Clear location" +msgstr "Ștergeți locația" + +#: includes/facets/date_range.php:21 +msgid "Date" +msgstr "Data" + +#: includes/facets/sort.php:180 +msgid "Date (Newest)" +msgstr "Data (cele mai noi)" + +#: includes/facets/sort.php:187 +msgid "Date (Oldest)" +msgstr "Data (cea mai veche)" + +#: includes/facets/proximity.php:357 +msgid "Distance" +msgstr "Distanță" + +#: includes/facets/map.php:397 +msgid "Enable map filtering" +msgstr "" + +#: includes/facets/date_range.php:23 +msgid "End date" +msgstr "Data de sfârșit" + +#: includes/facets/search.php:23 +msgid "Enter keywords" +msgstr "Introdu cuvinte cheie" + +#: includes/facets/proximity.php:52 +msgid "Enter location" +msgstr "Vă rugăm să introduceți ID-ul Locatiei" + +#: includes/facets/autocomplete.php:165 +msgid "Enter {n} or more characters" +msgstr "" + +#. Plugin Name of the plugin +#: index.php +msgid "FacetWP" +msgstr "" + +#. Author of the plugin +#: index.php +msgid "FacetWP, LLC" +msgstr "" + +#: includes/integrations/woocommerce/woocommerce.php:532 +msgid "Featured" +msgstr "" + +#: includes/facets/autocomplete.php:66 includes/facets/number_range.php:32 +msgid "Go" +msgstr "Mergi" + +#: includes/class-display.php:208 +msgid "Go to next page" +msgstr "" + +#: includes/class-display.php:207 +msgid "Go to page" +msgstr "" + +#: includes/class-display.php:209 +msgid "Go to previous page" +msgstr "" + +#. Author URI of the plugin +#: index.php +msgid "https://facetwp.com/" +msgstr "" + +#: includes/integrations/woocommerce/woocommerce.php:522 +msgid "In Stock" +msgstr "În stoc" + +#: includes/facets/proximity.php:51 +msgid "km" +msgstr "" + +#: includes/facets/autocomplete.php:164 +msgid "Loading" +msgstr "" + +#: includes/facets/number_range.php:29 +msgid "Max" +msgstr "Max" + +#: includes/facets/proximity.php:51 +msgid "mi" +msgstr "" + +#: includes/facets/number_range.php:26 +msgid "Min" +msgstr "Min" + +#: includes/integrations/acf/acf.php:325 +msgid "No" +msgstr "Nu" + +#: includes/facets/date_range.php:25 +msgid "No dates" +msgstr "" + +#: includes/facets/date_range.php:27 +msgid "No end dates" +msgstr "" + +#: includes/facets/autocomplete.php:99 includes/facets/autocomplete.php:166 +msgid "No results" +msgstr "Nici un rezultat" + +#: includes/class-display.php:227 includes/facets/fselect.php:87 +msgid "No results found" +msgstr "Niciun rezultat găsit" + +#: includes/facets/date_range.php:26 +msgid "No start dates" +msgstr "" + +#: includes/facets/number_range.php:23 +msgid "Number" +msgstr "Număr" + +#: includes/class-renderer.php:535 +msgid "of" +msgstr "din" + +#: includes/integrations/woocommerce/woocommerce.php:529 +msgid "On Sale" +msgstr "La reducere" + +#: includes/integrations/woocommerce/woocommerce.php:525 +msgid "Out of Stock" +msgstr "Stoc epuizat" + +#: includes/class-renderer.php:592 +msgid "Per page" +msgstr "Per pagină" + +#: includes/facets/map.php:400 includes/facets/reset.php:15 +#: includes/facets/slider.php:20 +msgid "Reset" +msgstr "Reset" + +#: includes/facets/fselect.php:86 +msgid "Search" +msgstr "Caută" + +#: includes/facets/checkboxes.php:134 includes/facets/hierarchy.php:164 +msgid "See less" +msgstr "Vedea mai puțin" + +#: includes/facets/checkboxes.php:133 includes/facets/hierarchy.php:163 +msgid "See {num} more" +msgstr "Vezi {num} mai multe" + +#: includes/facets/sort.php:162 +msgid "Sort by" +msgstr "Sortare după" + +#: includes/facets/date_range.php:22 +msgid "Start date" +msgstr "Data de început" + +#: includes/facets/autocomplete.php:63 +msgid "Start typing" +msgstr "Începeți să tastați" + +#: includes/facets/sort.php:166 +msgid "Title (A-Z)" +msgstr "Titlu (A-Z)" + +#: includes/facets/sort.php:173 +msgid "Title (Z-A)" +msgstr "Titlu (Z-A)" + +#: includes/facets/rating.php:166 +msgid "Undo" +msgstr "Anulare" + +#: includes/integrations/acf/acf.php:325 +msgid "Yes" +msgstr "Da" + +#: includes/facets/fselect.php:85 +msgid "{n} selected" +msgstr "{n} selectat" diff --git a/wp-content/plugins/facetwp/languages/fwp-front-ru_RU.mo b/wp-content/plugins/facetwp/languages/fwp-front-ru_RU.mo new file mode 100644 index 000000000..7edf8a178 Binary files /dev/null and b/wp-content/plugins/facetwp/languages/fwp-front-ru_RU.mo differ diff --git a/wp-content/plugins/facetwp/languages/fwp-front-ru_RU.po b/wp-content/plugins/facetwp/languages/fwp-front-ru_RU.po new file mode 100644 index 000000000..08ba1706d --- /dev/null +++ b/wp-content/plugins/facetwp/languages/fwp-front-ru_RU.po @@ -0,0 +1,227 @@ +msgid "" +msgstr "" +"Project-Id-Version: FacetWP (front)\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-02-21 14:01+0000\n" +"PO-Revision-Date: 2025-04-18 10:52+0000\n" +"Last-Translator: \n" +"Language-Team: Russian\n" +"Language: ru-RU\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " +"n%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n" +"X-Generator: Loco https://localise.biz/\n" +"X-Loco-Version: 2.7.1; wp-6.7.1\n" +"X-Poedit-SourceCharset: UTF-8\n" + +#: includes/facets/rating.php:165 +msgid "& up" +msgstr "и вверх" + +#. Description of the plugin +#: index.php +msgid "Advanced Filtering for WordPress" +msgstr "" + +#: includes/facets/dropdown.php:35 includes/facets/fselect.php:37 +#: includes/facets/fselect.php:80 includes/facets/hierarchy.php:26 +msgid "Any" +msgstr "Любой" + +#: includes/facets/date_range.php:300 +msgid "Clear" +msgstr "Очистить" + +#: includes/facets/proximity.php:243 +msgid "Clear location" +msgstr "Очистить местоположение" + +#: includes/facets/date_range.php:21 +msgid "Date" +msgstr "Дата" + +#: includes/facets/sort.php:180 +msgid "Date (Newest)" +msgstr "Дата (новые)" + +#: includes/facets/sort.php:187 +msgid "Date (Oldest)" +msgstr "Дата (самая старая)" + +#: includes/facets/proximity.php:357 +msgid "Distance" +msgstr "Расстояние" + +#: includes/facets/map.php:397 +msgid "Enable map filtering" +msgstr "" + +#: includes/facets/date_range.php:23 +msgid "End date" +msgstr "Дата окончания" + +#: includes/facets/search.php:23 +msgid "Enter keywords" +msgstr "Введите ключевые слова" + +#: includes/facets/proximity.php:52 +msgid "Enter location" +msgstr "Введите местоположение" + +#: includes/facets/autocomplete.php:165 +msgid "Enter {n} or more characters" +msgstr "" + +#. Plugin Name of the plugin +#: index.php +msgid "FacetWP" +msgstr "" + +#. Author of the plugin +#: index.php +msgid "FacetWP, LLC" +msgstr "" + +#: includes/integrations/woocommerce/woocommerce.php:532 +msgid "Featured" +msgstr "" + +#: includes/facets/autocomplete.php:66 includes/facets/number_range.php:32 +msgid "Go" +msgstr "Перейти" + +#: includes/class-display.php:208 +msgid "Go to next page" +msgstr "" + +#: includes/class-display.php:207 +msgid "Go to page" +msgstr "" + +#: includes/class-display.php:209 +msgid "Go to previous page" +msgstr "" + +#. Author URI of the plugin +#: index.php +msgid "https://facetwp.com/" +msgstr "" + +#: includes/integrations/woocommerce/woocommerce.php:522 +msgid "In Stock" +msgstr "В наличии" + +#: includes/facets/proximity.php:51 +msgid "km" +msgstr "" + +#: includes/facets/autocomplete.php:164 +msgid "Loading" +msgstr "" + +#: includes/facets/number_range.php:29 +msgid "Max" +msgstr "Макс" + +#: includes/facets/proximity.php:51 +msgid "mi" +msgstr "" + +#: includes/facets/number_range.php:26 +msgid "Min" +msgstr "Мин" + +#: includes/integrations/acf/acf.php:325 +msgid "No" +msgstr "Нет" + +#: includes/facets/date_range.php:25 +msgid "No dates" +msgstr "" + +#: includes/facets/date_range.php:27 +msgid "No end dates" +msgstr "" + +#: includes/facets/autocomplete.php:99 includes/facets/autocomplete.php:166 +msgid "No results" +msgstr "Нет результатов" + +#: includes/class-display.php:227 includes/facets/fselect.php:87 +msgid "No results found" +msgstr "Результатов не найдено" + +#: includes/facets/date_range.php:26 +msgid "No start dates" +msgstr "" + +#: includes/facets/number_range.php:23 +msgid "Number" +msgstr "Номер" + +#: includes/class-renderer.php:535 +msgid "of" +msgstr "из" + +#: includes/integrations/woocommerce/woocommerce.php:529 +msgid "On Sale" +msgstr "Распродажа" + +#: includes/integrations/woocommerce/woocommerce.php:525 +msgid "Out of Stock" +msgstr "Нет в наличии" + +#: includes/class-renderer.php:592 +msgid "Per page" +msgstr "На страницу" + +#: includes/facets/map.php:400 includes/facets/reset.php:15 +#: includes/facets/slider.php:20 +msgid "Reset" +msgstr "Сброс" + +#: includes/facets/fselect.php:86 +msgid "Search" +msgstr "Поиск" + +#: includes/facets/checkboxes.php:134 includes/facets/hierarchy.php:164 +msgid "See less" +msgstr "Свернуть" + +#: includes/facets/checkboxes.php:133 includes/facets/hierarchy.php:163 +msgid "See {num} more" +msgstr "Посмотреть {num} Подробнее" + +#: includes/facets/sort.php:162 +msgid "Sort by" +msgstr "Сортировать по" + +#: includes/facets/date_range.php:22 +msgid "Start date" +msgstr "Дата начала" + +#: includes/facets/autocomplete.php:63 +msgid "Start typing" +msgstr "Напишите " + +#: includes/facets/sort.php:166 +msgid "Title (A-Z)" +msgstr "Имя (А-Я)" + +#: includes/facets/sort.php:173 +msgid "Title (Z-A)" +msgstr "Имя (Я-А)" + +#: includes/facets/rating.php:166 +msgid "Undo" +msgstr "Отменить" + +#: includes/integrations/acf/acf.php:325 +msgid "Yes" +msgstr "Да" + +#: includes/facets/fselect.php:85 +msgid "{n} selected" +msgstr "{n} выбрано" diff --git a/wp-content/plugins/facetwp/languages/fwp-front-sv_SE.mo b/wp-content/plugins/facetwp/languages/fwp-front-sv_SE.mo new file mode 100644 index 000000000..050548940 Binary files /dev/null and b/wp-content/plugins/facetwp/languages/fwp-front-sv_SE.mo differ diff --git a/wp-content/plugins/facetwp/languages/fwp-front-sv_SE.po b/wp-content/plugins/facetwp/languages/fwp-front-sv_SE.po new file mode 100644 index 000000000..fae2d3205 --- /dev/null +++ b/wp-content/plugins/facetwp/languages/fwp-front-sv_SE.po @@ -0,0 +1,226 @@ +msgid "" +msgstr "" +"Project-Id-Version: FacetWP (front)\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-02-21 14:01+0000\n" +"PO-Revision-Date: 2025-04-18 10:53+0000\n" +"Last-Translator: \n" +"Language-Team: Swedish\n" +"Language: sv-SE\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Loco https://localise.biz/\n" +"X-Loco-Version: 2.7.1; wp-6.7.1\n" +"X-Poedit-SourceCharset: UTF-8\n" + +#: includes/facets/rating.php:165 +msgid "& up" +msgstr "& upp" + +#. Description of the plugin +#: index.php +msgid "Advanced Filtering for WordPress" +msgstr "" + +#: includes/facets/dropdown.php:35 includes/facets/fselect.php:37 +#: includes/facets/fselect.php:80 includes/facets/hierarchy.php:26 +msgid "Any" +msgstr "Någon" + +#: includes/facets/date_range.php:300 +msgid "Clear" +msgstr "Rensa" + +#: includes/facets/proximity.php:243 +msgid "Clear location" +msgstr "Rensa plats" + +#: includes/facets/date_range.php:21 +msgid "Date" +msgstr "Datum" + +#: includes/facets/sort.php:180 +msgid "Date (Newest)" +msgstr "Datum (nyaste)" + +#: includes/facets/sort.php:187 +msgid "Date (Oldest)" +msgstr "Datum (äldsta)" + +#: includes/facets/proximity.php:357 +msgid "Distance" +msgstr "Avstånd" + +#: includes/facets/map.php:397 +msgid "Enable map filtering" +msgstr "" + +#: includes/facets/date_range.php:23 +msgid "End date" +msgstr "Slutdatum" + +#: includes/facets/search.php:23 +msgid "Enter keywords" +msgstr "Ange nyckelord" + +#: includes/facets/proximity.php:52 +msgid "Enter location" +msgstr "Ange plats" + +#: includes/facets/autocomplete.php:165 +msgid "Enter {n} or more characters" +msgstr "" + +#. Plugin Name of the plugin +#: index.php +msgid "FacetWP" +msgstr "" + +#. Author of the plugin +#: index.php +msgid "FacetWP, LLC" +msgstr "" + +#: includes/integrations/woocommerce/woocommerce.php:532 +msgid "Featured" +msgstr "" + +#: includes/facets/autocomplete.php:66 includes/facets/number_range.php:32 +msgid "Go" +msgstr "Start" + +#: includes/class-display.php:208 +msgid "Go to next page" +msgstr "" + +#: includes/class-display.php:207 +msgid "Go to page" +msgstr "" + +#: includes/class-display.php:209 +msgid "Go to previous page" +msgstr "" + +#. Author URI of the plugin +#: index.php +msgid "https://facetwp.com/" +msgstr "" + +#: includes/integrations/woocommerce/woocommerce.php:522 +msgid "In Stock" +msgstr "I lager" + +#: includes/facets/proximity.php:51 +msgid "km" +msgstr "" + +#: includes/facets/autocomplete.php:164 +msgid "Loading" +msgstr "" + +#: includes/facets/number_range.php:29 +msgid "Max" +msgstr "Max" + +#: includes/facets/proximity.php:51 +msgid "mi" +msgstr "" + +#: includes/facets/number_range.php:26 +msgid "Min" +msgstr "Min" + +#: includes/integrations/acf/acf.php:325 +msgid "No" +msgstr "Nej" + +#: includes/facets/date_range.php:25 +msgid "No dates" +msgstr "" + +#: includes/facets/date_range.php:27 +msgid "No end dates" +msgstr "" + +#: includes/facets/autocomplete.php:99 includes/facets/autocomplete.php:166 +msgid "No results" +msgstr "Inga resultat" + +#: includes/class-display.php:227 includes/facets/fselect.php:87 +msgid "No results found" +msgstr "Inga resultat hittades" + +#: includes/facets/date_range.php:26 +msgid "No start dates" +msgstr "" + +#: includes/facets/number_range.php:23 +msgid "Number" +msgstr "Nummer" + +#: includes/class-renderer.php:535 +msgid "of" +msgstr "av" + +#: includes/integrations/woocommerce/woocommerce.php:529 +msgid "On Sale" +msgstr "Rea" + +#: includes/integrations/woocommerce/woocommerce.php:525 +msgid "Out of Stock" +msgstr "Slut i lager" + +#: includes/class-renderer.php:592 +msgid "Per page" +msgstr "Per sida" + +#: includes/facets/map.php:400 includes/facets/reset.php:15 +#: includes/facets/slider.php:20 +msgid "Reset" +msgstr "Återställ" + +#: includes/facets/fselect.php:86 +msgid "Search" +msgstr "Sök" + +#: includes/facets/checkboxes.php:134 includes/facets/hierarchy.php:164 +msgid "See less" +msgstr "Se mindre" + +#: includes/facets/checkboxes.php:133 includes/facets/hierarchy.php:163 +msgid "See {num} more" +msgstr "Se {num} mer" + +#: includes/facets/sort.php:162 +msgid "Sort by" +msgstr "Sortera efter" + +#: includes/facets/date_range.php:22 +msgid "Start date" +msgstr "Startdatum" + +#: includes/facets/autocomplete.php:63 +msgid "Start typing" +msgstr "Börja skriv" + +#: includes/facets/sort.php:166 +msgid "Title (A-Z)" +msgstr "Titel (A-Z)" + +#: includes/facets/sort.php:173 +msgid "Title (Z-A)" +msgstr "Titel (Z-A)" + +#: includes/facets/rating.php:166 +msgid "Undo" +msgstr "Ångra" + +#: includes/integrations/acf/acf.php:325 +msgid "Yes" +msgstr "Ja" + +#: includes/facets/fselect.php:85 +msgid "{n} selected" +msgstr "{n} markerat" diff --git a/wp-content/plugins/facetwp/languages/fwp-front.pot b/wp-content/plugins/facetwp/languages/fwp-front.pot new file mode 100644 index 000000000..a3d89da7b --- /dev/null +++ b/wp-content/plugins/facetwp/languages/fwp-front.pot @@ -0,0 +1,234 @@ +# Copyright (C) 2025 FacetWP, LLC +# This file is distributed under the same license as the FacetWP plugin. +# Generated with WP-CLI: wp i18n make-pot . languages/fwp-front.pot --domain=fwp-front +msgid "" +msgstr "" +"Project-Id-Version: FacetWP (front)\n" +"Report-Msgid-Bugs-To: https://facetwp.com/help-center/get-support/\n" +"Last-Translator: Matt Gibbs \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"POT-Creation-Date: 2025-04-18T09:05:51+00:00\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"X-Generator: WP-CLI 2.11.0\n" +"X-Domain: fwp-front\n" + +#. Plugin Name of the plugin +#: index.php +msgid "FacetWP" +msgstr "" + +#. Description of the plugin +#: index.php +msgid "Advanced Filtering for WordPress" +msgstr "" + +#. Author of the plugin +#: index.php +msgid "FacetWP, LLC" +msgstr "" + +#. Author URI of the plugin +#: index.php +msgid "https://facetwp.com/" +msgstr "" + +#: includes/class-display.php:207 +msgid "Go to page" +msgstr "" + +#: includes/class-display.php:208 +msgid "Go to next page" +msgstr "" + +#: includes/class-display.php:209 +msgid "Go to previous page" +msgstr "" + +#: includes/class-display.php:227 +#: includes/facets/fselect.php:87 +msgid "No results found" +msgstr "" + +#: includes/class-renderer.php:535 +msgid "of" +msgstr "" + +#: includes/class-renderer.php:592 +msgid "Per page" +msgstr "" + +#: includes/facets/autocomplete.php:63 +msgid "Start typing" +msgstr "" + +#: includes/facets/autocomplete.php:66 +#: includes/facets/number_range.php:32 +msgid "Go" +msgstr "" + +#: includes/facets/autocomplete.php:99 +#: includes/facets/autocomplete.php:166 +msgid "No results" +msgstr "" + +#: includes/facets/autocomplete.php:164 +msgid "Loading" +msgstr "" + +#: includes/facets/autocomplete.php:165 +msgid "Enter {n} or more characters" +msgstr "" + +#: includes/facets/checkboxes.php:133 +#: includes/facets/hierarchy.php:163 +msgid "See {num} more" +msgstr "" + +#: includes/facets/checkboxes.php:134 +#: includes/facets/hierarchy.php:164 +msgid "See less" +msgstr "" + +#: includes/facets/date_range.php:21 +msgid "Date" +msgstr "" + +#: includes/facets/date_range.php:22 +msgid "Start date" +msgstr "" + +#: includes/facets/date_range.php:23 +msgid "End date" +msgstr "" + +#: includes/facets/date_range.php:25 +msgid "No dates" +msgstr "" + +#: includes/facets/date_range.php:26 +msgid "No start dates" +msgstr "" + +#: includes/facets/date_range.php:27 +msgid "No end dates" +msgstr "" + +#: includes/facets/date_range.php:300 +msgid "Clear" +msgstr "" + +#: includes/facets/dropdown.php:35 +#: includes/facets/fselect.php:37 +#: includes/facets/fselect.php:80 +#: includes/facets/hierarchy.php:26 +msgid "Any" +msgstr "" + +#: includes/facets/fselect.php:85 +msgid "{n} selected" +msgstr "" + +#: includes/facets/fselect.php:86 +msgid "Search" +msgstr "" + +#: includes/facets/map.php:397 +msgid "Enable map filtering" +msgstr "" + +#: includes/facets/map.php:400 +#: includes/facets/reset.php:15 +#: includes/facets/slider.php:20 +msgid "Reset" +msgstr "" + +#: includes/facets/number_range.php:23 +msgid "Number" +msgstr "" + +#: includes/facets/number_range.php:26 +msgid "Min" +msgstr "" + +#: includes/facets/number_range.php:29 +msgid "Max" +msgstr "" + +#: includes/facets/proximity.php:51 +msgid "km" +msgstr "" + +#: includes/facets/proximity.php:51 +msgid "mi" +msgstr "" + +#: includes/facets/proximity.php:52 +msgid "Enter location" +msgstr "" + +#: includes/facets/proximity.php:243 +msgid "Clear location" +msgstr "" + +#: includes/facets/proximity.php:357 +msgid "Distance" +msgstr "" + +#: includes/facets/rating.php:165 +msgid "& up" +msgstr "" + +#: includes/facets/rating.php:166 +msgid "Undo" +msgstr "" + +#: includes/facets/search.php:23 +msgid "Enter keywords" +msgstr "" + +#: includes/facets/sort.php:162 +msgid "Sort by" +msgstr "" + +#: includes/facets/sort.php:166 +msgid "Title (A-Z)" +msgstr "" + +#: includes/facets/sort.php:173 +msgid "Title (Z-A)" +msgstr "" + +#: includes/facets/sort.php:180 +msgid "Date (Newest)" +msgstr "" + +#: includes/facets/sort.php:187 +msgid "Date (Oldest)" +msgstr "" + +#: includes/integrations/acf/acf.php:325 +msgid "Yes" +msgstr "" + +#: includes/integrations/acf/acf.php:325 +msgid "No" +msgstr "" + +#: includes/integrations/woocommerce/woocommerce.php:522 +msgid "In Stock" +msgstr "" + +#: includes/integrations/woocommerce/woocommerce.php:525 +msgid "Out of Stock" +msgstr "" + +#: includes/integrations/woocommerce/woocommerce.php:529 +msgid "On Sale" +msgstr "" + +#: includes/integrations/woocommerce/woocommerce.php:532 +msgid "Featured" +msgstr "" diff --git a/wp-content/plugins/facetwp/languages/fwp.pot b/wp-content/plugins/facetwp/languages/fwp.pot new file mode 100644 index 000000000..80756f0e5 --- /dev/null +++ b/wp-content/plugins/facetwp/languages/fwp.pot @@ -0,0 +1,1279 @@ +# Copyright (C) 2025 FacetWP, LLC +# This file is distributed under the same license as the FacetWP plugin. +# Generated with WP-CLI: wp i18n make-pot . languages/fwp.pot --domain=fwp +msgid "" +msgstr "" +"Project-Id-Version: FacetWP\n" +"Report-Msgid-Bugs-To: https://facetwp.com/help-center/get-support/\n" +"Last-Translator: Matt Gibbs \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"POT-Creation-Date: 2025-04-18T08:59:58+00:00\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"X-Generator: WP-CLI 2.11.0\n" +"X-Domain: fwp\n" + +#. Plugin Name of the plugin +#: index.php +msgid "FacetWP" +msgstr "" + +#. Description of the plugin +#: index.php +msgid "Advanced Filtering for WordPress" +msgstr "" + +#. Author of the plugin +#: index.php +msgid "FacetWP, LLC" +msgstr "" + +#. Author URI of the plugin +#: index.php +msgid "https://facetwp.com/" +msgstr "" + +#: includes/class-ajax.php:72 +msgid "Settings saved, please re-index" +msgstr "" + +#: includes/class-ajax.php:78 +msgid "Settings saved" +msgstr "" + +#: includes/class-ajax.php:85 +msgid "Error: invalid JSON" +msgstr "" + +#: includes/class-ajax.php:143 +msgid "Done, please re-index" +msgstr "" + +#: includes/class-ajax.php:205 +msgid "Error" +msgstr "" + +#: includes/class-ajax.php:243 +msgid "Nothing to import" +msgstr "" + +#: includes/class-ajax.php:279 +msgid "Imported" +msgstr "" + +#: includes/class-ajax.php:282 +msgid "Skipped" +msgstr "" + +#: includes/class-builder.php:281 +msgid "In Stock" +msgstr "" + +#: includes/class-builder.php:281 +msgid "Out of Stock" +msgstr "" + +#: includes/class-builder.php:286 +msgid "On Sale" +msgstr "" + +#: includes/class-helper.php:523 +#: includes/class-settings.php:608 +msgid "Posts" +msgstr "" + +#: includes/class-helper.php:525 +#: includes/class-settings.php:611 +msgid "Post Type" +msgstr "" + +#: includes/class-helper.php:526 +#: includes/class-settings.php:612 +msgid "Post Date" +msgstr "" + +#: includes/class-helper.php:527 +#: includes/class-settings.php:613 +msgid "Post Modified" +msgstr "" + +#: includes/class-helper.php:528 +#: includes/class-settings.php:609 +msgid "Post Title" +msgstr "" + +#: includes/class-helper.php:529 +msgid "Post Author" +msgstr "" + +#: includes/class-helper.php:534 +msgid "Taxonomies" +msgstr "" + +#: includes/class-helper.php:539 +#: includes/class-settings.php:616 +msgid "Custom Fields" +msgstr "" + +#: includes/class-init.php:200 +#: templates/page-settings.php:109 +msgid "Settings" +msgstr "" + +#: includes/class-request.php:372 +msgid "FacetWP was unable to auto-detect the post listing" +msgstr "" + +#: includes/class-settings.php:13 +msgid "General" +msgstr "" + +#: includes/class-settings.php:16 +msgid "License key" +msgstr "" + +#: includes/class-settings.php:20 +msgid "Google Maps API key" +msgstr "" + +#: includes/class-settings.php:24 +msgid "Test Google APIs" +msgstr "" + +#: includes/class-settings.php:29 +msgid "Use Legacy Proximity facet" +msgstr "" + +#: includes/class-settings.php:38 +msgid "Separators" +msgstr "" + +#: includes/class-settings.php:43 +msgid "URL prefix" +msgstr "" + +#: includes/class-settings.php:49 +msgid "Load jQuery" +msgstr "" + +#: includes/class-settings.php:57 +msgid "Load a11y support" +msgstr "" + +#: includes/class-settings.php:65 +msgid "Strict query detection" +msgstr "" + +#: includes/class-settings.php:73 +msgid "Debug mode" +msgstr "" + +#: includes/class-settings.php:82 +msgid "Enable automatic indexing" +msgstr "" + +#: includes/class-settings.php:92 +#: includes/integrations/woocommerce/woocommerce.php:69 +msgid "WooCommerce" +msgstr "" + +#: includes/class-settings.php:95 +msgid "Support product variations?" +msgstr "" + +#: includes/class-settings.php:96 +msgid "Enable if your store uses variable products." +msgstr "" + +#: includes/class-settings.php:100 +msgid "Index out-of-stock products?" +msgstr "" + +#: includes/class-settings.php:101 +msgid "Show facet choices for out-of-stock products?" +msgstr "" + +#: includes/class-settings.php:107 +msgid "Import / Export" +msgstr "" + +#: includes/class-settings.php:110 +#: includes/class-settings.php:460 +msgid "Export" +msgstr "" + +#: includes/class-settings.php:114 +#: includes/class-settings.php:468 +msgid "Import" +msgstr "" + +#: includes/class-settings.php:160 +#: includes/facets/pager.php:285 +#: includes/facets/sort.php:55 +msgid "Default label" +msgstr "" + +#: includes/class-settings.php:162 +msgid "Any" +msgstr "" + +#: includes/class-settings.php:166 +msgid "Placeholder text" +msgstr "" + +#: includes/class-settings.php:169 +msgid "Parent term" +msgstr "" + +#: includes/class-settings.php:175 +msgid "Hierarchical" +msgstr "" + +#: includes/class-settings.php:181 +msgid "Show expanded" +msgstr "" + +#: includes/class-settings.php:187 +msgid "Multi-select" +msgstr "" + +#: includes/class-settings.php:196 +#: includes/class-settings.php:288 +msgid "Show ghosts" +msgstr "" + +#: includes/class-settings.php:201 +msgid "Preserve ghost order" +msgstr "" + +#: includes/class-settings.php:212 +msgid "Value modifiers" +msgstr "" + +#: includes/class-settings.php:215 +msgid "Off" +msgstr "" + +#: includes/class-settings.php:216 +msgid "Exclude these values" +msgstr "" + +#: includes/class-settings.php:217 +msgid "Show only these values" +msgstr "" + +#: includes/class-settings.php:230 +msgid "Facet logic" +msgstr "" + +#: includes/class-settings.php:233 +msgid "AND (match all)" +msgstr "" + +#: includes/class-settings.php:234 +msgid "OR (match any)" +msgstr "" + +#: includes/class-settings.php:240 +#: includes/class-settings.php:607 +#: includes/facets/sort.php:57 +msgid "Sort by" +msgstr "" + +#: includes/class-settings.php:243 +msgid "Highest count" +msgstr "" + +#: includes/class-settings.php:244 +msgid "Display value" +msgstr "" + +#: includes/class-settings.php:245 +msgid "Raw value" +msgstr "" + +#: includes/class-settings.php:246 +msgid "Term order" +msgstr "" + +#: includes/class-settings.php:250 +msgid "Count" +msgstr "" + +#: includes/class-settings.php:255 +msgid "Soft limit" +msgstr "" + +#: includes/class-settings.php:261 +msgid "Other data source" +msgstr "" + +#: includes/class-settings.php:267 +msgid "Compare type" +msgstr "" + +#: includes/class-settings.php:270 +msgid "Basic" +msgstr "" + +#: includes/class-settings.php:271 +msgid "Enclose" +msgstr "" + +#: includes/class-settings.php:272 +msgid "Intersect" +msgstr "" + +#: includes/class-settings.php:277 +msgid "UI type" +msgstr "" + +#: includes/class-settings.php:280 +#: includes/facets/checkboxes.php:7 +msgid "Checkboxes" +msgstr "" + +#: includes/class-settings.php:281 +#: includes/facets/radio.php:7 +msgid "Radio" +msgstr "" + +#: includes/class-settings.php:282 +#: includes/facets/dropdown.php:7 +#: includes/facets/proximity.php:273 +msgid "Dropdown" +msgstr "" + +#: includes/class-settings.php:283 +#: includes/facets/fselect.php:7 +msgid "fSelect" +msgstr "" + +#: includes/class-settings.php:292 +msgid "Reset text" +msgstr "" + +#: includes/class-settings.php:405 +msgid "Activate" +msgstr "" + +#: includes/class-settings.php:411 +msgid "Get an API key" +msgstr "" + +#: includes/class-settings.php:418 +#: includes/class-settings.php:428 +msgid "Maps JavaScript API" +msgstr "" + +#: includes/class-settings.php:419 +#: includes/class-settings.php:429 +msgid "Geocoding API" +msgstr "" + +#: includes/class-settings.php:420 +#: includes/class-settings.php:430 +msgid "Places API (Legacy)" +msgstr "" + +#: includes/class-settings.php:421 +#: includes/class-settings.php:431 +msgid "Places API (New)" +msgstr "" + +#: includes/class-settings.php:422 +#: includes/class-settings.php:432 +msgid "Retest" +msgstr "" + +#: includes/class-settings.php:426 +msgid "Enter a Google Maps API key above to test." +msgstr "" + +#: includes/class-settings.php:458 +msgid "All" +msgstr "" + +#: includes/class-settings.php:458 +msgid "All facet" +msgstr "" + +#: includes/class-settings.php:458 +msgid "All listings" +msgstr "" + +#: includes/class-settings.php:458 +#: includes/facets/map.php:437 +#: includes/facets/reset.php:7 +msgid "Reset" +msgstr "" + +#: includes/class-settings.php:465 +msgid "Paste the import code here" +msgstr "" + +#: includes/class-settings.php:466 +msgid "Overwrite existing items?" +msgstr "" + +#: includes/class-settings.php:533 +msgid "Not yet activated" +msgstr "" + +#: includes/class-settings.php:540 +msgid "Valid until" +msgstr "" + +#: includes/class-settings.php:556 +msgid "Number of grid columns" +msgstr "" + +#: includes/class-settings.php:557 +msgid "Spacing between results" +msgstr "" + +#: includes/class-settings.php:558 +msgid "No results text" +msgstr "" + +#: includes/class-settings.php:559 +msgid "Text style" +msgstr "" + +#: includes/class-settings.php:560 +msgid "Text color" +msgstr "" + +#: includes/class-settings.php:561 +msgid "Font size" +msgstr "" + +#: includes/class-settings.php:562 +msgid "Background color" +msgstr "" + +#: includes/class-settings.php:563 +msgid "Border" +msgstr "" + +#: includes/class-settings.php:564 +msgid "Border style" +msgstr "" + +#: includes/class-settings.php:565 +#: includes/facets/proximity.php:275 +msgid "None" +msgstr "" + +#: includes/class-settings.php:566 +msgid "Solid" +msgstr "" + +#: includes/class-settings.php:567 +msgid "Dashed" +msgstr "" + +#: includes/class-settings.php:568 +msgid "Dotted" +msgstr "" + +#: includes/class-settings.php:569 +msgid "Double" +msgstr "" + +#: includes/class-settings.php:570 +msgid "Border color" +msgstr "" + +#: includes/class-settings.php:571 +msgid "Border width" +msgstr "" + +#: includes/class-settings.php:572 +msgid "Button text" +msgstr "" + +#: includes/class-settings.php:573 +msgid "Button text color" +msgstr "" + +#: includes/class-settings.php:574 +msgid "Button padding" +msgstr "" + +#: includes/class-settings.php:575 +msgid "Separator" +msgstr "" + +#: includes/class-settings.php:576 +msgid "Custom CSS" +msgstr "" + +#: includes/class-settings.php:577 +msgid "Column widths" +msgstr "" + +#: includes/class-settings.php:578 +#: includes/class-settings.php:625 +msgid "Content" +msgstr "" + +#: includes/class-settings.php:579 +msgid "Image size" +msgstr "" + +#: includes/class-settings.php:580 +msgid "Author field" +msgstr "" + +#: includes/class-settings.php:581 +msgid "Display name" +msgstr "" + +#: includes/class-settings.php:582 +msgid "User login" +msgstr "" + +#: includes/class-settings.php:583 +msgid "User ID" +msgstr "" + +#: includes/class-settings.php:584 +msgid "Field type" +msgstr "" + +#: includes/class-settings.php:585 +msgid "Text" +msgstr "" + +#: includes/class-settings.php:586 +msgid "Date" +msgstr "" + +#: includes/class-settings.php:587 +msgid "Number" +msgstr "" + +#: includes/class-settings.php:588 +msgid "Date format" +msgstr "" + +#: includes/class-settings.php:589 +msgid "Input format" +msgstr "" + +#: includes/class-settings.php:590 +msgid "Number format" +msgstr "" + +#: includes/class-settings.php:591 +#: includes/facets/reset.php:60 +msgid "Link" +msgstr "" + +#: includes/class-settings.php:592 +msgid "Link type" +msgstr "" + +#: includes/class-settings.php:593 +msgid "Post URL" +msgstr "" + +#: includes/class-settings.php:594 +msgid "Custom URL" +msgstr "" + +#: includes/class-settings.php:595 +msgid "Open in new tab?" +msgstr "" + +#: includes/class-settings.php:596 +#: includes/facets/slider.php:139 +msgid "Prefix" +msgstr "" + +#: includes/class-settings.php:597 +#: includes/facets/slider.php:143 +msgid "Suffix" +msgstr "" + +#: includes/class-settings.php:598 +msgid "Hide item?" +msgstr "" + +#: includes/class-settings.php:599 +msgid "Padding" +msgstr "" + +#: includes/class-settings.php:600 +msgid "CSS class" +msgstr "" + +#: includes/class-settings.php:601 +msgid "Button border" +msgstr "" + +#: includes/class-settings.php:602 +msgid "Term URL" +msgstr "" + +#: includes/class-settings.php:603 +msgid "Fetch" +msgstr "" + +#: includes/class-settings.php:604 +msgid "All post types" +msgstr "" + +#: includes/class-settings.php:605 +msgid "and show" +msgstr "" + +#: includes/class-settings.php:606 +msgid "per page" +msgstr "" + +#: includes/class-settings.php:610 +msgid "Post Name" +msgstr "" + +#: includes/class-settings.php:614 +msgid "Comment Count" +msgstr "" + +#: includes/class-settings.php:615 +msgid "Menu Order" +msgstr "" + +#: includes/class-settings.php:617 +msgid "Narrow results by" +msgstr "" + +#: includes/class-settings.php:618 +msgid "Hit Enter" +msgstr "" + +#: includes/class-settings.php:619 +msgid "Add query sort" +msgstr "" + +#: includes/class-settings.php:620 +msgid "Add query filter" +msgstr "" + +#: includes/class-settings.php:621 +msgid "Clear" +msgstr "" + +#: includes/class-settings.php:622 +msgid "Enter term slugs" +msgstr "" + +#: includes/class-settings.php:623 +msgid "Enter values" +msgstr "" + +#: includes/class-settings.php:624 +msgid "Layout" +msgstr "" + +#: includes/class-settings.php:626 +msgid "Style" +msgstr "" + +#: includes/class-settings.php:627 +msgid "Row" +msgstr "" + +#: includes/class-settings.php:628 +msgid "Column" +msgstr "" + +#: includes/class-settings.php:629 +msgid "Start typing" +msgstr "" + +#: includes/class-settings.php:630 +#: templates/page-settings.php:158 +#: templates/page-settings.php:186 +msgid "Label" +msgstr "" + +#: includes/class-settings.php:631 +msgid "Unique name" +msgstr "" + +#: includes/class-settings.php:632 +msgid "Facet type" +msgstr "" + +#: includes/class-settings.php:633 +msgid "Copy shortcode" +msgstr "" + +#: includes/class-settings.php:634 +msgid "Data source" +msgstr "" + +#: includes/class-settings.php:635 +msgid "Switch to advanced mode" +msgstr "" + +#: includes/class-settings.php:636 +msgid "Switch to visual mode" +msgstr "" + +#: includes/class-settings.php:637 +msgid "Display" +msgstr "" + +#: includes/class-settings.php:638 +msgid "Query" +msgstr "" + +#: includes/class-settings.php:639 +msgid "Help" +msgstr "" + +#: includes/class-settings.php:640 +msgid "Display Code" +msgstr "" + +#: includes/class-settings.php:641 +msgid "Query Arguments" +msgstr "" + +#: includes/class-settings.php:642 +msgid "Saving" +msgstr "" + +#: includes/class-settings.php:643 +msgid "Indexing" +msgstr "" + +#: includes/class-settings.php:644 +msgid "The index table is empty" +msgstr "" + +#: includes/class-settings.php:645 +msgid "Indexing complete" +msgstr "" + +#: includes/class-settings.php:646 +msgid "Looking" +msgstr "" + +#: includes/class-settings.php:647 +msgid "Purging" +msgstr "" + +#: includes/class-settings.php:648 +msgid "Copied!" +msgstr "" + +#: includes/class-settings.php:649 +msgid "Press CTRL+C to copy" +msgstr "" + +#: includes/class-settings.php:650 +msgid "Activating" +msgstr "" + +#: includes/class-settings.php:651 +#: templates/page-settings.php:115 +msgid "Re-index" +msgstr "" + +#: includes/class-settings.php:652 +msgid "Stop indexer" +msgstr "" + +#: includes/class-settings.php:653 +msgid "Loading" +msgstr "" + +#: includes/class-settings.php:654 +msgid "Importing" +msgstr "" + +#: includes/class-settings.php:655 +msgid "Convert to query args" +msgstr "" + +#: includes/class-settings.php:699 +msgid "full" +msgstr "" + +#: includes/class-updater.php:163 +msgid "Please renew your license for automatic updates." +msgstr "" + +#: includes/class-updater.php:168 +msgid "Please activate your FacetWP license for automatic updates." +msgstr "" + +#: includes/facets/autocomplete.php:11 +msgid "Autocomplete" +msgstr "" + +#: includes/facets/date_range.php:7 +msgid "Date Range" +msgstr "" + +#: includes/facets/date_range.php:137 +#: includes/facets/number_range.php:112 +msgid "Fields to show" +msgstr "" + +#: includes/facets/date_range.php:139 +msgid "Start + End Dates" +msgstr "" + +#: includes/facets/date_range.php:140 +msgid "Exact Date" +msgstr "" + +#: includes/facets/date_range.php:141 +msgid "Start Date" +msgstr "" + +#: includes/facets/date_range.php:142 +msgid "End Date" +msgstr "" + +#: includes/facets/date_range.php:151 +msgid "Display format" +msgstr "" + +#: includes/facets/hierarchy.php:7 +msgid "Hierarchy" +msgstr "" + +#: includes/facets/map.php:24 +msgid "Map" +msgstr "" + +#: includes/facets/map.php:413 +#: includes/facets/map.php:474 +#: includes/facets/proximity.php:255 +msgid "Longitude" +msgstr "" + +#: includes/facets/map.php:414 +msgid "(Optional) use a separate longitude field." +msgstr "" + +#: includes/facets/map.php:420 +msgid "Map ID" +msgstr "" + +#: includes/facets/map.php:422 +msgid "In the Google Cloud Console's Map Management section, create a Map ID and enter it here. Choose a map rendering type (raster or vector) in the Map ID settings. To change your map styling, create a Map Style in the Map Styles section and attach it to the Map ID." +msgstr "" + +#: includes/facets/map.php:425 +msgid "Map design" +msgstr "" + +#: includes/facets/map.php:426 +msgid "In the Google Cloud Console's Map Management section, create a Map ID and enter it in the above Map ID setting. Then create a Map Style in the Map Styles section and attach it to the Map ID." +msgstr "" + +#: includes/facets/map.php:432 +msgid "Enable filtering button label" +msgstr "" + +#: includes/facets/map.php:433 +msgid "Enable map filtering" +msgstr "" + +#: includes/facets/map.php:436 +msgid "Reset button label" +msgstr "" + +#: includes/facets/map.php:441 +msgid "Marker clustering" +msgstr "" + +#: includes/facets/map.php:442 +msgid "Group markers into clusters?" +msgstr "" + +#: includes/facets/map.php:446 +msgid "Marker limit" +msgstr "" + +#: includes/facets/map.php:448 +msgid "Show all results" +msgstr "" + +#: includes/facets/map.php:449 +msgid "Show current page results" +msgstr "" + +#: includes/facets/map.php:454 +msgid "Width" +msgstr "" + +#: includes/facets/map.php:454 +msgid "Height" +msgstr "" + +#: includes/facets/map.php:455 +msgid "Map width / height" +msgstr "" + +#: includes/facets/map.php:456 +msgid "Set width and height of the map. Without units, px is assumed. Use other CSS units if needed, e.g. 100% for responsive full width of the parent container. Don't use 100% for the height if the map's container does not have a fixed height, else the map will have no height and will not show." +msgstr "" + +#: includes/facets/map.php:463 +#: includes/facets/number_range.php:116 +msgid "Min" +msgstr "" + +#: includes/facets/map.php:464 +#: includes/facets/number_range.php:117 +msgid "Max" +msgstr "" + +#: includes/facets/map.php:465 +msgid "Zoom min / max" +msgstr "" + +#: includes/facets/map.php:466 +msgid "Set zoom bounds (between 1 and 20)." +msgstr "" + +#: includes/facets/map.php:473 +msgid "Latitude" +msgstr "" + +#: includes/facets/map.php:475 +msgid "Zoom (1-20)" +msgstr "" + +#: includes/facets/map.php:476 +msgid "Fallback lat / lng / zoom" +msgstr "" + +#: includes/facets/map.php:477 +msgid "Center the map here, and set a custom zoom level, if there are no results." +msgstr "" + +#: includes/facets/map.php:487 +msgid "Info window ajax loading" +msgstr "" + +#: includes/facets/map.php:488 +msgid "Dynamically load marker info window content on click of markers, which could improve load times for pages with many markers." +msgstr "" + +#: includes/facets/map.php:492 +msgid "Info window content" +msgstr "" + +#: includes/facets/map.php:495 +msgid "To search your code, click in the editor, then use Ctrl+F (Windows/Linux) or Cmd+F (Mac)." +msgstr "" + +#: includes/facets/number_range.php:7 +msgid "Number Range" +msgstr "" + +#: includes/facets/number_range.php:114 +msgid "Min + Max" +msgstr "" + +#: includes/facets/number_range.php:115 +msgid "Exact" +msgstr "" + +#: includes/facets/pager.php:10 +msgid "Pager" +msgstr "" + +#: includes/facets/pager.php:213 +msgid "Pager type" +msgstr "" + +#: includes/facets/pager.php:215 +msgid "Page numbers" +msgstr "" + +#: includes/facets/pager.php:216 +msgid "Result counts" +msgstr "" + +#: includes/facets/pager.php:217 +msgid "Load more" +msgstr "" + +#: includes/facets/pager.php:218 +msgid "Per page" +msgstr "" + +#: includes/facets/pager.php:222 +msgid "Inner size" +msgstr "" + +#: includes/facets/pager.php:228 +msgid "Dots label" +msgstr "" + +#: includes/facets/pager.php:234 +msgid "Prev button label" +msgstr "" + +#: includes/facets/pager.php:240 +msgid "Next button label" +msgstr "" + +#: includes/facets/pager.php:246 +msgid "Scroll target" +msgstr "" + +#: includes/facets/pager.php:253 +msgid "Scroll offset" +msgstr "" + +#: includes/facets/pager.php:259 +msgid "Count text (plural)" +msgstr "" + +#: includes/facets/pager.php:265 +msgid "Count text (singular)" +msgstr "" + +#: includes/facets/pager.php:270 +msgid "Count text (no results)" +msgstr "" + +#: includes/facets/pager.php:275 +msgid "Load more text" +msgstr "" + +#: includes/facets/pager.php:280 +msgid "Loading text" +msgstr "" + +#: includes/facets/pager.php:290 +msgid "Per page options" +msgstr "" + +#: includes/facets/proximity.php:13 +msgid "Proximity" +msgstr "" + +#: includes/facets/proximity.php:263 +msgid "Unit of measurement" +msgstr "" + +#: includes/facets/proximity.php:265 +msgid "Miles" +msgstr "" + +#: includes/facets/proximity.php:266 +msgid "Kilometers" +msgstr "" + +#: includes/facets/proximity.php:271 +msgid "Radius UI" +msgstr "" + +#: includes/facets/proximity.php:274 +#: includes/facets/slider.php:7 +msgid "Slider" +msgstr "" + +#: includes/facets/proximity.php:279 +msgid "Radius options" +msgstr "" + +#: includes/facets/proximity.php:285 +msgid "Range (min)" +msgstr "" + +#: includes/facets/proximity.php:290 +msgid "Range (max)" +msgstr "" + +#: includes/facets/proximity.php:295 +msgid "Default radius" +msgstr "" + +#: includes/facets/rating.php:7 +msgid "Star Rating" +msgstr "" + +#: includes/facets/rating.php:118 +msgid "Rating icon" +msgstr "" + +#: includes/facets/rating.php:121 +msgid "Stars" +msgstr "" + +#: includes/facets/rating.php:122 +msgid "Star Outlines" +msgstr "" + +#: includes/facets/rating.php:123 +msgid "Hearts" +msgstr "" + +#: includes/facets/rating.php:128 +msgid "Show ghost ratings" +msgstr "" + +#: includes/facets/rating.php:133 +msgid "Color" +msgstr "" + +#: includes/facets/rating.php:140 +msgid "Selected color" +msgstr "" + +#: includes/facets/rating.php:146 +msgid "Undo color" +msgstr "" + +#: includes/facets/rating.php:152 +msgid "Ghost color" +msgstr "" + +#: includes/facets/reset.php:57 +msgid "Reset UI" +msgstr "" + +#: includes/facets/reset.php:59 +msgid "Button" +msgstr "" + +#: includes/facets/reset.php:65 +msgid "Include / exclude" +msgstr "" + +#: includes/facets/reset.php:68 +msgid "Reset everything" +msgstr "" + +#: includes/facets/reset.php:69 +msgid "Reset only these facets" +msgstr "" + +#: includes/facets/reset.php:70 +msgid "Reset all except these facets" +msgstr "" + +#: includes/facets/reset.php:80 +msgid "Auto-hide" +msgstr "" + +#: includes/facets/search.php:8 +msgid "Search" +msgstr "" + +#: includes/facets/search.php:68 +msgid "WP Default" +msgstr "" + +#: includes/facets/search.php:77 +msgid "Search engine" +msgstr "" + +#: includes/facets/search.php:82 +msgid "Auto refresh" +msgstr "" + +#: includes/facets/search.php:87 +msgid "Order by relevance" +msgstr "" + +#: includes/facets/slider.php:151 +msgid "Format" +msgstr "" + +#: includes/facets/slider.php:158 +msgid "Step" +msgstr "" + +#: includes/facets/sort.php:10 +msgid "Sort" +msgstr "" + +#: includes/facets/sort.php:62 +msgid "Sort options" +msgstr "" + +#: includes/integrations/searchwp/searchwp.php:209 +msgid "Default" +msgstr "" + +#: index.php:86 +msgid "FacetWP requires PHP %s or above. Please contact your host and request a PHP upgrade." +msgstr "" + +#: templates/page-settings.php:107 +#: templates/page-settings.php:147 +#: templates/page-settings.php:151 +msgid "Facets" +msgstr "" + +#: templates/page-settings.php:108 +#: templates/page-settings.php:175 +#: templates/page-settings.php:179 +msgid "Listings" +msgstr "" + +#: templates/page-settings.php:110 +msgid "Support" +msgstr "" + +#: templates/page-settings.php:119 +msgid "Show indexer stats" +msgstr "" + +#: templates/page-settings.php:120 +msgid "Show indexable post types" +msgstr "" + +#: templates/page-settings.php:121 +msgid "Purge the index table" +msgstr "" + +#: templates/page-settings.php:126 +msgid "Save changes" +msgstr "" + +#: templates/page-settings.php:152 +#: templates/page-settings.php:180 +msgid "Add new" +msgstr "" + +#: templates/page-settings.php:159 +#: templates/page-settings.php:187 +msgid "Name" +msgstr "" + +#: templates/page-settings.php:160 +msgid "Type" +msgstr "" + +#: templates/page-settings.php:161 +msgid "Source" +msgstr "" + +#: templates/page-settings.php:162 +msgid "Rows" +msgstr "" + +#: templates/page-settings.php:188 +msgid "Display mode" +msgstr "" + +#: templates/page-settings.php:189 +msgid "Post types" +msgstr "" diff --git a/wp-content/plugins/facetwp/package.json b/wp-content/plugins/facetwp/package.json new file mode 100644 index 000000000..e3a815db3 --- /dev/null +++ b/wp-content/plugins/facetwp/package.json @@ -0,0 +1,14 @@ +{ + "name": "facetwp", + "type": "module", + "devDependencies": { + "@rollup/plugin-buble": "^1.0.3", + "@rollup/plugin-multi-entry": "^6.0.1", + "@rollup/plugin-terser": "^0.4.4", + "rollup": "^2.79.1" + }, + "scripts": { + "build": "rollup -c", + "watch": "rollup -c -w" + } +} diff --git a/wp-content/plugins/facetwp/rollup.config.js b/wp-content/plugins/facetwp/rollup.config.js new file mode 100644 index 000000000..a9a6c1daf --- /dev/null +++ b/wp-content/plugins/facetwp/rollup.config.js @@ -0,0 +1,52 @@ +import terser from '@rollup/plugin-terser'; +import multiEntry from '@rollup/plugin-multi-entry'; +import buble from '@rollup/plugin-buble'; + +export default [{ + input: [ + 'assets/vendor/fUtil/fUtil.js', + 'assets/js/src/event-manager.js', + 'assets/js/src/front.js', + 'assets/js/src/front-facets.js' + ], + output: { + file: 'assets/js/dist/front.min.js', + format: 'iife' + }, + plugins: [ + multiEntry(), + terser() + ] +}, +{ + input: 'assets/js/src/admin.js', + output: { + file: 'assets/js/dist/admin.min.js', + format: 'iife' + }, + plugins: [ + buble( { transforms: { asyncAwait: false } } ) + ] +}, +{ + input: 'assets/vendor/fDate/fDate.js', + output: { + file: 'assets/vendor/fDate/fDate.min.js', + format: 'iife' + }, + plugins: [ + buble(), + terser() + ] +}, +{ + input: 'assets/vendor/nummy/nummy.js', + output: { + file: 'assets/vendor/nummy/nummy.min.js', + format: 'iife' + }, + plugins: [ + buble(), + terser() + ] +}] diff --git a/wp-content/plugins/facetwp/templates/page-settings.php b/wp-content/plugins/facetwp/templates/page-settings.php new file mode 100755 index 000000000..409a6a37d --- /dev/null +++ b/wp-content/plugins/facetwp/templates/page-settings.php @@ -0,0 +1,239 @@ +get_html(); + +// Settings +$settings = FWP()->settings->get_registered_settings(); +$i18n = FWP()->settings->get_i18n_strings(); +$image_sizes = FWP()->settings->get_image_size_labels(); + +// Useful data +$data = FWP()->helper->settings; +$facet_types = FWP()->helper->facet_types; +$data_sources = FWP()->helper->get_data_sources(); +$layout_data = FWP()->builder->get_layout_data(); +$query_data = FWP()->builder->get_query_data(); + +// Generate fields and handle aliases +$facet_fields = []; +$fields = FWP()->settings->get_registered_facet_fields(); + +foreach ( $fields as $name => $field ) { + $output = FWP()->settings->get_facet_field_html( $name ); + $output = preg_replace( '/[ ]+/s', ' ', $output ); + $facet_fields[ $name ] = [ + 'names' => isset( $field['items'] ) ? array_keys( $field['items'] ) : [ $name ], + 'html' => trim( $output ) + ]; +} + +// Get SVG icons +$svg_icons = FWP()->settings->get_svg(); + +// Clone facet settings HTML +$facet_clone = []; +$admin_scripts = ''; + +foreach ( $facet_types as $name => $class ) { + if (method_exists( $class, 'admin_scripts' ) ) { + ob_start(); + $class->admin_scripts(); + $admin_scripts .= ob_get_clean(); + } + + if ( method_exists( $class, 'settings_html' ) ) { + ob_start(); + $class->settings_html(); + $facet_clone[ $name ] = trim( ob_get_clean() ); + } +} + +?> + + + + + + + + + + + + + + + + + + +
    +
    + + v + + + + + +
    +
    +
    + +
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    + Check your browser console if this text doesn't disappear +
    + + + +
    +

    +  »  + {{ getItemLabel() }} +

    +

    + + +

    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    + + +
    + + + +
    +

    +  »  + {{ getItemLabel() }} +

    +

    + + +

    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    + + +
    + + + +
    +
    + $tab ) : ?> + + +
    + + $tab ) : ?> +
    + +
    > +
    + +
    + +
    +
    + + + +
    +
    +
    + +
    + +
    + + + +
    +
    +
    + + + + + +
    +
    diff --git a/wp-content/plugins/facetwp/templates/page-support.php b/wp-content/plugins/facetwp/templates/page-support.php new file mode 100644 index 000000000..d2c9c8998 --- /dev/null +++ b/wp-content/plugins/facetwp/templates/page-support.php @@ -0,0 +1,121 @@ +payment_id = (int) FWP()->helper->get_license_meta( 'payment_id' ); + } + + + function get_html() { + if ( 0 < $this->payment_id ) { + $output = ''; + } + else { + $output = '

    Active License Required

    '; + $output .= '

    Please activate or renew your license to access support.

    '; + } + + return $output; + } + + + function get_sysinfo() { + $plugins = get_plugins(); + $active_plugins = get_option( 'active_plugins', [] ); + $theme = wp_get_theme(); + $parent = $theme->parent(); + + ob_start(); + +?> +Home URL: + +Payment ID: payment_id; ?> + +WordPress Version: + +Theme: get( 'Name' ) . ' ' . $theme->get( 'Version' ); ?> + +Parent Theme: get( 'Name' ) . ' ' . $parent->get( 'Version' ); ?> + + +Debug Mode: helper->get_setting( 'debug_mode' ) ) ? 'ON' : 'OFF'; ?> + +PHP Version: + +MySQL Version: db_version() ); ?> + +Web Server Info: + +PHP Memory Limit: + +WP_MEMORY_LIMIT: + +WP_MAX_MEMORY_LIMIT: + +WP_DEBUG: + +WP_DEBUG_LOG: + + + $plugin_data ) { + $network_plugin = get_plugin_data(WP_PLUGIN_DIR . '/' . $plugin_path); + echo $network_plugin['Name'] . ' ' . $network_plugin['Version'] . '\n'; + } + + $sites = get_sites(); + $current_subsite_id = get_current_blog_id(); + + foreach ( $sites as $subsite ) { + + $subsite_id = get_object_vars( $subsite )['blog_id']; + $subsite_name = get_blog_details( $subsite_id )->blogname; + $active_plugins = get_blog_option( $subsite->blog_id, 'active_plugins' ); + + if ( intval( $subsite_id ) === $current_subsite_id ) { + echo '\n### Sub-site #' . $subsite_id . ' - ' . $subsite_name . ' (Current):\n'; + } else { + echo '\n### Sub-site #' . $subsite_id . ' - ' . $subsite_name . ':\n'; + } + + if ( ! empty( $active_plugins ) ) { + foreach ( $plugins as $plugin_path => $plugin ) { + if ( in_array( $plugin_path, $active_plugins ) ) { + echo $plugin['Name'] . ' ' . $plugin['Version'] . '\n'; + } + } + } else { + echo 'No active plugins' . '\n'; + } + } + + } else { + foreach ( $plugins as $plugin_path => $plugin ) { + if ( in_array( $plugin_path, $active_plugins ) ) { + echo $plugin['Name'] . ' ' . $plugin['Version'] . '\n'; + } + } + } + + $output = ob_get_clean(); + $output = str_replace( '.php', '-php', $output ); + $output = preg_replace( "/[ ]{2,}/", ' ', trim( $output ) ); + $output = str_replace( '\n', '{n}', $output ); + $output = urlencode( $output ); + return $output; + } +} \ No newline at end of file