UNPKG

@pisell/pisellos

Version:

一个可扩展的前端模块化SDK框架,支持插件系统

747 lines (677 loc) 25.5 kB
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } import dayjs from "dayjs"; // 获取时间是否在日程范围内 export var getDateIsInSchedule = function getDateIsInSchedule(dateTime, scheduleList) { if (!dateTime || !scheduleList || scheduleList.length === 0) { return false; } var targetDate = dayjs(dateTime); if (!targetDate.isValid()) { return false; } // 遍历所有日程项 var _iterator = _createForOfIteratorHelper(scheduleList), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { var schedule = _step.value; if (isTimeInScheduleItem(dateTime, schedule)) { return true; } } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } return false; }; // 判断时间是否在单个日程项范围内 var isTimeInScheduleItem = function isTimeInScheduleItem(dateTime, schedule) { var targetDate = dayjs(dateTime); var targetDateString = dateTime.split(' ')[0]; // YYYY-MM-DD var targetTimeString = dateTime.split(' ')[1] || '00:00:00'; // HH:mm:ss switch (schedule.type) { case 'standard': return isInStandardSchedule(targetDate, targetDateString, targetTimeString, schedule); case 'time-slots': return isInTimeSlotsSchedule(targetDate, targetDateString, targetTimeString, schedule); case 'designation': return isInDesignationSchedule(targetDate, targetDateString, targetTimeString, schedule); default: return false; } }; // 处理标准类型日程 var isInStandardSchedule = function isInStandardSchedule(targetDate, targetDateString, targetTimeString, schedule) { if (!schedule.start_time || !schedule.end_time) { return false; } var startDate = dayjs(schedule.start_time); var endDate = dayjs(schedule.end_time); // 先检查是否在基础时间范围内(不考虑重复) var isInBasicRange = targetDate.isSameOrAfter(startDate) && targetDate.isSameOrBefore(endDate); if (schedule.repeat_type === 'none') { return isInBasicRange; } // 处理重复日程 return isInRepeatingSchedule(targetDate, targetDateString, targetTimeString, schedule, startDate, endDate); }; // 处理时间段类型日程 var isInTimeSlotsSchedule = function isInTimeSlotsSchedule(targetDate, targetDateString, targetTimeString, schedule) { if (!schedule.start_time) { return false; } var scheduleDate = schedule.start_time.split(' ')[0]; // 检查日期是否匹配 if (targetDateString !== scheduleDate) { return false; } // 检查是否在任一时间段内 var _iterator2 = _createForOfIteratorHelper(schedule.time_slot), _step2; try { for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { var timeSlot = _step2.value; var slotStart = "".concat(scheduleDate, " ").concat(timeSlot.start_time, ":00"); var slotEnd = "".concat(scheduleDate, " ").concat(timeSlot.end_time, ":00"); var slotStartDate = dayjs(slotStart); var slotEndDate = dayjs(slotEnd); if (targetDate.isSameOrAfter(slotStartDate) && targetDate.isSameOrBefore(slotEndDate)) { return true; } } } catch (err) { _iterator2.e(err); } finally { _iterator2.f(); } return false; }; // 处理指定类型日程 var isInDesignationSchedule = function isInDesignationSchedule(targetDate, targetDateString, targetTimeString, schedule) { if (!schedule.designation) { return false; } // @ts-ignore var _iterator3 = _createForOfIteratorHelper(schedule.designation), _step3; try { for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) { var designation = _step3.value; var startDateTime = "".concat(designation.start_date, " ").concat(designation.start_time, ":00"); var endDateTime = "".concat(designation.end_date, " ").concat(designation.end_time, ":00"); var startDate = dayjs(startDateTime); var endDate = dayjs(endDateTime); if (targetDate.isSameOrAfter(startDate) && targetDate.isSameOrBefore(endDate)) { return true; } } } catch (err) { _iterator3.e(err); } finally { _iterator3.f(); } return false; }; // 处理重复日程逻辑 var isInRepeatingSchedule = function isInRepeatingSchedule(targetDate, targetDateString, targetTimeString, schedule, startDate, endDate) { if (!schedule.repeat_rule) { return false; } var repeat_rule = schedule.repeat_rule, repeat_type = schedule.repeat_type; var targetDateOnly = dayjs(targetDateString); // 只取日期部分 // 首先检查是否在包含日期中(包含日期是额外增加的有效日期) if (repeat_rule.included_date && repeat_rule.included_date.length > 0) { var _iterator4 = _createForOfIteratorHelper(repeat_rule.included_date), _step4; try { for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) { var includedDate = _step4.value; var includeStartDate = dayjs(includedDate.start); var includeEndDate = dayjs(includedDate.end); if (targetDateOnly.isSameOrAfter(includeStartDate, 'day') && targetDateOnly.isSameOrBefore(includeEndDate, 'day')) { // 在包含日期中,直接返回true,无需检查其他条件 return true; } } } catch (err) { _iterator4.e(err); } finally { _iterator4.f(); } } // 检查是否在排除日期中 var _iterator5 = _createForOfIteratorHelper(repeat_rule.excluded_date), _step5; try { for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) { var excludedDate = _step5.value; var excludeStartDate = dayjs(excludedDate.start); var excludeEndDate = dayjs(excludedDate.end); // 检查目标日期是否在排除范围内(包含边界) if (targetDateOnly.isSameOrAfter(excludeStartDate, 'day') && targetDateOnly.isSameOrBefore(excludeEndDate, 'day')) { return false; } } // 检查重复规则的结束条件 } catch (err) { _iterator5.e(err); } finally { _iterator5.f(); } if (repeat_rule.end.type === 'date' && repeat_rule.end.end_date) { var ruleEndDate = dayjs(repeat_rule.end.end_date); if (targetDate.isAfter(ruleEndDate)) { return false; } } // 根据重复类型检查 switch (repeat_type) { case 'daily': return isInDailyRepeat(targetDate, startDate, endDate, repeat_rule); case 'weekly': return isInWeeklyRepeat(targetDate, startDate, endDate, repeat_rule); default: return false; // 不支持月和年重复 } }; // 每日重复逻辑 var isInDailyRepeat = function isInDailyRepeat(targetDate, startDate, endDate, repeatRule) { var daysDiff = targetDate.diff(startDate, 'day'); if (daysDiff < 0) { return false; } // 检查频率 if (daysDiff % repeatRule.frequency !== 0) { return false; } // 检查时间是否在日程的时间范围内 var targetTimeOfDay = targetDate.hour() * 3600 + targetDate.minute() * 60 + targetDate.second(); var startTimeOfDay = startDate.hour() * 3600 + startDate.minute() * 60 + startDate.second(); var endTimeOfDay = endDate.hour() * 3600 + endDate.minute() * 60 + endDate.second(); // 每日重复:每天都在相同的时间段内有效 // 例如:01:00:00 - 16:00:00,每天都是这个时间段 return targetTimeOfDay >= startTimeOfDay && targetTimeOfDay <= endTimeOfDay; }; // 每周重复逻辑 var isInWeeklyRepeat = function isInWeeklyRepeat(targetDate, startDate, endDate, repeatRule) { var targetDayOfWeek = targetDate.day(); // 0=周日, 1=周一, ..., 6=周六 // 检查是否在指定的星期几内 if (!repeatRule.frequency_date.includes(targetDayOfWeek)) { return false; } // 计算周差 var weeksDiff = targetDate.diff(startDate, 'week'); if (weeksDiff < 0) { return false; } // 检查频率 if (weeksDiff % repeatRule.frequency !== 0) { return false; } // 检查时间是否在日程的时间范围内 var targetTimeOfDay = targetDate.hour() * 3600 + targetDate.minute() * 60 + targetDate.second(); var startTimeOfDay = startDate.hour() * 3600 + startDate.minute() * 60 + startDate.second(); var endTimeOfDay = endDate.hour() * 3600 + endDate.minute() * 60 + endDate.second(); return targetTimeOfDay >= startTimeOfDay && targetTimeOfDay <= endTimeOfDay; }; // 日程时间段类型 /** * 获取当前日期日程时间点 * @param date 日期 YYYY-MM-DD * @param scheduleList 日程列表 * @returns 当前日期日程时间点 结构,数组 [{ * * // HH:mm * end_time: string, * // HH:mm * start_time: string, * // 日期时间 * // YYYY-MM-DD HH:mm:ss * end_at: Dayjs, * // YYYY-MM-DD HH:mm:ss * start_at: Dayjs, * * }] */ export var getScheduleStartEndTimePoints = function getScheduleStartEndTimePoints(date, scheduleList) { if (!date || !scheduleList || scheduleList.length === 0) { return []; } var targetDate = dayjs(date); if (!targetDate.isValid()) { return []; } var timeSlots = []; // 遍历所有日程 var _iterator6 = _createForOfIteratorHelper(scheduleList), _step6; try { for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) { var schedule = _step6.value; var slots = extractTimeSlotsFromSchedule(date, schedule); timeSlots.push.apply(timeSlots, slots); } // 去重并排序时间段 } catch (err) { _iterator6.e(err); } finally { _iterator6.f(); } return mergeAndSortTimeSlots(timeSlots); }; /** * 从单个日程中提取指定日期的时间段 */ var extractTimeSlotsFromSchedule = function extractTimeSlotsFromSchedule(date, schedule) { switch (schedule.type) { case 'standard': return extractStandardScheduleTimeSlots(date, schedule); case 'time-slots': return extractTimeSlotsScheduleTimeSlots(date, schedule); case 'designation': return extractDesignationScheduleTimeSlots(date, schedule); default: return []; } }; /** * 提取标准类型日程的时间段 */ var extractStandardScheduleTimeSlots = function extractStandardScheduleTimeSlots(date, schedule) { if (!schedule.start_time || !schedule.end_time) { return []; } var targetDate = dayjs(date); var startDate = dayjs(schedule.start_time); var endDate = dayjs(schedule.end_time); // 检查日期是否在日程范围内 if (!isDateInStandardSchedule(targetDate, schedule, startDate, endDate)) { return []; } // 提取时间部分 var startTime = startDate.format('HH:mm'); var endTime = endDate.format('HH:mm'); // 构造完整的日期时间 var startAt = dayjs("".concat(date, " ").concat(startTime, ":00")); var endAt = dayjs("".concat(date, " ").concat(endTime, ":00")); return [{ start_time: startTime, end_time: endTime, start_at: startAt, end_at: endAt }]; }; /** * 提取时间段类型日程的时间段 */ var extractTimeSlotsScheduleTimeSlots = function extractTimeSlotsScheduleTimeSlots(date, schedule) { if (!schedule.start_time || !schedule.time_slot || schedule.time_slot.length === 0) { return []; } var targetDate = dayjs(date); var scheduleStartDate = dayjs(schedule.start_time.split(' ')[0]); // 检查日期是否在日程的有效范围内(考虑重复规则) if (!isDateInTimeSlotsSchedule(targetDate, schedule, scheduleStartDate)) { return []; } var slots = []; // 提取所有时间段 var _iterator7 = _createForOfIteratorHelper(schedule.time_slot), _step7; try { for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) { var timeSlot = _step7.value; if (!timeSlot.start_time || !timeSlot.end_time) { continue; } var startAt = dayjs("".concat(date, " ").concat(timeSlot.start_time, ":00")); var endAt = dayjs("".concat(date, " ").concat(timeSlot.end_time, ":00")); slots.push({ start_time: timeSlot.start_time, end_time: timeSlot.end_time, start_at: startAt, end_at: endAt }); } } catch (err) { _iterator7.e(err); } finally { _iterator7.f(); } return slots; }; /** * 检查日期是否在时间段类型日程范围内 */ var isDateInTimeSlotsSchedule = function isDateInTimeSlotsSchedule(targetDate, schedule, scheduleStartDate) { // 不重复的日程 if (schedule.repeat_type === 'none') { // 只在 start_time 的日期有效 return targetDate.isSame(scheduleStartDate, 'day'); } // 重复的日程,需要检查重复规则 if (!schedule.repeat_rule) { return false; } var repeat_rule = schedule.repeat_rule, repeat_type = schedule.repeat_type; // 检查是否在包含日期中(优先级最高) if (repeat_rule.included_date && repeat_rule.included_date.length > 0) { var _iterator8 = _createForOfIteratorHelper(repeat_rule.included_date), _step8; try { for (_iterator8.s(); !(_step8 = _iterator8.n()).done;) { var includedDate = _step8.value; var includeStartDate = dayjs(includedDate.start); var includeEndDate = dayjs(includedDate.end); if (targetDate.isSameOrAfter(includeStartDate, 'day') && targetDate.isSameOrBefore(includeEndDate, 'day')) { return true; } } } catch (err) { _iterator8.e(err); } finally { _iterator8.f(); } } // 检查是否在排除日期中 if (repeat_rule.excluded_date && repeat_rule.excluded_date.length > 0) { var _iterator9 = _createForOfIteratorHelper(repeat_rule.excluded_date), _step9; try { for (_iterator9.s(); !(_step9 = _iterator9.n()).done;) { var excludedDate = _step9.value; var excludeStartDate = dayjs(excludedDate.start); var excludeEndDate = dayjs(excludedDate.end); if (targetDate.isSameOrAfter(excludeStartDate, 'day') && targetDate.isSameOrBefore(excludeEndDate, 'day')) { return false; } } } catch (err) { _iterator9.e(err); } finally { _iterator9.f(); } } // 检查重复规则的结束条件 if (repeat_rule.end.type === 'date' && repeat_rule.end.end_date) { var ruleEndDate = dayjs(repeat_rule.end.end_date); if (targetDate.isAfter(ruleEndDate, 'day')) { return false; } } // 检查是否在开始日期之前 if (targetDate.isBefore(scheduleStartDate, 'day')) { return false; } // 根据重复类型检查 switch (repeat_type) { case 'daily': return isDateInDailyRepeat(targetDate, scheduleStartDate, repeat_rule); case 'weekly': return isDateInWeeklyRepeat(targetDate, scheduleStartDate, repeat_rule); default: return false; } }; /** * 提取指定日期类型日程的时间段 */ var extractDesignationScheduleTimeSlots = function extractDesignationScheduleTimeSlots(date, schedule) { if (!schedule.designation) { return []; } var targetDate = dayjs(date); var designations = Array.isArray(schedule.designation) ? schedule.designation : [schedule.designation]; var slots = []; var _iterator10 = _createForOfIteratorHelper(designations), _step10; try { for (_iterator10.s(); !(_step10 = _iterator10.n()).done;) { var designation = _step10.value; var startDate = dayjs(designation.start_date); var endDate = dayjs(designation.end_date); // 检查日期是否在指定范围内(考虑重复规则) if (isDateInDesignationSchedule(targetDate, schedule, designation, startDate, endDate)) { var startAt = dayjs("".concat(date, " ").concat(designation.start_time, ":00")); var endAt = dayjs("".concat(date, " ").concat(designation.end_time, ":00")); slots.push({ start_time: designation.start_time, end_time: designation.end_time, start_at: startAt, end_at: endAt }); } } } catch (err) { _iterator10.e(err); } finally { _iterator10.f(); } return slots; }; /** * 检查日期是否在指定日期类型日程范围内 */ var isDateInDesignationSchedule = function isDateInDesignationSchedule(targetDate, schedule, designation, startDate, endDate) { // 不重复的日程 if (schedule.repeat_type === 'none') { // 检查日期是否在指定的日期范围内 return targetDate.isSameOrAfter(startDate, 'day') && targetDate.isSameOrBefore(endDate, 'day'); } // 重复的日程,需要检查重复规则 if (!schedule.repeat_rule) { return false; } var repeat_rule = schedule.repeat_rule, repeat_type = schedule.repeat_type; // 检查是否在包含日期中(优先级最高) if (repeat_rule.included_date && repeat_rule.included_date.length > 0) { var _iterator11 = _createForOfIteratorHelper(repeat_rule.included_date), _step11; try { for (_iterator11.s(); !(_step11 = _iterator11.n()).done;) { var includedDate = _step11.value; var includeStartDate = dayjs(includedDate.start); var includeEndDate = dayjs(includedDate.end); if (targetDate.isSameOrAfter(includeStartDate, 'day') && targetDate.isSameOrBefore(includeEndDate, 'day')) { return true; } } } catch (err) { _iterator11.e(err); } finally { _iterator11.f(); } } // 检查是否在排除日期中 if (repeat_rule.excluded_date && repeat_rule.excluded_date.length > 0) { var _iterator12 = _createForOfIteratorHelper(repeat_rule.excluded_date), _step12; try { for (_iterator12.s(); !(_step12 = _iterator12.n()).done;) { var excludedDate = _step12.value; var excludeStartDate = dayjs(excludedDate.start); var excludeEndDate = dayjs(excludedDate.end); if (targetDate.isSameOrAfter(excludeStartDate, 'day') && targetDate.isSameOrBefore(excludeEndDate, 'day')) { return false; } } } catch (err) { _iterator12.e(err); } finally { _iterator12.f(); } } // 检查重复规则的结束条件 if (repeat_rule.end.type === 'date' && repeat_rule.end.end_date) { var ruleEndDate = dayjs(repeat_rule.end.end_date); if (targetDate.isAfter(ruleEndDate, 'day')) { return false; } } // 检查是否在基本日期范围内 if (!targetDate.isSameOrAfter(startDate, 'day') || !targetDate.isSameOrBefore(endDate, 'day')) { // 对于重复日程,如果不在基本范围内,还需要检查是否符合重复规则 // 只要在 startDate 之后,并且符合重复规则即可 if (targetDate.isBefore(startDate, 'day')) { return false; } } // 根据重复类型检查 switch (repeat_type) { case 'daily': return isDateInDailyRepeat(targetDate, startDate, repeat_rule); case 'weekly': return isDateInWeeklyRepeat(targetDate, startDate, repeat_rule); default: return false; } }; /** * 检查日期是否在标准日程范围内 */ var isDateInStandardSchedule = function isDateInStandardSchedule(targetDate, schedule, startDate, endDate) { var targetDateString = targetDate.format('YYYY-MM-DD'); // 不重复的日程 if (schedule.repeat_type === 'none') { // 检查日期是否在范围内 var scheduleStartDate = startDate.format('YYYY-MM-DD'); var scheduleEndDate = endDate.format('YYYY-MM-DD'); return targetDate.isSameOrAfter(scheduleStartDate, 'day') && targetDate.isSameOrBefore(scheduleEndDate, 'day'); } // 重复的日程,需要检查重复规则 if (!schedule.repeat_rule) { return false; } var repeat_rule = schedule.repeat_rule, repeat_type = schedule.repeat_type; // 检查是否在包含日期中 if (repeat_rule.included_date && repeat_rule.included_date.length > 0) { var _iterator13 = _createForOfIteratorHelper(repeat_rule.included_date), _step13; try { for (_iterator13.s(); !(_step13 = _iterator13.n()).done;) { var includedDate = _step13.value; var includeStartDate = dayjs(includedDate.start); var includeEndDate = dayjs(includedDate.end); if (targetDate.isSameOrAfter(includeStartDate, 'day') && targetDate.isSameOrBefore(includeEndDate, 'day')) { return true; } } } catch (err) { _iterator13.e(err); } finally { _iterator13.f(); } } // 检查是否在排除日期中 var _iterator14 = _createForOfIteratorHelper(repeat_rule.excluded_date), _step14; try { for (_iterator14.s(); !(_step14 = _iterator14.n()).done;) { var excludedDate = _step14.value; var excludeStartDate = dayjs(excludedDate.start); var excludeEndDate = dayjs(excludedDate.end); if (targetDate.isSameOrAfter(excludeStartDate, 'day') && targetDate.isSameOrBefore(excludeEndDate, 'day')) { return false; } } // 检查重复规则的结束条件 } catch (err) { _iterator14.e(err); } finally { _iterator14.f(); } if (repeat_rule.end.type === 'date' && repeat_rule.end.end_date) { var ruleEndDate = dayjs(repeat_rule.end.end_date); if (targetDate.isAfter(ruleEndDate, 'day')) { return false; } } // 检查是否在开始日期之前 if (targetDate.isBefore(startDate, 'day')) { return false; } // 根据重复类型检查 switch (repeat_type) { case 'daily': return isDateInDailyRepeat(targetDate, startDate, repeat_rule); case 'weekly': return isDateInWeeklyRepeat(targetDate, startDate, repeat_rule); default: return false; } }; /** * 检查日期是否符合每日重复规则 */ var isDateInDailyRepeat = function isDateInDailyRepeat(targetDate, startDate, repeatRule) { var daysDiff = targetDate.diff(startDate, 'day'); if (daysDiff < 0) { return false; } // 检查频率 return daysDiff % repeatRule.frequency === 0; }; /** * 检查日期是否符合每周重复规则 */ var isDateInWeeklyRepeat = function isDateInWeeklyRepeat(targetDate, startDate, repeatRule) { var targetDayOfWeek = targetDate.day(); // 0=周日, 1=周一, ..., 6=周六 // 检查是否在指定的星期几内 if (!repeatRule.frequency_date.includes(targetDayOfWeek)) { return false; } // 计算周差 var weeksDiff = targetDate.diff(startDate, 'week'); if (weeksDiff < 0) { return false; } // 检查频率 return weeksDiff % repeatRule.frequency === 0; }; /** * 对时间段去重并排序 * 只移除开始时间和结束时间完全相同的重复时间段 */ var mergeAndSortTimeSlots = function mergeAndSortTimeSlots(slots) { if (slots.length === 0) { return []; } // 按开始时间排序 var sorted = _toConsumableArray(slots).sort(function (a, b) { return a.start_at.valueOf() - b.start_at.valueOf(); }); // 去重:移除开始和结束时间完全相同的时间段 var unique = []; var seenKeys = new Set(); var _iterator15 = _createForOfIteratorHelper(sorted), _step15; try { for (_iterator15.s(); !(_step15 = _iterator15.n()).done;) { var slot = _step15.value; // 使用开始时间和结束时间作为唯一键 var key = "".concat(slot.start_time, "-").concat(slot.end_time); if (!seenKeys.has(key)) { seenKeys.add(key); unique.push(slot); } } } catch (err) { _iterator15.e(err); } finally { _iterator15.f(); } return unique; };