\r\n \r\n\r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n\r\n\r\n\r\n","// This file has been generated from LT.Web.App_Logic.SearchSetup.Models.SearchResultBaseVue\r\n\r\nexport default class SearchResultBaseVue {\r\n\r\n page: number;\r\n pageCount: number;\r\n pageSize: number;\r\n totalCount: number;\r\n hasResultsForParamsOnly: boolean;\r\n\r\n\r\n\tconstructor(page: number, pageCount: number, pageSize: number, totalCount: number, hasResultsForParamsOnly: boolean){\r\n\t\r\n this.page = page;\r\n this.pageCount = pageCount;\r\n this.pageSize = pageSize;\r\n this.totalCount = totalCount;\r\n this.hasResultsForParamsOnly = hasResultsForParamsOnly;\r\n\t}\r\n}","import FilterDefinitionsVue from '@Types/search/FilterDefinitionsVue';\r\nimport SearchMetaDefinitionsVue from '@Types/search/SearchMetaDefinitionsVue';\r\nimport SearchResultBaseVue from \"@Types/search/SearchResultBaseVue\";\r\nimport { FilterSelectionLocation, FilterSelectionLocationEntry } from '../Types/FilterSelections';\r\nimport GeoBoundingBoxTSVM from '../Types/search/GeoBoundingBoxTSVM';\r\nimport GeoPointTSVM from '../Types/search/GeoPointTSVM';\r\n\r\n\r\n\r\ndeclare const filterDefinitions: FilterDefinitionsVue | undefined;\r\ndeclare const searchMetaDefinitions: SearchMetaDefinitionsVue | undefined;\r\ndeclare const initialSearchResults: SearchResultBaseVue | undefined;\r\n\r\n\r\n/**\r\n * Returns the default filter definitions as set server side, this\r\n * is required for each search related page that needs the specific\r\n * id's etc for the search to work. e.g. amenitiyIds for specific\r\n * filter types.\r\n * \r\n * Note that setting these properties in c# is done through the SearchSetupHelper class.\r\n * \r\n * @returns\r\n */\r\nexport function getFilterDefinitions(): FilterDefinitionsVue {\r\n\r\n if (!filterDefinitions) {\r\n\r\n throw new Error(\"filterDefinitions is undefined\");\r\n }\r\n\r\n return filterDefinitions;\r\n}\r\n\r\n/**\r\n * Returns the search meta definitions as set server side, contains\r\n * specifics about the current search 'page' such as if it is from a \r\n * specific landing page, contains data about the initial location selections,\r\n * the search url it started from, fetchUrl etc.\r\n * \r\n * Note that setting these properties in c# is done through the SearchSetupHelper class.\r\n * \r\n * @returns\r\n */\r\nexport function getSearchMetaDefinitions(): SearchMetaDefinitionsVue {\r\n\r\n if (!searchMetaDefinitions) {\r\n\r\n throw new Error(\"searchMetaDefinitions is undefined\");\r\n }\r\n\r\n return searchMetaDefinitions;\r\n}\r\n\r\n/**\r\n * can check to see if the searchMetaDefinitions are set.\r\n * \r\n * @returns\r\n */\r\nexport function hasSearchMetaDefinitions() {\r\n\r\n return !!searchMetaDefinitions;\r\n}\r\n\r\n/**\r\n * The initial search is done with server side rendering, this method\r\n * returns the data about that initial search, which normally come with a \r\n * new fetch calls. This contains information such as the number of results,\r\n * page, etc.\r\n * \r\n * Note that setting these properties in c# is done through the SearchSetupHelper class.\r\n * \r\n * @returns\r\n */\r\nexport function getSearchBaseResultsFromServerSideRendering(createEmptyIfNotExists: boolean = false): SearchResultBaseVue {\r\n\r\n if (!initialSearchResults) {\r\n\r\n\r\n if (createEmptyIfNotExists) {\r\n return new SearchResultBaseVue(0, 0, 0, 0, false);\r\n }\r\n\r\n throw new Error(\"searchResultBase is undefined\");\r\n }\r\n\r\n return initialSearchResults;\r\n}\r\n\r\n\r\nexport function getBoundingBoxForLocationSelection(filter: FilterSelectionLocation) {\r\n\r\n if (filter.hasValue()) {\r\n\r\n const sm = getSearchMetaDefinitions();\r\n // try get for: sm.countriesWithRegions\r\n const bbs = new Array();\r\n for (let sel of filter.selection) {\r\n\r\n const bb = tryGetBoundingBox(sel);\r\n if (bb) {\r\n bbs.push(bb);\r\n }\r\n }\r\n\r\n // can also try with custom selections:\r\n //sm.locationSelections\r\n\r\n // future: landing page specific locations?\r\n\r\n // merge bbs to single bb (or null if no bb's found)\r\n return mergeBoundingBoxes(bbs);\r\n\r\n } else {\r\n // should reset to outer dimensions?\r\n }\r\n\r\n // fall back:\r\n\r\n\r\n return null;\r\n\r\n\r\n}\r\n\r\n/**\r\n * will merge bounding boxes into a single bounding box.\r\n * For b1 (top-left):\r\n Get the minimum longitude (most left) from all b1 points.\r\n Get the maximum latitude (most top) from all b1 points.\r\n For b2 (bottom-right):\r\n Get the maximum longitude (most right) from all b2 points.\r\n Get the minimum latitude (most bottom) from all b2 points.\r\n * @param bbs\r\n * @returns\r\n */\r\nexport function mergeBoundingBoxes(bbs: Array) {\r\n\r\n if (bbs == null || bbs.length == 0) {\r\n return null;\r\n }\r\n let minLng = bbs[0].b1.lng; // Most left\r\n let maxLat = bbs[0].b1.lat; // Most top\r\n let maxLng = bbs[0].b2.lng; // Most right\r\n let minLat = bbs[0].b2.lat; // Most bottom\r\n\r\n for (let bb of bbs) {\r\n minLng = Math.min(minLng, bb.b1.lng);\r\n maxLat = Math.max(maxLat, bb.b1.lat);\r\n maxLng = Math.max(maxLng, bb.b2.lng);\r\n minLat = Math.min(minLat, bb.b2.lat);\r\n }\r\n\r\n return new GeoBoundingBoxTSVM(new GeoPointTSVM(maxLat, minLng), new GeoPointTSVM(minLat, maxLng));\r\n}\r\n\r\n/**\r\n * will search the searchmeta definitions for the relevant entries (lowest level possible, e.g. city first then up to country).\r\n * will also fall back to country if bb not set in other levels.\r\n * \r\n * @param sel\r\n * @returns\r\n */\r\nexport function tryGetBoundingBox(sel: FilterSelectionLocationEntry): GeoBoundingBoxTSVM | null {\r\n\r\n let bb: GeoBoundingBoxTSVM | null = null;\r\n\r\n // note that data is nested, so we have to zoom in...\r\n\r\n if (sel.countryValueId) {\r\n\r\n const country = getSearchMetaDefinitions().countriesWithRegions.find(t => t.id == sel.countryValueId);\r\n\r\n\r\n if (country) {\r\n\r\n if (sel.regionValueId) {\r\n\r\n const region = country.subEntries.find(t => t.id == sel.regionValueId);\r\n if (region && region.geoBox) {\r\n\r\n bb = region.geoBox;\r\n }\r\n\r\n\r\n\r\n }\r\n\r\n\r\n\r\n\r\n // fallback to country if needed.\r\n if (!bb && country.geoBox) {\r\n\r\n bb = country.geoBox;\r\n }\r\n }\r\n }\r\n\r\n\r\n\r\n return bb;\r\n}\r\n\r\n\r\n\r\n/**\r\n * Helper function that can be used to read the location details for the initial\r\n * server side rendering. we have no complete list of location details, as they\r\n * are fetched from server on typeahead. Therefore we cannot show the initial\r\n * names, and this data is set on the initial search request, inside the searchMetaDefinitions .\r\n * \r\n * @param id\r\n */\r\nexport function getInitialLocationSelectionDetails(id: number) {\r\n\r\n const sm = getSearchMetaDefinitions();\r\n return sm.locationSelections.find(t => t.id == id);\r\n\r\n}\r\n\r\n/**\r\n * checks to see if in the initial search meta definitions there are any\r\n * locations selected. \r\n * @returns\r\n */\r\nexport function hasInitialLocationSelectionOnInit() {\r\n\r\n if (hasSearchMetaDefinitions()) {\r\n\r\n const sm = getSearchMetaDefinitions();\r\n return sm.locationSelections?.length > 0;\r\n }\r\n\r\n return false;\r\n\r\n}\r\n\r\n\r\n// todo: perhaps we should have an alt version that \r\n// should not clear all filters, but only the landing page data specifi.c (like we had\r\n// in a prev version)\r\nexport function clearFiltersAndRedirect() {\r\n\r\n // basically, this should clear everything, and reset full.\r\n // the easiest way it to in fact do a clean redirect to the current search (map/non map).\r\n // as that would automatically clear any and all filters, such as landing page, provider,\r\n // etc.\r\n\r\n let urlAction = getSearchMetaDefinitions().searchUrl;\r\n\r\n if (getSearchMetaDefinitions().isMapSearch) {\r\n\r\n urlAction = getSearchMetaDefinitions().mapSearchUrl;\r\n\r\n }\r\n\r\n let url = new URL(urlAction, window.location.href);\r\n window.location.assign(url.href);\r\n\r\n\r\n ////if (isLandingPage.value || isProviderPage.value) {\r\n //// var searchQuery = filterToQuery(filter, searchModel.paging.clone(0));\r\n //// const url = new URL(searchComposables.getSearchMetaDefinitions().searchUrl, window.location.href).href + '?' + searchQuery;\r\n\r\n //// // console.debug(\"clearning landing page, redirecting to default search page...\");\r\n //// window.location.assign(url);\r\n ////}\r\n}\r\n\r\n\r\n\r\n","export class FilterSelectionDate {\r\n\r\n arrival?: string;\r\n departure?: string;\r\n default?: string;\r\n\r\n}\r\n\r\n\r\n/*\r\n * \r\n * NL selectecetern> \r\n * selection[0] = {name: \"NL\", \"amenitytId: 12\", value: 4 };\r\n * Heel nederland?\r\n * \r\n * \r\n */\r\n\r\nexport class FilterSelectionLocation {\r\n\r\n selection = new Array();\r\n\r\n customName?: string;\r\n\r\n getSelectionName() {\r\n\r\n // selection[0].name\r\n // or if multiplee items return string.join (...)\r\n //or if custom name => return custom name\r\n }\r\n\r\n hasValue(countNoPreference = true): boolean {\r\n // returns true if there is 'any' selection. note that\r\n // this will also unclide the no preference.\r\n\r\n if (countNoPreference) {\r\n return !!this.selection.length;\r\n } else {\r\n\r\n if (this.selection.length) {\r\n\r\n if (this.selection.length == 1) {\r\n\r\n if (this.selection[0].countryNoPreference) {\r\n\r\n // no preference means no value\r\n return false;\r\n }\r\n }\r\n\r\n // either 1 with not a no-country-preference,\r\n // or more than 1\r\n return true; \r\n }\r\n\r\n return false;\r\n }\r\n }\r\n}\r\n\r\nexport class FilterSelectionLocationEntry {\r\n\r\n countryName?: string;\r\n countryValueId?: number;\r\n countryNoPreference!: boolean;\r\n regionName?: string;\r\n regionValueId?: number;\r\n regionNoPreference!: boolean;\r\n cityName?: string;\r\n cityValueId?: number;\r\n cityNoPreference!: boolean;\r\n\r\n /**\r\n * has selected at least a country\r\n */\r\n hasSelection(): boolean {\r\n\r\n return !!this.countryValueId;\r\n }\r\n\r\n getAsFormValue(): string | false {\r\n\r\n if (this.countryValueId) {\r\n let val = `${this.countryValueId}`;\r\n\r\n if (this.regionValueId) {\r\n\r\n val += `,${this.regionValueId}`;\r\n } else if (this.cityValueId) {\r\n\r\n val += `,-1`;\r\n }\r\n\r\n if (this.cityValueId) {\r\n\r\n val += `,${this.cityValueId}`;\r\n }\r\n return val;\r\n }\r\n else {\r\n return false;\r\n }\r\n };\r\n\r\n}\r\n\r\nexport class FilterSelectionCompanions {\r\n\r\n numberOfAdults = 2;\r\n numberOfPets = 0;\r\n numberOfChildren = 0;\r\n numberOfBabies = 0;\r\n\r\n}","import { } from 'vue';\r\nimport { FilterSelectionDate, FilterSelectionLocation, FilterSelectionCompanions } from '@Types/FilterSelections';\r\n\r\n\r\nexport class FilterModel {\r\n\r\n accommodationTypes = new Array();\r\n accommodationTypeNoPreference = true;\r\n criteria = [] as Array>;\r\n bedrooms = 1;\r\n bathrooms = 1;\r\n \r\n date = new FilterSelectionDate();\r\n location = new FilterSelectionLocation();\r\n companions = new FilterSelectionCompanions();\r\n orderBy?: string = \"\";\r\n \r\n public clearCriteria(): void {\r\n\r\n this.criteria.forEach(c => c.splice(0));\r\n }\r\n}\r\n\r\n// class that maps to a querystring set of variables that define a single property \r\nexport class FilterParamEntry {\r\n\r\n paramId: number;\r\n amount: number | null;\r\n minValue: number | null;\r\n values: number[];\r\n\r\n constructor() {\r\n\r\n this.values = [];\r\n this.amount = null;\r\n this.minValue = null;\r\n this.paramId = 0;\r\n }\r\n}\r\n\r\n","import { FilterModel, FilterParamEntry } from '@Apps/Search/Src/Models/FilterModel';\r\nimport PagingModel from '@Apps/Search/Src/Models/PagingModel';\r\nimport * as searchComposables from '@Composables/SearchComposables';\r\nimport { FilterSelectionLocationEntry } from '@Types/FilterSelections';\r\n\r\n\r\nconst getLocationFromDefinitions = (id: number) => {\r\n\r\n return searchComposables.getInitialLocationSelectionDetails(id);\r\n};\r\n\r\nconst urlConvertersDefaultModelBinder = {\r\n\r\n /**\r\n * parses a list of values (from query string usually) and\r\n * matches each param into a filter param entry.\r\n * where (query string) values are given as: is[0].param=1&is[0].value=3&is[1].param=2\r\n * this will group together based on the array indexer, so all is[0] are\r\n * bound together in the same object.\r\n * @param prefix\r\n * @param params\r\n */\r\n parse(prefix: string, params: { key: string, val: string }[]): Array {\r\n\r\n let result = new Array();\r\n\r\n // inline hlper function to get the value from the key\r\n const getParamNameFromKey = (key: string, ident: string): string => {\r\n\r\n return key.substr(ident.length + 1);\r\n };\r\n\r\n // these all start with \"key[xxx]\", and then contain paramid, amount or minvalue\r\n // we need to group them together based on the key, but we dont actually need the key\r\n // so we only need to check if the prefix/key is the same, and we need to remove the prefix\r\n // to determine which attribute it is\r\n let curIdent = \"--\";\r\n let curParam: FilterParamEntry | null = null;\r\n params.forEach((p) => {\r\n\r\n if (p.key.startsWith(curIdent)) {\r\n\r\n // belongs to the same item,\r\n // parse value and add to curParam\r\n }\r\n else {\r\n\r\n // new entry, so determine new curIdent,\r\n // create new curParam\r\n // add to results\r\n curIdent = p.key.substr(0, p.key.indexOf(\"]\") + 1);\r\n curParam = new FilterParamEntry();\r\n result.push(curParam);\r\n }\r\n\r\n // using current curId and curParam, parse id/value\r\n let paramName = getParamNameFromKey(p.key, curIdent);\r\n // note that values array will have the paramName formatted as values[0], values[1] etc.\r\n if (paramName.toLowerCase().startsWith('values')) {\r\n paramName = 'values';\r\n }\r\n // since we cannot be sure of casing, we need to lower compare\r\n Object.keys(curParam!).forEach(key => {\r\n\r\n \r\n\r\n if (key.toLowerCase() == paramName) {\r\n\r\n // workaround typescript forced type\r\n if (Array.isArray((curParam as any)[key])) {\r\n\r\n // we currently only have a values array... which is number\r\n (curParam as any)[key].push(+p.val);\r\n }\r\n else {\r\n (curParam as any)[key] = +p.val;\r\n }\r\n\r\n }\r\n });\r\n\r\n\r\n\r\n\r\n\r\n });\r\n\r\n\r\n return result;\r\n },\r\n\r\n\r\n toIs(index: number, amenityId: number, ...valueId: number[]) {\r\n\r\n //using old style c# modelbinder for now:\r\n let str = `Is[${index}].ParamId=${amenityId}`;\r\n for (let i = 0; i < valueId.length; i++) {\r\n str += `&Is[${index}].Values=${valueId[i]}`;\r\n }\r\n return str;\r\n },\r\n\r\n\r\n\r\n toAmount(index: number, amenityId: number, amount: number, isMin = true) {\r\n\r\n //using old style c# modelbinder for now:\r\n return `Amount[${index}].ParamId=${amenityId}&Amount[${index}].${(isMin ? \"MinValue\" : \"MaxValue\")}=${amount}`;\r\n\r\n },\r\n\r\n toHas(index: number, ...amenityId: number[]) {\r\n\r\n return amenityId.map((v, i) => `Has[${index + i}].ParamId=${v}`).join('&');\r\n\r\n },\r\n\r\n toHasSearch(index: number, ...amenityId: number[]) {\r\n\r\n return amenityId.map((v, i) => `HasSearch[${index + i}].ParamId=${v}`).join('&');\r\n\r\n },\r\n\r\n join(...params: Array>) {\r\n\r\n return params.filter(s => s.length > 0).flat().join(\"&\");\r\n }\r\n\r\n};\r\n\r\n\r\nexport function toUrlOrderBy(filter: FilterModel): Array {\r\n\r\n const other = new Array();\r\n\r\n if (filter.orderBy !== undefined && filter.orderBy !== \"\") {\r\n other.push(`OrderBy=${filter.orderBy}`);\r\n }\r\n\r\n return other;\r\n}\r\n\r\nexport function toUrlDate(filter: FilterModel): Array {\r\n\r\n const other = new Array();\r\n\r\n if (filter.date.arrival && filter.date.departure) {\r\n\r\n other.push(`Date.Min=${filter.date.arrival}`);\r\n other.push(`Date.Max=${filter.date.departure}`);\r\n\r\n }\r\n\r\n return other;\r\n}\r\n\r\nexport function toUrlCompanions(filter: FilterModel): Array {\r\n\r\n const other = new Array();\r\n\r\n if (filter.companions.numberOfAdults) {\r\n\r\n other.push(`NumberOfPersons=${filter.companions.numberOfAdults}`);\r\n }\r\n\r\n\r\n if (filter.companions.numberOfPets) {\r\n\r\n other.push(`NumberOfPets=${filter.companions.numberOfPets}`);\r\n }\r\n\r\n\r\n if (filter.companions.numberOfBabies) {\r\n\r\n other.push(`NumberOfBabies=${filter.companions.numberOfBabies}`);\r\n }\r\n\r\n return other;\r\n}\r\n\r\n\r\nexport function toUrl(filter: FilterModel, paging?: PagingModel): string {\r\n\r\n\r\n const has = new Array();\r\n let hasIndex = 0;\r\n const is = new Array();\r\n let isIndex = 0;\r\n\r\n const date = [];\r\n\r\n const amount = new Array();\r\n let amountIndex = 0;\r\n\r\n const hasSearch = new Array();\r\n let hasSearchIndex = 0;\r\n\r\n if (!filter.accommodationTypeNoPreference && filter.accommodationTypes.length > 0) {\r\n\r\n // only add specific preferences\r\n is.push(urlConvertersDefaultModelBinder.toIs(isIndex++, searchComposables.getFilterDefinitions().amenityIdAccommodationType, ...filter.accommodationTypes));\r\n }\r\n\r\n\r\n // validate for default.. no need to send default filter..\r\n if (filter.bedrooms !== 1) {\r\n\r\n amount.push(urlConvertersDefaultModelBinder.toAmount(amountIndex++, searchComposables.getFilterDefinitions().amenityIdBedrooms, filter.bedrooms));\r\n }\r\n if (filter.bathrooms !== 1) {\r\n\r\n amount.push(urlConvertersDefaultModelBinder.toAmount(amountIndex++, searchComposables.getFilterDefinitions().amenityIdBathrooms, filter.bathrooms));\r\n }\r\n\r\n\r\n\r\n\r\n\r\n // get all search criteria in one go:\r\n const searchCriteriaFlattened = filter.criteria.flat();\r\n if (searchCriteriaFlattened.length > 0) {\r\n\r\n hasSearch.push(urlConvertersDefaultModelBinder.toHasSearch(hasSearchIndex++, ...searchCriteriaFlattened));\r\n }\r\n\r\n\r\n const other = new Array();\r\n\r\n // currently these are added as regular values:\r\n if (filter.location.hasValue()) {\r\n\r\n\r\n filter.location.selection.forEach((selection, index) => {\r\n\r\n var fs = selection.getAsFormValue();\r\n if (fs !== false) {\r\n\r\n\r\n other.push(`Locations[${index}]=${fs}`);\r\n\r\n }\r\n });\r\n\r\n\r\n }\r\n\r\n other.push(...toUrlDate(filter));\r\n\r\n other.push(...toUrlCompanions(filter));\r\n\r\n \r\n\r\n\r\n if (paging !== undefined && paging.hasPaging()) {\r\n // no need to set page and pagesize if value is 0.. \r\n other.push(`Page=${paging.page}`);\r\n other.push(`PageSize=${paging.pageSize}`);\r\n\r\n }\r\n\r\n other.push(...toUrlOrderBy(filter));\r\n\r\n \r\n\r\n const searchQuery = urlConvertersDefaultModelBinder.join(has, is, hasSearch, amount, other);\r\n return searchQuery;\r\n\r\n}\r\n\r\n\r\nexport function fromUrlBase(filter: FilterModel, search: string): FilterModel {\r\n\r\n const params = new URLSearchParams(search.toLowerCase());\r\n\r\n\r\n // using default model binder c#\r\n const allIs = new Array<{ key: string; val: string }>();\r\n const allHas = new Array<{ key: string; val: string }>();\r\n const allAmount = new Array<{ key: string; val: string }>();\r\n const allHasSearch = new Array<{ key: string; val: string }>();\r\n\r\n // get each valid key:\r\n // has, date, is, hasSearch, amount\r\n params.forEach((val, key) => {\r\n\r\n\r\n // Date:\r\n // Date.Min\r\n if (key.startsWith('date.')) {\r\n\r\n if (key === 'date.min') {\r\n\r\n // todo: should check is valid date\r\n\r\n filter.date.arrival = val;\r\n }\r\n else if (key === 'date.max') {\r\n\r\n filter.date.departure = val;\r\n }\r\n }\r\n\r\n else if (key === \"defaultdate\") {\r\n filter.date.default = val;\r\n }\r\n\r\n else if (key === \"numberofpersons\") {\r\n\r\n filter.companions.numberOfAdults = +val;\r\n }\r\n else if (key === \"numberofbabies\") {\r\n\r\n filter.companions.numberOfBabies = +val;\r\n }\r\n else if (key === \"numberofpets\") {\r\n\r\n filter.companions.numberOfPets = +val;\r\n }\r\n else if (key === \"numberofchildren\") {\r\n\r\n filter.companions.numberOfChildren = +val;\r\n }\r\n\r\n // usually we have only one location. but more is possible. in that case we need to get all the values\r\n // from the definitions.\r\n else if (key.startsWith(\"locations[\")) {\r\n\r\n // we need to get the index, as that should match with the initLocations..\r\n const locIndex = + key.replaceAll(\"locations[\", \"\").replaceAll(\"]\", \"\");\r\n\r\n // val => string with one or more selections. e.g. 1,130,10 (meaning netherlands, zeeland, burgh haamstede)\r\n // 1 => nederland\r\n // 1, 130 => nederland, zeeland\r\n // 1,-1, 10 => nederland + burghaamstede...(no region selected)\r\n\r\n const locIds = val.split(',').map(t => +t);\r\n if (locIds.length > 0) {\r\n\r\n const le = new FilterSelectionLocationEntry();\r\n filter.location.selection.push(le);\r\n\r\n le.countryValueId = locIds[0]; // country\r\n const countryData = getLocationFromDefinitions(le.countryValueId);\r\n if (countryData) {\r\n le.countryName = countryData.label;\r\n }\r\n\r\n if (locIds.length > 1) {\r\n\r\n if (locIds[1] != -1) { // -1 => skip, no region selected, should have a city selected?\r\n\r\n le.regionValueId = locIds[1];\r\n const regionData = getLocationFromDefinitions(le.regionValueId);\r\n if (regionData) {\r\n le.regionName = regionData.label;\r\n }\r\n }\r\n }\r\n\r\n if (locIds.length > 2) {\r\n\r\n le.cityValueId = locIds[2];\r\n const cityData = getLocationFromDefinitions(le.cityValueId);\r\n if (cityData) {\r\n le.cityName = cityData.label;\r\n }\r\n\r\n }\r\n }\r\n\r\n }\r\n\r\n // the rest is parsing of the \"Is\", \"Has\", hasSearch, \"amount\",.\r\n // these all work the same, more or less\r\n // some are converted to specific properties, such as bedrooms, bathrooms, accommodation type and location.\r\n else if (key.startsWith(\"is[\")) {\r\n allIs.push({ key, val });\r\n }\r\n else if (key.startsWith(\"has[\")) {\r\n allHas.push({ key, val });\r\n }\r\n else if (key.startsWith(\"amount[\")) {\r\n allAmount.push({ key, val });\r\n }\r\n else if (key.startsWith(\"hassearch[\")) {\r\n allHasSearch.push({ key, val });\r\n }\r\n\r\n else if (key === \"orderby\") {\r\n filter.orderBy = val;\r\n }\r\n\r\n\r\n // note that paging data will normally be set through the init settings\r\n // as this is set server side. therefore we do not need to read it from the url \r\n //(since values will be absent in the url, but will be set in from the server )\r\n //else if (key === \"page\") { // do not use starts with here, because pageSize and pageCount will also trigger\r\n\r\n // paging.setPage(val);\r\n //}\r\n //else if (key === \"pageCount\") {\r\n\r\n // paging.setPageCount(val);\r\n //}\r\n //else if (key === \"pageSize\") {\r\n\r\n // paging.setPageSize(val);\r\n //}\r\n\r\n });\r\n\r\n\r\n const isParams = urlConvertersDefaultModelBinder.parse(\"is\", allIs);\r\n const hasParams = urlConvertersDefaultModelBinder.parse(\"has\", allHas);\r\n const hasSearchParams = urlConvertersDefaultModelBinder.parse(\"hassearch\", allHasSearch);\r\n const amountParams = urlConvertersDefaultModelBinder.parse(\"amount\", allAmount);\r\n\r\n // currently we do not use these params directly, but they are converted to specific types.\r\n\r\n // isParams => accommodation type and location\r\n\r\n // try get\r\n const hasAccType = isParams.filter((v) => v.paramId === searchComposables.getFilterDefinitions().amenityIdAccommodationType).flatMap(t => t.values);\r\n if (hasAccType.length > 0) {\r\n filter.accommodationTypes.push(...hasAccType);\r\n filter.accommodationTypeNoPreference = false;\r\n } else {\r\n filter.accommodationTypeNoPreference = true;\r\n }\r\n\r\n // location is now a seperate property on the search fields.\r\n\r\n //// currently we have no way to define if a isParam is used for a location, since we do not specify those definitions\r\n //// for now: anything that is not accommodation type is a location. if we have more Is definitiions in the future,\r\n //// we either need to check those as well, or move the location to a specific property for the querystring..\r\n //const locationSelection = isParams.find(v => ![definitions.amenityIdAccommodationType /* , add any other definitions to filter.. */].some(d => d == v.paramId));\r\n //if (locationSelection) {\r\n\r\n // filter.location.amenityId = locationSelection.paramId;\r\n // filter.location.valueId = locationSelection.values[0];\r\n\r\n //}\r\n\r\n // amountParams => bedrooms, bathrooms,\r\n const bedroomSelection = amountParams.find((v) => v.paramId === searchComposables.getFilterDefinitions().amenityIdBedrooms);\r\n if (bedroomSelection) {\r\n\r\n filter.bedrooms = bedroomSelection.minValue ?? 1;\r\n }\r\n\r\n const bathroomSelection = amountParams.find((v) => v.paramId === searchComposables.getFilterDefinitions().amenityIdBathrooms);\r\n if (bathroomSelection) {\r\n\r\n filter.bathrooms = bathroomSelection.minValue ?? 1;\r\n }\r\n\r\n\r\n\r\n // hasParams => currently not used since we use search criteria and not amenities directly.\r\n\r\n\r\n // hasSearch => criteria\r\n // note that the criteria is a bit of a special collection, as it contains\r\n // selected items based on the group to which the criterium belongs.\r\n // so we need to first check to which criteria group our selected item belongs.\r\n\r\n hasSearchParams.forEach((sp) => {\r\n\r\n // we only need param id here\r\n // search our search criteria definitions:\r\n loopGroups:\r\n for (let groupIndex = 0; groupIndex < searchComposables.getFilterDefinitions().searchCriteriaGrouping.length; groupIndex++) {\r\n\r\n loopCriteria:\r\n for (let critIndex = 0; critIndex < searchComposables.getFilterDefinitions().searchCriteriaGrouping[groupIndex].items.length; critIndex++) {\r\n\r\n if (searchComposables.getFilterDefinitions().searchCriteriaGrouping[groupIndex].items[critIndex].keyId == sp.paramId) {\r\n\r\n // add the current search param to:\r\n filter.criteria[groupIndex].push(sp.paramId);\r\n\r\n // break out of group loop, since we found what we need for the current search param.\r\n break loopGroups;\r\n }\r\n }\r\n }\r\n\r\n\r\n\r\n });\r\n\r\n\r\n\r\n return filter;\r\n}\r\n\r\n\r\n/**\r\n * Reads and fills the filter model from either the current search param on the url, or\r\n * in case of a rewrite, it will use the set initquerystring from the url rewrite parameters \r\n * as passed down from the controller.\r\n * \r\n * @param filter\r\n */\r\nexport function fromUrlOrRewrite(filter: FilterModel): FilterModel {\r\n\r\n if (searchComposables.hasSearchMetaDefinitions() && searchComposables.getSearchMetaDefinitions().queryStringFromRewrite) {\r\n\r\n return fromUrlBase(filter, searchComposables.getSearchMetaDefinitions().queryStringFromRewrite);\r\n }\r\n\r\n\r\n return fromUrlBase(filter, window.location.search);\r\n\r\n\r\n\r\n}","export default class PagingModel {\r\n\r\n\r\n pageCount?: number;\r\n page?: number;\r\n pageSize?: number; // currently not used internally\r\n\r\n\r\n /**\r\n * Will create a new paging model for a new page.\r\n * will keep the current pageSize.\r\n * \r\n * @param newPage\r\n */\r\n clone(newPage: number) {\r\n\r\n const cloned = new PagingModel();\r\n cloned.page = newPage;\r\n cloned.pageSize = this.pageSize;\r\n cloned.pageCount = this.pageCount;\r\n return cloned;\r\n }\r\n\r\n hasPaging(): boolean {\r\n\r\n // either pageCount is undefined, or 0 (default) otherwise it should be 1\r\n // for the 'first' page.\r\n\r\n return this.pageCount === undefined? false: (this.pageCount > 1);\r\n\r\n }\r\n\r\n\r\n private getValueFor(val: number| string | undefined): number | undefined {\r\n\r\n let returnValue = undefined;\r\n if (val !== undefined && val !== \"\") {\r\n\r\n if (typeof val === \"number\") {\r\n return val;\r\n }\r\n returnValue = parseInt(val);\r\n if (isNaN(returnValue)) {\r\n returnValue = undefined;\r\n }\r\n }\r\n\r\n\r\n return returnValue;\r\n }\r\n\r\n\r\n setPage(val: number | undefined) {\r\n\r\n this.page = this.getValueFor(val);\r\n }\r\n\r\n setPageCount(val: number | undefined) {\r\n\r\n this.pageCount = this.getValueFor(val);\r\n\r\n }\r\n\r\n setPageSize(val: number | undefined) {\r\n\r\n this.pageSize = this.getValueFor(val);\r\n }\r\n\r\n\r\n getPrevious(): number {\r\n\r\n \r\n if (this.page !== undefined) {\r\n\r\n if (this.page > 0) return this.page - 1;\r\n }\r\n return -1;\r\n }\r\n\r\n getNext() {\r\n\r\n if (this.page !== undefined && this.pageCount !== undefined) {\r\n\r\n if (this.page < (this.pageCount - 1)) return this.page + 1;\r\n }\r\n return -1;\r\n }\r\n\r\n\r\n}\r\n","import PagingModel from '@Apps/Search/Src/Models/PagingModel';\r\nimport { FilterModel } from '@Apps/Search/Src/Models/FilterModel';\r\n\r\nexport default class SearchModel {\r\n\r\n\r\n filter = new FilterModel();\r\n\r\n paging = new PagingModel();\r\n\r\n totalCount = 0;\r\n}","import { } from 'vue';\r\nimport SearchModel from '@Apps/Search/Src/Models/SearchModel';\r\nimport { fromUrlOrRewrite } from '@Apps/Search/Src/Logic/urlConvert';\r\nimport * as searchComposables from '@Composables/SearchComposables';\r\n\r\n\r\n//import PagingModel from '@Apps/Search/Src/Models/PagingModel';\r\n//import { FilterModel, FilterParamEntry } from '@Apps/Search/Src/Models/FilterModel';\r\n//import { FilterSelectionDate, FilterSelectionLocation, FilterSelectionCompanions } from '@Types/FilterSelections';\r\n\r\n\r\n\r\nexport function setupSearchModel(): SearchModel {\r\n\r\n \r\n const searchModel = new SearchModel();\r\n\r\n for (let i = 0; i < searchComposables.getFilterDefinitions().criteriaGroupCount; i++) {\r\n\r\n searchModel.filter.criteria.push(new Array());\r\n }\r\n\r\n // WRONG: setting non UI related data on filter, can trigger \r\n // a watch update when changed after a fetch.\r\n // these properties should be set on a different object which is not watched.\r\n // e.g. not the filter model, but a 'Search Model \"?\r\n\r\n\r\n // total count can be read from 'global'\r\n searchModel.totalCount = searchComposables.getSearchBaseResultsFromServerSideRendering().totalCount;\r\n\r\n // paging init can also be read from global;\r\n searchModel.paging.page = searchComposables.getSearchBaseResultsFromServerSideRendering().page;\r\n searchModel.paging.pageCount = searchComposables.getSearchBaseResultsFromServerSideRendering().pageCount;\r\n searchModel.paging.pageSize = searchComposables.getSearchBaseResultsFromServerSideRendering().pageSize;\r\n\r\n\r\n\r\n fromUrlOrRewrite(searchModel.filter);\r\n\r\n return searchModel;\r\n}\r\n\r\n\r\n\r\n","import { watch, computed, ComputedRef, reactive, ref, Ref } from 'vue';\r\n\r\nimport ICombinedFilterComponent from '@Components/CombinedFilter/ICombinedFilterComponent';\r\n\r\n\r\nexport interface CombinedFilterSetupResult {\r\n\r\n isFilterVisible: Ref;\r\n\r\n onSearchBarAction: (targetArea: string | undefined) => void;\r\n onHideFilter: () => void;\r\n\r\n\r\n}\r\n\r\n\r\nexport function setupCombinedFilter(filterInstance: Ref): CombinedFilterSetupResult {\r\n //performs setup for the two components and their interaction. that is what the ICombinedFilterComponent is for,\r\n // if not null, it will be used to trigger the correct area from the bar click.\r\n\r\n\r\n // note that depending on mobile or desktop, we might\r\n // actually want to have a different default value.\r\n // e.g. for mobile hidden, but for large desktop, (where it collapses left/right)\r\n // we might want to have it visible.\r\n\r\n // todo: we have done something in BHVK to do some default setup based on initial\r\n // screen size. We might want to do something similar here. is not perfect,\r\n // but works for most default cases.\r\n const isMapFilterVisible = ref(false);\r\n\r\n // handles clicks from the search bar action. initially\r\n // only opens the bar, but with parameters we can also determine\r\n // a specific action.\r\n const onSearchBarAction = (targetArea: string | undefined) => {\r\n\r\n\r\n filterInstance.value?.onFocusArea(targetArea);\r\n isMapFilterVisible.value = !isMapFilterVisible.value;\r\n\r\n\r\n };\r\n\r\n const onHideFilter = () => {\r\n\r\n\r\n isMapFilterVisible.value = false;\r\n };\r\n\r\n return {\r\n\r\n isFilterVisible: isMapFilterVisible,\r\n onSearchBarAction,\r\n onHideFilter\r\n };\r\n\r\n}","// This file has been generated from LT.Core.Logic.Search.SearchOrderingOption\r\nexport enum SearchOrderingOption {\r\n\r\n \r\n Default = 0,\r\n PriceAsc = 11,\r\n PriceDesc = 12,\r\n MaxGuestCountAsc = 21,\r\n MaxGuestCountDesc = 22\r\n\r\n}\r\nexport default SearchOrderingOption;","import { FilterModel } from \"@Apps/Search/Src/Models/FilterModel\";\r\n\r\n\r\n// this is v1 search\r\n//const accommodationLinkSelector = \".accommodation-card a\";\r\n\r\nconst accommodationLinkSelector = \"a.accommodation-card-v2023\";\r\n\r\n//const searchUrlParameter = \"searchUrl\";\r\nconst scrollPositionParameter = \"scrolly\";\r\nexport function addNewHistoryEntry(search: string): void {\r\n // update location?\r\n // note does not contain fragment\r\n const browserAddressHref = new URL(window.location.pathname, window.location.href).href + \"?\" + search;\r\n history.replaceState({ search }, \"\", browserAddressHref);\r\n\r\n}\r\n\r\nexport function getCurrentUrlAsRelative(): string {\r\n\r\n return location.pathname + location.search;\r\n\r\n}\r\n\r\n/**\r\n * will url encode the current url and format it as \"returnUrl=[currenturl as encodeUriComponent()];\r\n * */\r\nexport function getCurrentUrlAsReturnUrl(paramName = \"returnUrl\", includeScroll = false): string {\r\n\r\n let url = getCurrentUrlAsRelative();\r\n if (includeScroll) {\r\n\r\n\r\n if (url.indexOf(\"?\") >= 0) {\r\n url += \"&\";\r\n } else {\r\n url += \"?\";\r\n }\r\n\r\n url += scrollPositionParameter + \"=\" + 123; // todo:: get scroll.. onload: scroll to\r\n\r\n }\r\n\r\n return paramName + \"=\" + encodeURIComponent(url);\r\n}\r\n\r\nexport function addHistoryOnClick(container: HTMLElement, filter: FilterModel, clearAction: undefined | (() => void) = undefined): void {\r\n\r\n\r\n container.addEventListener(\"click\", function (e: Event) {\r\n\r\n const s = e.target as HTMLElement;\r\n if (s && s.classList.contains(\"btn-favorite\")){\r\n\r\n e.preventDefault();\r\n e.stopPropagation();\r\n return;\r\n }\r\n\r\n const link = s.closest(accommodationLinkSelector);\r\n if (link) {\r\n\r\n e.preventDefault();\r\n e.stopPropagation();\r\n\r\n const linkHref = link.getAttribute(\"href\")!;\r\n\r\n\r\n\r\n window.location.href = linkHref + (linkHref.indexOf(\"?\") >= 0 ? \"&\" : \"?\") + getCurrentUrlAsReturnUrl();\r\n }\r\n else {\r\n const reset = s.closest(\"[data-trigger-reset-filter]\");\r\n if (reset) {\r\n\r\n if (clearAction) {\r\n clearAction();\r\n } else {\r\n // this is done on the old search, but will in fact not clear all search filters.\r\n e.preventDefault();\r\n e.stopPropagation();\r\n filter.clearCriteria();\r\n }\r\n }\r\n }\r\n\r\n });\r\n\r\n}","import { watch, computed, ComputedRef, reactive, ref, Ref } from 'vue';\r\nimport { FilterModel, FilterParamEntry } from '@Apps/Search/Src/Models/FilterModel';\r\nimport PagingModel from '@Apps/Search/Src/Models/PagingModel';\r\nimport * as searchComposables from '@Composables/SearchComposables';\r\n\r\nimport { addNewHistoryEntry } from '@Apps/Search/Src/Logic/historyLogic';\r\nimport { toUrl } from '@Apps/Search/Src/Logic/urlConvert';\r\nimport SearchModel from '@Apps/Search/Src//Models/SearchModel';\r\nimport SearchOrderingOption from '../Types/search/SearchOrderingOption';\r\n\r\n\r\nexport interface SetupResult {\r\n\r\n stateIsFetching: ComputedRef;\r\n triggerNewSearch: () => void;\r\n\r\n}\r\n\r\nexport interface IFetchSearchResult {\r\n pageCount: number | undefined;\r\n page: number | undefined;\r\n pageSize: number | undefined;\r\n totalCount: number;\r\n}\r\n\r\nconst queueDelay = 200;\r\nlet _searchModel: SearchModel;\r\nlet _onProcessData: (data: IFetchSearchResult) => void;\r\n\r\nconst fetchQueue = reactive({\r\n\r\n currentTimeout: -1,\r\n activeFetches: new Map(),\r\n newFetchWaiting: false as number | boolean\r\n\r\n\r\n});\r\n\r\n\r\nexport function setup(searchModel: SearchModel, onProcessData: (data: IFetchSearchResult) => void): SetupResult {\r\n\r\n\r\n _searchModel = searchModel;\r\n _onProcessData = onProcessData;\r\n\r\n /**\r\n * true if there is a fetch requesting running to get \r\n * new search results.\r\n **/\r\n const stateIsFetching = computed(() => {\r\n\r\n return isInDebounce() || isFetching() || fetchQueue.newFetchWaiting === true;\r\n\r\n });\r\n\r\n\r\n return {\r\n\r\n stateIsFetching,\r\n triggerNewSearch: changeTriggered\r\n };\r\n}\r\n\r\n\r\n\r\nfunction isInDebounce() {\r\n\r\n return fetchQueue.currentTimeout > -1;\r\n}\r\n\r\nfunction isFetching() {\r\n\r\n return fetchQueue.activeFetches.size > 0;\r\n}\r\n\r\nfunction changeTriggered() {\r\n\r\n // this means a change has triggered on the model, and we should fetch new data, (with timeout)\r\n // but wait for the current fetch to complete, (if any)\r\n\r\n if (isInDebounce()) {\r\n\r\n // extend the debounce by resetting and setting a new timeout\r\n clearTimeout(fetchQueue.currentTimeout);\r\n fetchQueue.currentTimeout = setTimeout(fetchNext, queueDelay);\r\n }\r\n else if (isFetching()) {\r\n\r\n // already fetching, so we should wait until the fetch is complete\r\n fetchQueue.newFetchWaiting = Date.now();\r\n }\r\n else {\r\n\r\n // first trigger:\r\n fetchQueue.currentTimeout = setTimeout(fetchNext, queueDelay);\r\n }\r\n}\r\n\r\nexport function fetchPage(page: number) {\r\n\r\n // force clear timeout\r\n clearTimeout(fetchQueue.currentTimeout);\r\n fetchNext(page);\r\n}\r\n\r\n\r\nfunction fetchNext(page?: number) {\r\n\r\n // when fetching the next item, it means that the current timestamp has expired,\r\n // we add that to the queue\r\n const fetchId = fetchQueue.currentTimeout;\r\n fetchQueue.activeFetches.set(fetchId, true);\r\n fetchQueue.currentTimeout = -1;\r\n\r\n // always create new paging for page 0 unless specifically specified\r\n const url = toUrl(_searchModel.filter, _searchModel.paging.clone(page === undefined ? 0 : page));\r\n fetchDataAsync(url, fetchId);\r\n}\r\n\r\n\r\n/**\r\n * when a fetch call has ended, either succesfully or with errors, we need to \r\n * update our fetch queue.\r\n */\r\nfunction fetchEnded(fetchId: number) {\r\n\r\n // we can clear our fetchQueue map:\r\n fetchQueue.activeFetches.delete(fetchId); // we can check the result value to see if we actually had a fetchId...\r\n\r\n if (fetchQueue.newFetchWaiting !== false) {\r\n\r\n const timeout = fetchQueue.newFetchWaiting as number;\r\n\r\n // set timeout\r\n let diff = Date.now() - timeout;\r\n if (diff < 1) {\r\n diff = 1;\r\n }\r\n\r\n // should not have a new timeout, but just in case:\r\n if (fetchQueue.currentTimeout > -1) {\r\n clearTimeout(fetchQueue.currentTimeout);\r\n }\r\n\r\n fetchQueue.currentTimeout = setTimeout(fetchNext, diff);\r\n fetchQueue.newFetchWaiting = false; // reset\r\n }\r\n}\r\n\r\nexport enum CreateSearchUrlType {\r\n\r\n fetch = 0,\r\n search = 1,\r\n mapSearch = 2,\r\n /**\r\n * current means use whatever is currently active\r\n */\r\n current = 3,\r\n\r\n /**\r\n * Means switch from map to non map, or vice versa. for landingpages\r\n * this will automatically include the landing page id as part of the url rewrite.\r\n */\r\n switch = 4\r\n}\r\n\r\n\r\nexport function createSearchUrl(searchQuery: string, type: CreateSearchUrlType, includePath = true) {\r\n\r\n\r\n // determine which url we need..\r\n // if fetch, then easy, if not, check if map is required.\r\n let urlAction = searchComposables.getSearchMetaDefinitions().fetchUrl;\r\n\r\n if (type == CreateSearchUrlType.current) {\r\n\r\n if (searchComposables.getSearchMetaDefinitions().isMapSearch) {\r\n urlAction = searchComposables.getSearchMetaDefinitions().searchUrl;\r\n }\r\n else {\r\n urlAction = searchComposables.getSearchMetaDefinitions().mapSearchUrl;\r\n }\r\n\r\n }\r\n else if (type == CreateSearchUrlType.mapSearch) {\r\n\r\n urlAction = searchComposables.getSearchMetaDefinitions().mapSearchUrl;\r\n }\r\n else if (type == CreateSearchUrlType.search) {\r\n\r\n urlAction = searchComposables.getSearchMetaDefinitions().searchUrl;\r\n }\r\n else if (type == CreateSearchUrlType.switch) {\r\n\r\n urlAction = searchComposables.getSearchMetaDefinitions().switchUrl;\r\n }\r\n\r\n\r\n let url = new URL(urlAction, window.location.href).href;\r\n\r\n if (searchQuery) {\r\n\r\n url = url + '?' + searchQuery;\r\n }\r\n\r\n if (includePath) {\r\n\r\n url = url + (searchQuery?\"&\":\"?\") + (`path=${window.location.pathname}`);\r\n }\r\n\r\n \r\n // for switch we do not need these values as the url would include the id / rewritten path.\r\n if (type != CreateSearchUrlType.switch) {\r\n if (searchComposables.getSearchMetaDefinitions().landingPageId != null) {\r\n\r\n url += \"&landingPageId=\" + searchComposables.getSearchMetaDefinitions().landingPageId;\r\n }\r\n\r\n if (searchComposables.getSearchMetaDefinitions().providerPageId != undefined) {\r\n url += \"&providerId=\" + searchComposables.getSearchMetaDefinitions().providerPageId;\r\n }\r\n }\r\n\r\n\r\n return url;\r\n}\r\n\r\nfunction fetchDataAsync(search: string, fetchId: number) {\r\n\r\n\r\n let url = createSearchUrl(search, CreateSearchUrlType.fetch);\r\n\r\n addNewHistoryEntry(search);\r\n\r\n\r\n fetch(url,\r\n {\r\n method: 'GET',\r\n redirect: \"error\"\r\n })\r\n .then(response => response.json())\r\n\r\n .then(\r\n // closure for fetchId so that we can unset/update the fetchQueue\r\n // (also used for errors)\r\n (function (_fid: number) {\r\n\r\n return function (response: any) {\r\n\r\n fetchDataResult(_fid, response);\r\n }\r\n })(fetchId))\r\n\r\n .catch(\r\n // @see then, using closure\r\n (function (_fid: number) {\r\n\r\n return function (response: any) {\r\n\r\n fetchDataResultError(_fid, response);\r\n }\r\n })(fetchId)\r\n\r\n );\r\n}\r\n\r\nfunction fetchDataResult(fetchId: number, data: IFetchSearchResult) {\r\n\r\n // update content... (note that we should not update the filter\r\n // as that is being watched by vue!)\r\n\r\n _searchModel.totalCount = data.totalCount;\r\n\r\n\r\n _searchModel.paging.setPage(data.page);\r\n _searchModel.paging.setPageCount(data.pageCount);\r\n _searchModel.paging.setPageSize(data.pageSize);\r\n\r\n _onProcessData(data);\r\n\r\n fetchEnded(fetchId);\r\n}\r\n\r\nfunction fetchDataResultError(fetchId: number, response: any) {\r\n\r\n // something went wrong, do we need to show the user?\r\n // \r\n alert('Er ging iets mis bij het ophalen van de informatie, probeer de pagina te verversen');\r\n\r\n fetchEnded(fetchId);\r\n}\r\n\r\n\r\n\r\nexport type ISortableAccommodationEntry = {\r\n\r\n searchPrice?: number;\r\n reviewScore?: number;\r\n maxGuestCount?: number;\r\n orderKeyString: string;\r\n}\r\n\r\nexport function orderSearchResults(orderBy: SearchOrderingOption, hasDatesSelected: boolean, items: Array) {\r\n\r\n // we only order by price if we have dates selected..\r\n if (orderBy == SearchOrderingOption.PriceAsc && hasDatesSelected) {\r\n\r\n return [...items].sort((a, b) => (a.searchPrice ?? 0) - (b.searchPrice ?? 0));\r\n\r\n\r\n\r\n }\r\n else if (orderBy == SearchOrderingOption.PriceDesc && hasDatesSelected) {\r\n return [...items].sort((a, b) => (b.searchPrice ?? 0) - (a.searchPrice ?? 0));\r\n\r\n }\r\n else if (orderBy == SearchOrderingOption.MaxGuestCountAsc) {\r\n return [...items].sort((a, b) => (a.maxGuestCount ?? 0) - (b.maxGuestCount ?? 0));\r\n }\r\n else if (orderBy == SearchOrderingOption.MaxGuestCountDesc) {\r\n return [...items].sort((a, b) => (b.maxGuestCount ?? 0) - (a.maxGuestCount ?? 0));\r\n }\r\n\r\n else {\r\n\r\n return [...items].sort((a, b) => (a.orderKeyString ?? \"\").localeCompare(b.orderKeyString));\r\n }\r\n}","export default function scrollToTop(noDelay = false) {\r\n\r\n\r\n setTimeout(() => {\r\n\r\n document.querySelector(\"#scrollTopAnchor\")?.scrollIntoView({ behavior: \"smooth\" });\r\n\r\n }, noDelay?0: 130);\r\n \r\n}","declare let globalLanguage: string;\r\nlet currentLanguage = globalLanguage || \"nl\";\r\n\r\nconst translations = {\r\n\r\n nl: {\"filterPartial_Label_AccomodationsFound\":\"vakantieverblijven gevonden\",\"filterPartial_Label_AccomodationType\":\"Type vakantieverblijf\",\"filterPartial_Label_All\":\"Alles\",\"filterPartial_Label_Bathrooms\":\"Badkamers\",\"filterPartial_Label_Bedrooms\":\"Slaapkamers\",\"filterPartial_Label_CalculatePriceWhenDateChange\":\"We berekenen de prijsinformatie als je een datum kiest, pas dan kunnen we er op sorteren.\",\"filterPartial_Label_Close\":\"Sluit\",\"filterPartial_Label_RefineYourSearch\":\"Verfijn je zoekopdracht\",\"filterPartial_Label_RoomBeds\":\"Kamers en bedden\",\"filterPartial_Label_SortBy\":\"Sorteer op\",\"filterPartial_Label_SortBy_Price_HighFirst\":\"Prijs (hoogste eerst)\",\"filterPartial_Label_SortBy_Price_LowFirst\":\"Prijs (laagste eerst)\",\"filterPartial_Label_SortBy_Standard\":\"Standaard\",\"filterPartial_Label_SortBy_TotalPerson_HighFirst\":\"Aantal personen (hoogste eerst)\",\"filterPartial_Label_SortBy_TotalPerson_LowFirst\":\"Aantal personen (laagste eerst)\",\"filterPartial_Label_View\":\"Toon\",\"searchApp_Btn_ToTop\":\"naar boven\",\"searchApp_Label_ClearAllFilters\":\"Alle filters wissen\",\"searchApp_Label_NoFilterText\":\"Alle filters wissen\",\"searchApp_Label_ResultCount\":\"aantal resultaten:\",\"searchApp_Pagination_Back\":\"Vorige\",\"searchApp_Pagination_Next\":\"Volgende\",\"searhApp_Label_ShowMap\":\"Kaartweergave\"},\r\n en: {\"filterPartial_Label_AccomodationsFound\":\"vacation rentals found\",\"filterPartial_Label_AccomodationType\":\"Type of vacation rental\",\"filterPartial_Label_All\":\"All\",\"filterPartial_Label_Bathrooms\":\"Bathrooms\",\"filterPartial_Label_Bedrooms\":\"Bedrooms\",\"filterPartial_Label_CalculatePriceWhenDateChange\":\"We can calculate the price information when you choose a date, only then can we sort by it.\",\"filterPartial_Label_Close\":\"Close\",\"filterPartial_Label_RefineYourSearch\":\"Refine your search\",\"filterPartial_Label_RoomBeds\":\"Rooms and beds\",\"filterPartial_Label_SortBy\":\"Sort by\",\"filterPartial_Label_SortBy_Price_HighFirst\":\"Price (highest first)\",\"filterPartial_Label_SortBy_Price_LowFirst\":\"Price (lowest first)\",\"filterPartial_Label_SortBy_Standard\":\"Standard\",\"filterPartial_Label_SortBy_TotalPerson_HighFirst\":\"Number of persons (highest first)\",\"filterPartial_Label_SortBy_TotalPerson_LowFirst\":\"Number of persons (lowest first)\",\"filterPartial_Label_View\":\"Show\",\"searchApp_Btn_ToTop\":\"Go to top\",\"searchApp_Label_ClearAllFilters\":\"Clear all filters\",\"searchApp_Label_NoFilterText\":\"Clear all filters\",\"searchApp_Label_ResultCount\":\"Number of results:\",\"searchApp_Pagination_Back\":\"Back\",\"searchApp_Pagination_Next\":\"Next\",\"searhApp_Label_ShowMap\":\"Map View\"},\r\n de: {\"filterPartial_Label_AccomodationsFound\":\"Ferienunterk\\u00FCnfte gefunden\",\"filterPartial_Label_AccomodationType\":\"Art der Ferienunterk\\u00FCnft\",\"filterPartial_Label_All\":\"Alles\",\"filterPartial_Label_Bathrooms\":\"Badezimmer\",\"filterPartial_Label_Bedrooms\":\"Schlafzimmer\",\"filterPartial_Label_CalculatePriceWhenDateChange\":\"Wir berechnen die Preisinformationen, wenn Sie ein Datum ausw\\u00E4hlen, nur dann k\\u00F6nnen wir danach sortieren.\",\"filterPartial_Label_Close\":\"Schlie\\u00DFen\",\"filterPartial_Label_RefineYourSearch\":\"Verfeinern Sie Ihre Suche\",\"filterPartial_Label_RoomBeds\":\"Zimmer und Betten\",\"filterPartial_Label_SortBy\":\"Sortieren nach\",\"filterPartial_Label_SortBy_Price_HighFirst\":\"Preis (h\\u00F6chster zuerst)\",\"filterPartial_Label_SortBy_Price_LowFirst\":\"Preis (niedrigster zuerst)\",\"filterPartial_Label_SortBy_Standard\":\"Standard\",\"filterPartial_Label_SortBy_TotalPerson_HighFirst\":\"Anzahl der Personen (h\\u00F6chste zuerst)\",\"filterPartial_Label_SortBy_TotalPerson_LowFirst\":\"Anzahl der Personen (niedrigste zuerst)\",\"filterPartial_Label_View\":\"Anzeigen\",\"searchApp_Btn_ToTop\":\"nach oben\",\"searchApp_Label_ClearAllFilters\":\"Alle Filter l\\u00F6schen\",\"searchApp_Label_NoFilterText\":\"Alle Filter l\\u00F6schen\",\"searchApp_Label_ResultCount\":\"Anzahl der Ergebnisse:\",\"searchApp_Pagination_Back\":\"Zur\\u00FCck\",\"searchApp_Pagination_Next\":\"Weiter\",\"searhApp_Label_ShowMap\":\"Kartenansicht\"}\r\n};\r\n\r\ninterface appTranslations {\r\n\r\n filterPartial_Label_AccomodationsFound: string; \r\n filterPartial_Label_AccomodationType: string; \r\n filterPartial_Label_All: string; \r\n filterPartial_Label_Bathrooms: string; \r\n filterPartial_Label_Bedrooms: string; \r\n filterPartial_Label_CalculatePriceWhenDateChange: string; \r\n filterPartial_Label_Close: string; \r\n filterPartial_Label_RefineYourSearch: string; \r\n filterPartial_Label_RoomBeds: string; \r\n filterPartial_Label_SortBy: string; \r\n filterPartial_Label_SortBy_Price_HighFirst: string; \r\n filterPartial_Label_SortBy_Price_LowFirst: string; \r\n filterPartial_Label_SortBy_Standard: string; \r\n filterPartial_Label_SortBy_TotalPerson_HighFirst: string; \r\n filterPartial_Label_SortBy_TotalPerson_LowFirst: string; \r\n filterPartial_Label_View: string; \r\n searchApp_Btn_ToTop: string; \r\n searchApp_Label_ClearAllFilters: string; \r\n searchApp_Label_NoFilterText: string; \r\n searchApp_Label_ResultCount: string; \r\n searchApp_Pagination_Back: string; \r\n searchApp_Pagination_Next: string; \r\n searhApp_Label_ShowMap: string; \r\n\r\n}\r\n\r\n//@ts-ignore\r\nlet currentTranslation = (currentLanguage in translations) ? translations[currentLanguage] : translations[\"nl\"];\r\n\r\nexport default currentTranslation as appTranslations;","\r\n\r\n