\" + Object.keys(constants_1.WEEKDAY_OFFSET).join(\"|\") + \")\");\n\nclass ZHHansWeekdayParser extends AbstractParserWithWordBoundary_1.AbstractParserWithWordBoundaryChecking {\n innerPattern() {\n return PATTERN;\n }\n\n innerExtract(context, match) {\n const result = context.createParsingResult(match.index, match[0]);\n const dayOfWeek = match.groups.weekday;\n const offset = constants_1.WEEKDAY_OFFSET[dayOfWeek];\n if (offset === undefined) return null;\n let startMoment = dayjs_1.default(context.refDate);\n const startMomentFixed = false;\n const refOffset = startMoment.day();\n\n if (Math.abs(offset - 7 - refOffset) < Math.abs(offset - refOffset)) {\n startMoment = startMoment.day(offset - 7);\n } else if (Math.abs(offset + 7 - refOffset) < Math.abs(offset - refOffset)) {\n startMoment = startMoment.day(offset + 7);\n } else {\n startMoment = startMoment.day(offset);\n }\n\n result.start.assign(\"weekday\", offset);\n\n if (startMomentFixed) {\n result.start.assign(\"day\", startMoment.date());\n result.start.assign(\"month\", startMoment.month() + 1);\n result.start.assign(\"year\", startMoment.year());\n } else {\n result.start.imply(\"day\", startMoment.date());\n result.start.imply(\"month\", startMoment.month() + 1);\n result.start.imply(\"year\", startMoment.year());\n }\n\n return result;\n }\n\n}\n\nexports.default = ZHHansWeekdayParser;","\"use strict\";\n\nvar __importDefault = this && this.__importDefault || function (mod) {\n return mod && mod.__esModule ? mod : {\n \"default\": mod\n };\n};\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nconst AbstractMergeDateRangeRefiner_1 = __importDefault(require(\"../../../../common/refiners/AbstractMergeDateRangeRefiner\"));\n\nclass ZHHansMergeDateRangeRefiner extends AbstractMergeDateRangeRefiner_1.default {\n patternBetween() {\n return /^\\s*(至|到|-|~|~|-|ー)\\s*$/i;\n }\n\n}\n\nexports.default = ZHHansMergeDateRangeRefiner;","\"use strict\";\n\nvar __importDefault = this && this.__importDefault || function (mod) {\n return mod && mod.__esModule ? mod : {\n \"default\": mod\n };\n};\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nconst AbstractMergeDateTimeRefiner_1 = __importDefault(require(\"../../../../common/refiners/AbstractMergeDateTimeRefiner\"));\n\nclass ZHHansMergeDateTimeRefiner extends AbstractMergeDateTimeRefiner_1.default {\n patternBetween() {\n return /^\\s*$/i;\n }\n\n}\n\nexports.default = ZHHansMergeDateTimeRefiner;","\"use strict\";\n\nvar __importDefault = this && this.__importDefault || function (mod) {\n return mod && mod.__esModule ? mod : {\n \"default\": mod\n };\n};\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.createConfiguration = exports.createCasualConfiguration = exports.parseDate = exports.parse = exports.strict = exports.casual = void 0;\n\nconst RUTimeUnitWithinFormatParser_1 = __importDefault(require(\"./parsers/RUTimeUnitWithinFormatParser\"));\n\nconst RUMonthNameLittleEndianParser_1 = __importDefault(require(\"./parsers/RUMonthNameLittleEndianParser\"));\n\nconst RUMonthNameParser_1 = __importDefault(require(\"./parsers/RUMonthNameParser\"));\n\nconst RUTimeExpressionParser_1 = __importDefault(require(\"./parsers/RUTimeExpressionParser\"));\n\nconst RUTimeUnitAgoFormatParser_1 = __importDefault(require(\"./parsers/RUTimeUnitAgoFormatParser\"));\n\nconst RUMergeDateRangeRefiner_1 = __importDefault(require(\"./refiners/RUMergeDateRangeRefiner\"));\n\nconst RUMergeDateTimeRefiner_1 = __importDefault(require(\"./refiners/RUMergeDateTimeRefiner\"));\n\nconst configurations_1 = require(\"../../configurations\");\n\nconst RUCasualDateParser_1 = __importDefault(require(\"./parsers/RUCasualDateParser\"));\n\nconst RUCasualTimeParser_1 = __importDefault(require(\"./parsers/RUCasualTimeParser\"));\n\nconst RUWeekdayParser_1 = __importDefault(require(\"./parsers/RUWeekdayParser\"));\n\nconst RURelativeDateFormatParser_1 = __importDefault(require(\"./parsers/RURelativeDateFormatParser\"));\n\nconst chrono_1 = require(\"../../chrono\");\n\nconst SlashDateFormatParser_1 = __importDefault(require(\"../../common/parsers/SlashDateFormatParser\"));\n\nconst RUTimeUnitCasualRelativeFormatParser_1 = __importDefault(require(\"./parsers/RUTimeUnitCasualRelativeFormatParser\"));\n\nexports.casual = new chrono_1.Chrono(createCasualConfiguration());\nexports.strict = new chrono_1.Chrono(createConfiguration(true));\n\nfunction parse(text, ref, option) {\n return exports.casual.parse(text, ref, option);\n}\n\nexports.parse = parse;\n\nfunction parseDate(text, ref, option) {\n return exports.casual.parseDate(text, ref, option);\n}\n\nexports.parseDate = parseDate;\n\nfunction createCasualConfiguration() {\n const option = createConfiguration(false);\n option.parsers.unshift(new RUCasualDateParser_1.default());\n option.parsers.unshift(new RUCasualTimeParser_1.default());\n option.parsers.unshift(new RUMonthNameParser_1.default());\n option.parsers.unshift(new RURelativeDateFormatParser_1.default());\n option.parsers.unshift(new RUTimeUnitCasualRelativeFormatParser_1.default());\n return option;\n}\n\nexports.createCasualConfiguration = createCasualConfiguration;\n\nfunction createConfiguration(strictMode = true) {\n return configurations_1.includeCommonConfiguration({\n parsers: [new SlashDateFormatParser_1.default(true), new RUTimeUnitWithinFormatParser_1.default(), new RUMonthNameLittleEndianParser_1.default(), new RUWeekdayParser_1.default(), new RUTimeExpressionParser_1.default(strictMode), new RUTimeUnitAgoFormatParser_1.default()],\n refiners: [new RUMergeDateTimeRefiner_1.default(), new RUMergeDateRangeRefiner_1.default()]\n }, strictMode);\n}\n\nexports.createConfiguration = createConfiguration;","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nconst constants_1 = require(\"../constants\");\n\nconst results_1 = require(\"../../../results\");\n\nconst AbstractParserWithWordBoundary_1 = require(\"../../../common/parsers/AbstractParserWithWordBoundary\");\n\nconst PATTERN = `(?:(?:около|примерно)\\\\s*(?:~\\\\s*)?)?(${constants_1.TIME_UNITS_PATTERN})${constants_1.REGEX_PARTS.rightBoundary}`;\nconst PATTERN_WITH_PREFIX = new RegExp(`(?:в течение|в течении)\\\\s*${PATTERN}`, constants_1.REGEX_PARTS.flags);\nconst PATTERN_WITHOUT_PREFIX = new RegExp(PATTERN, \"i\");\n\nclass RUTimeUnitWithinFormatParser extends AbstractParserWithWordBoundary_1.AbstractParserWithWordBoundaryChecking {\n patternLeftBoundary() {\n return constants_1.REGEX_PARTS.leftBoundary;\n }\n\n innerPattern(context) {\n return context.option.forwardDate ? PATTERN_WITHOUT_PREFIX : PATTERN_WITH_PREFIX;\n }\n\n innerExtract(context, match) {\n const timeUnits = constants_1.parseTimeUnits(match[1]);\n return results_1.ParsingComponents.createRelativeFromReference(context.reference, timeUnits);\n }\n\n}\n\nexports.default = RUTimeUnitWithinFormatParser;","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nconst years_1 = require(\"../../../calculation/years\");\n\nconst constants_1 = require(\"../constants\");\n\nconst constants_2 = require(\"../constants\");\n\nconst constants_3 = require(\"../constants\");\n\nconst pattern_1 = require(\"../../../utils/pattern\");\n\nconst AbstractParserWithWordBoundary_1 = require(\"../../../common/parsers/AbstractParserWithWordBoundary\");\n\nconst PATTERN = new RegExp(`(?:с)?\\\\s*(${constants_3.ORDINAL_NUMBER_PATTERN})` + `(?:` + `\\\\s{0,3}(?:по|-|–|до)?\\\\s{0,3}` + `(${constants_3.ORDINAL_NUMBER_PATTERN})` + `)?` + `(?:-|\\\\/|\\\\s{0,3}(?:of)?\\\\s{0,3})` + `(${pattern_1.matchAnyPattern(constants_1.MONTH_DICTIONARY)})` + `(?:` + `(?:-|\\\\/|,?\\\\s{0,3})` + `(${constants_2.YEAR_PATTERN}(?![^\\\\s]\\\\d))` + `)?` + `${constants_1.REGEX_PARTS.rightBoundary}`, constants_1.REGEX_PARTS.flags);\nconst DATE_GROUP = 1;\nconst DATE_TO_GROUP = 2;\nconst MONTH_NAME_GROUP = 3;\nconst YEAR_GROUP = 4;\n\nclass RUMonthNameLittleEndianParser extends AbstractParserWithWordBoundary_1.AbstractParserWithWordBoundaryChecking {\n patternLeftBoundary() {\n return constants_1.REGEX_PARTS.leftBoundary;\n }\n\n innerPattern() {\n return PATTERN;\n }\n\n innerExtract(context, match) {\n const result = context.createParsingResult(match.index, match[0]);\n const month = constants_1.MONTH_DICTIONARY[match[MONTH_NAME_GROUP].toLowerCase()];\n const day = constants_3.parseOrdinalNumberPattern(match[DATE_GROUP]);\n\n if (day > 31) {\n match.index = match.index + match[DATE_GROUP].length;\n return null;\n }\n\n result.start.assign(\"month\", month);\n result.start.assign(\"day\", day);\n\n if (match[YEAR_GROUP]) {\n const yearNumber = constants_2.parseYear(match[YEAR_GROUP]);\n result.start.assign(\"year\", yearNumber);\n } else {\n const year = years_1.findYearClosestToRef(context.refDate, day, month);\n result.start.imply(\"year\", year);\n }\n\n if (match[DATE_TO_GROUP]) {\n const endDate = constants_3.parseOrdinalNumberPattern(match[DATE_TO_GROUP]);\n result.end = result.start.clone();\n result.end.assign(\"day\", endDate);\n }\n\n return result;\n }\n\n}\n\nexports.default = RUMonthNameLittleEndianParser;","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nconst constants_1 = require(\"../constants\");\n\nconst years_1 = require(\"../../../calculation/years\");\n\nconst pattern_1 = require(\"../../../utils/pattern\");\n\nconst constants_2 = require(\"../constants\");\n\nconst AbstractParserWithWordBoundary_1 = require(\"../../../common/parsers/AbstractParserWithWordBoundary\");\n\nconst PATTERN = new RegExp(`((?:в)\\\\s*)?` + `(${pattern_1.matchAnyPattern(constants_1.MONTH_DICTIONARY)})` + `\\\\s*` + `(?:` + `[,-]?\\\\s*(${constants_2.YEAR_PATTERN})?` + `)?` + `(?=[^\\\\s\\\\w]|\\\\s+[^0-9]|\\\\s+$|$)`, constants_1.REGEX_PARTS.flags);\nconst MONTH_NAME_GROUP = 2;\nconst YEAR_GROUP = 3;\n\nclass RUMonthNameParser extends AbstractParserWithWordBoundary_1.AbstractParserWithWordBoundaryChecking {\n patternLeftBoundary() {\n return constants_1.REGEX_PARTS.leftBoundary;\n }\n\n innerPattern() {\n return PATTERN;\n }\n\n innerExtract(context, match) {\n const monthName = match[MONTH_NAME_GROUP].toLowerCase();\n\n if (match[0].length <= 3 && !constants_1.FULL_MONTH_NAME_DICTIONARY[monthName]) {\n return null;\n }\n\n const result = context.createParsingResult(match.index, match.index + match[0].length);\n result.start.imply(\"day\", 1);\n const month = constants_1.MONTH_DICTIONARY[monthName];\n result.start.assign(\"month\", month);\n\n if (match[YEAR_GROUP]) {\n const year = constants_2.parseYear(match[YEAR_GROUP]);\n result.start.assign(\"year\", year);\n } else {\n const year = years_1.findYearClosestToRef(context.refDate, 1, month);\n result.start.imply(\"year\", year);\n }\n\n return result;\n }\n\n}\n\nexports.default = RUMonthNameParser;","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nconst index_1 = require(\"../../../index\");\n\nconst AbstractTimeExpressionParser_1 = require(\"../../../common/parsers/AbstractTimeExpressionParser\");\n\nconst constants_1 = require(\"../constants\");\n\nclass RUTimeExpressionParser extends AbstractTimeExpressionParser_1.AbstractTimeExpressionParser {\n constructor(strictMode) {\n super(strictMode);\n }\n\n patternFlags() {\n return constants_1.REGEX_PARTS.flags;\n }\n\n primaryPatternLeftBoundary() {\n return `(^|\\\\s|T|(?:[^\\\\p{L}\\\\p{N}_]))`;\n }\n\n followingPhase() {\n return `\\\\s*(?:\\\\-|\\\\–|\\\\~|\\\\〜|до|и|по|\\\\?)\\\\s*`;\n }\n\n primaryPrefix() {\n return `(?:(?:в|с)\\\\s*)??`;\n }\n\n primarySuffix() {\n return `(?:\\\\s*(?:утра|вечера|после полудня))?(?!\\\\/)${constants_1.REGEX_PARTS.rightBoundary}`;\n }\n\n extractPrimaryTimeComponents(context, match) {\n const components = super.extractPrimaryTimeComponents(context, match);\n\n if (components) {\n if (match[0].endsWith(\"вечера\")) {\n const hour = components.get(\"hour\");\n\n if (hour >= 6 && hour < 12) {\n components.assign(\"hour\", components.get(\"hour\") + 12);\n components.assign(\"meridiem\", index_1.Meridiem.PM);\n } else if (hour < 6) {\n components.assign(\"meridiem\", index_1.Meridiem.AM);\n }\n }\n\n if (match[0].endsWith(\"после полудня\")) {\n components.assign(\"meridiem\", index_1.Meridiem.PM);\n const hour = components.get(\"hour\");\n\n if (hour >= 0 && hour <= 6) {\n components.assign(\"hour\", components.get(\"hour\") + 12);\n }\n }\n\n if (match[0].endsWith(\"утра\")) {\n components.assign(\"meridiem\", index_1.Meridiem.AM);\n const hour = components.get(\"hour\");\n\n if (hour < 12) {\n components.assign(\"hour\", components.get(\"hour\"));\n }\n }\n }\n\n return components;\n }\n\n}\n\nexports.default = RUTimeExpressionParser;","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nconst constants_1 = require(\"../constants\");\n\nconst results_1 = require(\"../../../results\");\n\nconst AbstractParserWithWordBoundary_1 = require(\"../../../common/parsers/AbstractParserWithWordBoundary\");\n\nconst timeunits_1 = require(\"../../../utils/timeunits\");\n\nconst PATTERN = new RegExp(`(${constants_1.TIME_UNITS_PATTERN})\\\\s{0,5}назад(?=(?:\\\\W|$))`, constants_1.REGEX_PARTS.flags);\n\nclass RUTimeUnitAgoFormatParser extends AbstractParserWithWordBoundary_1.AbstractParserWithWordBoundaryChecking {\n patternLeftBoundary() {\n return constants_1.REGEX_PARTS.leftBoundary;\n }\n\n innerPattern() {\n return PATTERN;\n }\n\n innerExtract(context, match) {\n const timeUnits = constants_1.parseTimeUnits(match[1]);\n const outputTimeUnits = timeunits_1.reverseTimeUnits(timeUnits);\n return results_1.ParsingComponents.createRelativeFromReference(context.reference, outputTimeUnits);\n }\n\n}\n\nexports.default = RUTimeUnitAgoFormatParser;","\"use strict\";\n\nvar __importDefault = this && this.__importDefault || function (mod) {\n return mod && mod.__esModule ? mod : {\n \"default\": mod\n };\n};\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nconst AbstractMergeDateRangeRefiner_1 = __importDefault(require(\"../../../common/refiners/AbstractMergeDateRangeRefiner\"));\n\nclass RUMergeDateRangeRefiner extends AbstractMergeDateRangeRefiner_1.default {\n patternBetween() {\n return /^\\s*(и до|и по|до|по|-)\\s*$/i;\n }\n\n}\n\nexports.default = RUMergeDateRangeRefiner;","\"use strict\";\n\nvar __importDefault = this && this.__importDefault || function (mod) {\n return mod && mod.__esModule ? mod : {\n \"default\": mod\n };\n};\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nconst AbstractMergeDateTimeRefiner_1 = __importDefault(require(\"../../../common/refiners/AbstractMergeDateTimeRefiner\"));\n\nclass RUMergeDateTimeRefiner extends AbstractMergeDateTimeRefiner_1.default {\n patternBetween() {\n return new RegExp(`^\\\\s*(T|в|,|-)?\\\\s*$`);\n }\n\n}\n\nexports.default = RUMergeDateTimeRefiner;","\"use strict\";\n\nvar __createBinding = this && this.__createBinding || (Object.create ? function (o, m, k, k2) {\n if (k2 === undefined) k2 = k;\n Object.defineProperty(o, k2, {\n enumerable: true,\n get: function () {\n return m[k];\n }\n });\n} : function (o, m, k, k2) {\n if (k2 === undefined) k2 = k;\n o[k2] = m[k];\n});\n\nvar __setModuleDefault = this && this.__setModuleDefault || (Object.create ? function (o, v) {\n Object.defineProperty(o, \"default\", {\n enumerable: true,\n value: v\n });\n} : function (o, v) {\n o[\"default\"] = v;\n});\n\nvar __importStar = this && this.__importStar || function (mod) {\n if (mod && mod.__esModule) return mod;\n var result = {};\n if (mod != null) for (var k in mod) if (k !== \"default\" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);\n\n __setModuleDefault(result, mod);\n\n return result;\n};\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nconst AbstractParserWithWordBoundary_1 = require(\"../../../common/parsers/AbstractParserWithWordBoundary\");\n\nconst references = __importStar(require(\"../../../common/casualReferences\"));\n\nconst constants_1 = require(\"../constants\");\n\nconst PATTERN = new RegExp(`(?:с|со)?\\\\s*(сегодня|вчера|завтра|послезавтра|позавчера)${constants_1.REGEX_PARTS.rightBoundary}`, constants_1.REGEX_PARTS.flags);\n\nclass RUCasualDateParser extends AbstractParserWithWordBoundary_1.AbstractParserWithWordBoundaryChecking {\n patternLeftBoundary() {\n return constants_1.REGEX_PARTS.leftBoundary;\n }\n\n innerPattern(context) {\n return PATTERN;\n }\n\n innerExtract(context, match) {\n const lowerText = match[1].toLowerCase();\n const component = context.createParsingComponents();\n\n switch (lowerText) {\n case \"сегодня\":\n return references.today(context.reference);\n\n case \"вчера\":\n return references.yesterday(context.reference);\n\n case \"завтра\":\n return references.tomorrow(context.reference);\n\n case \"послезавтра\":\n return references.theDayAfter(context.reference, 2);\n\n case \"позавчера\":\n return references.theDayBefore(context.reference, 2);\n }\n\n return component;\n }\n\n}\n\nexports.default = RUCasualDateParser;","\"use strict\";\n\nvar __createBinding = this && this.__createBinding || (Object.create ? function (o, m, k, k2) {\n if (k2 === undefined) k2 = k;\n Object.defineProperty(o, k2, {\n enumerable: true,\n get: function () {\n return m[k];\n }\n });\n} : function (o, m, k, k2) {\n if (k2 === undefined) k2 = k;\n o[k2] = m[k];\n});\n\nvar __setModuleDefault = this && this.__setModuleDefault || (Object.create ? function (o, v) {\n Object.defineProperty(o, \"default\", {\n enumerable: true,\n value: v\n });\n} : function (o, v) {\n o[\"default\"] = v;\n});\n\nvar __importStar = this && this.__importStar || function (mod) {\n if (mod && mod.__esModule) return mod;\n var result = {};\n if (mod != null) for (var k in mod) if (k !== \"default\" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);\n\n __setModuleDefault(result, mod);\n\n return result;\n};\n\nvar __importDefault = this && this.__importDefault || function (mod) {\n return mod && mod.__esModule ? mod : {\n \"default\": mod\n };\n};\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nconst AbstractParserWithWordBoundary_1 = require(\"../../../common/parsers/AbstractParserWithWordBoundary\");\n\nconst references = __importStar(require(\"../../../common/casualReferences\"));\n\nconst dayjs_1 = require(\"../../../utils/dayjs\");\n\nconst dayjs_2 = __importDefault(require(\"dayjs\"));\n\nconst constants_1 = require(\"../constants\");\n\nconst PATTERN = new RegExp(`(сейчас|прошлым\\\\s*вечером|прошлой\\\\s*ночью|следующей\\\\s*ночью|сегодня\\\\s*ночью|этой\\\\s*ночью|ночью|этим утром|утром|утра|в\\\\s*полдень|вечером|вечера|в\\\\s*полночь)` + `${constants_1.REGEX_PARTS.rightBoundary}`, constants_1.REGEX_PARTS.flags);\n\nclass RUCasualTimeParser extends AbstractParserWithWordBoundary_1.AbstractParserWithWordBoundaryChecking {\n patternLeftBoundary() {\n return constants_1.REGEX_PARTS.leftBoundary;\n }\n\n innerPattern() {\n return PATTERN;\n }\n\n innerExtract(context, match) {\n let targetDate = dayjs_2.default(context.refDate);\n const lowerText = match[0].toLowerCase();\n const component = context.createParsingComponents();\n\n if (lowerText === \"сейчас\") {\n return references.now(context.reference);\n }\n\n if (lowerText === \"вечером\" || lowerText === \"вечера\") {\n return references.evening(context.reference);\n }\n\n if (lowerText.endsWith(\"утром\") || lowerText.endsWith(\"утра\")) {\n return references.morning(context.reference);\n }\n\n if (lowerText.match(/в\\s*полдень/)) {\n return references.noon(context.reference);\n }\n\n if (lowerText.match(/прошлой\\s*ночью/)) {\n return references.lastNight(context.reference);\n }\n\n if (lowerText.match(/прошлым\\s*вечером/)) {\n return references.yesterdayEvening(context.reference);\n }\n\n if (lowerText.match(/следующей\\s*ночью/)) {\n const daysToAdd = targetDate.hour() < 22 ? 1 : 2;\n targetDate = targetDate.add(daysToAdd, \"day\");\n dayjs_1.assignSimilarDate(component, targetDate);\n component.imply(\"hour\", 0);\n }\n\n if (lowerText.match(/в\\s*полночь/) || lowerText.endsWith(\"ночью\")) {\n return references.midnight(context.reference);\n }\n\n return component;\n }\n\n}\n\nexports.default = RUCasualTimeParser;","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nconst constants_1 = require(\"../constants\");\n\nconst pattern_1 = require(\"../../../utils/pattern\");\n\nconst AbstractParserWithWordBoundary_1 = require(\"../../../common/parsers/AbstractParserWithWordBoundary\");\n\nconst weekdays_1 = require(\"../../../common/calculation/weekdays\");\n\nconst PATTERN = new RegExp(`(?:(?:,|\\\\(|()\\\\s*)?` + `(?:в\\\\s*?)?` + `(?:(эту|этот|прошлый|прошлую|следующий|следующую|следующего)\\\\s*)?` + `(${pattern_1.matchAnyPattern(constants_1.WEEKDAY_DICTIONARY)})` + `(?:\\\\s*(?:,|\\\\)|)))?` + `(?:\\\\s*на\\\\s*(этой|прошлой|следующей)\\\\s*неделе)?` + `${constants_1.REGEX_PARTS.rightBoundary}`, constants_1.REGEX_PARTS.flags);\nconst PREFIX_GROUP = 1;\nconst WEEKDAY_GROUP = 2;\nconst POSTFIX_GROUP = 3;\n\nclass RUWeekdayParser extends AbstractParserWithWordBoundary_1.AbstractParserWithWordBoundaryChecking {\n innerPattern() {\n return PATTERN;\n }\n\n patternLeftBoundary() {\n return constants_1.REGEX_PARTS.leftBoundary;\n }\n\n innerExtract(context, match) {\n const dayOfWeek = match[WEEKDAY_GROUP].toLowerCase();\n const weekday = constants_1.WEEKDAY_DICTIONARY[dayOfWeek];\n const prefix = match[PREFIX_GROUP];\n const postfix = match[POSTFIX_GROUP];\n let modifierWord = prefix || postfix;\n modifierWord = modifierWord || \"\";\n modifierWord = modifierWord.toLowerCase();\n let modifier = null;\n\n if (modifierWord == \"прошлый\" || modifierWord == \"прошлую\" || modifierWord == \"прошлой\") {\n modifier = \"last\";\n } else if (modifierWord == \"следующий\" || modifierWord == \"следующую\" || modifierWord == \"следующей\" || modifierWord == \"следующего\") {\n modifier = \"next\";\n } else if (modifierWord == \"этот\" || modifierWord == \"эту\" || modifierWord == \"этой\") {\n modifier = \"this\";\n }\n\n return weekdays_1.createParsingComponentsAtWeekday(context.reference, weekday, modifier);\n }\n\n}\n\nexports.default = RUWeekdayParser;","\"use strict\";\n\nvar __importDefault = this && this.__importDefault || function (mod) {\n return mod && mod.__esModule ? mod : {\n \"default\": mod\n };\n};\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nconst constants_1 = require(\"../constants\");\n\nconst results_1 = require(\"../../../results\");\n\nconst dayjs_1 = __importDefault(require(\"dayjs\"));\n\nconst AbstractParserWithWordBoundary_1 = require(\"../../../common/parsers/AbstractParserWithWordBoundary\");\n\nconst pattern_1 = require(\"../../../utils/pattern\");\n\nconst PATTERN = new RegExp(`(в прошлом|на прошлой|на следующей|в следующем|на этой|в этом)\\\\s*(${pattern_1.matchAnyPattern(constants_1.TIME_UNIT_DICTIONARY)})(?=\\\\s*)${constants_1.REGEX_PARTS.rightBoundary}`, constants_1.REGEX_PARTS.flags);\nconst MODIFIER_WORD_GROUP = 1;\nconst RELATIVE_WORD_GROUP = 2;\n\nclass RURelativeDateFormatParser extends AbstractParserWithWordBoundary_1.AbstractParserWithWordBoundaryChecking {\n patternLeftBoundary() {\n return constants_1.REGEX_PARTS.leftBoundary;\n }\n\n innerPattern() {\n return PATTERN;\n }\n\n innerExtract(context, match) {\n const modifier = match[MODIFIER_WORD_GROUP].toLowerCase();\n const unitWord = match[RELATIVE_WORD_GROUP].toLowerCase();\n const timeunit = constants_1.TIME_UNIT_DICTIONARY[unitWord];\n\n if (modifier == \"на следующей\" || modifier == \"в следующем\") {\n const timeUnits = {};\n timeUnits[timeunit] = 1;\n return results_1.ParsingComponents.createRelativeFromReference(context.reference, timeUnits);\n }\n\n if (modifier == \"в прошлом\" || modifier == \"на прошлой\") {\n const timeUnits = {};\n timeUnits[timeunit] = -1;\n return results_1.ParsingComponents.createRelativeFromReference(context.reference, timeUnits);\n }\n\n const components = context.createParsingComponents();\n let date = dayjs_1.default(context.reference.instant);\n\n if (timeunit.match(/week/i)) {\n date = date.add(-date.get(\"d\"), \"d\");\n components.imply(\"day\", date.date());\n components.imply(\"month\", date.month() + 1);\n components.imply(\"year\", date.year());\n } else if (timeunit.match(/month/i)) {\n date = date.add(-date.date() + 1, \"d\");\n components.imply(\"day\", date.date());\n components.assign(\"year\", date.year());\n components.assign(\"month\", date.month() + 1);\n } else if (timeunit.match(/year/i)) {\n date = date.add(-date.date() + 1, \"d\");\n date = date.add(-date.month(), \"month\");\n components.imply(\"day\", date.date());\n components.imply(\"month\", date.month() + 1);\n components.assign(\"year\", date.year());\n }\n\n return components;\n }\n\n}\n\nexports.default = RURelativeDateFormatParser;","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nconst constants_1 = require(\"../constants\");\n\nconst results_1 = require(\"../../../results\");\n\nconst AbstractParserWithWordBoundary_1 = require(\"../../../common/parsers/AbstractParserWithWordBoundary\");\n\nconst timeunits_1 = require(\"../../../utils/timeunits\");\n\nconst PATTERN = new RegExp(`(эти|последние|прошлые|следующие|после|через|\\\\+|-)\\\\s*(${constants_1.TIME_UNITS_PATTERN})${constants_1.REGEX_PARTS.rightBoundary}`, constants_1.REGEX_PARTS.flags);\n\nclass RUTimeUnitCasualRelativeFormatParser extends AbstractParserWithWordBoundary_1.AbstractParserWithWordBoundaryChecking {\n patternLeftBoundary() {\n return constants_1.REGEX_PARTS.leftBoundary;\n }\n\n innerPattern() {\n return PATTERN;\n }\n\n innerExtract(context, match) {\n const prefix = match[1].toLowerCase();\n let timeUnits = constants_1.parseTimeUnits(match[2]);\n\n switch (prefix) {\n case \"последние\":\n case \"прошлые\":\n case \"-\":\n timeUnits = timeunits_1.reverseTimeUnits(timeUnits);\n break;\n }\n\n return results_1.ParsingComponents.createRelativeFromReference(context.reference, timeUnits);\n }\n\n}\n\nexports.default = RUTimeUnitCasualRelativeFormatParser;","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n static values = {\n count: Number,\n\n linkSample: String,\n optOutString: String,\n\n frenchMarketing: Boolean\n }\n\n static targets = [\n \"extraCharactersCheckbox\",\n \"textarea\",\n \"price\"\n ]\n\n connect() {\n this.initValues()\n this.calculateCharacters()\n }\n\n initValues() {\n this.possibleWarnings = [\n \"multiple-messages-error\",\n \"weird-chars-error\"\n ]\n\n this.currentWarnings = []\n this.messagesCount = 1\n\n this.frenchMessage = \"STOP au 36179\"\n }\n\n calculateCharacters() {\n const text = this.finalText\n\n if (text.length <= this.threshold) {\n this.currentWarnings = this.currentWarnings.filter(item => item !== \"multiple-messages-error\")\n this.messagesCount = 1\n } else {\n this.messagesCount = Math.ceil(text.length / this.thresholdIfMultipleSMS)\n if (!this.currentWarnings.includes(\"multiple-messages-error\")) { this.currentWarnings.push(\"multiple-messages-error\") }\n }\n\n if (this.anyNonGsmChars(this.finalText) == true) {\n if (!this.currentWarnings.includes(\"weird-chars-error\")) { this.currentWarnings.push(\"weird-chars-error\") }\n } else {\n this.currentWarnings = this.currentWarnings.filter(item => item !== \"weird-chars-error\")\n }\n\n this.processWarnings()\n }\n\n processWarnings() {\n this.possibleWarnings.forEach((warning) => {\n const el = document.getElementById(warning)\n\n if (!el) { return }\n\n if (this.currentWarnings.includes(warning)) {\n el.classList.remove(\"hidden\")\n } else {\n el.classList.add(\"hidden\")\n }\n })\n\n this.displayMessageCount()\n }\n\n displayMessageCount() {\n const messagesCountSelector = document.getElementById(\"multiple-messages-error\").querySelector(\".notification-banner__body\")\n const mask = document.getElementById(\"multiple-messages-error\").dataset.warningText\n\n messagesCountSelector.innerHTML = mask.replace(\"%{count}\", this.messagesCount)\n this.displaySmsPrice()\n }\n\n displaySmsPrice() {\n if (!this.hasPriceTarget) { return }\n\n const smsPrice = this.messagesCount * this.priceTarget.dataset.price\n const formatCurrency = Intl.NumberFormat(this.priceTarget.dataset.locale, {\n style: \"currency\",\n currency: this.priceTarget.dataset.currency,\n currencyDisplay: \"narrowSymbol\",\n })\n this.priceTarget.querySelector(\"#cancellation-sms-charge\").textContent = `${formatCurrency.format(smsPrice)}`\n }\n\n /* eslint-disable */\n anyNonGsmChars(str) {\n const regexp = new RegExp(\"([^A-Za-z0-9 \\r\\n@£\\$¥èéùìòÇØøÅå\\u0394_\\u03A6\\u0393\\u039B\\u03A9\\u03A0\\u03A8\\u03A3\\u0398\\u039EÆæßÉ!\\\"#$%&'()\\*\\+,\\\\\\-\\.\\/\\:;\\<\\=\\>\\?¡ÄÖÑܧ¿äöñüà^{}\\[~\\]|\\u20AC]*)\")\n\n return regexp.test(str)\n }\n /* eslint-enable */\n\n get finalText() {\n let text = this.textareaTarget.value\n\n const extraSettings = this.hasExtraCharactersCheckboxTarget && this.hasLinkSampleValue && this.hasOptOutStringValue\n if (!extraSettings) { return text }\n\n if (this.extraCharactersCheckboxTarget.checked == true) {\n text = text + \" \" + this.linkSampleValue\n } else {\n text = text + \"\\n\\n\" + this.optOutStringValue + \": \" + this.linkSampleValue\n }\n\n if (this.frenchMarketingValue == true) {\n text = text + \" \" + this.frenchMessage\n }\n\n return text\n }\n\n get threshold() {\n return (this.anyNonGsmChars(this.finalText) == true) ? this.nonGsmThreshold : this.gsmThreshold\n }\n\n get thresholdIfMultipleSMS() {\n return (this.anyNonGsmChars(this.finalText) == true) ? this.nonGsmThresholdIfMultipleSMS : this.gsmThresholdIfMultipleSMS\n }\n\n get gsmThreshold() { return 160 }\n\n get gsmThresholdIfMultipleSMS() { return 153 }\n\n get nonGsmThreshold() { return 70 }\n\n get nonGsmThresholdIfMultipleSMS() { return 67 }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nconst documentClick = _event => {\n document.body.dispatchEvent(new Event(\"click\", { bubbles: true, cancelable: true, composed: true }))\n}\n\n// ajax requests prevent the event propagation. Some of our components depend on `click@document` events.\n// So, we allow the clicks to propagate to the document. It is safer to directly propagate these to the\n// document.body as some existing code might depend on the event being stopped before reaching the parent.\nexport default class extends Controller {\n connect() {\n this.element.addEventListener(\"ajax:before\", documentClick)\n }\n\n disconnect() {\n this.element.removeEventListener(\"ajax:before\", documentClick)\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n static targets = [\"target\"]\n\n click(_event) {\n if (!this.targetTarget) { return }\n\n this.targetTarget.click()\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n static targets = [\n \"source\",\n \"linkInputButton\"\n ]\n\n static values = {\n copyStr: String,\n copiedStr: String,\n primaryClass: String,\n secondaryClass: String\n }\n\n copy(event) {\n event.preventDefault()\n navigator.clipboard.writeText(this.sourceTarget.value || this.sourceTarget.innerText)\n this.animateButton()\n }\n\n animateButton() {\n const secondaryClass = this.secondaryClassValue || \"button--secondary\"\n const primaryClass = this.primaryClassValue || \"button--primary\"\n\n this.linkInputButtonTarget.innerHTML = this.copiedStrValue\n this.linkInputButtonTarget.classList.remove(primaryClass)\n this.linkInputButtonTarget.classList.add(secondaryClass)\n\n const timer = () => {\n this.linkInputButtonTarget.innerHTML = this.copyStrValue\n this.linkInputButtonTarget.classList.remove(secondaryClass)\n this.linkInputButtonTarget.classList.add(primaryClass)\n clearInterval(countdownTimer)\n }\n\n var countdownTimer = setInterval(timer, 1000)\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\nimport { Renderer } from \"models/schedule\"\n\nconst DATA = {\n \"2022-10-04\": [\n {\n startTime: \"2022-10-04T09:30:00+02:00\",\n endTime: \"2022-10-04T10:00:00+02:00\",\n referenceId: 1,\n title: \"Event 1\",\n color: \"#fbe8fc\"\n },\n {\n startTime: \"2022-10-04T10:30:00+02:00\",\n endTime: \"2022-10-04T11:30:00+02:00\",\n referenceId: 2,\n title: \"Event 2\",\n color: \"#fbe8fc\"\n },\n {\n startTime: \"2022-10-04T12:00:00+02:00\",\n endTime: \"2022-10-04T13:00:00+02:00\",\n referenceId: 3,\n title: \"Event 3\",\n color: \"#fbe8fc\"\n },\n {\n startTime: \"2022-10-04T12:45:00+02:00\",\n endTime: \"2022-10-04T13:45:00+02:00\",\n referenceId: 4,\n title: \"Event 4\",\n color: \"#fbe8fc\"\n },\n {\n startTime: \"2022-10-04T13:30:00+02:00\",\n endTime: \"2022-10-04T14:30:00+02:00\",\n referenceId: 5,\n title: \"Event 5\",\n color: \"#fbe8fc\"\n }\n ],\n \"2022-10-30\": [\n {\n startTime: \"2022-10-30T00:00:00+02:00\",\n endTime: \"2022-10-30T00:30:00+02:00\",\n referenceId: 1,\n title: \"Event 1\",\n color: \"#fbe8fc\"\n },\n {\n startTime: \"2022-10-30T01:00:00+02:00\",\n endTime: \"2022-10-30T01:30:00+02:00\",\n referenceId: 2,\n title: \"Event 2\",\n color: \"#fbe8fc\"\n },\n {\n startTime: \"2022-10-30T02:00:00+02:00\",\n endTime: \"2022-10-30T02:30:00+02:00\",\n referenceId: 3,\n title: \"Event 3\",\n color: \"#fbe8fc\"\n },\n {\n startTime: \"2022-10-30T02:00:00+01:00\",\n endTime: \"2022-10-30T02:30:00+01:00\",\n referenceId: 4,\n title: \"Event 4\",\n color: \"#fbe8fc\"\n },\n {\n startTime: \"2022-10-30T03:00:00+01:00\",\n endTime: \"2022-10-30T03:30:00+01:00\",\n referenceId: 5,\n title: \"Event 5\",\n color: \"#fbe8fc\"\n },\n {\n startTime: \"2022-10-30T04:00:00+01:00\",\n endTime: \"2022-10-29T04:30:00+01:00\",\n referenceId: 6,\n title: \"Event 6\",\n color: \"#fbe8fc\"\n }\n ]\n}\n\nexport default class extends Controller {\n connect() {\n if (this.data) {\n const renderer = new Renderer(this.data, { eventClass: this.eventClassName })\n renderer.render(element => this.element.appendChild(element))\n }\n }\n\n get date() {\n return this.element.dataset.date\n }\n\n get eventClassName() {\n return this.element.dataset.eventClassName\n }\n\n get data() {\n return DATA[this.date]\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n static targets = [\"dateText\"]\n\n renderer = {\n set: (target, property, value) => {\n target[property] = value\n\n if (property === \"selection\") this.renderer.printText()\n\n return true\n },\n\n printText: () => {\n if (this.state.selection === null) return\n\n const start = global.moment(this.state.selection.start).format(\"LL\")\n const end = global.moment(this.state.selection.end).format(\"LL\")\n\n if (start === end) {\n this.dateTextTarget.innerText = start\n\n return\n }\n\n this.dateTextTarget.innerText = `${start} - ${end}`\n }\n }\n\n connect() {\n this.state = new Proxy({}, this.renderer)\n this.state.selection = null\n }\n\n select(event) {\n this.state.selection = event.detail.selection\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n pick(event) {\n const option = event.currentTarget.innerText.trim()\n console.log(`You've picked ${option}.`)\n event.preventDefault()\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\nimport * as chrono from \"chrono-node\"\n\nexport default class extends Controller {\n static targets = [\"calendar\", \"yearPicker\", \"monthPicker\"]\n\n static values = {\n date: String,\n rangeSelection: Boolean,\n extendYearDropdownBy: Number\n }\n\n renderer = {\n set: (target, property, value) => {\n target[property] = value\n\n if (property === \"date\" || property === \"page\") this.renderer.renderCalendar()\n if (property === \"highlight\") this.renderer.highlight()\n\n return true\n },\n\n renderCalendar: () => {\n const start = global.moment(this.state.date).add(this.state.page, \"month\").startOf(\"month\")\n const end = global.moment(this.state.date).add(this.state.page, \"month\").endOf(\"month\")\n\n // Calculate the first day of the grid (aligned to the new week start)\n const startDayOfWeek = (start.day() + 7 - this.weekStartDay) % 7 // Adjust to week start\n const firstGridDate = global.moment(start).subtract(startDayOfWeek, \"days\")\n const totalGridDays = 42 // 6 weeks * 7 days\n\n this.calendarTarget.querySelectorAll(\"tbody td\").forEach(element => {\n element.innerHTML = \"\"\n element.dataset.date = \"\"\n element.dataset.action = \"\"\n })\n\n // Fill the 6x7 grid\n for (let i = 0; i < totalGridDays; i++) {\n const date = global.moment(firstGridDate).add(i, \"days\")\n let monthType = \"current\"\n\n if (date.isBefore(start, \"month\")) {\n monthType = \"previous\"\n } else if (date.isAfter(end, \"month\")) {\n monthType = \"next\"\n }\n\n this.renderer.renderDay(date, monthType)\n }\n\n const yearStart = global.moment(start).startOf(\"year\").subtract(this.extendYearDropdownByValue, \"year\")\n const yearEnd = global.moment(start).endOf(\"year\").add(this.extendYearDropdownByValue, \"year\")\n const yearRange = global.moment.range(yearStart, yearEnd)\n const years = Array.from(yearRange.by(\"years\")).map(m => m.format(\"YYYY\"))\n\n this.yearPickerTarget.innerHTML = \"\"\n\n years.forEach(year => {\n const option = document.createElement(\"option\")\n option.value = year\n option.innerHTML = year\n\n this.yearPickerTarget.appendChild(option)\n })\n\n this.yearPickerTarget.value = global.moment(start).format(\"YYYY\")\n this.monthPickerTarget.value = global.moment(start).format(\"M\")\n\n if (this.state.highlight) this.renderer.highlight()\n },\n\n highlight: () => {\n const start = global.moment(this.state.highlight.start)\n const end = global.moment(this.state.highlight.end)\n\n this.calendarTarget.querySelectorAll(\".date-picker__day-button\").forEach( element => {\n element.classList.remove(\"date-picker__day-button--highlighted\")\n element.classList.remove(\"date-picker__day-button--highlighted-start\")\n element.classList.remove(\"date-picker__day-button--highlighted-end\")\n })\n\n const range = global.moment().range(start, end)\n const dates = Array.from(range.by(\"days\"))\n\n dates.forEach((date) => {\n if (!this.renderer.isVisible(date)) return\n\n const column = this.renderer.findDayColumn(date)\n const datePickerElement = column.querySelector(\".date-picker__day-button\")\n\n datePickerElement.classList.add(\"date-picker__day-button--highlighted\")\n })\n\n if (this.renderer.isVisible(start)) {\n const startColumn = this.renderer.findDayColumn(start)\n const startDatePickerElement = startColumn.querySelector(\".date-picker__day-button\")\n startDatePickerElement.classList.add(\"date-picker__day-button--highlighted-start\")\n }\n\n if (this.renderer.isVisible(end)) {\n const endColumn = this.renderer.findDayColumn(end)\n const endDatePickerElement = endColumn.querySelector(\".date-picker__day-button\")\n endDatePickerElement.classList.add(\"date-picker__day-button--highlighted-end\")\n }\n },\n\n renderDay: (date, monthType = \"current\") => {\n const currentDate = global.moment().format(this.dateFormat)\n const formattedDate = global.moment(date).format(this.dateFormat)\n const datasetDate = `${global.moment(date).format(this.dateFormat)}`\n const column = this.renderer.findDayColumn(date)\n const datePickerElement = document.createElement(\"button\")\n datePickerElement.classList.add(\"date-picker__day-button\")\n datePickerElement.innerText = global.moment(date).format(\"D\")\n datePickerElement.dataset.date = datasetDate\n datePickerElement.dataset.action = \"click->date-picker#select\"\n datePickerElement.classList.add(`date-picker__day-button--${monthType}-month`)\n\n if (currentDate == formattedDate) datePickerElement.classList.add(\"date-picker__day-button--today\")\n\n column.innerHTML = datePickerElement.outerHTML\n },\n\n findDayColumn: (date) => {\n const coordinates = this.getCoordinates(date)\n const row = this.element.querySelector(`[data-date-picker-row=\"${coordinates.row}\"]`)\n const column = row.querySelector(`[data-date-picker-column=\"${coordinates.column}\"]`)\n\n return column\n },\n\n isVisible: (date) => {\n const coordinates = this.getCoordinates(date)\n const row = this.element.querySelector(`[data-date-picker-row=\"${coordinates.row}\"]`)\n if (!row) return false\n\n const column = row.querySelector(`[data-date-picker-column=\"${coordinates.column}\"]`)\n\n return !!column\n }\n }\n\n connect() {\n this.state = new Proxy({}, this.renderer)\n this.state.date = this.parseDate(this.dateValue)\n this.state.page = 0\n this.state.years\n\n this.selectionStart = null\n this.emitEvent(\"connect\")\n }\n\n currentMonth() {\n this.state.date = new Date()\n this.state.page = 0\n this.emitEvent(\"navigate\")\n }\n\n nextMonth() {\n this.state.page += 1\n this.emitEvent(\"navigate\")\n }\n\n previousMonth() {\n this.state.page -= 1\n this.emitEvent(\"navigate\")\n }\n\n select(event) {\n if (this.isRangeSelection) return this.selectRange(event)\n\n this.selectSingle(event)\n }\n\n selectSingle(event) {\n this.selection = { start: event.currentTarget.dataset.date, end: event.currentTarget.dataset.date }\n this.emitEvent(\"select\")\n }\n\n selectRange(event) {\n if (this.selectionStart === null) {\n this.selectionStart = event.currentTarget.dataset.date\n this.selection = { start: this.selectionStart, end: this.selectionStart }\n\n return\n }\n\n const start = global.moment(this.selectionStart)\n const end = global.moment(event.currentTarget.dataset.date)\n\n if (start.isBefore(end)) {\n this.selection = { start: this.selectionStart, end: event.currentTarget.dataset.date }\n } else {\n this.selection = { start: event.currentTarget.dataset.date, end: this.selectionStart }\n }\n\n this.selectionStart = null\n this.emitEvent(\"select\")\n }\n\n navigate() {\n const month = String(this.monthPickerTarget.value).padStart(2, \"0\")\n const year = this.yearPickerTarget.value\n const date = `${year}-${month}-01`\n\n this.state.date = global.moment(date)\n this.state.page = 0\n this.emitEvent(\"navigate\")\n }\n\n getCoordinates(date) {\n // Calculate the first day of the grid\n const start = global.moment(this.state.date).add(this.state.page, \"month\").startOf(\"month\")\n const startDayOfWeek = (start.day() + 7 - this.weekStartDay) % 7 // Adjust to week start\n const firstGridDate = global.moment(start).subtract(startDayOfWeek, \"days\")\n\n // Calculate the offset from the first grid date\n const daysOffset = date.diff(firstGridDate, \"days\")\n\n // Calculate row and column\n const coordinates = {}\n coordinates.row = Math.floor(daysOffset / 7)\n coordinates.column = daysOffset % 7\n\n return coordinates\n }\n\n parseDate(date = null) {\n const parsedDate = chrono.parseDate(date)\n const parsedMoment = parsedDate ? global.moment(parsedDate) : global.moment()\n\n return parsedMoment.format(this.dateFormat)\n }\n\n emitEvent(eventType) {\n const customEvent = new CustomEvent(`date-picker:${eventType}`, {\n bubbles: false,\n detail: this.state\n })\n this.element.dispatchEvent(customEvent)\n }\n\n set selection(selection) {\n this.state.selection = selection\n this.state.highlight = this.state.selection\n }\n\n get selection() {\n return this.state.selection\n }\n\n set highlight(highlight) {\n this.state.highlight = highlight\n }\n\n get highlight() {\n return this.state.highlight\n }\n\n get dateFormat() {\n return \"YYYY-MM-DD\"\n }\n\n get isRangeSelection() {\n return this.rangeSelectionValue === true\n }\n\n get weekStartDay() {\n return 1 // 1 represents Monday\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n static targets = [\"checkbox\", \"details\"]\n\n toggle(event) {\n this.checkboxTarget.checked = this.detailsTarget.value.trim() !== \"\"\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n static values = {\n selector: String\n }\n\n present(event) {\n event.preventDefault()\n\n // We set it here because the presentation controller could call its connect before the\n // dialog controller is created.\n // Here checking on `undefined` is equivalend to `if defined?(@variable)` check in ruby as\n // undefined is returned when a field/variable is not set.\n if (this.dialogController === undefined) this.setDialogController()\n\n if (this.dialogController) this.dialogController.open()\n }\n\n setDialogController() {\n const dialog = this.hasSelectorValue ? this.dialogElement = document.querySelector(this.selectorValue) : null\n if (dialog) {\n this.dialogController = global.application.getControllerForElementAndIdentifier(\n dialog, \"dialog\"\n )\n }\n\n if (!this.dialogController) this.dialogController = null\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n static targets = [\"header\", \"options\", \"description\"]\n static values = {\n state: String,\n swapIcon: Boolean,\n autoPositioning: Boolean,\n edgeBuffer: Number,\n alignment: String\n }\n\n renderer = {\n set: (target, property, value) => {\n target[property] = value\n\n if (property === \"state\") {\n this.element.classList.toggle(\"dropdown--disabled\", value === \"disabled\")\n }\n\n if (property === \"active\") {\n this.isActive ? this.renderer.present() : this.renderer.dismiss()\n\n this.emitEvent(\"toggle\")\n }\n\n if (property === \"alignment\") {\n this.element.classList.remove(...this.alignmentClasses)\n this.element.classList.add(`dropdown--${value}-alignment`)\n }\n\n if (property === \"currentOptionIndex\") {\n this.optionElements.forEach((optionElement, index) => {\n const linkElement = optionElement.querySelector(\".dropdown-option__link\")\n linkElement.classList.toggle(\"dropdown-option__link--highlighted\", index == value)\n })\n\n this.emitEvent(\"highlight\")\n }\n\n if (property === \"scrollToOptionIndex\") {\n const currentElement = this.optionsTarget.querySelector(\n `.dropdown-option[data-index=\"${this.state.scrollToOptionIndex}\"]`\n )\n global.helpers.scrollToOption(currentElement, this.optionsTarget)\n\n this.emitEvent(\"highlight\")\n }\n\n if (property === \"currentSelection\") {\n if (this.state.currentSelection) {\n this.optionElements.forEach(element => {\n element.querySelector(\".dropdown-option__link\")\n .classList.remove(\"dropdown-option__link--selected\")\n })\n this.state.currentSelection.classList.add(\"dropdown-option__link--selected\")\n\n if (this.hasDescriptionTarget) {\n this.descriptionTarget.innerText = this.state.currentSelection.innerText.trim()\n }\n\n if (this.swapIconValue) {\n const currentSelectionIconHTML = this.state.currentSelection.querySelector(\".dropdown-option__icon\")?.innerHTML\n if (this.iconElement && currentSelectionIconHTML) this.iconElement.innerHTML = currentSelectionIconHTML\n }\n\n this.emitEvent(\"change\")\n }\n }\n\n return true\n },\n\n present: () => {\n // Set an absolute max-height for the optionsTarget.\n const maxHeight = 500\n\n let maxHeightToSet = maxHeight\n\n if (this.autoPositioningValue) {\n // Check if we have more space at the top or the bottom.\n // If we have more space at the top, open the dropdown upwards.\n const windowHeight = window.innerHeight\n const middleOfWindow = windowHeight / 2\n const headerTop = this.headerTarget.getBoundingClientRect().top\n const openFromTop = headerTop > middleOfWindow\n\n if (openFromTop) {\n this.optionsTarget.classList.add(\"dropdown__options--open-from-top\")\n }\n\n // Prerender the optionsTarget to get the height.\n // This is necessary because the optionsTarget is hidden by default.\n this.optionsTarget.style.display = \"block\"\n this.optionsTarget.style.visibility = \"hidden\"\n\n const optionsTop = this.optionsTarget.getBoundingClientRect().top\n const optionsHeight = this.optionsTarget.offsetHeight\n const optionsBottom = optionsTop + optionsHeight\n\n this.optionsTarget.style.display = \"\"\n this.optionsTarget.style.visibility = \"\"\n\n // Get the fact that we scrolled past the top or the bottom of the optionsTarget.\n const scrolledPastTopBy = Math.abs(optionsTop)\n const scrolledPastBottomBy = optionsBottom - windowHeight\n const isScrolledPastTop = optionsTop < 0\n const isScrolledPastBottom = optionsBottom > windowHeight\n\n // Check the available space at the top and bottom, and set the max-height accordingly.\n if (openFromTop && isScrolledPastTop) {\n const currentMaxHeight = optionsHeight - scrolledPastTopBy - this.edgeBufferValue\n\n if (currentMaxHeight < maxHeight) maxHeightToSet = currentMaxHeight\n }\n\n if (!openFromTop && isScrolledPastBottom) {\n const currentMaxHeight = optionsHeight - scrolledPastBottomBy - this.edgeBufferValue\n\n if (currentMaxHeight < maxHeight) maxHeightToSet = currentMaxHeight\n }\n }\n\n if (this.contentElement) {\n const optionsPaddingTop = parseInt(window.getComputedStyle(this.optionsTarget).paddingTop)\n const optionsPaddingBottom = parseInt(window.getComputedStyle(this.optionsTarget).paddingBottom)\n const optionsPadding = optionsPaddingTop + optionsPaddingBottom\n const maxHeightToSetWithoutPadding = maxHeightToSet - optionsPadding\n\n this.optionsTarget.style.overflow = \"unset\"\n this.contentElement.style.maxHeight = `${maxHeightToSetWithoutPadding}px`\n this.contentElement.style.overflowY = \"auto\"\n }\n\n this.optionsTarget.style.maxHeight = `${maxHeightToSet}px`\n this.headerTarget.classList.add(\"dropdown__header--active\")\n this.optionsTarget.classList.add(\"element--on\")\n },\n\n dismiss: () => {\n this.headerTarget.classList.remove(\"dropdown__header--active\")\n this.optionsTarget.classList.remove(\"element--on\")\n this.optionsTarget.classList.remove(\"dropdown__options--open-from-top\")\n this.optionsTarget.style.maxHeight = \"\"\n\n if (this.contentElement) {\n this.optionsTarget.style.overflow = \"\"\n this.contentElement.style.maxHeight = \"\"\n this.contentElement.style.overflowY = \"\"\n }\n }\n }\n\n initialize() {\n this.state = new Proxy({}, this.renderer)\n this.state.state = this.stateValue\n this.state.active = false\n this.state.alignment = this.initialAlignment\n this.state.options = this.optionElements\n this.state.currentSelection = this.initialSelection\n this.indexOptions()\n this.resetCurrentOption()\n }\n\n indexOptions() {\n this.optionElements.forEach((optionElement, index) => optionElement.dataset.index = index)\n }\n\n highlight(event) {\n const optionElement = event.currentTarget.closest(\".dropdown-option\")\n const highlightedIndex = optionElement.dataset.index\n this.state.currentOptionIndex = highlightedIndex\n }\n\n resetCurrentOption(event) {\n this.state.currentOptionIndex = this.initialIndex\n }\n\n pick(event) {\n this.state.proposedSelection = event.currentTarget\n\n if (this.isProposedSelectionDisabled || this.isProposedSelectionInvalid) {\n event.preventDefault()\n return\n }\n\n if (this.isProposedSelectionDisabled) {\n event.stopImmediatePropagation()\n return\n }\n\n this.state.currentSelection = this.state.proposedSelection\n }\n\n toggle(event) {\n event.stopImmediatePropagation()\n event.preventDefault()\n\n if (this.isDisabled) return\n\n this.dismissOthers()\n this.resetCurrentOption()\n\n this.state.active = !this.state.active\n }\n\n dismiss(event) {\n if (event) {\n const clickedInside = this.optionsTarget.contains(event.target)\n const needsCustomDismissHandler = !this.isWithOptions\n\n if (clickedInside && needsCustomDismissHandler) return\n }\n\n this.state.active = false\n this.resetCurrentOption()\n }\n\n dismissOthers() {\n document.querySelectorAll(\".dropdown[data-controller~=\\\"dropdown\\\"]\").forEach((element) => {\n if (element !== this.element) {\n const dropdown = global.application.getControllerForElementAndIdentifier(element, \"dropdown\")\n dropdown.state.active = false\n }\n })\n }\n\n navigateOptions(event) {\n if (!this.isActive || !this.isWithOptions) return\n\n const currentOption = this.optionsTarget.querySelector(\n `li.dropdown-option[data-index=\"${this.state.currentOptionIndex}\"] .dropdown-option__link`\n )\n\n const { upKey, downKey, enterKey } = global.helpers.keyCodes\n\n switch (event.keyCode) {\n case upKey:\n event.preventDefault()\n\n if (this.state.currentOptionIndex == this.initialIndex) {\n this.state.currentOptionIndex = this.maxIndex\n } else if (this.state.currentOptionIndex > this.minIndex) {\n this.state.currentOptionIndex--\n }\n break\n case downKey:\n event.preventDefault()\n\n if (this.state.currentOptionIndex < this.maxIndex) {\n this.state.currentOptionIndex++\n }\n break\n case enterKey:\n if (this.state.currentOptionIndex != this.initialIndex) event.preventDefault()\n if (currentOption) {\n currentOption.click()\n return\n }\n break\n }\n\n this.state.scrollToOptionIndex = this.state.currentOptionIndex\n }\n\n emitEvent(eventType) {\n const customEvent = new CustomEvent(`dropdown:${eventType}`, {\n bubbles: false,\n detail: this.state\n })\n this.element.dispatchEvent(customEvent)\n }\n\n set alignment(value) {\n this.state.alignment = value\n }\n\n get optionElements() {\n return Array.from(this.optionsTarget.querySelectorAll(\".dropdown-option\"))\n }\n\n get iconElement() {\n return this.element.querySelector(\".dropdown__icon\")\n }\n\n get contentElement() {\n return this.optionsTarget.querySelector(\".dropdown__content\")\n }\n\n get isDisabled() {\n return this.state.state === \"disabled\"\n }\n\n get isActive() {\n return this.state.active === true\n }\n\n get alignment() {\n return this.state.alignment\n }\n\n get isProposedSelectionInvalid() {\n const emptyHrefs = [\"#\", \"\"]\n\n return emptyHrefs.includes(this.state.proposedSelection.getAttribute(\"href\"))\n }\n\n get isProposedSelectionDisabled() {\n return this.state.proposedSelection.classList.contains(\"dropdown-option__link--disabled\")\n }\n\n get initialSelection() {\n return this.optionsTarget.querySelector(\".dropdown-option__link--selected\")\n }\n\n get initialIndex() {\n return -1\n }\n\n get minIndex() {\n return 0\n }\n\n get maxIndex() {\n return this.state.options.length - 1\n }\n\n get isWithOptions() {\n return this.optionElements.length > 0\n }\n\n get initialAlignment() {\n return this.alignmentValue || \"left\"\n }\n\n get alignmentClasses() {\n return [\"dropdown--left-alignment\", \"dropdown--center-alignment\", \"dropdown--right-alignment\"]\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n static values = {\n url: String,\n five: Boolean,\n }\n\n static targets = [\n \"seconds\"\n ]\n\n secondsTotal() {\n return 5\n }\n\n connect() {\n if (this.fiveValue != true) return\n if (this.urlValue == \"\") return\n\n let sec = this.secondsTotal()\n\n this.counter = setInterval(() => {\n sec = sec - 1\n\n if (sec <= 0) {\n clearInterval(this.counter)\n this.redirect(this.urlValue)\n return\n }\n\n this.secondsTarget.textContent = sec\n }, 1000)\n }\n\n disconnect() {\n clearTimeout(this.counter)\n }\n\n redirect(url) {\n window.location.href = url\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n static values = {\n selectedView: String, // NOTE: will be useful after gallery view is also created\n files: Array,\n multiple: Boolean,\n max: Number,\n mimeTypes: String,\n maxFilesSelectedNotice: String,\n invalidFileTypeNotice: String,\n disabled: Boolean\n }\n static targets = [\n \"field\", \"container\", \"description\", \"selectedViewSimpleSingle\",\n \"selectedViewSimpleMultiple\"\n ]\n\n renderer = {\n set: (target, property, value) => {\n target[property] = value\n\n if (property === \"attachedFiles\") {\n this.renderer.showMaxFilesNotification(value)\n } else if (property === \"invalidFiles\") {\n this.renderer.showInvalidFilesNotification(value)\n } else if (property === \"files\") {\n this.renderer.updateSelectedView(value)\n }\n\n return true\n },\n showMaxFilesNotification: (files) => {\n if (files.length <= this.maxValue) return\n\n window.helpers.notify(this.maxFilesSelectedNoticeValue, {type: \"error\"})\n },\n showInvalidFilesNotification: (files) => {\n if (files.length === 0) return\n\n window.helpers.notify(this.invalidFileTypeNoticeValue, {type: \"error\"})\n },\n updateSelectedView: (files) => {\n switch (this.selectedViewValue) {\n case \"simple\":\n this.renderer.updateSelectedViewSimple(files.length)\n break\n // NOTE: A second case, which we know would definitely arise, would be a gallery view\n default:\n break\n }\n },\n updateSelectedViewSimple: (count) => {\n this.descriptionTarget.style.display = \"none\"\n this.selectedViewSimpleSingleTarget.style.display = \"none\"\n this.selectedViewSimpleMultipleTarget.style.display = \"none\"\n\n if (count === 0) {\n this.descriptionTarget.style.display = \"block\"\n } else if (count === 1) {\n this.selectedViewSimpleSingleTarget.style.display = \"block\"\n } else {\n this.selectedViewSimpleMultipleTarget.style.display = \"block\"\n this.selectedViewSimpleMultipleText.textContent =\n this.selectedViewSimpleMultipleText.textContent.replace(/\\d/g, count)\n }\n }\n }\n\n connect() {\n // Ref: https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/Drag_operations#specifying_drop_targets\n // Ref: https://www.smashingmagazine.com/2018/01/drag-drop-file-uploader-vanilla-js/\n [\"dragenter\", \"dragover\", \"dragleave\", \"drop\"].forEach((eventName) => {\n this.containerTarget.addEventListener(eventName, (event) => {\n event.preventDefault()\n event.stopPropagation()\n })\n })\n\n // To disable the click event to propagate up to the containerTarget again\n // when it is programmatically called in the click handler below.\n this.fieldTarget.addEventListener(\"click\", (event) => event.stopPropagation())\n this.state = new Proxy({}, this.renderer)\n\n this.state.files = this.filesValue\n }\n\n attachDroppedFiles(event) {\n if (this.disabledValue) return\n\n const eventFiles = event.dataTransfer.files\n this.fieldTarget.files = eventFiles\n // Since we are programmatically changing the target's files, we should\n // manually send the \"change\" event for anyone else listening for it, along\n // with our own listener\n this.fieldTarget.dispatchEvent(new Event(\"change\"))\n }\n\n openFileBrowser() {\n if (this.disabledValue) return\n\n this.fieldTarget.click()\n }\n\n sanitizeAttachedFiles() {\n if (this.disabledValue) return\n\n const files = this.fieldTarget.files\n\n this.state.attachedFiles = files\n\n const dataTransfer = new DataTransfer()\n const invalidFiles = []\n for (const file of files) {\n if (!this.validType(file)) {\n invalidFiles.push(file)\n } else if (dataTransfer.items.length === this.maxValue) {\n continue\n } else {\n dataTransfer.items.add(file)\n }\n }\n this.state.invalidFiles = invalidFiles\n this.fieldTarget.files = dataTransfer.files\n\n this.state.files = dataTransfer.files\n }\n\n validType(file) {\n return this.mimeTypesValue === \"*/*\" || this.mimeTypes.includes(file.type)\n }\n\n get filesCount() {\n return this.fieldTarget.files.length\n }\n\n get mimeTypes() {\n return this.mimeTypesValue.split(\", \")\n }\n\n get selectedViewSimpleMultipleText() {\n return this.selectedViewSimpleMultipleTarget.getElementsByClassName(\"file-upload__description\")[0]\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n static targets = [\n \"list\",\n \"notificationTemplate\",\n \"iconsTemplate\",\n \"message\"\n ]\n\n static values = {\n defaultActionText: String,\n dialogFlashContainerClass: String\n }\n\n initialize() {\n this.queueMessages(false, 100)\n }\n\n handleFlashNotify = (event) => {\n const { text, options } = event.detail\n const clearExisting = options.clearExisting || false\n const messageId = options.id || global.helpers.unicodeHash(text)\n\n global.state.flashMessages.push({ id: messageId, body: text, ...options})\n this.queueMessages(clearExisting)\n }\n\n queueMessages(clearExisting, delay = 0) {\n this.clearExisting = clearExisting\n\n if (!this.hasMessageTarget) {\n this.presentMessages()\n return\n }\n\n setTimeout(() => {\n this.messageTargets.forEach((messageElement, index) => {\n const messageBody = messageElement.innerHTML\n const messageType = messageElement.dataset.messageType\n const messageId = messageElement.dataset.messageId || global.helpers.unicodeHash(messageBody)\n\n global.state.flashMessages.push({ id: messageId, body: messageBody, type: messageType })\n messageElement.remove()\n })\n\n this.presentMessages()\n }, delay)\n }\n\n presentMessages() {\n if (this.clearExisting) this.listContainer.innerHTML = \"\"\n\n global.state.flashMessages.forEach((messageHolder, index) => {\n const presentAfter = this.timeoutMessagePeriod * (index + 1)\n\n const options = { presentAfter, ...messageHolder }\n this.presentMessage(messageHolder.body, options)\n })\n }\n\n presentMessage(content, options = {}) {\n const notificationElement = this.buildMessage(content, options)\n\n setTimeout(() => {\n if (this.notificationExists(options.id)) return\n this.listContainer.appendChild(notificationElement)\n this.removeMessage(options.id)\n }, options.presentAfter || 0)\n }\n\n notificationExists(messageId) {\n if (!messageId) return false\n\n const notificationElement = this.listContainer.querySelector(`[data-id=\"${messageId}\"]`)\n return notificationElement != null\n }\n\n removeMessage(messageId) {\n const messageIndex = global.state.flashMessages.findIndex((message) => message.id === messageId)\n if (messageIndex === -1) return\n\n global.state.flashMessages.splice(messageIndex, 1)\n }\n\n buildMessage(content, options) {\n const template = this.notificationTemplateTarget\n const notificationNode = template.content.firstElementChild.cloneNode(true)\n const actionInfo = options.action || {}\n const replacementMap = {\n title: options.title,\n description: content,\n actionBody: actionInfo.text || this.defaultActionTextValue,\n actionUrl: actionInfo.path,\n }\n\n notificationNode.innerHTML = global.helpers.tagReplacements(notificationNode.innerHTML, replacementMap)\n this.prepareDataAttributes(notificationNode, options)\n this.prepareTitle(notificationNode, options.title)\n this.prepareAction(notificationNode, actionInfo)\n this.prepareIcon(notificationNode, options.type, options.icon)\n this.prepareVariant(notificationNode, options.type, options.variant || [])\n\n return notificationNode\n }\n\n prepareDataAttributes(notificationNode, options) {\n if (!options.id) return\n\n notificationNode.dataset.id = options.id\n }\n\n prepareTitle(notificationNode, title) {\n if (!title) {\n notificationNode.querySelector(\".notification-banner__title\").remove()\n }\n }\n\n prepareAction(notificationNode, actionInfo) {\n const actionTemplate = notificationNode.querySelector(\".notification-banner__action\")\n\n if(!actionInfo?.path) {\n actionTemplate.remove()\n return\n }\n\n Object.keys(actionInfo || {}).forEach((key) => {\n actionTemplate.dataset[key] = actionInfo[key]\n })\n }\n\n prepareVariant(notificationNode, messageType, variant) {\n let variants = Array.isArray(variant) ? variant : [variant]\n variants = variants.concat(this.standardVariants(messageType))\n const bannerVariants = variants.filter((variant) => variant)\n .map((variant) => `notification-banner--${variant}`)\n\n notificationNode.classList.add(...bannerVariants)\n }\n\n prepareIcon(notificationNode, messageType, icon) {\n if (!this.hasIconsTemplateTarget) return\n if (messageType === \"error\" || messageType === \"alert\") {\n icon = \"warning\"\n }\n\n if (!icon) {\n notificationNode.querySelector(\".notification-banner__image\").remove()\n return\n }\n\n const iconsElement = this.iconsTemplateTarget.content.firstElementChild.cloneNode(true)\n const iconClassName = `.icon--${icon.replace(/^\\//,\"\")}`\n const targetIconElement = iconsElement.querySelector(iconClassName)\n\n if (!targetIconElement) return\n\n const notificationImageElement = notificationNode.querySelector(\".notification-banner__image svg\")\n notificationImageElement.replaceWith(targetIconElement)\n }\n\n standardVariants(messageType) {\n if (messageType === \"custom\") return []\n\n const colorType = {\n notice: \"blue\",\n success: \"green\",\n error: \"red\",\n alert: \"yellow\"\n }[messageType]\n\n return [colorType, \"flash-message\"]\n }\n\n get timeoutMessagePeriod() {\n return 125\n }\n\n get listContainer() {\n // dialog opened as modal without both closing data attribute and closing class name\n const openedDialogSelector = \"dialog[data-opened-as-modal=\\\"true\\\"]:not([data-closing=\\\"true\\\"]):not(.dialog--closing)\"\n const modalDialogs = document.querySelectorAll(openedDialogSelector)\n if (modalDialogs.length > 0) {\n const topMostModalDialog = modalDialogs[modalDialogs.length - 1]\n return topMostModalDialog.querySelector(this.dialogFlashContainerClassValue)\n }\n\n return this.listTarget\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n static values = {\n selector: String,\n focusOnConnect: Boolean,\n targetIsChild: Boolean // Let's us make the selector much simpler\n }\n\n connect() {\n if (this.focusOnConnectValue && this.targetElement) {\n this.focusTarget()\n }\n }\n\n focusTarget(_event) {\n if (!this.targetElement) { return }\n\n const el = this.targetElement\n el.focus()\n // This is to ensure that on focus the cursor is at the end of the text\n if (this.textInputType(el)) {\n const index = el.value.length\n el.setSelectionRange(index, index)\n }\n }\n\n textInputType(el) {\n return el.type === \"text\" || el.type === \"textarea\"\n }\n\n get targetElement() {\n const el = this.targetIsChildValue ? this.element : document\n return el.querySelector(this.selectorValue)\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n static values = { id: String }\n static targets = [\"email\", \"phone\"]\n\n initialize() {\n this.gtag(\"js\", new Date())\n this.gtag(\"config\", this.idValue, {\"allow_enhanced_conversions\": true})\n }\n\n signup(event) {\n const [data, ,] = event.detail\n\n const isSuccess = typeof data === \"string\"\n if (isSuccess) {\n const signUpEvent = {\n email: this.emailTarget.value,\n \"phone_number\": this.phoneTarget.value\n }\n\n this.gtag(\"event\", \"conversion\", signUpEvent)\n }\n }\n\n gtag() {\n this.dataLayer.push(arguments)\n }\n\n get dataLayer() {\n window.dataLayer = window.dataLayer || []\n return window.dataLayer\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n static targets = [\"result\"]\n\n greet() {\n this.resultTarget.textContent = \"Hello, Stimulus!\"\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n static targets = [\"hide\", \"show\"]\n\n toggle(event) {\n event.preventDefault()\n\n this.hideTargets.forEach((target) => {\n target.classList.toggle(\"hidden\")\n })\n\n this.showTargets.forEach((target) => {\n target.classList.toggle(\"hidden\")\n })\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n static targets = [ \"highlight\", \"toggler\" ]\n static classes = [ \"supported\" ]\n\n connect() {\n this.toggle()\n }\n\n toggle() {\n if (this.data.get(\"togglertype\") === \"check_box\") {\n if (this.togglerTarget.checked) {\n this.highlightTarget.classList.add(this.supportedClass)\n } else {\n this.highlightTarget.classList.remove(this.supportedClass)\n }\n }\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n static targets = [\"content\", \"leftButton\", \"rightButton\"]\n static values = {\n scrollBy: String,\n displayScrollbars: Boolean\n }\n\n renderer = {\n set: (target, property, value) => {\n target[property] = value\n\n if (property === \"overflows\") {\n if (this.state.overflows) {\n this.contentTarget.classList.add(\"horizontal-scroller__content--navigation-visible\")\n this.leftButtonTarget.classList.add(\"horizontal-scroller__left-button--navigation-visible\")\n this.rightButtonTarget.classList.add(\"horizontal-scroller__right-button--navigation-visible\")\n } else {\n this.contentTarget.classList.remove(\"horizonal-scroller__content--navigation-visible\")\n this.leftButtonTarget.classList.remove(\"horizontal-scroller__left-button--navigation-visible\")\n this.rightButtonTarget.classList.remove(\"horizontal-scroller__right-button--navigation-visible\")\n }\n }\n\n return true\n }\n }\n\n connect() {\n if (\"IntersectionObserver\" in window) {\n const bindedHandleVisibilityChange = this.handleVisibilityChange.bind(this)\n this.visibilityObserver = new IntersectionObserver(bindedHandleVisibilityChange)\n this.visibilityObserver.observe(this.contentTarget)\n }\n }\n\n disconnect() {\n if (\"IntersectionObserver\" in window){\n this.visibilityObserver.disconnect()\n this.visibilityObserver = null\n }\n }\n\n initialize() {\n this.state = new Proxy({}, this.renderer)\n\n if (!this.displayScrollbars) {\n this.contentTarget.classList.add(\"horizontal-scroller__content\")\n }\n this.leftButtonTarget.classList.add(\"horizontal-scroller__left-button\")\n this.rightButtonTarget.classList.add(\"horizontal-scroller__right-button\")\n\n const action = this.contentTarget.dataset.action\n this.contentTarget.dataset.action = `${action} resize@window->horizontal-scroller#layout`\n\n this.layout()\n }\n\n handleVisibilityChange(entries, observer) {\n entries.forEach(entry => {\n if (entry.isIntersecting) {\n this.layout()\n }\n })\n }\n\n layout(event) {\n this.state.overflows = this.isOverflowing\n }\n\n scrollLeft(event) {\n event.preventDefault()\n\n this.contentTarget.scrollTo({\n left: this.contentTarget.scrollLeft - this.scrollAmount,\n behavior: \"smooth\"\n })\n }\n\n scrollRight(event) {\n event.preventDefault()\n\n this.contentTarget.scrollTo({\n left: this.contentTarget.scrollLeft + this.scrollAmount,\n behavior: \"smooth\"\n })\n }\n\n get isOverflowing() {\n return this.contentTarget.scrollHeight > this.contentTarget.clientHeight ||\n this.contentTarget.scrollWidth > this.contentTarget.clientWidth\n }\n\n get scrollAmount() {\n const scrollBy = parseInt(this.scrollBy)\n\n if (this.scrollByUnit === \"%\") return this.contentTarget.offsetWidth * (scrollBy / 100)\n\n return scrollBy\n }\n\n get scrollBy() {\n return this.scrollByValue || \"100\"\n }\n\n get displayScrollbars() {\n return this.displayScrollbarsValue || false\n }\n\n get scrollByUnit() {\n const scrollByUnit = this.scrollByUnits.find(unit => this.scrollBy.toString().endsWith(unit))\n\n if (scrollByUnit) return scrollByUnit\n\n return this.scrollByUnits[0]\n }\n\n get scrollByUnits() {\n return [\"px\", \"%\"]\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n static targets = [ \"contentToReplace\" ]\n\n load() {\n Rails.ajax({\n type: \"get\",\n url: this.data.get(\"url\"),\n dataType: \"script\"\n })\n }\n\n fetch() {\n if (this.hasContentToReplaceTarget && (this.isVisible(this.contentToReplaceTarget) === true)) {\n this.load()\n this.contentToReplaceTarget.remove() //to stop extra requests\n }\n }\n\n isVisible(ele) {\n const { top, bottom } = ele.getBoundingClientRect()\n const vHeight = (window.innerHeight || document.documentElement.clientHeight)\n\n return (\n (top > 0 || bottom > 0) &&\n top < vHeight\n )\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n static targets = [\n \"latInput\",\n \"longInput\",\n\n \"container\",\n ]\n\n static values = {\n lat : String,\n long : String,\n }\n\n connect() {\n let zoom = 2\n let lat = 55.378051\n let lng = -3.435973\n\n if (this.latInputTarget.value !== \"\" && this.longInputTarget.value !== \"\") {\n lat = this.latInputTarget.value\n lng = this.longInputTarget.value\n zoom = 14\n }\n\n /* disabling eslint here due to mapboxgl not being bundled */\n /* eslint-disable */\n this.map = new mapboxgl.Map({\n container: \"edit_google_map\",\n center: new mapboxgl.LngLat(lng, lat),\n style: \"https://api.maptiler.com/maps/basic/style.json?key=AUqGbDDrd3lWF7lPdu0g\",\n zoom: zoom\n })\n\n const nav = new mapboxgl.NavigationControl({showCompass: false})\n\n this.map.addControl(nav, \"top-left\")\n this.map.on(\"click\", (evt) => this.addMarker(evt))\n\n this.marker = new mapboxgl.Marker({\n element: this.containerTarget,\n offset: {x: 0, y: -40}\n }).setLngLat([lng, lat])\n .addTo(this.map)\n\n /* eslint-enable */\n }\n\n addMarker(evt) {\n const ll = evt.lngLat\n\n this.latInputTarget.value = ll.lat\n this.longInputTarget.value = ll.lng\n\n this.marker.setLngLat(ll.toArray()).addTo(this.map)\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\nimport anime from \"animejs/lib/anime.es.js\"\n\nexport default class extends Controller {\n static targets = [\"closeProgress\"]\n static values = {\n closeAfter: Number\n }\n\n renderer = {\n set: (target, property, value) => {\n target[property] = value\n\n if (property === \"closed\") {\n if (this.state.closed === true) {\n anime({\n targets: this.element,\n height: \"0\",\n paddingTop: \"0\",\n paddingBottom: \"0\",\n marginTop: \"0\",\n marginBottom: \"0\",\n opacity: \"0\",\n scale: 0.5,\n easing: \"easeInOutQuad\",\n duration: 250,\n complete: (animation) => {\n this.element.remove()\n }\n })\n }\n }\n\n return true\n }\n }\n\n initialize() {\n this.state = new Proxy({}, this.renderer)\n this.state.closed = false\n this.scheduleClose()\n }\n\n close(event) {\n if (event) event.preventDefault()\n\n this.state.closed = true\n }\n\n scheduleClose() {\n if (!this.hasCloseAfterValue) return\n\n setTimeout(() => this.close(), this.closeAfterValue)\n\n if (this.hasCloseProgressTarget) {\n const progressAnimationDuration = this.closeAfterValue / 1000\n this.closeProgressTarget.style.animationDuration = `${progressAnimationDuration}s`\n }\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n connect() {\n this.element.addEventListener(\"wheel\", (e) => e.preventDefault())\n }\n\n preventSubmit(event) {\n if (event.key === \"Enter\") {\n event.preventDefault()\n }\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n static values = { collapsed: Boolean, scrollContainerSelector: String }\n\n connect() {\n const lists = this.element.querySelectorAll(\"ul\")\n\n lists.forEach(list => list.classList.add(\"outline\"))\n\n if (this.collapsedValue) {\n this.element.querySelectorAll(\"[data-action='outline#toggle']\").forEach(toggle => {\n this.slideToggle(toggle, { duration: 0 })\n })\n }\n }\n\n toggle(event) {\n const element = event.currentTarget\n\n if (this.isTogglingWith(element)) {\n event.preventDefault()\n\n this.slideToggle(element)\n }\n }\n\n slideToggle(toggle, { duration = 200 } = {}) {\n const listItem = toggle.parentNode\n const list = listItem.querySelector(\"ul\")\n\n if (!list) return\n\n if (list.classList.contains(\"outline--collapsed\")) {\n toggle.classList.remove(\"outline__toggle--collapsed\")\n list.classList.remove(\"outline--collapsed\")\n listItem.classList.remove(\"outline__item--collapsed\")\n global.helpers.slide.down(list, duration)\n\n this.scrollItemToTopOfSection(toggle)\n } else {\n toggle.classList.add(\"outline__toggle--collapsed\")\n list.classList.add(\"outline--collapsed\")\n listItem.classList.add(\"outline__item--collapsed\")\n global.helpers.slide.up(list, duration)\n }\n }\n\n isTogglingWith(element) {\n return element.getAttribute(\"href\") === \"#\"\n }\n\n scrollItemToTopOfSection(element) {\n if (!this.scrollContainer(element)) return\n\n // Calculate the position to scroll to\n const menuTop = this.scrollContainer(element).getBoundingClientRect().top\n const itemTop = element.getBoundingClientRect().top\n\n // Scroll the menu container to make the list item top aligned with the menu top\n this.scrollContainer(element).scrollBy({\n top: itemTop - menuTop,\n behavior: \"smooth\"\n })\n }\n\n scrollContainer(element) {\n if (!this.scrollContainerSelectorValue) return\n\n return element.closest(this.scrollContainerSelectorValue)\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n static values = {\n eventName: String,\n properties: Object,\n sendEventOnConnect: { type: Boolean, default: false }\n }\n\n connect() {\n if (this.sendEventOnConnectValue) this.captureEvent()\n }\n\n captureEvent() {\n if (!window.posthog) return\n\n window.posthog.capture(this.eventNameValue, this.propertiesValue || {})\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n static targets = [\"label\"]\n static values = {\n disabledClassName: String,\n iconOnly: Boolean\n }\n\n connect() {\n const connectEvent = new CustomEvent(\"push-button:connect\", { detail: { controller: this } })\n\n this.element.dispatchEvent(connectEvent)\n }\n\n disable() {\n this.element.classList.add(this.disabledClassNameValue)\n }\n\n enable() {\n this.element.classList.remove(this.disabledClassNameValue)\n }\n\n toggleDisabled(statement = undefined) {\n if (statement === undefined) {\n this.element.classList.toggle(this.disabledClassNameValue)\n } else if (statement) {\n this.disable()\n } else {\n this.enable()\n }\n }\n\n set text(text) {\n if (!this.hasLabelTarget) return\n\n this.element.setAttribute(\"title\", text)\n this.labelTarget.textContent = text\n }\n\n get text() {\n if (!this.hasLabelTarget) return \"\"\n\n return this.labelTarget.textContent.trim()\n }\n\n get isIconOnly() {\n return this.iconOnlyValue\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n static targets = [\"validationBannerTemplate\", \"validationBanners\"]\n\n renderer = {\n set: (target, property, value) => {\n target[property] = value\n\n if (property === \"errors\") {\n this.renderer.removeExistingValidationMessages()\n\n if (this.state.errors) {\n for (const [field, validationMessages] of Object.entries(this.state.errors)) {\n if (field === \"base\") {\n this.renderer.renderValidationBannerMessages(field, validationMessages)\n } else {\n this.renderer.renderFormFieldValidationMessages(field, validationMessages)\n }\n }\n }\n }\n\n return true\n },\n\n removeExistingValidationMessages: () => {\n this.validationBannersTarget.innerHTML = \"\"\n\n this.element.querySelectorAll(\".form-field__validation-messages\").forEach((validationMessagesNode) => {\n validationMessagesNode.innerHTML = \"\"\n })\n\n this.element.querySelectorAll(\".form-field\").forEach((formFieldNode) => {\n formFieldNode.classList.remove(\"form-field--error\")\n })\n },\n\n renderFormFieldValidationMessages: (field, validationMessages) => {\n const fieldNode = this.element.querySelector(`[name*=\"[${field}]\"]:not([type=\"hidden\"])`)\n\n const formFieldNode = fieldNode.closest(\".form-field\")\n const validationMessageTemplateNode = formFieldNode.querySelector(\"template.form-field__validation-message-template\")\n const validationMessagesNode = formFieldNode.querySelector(\".form-field__validation-messages\")\n\n validationMessages.forEach((validationMessage) => {\n const validationMessageNode = validationMessageTemplateNode.content.cloneNode(true)\n const validationMessageTextNode = validationMessageNode.querySelector(\".form-field__validation-message-text\")\n validationMessageTextNode.innerText = validationMessage\n validationMessagesNode.appendChild(validationMessageNode)\n })\n\n formFieldNode.classList.add(\"form-field--error\")\n\n },\n\n renderValidationBannerMessages: (field, validationMessages) => {\n validationMessages.forEach((validationMessage) => {\n const validationBannerNode = this.validationBannerTemplateTarget.content.cloneNode(true)\n const validationBannerBodyNode = validationBannerNode.querySelector(\".notification-banner__body\")\n validationBannerBodyNode.innerText = validationMessage\n this.validationBannersTarget.appendChild(validationBannerNode)\n })\n }\n }\n\n initialize() {\n this.state = new Proxy({}, this.renderer)\n\n this.clearErrors()\n }\n\n submit(event) {\n const [data, , ] = event.detail\n\n this.state.errors = data[\"errors\"] || []\n }\n\n clearErrors(event) {\n this.state.errors = []\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n static values = { selector: String }\n\n remove(_event) {\n if (!this.targetElement) { return }\n\n this.targetElement.remove()\n }\n\n get targetElement() {\n return document.querySelector(this.selectorValue)\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n static targets = [ \"source\" ]\n static values = {\n toggleClass: String,\n idTrigger: String\n }\n\n toggle() {\n if (event.currentTarget.getAttribute(\"id\") === this.idTriggerValue) {\n this.sourceTarget.classList.remove(this.toggleClassValue)\n } else {\n this.sourceTarget.classList.add(this.toggleClassValue)\n }\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n static values = {\n selector: String\n }\n\n scrollIntoView(_event) {\n console.log(this.selectorValue)\n console.log(this.targetElement)\n if (!this.targetElement) { return }\n\n console.log(this.targetElement)\n\n this.targetElement.scrollIntoView()\n }\n\n get targetElement() {\n return this.element.querySelector(this.selectorValue)\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n static targets = [\"option\", \"selectedCount\", \"selectedCountContainer\", \"selectedTagTemplate\", \"selectedTags\", \"emptySearch\", \"select\"]\n static classes = [\"optionHidden\"]\n\n connect() {\n this.optionTargets.forEach(el => {\n this.updateSelectedOption(this.checkboxFor(el))\n })\n }\n\n search(event) {\n const text = event.target.value.toLocaleLowerCase().trim()\n let empty = true\n\n this.optionTargets.forEach(el => {\n if (text === \"\" || this.labelFor(el).toLocaleLowerCase().search(text) !== -1) {\n el.classList.remove(this.optionHiddenClass)\n empty = false\n } else {\n el.classList.add(this.optionHiddenClass)\n }\n })\n\n if (this.hasEmptySearchTarget) {\n if (empty) {\n this.emptySearchTarget.style.display = \"flex\"\n } else {\n this.emptySearchTarget.style.display = \"none\"\n }\n }\n }\n\n toggleOptionSelection(event) {\n const checkbox = event.target\n this.updateSelectedOption(checkbox)\n }\n\n removeTag(event) {\n // Can use this since, tags are not something that will be created differently for different types of options\n const { selectFieldId } = event.target.parentElement.dataset\n const el = this.optionTargets.find(el => el.dataset.selectFieldId === selectFieldId)\n if (el) {\n this.checkboxFor(el).click()\n }\n }\n\n // count the number of selected options and update the selected count\n updateSelectedCount(checked) {\n const selectedOptions = this.optionTargets.filter(el => this.checkboxFor(el).checked)\n const newCount = selectedOptions.length\n if (newCount === 0) {\n this.selectedCountTarget.innerText = this.selectedCountTarget.dataset.selectedCountMessageZero\n } else if (newCount === 1) {\n this.selectedCountTarget.innerText = this.selectedCountTarget.dataset.selectedCountMessageOne\n } else {\n this.selectedCountTarget.innerText = this.selectedCountTarget.dataset.selectedCountMessageOther.replace(\"%{count}\", newCount)\n }\n\n if (newCount > 0) {\n this.selectedCountContainerTarget.style.display = \"flex\"\n } else {\n this.selectedCountContainerTarget.style.display = \"none\"\n }\n }\n\n updateSelectedTag(checkbox) {\n const { checked } = checkbox\n const { selectFieldId } = this.elementFor(checkbox).dataset\n if (checked) {\n const node = this.selectedTagTemplateTarget.content.cloneNode(true)\n const tag = node.firstElementChild\n tag.dataset.selectFieldId = selectFieldId\n\n const textNode = tag.querySelector(\".text\")\n textNode.innerText = this.labelFor(this.elementFor(checkbox))\n\n this.tagsRowThree.appendChild(node)\n } else {\n outer:\n for (const container of this.selectedTagsTargets) {\n for (const el of container.children) {\n if (el.dataset.selectFieldId === selectFieldId) {\n el.remove()\n break outer\n }\n }\n }\n }\n\n this.arrangeSelectedTags()\n }\n\n updateSelectedOption(checkbox) {\n // This might need to be updated if we want to create an option component which simply cannot\n // conform to the convention of having the value in the checkbox field\n const { checked, value } = checkbox\n this.selectTarget.querySelector(`option[value=\"${value}\"]`).selected = checked\n\n if (this.hasSelectedCountTarget) {\n this.updateSelectedCount(checkbox.checked)\n }\n if (this.hasSelectedTagsTarget) {\n this.updateSelectedTag(checkbox)\n }\n }\n\n arrangeSelectedTags() {\n this.tagsRowOne.style.display = \"none\"\n this.tagsRowTwo.style.display = \"none\"\n this.tagsRowThree.style.display = \"none\"\n\n if (this.selectedTagsTargets.length === 0) {\n return\n }\n\n const tags = this.selectedTagsTargets.reduce((acc, parent) => {\n for (const el of parent.children) {\n acc.push(el)\n }\n return acc\n }, [])\n\n let limit = 5\n let numRows = 1\n\n if (this.isMobile) {\n limit = 3\n }\n\n this.tagsRowOne.style.display = \"flex\"\n\n if (tags.length > limit) {\n this.tagsRowTwo.style.display = \"flex\"\n numRows = 2\n }\n\n if (tags.length > (limit * 2)) {\n this.tagsRowThree.style.display = \"flex\"\n numRows = 3\n }\n\n const hasThirdRow = numRows > 2\n\n if (numRows === 1) {\n tags.forEach(el => this.tagsRowOne.appendChild(el))\n } else {\n const rowTwoStart = Math.floor(tags.length / numRows)\n // This is required so that we evenly divide the tags\n const halfwayBetweenRemainingTags = Math.floor((tags.length - rowTwoStart) / 2)\n const rowThreeStart = hasThirdRow ? (rowTwoStart + halfwayBetweenRemainingTags) : tags.length\n tags.forEach((el, i) => {\n if (i < rowTwoStart) {\n this.tagsRowOne.appendChild(el)\n } else if (i < rowThreeStart) {\n this.tagsRowTwo.appendChild(el)\n } else {\n this.tagsRowThree.appendChild(el)\n }\n })\n }\n }\n\n labelFor(el) {\n return el.dataset.selectFieldLabel\n }\n\n checkboxFor(el) {\n return el.querySelector(\"input[type=\\\"checkbox\\\"]\")\n }\n\n elementFor(checkbox) {\n return checkbox.parentElement\n }\n\n get tagsRowOne() {\n return this.selectedTagsTargets[0]\n }\n\n get tagsRowTwo() {\n return this.selectedTagsTargets[1]\n }\n\n get tagsRowThree() {\n return this.selectedTagsTargets[2]\n }\n\n get isMobile() {\n return this.element.offsetWidth <= 420\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n static values = {\n vapidPublicKey: String,\n requestNotificationPermission: Boolean\n }\n\n connect() {\n this.registrationHandlerBound = this.handleRegistrationEvent.bind(this)\n window.addEventListener(\"serviceworker:register\", this.registrationHandlerBound)\n this.startRegistration(\n { scope: \"/\" },\n registration => {\n this.handleRegistration(registration)\n },\n error => console.error(\"Service Worker registration failed:\", error)\n )\n }\n\n disconnect() {\n window.removeEventListener(\"serviceworker:register\", this.registrationHandlerBound)\n }\n\n\n // Handle service worker registration through dispatched event\n handleRegistrationEvent = (event) => {\n const { registrationDetails, onSuccess, onError } = event.detail\n\n this.startRegistration(registrationDetails, onSuccess, onError)\n }\n\n startRegistration(registrationDetails, onSuccess, onError) {\n if (!(\"serviceWorker\" in navigator)) {\n console.error(\"Service Worker not supported\")\n return\n }\n\n navigator.serviceWorker.register(\"/service-worker.js\", registrationDetails)\n .then((registration) => {\n onSuccess(registration)\n })\n .catch((error) => {\n onError(error)\n })\n }\n\n // We will only proceed with the notification subscription flow\n // if the requestNotificationPermissionValue is true\n // otherwise, we will not ask for permission\n handleRegistration(registration) {\n if (this.requestNotificationPermissionValue === true) {\n return Promise.all([\n this.handleNotificationSubscription(registration),\n this.registerBackgroundSync(registration)\n ])\n } else {\n return Promise.resolve()\n }\n }\n\n handleNotificationSubscription(registration) {\n return Notification.requestPermission()\n .then(permission => {\n if (permission !== \"granted\") {\n console.error(\"Notification permission denied\")\n return Promise.reject(\"Notification permission denied\")\n }\n return navigator.serviceWorker.ready\n })\n .then(registration => registration.pushManager.getSubscription())\n .then(subscription => {\n const subscribedInServer = window.localStorage.getItem(\"push-notifications-subscribed-in-server\")\n if (subscription && subscribedInServer) {\n return Promise.resolve(subscription)\n }\n return this.subscribeToPushNotifications(registration)\n })\n .catch(error => console.error(\"Subscription flow failed:\", error))\n }\n\n subscribeToPushNotifications(registration) {\n return registration.pushManager.subscribe({\n userVisibleOnly: true,\n applicationServerKey: window.helpers.urlBase64ToUint8Array(this.vapidPublicKeyValue)\n })\n .then(subscription => {\n this.sendSubscriptionToServer(subscription)\n })\n .catch(error => console.error(\"PushManager Subscription failed:\", error))\n }\n\n sendSubscriptionToServer(subscription) {\n const body = JSON.stringify(subscription)\n const csrfToken = document.querySelector(\"meta[name=\\\"csrf-token\\\"]\").content\n const headers = {\n \"Content-Type\": \"application/json\",\n \"X-CSRF-Token\": csrfToken\n }\n return fetch(\"/service-worker/subscribe\", { method: \"POST\", headers, body })\n .then(response => {\n if (response.ok) {\n window.localStorage.setItem(\"push-notifications-subscribed-in-server\", true)\n } else {\n console.error(\"Failed Subscription. Bad status code from server:\", response.status)\n }\n })\n }\n\n registerBackgroundSync(registration) {\n if (!(\"SyncManager\" in window)) {\n return Promise.reject(\"SyncManager not supported\")\n }\n\n return navigator.serviceWorker.ready\n .then(registration => registration.sync.getTags())\n .then(tags => {\n if (tags.includes(\"nearcut-sw\")) {\n return Promise.resolve()\n }\n return this.registerSync(registration)\n })\n .catch(error => console.error(\"Background Sync registration failed:\", error))\n }\n\n registerSync(registration) {\n return registration.sync.register(\"nearcut-sw\")\n .then(() => {\n console.log(\"Background Sync registered\")\n })\n .catch(error => console.error(\"Background Sync registration failed:\", error))\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n static values = { extraSubmitParams: Object }\n\n static targets = [\n \"form\"\n ]\n\n connect() {\n this.setParamAfterHours()\n }\n\n setParamAfterHours() {\n if(!window.location.href.includes(\"calendar\")) { return }\n if(!this.formTarget || !this.extraSubmitParamsValue) { return }\n\n const formUrl = new URL(this.formTarget.action, global.location.origin)\n const newParams = this.extraSubmitParamsValue\n\n for (const key in newParams) {\n formUrl.searchParams.append(key, newParams[key])\n }\n\n this.formTarget.action = formUrl.toString()\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\nimport StackManager from \"models/stack_manager\"\nimport BreakpointChange from \"shared/breakpoint_change\"\n\nclass Sheet extends Controller {\n static targets = [\n \"desktopCloseButton\",\n \"mobileCloseButton\",\n \"closeButtonHolder\"\n ]\n\n connect() {\n this.element.addEventListener(\"sheet-update:pop\", this.onPop.bind(this))\n this._stackManager = this._stackManagerFor(this.element)\n this._buildCloseButtons()\n this._attachBreakpointListeners()\n }\n\n disconnect() {\n this.element.removeEventListener(\"sheet-update:pop\", this.onPop.bind(this))\n }\n\n pop(event) {\n event.preventDefault()\n event.stopImmediatePropagation()\n\n this._stackManager.pop()\n }\n\n popWithoutPrevent(event) {\n this._stackManager.pop()\n }\n\n // This in essence clear the stack (trigger all pop events which close all dialogs, including the primary one)\n // and then the primary sheet controller has a callback (i.e.: timetable__payment_sheet_controller#onSheetReload)\n // where the logic is applied to reload the primary sheet\n reloadPrimary(event) {\n event.stopImmediatePropagation()\n\n const element = event.target || event.currentTarget\n this._afterServerRequestIfAny(element, () => {\n const eventParams = event.params || {}\n this._stackManager.clear({ reloadPrimary: true, ...eventParams })\n })\n }\n\n close(event) {\n event.preventDefault()\n\n // TODO: Change this once we have a shared context between all dialogs\n this._closeStack = true\n this._stackManager.clear()\n }\n\n onPop(event) {\n event.preventDefault()\n\n this._closeDialog(this.element)\n }\n\n // We need to check if the element is a form or a button with remote: true\n // And then listen for the ajax:success event to reload the stack AFTER\n // the server request has been completed\n _afterServerRequestIfAny(element, reloadStackFn) {\n if (element.getAttribute(\"type\") === \"submit\") {\n this._hookSuccessAjaxEvent(element.closest(\"form\"), reloadStackFn)\n } else if (element.getAttribute(\"type\") === \"button\" && element.dataset.remote === \"true\") {\n this._hookSuccessAjaxEvent(element, reloadStackFn)\n } else {\n reloadStackFn()\n }\n }\n\n _hookSuccessAjaxEvent(element, reloadStackFn) {\n if (!element) return\n\n element.addEventListener(\"ajax:success\", (event) => {\n reloadStackFn()\n }, { once: true })\n }\n\n\n _closeDialog(dialog) {\n if (!dialog) return\n\n const dialogCtrl = this._loadControllerFor(dialog)\n if (dialogCtrl) {\n dialogCtrl.close({\n detail: { dialogCloseClassName: this.dialogCloseClassName }\n })\n }\n }\n\n _loadControllerFor(dialog) {\n let dialogCtrl = this.application.getControllerForElementAndIdentifier(dialog, \"dialog\")\n if (!dialogCtrl) {\n // We need to fallback to action-sheet\n dialogCtrl = this.application.getControllerForElementAndIdentifier(dialog, \"action-sheet\")\n }\n return dialogCtrl\n }\n\n _stackManagerFor(dialog) {\n let targetStackName = dialog.dataset.dialogStackNameValue\n if (!targetStackName) {\n targetStackName = dialog.dataset.actionSheetStackNameValue\n }\n return StackManager.loadFor(targetStackName)\n }\n\n _buildCloseButtons() {\n if (this.hasCloseButtonHolderTarget) {\n this._desktopCloseButton = this.desktopCloseButtonTarget.content.firstElementChild.cloneNode(true)\n this._mobileCloseButton = this.mobileCloseButtonTarget.content.firstElementChild.cloneNode(true)\n this.closeButtonHolderTarget.appendChild(this._desktopCloseButton)\n this.closeButtonHolderTarget.appendChild(this._mobileCloseButton)\n }\n }\n\n _attachBreakpointListeners() {\n const breakpointChange = BreakpointChange.buildFromGlobal()\n breakpointChange.triggerOnAddition = true\n\n // on tiny, small breakpoints, we show the mobile close button\n breakpointChange.on([\"tiny\", \"small\"], (mediaQueryMatches) => {\n if (mediaQueryMatches) {\n this._showMobileCloseButton()\n }\n })\n\n breakpointChange.on([\"medium\", \"large\", \"huge\"], (mediaQueryMatches) => {\n if (mediaQueryMatches) {\n this._showDesktopCloseButton()\n }\n })\n }\n\n _showMobileCloseButton() {\n if (!this.hasCloseButtonHolderTarget) {\n return\n }\n\n this._hideCloseButtons()\n this._mobileCloseButton.style.display = \"\"\n }\n\n _showDesktopCloseButton() {\n if (!this.hasCloseButtonHolderTarget) {\n return\n }\n\n this._hideCloseButtons()\n this._desktopCloseButton.style.display = \"\"\n }\n\n _hideCloseButtons() {\n this.closeButtonHolderTarget.querySelectorAll(\"a.button\").forEach((button) => {\n button.style.display = \"none\"\n })\n }\n\n get dialogCloseClassName() {\n if (this._closeStack) {\n return \"dialog--closing-stack\"\n } else {\n return \"dialog--closing\"\n }\n }\n}\n\nexport default Sheet\n","import { Controller } from \"@hotwired/stimulus\"\nimport StackManager from \"models/stack_manager\"\n\n// This acts as the high-level controller for the sheet stack\n// to intercept the openining of dialog (or action-sheet) and push it to the stack\nclass SheetStack extends Controller {\n static targets = [\"origin\"]\n\n initialize() {\n this._dialogOpenedBind = this.openedDialog.bind(this)\n }\n\n connect() {\n window.addEventListener(\"sheet-stack-dialog:present\", this._dialogOpenedBind)\n window.addEventListener(\"sheet-stack-action-sheet:present\", this._dialogOpenedBind)\n }\n\n disconnect() {\n window.removeEventListener(\"sheet-stack-dialog:present\", this._dialogOpenedBind)\n window.removeEventListener(\"sheet-stack-action-sheet:present\", this._dialogOpenedBind)\n }\n\n openedDialog(event) {\n const currentDialog = event.detail.targetDialog\n if (!currentDialog) return\n\n const stackManager = this._stackManagerFor(currentDialog)\n this._appendSheetController(currentDialog, stackManager)\n stackManager.push(currentDialog)\n }\n\n _appendSheetController(dialog, stackManager) {\n let dataControllers = dialog.dataset.controller || \"\"\n\n // We need to specifically check for sheet controller to prevent duplication\n if (dataControllers.split(\" \").includes(\"sheet\")) return\n\n dataControllers = `${dataControllers} sheet`\n dialog.dataset.sheetStackIndexValue = stackManager.length + 1\n dialog.dataset.controller = dataControllers\n }\n\n _stackManagerFor(dialog) {\n let targetStackName = dialog.dataset.dialogStackNameValue\n if (!targetStackName) {\n targetStackName = dialog.dataset.actionSheetStackNameValue\n }\n return StackManager.loadFor(targetStackName)\n }\n}\n\nexport default SheetStack\n","import { Controller } from \"@hotwired/stimulus\"\n\nconst VALID_MICH_HEREINLASSEN_VALUE = \"1\"\n\nexport default class extends Controller {\n static targets = [ \"michHereinlassen\" ]\n\n connect() {\n // We do this instead of setting the value directly because we need js\n // enabled for our onboarding process and anyone who doesn't have js\n // enabled is a bot\n if (this.hasMichHereinlassenTarget) {\n this.michHereinlassenTarget.value = VALID_MICH_HEREINLASSEN_VALUE\n }\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nconst upKey = 38\nconst downKey = 40\n\nexport default class extends Controller {\n static targets = [\"input\", \"decrease\", \"increase\"]\n\n renderer = {\n set: (target, property, value) => {\n target[property] = value\n\n if (property === \"value\") {\n this.inputTarget.value = this.state.value\n this.inputTarget.dispatchEvent(new Event(\"change\"))\n\n this.enableButtons()\n\n if (this.isReachedMin) this.disableDecreaseButton()\n if (this.isReachedMax) this.disableIncreaseButton()\n }\n\n return true\n }\n }\n\n initialize() {\n this.state = new Proxy({}, this.renderer)\n this.propose()\n this.commit()\n }\n\n commit(event) {\n this.state.value = this.state.proposedValue\n }\n\n decrease(event) {\n if (event) event.preventDefault()\n\n if (this.isDisabled) return\n if (this.isReachedMin) return\n\n this.proposeDecrease()\n this.commit()\n }\n\n increase(event) {\n if (event) event.preventDefault()\n\n if (this.isDisabled) return\n if (this.isReachedMax) return\n\n this.proposeIncrease()\n this.commit()\n }\n\n propose(event) {\n const proposedValue = this.parsedInputValue\n\n if (proposedValue !== undefined) this.state.proposedValue = proposedValue\n\n if (this.isReachedMax) this.state.proposedValue = this.max\n if (this.isReachedMin) this.state.proposedValue = this.min\n }\n\n vary(event) {\n switch (event.keyCode) {\n case upKey:\n event.preventDefault()\n\n this.propose()\n this.increase()\n\n break\n case downKey:\n event.preventDefault()\n\n this.propose()\n this.decrease()\n\n break\n }\n }\n\n proposeDecrease() {\n this.state.proposedValue -= this.step\n }\n\n proposeIncrease() {\n this.state.proposedValue += this.step\n }\n\n enableButtons() {\n [this.decreaseTarget, this.increaseTarget].forEach(el => el.classList.remove(\"button--disabled\"))\n }\n\n disableDecreaseButton() {\n this.decreaseTarget.classList.add(\"button--disabled\")\n }\n\n disableIncreaseButton() {\n this.increaseTarget.classList.add(\"button--disabled\")\n }\n\n get parsedInputValue() {\n return this.parseInt(this.inputTarget.value, 0)\n }\n\n get step() {\n return this.parseInt(this.inputTarget.getAttribute(\"step\"), 1)\n }\n\n get min() {\n return this.parseInt(this.inputTarget.getAttribute(\"min\"), null)\n }\n\n get max() {\n return this.parseInt(this.inputTarget.getAttribute(\"max\"), null)\n }\n\n get isReachedMin() {\n return this.min && this.nextDecreasedValue < this.min\n }\n\n get isReachedMax() {\n return this.max && this.nextIncreasedValue > this.max\n }\n\n get nextIncreasedValue() {\n return this.state.proposedValue + this.step\n }\n\n get nextDecreasedValue() {\n return this.state.proposedValue - this.step\n }\n\n get isDisabled() {\n return this.element.classList.contains(\"stepper-field--disabled\")\n }\n\n parseInt(originalValue, defaultValue) {\n if (originalValue == \"\") return\n\n const value = parseInt(originalValue?.match(/-?\\d+/g)?.join(\"\"))\n return Number.isNaN(value) ? defaultValue : value\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n static targets = [\"cta\"]\n static values = {\n mainCtaSelector: String,\n showOnDesktop: Boolean\n }\n\n connect() {\n // With timeout we can be certain that any css/image loading stuff has happened and we are\n // at the correct scroll position.\n setTimeout(() => this.onScroll(), 100)\n }\n\n onScroll() {\n if (this.mainCta() === null) {\n console.log(\"Sticky CTA: cannot find mainCTA\")\n return\n }\n if (this.isScrolledIntoView(this.mainCta()) || this.hideOnDesktop()) {\n this.hideCTA()\n } else {\n this.showCTA()\n }\n }\n\n hideCTA() {\n this.ctaTarget.style.display = \"none\"\n }\n\n showCTA() {\n this.ctaTarget.style.display = \"block\"\n }\n\n mainCta() {\n return document.querySelector(this.mainCtaSelectorValue)\n }\n\n isScrolledIntoView(el) {\n var rect = el.getBoundingClientRect()\n var elemTop = rect.top\n var elemBottom = rect.bottom\n\n // Only completely visible elements return true:\n var isVisible = (elemTop >= 0) && (elemBottom <= window.innerHeight)\n // Partially visible elements return true:\n //isVisible = elemTop < window.innerHeight && elemBottom >= 0\n return isVisible\n }\n\n hideOnDesktop() {\n return this.desktopViewPort() && !this.showOnDesktopValue\n }\n\n desktopViewPort() {\n return window.innerWidth > 768\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n static targets = [\"submit\", \"button\"]\n static classes = [\"hide\"]\n static values = {\n visible: Boolean,\n submitDisabled: Boolean\n }\n\n connect() {\n if (this.visibleValue) {\n this.show()\n }\n if (this.submitDisabledValue) {\n this.disableSubmit()\n }\n }\n\n toggle() {\n if (this.visible) {\n this.hide()\n } else {\n this.show()\n }\n }\n\n show() {\n this.submitTarget.classList.remove(this.hideClass)\n }\n\n hide() {\n this.submitTarget.classList.add(this.hideClass)\n }\n\n disableSubmit() {\n this.button.disabled = true\n this.button.classList.add(\"button--disabled\")\n }\n\n enableSubmit() {\n this.button.disabled = false\n this.button.classList.remove(\"button--disabled\")\n }\n\n get visible() {\n return !this.submitTarget.classList.contains(this.hideClass)\n }\n\n get button() {\n return this.element.querySelector(\".button\")\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n static values = { initialSection: String }\n static targets = [\"tab\", \"section\"]\n static classes = [\"sectionToggle\", \"tabToggle\"]\n\n renderer = {\n set: (target, property, value) => {\n target[property] = value\n\n this.renderer.toggleCurrentSection(property)\n\n return true\n },\n toggleCurrentSection: (property) => {\n if (property !== \"currentSectionSelector\") return\n\n this.sectionTargets.forEach((section) => section.classList.remove(this.sectionToggleSelector))\n this.tabTargets.forEach((tab) => tab.classList.remove(this.tabToggleSelector))\n\n this.currentSectionElement.classList.add(this.sectionToggleSelector)\n this.currentTabElement.classList.add(this.tabToggleSelector)\n }\n }\n\n initialize() {\n this.hashChangeBind = this.loadTabFromAnchor.bind(this)\n this.state = new Proxy({}, this.renderer)\n if (this.hasInitialSectionValue) {\n this.state.currentSectionSelector = this.initialSectionValue\n }\n }\n\n connect() {\n window.addEventListener(\"hashchange\", this.hashChangeBind)\n // Check if the current anchor is a valid tab when connecting\n this.loadTabFromAnchor()\n }\n\n disconnect() {\n window.removeEventListener(\"hashchange\", this.hashChangeBind)\n }\n\n show(event) {\n event.preventDefault()\n const currentHref = event.currentTarget.getAttribute(\"href\")\n\n const hashPortion = currentHref.split(\"#\")[1]\n if (hashPortion) window.location.hash = hashPortion\n }\n\n loadTabFromAnchor(event) {\n const anchor = window.location.hash\n if (!anchor) return\n\n if (this.isTabAvailable(anchor)) {\n this.switchTab(anchor)\n }\n }\n\n isTabAvailable(tab) {\n if (!tab) return false\n\n return Array.from(this.tabTargets).some(tabElement => {\n return tabElement.hash.includes(tab)\n })\n }\n\n switchTab(tab) {\n this.state.currentSectionSelector = tab\n }\n\n get currentSectionElement() {\n return this.sectionTargets.find((el) => el.matches(this.state.currentSectionSelector))\n }\n\n get currentTabElement() {\n return this.tabTargets.find((el) => el.matches(`a[href=\"${this.state.currentSectionSelector}\"]`))\n }\n\n get sectionToggleSelector() {\n if (this.hasSectionToggleClass) {\n return this.sectionToggleClass\n } else {\n return \"element--on\"\n }\n }\n\n get tabToggleSelector() {\n if (this.hasTabToggleClass) {\n return this.tabToggleClass\n } else {\n return \"tab__link--active\"\n }\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n static targets = [\"upload\", \"picker\"]\n static classes = [\"hideUpload\", \"hidePicker\"]\n\n hidePicker(event) {\n event.preventDefault()\n\n if (this.hasUploadTarget && this.hasPickerTarget) {\n this.pickerTarget.classList.add(this.hidePickerClass)\n this.uploadTarget.classList.remove(this.hideUploadClass)\n }\n }\n\n showPicker(event) {\n event.preventDefault()\n\n if (this.hasUploadTarget && this.hasPickerTarget) {\n this.pickerTarget.classList.remove(this.hidePickerClass)\n this.uploadTarget.classList.add(this.hideUploadClass)\n }\n }\n}\n","\nimport { Controller } from \"@hotwired/stimulus\"\n\n// Ref: https://github.com/STRML/textFit/blob/master/textFit.js\nexport default class extends Controller {\n static values = {\n alignVert: Boolean,\n alignHoriz: Boolean,\n multiLine: Boolean,\n detectMultiLine: Boolean,\n minFontSize: Number,\n maxFontSize: Number,\n reProcess: Boolean,\n widthOnly: Boolean,\n alignVertWithFlexbox: Boolean,\n observeResize: Boolean\n }\n\n initialize() {\n if (!this.hasObserveResizeValue) {\n this.observeResizeValue = true\n }\n\n if (this.observeResizeValue) {\n this.resizeObserver = new ResizeObserver(() => this.fitText())\n }\n }\n\n connect() {\n if (this.observeResizeValue) {\n this.resizeObserver.observe(this.element)\n }\n\n this.fitText()\n }\n\n disconnect() {\n if (this.observeResizeValue) {\n this.resizeObserver.unobserve(this.element)\n }\n }\n\n fitText() {\n // To understand them all, checkout `this.defaultSettings`\n const options = {\n alignVert: this.alignVertValue,\n alignHoriz: this.alignHorizValue,\n multiLine: this.multiLineValue,\n detectMultiLine: this.hasDetectMultilineValue ? this.detectMultiLineValue : true,\n minFontSize: this.minFontSizeValue,\n maxFontSize: this.maxFontSizeValue,\n reProcess: this.hasReProcessValue ? this.reProcessValue : true,\n widthOnly: this.widthOnlyValue,\n alignVertWithFlexbox: this.alignVertWithFlexboxValue,\n }\n\n this.textFit(this.element, options)\n this.dispatchTextFitEvent()\n }\n\n // I've kept the entire textFit code the same, except making it object oriented\n\n textFit(els, options) {\n if (!options) options = {}\n // Extend options.\n var settings = {}\n for (var key in this.defaultSettings) {\n if (options[key]) {\n settings[key] = options[key]\n } else {\n settings[key] = this.defaultSettings[key]\n }\n }\n\n // Convert jQuery objects into arrays\n if (typeof els.toArray === \"function\") {\n els = els.toArray()\n }\n\n // Support passing a single el\n var elType = Object.prototype.toString.call(els)\n if (elType !== \"[object Array]\" && elType !== \"[object NodeList]\" &&\n elType !== \"[object HTMLCollection]\") {\n els = [els]\n }\n\n // Process each el we\"ve passed.\n for (var i = 0; i < els.length; i++) {\n this.processItem(els[i], settings)\n }\n }\n\n /**\n * The meat. Given an el, make the text inside it fit its parent.\n * @param {DOMElement} el Child el.\n * @param {Object} settings Options for fit.\n */\n processItem(el, settings) {\n if (!this.isElement(el) || (!settings.reProcess && el.getAttribute(\"textFitted\"))) {\n return false\n }\n\n // Set textFitted attribute so we know this was processed.\n if (!settings.reProcess) {\n el.setAttribute(\"textFitted\", 1)\n }\n\n var innerSpan, originalHeight, originalHTML, originalWidth\n var low, mid, high\n // Get element data.\n originalHTML = el.innerHTML\n originalWidth = this.innerWidth(el)\n originalHeight = this.innerHeight(el)\n // Don\"t process if we can\"t find box dimensions\n if (!originalWidth || (!settings.widthOnly && !originalHeight)) {\n if (!settings.widthOnly) throw new Error(\"Set a static height and width on the target element \" + el.outerHTML + \" before using textFit!\")\n else throw new Error(\"Set a static width on the target element \" + el.outerHTML + \" before using textFit!\")\n }\n\n // Add textFitted span inside this container.\n if (originalHTML.indexOf(\"textFitted\") === -1) {\n innerSpan = document.createElement(\"span\")\n innerSpan.className = \"textFitted\"\n // Inline block ensure it takes on the size of its contents, even if they are enclosed\n // in other tags like \n innerSpan.style[\"display\"] = \"inline-block\"\n innerSpan.innerHTML = originalHTML\n el.innerHTML = \"\"\n el.appendChild(innerSpan)\n } else {\n // Reprocessing.\n innerSpan = el.querySelector(\"span.textFitted\")\n // Remove vertical align if we\"re reprocessing.\n if (this.hasClass(innerSpan, \"textFitAlignVert\")) {\n innerSpan.className = innerSpan.className.replace(\"textFitAlignVert\", \"\")\n innerSpan.style[\"height\"] = \"\"\n el.className.replace(\"textFitAlignVertFlex\", \"\")\n }\n }\n\n // Prepare & set alignment\n if (settings.alignHoriz) {\n el.style[\"text-align\"] = \"center\"\n innerSpan.style[\"text-align\"] = \"center\"\n }\n\n // Check if this string is multiple lines\n // Not guaranteed to always work if you use wonky line-heights\n var multiLine = settings.multiLine\n if (settings.detectMultiLine && !multiLine &&\n innerSpan.getBoundingClientRect().height >= parseInt(window.getComputedStyle(innerSpan)[\"font-size\"], 10) * 2) {\n multiLine = true\n }\n\n // If we\"re not treating this as a multiline string, don\"t let it wrap.\n if (!multiLine) {\n el.style[\"white-space\"] = \"nowrap\"\n }\n\n low = settings.minFontSize\n high = settings.maxFontSize\n // Binary search for highest best fit\n var size = low\n while (low <= high) {\n mid = (high + low) >> 1\n innerSpan.style.fontSize = mid + \"px\"\n var innerSpanBoundingClientRect = innerSpan.getBoundingClientRect()\n if (\n innerSpanBoundingClientRect.width <= originalWidth\n && (settings.widthOnly || innerSpanBoundingClientRect.height <= originalHeight)\n ) {\n size = mid\n low = mid + 1\n } else {\n high = mid - 1\n }\n // await injection point\n }\n // found, updating font if differs:\n if (innerSpan.style.fontSize != size + \"px\") innerSpan.style.fontSize = size + \"px\"\n // Our height is finalized. If we are aligning vertically, set that up.\n if (settings.alignVert) {\n this.addStyleSheet()\n var height = innerSpan.scrollHeight\n if (window.getComputedStyle(el)[\"position\"] === \"static\") {\n el.style[\"position\"] = \"relative\"\n }\n if (!this.hasClass(innerSpan, \"textFitAlignVert\")) {\n innerSpan.className = innerSpan.className + \" textFitAlignVert\"\n }\n innerSpan.style[\"height\"] = height + \"px\"\n if (settings.alignVertWithFlexbox && !this.hasClass(el, \"textFitAlignVertFlex\")) {\n el.className = el.className + \" textFitAlignVertFlex\"\n }\n }\n }\n\n // Calculate height without padding.\n innerHeight(el) {\n var style = window.getComputedStyle(el, null)\n return el.getBoundingClientRect().height -\n parseInt(style.getPropertyValue(\"padding-top\"), 10) -\n parseInt(style.getPropertyValue(\"padding-bottom\"), 10)\n }\n\n // Calculate width without padding.\n innerWidth(el) {\n var style = window.getComputedStyle(el, null)\n return el.getBoundingClientRect().width -\n parseInt(style.getPropertyValue(\"padding-left\"), 10) -\n parseInt(style.getPropertyValue(\"padding-right\"), 10)\n }\n\n //Returns true if it is a DOM element\n isElement(o) {\n return (\n typeof HTMLElement === \"object\" ? o instanceof HTMLElement : //DOM2\n o && typeof o === \"object\" && o !== null && o.nodeType === 1 && typeof o.nodeName === \"string\"\n )\n }\n\n hasClass(element, cls) {\n return (\" \" + element.className + \" \").indexOf(\" \" + cls + \" \") > -1\n }\n\n // Better than a stylesheet dependency\n addStyleSheet() {\n if (document.getElementById(\"textFitStyleSheet\")) return\n var style = [\n \".textFitAlignVert{\",\n \"position: absolute;\",\n \"top: 0; right: 0; bottom: 0; left: 0;\",\n \"margin: auto;\",\n \"display: flex;\",\n \"justify-content: center;\",\n \"flex-direction: column;\",\n \"}\",\n \".textFitAlignVertFlex{\",\n \"display: flex;\",\n \"}\",\n \".textFitAlignVertFlex .textFitAlignVert{\",\n \"position: static;\",\n \"}\",].join(\"\")\n var css = document.createElement(\"style\")\n css.type = \"text/css\"\n css.id = \"textFitStyleSheet\"\n css.innerHTML = style\n document.body.appendChild(css)\n }\n\n dispatchTextFitEvent() {\n const event = new Event(\"text-fit\")\n this.element.dispatchEvent(event)\n }\n\n\n get defaultSettings() {\n return {\n alignVert: false, // if true, textFit will align vertically using css tables\n alignHoriz: false, // if true, textFit will set text-align: center\n multiLine: false, // if true, textFit will not set white-space: no-wrap\n detectMultiLine: true, // disable to turn off automatic multi-line sensing\n minFontSize: 6,\n maxFontSize: 80,\n reProcess: true, // if true, textFit will re-process already-fit nodes. Set to \"false\" for better performance\n widthOnly: false, // if true, textFit will fit text to element width, regardless of text height\n alignVertWithFlexbox: false, // if true, textFit will use flexbox for vertical alignment\n }\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n static values = {\n selector: String\n }\n\n show(event) {\n if (!this.targetElement) { return }\n\n this.targetElement.classList.add(\"element--on\")\n }\n\n dismiss(event) {\n if (!this.targetElement) { return }\n\n this.targetElement.classList.remove(\"element--on\")\n }\n\n toggle(event) {\n event.preventDefault()\n\n if (!this.targetElement) { return }\n\n if (this.targetElement.classList.contains(\"element--on\")) {\n this.dismiss(event)\n } else {\n this.show(event)\n }\n }\n\n get targetElement() {\n return document.querySelector(this.selectorValue)\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n static targets = [\"input\"]\n\n connect() {\n this.toggle()\n }\n\n toggle() {\n if (!this.inputTargets) { return }\n\n this.inputTargets.forEach(el => {\n const toggleTargets = this.toggleTargetsFor(el)\n if (el.checked) {\n toggleTargets.forEach(tgt => tgt.classList.add(\"element--on\"))\n } else {\n toggleTargets.forEach(tgt => tgt.classList.remove(\"element--on\"))\n }\n })\n }\n\n toggleTargetsFor(el) {\n return document.querySelectorAll(el.dataset.toggleTargets)\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n static values = {\n automatic: Boolean\n }\n\n renderer = {\n set: (target, property, value) => {\n target[property] = value\n\n if (property === \"closestEdge\") {\n if (this.state.closestEdge === \"top\") this.renderer.positionWhenCloseToTop()\n if (this.state.closestEdge === \"bottom\") this.renderer.positionWhenCloseToBottom()\n if (this.state.closestEdge === \"none\") this.renderer.positionWhenCloseToTop()\n }\n\n if (property === \"arrowPosition\") {\n if (this.state.arrowPosition === \"left\") this.renderer.alignToLeft()\n if (this.state.arrowPosition === \"center\") this.renderer.alignToCenter()\n if (this.state.arrowPosition === \"right\") this.renderer.alignToRight()\n }\n\n return true\n },\n\n positionWhenCloseToTop: () => {\n this.element.classList.replace(\"tooltip--arrow-bottom-left\", \"tooltip--arrow-top-left\")\n this.element.classList.replace(\"tooltip--arrow-bottom-center\", \"tooltip--arrow-top-center\")\n this.element.classList.replace(\"tooltip--arrow-bottom-right\", \"tooltip--arrow-top-right\")\n\n this.element.style.top = `${this.togglerElement.offsetHeight + this.arrowSize}px`\n },\n\n positionWhenCloseToBottom: () => {\n this.element.classList.replace(\"tooltip--arrow-top-left\", \"tooltip--arrow-bottom-left\")\n this.element.classList.replace(\"tooltip--arrow-top-center\", \"tooltip--arrow-bottom-center\")\n this.element.classList.replace(\"tooltip--arrow-top-right\", \"tooltip--arrow-bottom-right\")\n\n this.element.style.top = `-${this.elementHeight + this.arrowSize}px`\n },\n\n alignToLeft: () => {\n this.element.style.left = `${this.togglerElement.offsetLeft}px`\n },\n\n alignToCenter: () => {\n const elementWidth = this.element.offsetWidth\n const togglerWidth = this.togglerElement.offsetWidth\n const togglerLeft = this.togglerElement.offsetLeft\n var leftPosition\n if (togglerWidth > elementWidth) {\n leftPosition = togglerLeft + parseInt((togglerWidth - elementWidth) / 2)\n } else {\n leftPosition = togglerLeft - parseInt((elementWidth - togglerWidth) / 2)\n }\n this.element.style.left = `${leftPosition}px`\n },\n\n alignToRight: () => {\n const elementWidth = this.element.offsetWidth\n const togglerWidth = this.togglerElement.offsetWidth\n const togglerLeft = this.togglerElement.offsetLeft\n\n var leftPosition = togglerLeft + togglerWidth - elementWidth\n\n this.element.style.left = `${leftPosition}px`\n }\n }\n\n initialize() {\n this.state = new Proxy({}, this.renderer)\n }\n\n connect() {\n this.setup()\n }\n\n setup(event) {\n if (!this.togglerElement || !this.isAutomatic) return\n\n if (this.distanceToTop < this.elementHeightWithBuffer) {\n this.state.closestEdge = \"top\"\n } else if (this.distanceToBottom < this.elementHeightWithBuffer) {\n this.state.closestEdge = \"bottom\"\n } else {\n this.state.closestEdge = \"none\"\n }\n\n this.state.arrowPosition = this.arrowPosition\n }\n\n get togglerElement() {\n if (this.elementID == undefined) return undefined\n\n return document.querySelector(`[data-toggle-selector-value~=\"#${this.elementID}\"]`)\n }\n\n get elementID() {\n return this.element.id\n }\n\n get elementHeight() {\n return this.element.offsetHeight\n }\n\n get distanceToBottom() {\n return window.innerHeight - this.togglerElement.getBoundingClientRect().bottom\n }\n\n get distanceToTop() {\n return this.togglerElement.getBoundingClientRect().top\n }\n\n get arrowPosition() {\n return this.element.getAttribute(\"class\").match(\"arrow-.*-(left|center|right)\")[1]\n }\n\n get arrowSize() {\n return 15\n }\n\n get elementHeightWithBuffer() {\n return this.elementHeight + 20\n }\n\n get isAutomatic() {\n return this.automaticValue == true\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n\n connect() {\n this.prepare()\n }\n\n disconnect() {\n this.destroyWidget()\n }\n\n prepare() {\n this.loadWidget(this.scriptUrl)\n .then(() => { this.initWidget() })\n .catch((error) => { console.error(\"Trustpilot load failed:\", error) })\n }\n\n loadWidget(url) {\n return new Promise((resolve, reject) => {\n const script = document.createElement(\"script\")\n script.src = url\n script.async = true\n script.onload = resolve\n script.onerror = reject\n document.head.appendChild(script)\n })\n }\n\n initWidget() {\n if (global.Trustpilot && typeof global.Trustpilot.loadFromElement === \"function\") {\n global.Trustpilot.loadFromElement(this.element)\n } else {\n console.error(\"Trustpilot init failed\")\n }\n }\n\n destroyWidget() {\n this.element.innerHTML = \"\"\n }\n\n get scriptUrl() {\n return \"//widget.trustpilot.com/bootstrap/v5/tp.widget.bootstrap.min.js\"\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n visit() {\n Turbolinks.visit(this.data.get(\"location\"))\n }\n\n skip() {\n event.stopPropagation()\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n connect() {\n // We have to manually manage the event listener because setting\n // data-action=\"usetiful-tour:visited@window->usetiful-tour#markAsVisited\"\n // breaks the layout at the -> character.\n // TODO: Investigate why the data-action is not working in the layout.\n this.markAsVisitedBind = this.markAsVisited.bind(this)\n\n window.addEventListener(\"usetiful-tour:visited\", this.markAsVisitedBind)\n }\n\n disconnect() {\n window.removeEventListener(\"usetiful-tour:visited\", this.markAsVisitedBind)\n }\n\n markAsVisited(event) {\n const tourName = event.detail.name\n const onSuccess = event.detail.onSuccess\n const method = \"POST\"\n const params = new URLSearchParams()\n params.append(\"visited_tour[tour_name]\", tourName)\n\n Rails.ajax({\n type: method,\n dataType: \"json\",\n url: \"/admin/usetiful/visited_tours\",\n data: params.toString(),\n success: onSuccess\n })\n }\n}\n","import { Controller } from \"@hotwired/stimulus\"\n\nexport default class extends Controller {\n static targets = [\"form\"]\n\n validate(event) {\n if (!this.formTarget.checkValidity()) {\n event.stopImmediatePropagation()\n }\n }\n}\n","// nb. This is for IE10 and lower _only_.\nvar supportCustomEvent = window.CustomEvent;\n\nif (!supportCustomEvent || typeof supportCustomEvent === 'object') {\n supportCustomEvent = function CustomEvent(event, x) {\n x = x || {};\n var ev = document.createEvent('CustomEvent');\n ev.initCustomEvent(event, !!x.bubbles, !!x.cancelable, x.detail || null);\n return ev;\n };\n\n supportCustomEvent.prototype = window.Event.prototype;\n}\n/**\n * Dispatches the passed event to both an \"on\" handler as well as via the\n * normal dispatch operation. Does not bubble.\n *\n * @param {!EventTarget} target\n * @param {!Event} event\n * @return {boolean}\n */\n\n\nfunction safeDispatchEvent(target, event) {\n var check = 'on' + event.type.toLowerCase();\n\n if (typeof target[check] === 'function') {\n target[check](event);\n }\n\n return target.dispatchEvent(event);\n}\n/**\n * @param {Element} el to check for stacking context\n * @return {boolean} whether this el or its parents creates a stacking context\n */\n\n\nfunction createsStackingContext(el) {\n while (el && el !== document.body) {\n var s = window.getComputedStyle(el);\n\n var invalid = function (k, ok) {\n return !(s[k] === undefined || s[k] === ok);\n };\n\n if (s.opacity < 1 || invalid('zIndex', 'auto') || invalid('transform', 'none') || invalid('mixBlendMode', 'normal') || invalid('filter', 'none') || invalid('perspective', 'none') || s['isolation'] === 'isolate' || s.position === 'fixed' || s.webkitOverflowScrolling === 'touch') {\n return true;\n }\n\n el = el.parentElement;\n }\n\n return false;\n}\n/**\n * Finds the nearest