ontentContainer, HeaderText, ContentText,\n} from \"./styled\"\n\ninterface NotificationProps {\n  header?: string\n  leftContent?: React.ReactNode\n  rightContent?: React.ReactNode\n  text?: React.ReactNode\n  className?: string\n  renderContent?: (props: NotificationProps) => React.ReactNode | React.ReactNodeArray | null\n  success?: boolean\n  error?: boolean\n}\n\nexport const UINotification = (props: NotificationProps) => {\n  const {\n    header, text, leftContent, rightContent, renderContent, success, error,\n  } = props\n  return (\n    <Container>\n      {leftContent && <SideContent>{leftContent}</SideContent>}\n      <ContentContainer>\n        {header && (\n          <HeaderText success={success} error={error}>\n            {header}\n          </HeaderText>\n        )}\n        {text && (\n          <ContentText success={success} error={error}>\n            {text}\n          </ContentText>\n        )}\n        {renderContent && renderContent(props)}\n      </ContentContainer>\n      {rightContent && <SideContent right>{rightContent}</SideContent>}\n    </Container>\n  )\n}\n\n// for usage in non-jsx contexts\n// eslint-disable-next-line react/jsx-props-no-spreading\nexport const createUINotification = (props: NotificationProps) => <UINotification {...props} />\n","import styled from \"styled-components\"\nimport { getSizeBy } from \"@netdata/netdata-ui\"\n\nexport const NodeIconContainer = styled.div`\n  width: ${getSizeBy(5)};\n  height: ${getSizeBy(5)};\n  margin-right: ${getSizeBy(2)};\n  display: flex;\n  justify-content: center;\n  align-items: center;\n`\n\nexport const NotificationLink = styled.a`\n  &,\n  &:hover {\n    text-decoration: underline;\n    color: inherit;\n  }\n`\n","import React from \"react\"\nimport { Icon } from \"@netdata/netdata-ui\"\nimport { toast } from \"react-toastify\"\n\nimport { createUINotification } from \"components/ui-notification\"\n\nimport * as S from \"./styled\"\n\nexport const toastOptions = {\n  position: toast.POSITION.BOTTOM_RIGHT,\n  autoClose: 10000,\n  pauseOnFocusLoss: false,\n}\n\nexport const showCloudInstallationProblemNotification = () => {\n  const uiNotification = {\n    header: \"Installation error\",\n    text: \"The installer could not prepare the required dependencies to enable Netdata Cloud\"\n      + \" functionality\",\n  }\n  const notificationComponent = createUINotification({\n    ...uiNotification,\n    error: true,\n    leftContent: (\n      <S.NodeIconContainer>\n        <Icon name=\"gear\" size=\"large\" color=\"error\" />\n      </S.NodeIconContainer>\n    ),\n  })\n  toast.error(notificationComponent, toastOptions)\n}\n\nexport const showCloudConnectionProblemNotification = () => {\n  const uiNotification = {\n    header: \"Connection Problem\",\n    text: (\n      <S.NotificationLink\n        href=\"https://learn.netdata.cloud/docs/agent/packaging/installer#automatic-one-line-installation-script\"\n        target=\"_blank\"\n      >\n        To access Cloud install again your agent via the kickstart script\n      </S.NotificationLink>\n    ),\n  }\n  const notificationComponent = createUINotification({\n    ...uiNotification,\n    error: true,\n    leftContent: (\n      <S.NodeIconContainer>\n        <Icon name=\"gear\" size=\"large\" color=\"error\" />\n      </S.NodeIconContainer>\n    ),\n  })\n  toast.error(notificationComponent, toastOptions)\n}\n","import {\n  mergeAll, pipe, split, mergeRight,\n} from \"ramda\"\nimport { mapIndexed } from \"ramda-adjunct\"\n\nconst defaultUrlOptions = {\n  hash: \"#\",\n  theme: null,\n  help: null,\n  mode: \"live\", // 'live', 'print'\n  update_always: false,\n  pan_and_zoom: false,\n  server: null,\n  after: 0,\n  before: 0,\n  highlight: false,\n  highlight_after: 0,\n  highlight_before: 0,\n  nowelcome: false,\n  show_alarms: false,\n  chart: null,\n  family: null,\n  alarm: null,\n  alarm_unique_id: 0,\n  alarm_id: 0,\n  alarm_event_id: 0,\n  alarm_when: 0,\n} as {[key: string]: unknown}\n\nconst isInvalidPair = ([key, value]: [string, string]) => (\n  defaultUrlOptions[key] === undefined || value === undefined\n)\n\nconst parseQueryPair = ([key, value]: [string, string]): {[key: string] : unknown} => {\n  if (isInvalidPair([key, value])) {\n    return {}\n  }\n  return {\n    [key]: decodeURIComponent(value),\n  }\n}\n\nexport const parseUrl = pipe(\n  split(\";\"),\n  mapIndexed((value, index) => (\n    (index === 0) ? { hash: value } : parseQueryPair((value.split(\"=\") as [string, string]))\n  )),\n  mergeAll,\n  mergeRight(defaultUrlOptions),\n)\n\nconst urlParsed = parseUrl(document.location.hash)\n\nexport const isPrintMode = urlParsed.mode === \"print\"\n","import {\n  call,\n  put,\n  takeEvery,\n  select,\n  spawn,\n  take,\n  delay,\n} from \"redux-saga/effects\"\nimport { channel } from \"redux-saga\"\nimport { Action } from \"redux-act\"\n\nimport { axiosInstance } from \"utils/api\"\nimport { alwaysEndWithSlash, serverDefault } from \"utils/server-detection\"\nimport { getFetchStream } from \"utils/netdata-sdk\"\nimport { isMainJs } from \"utils/env\"\nimport { fillMissingData, transformResults } from \"utils/fill-missing-data\"\nimport {\n  showCloudInstallationProblemNotification, showCloudConnectionProblemNotification,\n} from \"components/notifications\"\nimport { selectGlobalPanAndZoom, selectSnapshot, selectRegistry } from \"domains/global/selectors\"\nimport { StateT as GlobalStateT } from \"domains/global/reducer\"\nimport { stopSnapshotModeAction } from \"domains/dashboard/actions\"\nimport { isPrintMode } from \"domains/dashboard/utils/parse-url\"\nimport { INFO_POLLING_FREQUENCY } from \"domains/global/constants\"\n\nimport {\n  fetchDataAction,\n  FetchDataPayload,\n  fetchChartAction,\n  FetchChartPayload,\n  fetchDataForSnapshotAction,\n  FetchDataForSnapshotPayload,\n  fetchInfoAction,\n  FetchInfoPayload,\n  fetchDataCancelAction,\n} from \"./actions\"\nimport { ChartData } from \"./chart-types\"\n\nconst CONCURRENT_CALLS_LIMIT_METRICS = isMainJs ? 30 : 60\nconst CONCURRENT_CALLS_LIMIT_PRINT = 2\nconst CONCURRENT_CALLS_LIMIT_SNAPSHOTS = 1\n\nconst fetchDataResponseChannel = channel()\n\nexport function* watchFetchDataResponseChannel() {\n  while (true) {\n    const action = (yield take(fetchDataResponseChannel))\n\n    // special case - if requested relative timeRange, and during request the mode has been changed\n    // to absolute global-pan-and-zoom, cancel the store update\n    // todo do xss check of data\n    if (action.type === fetchDataAction.success.toString()) {\n      const payload = (action.payload as FetchDataPayload)\n      const { viewRange } = payload.fetchDataParams\n      const [start, end] = viewRange\n      const globalPanAndZoom = (yield select(\n        selectGlobalPanAndZoom,\n      )) as GlobalStateT[\"globalPanAndZoom\"]\n\n      if (globalPanAndZoom\n        && (start <= 0 || end <= 0) // check if they are not timestamps\n      ) {\n        yield put(fetchDataCancelAction({\n          id: payload.id,\n        }))\n        // eslint-disable-next-line no-continue\n        continue\n      }\n    }\n\n    yield put(action)\n  }\n}\n\n// todo construct a new version of key that will be safer to be used in future\n// (while keeping old key supported for some time)\n// perhaps the key could be passed as attribute to the chart, to avoid matching\nconst constructCompatibleKey = (dimensions: undefined | string, options: string) => (\n  // strange transformations for backwards compatibility. old snapshot keys were encoded this way\n  // that empty dimensions were actually \"null\" string\n  `${dimensions === undefined\n    ? \"null\"\n    : encodeURIComponent(dimensions)\n  },${encodeURIComponent(options)}`\n)\n\n// currently BE always transforms data as if `flip` was there\nconst IS_FLIP_RESPECTED_IN_COMPOSITE_CHARTS = false\n\nconst getGroupByValues = (groupBy) => {\n  if (groupBy === \"chart\") return \"node\"\n  if (groupBy === \"node\" || groupBy === \"dimension\") return groupBy\n  return `label=${groupBy}`\n}\n\nconst [fetchMetrics$] = getFetchStream(\n  isPrintMode ? CONCURRENT_CALLS_LIMIT_PRINT : CONCURRENT_CALLS_LIMIT_METRICS,\n)\nfunction* fetchDataSaga({ payload }: Action<FetchDataPayload>) {\n  const {\n    // props for api\n    host, context, chart, format, points, group, gtime, options,\n    after, before, dimensions, labels, postGroupBy, postAggregationMethod,\n    aggrMethod, dimensionsAggrMethod, nodeIDs, httpMethod,\n    groupBy = \"dimension\", // group by node, dimension, or label keys\n    aggrGroups = [],\n    // props for the store\n    fetchDataParams, id, cancelTokenSource,\n  } = payload\n\n  const snapshot = yield select(selectSnapshot)\n  if (snapshot) {\n    // if reading snapshot\n    const dimensionsWithUrlOptions = constructCompatibleKey(dimensions, options)\n    const matchingKey = Object.keys(snapshot.data).find((snapshotKey) => (\n      snapshotKey.startsWith(chart) && snapshotKey.includes(dimensionsWithUrlOptions)\n    ))\n    if (!matchingKey) {\n      // eslint-disable-next-line no-console\n      console.warn(`Could not find snapshot key for chart: ${chart} and id ${id}`)\n      return\n    }\n    const data = snapshot.data[matchingKey]\n    yield put(fetchDataAction.success({\n      chartData: data,\n      fetchDataParams,\n      id,\n    }))\n    return\n  }\n\n  const url = isMainJs\n    ? `${alwaysEndWithSlash(host)}api/v1/data`\n    : host\n\n  const agentOptionsOriginal = options.split(\"|\")\n  const hasFlip = agentOptionsOriginal.includes(\"flip\")\n  const shouldAddFakeFlip = !IS_FLIP_RESPECTED_IN_COMPOSITE_CHARTS && !hasFlip\n  // if flip is not respected in composite-charts, send it always (like dygraph charts normally do)\n  const agentOptions = shouldAddFakeFlip\n    ? agentOptionsOriginal.concat(\"flip\") : agentOptionsOriginal\n\n  const groupValues = [\n    getGroupByValues(groupBy),\n    postGroupBy && `label=${postGroupBy}`,\n  ].filter(Boolean)\n\n  const axiosOptions = httpMethod === \"POST\" ? {\n    // used by cloud's room-overview\n    data: {\n      filter: {\n        nodeIDs,\n        context,\n        dimensions: dimensions ? dimensions.split(/['|]/) : undefined,\n        labels,\n      },\n      after,\n      before,\n      points,\n      group,\n      gtime,\n      agent_options: agentOptions,\n      ...(postAggregationMethod && { post_aggregation_methods: [postAggregationMethod] }),\n      aggregations: [groupBy !== \"dimension\" && {\n        method: dimensionsAggrMethod || \"sum\",\n        groupBy: [\"chart\", ...groupValues],\n      },\n      groupBy !== \"chart\" && {\n        method: aggrMethod,\n        groupBy: groupValues,\n        ...(aggrGroups.length && { labels: aggrGroups }),\n      }].filter(Boolean),\n    },\n  } : {\n    params: {\n      chart,\n      _: new Date().valueOf(),\n      format,\n      points,\n      group,\n      gtime,\n      options,\n      after,\n      before,\n      dimensions,\n    },\n  }\n\n  const onSuccessCallback = (data: { [id: string]: unknown}) => {\n    if (!data?.result) {\n      fetchDataResponseChannel.put(fetchDataAction.failure({ id }))\n    } else {\n      const { fillMissingPoints } = fetchDataParams\n\n      const transformedResults = transformResults(\n        (data as unknown) as ChartData,\n        format,\n        shouldAddFakeFlip,\n      )\n\n      const chartData = {\n        ...transformedResults,\n        // @ts-ignore\n        ...((\"post_aggregated_data\" in data.result) && {\n          postAggregationMethod,\n          groupBy,\n          postGroupBy,\n          aggrGroups,\n          // @ts-ignore\n          postAggregated: data.result.post_aggregated_data[postAggregationMethod],\n        }),\n      }\n\n      fetchDataResponseChannel.put(fetchDataAction.success({\n        chartData: fillMissingPoints\n          ? fillMissingData(chartData as ChartData, fillMissingPoints)\n          : chartData,\n        fetchDataParams,\n        id,\n      }))\n    }\n  }\n\n  const onErrorCallback = (error: Error) => {\n    console.warn(\"fetch chart data failure\", error) // eslint-disable-line no-console\n    fetchDataResponseChannel.put(fetchDataAction.failure({ id }))\n  }\n\n  fetchMetrics$.next({\n    ...axiosOptions,\n    method: httpMethod || \"GET\",\n    url,\n    onErrorCallback,\n    onSuccessCallback,\n    cancelTokenSource,\n  })\n}\n\nconst [fetchForSnapshot$, resetFetchForSnapshot$] = getFetchStream(CONCURRENT_CALLS_LIMIT_SNAPSHOTS)\nfunction fetchDataForSnapshotSaga({ payload }: Action<FetchDataForSnapshotPayload>) {\n  const {\n    host, chart, format, points, group, gtime, options,\n    after, before, dimensions, aggrMethod,\n    groupBy,\n    nodeIDs,\n    chartLibrary, id,\n  } = payload\n\n  // backwards-compatibility, the keys look like this:\n  // net_errors.stf0,dygraph,null,ms%7Cflip%7Cjsonwrap%7Cnonzero\n  const chartDataUniqueID = `${chart},${chartLibrary},${constructCompatibleKey(\n    dimensions,\n    options,\n  )}`\n\n  const url = `${alwaysEndWithSlash(host)}api/v1/data`\n  const params = {\n    chart,\n    _: new Date().valueOf(),\n    format,\n    points,\n    group,\n    gtime,\n    options,\n    after,\n    before,\n    dimensions,\n    ...(aggrMethod && { aggr_method: aggrMethod }),\n    ...(nodeIDs && { node_ids: nodeIDs.join(\",\") }),\n    ...(groupBy && { groupBy }),\n  }\n\n  const onSuccessCallback = (data: unknown) => {\n    fetchDataResponseChannel.put(fetchDataForSnapshotAction.success({\n      snapshotData: data,\n      id,\n    }))\n    // temporarily, until main.js finished rewrite\n    // @ts-ignore\n    window.chartUpdated({\n      chartDataUniqueID,\n      data,\n    })\n  }\n\n  const onErrorCallback = () => {\n    fetchDataResponseChannel.put(fetchDataForSnapshotAction.failure({ id }))\n    // @ts-ignore\n    window.chartUpdated({\n      chartDataUniqueID,\n      chart,\n      data: null,\n    })\n  }\n\n  fetchForSnapshot$.next({\n    url,\n    params,\n    onErrorCallback,\n    onSuccessCallback,\n  })\n}\n\nfunction stopSnapshotModeSaga() {\n  // any calls in the queue should stop when save-snapshot modal is closed\n  resetFetchForSnapshot$.next()\n}\n\nfunction* fetchChartSaga({ payload }: Action<FetchChartPayload>) {\n  const { chart, id, host } = payload\n\n  const snapshot = yield select(selectSnapshot)\n  if (snapshot) {\n    yield put(fetchChartAction.success({\n      chartMetadata: snapshot.charts.charts[chart],\n      id,\n    }))\n    return\n  }\n\n  let response\n  const url = isMainJs\n    ? `${alwaysEndWithSlash(host)}api/v1/chart`\n    : host.replace(\"/data\", \"/chart\")\n  try {\n    response = yield call(axiosInstance.get, url, {\n      params: {\n        chart,\n      },\n    })\n  } catch (e) {\n    console.warn(\"fetch chart details failure\") // eslint-disable-line no-console\n    yield put(fetchChartAction.failure({ id }))\n    return\n  }\n  yield put(fetchChartAction.success({\n    chartMetadata: response.data,\n    id,\n  }))\n}\n\nfunction* fetchInfoSaga({ payload }: Action<FetchInfoPayload>) {\n  const { poll } = payload\n  let isCloudEnabled = false\n  let isAgentClaimed = false\n  let isCloudAvailable = false\n  let isACLKAvailable = false\n\n  try {\n    const registry: GlobalStateT[\"registry\"] = yield select(selectRegistry)\n    const wasCloudAvailable = registry?.isCloudAvailable\n    const wasACLKAvailable = registry?.isACLKAvailable\n\n    const { data } = yield call(axiosInstance.get, `${serverDefault}/api/v1/info`)\n    isCloudAvailable = data?.[\"cloud-available\"] || false\n    isCloudEnabled = data?.[\"cloud-enabled\"] || false\n    isAgentClaimed = data?.[\"agent-claimed\"] || false\n    isACLKAvailable = data?.[\"aclk-available\"] || false\n\n    yield put(fetchInfoAction.success({\n      isCloudAvailable, isCloudEnabled, isAgentClaimed, isACLKAvailable, fullInfoPayload: data,\n    }))\n\n    if (isCloudEnabled && (wasCloudAvailable === null) && !isCloudAvailable) {\n      // show only once per session\n      showCloudInstallationProblemNotification()\n    }\n    if (isCloudAvailable && isAgentClaimed && (wasACLKAvailable !== false) && !isACLKAvailable) {\n      // show at session-init and if we see a change of isACLKAvailable from true to false\n      showCloudConnectionProblemNotification()\n    }\n    // TODO: No success notification spec`ed?\n    // else if (!wasACLKAvailable && isACLKAvailable) {\n    //   toast.success(\"Connected to the Cloud!\", {\n    //     position: \"bottom-right\",\n    //     type: toast.TYPE.SUCCESS,\n    //     autoClose: NOTIFICATIONS_TIMEOUT,\n    //   })\n    // }\n  } catch (e) {\n    console.warn(\"fetch agent info failure\") // eslint-disable-line no-console\n    yield put(fetchInfoAction.failure())\n  }\n\n  if (poll && isCloudEnabled && isAgentClaimed) {\n    yield delay(INFO_POLLING_FREQUENCY)\n    yield put(fetchInfoAction({ poll: true }))\n  }\n}\n\n\nexport function* chartSagas() {\n  yield takeEvery(fetchDataAction.request, fetchDataSaga)\n  yield takeEvery(fetchChartAction.request, fetchChartSaga)\n  yield takeEvery(fetchDataForSnapshotAction.request, fetchDataForSnapshotSaga)\n  yield takeEvery(stopSnapshotModeAction, stopSnapshotModeSaga)\n  yield takeEvery(fetchInfoAction.request, fetchInfoSaga)\n  yield spawn(watchFetchDataResponseChannel)\n}\n","export const sidePanelTransitionTimeInSeconds = 0.2\n","import { sortBy, prop, last } from \"ramda\"\nimport { Action } from \"redux-act\"\nimport {\n  call, delay, spawn, take, takeEvery, put,\n} from \"redux-saga/effects\"\n\nimport { axiosInstance } from \"utils/api\"\nimport { serverStatic } from \"utils/server-detection\"\nimport { name2id } from \"utils/name-2-id\"\n\nimport {\n  startAlarmsAction, StartAlarmsPayload, fetchAllAlarmsAction, updateActiveAlarmsAction,\n} from \"./actions\"\nimport { AlarmLogs, AlarmLog, ActiveAlarms } from \"./types\"\n\nconst ALARMS_INITIALIZATION_DELAY = 1000\nconst ALARMS_UPDATE_EVERY = 10000 // the time in ms between alarm checks\nconst CHART_DIV_OFFSET = -50\n\n// firefox moves the alarms off-screen (above, outside the top of the screen)\n// if alarms are shown faster than: one per 500ms\nconst ALARMS_MS_BETWEEN_NOTIFICATIONS = 500\n\n// equal to old NETDATA.alarms.notifications\nconst areNotificationsAvailable = \"Notification\" in window\n\nconst notificationCallback = window.netdataAlarmsNotifCallback\n\n\n// todo this doesn't change in the session, but should be moved to the redux state anyway\nlet firstNotificationId = 0\nlet lastNotificationId = 0\n\n\nconst scrollToChart = (chartID: unknown): boolean => {\n  if (typeof chartID === \"string\") {\n    const chartElement = document.querySelector(`#chart_${name2id(chartID)}`)\n    if (chartElement) {\n      const offset = (chartElement as HTMLDivElement).offsetTop + CHART_DIV_OFFSET;\n      (document.querySelector(\"html\") as HTMLElement).scrollTop = offset\n      return true\n    }\n  }\n  return false\n}\n\n// perhaps sagas are not the best place for this\nconst scrollToAlarm = (alarm: AlarmLog) => {\n  if (typeof alarm === \"object\") {\n    const hasFoundChart = scrollToChart(alarm.chart)\n    if (hasFoundChart) {\n      window.focus()\n    }\n  }\n}\n\nconst requestPermissions = () => {\n  if (areNotificationsAvailable) {\n    if (Notification.permission === \"default\") {\n      Notification.requestPermission()\n    }\n  }\n}\n\nconst hasGivenNotificationPermissions = () => (areNotificationsAvailable\n  && Notification.permission === \"granted\"\n)\n\nfunction* getLog(lastNotificationIdArg: number, serverDefault: string) {\n  try {\n    const { data } = yield call(\n      axiosInstance.get,\n      `${serverDefault}/api/v1/alarm_log?after=${lastNotificationIdArg}`,\n    )\n    // todo xss check\n    return data\n  } catch (error) {\n    console.warn(\"Error fetching alarms log\", error) // eslint-disable-line no-console\n    return null\n  }\n}\n\ninterface NotificationConfig {\n  notificationTitle: string\n  notificationOptions: NotificationOptions\n  notificationHandler: (event: Event) => void\n}\n// called \"notify\" in old codebase\nconst getNotification = (\n  entry: AlarmLog, activeAlarms: ActiveAlarms, firstNotificationIdArg: number,\n): NotificationConfig | undefined => {\n  if (entry.updated) {\n    // has been updated by another alarm\n    return\n  }\n\n  let valueString = entry.value_string\n  const t = activeAlarms.alarms[`${entry.chart}.${entry.name}`]\n  if (typeof t !== \"undefined\"\n    && entry.status === t.status\n    && typeof t.value_string !== \"undefined\"\n  ) {\n    valueString = t.value_string\n  }\n\n  const name = entry.name.replace(/_/g, \" \")\n  let status = entry.status.toLowerCase()\n  let title = `${name} = ${valueString}`\n  const tag = entry.alarm_id\n  let icon = \"images/banner-icon-144x144.png\"\n  let interaction = false\n  let show = true\n\n  // switch/case left here to simplify refractor (it's very similar to old code)\n  switch (entry.status) {\n    case \"REMOVED\":\n      show = false\n      break\n\n    case \"UNDEFINED\":\n      return\n\n    case \"UNINITIALIZED\":\n      return\n\n    case \"CLEAR\":\n      if (entry.unique_id < firstNotificationIdArg) {\n        // alarm is not current\n        return\n      }\n      if (entry.old_status === \"UNINITIALIZED\" || entry.old_status === \"UNDEFINED\") {\n        // alarm switch to CLEAR from old_status\n        return\n      }\n      if (entry.no_clear_notification) {\n        // alarm is CLEAR but has no_clear_notification flag\n        return\n      }\n      title = `${name} back to normal (${valueString})`\n      icon = \"images/check-mark-2-128-green.png\"\n      interaction = false\n      break\n\n    case \"WARNING\":\n      if (entry.old_status === \"CRITICAL\") {\n        status = `demoted to ${entry.status.toLowerCase()}`\n      }\n\n      icon = \"images/alert-128-orange.png\"\n      interaction = false\n      break\n\n    case \"CRITICAL\":\n      if (entry.old_status === \"WARNING\") {\n        status = `escalated to ${entry.status.toLowerCase()}`\n      }\n\n      icon = \"images/alert-128-red.png\"\n      interaction = true\n      break\n\n    default:\n      console.warn(`invalid alarm status ${entry.status}`) // eslint-disable-line no-console\n      return\n  }\n\n  // filter recipients\n  // if (show) {\n  //   show = NETDATA.alarms.recipientMatches(entry.recipient, NETDATA.alarms.recipients)\n  // }\n\n\n  if (show) {\n    if (typeof notificationCallback === \"function\") {\n      show = notificationCallback(entry)\n    }\n\n    if (show) {\n      // show this notification\n      // eslint-disable-next-line consistent-return\n      return {\n        notificationTitle: title,\n        notificationOptions: {\n          body: `${entry.hostname} - ${entry.chart} (${entry.family}) - ${status}: ${entry.info}`,\n          tag: `${tag}`,\n          requireInteraction: interaction,\n          icon: serverStatic + icon,\n          data: entry,\n        },\n        notificationHandler: (event: Event) => {\n          event.preventDefault()\n          if (event.target) {\n            const { data } = event.target as Notification\n            scrollToAlarm(data)\n          }\n        },\n      }\n    }\n  }\n}\n\nfunction* notifyAll(serverDefault: string, activeAlarms: ActiveAlarms) {\n  const alarmLogs: AlarmLogs = yield call(getLog, lastNotificationId, serverDefault)\n  if (alarmLogs === null || typeof alarmLogs !== \"object\") {\n    console.warn(\"invalid alarms log response\") // eslint-disable-line no-console\n    return\n  }\n\n  if (alarmLogs.length === 0) {\n    console.log(\"received empty alarm log\") // eslint-disable-line no-console\n    return\n  }\n\n  const logsSorted = sortBy(prop(\"unique_id\"), alarmLogs)\n\n  // eslint-disable-next-line camelcase\n  const newLogs = logsSorted.filter(({ unique_id }) => unique_id > lastNotificationId)\n  const notifications = newLogs\n    .map((entry) => (getNotification(entry, activeAlarms, firstNotificationId)))\n    .filter((x) => x !== undefined) as NotificationConfig[]\n\n  for (let i = 0; i < notifications.length; i += 1) {\n    const {\n      notificationTitle, notificationOptions, notificationHandler,\n    } = notifications[i]\n    const notification = new Notification(\n      notificationTitle,\n      notificationOptions,\n    )\n    notification.onclick = notificationHandler\n\n    yield delay(ALARMS_MS_BETWEEN_NOTIFICATIONS)\n  }\n\n  // todo put to redux store\n  lastNotificationId = (last(logsSorted) as AlarmLog).unique_id\n\n  if (typeof window.netdataAlarmsRemember === \"undefined\" || window.netdataAlarmsRemember) {\n    localStorage.setItem(\"last_notification_id\", `${lastNotificationId}`)\n  }\n}\n\n\nfunction* get(what: string, serverDefault: string) {\n  const { data } = yield call(axiosInstance.get, `${serverDefault}/api/v1/alarms?${what}`)\n  if (firstNotificationId === 0 && typeof data.latest_alarm_log_unique_id === \"number\") {\n    firstNotificationId = data.latest_alarm_log_unique_id\n  }\n  return data\n}\n\nfunction* alarmsLoop(serverDefault: string) {\n  while (true) {\n    const activeAlarms = (yield call(get, \"active\", serverDefault)) as ActiveAlarms\n    if (activeAlarms) {\n      yield put(updateActiveAlarmsAction({ activeAlarms }))\n      if (\n        hasGivenNotificationPermissions()\n        // timestamps in seconds\n        && (activeAlarms.latest_alarm_log_unique_id > lastNotificationId)\n      ) {\n        yield call(notifyAll, serverDefault, activeAlarms)\n\n        if (activeAlarms.status === false) {\n          // Health monitoring is disabled on this netdata\n          break\n        }\n      }\n    }\n    yield delay(ALARMS_UPDATE_EVERY)\n  }\n}\n\nfunction* startAlarms() {\n  // make sure we handle that action only once, we don't want multiple intervals/loops\n  const { payload }: { payload: StartAlarmsPayload } = yield take(startAlarmsAction)\n  const { serverDefault } = payload\n\n  yield delay(ALARMS_INITIALIZATION_DELAY)\n\n  lastNotificationId = +(localStorage.getItem(\"last_notification_id\") || lastNotificationId)\n  requestPermissions()\n  yield call(alarmsLoop, serverDefault)\n}\n\ntype FetchAllAlarmsPayload = {\n  callback: (x: unknown) => void,\n  serverDefault: string,\n}\nfunction* fetchAllAlarmsSaga({ payload }: Action<FetchAllAlarmsPayload>) {\n  const { callback, serverDefault } = payload\n  const allAlarms = yield call(get, \"all\", serverDefault)\n  callback(allAlarms)\n}\n\nexport function* alarmsSagas() {\n  yield spawn(startAlarms)\n  yield takeEvery(fetchAllAlarmsAction.request, fetchAllAlarmsSaga)\n}\n","const allowedReferrerDomains = [\n  \"\",\n  \"https://www.google.com/\",\n  \"https://duckduckgo.com/\",\n  \"https://www.reddit.com/\",\n]\n\nexport const isAllowedReferrer = (referrer: string) => allowedReferrerDomains.includes(referrer)\n  || referrer.endsWith(\".my-netdata.io/\")\n  || referrer.startsWith(\"https://github.com/\")\n  || referrer.endsWith(\"netdata.cloud/\")\n  || referrer.startsWith(\"https://app.netdata.cloud/\")\n","import { uniq, filter } from \"ramda\"\nimport {\n  spawn, take, put, takeEvery, call, delay, select,\n} from \"redux-saga/effects\"\nimport { channel } from \"redux-saga\"\nimport { AxiosResponse } from \"axios\"\nimport { Action } from \"redux-act\"\n\nimport { NETDATA_REGISTRY_SERVER } from \"utils/utils\"\nimport { axiosInstance } from \"utils/api\"\nimport { isDemo } from \"utils/is-demo\"\nimport { sidePanelTransitionTimeInSeconds } from \"components/space-panel/settings\"\nimport { fetchInfoAction } from \"domains/chart/actions\"\n\nimport {\n  fetchHelloAction,\n  FetchHelloPayload,\n  windowFocusChangeAction,\n  updatePersonUrlsAction,\n  SetOptionAction,\n  setOptionAction,\n  setSpacePanelStatusAction,\n  SetSpacePanelStatusActionPayload,\n  setSpacePanelTransitionEndAction,\n  HelloResponse,\n  accessRegistrySuccessAction,\n} from \"./actions\"\nimport { alarmsSagas } from \"./alarms-sagas\"\nimport { MASKED_DATA } from \"./constants\"\nimport { selectFullInfoPayload } from \"./selectors\"\nimport { isAllowedReferrer } from \"./utils\"\nimport { InfoPayload } from \"./__mocks__/info-mock\"\n\nconst windowFocusChannel = channel()\n\nexport function listenToWindowFocus() {\n  window.addEventListener(\"focus\", () => {\n    windowFocusChannel.put(windowFocusChangeAction({ hasWindowFocus: true }))\n  })\n  window.addEventListener(\"blur\", () => {\n    windowFocusChannel.put(windowFocusChangeAction({ hasWindowFocus: false }))\n  })\n}\n\nexport function* watchWindowFocusChannel() {\n  while (true) {\n    const action = yield take(windowFocusChannel)\n    yield put(action)\n  }\n}\n\nfunction* waitForFullInfoPayload() {\n  return (yield take(fetchInfoAction.success)).payload.fullInfoPayload\n}\n\nfunction* injectPosthog(machineGuid: string, personGuid?: string) {\n  if (window.posthog) {\n    return\n  }\n  const info: InfoPayload = (yield select(selectFullInfoPayload))\n    || (yield call(waitForFullInfoPayload))\n    || {}\n\n  /* eslint-disable */\n  // @ts-ignore\n  !function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(\".\");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement(\"script\")).type=\"text/javascript\",p.async=!0,p.src=s.api_host+\"/static/array.js\",(r=t.getElementsByTagName(\"script\")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a=\"posthog\",u.people=u.people||[],u.toString=function(t){var e=\"posthog\";return\"posthog\"!==a&&(e+=\".\"+a),t||(e+=\" (stub)\"),e},u.people.toString=function(){return u.toString(1)+\".people (stub)\"},o=\"capture identify alias people.set people.set_once set_config register register_once unregister opt_out_capturing has_opted_out_capturing opt_in_capturing reset isFeatureEnabled onFeatureFlags\".split(\" \"),n=0;n<o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||[]);\n  /* eslint-enable */\n  window.posthog.init(\"mqkwGT0JNFqO-zX2t0mW6Tec9yooaVu7xCBlXtHnt5Y\", {\n    api_host: \"https://app.posthog.com\",\n    loaded: (posthog: any) => {\n      if (personGuid) {\n        posthog.identify(personGuid)\n      }\n    },\n  })\n  const shouldMaskReferrer = !isDemo && !isAllowedReferrer(document.referrer)\n  const MASKED = \"masked\"\n  window.posthog.register(\n    // remove properties with unavailable values\n    filter((value) => value !== undefined && value !== null,\n      {\n        $ip: \"127.0.0.1\",\n        $current_url: isDemo ? null : \"agent dashboard\",\n        $pathname: isDemo ? null : \"netdata-dashboard\",\n        $host: isDemo ? null : \"dashboard.netdata.io\",\n\n        $initial_referring_domain: shouldMaskReferrer ? MASKED : null,\n        $initial_referrer: shouldMaskReferrer ? MASKED : null,\n        $referring_domain: shouldMaskReferrer ? MASKED : null,\n        $referrer: shouldMaskReferrer ? MASKED : null,\n\n        event_source: \"agent dashboard\",\n\n        netdata_version: info.version,\n        netdata_machine_guid: machineGuid,\n        netdata_person_id: personGuid || \"Unavailable\",\n        netdata_buildinfo: info[\"buildinfo\"],\n        netdata_release_channel: info[\"release-channel\"],\n        mirrored_host_count: info.mirrored_hosts?.length,\n        alarms_normal: info.alarms?.normal,\n        alarms_warning: info.alarms?.warning,\n        alarms_critical: info.alarms.critical,\n        host_os_name: info.os_name,\n        host_os_id: info.os_id,\n        host_os_id_like: info.os_id_like,\n        host_os_version: info.os_version,\n        host_os_version_id: info.os_version_id,\n        host_os_detection: info.os_detection,\n        system_cores_total: info.cores_total,\n        system_total_disk_space: info.total_disk_space,\n        system_cpu_freq: info.cpu_freq,\n        system_ram_total: info.ram_total,\n        system_kernel_name: info.kernel_name,\n        system_kernel_version: info.kernel_version,\n        system_architecture: info.architecture,\n        system_virtualization: info.virtualization,\n        system_virt_detection: info.virt_detection,\n        system_container: info.container,\n        system_container_detection: info.container_detection,\n        container_os_name: info.container_os_name,\n        container_os_id: info.container_os_id,\n        container_os_id_like: info.container_os_id_like,\n        container_os_version: info.container_os_version,\n        container_os_version_id: info.container_os_version_id,\n        host_collectors_count: info.collectors.length,\n        host_cloud_enabled: info[\"cloud-enabled\"],\n        host_cloud_available: info[\"cloud-available\"],\n        host_agent_claimed: info[\"agent-claimed\"],\n        host_aclk_available: info[\"aclk-available\"],\n        host_aclk_implementation: info[\"aclk-implementation\"],\n        host_allmetrics_json_used: info[\"allmetrics-json-used\"],\n        host_allmetrics_prometheus_used: info[\"allmetrics-prometheus-used\"],\n        host_allmetrics_shell_used: info[\"allmetrics-shell-used\"],\n        host_charts_count: info[\"charts-count\"],\n        host_dashboard_used: info[\"dashboard-used\"],\n        host_metrics_count: info[\"metrics-count\"],\n        host_notification_methods: info[\"notification-methods\"],\n        config_memory_mode: info[\"memory-mode\"],\n        config_exporting_enabled: info[\"exporting-enabled\"],\n        config_exporting_connectors: info[\"exporting-connectors\"],\n        config_hosts_available: info[\"hosts-available\"],\n        config_https_enabled: info[\"https-enabled\"],\n        config_multidb_disk_quota: info[\"multidb-disk-quota\"],\n        config_page_cache_size: info[\"page-cache-size\"],\n        config_stream_enabled: info[\"stream-enabled\"],\n        config_web_enabled: info[\"web-enabled\"],\n        // eslint-disable-next-line camelcase\n        host_is_parent: info.host_labels?._is_parent,\n        mirrored_hosts_reachable: info.mirrored_hosts_status\n          .filter(({ reachable }) => reachable).length,\n        mirrored_hosts_unreachable: info.mirrored_hosts_status\n          .filter(({ reachable }) => !reachable).length,\n        host_collectors: info.collectors,\n        host_is_k8s_node: info.is_k8s_node,\n      }),\n  )\n}\n\nexport type PersonUrl = [\n  string, // guid\n  string, // url\n  number, // last timestamp (ms)\n  number, // accesses\n  string // name\n]\n\ntype AccessRegistryResponse = null | {\n  personGuid?: string\n  registryServer: string\n  urls?: PersonUrl[]\n}\n\ntype AccessRegistry = (args: {\n  machineGuid: string\n  maxRedirects: number\n  name: string\n  registryServer: string\n  url: string\n}) => Promise<AccessRegistryResponse>\nconst accessRegistry: AccessRegistry = ({\n  machineGuid, maxRedirects, name, registryServer, url,\n}) => axiosInstance.get(`${registryServer}/api/v1/registry`, {\n  headers: {\n    \"Cache-Control\": \"no-cache, no-store\",\n    Pragma: \"no-cache\",\n  },\n  params: {\n    action: \"access\",\n    machine: machineGuid,\n    name,\n    url,\n  },\n  withCredentials: true, // required for the cookie\n}).then(({ data }) => {\n  // todo xss check\n  const isRedirect = typeof data.registry === \"string\"\n\n  let returnData = data\n  if (typeof data.status !== \"string\" || data.status !== \"ok\") {\n    // todo throw error (409 in old dashboard)\n    returnData = null\n  }\n\n  if (returnData === null) {\n    if (isRedirect && maxRedirects > 0) {\n      return accessRegistry({\n        maxRedirects: maxRedirects - 1,\n        machineGuid,\n        name,\n        registryServer: data.registry,\n        url,\n      })\n    }\n    return { registryServer }\n  }\n  const urls = data.urls.filter((u: [string, string]) => u[1] !== MASKED_DATA)\n  return {\n    personGuid: data.person_guid || null,\n    registryServer,\n    urls,\n  }\n}).catch(() => {\n  // todo handle error in better way (410 in old dashboard)\n  console.warn(\"error calling registry:\", registryServer) // eslint-disable-line no-console\n  return null\n})\n\nexport interface RegistryMachine {\n  guid: string\n  url: string\n  lastTimestamp: number\n  accesses: number\n  name: string\n  alternateUrls: string[]\n}\n\ntype ParsePersonUrls = (\n  personUrls: PersonUrl[]\n) => {\n  registryMachines: { [key: string]: RegistryMachine }\n  registryMachinesArray: RegistryMachine[]\n}\nexport const parsePersonUrls: ParsePersonUrls = (personUrls) => {\n  // todo main.js is using registryMachines, but should use only the array\n  const registryMachines: { [key: string]: RegistryMachine } = {}\n\n  personUrls\n    .slice()\n    .reverse()\n    .forEach(([guid, url, lastTimestamp, accesses, name]: PersonUrl) => {\n      const existingObj = registryMachines[guid] || {\n        lastTimestamp: 0,\n        accesses: 0,\n        alternateUrls: [],\n        guid: \"\",\n        url: \"\",\n        name: \"\"\n      }\n      const isNewer = existingObj.lastTimestamp < lastTimestamp\n      const extended: RegistryMachine = {\n        guid: existingObj.guid || guid,\n        url: isNewer ? url : existingObj.url,\n        lastTimestamp: isNewer ? lastTimestamp : existingObj.lastTimestamp,\n        accesses: existingObj.accesses + accesses,\n        name: isNewer ? name : existingObj.name,\n        alternateUrls: existingObj.alternateUrls.concat(url),\n      }\n      registryMachines[guid] = extended\n    })\n\n  const registryMachinesArray = uniq(\n    // not sure if reverse is needed, but it was in old dashboard\n    personUrls\n      .slice()\n      .reverse()\n      .map(([guid]: PersonUrl) => guid),\n  ).map((guid) => registryMachines[guid])\n  return {\n    registryMachines,\n    registryMachinesArray,\n  }\n}\n\nfunction* fetchHelloSaga({ payload }: Action<FetchHelloPayload>) {\n  const { serverDefault } = payload\n  const helloCallUrl = `${serverDefault}api/v1/registry?action=hello`\n  let response: AxiosResponse<HelloResponse>\n  try {\n    response = yield call(axiosInstance.get, helloCallUrl, {\n      headers: {\n        \"Cache-Control\": \"no-cache, no-store\",\n        Pragma: \"no-cache\",\n      },\n      withCredentials: true,\n    })\n  } catch (error) {\n    console.warn(\"error accessing registry or Do-Not-Track is enabled\") // eslint-disable-line\n    yield put(fetchHelloAction.failure())\n    return\n  }\n  const cloudBaseURL = response.data.cloud_base_url\n  const { hostname } = response.data\n  const machineGuid = response.data.machine_guid\n  const registryServer = response.data.registry\n  const isUsingGlobalRegistry = registryServer === NETDATA_REGISTRY_SERVER\n\n  yield put(fetchHelloAction.success({\n    cloudBaseURL,\n    hostname,\n    isUsingGlobalRegistry,\n    machineGuid,\n  }))\n\n  const name = isUsingGlobalRegistry ? MASKED_DATA : hostname\n  const url = isUsingGlobalRegistry ? MASKED_DATA : serverDefault\n\n  // now make access call - max_redirects, callback, etc...\n  const accessRegistryResponse: AccessRegistryResponse = yield call(accessRegistry, {\n    machineGuid,\n    maxRedirects: 2,\n    name,\n    registryServer,\n    url,\n  })\n\n  if (response.data.anonymous_statistics) {\n    yield spawn(injectPosthog, response.data.machine_guid, accessRegistryResponse?.personGuid)\n  }\n\n  if (accessRegistryResponse?.urls && accessRegistryResponse?.personGuid) {\n    const personUrls = parsePersonUrls(accessRegistryResponse.urls)\n    const { registryMachines, registryMachinesArray } = personUrls\n    yield put(updatePersonUrlsAction({\n      personGuid: accessRegistryResponse.personGuid,\n      registryMachines,\n      registryMachinesArray,\n    }))\n  }\n\n  yield put(accessRegistrySuccessAction({\n    registryServer: accessRegistryResponse?.registryServer || registryServer,\n  }))\n}\n\nconst constructOptionStorageKey = (key: string) => `options.${key}`\nfunction setOptionSaga({ payload }: Action<SetOptionAction>) {\n  const { key, value } = payload\n  if (key === \"stop_updates_when_focus_is_lost\") {\n    // old dashboard was saving that property to localStorage, but was always omitting it when\n    // reading. it was only possible to persist this setting via url (update_always hash param)\n    return\n  }\n  localStorage.setItem(constructOptionStorageKey(key), JSON.stringify(value))\n}\n\nfunction* spacePanelSaga({ payload }: Action<SetSpacePanelStatusActionPayload>) {\n  if (payload.isActive) {\n    document.body.className = \"with-panel\"\n  } else {\n    document.body.className = \"\"\n  }\n  yield delay(sidePanelTransitionTimeInSeconds * 1000)\n  yield put(setSpacePanelTransitionEndAction({ isActive: payload.isActive }))\n}\n\nexport function* globalSagas() {\n  yield spawn(listenToWindowFocus)\n  yield spawn(watchWindowFocusChannel)\n  yield takeEvery(fetchHelloAction.request, fetchHelloSaga)\n  yield spawn(alarmsSagas)\n  yield takeEvery(setOptionAction, setOptionSaga)\n  yield takeEvery(setSpacePanelStatusAction, spacePanelSaga)\n}\n","/* eslint-disable camelcase */\n/* eslint-disable operator-linebreak */\nimport { take, takeEvery } from \"redux-saga/effects\"\nimport { Action } from \"redux-act\"\n\nimport {\n  clearHighlightAction,\n  SetGlobalChartUnderlayAction,\n  setGlobalChartUnderlayAction,\n} from \"domains/global/actions\"\nimport {\n  explicitlySignInAction,\n  showSignInModalAction,\n  ShowSignInModalAction,\n} from \"domains/dashboard/actions\"\nimport { setHashParams, getHashParams, removeHashParams } from \"utils/hash-utils\"\n\nexport const LOCAL_STORAGE_NEEDS_SYNC = \"LOCAL-STORAGE-NEEDS-SYNC\"\n\nfunction setGlobalChartUnderlaySaga({ payload }: Action<SetGlobalChartUnderlayAction>) {\n  const { after, before } = payload\n  if (window.urlOptions) {\n    // additional check to prevent loop, after setting initial state from url\n    if (window.urlOptions.after !== after || window.urlOptions.before !== before) {\n      window.urlOptions.netdataHighlightCallback(true, after, before)\n    }\n  } else {\n    // TODO: Consider a setting to control whether the component sets these hash params\n    const hashParams = getHashParams()\n    const highlight_after = Math.round(after).toString()\n    const highlight_before = Math.round(before).toString()\n    if (\n      hashParams.highlight_after !== highlight_after ||\n      hashParams.highlight_before !== highlight_before\n    ) {\n      setHashParams({ highlight_after, highlight_before })\n    }\n  }\n}\n\nfunction clearHighlightSaga() {\n  if (window.urlOptions) {\n    window.urlOptions.netdataHighlightCallback(false, 0, 0)\n  } else {\n    removeHashParams([\"highlight_after\", \"highlight_before\"])\n  }\n}\n\nfunction* showSignInSaga({ payload }: Action<ShowSignInModalAction>) {\n  if (window.showSignInModal) {\n    window.showSignInModal()\n\n    yield take(explicitlySignInAction)\n    const { signInLinkHref } = payload\n    window.localStorage.setItem(LOCAL_STORAGE_NEEDS_SYNC, \"true\")\n    window.location.href = signInLinkHref\n  }\n}\n\nexport function* mainJsSagas() {\n  yield takeEvery(setGlobalChartUnderlayAction, setGlobalChartUnderlaySaga)\n  yield takeEvery(clearHighlightAction, clearHighlightSaga)\n  yield takeEvery(showSignInModalAction, showSignInSaga)\n}\n","import { spawn } from \"redux-saga/effects\"\n\nimport { chartSagas } from \"domains/chart/sagas\"\nimport { globalSagas } from \"domains/global/sagas\"\nimport { mainJsSagas } from \"domains/dashboard/sagas\"\n\nexport function* rootSaga() {\n  yield spawn(globalSagas)\n  yield spawn(chartSagas)\n  yield spawn(mainJsSagas)\n}\n","import { compose, applyMiddleware, createStore } from \"redux\"\nimport createSagaMiddleware from \"redux-saga\"\nimport rootReducer from \"./root-reducer\"\nimport { rootSaga } from \"./root-saga\"\n\nconst sagaMiddleware = createSagaMiddleware()\n\nconst reduxDevTools = process.env.NODE_ENV === \"development\"\n  && window.__REDUX_DEVTOOLS_EXTENSION__\n  // @ts-ignore\n  && window.__REDUX_DEVTOOLS_EXTENSION__({ name: \"Dashboard Charts\" })\n\nconst composeMiddlewaresWithDevTools = () => (reduxDevTools\n  ? compose(applyMiddleware(sagaMiddleware), reduxDevTools)\n  : compose(applyMiddleware(sagaMiddleware)))\n\nexport const configureStore = () => {\n  const store = createStore(\n    rootReducer,\n    composeMiddlewaresWithDevTools(),\n  )\n  sagaMiddleware.run(rootSaga)\n  return store\n}\n\nexport const store = configureStore()\n","import $ from \"jquery\"\n\nwindow.$ = $\nwindow.jQuery = $\n","let loadCssPromise: Promise<void>\n\ntype LoadCss = (href: string) => Promise<void>\nexport const loadCss: LoadCss = (href) => {\n  if (loadCssPromise) {\n    return loadCssPromise\n  }\n  return new Promise((resolve, reject) => {\n    const fileRef = document.createElement(\"link\")\n    fileRef.setAttribute(\"rel\", \"stylesheet\")\n    fileRef.setAttribute(\"type\", \"text/css\")\n    fileRef.setAttribute(\"href\", href)\n\n    fileRef.onload = () => {\n      resolve()\n    }\n\n    fileRef.onerror = () => {\n      reject(Error(`Error loading css: ${href}`))\n    }\n\n    document.getElementsByTagName(\"head\")[0].appendChild(fileRef)\n  })\n}\n","import classNames from \"classnames\"\nimport { Attributes } from \"./transformDataAttributes\"\n\nexport type ChartLibraryName =\n  | \"dygraph\"\n  | \"sparkline\"\n  | \"peity\"\n  | \"google\"\n  // | \"d3\"\n  | \"d3pie\"\n  | \"easypiechart\"\n  | \"gauge\"\n  | \"textonly\"\n  | \"groupbox\"\nexport interface ChartLibraryConfig {\n  aspectRatio?: number\n  format: string\n  hasLegend: (attributes: Attributes) => boolean\n  hasToolboxPanAndZoom?: boolean\n  isLogScale?: (attributes: Attributes) => boolean\n  options: (attributes: Attributes) => string\n  trackColors: boolean\n  pixelsPerPoint: (attributes: Attributes) => number\n  xssRegexIgnore: RegExp\n  containerClass: (attributes: Attributes) => string\n}\nexport type ChartLibrariesSettings = {\n  [key in ChartLibraryName]: ChartLibraryConfig\n}\n\ntype IsDygraphSparkline = (attributes: Attributes) => boolean\nconst isDygraphSparkline: IsDygraphSparkline = (attributes) => (\n  attributes.dygraphTheme === \"sparkline\"\n)\n\nexport const chartLibrariesSettings: ChartLibrariesSettings = {\n  dygraph: {\n    // initialize: window.NETDATA.dygraphInitialize,\n    // create: window.NETDATA.dygraphChartCreate,\n    // update: window.NETDATA.dygraphChartUpdate,\n    // resize(state) {\n    //   if (typeof state.tmp.dygraph_instance !== \"undefined\"\n    //     && typeof state.tmp.dygraph_instance.resize === \"function\") {\n    //     state.tmp.dygraph_instance.resize()\n    //   }\n    // },\n    // setSelection: window.NETDATA.dygraphSetSelection,\n    // clearSelection: window.NETDATA.dygraphClearSelection,\n    hasToolboxPanAndZoom: true,\n    // initialized: false,\n    // enabled: true,\n    xssRegexIgnore: new RegExp(\"^/api/v1/data.result.data$\"),\n    format: \"json\",\n    options(attributes: Attributes) {\n      if (typeof this.isLogScale === \"function\") {\n        // flip - in proper order (from oldest to newest)\n        return `ms|flip${this.isLogScale(attributes) ? \"|abs\" : \"\"}`\n      }\n      return \"\"\n    },\n    hasLegend(attributes: Attributes) {\n      // not using __hasLegendCache__ as in old-dashboard, because performance tweaks like this\n      // probably won't be needed in react app\n      const { legend = true } = attributes\n      return !isDygraphSparkline(attributes) && Boolean(legend)\n    },\n    // autoresize(state) {\n    //   void (state)\n    //   return true\n    // },\n    // max_updates_to_recreate(state) {\n    //   void (state)\n    //   return 5000\n    // },\n    trackColors: true,\n    pixelsPerPoint: ((attributes: Attributes) => (isDygraphSparkline(attributes) ? 2 : 3)),\n    // pixels_per_point(state) {\n    //   return (this.isSparkline(state) === false) ? 3 : 2\n    // },\n    isLogScale(attributes: Attributes) {\n      return attributes.dygraphTheme === \"logscale\"\n    },\n    containerClass(attributes: Attributes) {\n      return this.hasLegend(attributes)\n        ? classNames(\n          \"netdata-container-with-legend\",\n          attributes.legendPosition === \"bottom\" && \"netdata-container-with-legend--bottom\",\n        )\n        : \"netdata-container\"\n    },\n    // container_class(state) {\n    //   if (this.legend(state) !== null) {\n    //     return \"netdata-container-with-legend\"\n    //   }\n    //   return \"netdata-container\"\n    // },\n  },\n  sparkline: {\n  //   initialize: window.NETDATA.sparklineInitialize,\n  //   create: window.NETDATA.sparklineChartCreate,\n  //   update: window.NETDATA.sparklineChartUpdate,\n  //   resize: null,\n  //   setSelection: undefined, // function(state, t) { void(state); return true; },\n  //   clearSelection: undefined, // function(state) { void(state); return true; },\n    hasToolboxPanAndZoom: false,\n    //   initialized: false,\n    //   enabled: true,\n    xssRegexIgnore: new RegExp(\"^/api/v1/data.result$\"),\n    format: \"array\",\n    options: () => \"flip|abs\",\n    hasLegend: () => false,\n    //   autoresize(state) {\n    //     void (state)\n    //     return false\n    //   },\n    //   max_updates_to_recreate(state) {\n    //     void (state)\n    //     return 5000\n    //   },\n    trackColors: false,\n    pixelsPerPoint: () => 3,\n    containerClass: () => \"netdata-container\",\n  },\n  peity: {\n    // initialize: window.NETDATA.peityInitialize,\n    // create: window.NETDATA.peityChartCreate,\n    // update: window.NETDATA.peityChartUpdate,\n    // resize: null,\n    // setSelection: undefined, // function(state, t) { void(state); return true; },\n    // clearSelection: undefined, // function(state) { void(state); return true; },\n    hasToolboxPanAndZoom: false,\n    // initialized: false,\n    // enabled: true,\n    xssRegexIgnore: new RegExp(\"^/api/v1/data.result$\"),\n    format: \"ssvcomma\",\n    options: () => \"null2zero|flip|abs\",\n    hasLegend: () => false,\n    // autoresize(state) {\n    //   void (state)\n    //   return false\n    // },\n    // max_updates_to_recreate(state) {\n    //   void (state)\n    //   return 5000\n    // },\n    trackColors: false,\n    pixelsPerPoint: () => 3,\n    containerClass: () => \"netdata-container\",\n  },\n  google: {\n    // initialize: window.NETDATA.googleInitialize,\n    // create: window.NETDATA.googleChartCreate,\n    // update: window.NETDATA.googleChartUpdate,\n    // resize: null,\n    // setSelection: undefined, // function(state, t) { void(state); return true; },\n    // clearSelection: undefined, // function(state) { void(state); return true; },\n    hasToolboxPanAndZoom: false,\n    // initialized: false,\n    // enabled: true,\n    xssRegexIgnore: new RegExp(\"^/api/v1/data.result.rows$\"),\n    format: \"datatable\",\n    options: () => \"\",\n    hasLegend: () => false,\n    // autoresize(state) {\n    //   void (state)\n    //   return false\n    // },\n    // max_updates_to_recreate(state) {\n    //   void (state)\n    //   return 300\n    // },\n    trackColors: false,\n    pixelsPerPoint: () => 4,\n    containerClass: () => \"netdata-container\",\n  },\n  d3pie: {\n    // initialize: window.NETDATA.d3pieInitialize,\n    // create: window.NETDATA.d3pieChartCreate,\n    // update: window.NETDATA.d3pieChartUpdate,\n    // resize: null,\n    // setSelection: window.NETDATA.d3pieSetSelection,\n    // clearSelection: window.NETDATA.d3pieClearSelection,\n    hasToolboxPanAndZoom: false,\n    xssRegexIgnore: new RegExp(\"^/api/v1/data.result.data$\"),\n    format: \"json\",\n    hasLegend: () => false,\n    options: () => \"objectrows|ms\",\n    // autoresize(state) {\n    //   void (state)\n    //   return false\n    // },\n    // max_updates_to_recreate(state) {\n    //   void (state)\n    //   return 5000\n    // },\n    trackColors: false,\n    pixelsPerPoint: () => 15,\n    containerClass: () => \"netdata-container\",\n  },\n  // d3: {\n  //   initialize: window.NETDATA.d3Initialize,\n  //   create: window.NETDATA.d3ChartCreate,\n  //   update: window.NETDATA.d3ChartUpdate,\n  //   resize: null,\n  //   setSelection: undefined, // function(state, t) { void(state); return true; },\n  //   clearSelection: undefined, // function(state) { void(state); return true; },\n  //   toolboxPanAndZoom: null,\n  //   initialized: false,\n  //   enabled: true,\n  //   xssRegexIgnore: new RegExp(\"^/api/v1/data\\.result.data$\"),\n  //   format(state) {\n  //     void (state)\n  //     return \"json\"\n  //   },\n  //   options(state) {\n  //     void (state)\n  //     return \"\"\n  //   },\n  //   legend(state) {\n  //     void (state)\n  //     return null\n  //   },\n  //   autoresize(state) {\n  //     void (state)\n  //     return false\n  //   },\n  //   max_updates_to_recreate(state) {\n  //     void (state)\n  //     return 5000\n  //   },\n  //   track_colors(state) {\n  //     void (state)\n  //     return false\n  //   },\n  //   pixels_per_point(state) {\n  //     void (state)\n  //     return 3\n  //   },\n  //   container_class(state) {\n  //     void (state)\n  //     return \"netdata-container\"\n  //   },\n  // },\n  easypiechart: {\n  //   initialize: window.NETDATA.easypiechartInitialize,\n  //   create: window.NETDATA.easypiechartChartCreate,\n  //   update: window.NETDATA.easypiechartChartUpdate,\n  //   resize: null,\n  //   setSelection: window.NETDATA.easypiechartSetSelection,\n  //   clearSelection: window.NETDATA.easypiechartClearSelection,\n    hasToolboxPanAndZoom: false,\n    //   initialized: false,\n    //   enabled: true,\n    xssRegexIgnore: new RegExp(\"^/api/v1/data.result$\"),\n    format: \"array\",\n    options() {\n      return \"absolute\"\n    },\n    hasLegend() {\n      return false\n    },\n    //   autoresize(state) {\n    //     void (state)\n    //     return false\n    //   },\n    //   max_updates_to_recreate(state) {\n    //     void (state)\n    //     return 5000\n    //   },\n    trackColors: true,\n    pixelsPerPoint: () => 3,\n    aspectRatio: 100,\n    containerClass: () => \"netdata-container-easypiechart\",\n  },\n  gauge: {\n    // initialize: window.NETDATA.gaugeInitialize,\n    // create: window.NETDATA.gaugeChartCreate,\n    // update: window.NETDATA.gaugeChartUpdate,\n    // resize: null,\n    // setSelection: window.NETDATA.gaugeSetSelection,\n    // clearSelection: window.NETDATA.gaugeClearSelection,\n    hasToolboxPanAndZoom: false,\n    // initialized: false,\n    // enabled: true,\n    xssRegexIgnore: new RegExp(\"^/api/v1/data.result$\"),\n    format: \"array\",\n    options: () => \"absolute\",\n    hasLegend: () => false,\n    // autoresize(state) {\n    //   void (state)\n    //   return false\n    // },\n    // max_updates_to_recreate(state) {\n    //   void (state)\n    //   return 5000\n    // },\n    trackColors: true,\n    pixelsPerPoint: () => 3,\n    aspectRatio: 60,\n    containerClass: () => \"netdata-container-gauge\",\n  },\n  textonly: {\n    // autoresize(state) {\n    //   void (state)\n    //   return false\n    // },\n    containerClass: () => \"netdata-container\",\n    // create: window.NETDATA.textOnlyCreate,\n    // enabled: true,\n    format: \"array\",\n    // initialized: true,\n    // initialize(callback) {\n    //   callback()\n    // },\n    hasLegend: () => false,\n    // max_updates_to_recreate(state) {\n    //   void (state)\n    //   return 5000\n    // },\n    options: () => \"absolute\",\n    pixelsPerPoint: () => 3,\n    trackColors: false,\n    // update: window.NETDATA.textOnlyUpdate,\n    xssRegexIgnore: new RegExp(\"^/api/v1/data.result$\"),\n  },\n  groupbox: {\n    containerClass: () => \"netdata-container\",\n    hasLegend: () => false,\n    options: () => \"absolute\",\n    format: \"json\",\n    trackColors: false,\n    pixelsPerPoint: () => 3,\n    xssRegexIgnore: new RegExp(\"^/api/v1/data.result$\"),\n  },\n}\n","import { Attributes } from \"domains/chart/utils/transformDataAttributes\"\nimport { chartLibrariesSettings } from \"domains/chart/utils/chartLibrariesSettings\"\n\nexport const getChartURLOptions = (\n  attributes: Attributes, shouldEliminateZeroDimensions: boolean,\n) => {\n  const {\n    appendOptions,\n    overrideOptions,\n  } = attributes\n  let ret = \"\"\n\n  ret += overrideOptions\n    ? overrideOptions.toString()\n    : chartLibrariesSettings[attributes.chartLibrary].options(attributes)\n\n  if (typeof appendOptions === \"string\") {\n    ret += `|${encodeURIComponent(appendOptions)}`\n  }\n\n  ret += \"|jsonwrap\"\n\n  if (shouldEliminateZeroDimensions) {\n    ret += \"|nonzero\"\n  }\n\n  if (attributes.dimensionsAggrMethod === \"sum-of-abs\"\n  || (!attributes.dimensionsAggrMethod && attributes.groupBy && attributes.groupBy !== \"dimension\")\n  ) {\n    ret += \"|absolute\"\n  }\n\n  return ret\n}\n","export const BIGGEST_INTERVAL_NUMBER = 2 ** 31 - 1\n","import React from \"react\"\n\nimport { Icon } from \"components/icon\"\n\ninterface Props {\n  containerNode: HTMLElement\n  hasEmptyData: boolean\n}\n\nexport const Loader = ({\n  containerNode,\n  hasEmptyData,\n}: Props) => {\n  // below is 90% of original logic.\n  // since it rerenders when IntersectionObserver turns the chart back on,\n  // it's not that important to detect screen height and container sizes changes\n  const screenHeight = window.screen.height\n\n  // normally we want a font size, as tall as the element\n  let h = containerNode.clientHeight\n\n  // but give it some air, 20% let's say, or 5 pixels min\n  const lost = Math.max(h * 0.2, 5)\n  h -= lost\n\n  // center the text, vertically\n  let paddingTop = (lost - 5) / 2\n\n  // but check the width too\n  // it should fit 10 characters in it\n  const w = containerNode.clientWidth / 10\n  if (h > w) {\n    paddingTop += (h - w) / 2\n    h = w\n  }\n\n  // and don't make it too huge\n  // 5% of the screen size is good\n  if (h > screenHeight / 20) {\n    paddingTop += (h - (screenHeight / 20)) / 2\n    h = screenHeight / 20\n  }\n\n  const label = hasEmptyData ? \" empty\" : \" netdata\"\n  const iconType = hasEmptyData ? \"noData\" : \"loading\"\n\n  return (\n    <div\n      className=\"netdata-message icon\"\n      style={{\n        fontSize: h,\n        paddingTop,\n      }}\n    >\n      <Icon iconType={iconType} />\n      {label}\n    </div>\n  )\n}\n","type GetPanAndZoomStep = (event: React.MouseEvent) => number\nexport const getPanAndZoomStep: GetPanAndZoomStep = (event) => {\n  if (event.ctrlKey) {\n    return window.NETDATA.options.current.pan_and_zoom_factor\n      * window.NETDATA.options.current.pan_and_zoom_factor_multiplier_control\n  } if (event.shiftKey) {\n    return window.NETDATA.options.current.pan_and_zoom_factor\n      * window.NETDATA.options.current.pan_and_zoom_factor_multiplier_shift\n  } if (event.altKey) {\n    return window.NETDATA.options.current.pan_and_zoom_factor\n      * window.NETDATA.options.current.pan_and_zoom_factor_multiplier_alt\n  }\n  return window.NETDATA.options.current.pan_and_zoom_factor\n}\n","export const safeEqualCheck = (a: unknown, b: unknown) => {\n  if (a === b) {\n    return true\n  }\n  return Number.isNaN(a as number) && Number.isNaN(b as number)\n}\n","import { identity } from \"ramda\"\nimport { useCallback, useState, useMemo, useRef } from \"react\"\n\nimport { useSelector } from \"store/redux-separate-context\"\nimport { selectTemperatureSetting, selectSecondsAsTimeSetting } from \"domains/global/selectors\"\nimport { unitsConversionCreator } from \"utils/units-conversion\"\nimport { safeEqualCheck } from \"utils/safe-equal-check\"\n\nimport { ChartData } from \"../chart-types\"\nimport { Attributes } from \"./transformDataAttributes\"\n\ntype Converter = (v: number) => number | string\n// only time units are converted into strings, the rest are numbers\n\n// todo - memoization similar to the one as in old dashboard, but probably not needed\nconst formattersFixed: any[] = []\nconst formattersZeroBased: any[] = []\nconst fastNumberFormat = (min: number, max: number) => {\n  const key = max\n  if (min === max) {\n    if (typeof formattersFixed[key] === \"undefined\") {\n      formattersFixed[key] = new Intl.NumberFormat(undefined, {\n        useGrouping: true,\n        minimumFractionDigits: min,\n        maximumFractionDigits: max,\n      })\n    }\n\n    return formattersFixed[key]\n  }\n  if (min === 0) {\n    if (typeof formattersZeroBased[key] === \"undefined\") {\n      formattersZeroBased[key] = new Intl.NumberFormat(undefined, {\n        useGrouping: true,\n        minimumFractionDigits: min,\n        maximumFractionDigits: max,\n      })\n    }\n\n    return formattersZeroBased[key]\n  }\n  // (old dashboard comment)\n  // this is never used\n  // it is added just for completeness\n  return new Intl.NumberFormat(undefined, {\n    useGrouping: true,\n    minimumFractionDigits: min,\n    maximumFractionDigits: max,\n  })\n}\n\nconst getLegendFormatValue =\n  (\n    convertUnits: Converter,\n    intlNumberFormat: Intl.NumberFormat | null,\n    valueDecimalDetail: number\n  ) =>\n  (value: number | string | null) => {\n    if (typeof value !== \"number\") {\n      return \"-\"\n    }\n\n    const convertedValue = convertUnits(value)\n    if (typeof convertedValue !== \"number\") {\n      return convertedValue\n    }\n\n    if (intlNumberFormat !== null) {\n      return intlNumberFormat.format(convertedValue)\n    }\n\n    let dmin\n    let dmax\n    if (valueDecimalDetail !== -1) {\n      dmin = valueDecimalDetail\n      dmax = valueDecimalDetail\n    } else {\n      dmin = 0\n      const abs = convertedValue < 0 ? -convertedValue : convertedValue\n      if (abs > 1000) {\n        dmax = 0\n      } else if (abs > 10) {\n        dmax = 1\n      } else if (abs > 1) {\n        dmax = 2\n      } else if (abs > 0.1) {\n        dmax = 2\n      } else if (abs > 0.01) {\n        dmax = 4\n      } else if (abs > 0.001) {\n        dmax = 5\n      } else if (abs > 0.0001) {\n        dmax = 6\n      } else {\n        dmax = 7\n      }\n    }\n\n    return fastNumberFormat(dmin, dmax).format(convertedValue)\n  }\n\ntype LegendFormatValue = (value: string | number | null) => string | number\n\ninterface Arguments {\n  attributes: Attributes\n  data: ChartData\n  units: string\n  unitsCommon: string | undefined\n  unitsDesired: string\n  uuid: string\n}\nexport const useFormatters = ({\n  attributes,\n  data,\n  units,\n  unitsCommon,\n  unitsDesired,\n  uuid,\n}: Arguments) => {\n  const temperatureSetting = useSelector(selectTemperatureSetting)\n  const secondsAsTimeSetting = useSelector(selectSecondsAsTimeSetting)\n\n  // previously _unitsConversion\n  const [convertUnits, setConvertUnits] = useState<Converter>(() => identity)\n\n  // probably can also be removed\n  const [min, setMin] = useState<number>()\n  const [max, setMax] = useState<number>()\n\n  // todo most of this state is not needed, that hook can be refactored\n  const [unitsCurrent, setUnitsCurrent] = useState<string>(units)\n\n  const [decimals, setDecimals] = useState<number>(-1)\n  const [intlNumberFormat, setIntlNumberFormat] = useState<Intl.NumberFormat | null>(null)\n\n  const {\n    // \"valueDecimalDetail\" in old app\n    decimalDigits = -1,\n  } = attributes\n\n  const legendFormatValue: LegendFormatValue = useMemo(\n    () => getLegendFormatValue(convertUnits, intlNumberFormat, decimalDigits),\n    [convertUnits, decimalDigits, intlNumberFormat]\n  )\n\n  const legendFormatValueRef = useRef(legendFormatValue)\n  const updateLegendFormatValueRef = (\n    newConvertUnits: Converter,\n    newIntlNumberFormat: any,\n    newDecimalDigits: any\n  ) => {\n    legendFormatValueRef.current = getLegendFormatValue(\n      newConvertUnits,\n      newIntlNumberFormat,\n      newDecimalDigits\n    )\n  }\n\n  const legendFormatValueDecimalsFromMinMax = useCallback(\n    (newMin: number, newMax: number) => {\n      if (safeEqualCheck(min, newMin) && safeEqualCheck(max, newMax)) {\n        return legendFormatValueRef.current\n      }\n      // we should call the convertUnits-creation only when original app was doing this\n      // so we don't get new updates in improper places\n      setMin(newMin)\n      setMax(newMax)\n\n      const newConvertUnits = unitsConversionCreator.get(\n        uuid,\n        newMin,\n        newMax,\n        units,\n        unitsDesired,\n        unitsCommon,\n        switchedUnits => {\n          setUnitsCurrent(switchedUnits)\n          // that.legendSetUnitsString(that.units_current);\n          // that.legendSetUnitsString just populates some DOM with unitsCurrent\n          // on all occurrences just take the unitsCurrent from this state\n        },\n        temperatureSetting,\n        secondsAsTimeSetting\n      )\n\n      // as function, so useState() interprets it properly\n      setConvertUnits(() => newConvertUnits)\n\n      const convertedMin = newConvertUnits(newMin)\n      const convertedMax = newConvertUnits(newMax)\n\n      // if number is returned, we format it!!!!\n      if (typeof convertedMin !== \"number\" || typeof convertedMax !== \"number\") {\n        updateLegendFormatValueRef(newConvertUnits, intlNumberFormat, decimalDigits)\n        return legendFormatValueRef.current\n      }\n\n      let newDecimals\n\n      if (data.min === data.max) {\n        // it is a fixed number, let the visualizer decide based on the value\n        newDecimals = -1\n      } else if (decimalDigits !== -1) {\n        // there is an override\n        newDecimals = decimalDigits\n      } else {\n        // ok, let's calculate the proper number of decimal points\n        let delta\n\n        if (convertedMin === convertedMax) {\n          delta = Math.abs(convertedMin)\n        } else {\n          delta = Math.abs(convertedMax - convertedMin)\n        }\n\n        if (delta > 1000) {\n          newDecimals = 0\n        } else if (delta > 10) {\n          newDecimals = 1\n        } else if (delta > 1) {\n          newDecimals = 2\n        } else if (delta > 0.1) {\n          newDecimals = 2\n        } else if (delta > 0.01) {\n          newDecimals = 4\n        } else if (delta > 0.001) {\n          newDecimals = 5\n        } else if (delta > 0.0001) {\n          newDecimals = 6\n        } else {\n          newDecimals = 7\n        }\n      }\n\n      let newIntlNumberFormat = intlNumberFormat\n\n      if (newDecimals !== decimals) {\n        if (newDecimals < 0) {\n          newIntlNumberFormat = null\n        } else {\n          newIntlNumberFormat = fastNumberFormat(newDecimals, newDecimals)\n        }\n        setIntlNumberFormat(() => newIntlNumberFormat)\n        setDecimals(newDecimals)\n      }\n      updateLegendFormatValueRef(newConvertUnits, newIntlNumberFormat, newDecimals)\n      return legendFormatValueRef.current\n    },\n    [\n      decimals,\n      decimalDigits,\n      min,\n      max,\n      uuid,\n      temperatureSetting,\n      units,\n      unitsDesired,\n      unitsCommon,\n      secondsAsTimeSetting,\n      data.min,\n      data.max,\n      intlNumberFormat,\n    ]\n  )\n\n  return {\n    legendFormatValue,\n    legendFormatValueDecimalsFromMinMax,\n    unitsCurrent,\n  }\n}\n","const defaultColor = {\n  r: 255,\n  g: 0,\n  b: 0,\n}\n\ntype ColorHex2Rgb = (hex: string) => {\n  r: number,\n  g: number,\n  b: number\n}\nexport const colorHex2Rgb: ColorHex2Rgb = (hex) => {\n  // Expand shorthand form (e.g. \"03F\") to full form (e.g. \"0033FF\")\n  const shorthandRegex = /^#?([a-f\\d])([a-f\\d])([a-f\\d])$/i\n  if (!hex) {\n    return defaultColor\n  }\n  const hexFull = hex.replace(shorthandRegex, (m, r, g, b) => r + r + g + g + b + b)\n\n  const result = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(hexFull)\n  if (!result) {\n    console.warn(\"wrong color format:\", hex) // eslint-disable-line no-console\n  }\n  return result\n    ? {\n      r: parseInt(result[1], 16),\n      g: parseInt(result[2], 16),\n      b: parseInt(result[3], 16),\n    } : defaultColor\n}\n","import styled from \"styled-components\"\nimport { getSizeBy } from \"@netdata/netdata-ui\"\n\nexport const LegendContainer = styled.div`\n  margin-bottom: ${getSizeBy(3)};\n  padding-left: 35px;\n`\n\nexport const LegendFirstRow = styled.div`\n  margin-top: 4px;\n  display: flex;\n  justify-content: space-between;\n`\n\nexport const LegendSecondRow = styled.div`\n  margin-top: 4px;\n  display: flex;\n  justify-content: space-between;\n`\n\nexport const LegendUnit = styled.div`\n`\n\nexport const DateTimeSeparator = styled.span`\n  margin: 0 3px;\n`\n\nexport const LegendItems = styled.div`\n  display: flex;\n  flex-wrap: wrap;\n  overflow: auto;\n  max-height: 80px;\n`\n\nexport const DimensionItem = styled.div<{ color: string, isDisabled: boolean }>`\n  display: flex;\n  align-items: center;\n  color: ${({ color }) => color};\n  margin-right: 12px;\n  cursor: pointer;\n  opacity: ${({ isDisabled }) => (isDisabled ? 0.3 : null)};\n  user-select: none;\n  font-size: 11px;\n  &:focus {\n    outline: none;\n  }\n`\n\n// toolbox is based on \"absolute\", so to make sure it's not put on top of dimension-item\n// let's put a transparent block as last in dimension-items container. Toolbox will soon be moved\n// to other place so it's temporary\nexport const DimensionItemToolboxPlaceholder = styled.div`\n  width: 140px;\n  height: 20px;\n`\n\nexport const DimensionIcon = styled.div<{ color: string }>`\n  width: 14px;\n  height: 7px;\n  border-radius: 4px;\n  overflow: hidden;\n  background-color: ${({ color }) => color};\n`\n\nexport const DimensionLabel = styled.span`\n  margin-left: 3px;\n`\n\nexport const DimensionValue = styled.span`\n  margin-left: 5px;\n  min-width: 30px;\n`\n\nexport const ToolboxContainer = styled.div`\n  position: relative;\n  touch-action: none;\n`\n","import React, { useCallback } from \"react\"\nimport { createSelector } from \"reselect\"\nimport { useSelector } from \"store/redux-separate-context\"\nimport { selectChartData } from \"domains/chart/selectors\"\nimport { DimensionLabel } from \"./chart-legend-bottom.styled\"\n\nconst emptyObject = {}\n\nconst selector = createSelector(\n  selectChartData,\n  ({ dimension_names: dimensionNames, keys = emptyObject }) => ({\n    dimensionNames,\n    keys,\n  })\n)\n\nconst LegendText = ({ id, index }) => {\n  const { dimensionNames, keys } = useSelector(useCallback(state => selector(state, { id }), [id]))\n  const { chart, node } = keys\n\n  if (chart && node && Object.keys(keys).length === 2) {\n    return (\n      <DimensionLabel>\n        {chart[index]}@{node[index]}\n      </DimensionLabel>\n    )\n  }\n\n  const name = dimensionNames[index]\n\n  return <DimensionLabel>{name}</DimensionLabel>\n}\n\nexport default LegendText\n","import React, { Fragment, useRef, useEffect, useCallback } from \"react\"\nimport classNames from \"classnames\"\nimport { useSelector } from \"store/redux-separate-context\"\nimport { selectChartData } from \"domains/chart/selectors\"\n\nimport { colorHex2Rgb } from \"utils/color-hex-2-rgb\"\nimport { useDateTime } from \"utils/date-time\"\n\nimport { legendResolutionTooltip, legendPluginModuleString } from \"../utils/legend-utils\"\n\nimport { ChartMetadata } from \"../chart-types\"\nimport LegendText from \"./legendText\"\n\ninterface Props {\n  chartUuid: string\n  chartMetadata: ChartMetadata\n  chartLibrary: string\n  colors: {\n    [key: string]: string\n  }\n  hoveredRow: number\n  hoveredX: number | null\n  legendFormatValue: (value: number | string | null) => (number | string)\n  onDimensionClick: (clickedDimensionName: string, event: React.MouseEvent) => void\n  selectedDimensions: string[]\n  showLatestOnBlur: boolean\n  unitsCurrent: string\n  viewBefore: number\n}\n\nexport const ChartLegendRight = ({\n  chartUuid,\n  chartMetadata,\n  chartLibrary,\n  colors,\n  hoveredRow,\n  hoveredX,\n  legendFormatValue,\n  onDimensionClick,\n  selectedDimensions,\n  showLatestOnBlur,\n  unitsCurrent,\n  viewBefore,\n}: Props) => {\n  const chartData = useSelector(\n    useCallback((state: any) => selectChartData(state, { id: chartUuid }), [chartUuid])\n  )\n  const { dimension_names: dimensionNames, dimension_ids: dimensionIds } = chartData\n\n  // todo handle also this case:\n  // const netdataLast = chartData.last_entry * 1000\n  // const dataUpdateEvery = chartData.view_update_every * 1000\n  // showUndefined = Math.abs(netdataLast - viewBefore) > dataUpdateEvery\n  // (showUndefined also when difference between last and before is bigger than granularity)\n  const showUndefined = hoveredRow === -1 && !showLatestOnBlur\n\n  // todo support timezone\n  const legendDate = new Date(hoveredX || viewBefore)\n\n  // todo make a possibility to add chartLegened when there's not chartData\n  // (if this situation is possible)\n\n  // @ts-ignore ignoring because options.current has inconsistent structure\n  const colorFillOpacity = window.NETDATA.options.current[\n    `color_fill_opacity_${chartMetadata.chart_type}`\n  ]\n\n  const { localeDateString, localeTimeString } = useDateTime()\n\n  const scrollbarRef = useRef(null)\n  useEffect(() => {\n    if (scrollbarRef.current) {\n      window.Ps.initialize(scrollbarRef.current, {\n        wheelSpeed: 0.2,\n        wheelPropagation: true,\n        swipePropagation: true,\n        minScrollbarLength: null,\n        maxScrollbarLength: null,\n        useBothWheelAxes: false,\n        suppressScrollX: true,\n        suppressScrollY: false,\n        scrollXMarginOffset: 0,\n        scrollYMarginOffset: 0,\n        theme: \"default\",\n      })\n    }\n  }, [scrollbarRef])\n\n  return (\n    <div className={classNames(\n      \"netdata-chart-legend\",\n      `netdata-${chartLibrary}-legend`,\n    )}\n    >\n      <span\n        className=\"netdata-legend-title-date\"\n        title={legendPluginModuleString(true, chartMetadata)}\n      >\n        {showUndefined\n          ? legendPluginModuleString(false, chartMetadata)\n          : localeDateString(legendDate)}\n      </span>\n      <br />\n      <span\n        className=\"netdata-legend-title-time\"\n        title={legendResolutionTooltip(chartData, chartMetadata)}\n      >\n        {showUndefined\n          ? chartMetadata.context.toString()\n          : localeTimeString(legendDate)}\n      </span>\n      <br />\n      <span className=\"netdata-legend-title-units\">{unitsCurrent}</span>\n      <br />\n      <div className=\"netdata-legend-series\" ref={scrollbarRef}>\n        <div className=\"netdata-legend-series-content\">\n          {dimensionIds.map((dimensionId, i) => {\n            const dimensionName = dimensionNames[i]\n            // todo dimension could be a separate component\n            const color = colors[dimensionName]\n            const rgb = colorHex2Rgb(color)\n\n            const isSelected = selectedDimensions.length === 0\n              || selectedDimensions.includes(dimensionName)\n\n            let value\n            if (showUndefined) {\n              value = null\n            } else if (hoveredRow !== -1) {\n              const hoveredValueArray = chartData.result.data[hoveredRow]\n              // [timestamp, valueDim1, valueDim2, ...]\n              value = hoveredValueArray ? hoveredValueArray[i + 1] : null\n            } else {\n              value = chartData.view_latest_values[i]\n            }\n\n            return (\n              <Fragment key={dimensionId}>\n                {i !== 0 && <br />}\n                {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events */}\n                <span\n                  title={dimensionName}\n                  className={classNames(\n                    \"netdata-legend-name\",\n                    isSelected ? \"selected\" : \"not-selected\",\n                  )}\n                  onClick={(event) => {\n                    onDimensionClick(dimensionName, event)\n                  }}\n                  role=\"button\"\n                  style={{ color }}\n                  tabIndex={0}\n                >\n                  <table\n                    className={`netdata-legend-name-table-${chartMetadata.chart_type}`}\n                    style={{\n                      backgroundColor: `rgba(${rgb.r},${rgb.g},${rgb.b},${colorFillOpacity})`,\n                    }}\n                  >\n                    <tbody>\n                      <tr className=\"netdata-legend-name-tr\">\n                        <td className=\"netdata-legend-name-td\" />\n                      </tr>\n                    </tbody>\n                  </table>\n                  {\" \"}\n                  <LegendText id={chartUuid} index={i} />\n                </span>\n                {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events */}\n                <span\n                  title={dimensionName}\n                  className={classNames(\n                    \"netdata-legend-value\",\n                    !isSelected && \"hidden\",\n                  )}\n                  onClick={(event) => {\n                    onDimensionClick(dimensionName, event)\n                  }}\n                  role=\"button\"\n                  style={{ color }} // omitted !important during refractor, react doesn't support it\n                  tabIndex={0}\n                >\n                  {legendFormatValue(\n                    value,\n                  )}\n                </span>\n              </Fragment>\n            )\n          })}\n        </div>\n      </div>\n    </div>\n  )\n}\n","import React, { useCallback } from \"react\"\nimport { useDateTime } from \"utils/date-time\"\nimport { useSelector } from \"store/redux-separate-context\"\nimport { selectChartData } from \"domains/chart/selectors\"\nimport { legendPluginModuleString, legendResolutionTooltip } from \"domains/chart/utils/legend-utils\"\nimport { ChartMetadata } from \"../chart-types\"\nimport LegendText from \"./legendText\"\nimport * as S from \"./chart-legend-bottom.styled\"\ninterface Props {\n  chartUuid: string\n  chartMetadata: ChartMetadata\n  chartLibrary: string\n  colors: {\n    [key: string]: string\n  }\n  hoveredRow: number\n  hoveredX: number | null\n  legendFormatValue: (value: number | string | null) => number | string\n  onDimensionClick: (clickedDimensionName: string, event: React.MouseEvent) => void\n  selectedDimensions: string[]\n  showLatestOnBlur: boolean\n  unitsCurrent: string\n  viewBefore: number\n  legendToolbox: JSX.Element\n  resizeHandler: React.ReactNode\n}\n\nexport const ChartTimeframe = ({\n  chartMetadata,\n  showUndefined,\n  hoveredX,\n  viewBefore,\n  chartData,\n}: any) => {\n  const { localeDateString, localeTimeString } = useDateTime()\n\n  const legendDate = new Date(hoveredX || viewBefore)\n\n  return (\n    <div>\n      <span title={legendPluginModuleString(true, chartMetadata)}>\n        {showUndefined\n          ? legendPluginModuleString(false, chartMetadata)\n          : localeDateString(legendDate)}\n      </span>\n      <S.DateTimeSeparator>|</S.DateTimeSeparator>\n      <span title={legendResolutionTooltip(chartData, chartMetadata)}>\n        {showUndefined ? chartMetadata.context.toString() : localeTimeString(legendDate)}\n      </span>\n    </div>\n  )\n}\n\nexport const ChartLegendBottom = ({\n  chartUuid,\n  chartMetadata,\n  colors,\n  hoveredRow,\n  hoveredX,\n  legendFormatValue,\n  onDimensionClick,\n  selectedDimensions,\n  showLatestOnBlur,\n  unitsCurrent,\n  viewBefore,\n  legendToolbox,\n  resizeHandler,\n}: Props) => {\n  const showUndefined = hoveredRow === -1 && !showLatestOnBlur\n  const chartData = useSelector(\n    useCallback((state: any) => selectChartData(state, { id: chartUuid }), [chartUuid])\n  )\n  const { dimension_names: dimensionNames, dimension_ids: dimensionIds } = chartData\n\n  return (\n    <S.LegendContainer>\n      <S.LegendFirstRow>\n        <S.LegendUnit>{unitsCurrent}</S.LegendUnit>\n        <ChartTimeframe\n          chartMetadata={chartMetadata}\n          showUndefined={showUndefined}\n          hoveredX={hoveredX}\n          viewBefore={viewBefore}\n          chartData={chartData}\n        />\n      </S.LegendFirstRow>\n      <S.LegendSecondRow>\n        <S.LegendItems>\n          {dimensionIds.map((dimensionId, i) => {\n            const dimensionName = dimensionNames[i]\n            const color = colors[dimensionName]\n\n            const isSelected =\n              selectedDimensions.length === 0 || selectedDimensions.includes(dimensionName)\n\n            let value\n            if (showUndefined) {\n              value = null\n            } else if (hoveredRow !== -1) {\n              const hoveredValueArray = chartData.result.data[hoveredRow]\n              // [timestamp, valueDim1, valueDim2, ...]\n              value = hoveredValueArray ? hoveredValueArray[i + 1] : null\n            } else {\n              value = chartData.view_latest_values[i]\n            }\n            return (\n              <S.DimensionItem\n                color={color}\n                onClick={event => {\n                  onDimensionClick(dimensionName, event)\n                }}\n                role=\"button\"\n                tabIndex={0}\n                isDisabled={!isSelected}\n                key={dimensionId}\n              >\n                <S.DimensionIcon title={dimensionName} color={color} />\n                <LegendText id={chartUuid} index={i} />\n                <S.DimensionValue>{isSelected && legendFormatValue(value)}</S.DimensionValue>\n              </S.DimensionItem>\n            )\n          })}\n          <S.DimensionItemToolboxPlaceholder />\n        </S.LegendItems>\n        <S.ToolboxContainer>\n          {legendToolbox}\n          {resizeHandler}\n        </S.ToolboxContainer>\n      </S.LegendSecondRow>\n    </S.LegendContainer>\n  )\n}\n","import React, { useCallback } from \"react\"\n\nimport { useSelector } from \"store/redux-separate-context\"\nimport { selectChartData } from \"domains/chart/selectors\"\nimport { getNewSelectedDimensions } from \"domains/chart/utils/legend-utils\"\nimport { Attributes } from \"../utils/transformDataAttributes\"\nimport { ChartMetadata } from \"../chart-types\"\n\nimport { ChartLegendRight } from \"./chart-legend-right\"\nimport { ChartLegendBottom } from \"./chart-legend-bottom\"\n\ninterface Props {\n  attributes: Attributes\n  chartUuid: string\n  chartMetadata: ChartMetadata\n  chartLibrary: string\n  colors: {\n    [key: string]: string\n  }\n  hoveredRow: number\n  hoveredX: number | null\n  legendFormatValue: (value: number | string | null) => number | string\n  selectedDimensions: string[]\n  setSelectedDimensions: (selectedDimensions: string[]) => void\n  showLatestOnBlur: boolean\n  unitsCurrent: string\n  viewBefore: number\n  legendToolbox: JSX.Element\n  resizeHandler: React.ReactNode\n}\n\nexport const ChartLegend = ({\n  attributes,\n  chartUuid,\n  chartMetadata,\n  chartLibrary,\n  colors,\n  hoveredRow,\n  hoveredX,\n  legendFormatValue,\n  selectedDimensions,\n  setSelectedDimensions,\n  showLatestOnBlur,\n  unitsCurrent,\n  viewBefore,\n  legendToolbox,\n  resizeHandler,\n}: Props) => {\n  const dimension_names = useSelector(\n    useCallback((state: any) => selectChartData(state, { id: chartUuid }).dimension_names, [\n      chartUuid,\n    ])\n  )\n\n  const onDimensionClick = (clickedDimensionName: string, event: React.MouseEvent) => {\n    event.preventDefault()\n    const isModifierKeyPressed = event.shiftKey || event.ctrlKey\n    const newSelectedDimensions = getNewSelectedDimensions({\n      allDimensions: dimension_names,\n      selectedDimensions,\n      clickedDimensionName,\n      isModifierKeyPressed,\n    })\n    setSelectedDimensions(newSelectedDimensions)\n  }\n\n  if (attributes.legendPosition === \"bottom\") {\n    return (\n      <ChartLegendBottom\n        chartUuid={chartUuid}\n        chartLibrary={chartLibrary}\n        chartMetadata={chartMetadata}\n        colors={colors}\n        hoveredRow={hoveredRow}\n        hoveredX={hoveredX}\n        legendFormatValue={legendFormatValue}\n        onDimensionClick={onDimensionClick}\n        selectedDimensions={selectedDimensions}\n        showLatestOnBlur={showLatestOnBlur}\n        unitsCurrent={unitsCurrent}\n        viewBefore={viewBefore}\n        legendToolbox={legendToolbox}\n        resizeHandler={resizeHandler}\n      />\n    )\n  }\n\n  return (\n    <ChartLegendRight\n      chartUuid={chartUuid}\n      chartLibrary={chartLibrary}\n      chartMetadata={chartMetadata}\n      colors={colors}\n      hoveredRow={hoveredRow}\n      hoveredX={hoveredX}\n      legendFormatValue={legendFormatValue}\n      onDimensionClick={onDimensionClick}\n      selectedDimensions={selectedDimensions}\n      showLatestOnBlur={showLatestOnBlur}\n      unitsCurrent={unitsCurrent}\n      viewBefore={viewBefore}\n    />\n  )\n}\n","import React from \"react\"\n\nimport { ToolboxButton } from \"./toolbox-button\"\n\ntype ClickCallback = (event: React.MouseEvent) => void\ninterface Props {\n  onToolboxLeftClick: ClickCallback\n  onToolboxRightClick: ClickCallback\n  onToolboxZoomInClick: ClickCallback\n  onToolboxZoomOutClick: ClickCallback\n}\nexport const LegendToolbox = ({\n  onToolboxLeftClick,\n  onToolboxRightClick,\n  onToolboxZoomInClick,\n  onToolboxZoomOutClick,\n}: Props) => (\n  <div className=\"netdata-legend-toolbox\">\n    <ToolboxButton\n      className=\"netdata-legend-toolbox-button\"\n      onClick={onToolboxLeftClick}\n      iconType=\"left\"\n      popoverTitle=\"Pan Left\"\n      popoverContent=\"Pan the chart to the left. You can also <b>drag it</b> with your mouse or your\n       finger (on touch devices).<br/><small>Help can be disabled from the settings.</small>\"\n    />\n    <ToolboxButton\n      className=\"netdata-legend-toolbox-button\"\n      onClick={onToolboxRightClick}\n      iconType=\"right\"\n      popoverTitle=\"Pan Right\"\n      popoverContent=\"Pan the chart to the right. You can also <b>drag it</b> with your mouse or\n       your finger (on touch devices).<br/><small>Help can be disabled from the settings.</small>\"\n    />\n    <ToolboxButton\n      className=\"netdata-legend-toolbox-button\"\n      onClick={onToolboxZoomInClick}\n      iconType=\"zoomIn\"\n      popoverTitle=\"Chart Zoom In\"\n      popoverContent=\"Zoom in the chart. You can also press SHIFT and select an area of the chart,\n       or press SHIFT or ALT and use the mouse wheel or 2-finger touchpad scroll to zoom in or out.\n       <br/><small>Help can be disabled from the settings.</small>\"\n    />\n    <ToolboxButton\n      className=\"netdata-legend-toolbox-button\"\n      onClick={onToolboxZoomOutClick}\n      iconType=\"zoomOut\"\n      popoverTitle=\"Chart Zoom Out\"\n      popoverContent=\"Zoom out the chart. You can also press SHIFT or ALT and use the mouse wheel,\n       or 2-finger touchpad scroll to zoom in or out.<br/><small>Help can be disabled from the\n        settings.</small>\"\n    />\n  </div>\n)\n","/* eslint-disable no-nested-ternary */\nimport { always, memoizeWith } from \"ramda\"\nimport Color from \"color\"\n\nimport { ChartMetadata, DygraphData } from \"domains/chart/chart-types\"\nimport { Attributes } from \"domains/chart/utils/transformDataAttributes\"\nimport { ChartLibraryConfig } from \"domains/chart/utils/chartLibrariesSettings\"\n\nexport const getDataForFakeStacked = (\n  data: number[][],\n  dimensionsVisibility: boolean[],\n): number[][] => data.map((point) => {\n  const [timestamp, ...values] = point\n  const rest: number[] = []\n  let currentMin = 0\n  let currentMax = 0\n  values\n    .map((value, i) => ({ isVisible: dimensionsVisibility[i], value }))\n    // reverse because first dimensions should be \"on top\" (at least positive ones)\n    .slice().reverse()\n    .forEach(({ isVisible, value }) => {\n      if (!isVisible) {\n        rest.push(0) // push with value '0'. it won't be visible but needs to be present in array\n        return\n      }\n      if (value >= 0) {\n        currentMax += value\n        rest.push(currentMax)\n      } else {\n        currentMin += value\n        rest.push(currentMin)\n      }\n    })\n  return [\n    timestamp,\n    ...rest,\n  ]\n})\n\nconst isPercentage = (unit: string) => unit === \"percentage\"\n  || unit === \"percent\"\n  || unit.indexOf(\"%\") !== -1\n\nexport const getDygraphChartType = (\n  attributes: Attributes, chartData: DygraphData, chartMetadata: ChartMetadata,\n  chartSettings: ChartLibraryConfig,\n) => {\n  const isLogScale = (chartSettings.isLogScale as ((a: Attributes) => boolean))(attributes)\n  const {\n    dygraphType: dygraphRequestedType = chartMetadata.chart_type,\n    groupBy,\n  } = attributes\n\n  if (groupBy && groupBy !== \"dimension\" && isPercentage(chartMetadata.units)) {\n    return \"line\"\n  }\n\n  // corresponds to state.tmp.dygraph_chart_type in old app\n  let dygraphChartType = dygraphRequestedType\n  if (dygraphChartType === \"stacked\" && chartData.dimensions === 1) {\n    dygraphChartType = \"area\"\n  }\n  if (dygraphChartType === \"stacked\" && isLogScale) {\n    dygraphChartType = \"area\"\n  }\n  return dygraphChartType\n}\n\nconst getBackgroundColor = memoizeWith(\n  always(\"true\"),\n  () => Color(window.NETDATA.themes.current.background),\n)\n// when in \"fakeStacked\" mode, we cannot use opacity for fill in charts, because the areas would\n// be visible under each other. So the darkening / whitening needs to be added directly to colors\n// (the colors are too saturated for areas and in stacked mode they were with 0.8 opacity)\nexport const transformColors = (colors: string[]) => (\n  colors.map((color) => Color(color).mix(getBackgroundColor(), 0.2).hex())\n)\n\nexport const getDygraphFillAlpha = (\n  isFakeStacked: boolean, dygraphChartType: string,\n) => (isFakeStacked\n  ? window.NETDATA.options.current.color_fill_opacity_fake_stacked\n  : dygraphChartType === \"stacked\"\n    ? window.NETDATA.options.current.color_fill_opacity_stacked\n    : window.NETDATA.options.current.color_fill_opacity_area)\n\n\n// https://github.com/danvk/dygraphs/blob/master/src/iframe-tarp.js#L1-L23\n// On mouseUp dygraphs put rectangles above all iframes so mouseUp can be properly intercepted.\n// this causes a problem with some analytics iframes that place themselves in regions where they\n// aren't visible anyway (for example hubspot iframe on Cloud), and this creates a problematic\n// horizontal scrollbar to appear. This function filters those \"rectangles\" (tarps) to omit\n// elements with unreachable \"left\" styles\nexport const hackDygraphIFrameTarps = (tarps: HTMLDivElement[]): HTMLDivElement[] => (\n  tarps.filter((element: HTMLDivElement) => {\n    const isOutsideReasonableViewport = Number(element.style.left.replace(\"px\", \"\")) > 10000\n    if (isOutsideReasonableViewport) {\n      element.parentNode!.removeChild(element)\n    }\n    return !isOutsideReasonableViewport\n  })\n)\n","/* eslint-disable react-hooks/exhaustive-deps */\n/* eslint-disable function-paren-newline */\n/* eslint-disable comma-dangle */\nimport React, { useRef, useCallback } from \"react\"\nimport { useToggle } from \"react-use\"\n\nconst useProceededChart = (\n  chartRef: any,\n  propsRef: any\n): [boolean, React.Ref<HTMLElement>, (g: Dygraph) => void] => {\n  const [proceeded, toggleProceeded] = useToggle(false)\n\n  const ref = useRef<HTMLElement>(null)\n\n  const updatePosition = useCallback((g: Dygraph) => {\n    const { x } = g.getArea()\n    const distance = g.toDomXCoord(propsRef.current.chartData.first_entry * 1000)\n    const hasProceeded = distance > x\n    toggleProceeded(hasProceeded)\n\n    if (hasProceeded && ref.current) {\n      const { height } = chartRef.current.getBoundingClientRect()\n      ref.current.style.left = `${x}px`\n      ref.current.style.right = `calc(100% - ${distance}px)`\n      ref.current.style.top = `${height / 2}px`\n    }\n  }, [])\n\n  return [proceeded, ref, updatePosition]\n}\n\nexport default useProceededChart\n","import { useRef } from \"react\"\nimport { useToggle } from \"react-use\"\n\nconst badgeTopMargin = \"40px\"\n\nconst defaultPositionTo = (ref, x, position, topMargin) => {\n  ref.current.style.left = `${x}px`\n  ref.current.style.right = `calc(100% - ${position}px)`\n  ref.current.style.top = topMargin\n}\n\nexport default () => {\n  const [isRendered, toggleIsRendered] = useToggle(false)\n\n  const ref = useRef(null)\n\n  const updatePosition = (isVisible, g, position, positionTo = defaultPositionTo) => {\n    if (!isVisible) {\n      toggleIsRendered(false)\n      return\n    }\n\n    if (ref.current) {\n      toggleIsRendered(true)\n      const { x } = g.getArea()\n\n      positionTo(ref, x, position, badgeTopMargin)\n    }\n  }\n\n  return [isRendered, ref, updatePosition]\n}\n","import React, { forwardRef } from \"react\"\nimport styled from \"styled-components\"\n\nconst Container = styled.div`\n  display: block;\n`\n\nconst ProceededChartDisclaimer = forwardRef((\n  props: React.HTMLAttributes<HTMLElement>,\n  ref: React.Ref<HTMLDivElement>,\n) => (\n  <Container ref={ref} className=\"dygraph__history-tip\" data-testid=\"proceededChartDisclaimer\">\n    <span className=\"dygraph__history-tip-content\">\n      Want to extend your history of real-time metrics?\n      <br />\n      <a href=\"https://learn.netdata.cloud/guides/longer-metrics-storage/\" target=\"_blank\" rel=\"noopener noreferrer\" data-testid=\"proceededChartDisclaimer-configure\">\n        Configure Netdata&apos;s&nbsp;\n        <b>history</b>\n      </a>\n      &nbsp;or use the&nbsp;\n      <a href=\"https://learn.netdata.cloud/docs/agent/database/engine/\" target=\"_blank\" rel=\"noopener noreferrer\" data-testid=\"proceededChartDisclaimer-engine\">DB engine</a>\n      .\n    </span>\n  </Container>\n))\n\nexport default ProceededChartDisclaimer\n","import React, { forwardRef } from \"react\"\nimport styled from \"styled-components\"\n\nconst backgroundColorMap = {\n  WARNING: \"#FFF8E1\",\n  CRITICAL: \"#FFEBEF\",\n  CLEAR: \"#E5F5E8\",\n}\nexport const getBackgroundColor = (status) => backgroundColorMap[status] || null\n\nconst borderColorMap = {\n  WARNING: \"#FFC300\",\n  CRITICAL: \"#F59B9B\",\n  CLEAR: \"#68C47D\",\n}\nexport const getBorderColor = (status) => borderColorMap[status] || null\n\nconst textColorMap = {\n  WARNING: \"#536775\",\n  CRITICAL: \"#FF4136\",\n  CLEAR: \"#00AB44\",\n}\nexport const getColor = (status) => textColorMap[status] || null\n\nconst Container = styled.div`\n  position: absolute;\n  margin-right: 10px;\n  overflow: hidden;\n  pointer-events: none;\n  direction: rtl;\n  z-index: 10; // higher than chart\n`\n\nconst Badge = styled.div`\n  display: inline-block;\n  border-radius: 36px;\n  padding: 2px 12px;\n  background: ${({ background }) => background};\n  border: 1px solid ${({ border }) => border};\n  color: ${({ color }) => color};\n  font-size: 12px;\n  font-weight: 700;\n  direction: ltr;\n  white-space: nowrap;\n`\n\nexport default forwardRef((\n  { isVisible, status, label },\n  ref,\n) => (\n  <Container ref={ref}>\n    {isVisible && (\n      <Badge\n        background={getBackgroundColor(status)}\n        border={getBorderColor(status)}\n        color={getColor(status)}\n      >\n        {label}\n      </Badge>\n    )}\n  </Container>\n))\n","//@ts-nocheck\nimport { sortBy, reverse } from \"ramda\"\nimport React, { useLayoutEffect, useRef, useCallback } from \"react\"\nimport classNames from \"classnames\"\nimport { useUpdateEffect, useUnmount, useMount } from \"react-use\"\n// this version is needed because it contains a fix for handling constant value in the chart\n// ie. https://github.com/danvk/dygraphs/pull/909\nimport Dygraph from \"vendor/dygraph-c91c859.min\"\nimport \"dygraphs/src-es5/extras/smooth-plotter\"\nimport ResizeObserver from \"resize-observer-polyfill\"\n\nimport { useDispatch, useSelector } from \"store/redux-separate-context\"\nimport { AppStateT } from \"store/app-state\"\nimport { DygraphArea, NetdataDygraph } from \"types/vendor-overrides\"\nimport { TimeRange } from \"types/common\"\nimport { useDateTime } from \"utils/date-time\"\nimport { debounce } from \"utils/debounce\"\n\nimport {\n  selectCommonMin,\n  selectCommonMax,\n  selectGlobalChartUnderlay,\n  selectGlobalSelectionMaster,\n  selectSmoothPlot,\n  selectSyncPanAndZoom,\n  selectSpacePanelTransitionEndIsActive,\n  selectAlarm,\n  selectTimezoneSetting,\n} from \"domains/global/selectors\"\nimport {\n  resetGlobalPanAndZoomAction,\n  setCommonMaxAction,\n  setCommonMinAction,\n  setGlobalPauseAction,\n  resetGlobalPauseAction,\n} from \"domains/global/actions\"\n\nimport { resetChartPanAndZoomAction } from \"domains/chart/actions\"\n\nimport { Attributes } from \"../../utils/transformDataAttributes\"\nimport {\n  chartLibrariesSettings,\n  ChartLibraryConfig,\n  ChartLibraryName,\n} from \"../../utils/chartLibrariesSettings\"\nimport { ChartMetadata, DygraphData } from \"../../chart-types\"\nimport { selectResizeHeight } from \"../../selectors\"\n\nimport {\n  getDygraphChartType,\n  getDataForFakeStacked,\n  transformColors,\n  getDygraphFillAlpha,\n  hackDygraphIFrameTarps,\n} from \"./dygraph/utils\"\nimport \"./dygraph-chart.css\"\n\nimport useProceededChart from \"../../hooks/use-proceeded-chart\"\nimport useDygraphBadge from \"../../hooks/useDygraphBadge\"\nimport ProceededChartDisclaimer from \"./proceeded-chart-disclaimer\"\nimport AlarmBadge, { getBorderColor } from \"./alarmBadge\"\n\n// This is the threshold above which we assume chart shown duration has changed\nconst timeframeThreshold = 5000\nconst dygraphResizeDebounceTime = 500\n\ntype IsInRangeOfAvailableData = (props: {\n  after: number, before: number, chartData: DygraphData,\n}) => boolean\nconst isInRangeOfAvailableData: IsInRangeOfAvailableData = ({ after, before, chartData }) => (\n  after >= (chartData.first_entry * 1000) && before <= (chartData.last_entry * 1000)\n)\n\ninterface GetInitialDygraphOptions {\n  attributes: Attributes,\n  chartData: DygraphData,\n  chartMetadata: ChartMetadata,\n  chartSettings: ChartLibraryConfig,\n  dimensionsVisibility: boolean[]\n  hiddenLabelsElementId: string,\n  isFakeStacked: boolean,\n  orderedColors: string[],\n  setMinMax: (minMax: TimeRange) => void\n  shouldSmoothPlot: boolean,\n  unitsCurrent: string,\n  xAxisDateString: (d: Date) => string,\n  xAxisTimeString: (d: Date) => string,\n}\nconst getInitialDygraphOptions = ({\n  attributes,\n  chartData,\n  chartMetadata,\n  chartSettings,\n  dimensionsVisibility,\n  hiddenLabelsElementId,\n  isFakeStacked,\n  orderedColors,\n  setMinMax,\n  shouldSmoothPlot,\n  unitsCurrent,\n  xAxisDateString,\n  xAxisTimeString,\n}: GetInitialDygraphOptions) => {\n  const isSparkline = attributes.dygraphTheme === \"sparkline\"\n  const highlightCircleSize = isSparkline ? 3 : 4\n\n  const isLogScale = (chartSettings.isLogScale as ((a: Attributes) => boolean))(attributes)\n  const dygraphChartType = getDygraphChartType(attributes, chartData, chartMetadata, chartSettings)\n  const {\n    dygraphSmooth = dygraphChartType === \"line\"\n      && !isSparkline,\n    dygraphDrawAxis = true,\n    legendPosition,\n  } = attributes\n  const isLegendOnBottom = legendPosition === \"bottom\"\n  const {\n    // destructuring with default values\n    dygraphColors = orderedColors,\n    dygraphRightGap = 5,\n    dygraphShowRangeSelector = false,\n    dygraphShowRoller = false,\n    dygraphTitle = attributes.title || chartMetadata.title,\n    dygraphTitleHeight = 19,\n    dygraphLegend = \"always\",\n    dygraphLabelsDiv = hiddenLabelsElementId,\n    dygraphLabelsSeparateLine = true,\n    dygraphIncludeZero = dygraphChartType === \"stacked\",\n    dygraphShowZeroValues = true,\n    dygraphShowLabelsOnHighLight = true,\n    dygraphHideOverlayOnMouseOut = true,\n    dygraphXRangePad = 0,\n    dygraphYRangePad = 1,\n    dygraphValueRange = [null, null],\n    dygraphYLabelWidth = 12,\n    // eslint-disable-next-line no-nested-ternary\n    dygraphStrokeWidth = dygraphChartType === \"stacked\"\n      ? 0.1\n      : (dygraphSmooth === true\n        ? 1.5\n        : 0.7),\n\n    dygraphStrokePattern,\n    dygraphDrawPoints = false,\n    dygraphDrawGapEdgePoints = true,\n    dygraphConnectSeparatedPoints = false,\n    dygraphPointSize = 1,\n    dygraphStepPlot = false,\n    dygraphStrokeBorderColor = window.NETDATA.themes.current.background,\n    dygraphStrokeBorderWidth = 0,\n    dygraphFillGraph = (dygraphChartType === \"area\" || dygraphChartType === \"stacked\"),\n    dygraphFillAlpha = getDygraphFillAlpha(isFakeStacked, dygraphChartType),\n    dygraphStackedGraph = dygraphChartType === \"stacked\" && !isFakeStacked,\n    dygraphStackedGraphNanFill = \"none\",\n    dygraphAxisLabelFontSize = 10,\n    dygraphAxisLineColor = window.NETDATA.themes.current.axis,\n    dygraphAxisLineWidth = 1.0,\n    dygraphDrawGrid = true,\n    dygraphGridLinePattern,\n    dygraphGridLineWidth = 1.0,\n    dygraphGridLineColor = window.NETDATA.themes.current.grid,\n    dygraphMaxNumberWidth = 8,\n    dygraphSigFigs,\n    dygraphDigitsAfterDecimal = 2,\n    dygraphHighlighCircleSize = highlightCircleSize,\n    dygraphHighlightSeriesOpts,\n    dygraphHighlightSeriesBackgroundAlpha,\n\n    dygraphXPixelsPerLabel = 50,\n    dygraphXAxisLabelWidth = 60,\n    dygraphDrawXAxis = dygraphDrawAxis,\n    dygraphYPixelsPerLabel = 15,\n    dygraphYAxisLabelWidth = isLegendOnBottom ? 30 : 50,\n    dygraphDrawYAxis = dygraphDrawAxis,\n  } = attributes\n  return {\n    colors: isFakeStacked ? transformColors(reverse(dygraphColors)) : dygraphColors,\n\n    // leave a few pixels empty on the right of the chart\n    rightGap: isSparkline ? 0 : dygraphRightGap,\n    showRangeSelector: dygraphShowRangeSelector,\n    showRoller: dygraphShowRoller,\n    title: isSparkline ? undefined : dygraphTitle,\n    titleHeight: dygraphTitleHeight,\n    legend: dygraphLegend, // we need this to get selection events\n    labels: chartData.result.labels,\n    labelsDiv: dygraphLabelsDiv,\n\n    labelsSeparateLines: isSparkline ? true : dygraphLabelsSeparateLine,\n    labelsShowZeroValues: isLogScale ? false : dygraphShowZeroValues,\n    labelsKMB: false,\n    labelsKMG2: false,\n    showLabelsOnHighlight: dygraphShowLabelsOnHighLight,\n    hideOverlayOnMouseOut: dygraphHideOverlayOnMouseOut,\n    includeZero: dygraphIncludeZero,\n    xRangePad: dygraphXRangePad,\n    yRangePad: isSparkline ? 1 : dygraphYRangePad,\n    valueRange: dygraphValueRange,\n    ylabel: (isSparkline || isLegendOnBottom) ? undefined : unitsCurrent,\n    yLabelWidth: (isSparkline || isLegendOnBottom) ? 0 : dygraphYLabelWidth,\n\n    // the function to plot the chart\n    plotter: (dygraphSmooth && shouldSmoothPlot) ? window.smoothPlotter : null,\n\n    // The width of the lines connecting data points.\n    // This can be used to increase the contrast or some graphs.\n    strokeWidth: dygraphStrokeWidth,\n    strokePattern: dygraphStrokePattern,\n\n    // The size of the dot to draw on each point in pixels (see drawPoints).\n    // A dot is always drawn when a point is \"isolated\",\n    // i.e. there is a missing point on either side of it.\n    // This also controls the size of those dots.\n    drawPoints: dygraphDrawPoints,\n\n    // Draw points at the edges of gaps in the data.\n    // This improves visibility of small data segments or other data irregularities.\n    drawGapEdgePoints: dygraphDrawGapEdgePoints,\n    connectSeparatedPoints: isLogScale ? false : dygraphConnectSeparatedPoints,\n    pointSize: dygraphPointSize,\n\n    // enabling this makes the chart with little square lines\n    stepPlot: dygraphStepPlot,\n\n    // Draw a border around graph lines to make crossing lines more easily\n    // distinguishable. Useful for graphs with many lines.\n    strokeBorderColor: dygraphStrokeBorderColor,\n    strokeBorderWidth: dygraphStrokeBorderWidth,\n    fillGraph: dygraphFillGraph,\n    fillAlpha: dygraphFillAlpha,\n    stackedGraph: dygraphStackedGraph,\n    stackedGraphNaNFill: dygraphStackedGraphNanFill,\n    drawAxis: isSparkline ? false : dygraphDrawAxis,\n    axisLabelFontSize: dygraphAxisLabelFontSize,\n    axisLineColor: dygraphAxisLineColor,\n    axisLineWidth: dygraphAxisLineWidth,\n    drawGrid: isSparkline ? false : dygraphDrawGrid,\n    gridLinePattern: dygraphGridLinePattern,\n    gridLineWidth: dygraphGridLineWidth,\n    gridLineColor: dygraphGridLineColor,\n    maxNumberWidth: dygraphMaxNumberWidth,\n    sigFigs: dygraphSigFigs,\n    digitsAfterDecimal: dygraphDigitsAfterDecimal,\n    highlightCircleSize: dygraphHighlighCircleSize,\n    highlightSeriesOpts: dygraphHighlightSeriesOpts, // TOO SLOW: { strokeWidth: 1.5 },\n    // TOO SLOW: (state.tmp.dygraph_chart_type === 'stacked')?0.7:0.5,\n    highlightSeriesBackgroundAlpha: dygraphHighlightSeriesBackgroundAlpha,\n    visibility: dimensionsVisibility,\n    logscale: isLogScale,\n\n    axes: {\n      x: {\n        pixelsPerLabel: dygraphXPixelsPerLabel,\n        // insufficient typings for Dygraph\n        // @ts-ignore\n        ticker: Dygraph.dateTicker,\n        axisLabelWidth: dygraphXAxisLabelWidth,\n        drawAxis: isSparkline ? false : dygraphDrawXAxis,\n        axisLabelFormatter: (d: Date | number) => ((d as Date).toTimeString().startsWith(\"00:00:00\")\n          ? xAxisDateString(d as Date)\n          : xAxisTimeString(d as Date)\n        ),\n      },\n      y: {\n        logscale: isLogScale,\n        pixelsPerLabel: dygraphYPixelsPerLabel,\n        axisLabelWidth: dygraphYAxisLabelWidth,\n        drawAxis: isSparkline ? false : dygraphDrawYAxis,\n        // axisLabelFormatter is added on the updates\n        axisLabelFormatter(y: Date | number) {\n          const formatter = setMinMax([\n            // @ts-ignore\n            // eslint-disable-next-line no-underscore-dangle\n            this.axes_[0].extremeRange[0],\n            // @ts-ignore\n            // eslint-disable-next-line no-underscore-dangle\n            this.axes_[0].extremeRange[1],\n          ]) as unknown as ((value: Date | number) => string)\n          return formatter(y as number)\n        },\n      },\n    },\n  }\n}\n\ninterface Props {\n  attributes: Attributes\n  chartData: DygraphData\n  chartMetadata: ChartMetadata\n  chartElementClassName: string\n  chartElementId: string\n  chartLibrary: ChartLibraryName\n  chartUuid: string\n  colors: {\n    [key: string]: string\n  }\n  dimensionsVisibility: boolean[]\n  hasEmptyData: boolean\n  hasLegend: boolean\n  isRemotelyControlled: boolean\n  onUpdateChartPanAndZoom: (arg: {\n    after: number, before: number,\n    callback: (after: number, before: number) => void,\n    masterID: string,\n    shouldNotExceedAvailableRange: boolean,\n  }) => void\n  orderedColors: string[]\n  immediatelyDispatchPanAndZoom: () => void\n\n  hoveredRow: number\n  hoveredX: number | null\n  setGlobalChartUnderlay: (arg: { after: number, before: number, masterID: string }) => void\n  setHoveredX: (hoveredX: number | null, noMaster?: boolean) => void\n  setMinMax: (minMax: TimeRange) => void\n  unitsCurrent: string\n  viewAfter: number\n  viewBefore: number\n}\nexport const DygraphChart = ({\n  attributes,\n  chartData,\n  chartMetadata,\n  chartElementClassName,\n  chartElementId,\n  chartLibrary,\n  // colors,\n  chartUuid,\n  dimensionsVisibility,\n  hasEmptyData,\n  hasLegend,\n  isRemotelyControlled,\n  onUpdateChartPanAndZoom,\n  orderedColors,\n  immediatelyDispatchPanAndZoom,\n\n  hoveredRow,\n  hoveredX,\n  setGlobalChartUnderlay,\n  setHoveredX,\n  setMinMax,\n  unitsCurrent,\n  viewAfter,\n  viewBefore,\n}: Props) => {\n  const globalChartUnderlay = useSelector(selectGlobalChartUnderlay)\n  const selectedAlarm = useSelector(selectAlarm)\n  const alarm = selectedAlarm?.chartId === chartData.id ? selectedAlarm : null\n\n  const timezone = useSelector(selectTimezoneSetting)\n\n  const { xAxisDateString, xAxisTimeString } = useDateTime()\n  const chartSettings = chartLibrariesSettings[chartLibrary]\n  const hiddenLabelsElementId = `${chartUuid}-hidden-labels-id`\n\n  const dygraphChartType = getDygraphChartType(attributes, chartData, chartMetadata, chartSettings)\n  // isFakeStacked - is a special mode for displaying stacked charts with both positive and negative\n  // values. Dygraph.js doesn't support it so in this case we need to sum the values manually\n  // and display the chart as \"area\" type, but with keeping all styling (fill etc.) properties\n  // as in \"stacked\" type\n  // because first values need to be \"on top\" (at least for positive values), the dimension order\n  // needs to be reversed (in getDataForFakeStacked function and when assigning dimension colors)\n  const isFakeStacked = chartData.min < 0 && dygraphChartType === \"stacked\"\n  const dygraphFillAlpha = getDygraphFillAlpha(isFakeStacked, dygraphChartType)\n\n  const chartElement = useRef<HTMLDivElement>(null)\n\n  const updateChartPanOrZoom = useCallback(({\n    after, before,\n    callback,\n    shouldNotExceedAvailableRange,\n  }) => {\n    onUpdateChartPanAndZoom({\n      after,\n      before,\n      callback,\n      masterID: chartUuid,\n      shouldNotExceedAvailableRange,\n    })\n  }, [chartUuid, onUpdateChartPanAndZoom])\n\n  // keep in ref to prevent additional updates\n  const dygraphInstance = useRef<Dygraph | null>()\n  // state.tmp.dygraph_user_action in old dashboard\n  const latestIsUserAction = useRef(false)\n  // state.tmp.dygraph_mouse_down in old dashboard\n  const isMouseDown = useRef(false)\n  // state.tmp.dygraph_highlight_after in old dashboard\n  const dygraphHighlightAfter = useRef<null | number>(null)\n  // state.dygraph_last_touch_move in old dashboard\n  const dygraphLastTouchMove = useRef(0)\n  // state.dygraph_last_touch_page_x in old dashboard\n  const dygraphLastTouchPageX = useRef(0)\n  // state.dygraph_last_touch_end in old dashboard\n  const dygraphLastTouchEnd = useRef<undefined | number>()\n\n  const dispatch = useDispatch()\n  const isSyncPanAndZoom = useSelector(selectSyncPanAndZoom)\n\n  const resetGlobalPanAndZoom = useCallback(() => {\n    latestIsUserAction.current = false // prevent starting panAndZoom\n    if (dygraphInstance.current) {\n      // todo on toolbox reset click, do updateOptions({ dateWindow: null })\n      // (issue existed also before rewrite)\n      dygraphInstance.current.updateOptions({\n        // reset dateWindow to the current\n        // @ts-ignore external typings dont support null\n        dateWindow: null,\n      })\n    }\n\n    if (isSyncPanAndZoom) {\n      dispatch(resetGlobalPanAndZoomAction())\n    } else {\n      dispatch(resetChartPanAndZoomAction({ id: chartUuid }))\n    }\n  }, [chartUuid, dispatch, isSyncPanAndZoom])\n\n  const [isAlarmBadgeVisible, alarmBadgeRef, updateAlarmBadge] = useDygraphBadge() as any\n\n  // setGlobalChartUnderlay is using state from closure (chartData.after), so we need to have always\n  // the newest callback. Unfortunately we cannot use Dygraph.updateOptions() (library restriction)\n  // for interactionModel callbacks so we need to keep the callback in mutable ref\n  const propsRef = useRef({\n    alarm,\n    chartData,\n    globalChartUnderlay,\n    hoveredX,\n    immediatelyDispatchPanAndZoom,\n    // put it to ref to prevent additional updateOptions() after creating dygraph\n    resetGlobalPanAndZoom,\n    setGlobalChartUnderlay,\n    updateAlarmBadge,\n    updateChartPanOrZoom,\n    viewAfter,\n    viewBefore,\n  })\n\n  const [\n    isProceeded, precededChartRef, updatePrecededPosition,\n  ] = useProceededChart(chartElement, propsRef)\n\n  useLayoutEffect(() => {\n    propsRef.current.alarm = alarm\n    propsRef.current.chartData = chartData\n    propsRef.current.hoveredX = hoveredX\n    propsRef.current.immediatelyDispatchPanAndZoom = immediatelyDispatchPanAndZoom\n    propsRef.current.globalChartUnderlay = globalChartUnderlay\n    propsRef.current.resetGlobalPanAndZoom = resetGlobalPanAndZoom\n    propsRef.current.setGlobalChartUnderlay = setGlobalChartUnderlay\n    propsRef.current.updateAlarmBadge = updateAlarmBadge\n    propsRef.current.updateChartPanOrZoom = updateChartPanOrZoom\n    propsRef.current.viewAfter = viewAfter\n    propsRef.current.viewBefore = viewBefore\n  }, [\n    alarm,\n    chartData,\n    globalChartUnderlay,\n    hoveredX,\n    immediatelyDispatchPanAndZoom,\n    resetGlobalPanAndZoom,\n    setGlobalChartUnderlay,\n    updateAlarmBadge,\n    updateChartPanOrZoom,\n    viewAfter,\n    viewBefore,\n  ])\n\n  const shouldSmoothPlot = useSelector(selectSmoothPlot)\n  useLayoutEffect(() => {\n    if (chartElement && chartElement.current && !dygraphInstance.current && !hasEmptyData) {\n      const dygraphOptionsStatic = getInitialDygraphOptions({\n        attributes,\n        chartData,\n        chartMetadata,\n        chartSettings,\n        dimensionsVisibility,\n        hiddenLabelsElementId,\n        isFakeStacked,\n        orderedColors,\n        setMinMax,\n        shouldSmoothPlot,\n        unitsCurrent,\n        xAxisDateString,\n        xAxisTimeString,\n      })\n\n      latestIsUserAction.current = false\n\n      const dygraphOptions = {\n        ...dygraphOptionsStatic,\n        // set dateWindow on init - this is needed when chart is globalPanAndZoom-master\n        // and user scrolls down/up so the chart hides and then unhides. This causes the chart\n        // to re-create, but the data has additional padding which should be outside of\n        // visible range\n        dateWindow: [propsRef.current.viewAfter, propsRef.current.viewBefore],\n\n        highlightCallback(\n          event: MouseEvent, xval: number,\n        ) {\n          // todo\n          // state.pauseChart()\n\n          const newHoveredX = isMouseDown.current\n            ? null\n            : xval\n\n          const currentHoveredX = propsRef.current.hoveredX\n          if (newHoveredX !== currentHoveredX) {\n            setHoveredX(newHoveredX)\n          }\n        },\n\n        unhighlightCallback() {\n          // todo\n          // state.unpauseChart();\n          if (propsRef.current.hoveredX !== null) {\n            setHoveredX(null)\n          }\n        },\n        drawCallback(dygraph: Dygraph) {\n          // the user has panned the chart and this is called to re-draw the chart\n          // 1. refresh this chart by adding data to it\n          // 2. notify all the other charts about the update they need\n\n          // to prevent an infinite loop (feedback), we use\n          //     state.tmp.dygraph_user_action\n          // - when true, this is initiated by a user\n          // - when false, this is feedback\n\n          if (latestIsUserAction.current) {\n            latestIsUserAction.current = false\n            const xRange = dygraph.xAxisRange()\n            const after = Math.round(xRange[0])\n            const before = Math.round(xRange[1])\n\n            if (isInRangeOfAvailableData({\n              after, before, chartData: propsRef.current.chartData,\n            })) {\n              propsRef.current.updateChartPanOrZoom({ after, before })\n            }\n          }\n        },\n        zoomCallback: (minDate: number, maxDate: number) => {\n          latestIsUserAction.current = true\n          propsRef.current.updateChartPanOrZoom({ after: minDate, before: maxDate })\n        },\n\n        underlayCallback(canvas: CanvasRenderingContext2D, area: DygraphArea, g: Dygraph) {\n          updatePrecededPosition(g)\n\n          if (propsRef.current.alarm) {\n            const { alarm: currentAlarm } = propsRef.current\n\n            const alarmPosition = g.toDomXCoord(currentAlarm.when * 1000)\n            const fillColor = getBorderColor(currentAlarm.status)\n            const horizontalPadding = 3\n            // use RAF, because dygraph doesn't provide any callback called after drawing the chart\n            requestAnimationFrame(() => {\n              canvas.fillStyle = fillColor\n              const globalAlphaCache = canvas.globalAlpha\n              canvas.globalAlpha = 0.7\n              canvas.fillRect(alarmPosition - horizontalPadding, area.y, 2 * horizontalPadding, area.h)\n              canvas.globalAlpha = globalAlphaCache\n            })\n\n            propsRef.current.updateAlarmBadge(\n              propsRef.current.alarm,\n              g,\n              alarmPosition - horizontalPadding,\n            )\n          }\n\n          // the chart is about to be drawn\n          // this function renders global highlighted time-frame\n\n          if (propsRef.current.globalChartUnderlay) {\n            const { after, before } = propsRef.current.globalChartUnderlay\n\n            if (after < before) {\n              const HIGHLIGHT_HORIZONTAL_PADDING = 20\n              const bottomLeft = g.toDomCoords(after, -HIGHLIGHT_HORIZONTAL_PADDING)\n              const topRight = g.toDomCoords(before, HIGHLIGHT_HORIZONTAL_PADDING)\n\n              const left = bottomLeft[0]\n              const right = topRight[0]\n\n              // eslint-disable-next-line no-param-reassign\n              canvas.fillStyle = window.NETDATA.themes.current.highlight\n              canvas.fillRect(left, area.y, right - left, area.h)\n            }\n          }\n        },\n\n        // interactionModel cannot be replaced with updateOptions(). we need to keep all changing\n        // values and callbacks in mutable ref,\n        interactionModel: {\n          mousedown(event: MouseEvent, dygraph: Dygraph, context: any) {\n            // Right-click should not initiate anything.\n            if (event.button && event.button === 2) {\n              return\n            }\n\n            latestIsUserAction.current = true\n            isMouseDown.current = true\n            context.initializeMouseDown(event, dygraph, context)\n\n            // limit problematic dygraph's feature, more info above the function\n            // eslint-disable-next-line no-param-reassign\n            context.tarp.tarps = hackDygraphIFrameTarps(context.tarp.tarps)\n\n            dispatch(setGlobalPauseAction())\n\n            if (event.button && event.button === 1) {\n              // middle mouse button\n\n              if (event.shiftKey) {\n                // panning\n                dygraphHighlightAfter.current = null\n                // @ts-ignore\n                Dygraph.startPan(event, dygraph, context)\n              } else if (event.altKey || event.ctrlKey || event.metaKey) {\n                // middle mouse button highlight\n                dygraphHighlightAfter.current = dygraph.toDataXCoord(event.offsetX)\n                // @ts-ignore\n                Dygraph.startZoom(event, dygraph, context)\n              } else {\n                // middle mouse button selection for zoom\n                dygraphHighlightAfter.current = null\n                // @ts-ignore\n                Dygraph.startZoom(event, dygraph, context)\n              }\n            } else if (event.shiftKey) {\n              // left mouse button selection for zoom (ZOOM)\n              dygraphHighlightAfter.current = null\n              // @ts-ignore\n              Dygraph.startZoom(event, dygraph, context)\n            } else if (event.altKey || event.ctrlKey || event.metaKey) {\n              // left mouse button highlight\n              dygraphHighlightAfter.current = dygraph.toDataXCoord(event.offsetX)\n              // @ts-ignore\n              Dygraph.startZoom(event, dygraph, context)\n            } else {\n              // left mouse button dragging (PAN)\n              dygraphHighlightAfter.current = null\n              // @ts-ignore\n              Dygraph.startPan(event, dygraph, context)\n            }\n          },\n\n          mousemove(event: MouseEvent, dygraph: Dygraph, context: any) {\n            // if (state.tmp.dygraph_highlight_after !== null) {\n            // else if (\n            if (dygraphHighlightAfter.current !== null) {\n              // highlight selection\n              latestIsUserAction.current = true\n              // @ts-ignore\n              Dygraph.moveZoom(event, dygraph, context)\n              event.preventDefault()\n            } else if (context.isPanning) {\n              latestIsUserAction.current = true\n              // eslint-disable-next-line no-param-reassign\n              context.is2DPan = false\n              // @ts-ignore\n              Dygraph.movePan(event, dygraph, context)\n            } else if (context.isZooming) {\n              // @ts-ignore\n              Dygraph.moveZoom(event, dygraph, context)\n            }\n          },\n\n          mouseup(event: MouseEvent, dygraph: Dygraph, context: any) {\n            isMouseDown.current = false\n            if (dygraphHighlightAfter.current !== null) {\n              const sortedRange = sortBy((x) => +x, [\n                dygraphHighlightAfter.current,\n                dygraph.toDataXCoord(event.offsetX),\n              ])\n\n              propsRef.current.setGlobalChartUnderlay({\n                after: sortedRange[0],\n                before: sortedRange[1],\n                masterID: chartData.id,\n              })\n              dygraphHighlightAfter.current = null\n              // eslint-disable-next-line no-param-reassign\n              context.isZooming = false\n\n              // old dashboard code\n              // @ts-ignore\n              // eslint-disable-next-line no-underscore-dangle\n              dygraph.clearZoomRect_()\n              // this call probably fixes the broken selection circle during highlighting\n              // and forces underlayCallback to fire (and draw highlight-rect\n              // @ts-ignore\n              // eslint-disable-next-line no-underscore-dangle\n              dygraph.drawGraph_(false)\n            } else if (context.isPanning) {\n              latestIsUserAction.current = true\n              // @ts-ignore\n              Dygraph.endPan(event, dygraph, context)\n              propsRef.current.immediatelyDispatchPanAndZoom()\n            } else if (context.isZooming) {\n              latestIsUserAction.current = true\n              // @ts-ignore\n              Dygraph.endZoom(event, dygraph, context)\n              propsRef.current.immediatelyDispatchPanAndZoom()\n            }\n          },\n\n          wheel(event: WheelEvent, dygraph: Dygraph) {\n            if (!event.shiftKey && !event.altKey) return\n\n            latestIsUserAction.current = true\n            event.preventDefault()\n            event.stopPropagation()\n\n            // https://dygraphs.com/gallery/interaction-api.js\n            const zoom = (g, zoomInPercentage, bias) => {\n              bias = bias || 0.5\n              const [afterAxis, beforeAxis] = g.xAxisRange()\n              const delta = beforeAxis - afterAxis\n              const increment = delta * zoomInPercentage\n              const [afterIncrement, beforeIncrement] = [increment * bias, increment * (1 - bias)]\n\n              const after = afterAxis + afterIncrement\n              const before = beforeAxis - beforeIncrement\n\n              propsRef.current.updateChartPanOrZoom({\n                after,\n                before,\n                shouldNotExceedAvailableRange: true,\n                callback: (updatedAfter: number, updatedBefore: number) => {\n                  dygraph.updateOptions({\n                    dateWindow: [updatedAfter, updatedBefore],\n                  })\n                },\n              })\n            }\n\n            const offsetToPercentage = (g, offsetX) => {\n              // This is calculating the pixel offset of the leftmost date.\n              const [axisAfterOffset] = g.toDomCoords(g.xAxisRange()[0], null)\n              // x and w are relative to the corner of the drawing area,\n              // so that the upper corner of the drawing area is (0, 0).\n              const x = offsetX - axisAfterOffset\n              // This is computing the rightmost pixel, effectively defining the\n              // width.\n              const w = g.toDomCoords(g.xAxisRange()[1], null)[0] - axisAfterOffset\n\n              // Percentage from the left.\n              return w === 0 ? 0 : x / w\n            }\n\n            const normalDef =\n              typeof event.wheelDelta === \"number\" && !Number.isNaN(event.wheelDelta)\n                ? event.wheelDelta / 40\n                : event.deltaY * -1.2\n\n            const normal = event.detail ? event.detail * -1 : normalDef\n            const percentage = normal / 50\n\n            if (!event.offsetX) event.offsetX = event.layerX - event.target.offsetLeft\n            const xPct = offsetToPercentage(dygraph, event.offsetX)\n\n            zoom(dygraph, percentage, xPct)\n          },\n\n          click(event: MouseEvent) {\n            event.preventDefault()\n          },\n\n          dblclick() {\n            dispatch(resetGlobalPauseAction({ forcePlay: false }))\n            propsRef.current.resetGlobalPanAndZoom()\n          },\n\n          touchstart(event: TouchEvent, dygraph: Dygraph, context: any) {\n            isMouseDown.current = true\n            latestIsUserAction.current = true\n\n            // todo\n            // state.pauseChart()\n\n            Dygraph.defaultInteractionModel.touchstart(event, dygraph, context)\n\n            // we overwrite the touch directions at the end, to overwrite\n            // the internal default of dygraph\n            // eslint-disable-next-line no-param-reassign\n            context.touchDirections = { x: true, y: false }\n\n            dygraphLastTouchMove.current = 0\n\n            if (typeof event.touches[0].pageX === \"number\") {\n              dygraphLastTouchPageX.current = event.touches[0].pageX\n            } else {\n              dygraphLastTouchPageX.current = 0\n            }\n          },\n          touchmove(event: TouchEvent, dygraph: Dygraph, context: any) {\n            latestIsUserAction.current = true\n            Dygraph.defaultInteractionModel.touchmove(event, dygraph, context)\n\n            dygraphLastTouchMove.current = Date.now()\n          },\n\n          touchend(event: TouchEvent, dygraph: Dygraph, context: any) {\n            isMouseDown.current = false\n            latestIsUserAction.current = true\n            Dygraph.defaultInteractionModel.touchend(event, dygraph, context)\n\n            // if it didn't move, it is a selection\n            if (dygraphLastTouchMove.current === 0 && dygraphLastTouchPageX.current !== 0\n              && chartElement.current // this is just for TS\n            ) {\n              latestIsUserAction.current = false // prevent updating pan-and-zoom\n              // internal api of dygraph\n              // @ts-ignore\n              // eslint-disable-next-line no-underscore-dangle\n              const dygraphPlotter = dygraph.plotter_\n              const pct = (dygraphLastTouchPageX.current - (\n                dygraphPlotter.area.x + chartElement.current.getBoundingClientRect().left\n              )) / dygraphPlotter.area.w\n\n              const { current } = propsRef\n              const t = Math.round(current.viewAfter\n                + (current.viewBefore - current.viewAfter) * pct)\n              // dont set \"master\" so the highlight is recalculated (to match existing row)\n              setHoveredX(t, true)\n            }\n\n            // if it was double tap within double click time, reset the charts\n            const now = Date.now()\n            if (typeof dygraphLastTouchEnd.current !== \"undefined\") {\n              if (dygraphLastTouchMove.current === 0) {\n                const dt = now - dygraphLastTouchEnd.current\n                if (dt <= window.NETDATA.options.current.double_click_speed) {\n                  propsRef.current.resetGlobalPanAndZoom()\n                }\n              }\n            }\n\n            // remember the timestamp of the last touch end\n            dygraphLastTouchEnd.current = now\n            propsRef.current.immediatelyDispatchPanAndZoom()\n          },\n        },\n      }\n\n      const data = isFakeStacked\n        ? getDataForFakeStacked(chartData.result.data, dimensionsVisibility)\n        : chartData.result.data\n      const instance = new Dygraph((chartElement.current), data, dygraphOptions)\n      dygraphInstance.current = instance\n    }\n  }, [attributes, chartData, chartMetadata, chartSettings, chartUuid, dimensionsVisibility,\n    hasEmptyData, hiddenLabelsElementId, isFakeStacked,\n    orderedColors, setHoveredX, setMinMax, shouldSmoothPlot, unitsCurrent,\n    xAxisDateString, xAxisTimeString, updatePrecededPosition, dispatch])\n\n  useUpdateEffect(() => {\n    if (dygraphInstance.current) {\n      const isSparkline = attributes.dygraphTheme === \"sparkline\"\n      const isLegendOnBottom = attributes.legendPosition === \"bottom\"\n      dygraphInstance.current.updateOptions({\n        ylabel: (isSparkline || isLegendOnBottom) ? undefined : unitsCurrent,\n      })\n    }\n  }, [attributes, unitsCurrent])\n\n\n  // immediately update when changing global chart underlay or currently showed alarm\n  useUpdateEffect(() => {\n    if (dygraphInstance.current) {\n      dygraphInstance.current.updateOptions({})\n    }\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [alarm, globalChartUnderlay])\n\n  const spacePanelTransitionEndIsActive = useSelector(selectSpacePanelTransitionEndIsActive)\n  useUpdateEffect(() => {\n    if (dygraphInstance.current) {\n      // dygraph always resizes on browser width change, but doesn't resize when the container\n      // has different width.\n      window.requestAnimationFrame(() => {\n        if (dygraphInstance.current) {\n          (dygraphInstance.current as NetdataDygraph).resize()\n        }\n      })\n    }\n  }, [spacePanelTransitionEndIsActive])\n\n  // update data of the chart\n  // first effect should only be made by new DygraphInstance()\n  useUpdateEffect(() => {\n    // dont update when there is no data - in this case we should still show old chart\n    if (dygraphInstance.current && !hasEmptyData) {\n      // todo support state.tmp.dygraph_force_zoom\n      const forceDateWindow = [viewAfter, viewBefore]\n\n      // in old dashboard, when chart needed to reset internal dateWindow state,\n      // dateWindow was set to null, and new dygraph got the new dateWindow from results.\n      // this caused small unsync between dateWindow of parent (master) and child charts\n      // i also detected that forceDateWindow timestamps have slightly better performance (10%)\n      // so if the chart needs to change local dateWindow, we'll always use timestamps instead of\n      // null.\n\n      const xAxisRange = dygraphInstance.current.xAxisRange()\n      // eslint-disable-next-line max-len\n      const hasChangedDuration = Math.abs((viewBefore - viewAfter) - (xAxisRange[1] - xAxisRange[0])) > timeframeThreshold\n\n      // check if the time is relative\n      const hasScrolledToTheFutureDuringPlayMode = viewBefore <= 0\n      && (xAxisRange[1] > viewBefore)\n      // if viewAfter is bigger than current dateWindow start, just reset dateWindow\n      && (xAxisRange[0] > viewAfter)\n      && !hasChangedDuration\n\n      const optionsDateWindow = (isRemotelyControlled && !hasScrolledToTheFutureDuringPlayMode)\n        ? { dateWindow: forceDateWindow }\n        : {}\n\n      const { dygraphColors = orderedColors } = attributes\n      const file = isFakeStacked\n        ? getDataForFakeStacked(chartData.result.data, dimensionsVisibility)\n        : chartData.result.data\n\n      const includeZero = dimensionsVisibility.length === 1 ||\n        dimensionsVisibility.filter(x => x === true).length > 1\n\n      dygraphInstance.current.updateOptions({\n        ...optionsDateWindow,\n        colors: isFakeStacked ? transformColors(reverse(dygraphColors)) : dygraphColors,\n        file,\n        labels: chartData.result.labels,\n        fillAlpha: dygraphFillAlpha,\n        ...(dygraphChartType === \"stacked\" ? { includeZero } : {}),\n        stackedGraph: dygraphChartType === \"stacked\" && !isFakeStacked,\n        // see explanation about reversing before isFakeStacked assignment\n        visibility: isFakeStacked ? reverse(dimensionsVisibility) : dimensionsVisibility,\n      })\n    }\n  }, [attributes, chartData.result, chartUuid, dimensionsVisibility, dygraphChartType,\n    dygraphFillAlpha, hasEmptyData, isFakeStacked, isRemotelyControlled, orderedColors,\n    viewAfter, viewBefore])\n\n  useUpdateEffect(() => {\n    if (!dygraphInstance.current) {\n      return\n    }\n\n    const dygraphOptionsStatic = getInitialDygraphOptions({\n      attributes,\n      chartData,\n      chartMetadata,\n      chartSettings,\n      dimensionsVisibility,\n      hiddenLabelsElementId,\n      isFakeStacked,\n      orderedColors,\n      setMinMax,\n      shouldSmoothPlot,\n      unitsCurrent,\n      xAxisDateString,\n      xAxisTimeString,\n    })\n    if (!hasEmptyData) dygraphInstance.current.updateOptions(dygraphOptionsStatic)\n  }, [dygraphChartType, timezone])\n\n  // set selection\n  const currentSelectionMasterId = useSelector(selectGlobalSelectionMaster)\n  useLayoutEffect(() => {\n    if (dygraphInstance.current && currentSelectionMasterId !== chartUuid) {\n      if (hoveredRow === -1) {\n        // getSelection is 100 times faster that clearSelection\n        if (dygraphInstance.current.getSelection() !== -1) {\n          dygraphInstance.current.clearSelection()\n        }\n        return\n      }\n      dygraphInstance.current.setSelection(hoveredRow)\n    }\n  }, [chartData, chartUuid, currentSelectionMasterId, hoveredRow,\n    viewAfter, viewBefore])\n\n\n  // handle resizeHeight change\n  const resizeHeight = useSelector(\n    (state: AppStateT) => selectResizeHeight(state, { id: chartUuid }),\n  )\n  useLayoutEffect(() => {\n    if (dygraphInstance.current) {\n      (dygraphInstance.current as NetdataDygraph).resize()\n    }\n  }, [resizeHeight, chartData.dimension_names.length])\n\n\n  const commonMinState = useSelector((state: AppStateT) => (\n    attributes.commonMin\n      ? selectCommonMin(state, attributes.commonMin)\n      : undefined\n  ))\n  const commonMaxState = useSelector((state: AppStateT) => (\n    attributes.commonMax\n      ? selectCommonMax(state, attributes.commonMax)\n      : undefined\n  ))\n\n  useLayoutEffect(() => {\n    const { commonMin: commonMinKey, commonMax: commonMaxKey } = attributes\n\n    if (\n      dygraphInstance.current\n      && (commonMinKey || commonMaxKey)\n    ) {\n      const extremes = (dygraphInstance.current as NetdataDygraph).yAxisExtremes()[0]\n      const [currentMin, currentMax] = extremes\n\n      const {\n        dygraphValueRange = [null, null],\n      } = attributes\n      // if the user gave a valueRange, respect it\n      const shouldUseCommonMin = dygraphValueRange[0] === null\n      const shouldUseCommonMax = dygraphValueRange[1] === null\n\n\n      let shouldUpdate = false\n      let valueRange: number[] = [...extremes]\n\n      // check if current extreme (painted by dygraph) is not more extreme than commonMin/Max\n      // if yes - update the chart\n      if (commonMinKey && shouldUseCommonMin) {\n        if (commonMinState && commonMinState.currentExtreme < currentMin) {\n          valueRange[0] = commonMinState.currentExtreme\n          shouldUpdate = true\n        }\n      }\n      if (commonMaxKey && shouldUseCommonMax) {\n        if (commonMaxState && commonMaxState.currentExtreme > currentMax) {\n          valueRange[1] = commonMaxState.currentExtreme\n          shouldUpdate = true\n        }\n      }\n\n      if (shouldUpdate) {\n        dygraphInstance.current.updateOptions({ valueRange })\n        const newExtremes = (dygraphInstance.current as NetdataDygraph).yAxisExtremes()[0]\n        // get updated valueRange (rounded by dygraph)\n        valueRange = [...newExtremes]\n      }\n\n      // if the value is different than the one stored in state, update redux state\n      if (commonMinKey && shouldUseCommonMin\n        && (valueRange[0] !== commonMinState?.charts[chartUuid])\n      ) {\n        dispatch(setCommonMinAction({ chartUuid, commonMinKey, value: valueRange[0] }))\n      }\n      if (commonMaxKey && shouldUseCommonMax\n        && (valueRange[1] !== commonMaxState?.charts[chartUuid])\n      ) {\n        dispatch(setCommonMaxAction({ chartUuid, commonMaxKey, value: valueRange[1] }))\n      }\n    }\n  }, [attributes, chartData.result, chartUuid, commonMinState, commonMaxState, dispatch])\n\n  useLayoutEffect(() => {\n    if (isProceeded && dygraphInstance.current) {\n      updatePrecededPosition(dygraphInstance.current)\n    }\n  // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [isProceeded])\n\n  useUnmount(() => {\n    if (dygraphInstance.current) {\n      dygraphInstance.current.destroy()\n    }\n  })\n\n  /**\n   * resize with ResizeObserver\n   */\n  const resizeObserver = useRef<ResizeObserver>()\n  useMount(() => {\n    if (!attributes.detectResize) {\n      return\n    }\n    // flag used to prevent first callback (and resize) on dygraph initial draw\n    let hasOmitedFirstCallback = false\n    const callbackDebounced = debounce(() => {\n      if (!hasOmitedFirstCallback) {\n        hasOmitedFirstCallback = true\n        return\n      }\n\n      if (dygraphInstance.current) {\n        (dygraphInstance.current as NetdataDygraph).resize()\n      }\n    }, dygraphResizeDebounceTime)\n\n    resizeObserver.current = new ResizeObserver(() => {\n      callbackDebounced()\n    })\n    resizeObserver.current.observe(chartElement.current as HTMLDivElement)\n  })\n\n  useUnmount(() => {\n    dygraphInstance.current = null // clear it for debounce purposes\n    if (resizeObserver.current) {\n      resizeObserver.current.disconnect()\n    }\n  })\n\n  const isLegendOnBottom = attributes.legendPosition === \"bottom\"\n\n  return (\n    <>\n      <div\n        ref={chartElement}\n        id={chartElementId}\n        className={classNames(\n          chartElementClassName,\n          { \"dygraph-chart--legend-bottom\": isLegendOnBottom },\n        )}\n      />\n      {isProceeded && hasLegend && (\n        <ProceededChartDisclaimer ref={precededChartRef as React.Ref<HTMLDivElement>} />\n      )}\n      {alarm?.value && hasLegend && (\n        // @ts-ignore\n        <AlarmBadge\n          isVisible={isAlarmBadgeVisible}\n          ref={alarmBadgeRef}\n          status={alarm.status}\n          label={alarm.value}\n        />\n      )}\n      <div className=\"dygraph-chart__labels-hidden\" id={hiddenLabelsElementId} />\n    </>\n  )\n}\n","// https://gist.github.com/ca0v/73a31f57b397606c9813472f7493a940\n\nexport const debounce = <F extends (...args: any[]) => any>(func: F, waitFor: number) => {\n  let timeout: ReturnType<typeof setTimeout> | null = null\n\n  const debounced = (...args: Parameters<F>) => {\n    if (timeout !== null) {\n      clearTimeout(timeout)\n      timeout = null\n    }\n    timeout = setTimeout(() => func(...args), waitFor)\n  }\n\n  return debounced as (...args: Parameters<F>) => ReturnType<F>\n}\n","import React, { useRef, useEffect, useState } from \"react\"\n// @ts-ignore \"declare module\" doesn't work properly when importing dashboard in cloud\nimport EasyPie from \"easy-pie-chart\"\n\nimport { Attributes } from \"domains/chart/utils/transformDataAttributes\"\nimport { ChartMetadata, EasyPieChartData } from \"domains/chart/chart-types\"\nimport { ChartLibraryName } from \"domains/chart/utils/chartLibrariesSettings\"\nimport {\n  always, cond, identity, T, sortBy, map, pipe,\n} from \"ramda\"\n\ntype GetPercentFromValueMinMax = (arg: {\n  value: number | undefined\n  min: number | undefined\n  max: number | undefined\n  isMinOverride: boolean\n  isMaxOverride: boolean\n}) => number\nconst getPercentFromValueMinMax: GetPercentFromValueMinMax = ({\n  value = 0, min = 0, max = 0,\n  isMinOverride,\n  isMaxOverride,\n}) => {\n  /* eslint-disable no-param-reassign */\n  // todo refractor old logic to readable functions\n  // if no easyPiechart-min-value attribute\n  if (!isMinOverride && min > 0) {\n    min = 0\n  }\n  if (!isMaxOverride && max < 0) {\n    max = 0\n  }\n\n  let pcent\n\n  if (min < 0 && max > 0) {\n    // it is both positive and negative\n    // zero at the top center of the chart\n    max = (-min > max) ? -min : max\n    pcent = Math.round((value * 100) / max)\n  } else if (value >= 0 && min >= 0 && max >= 0) {\n    // clockwise\n    pcent = Math.round(((value - min) * 100) / (max - min))\n    if (pcent === 0) {\n      pcent = 0.1\n    }\n  } else {\n    // counter clockwise\n    pcent = Math.round(((value - max) * 100) / (max - min))\n    if (pcent === 0) {\n      pcent = -0.1\n    }\n  }\n  /* eslint-enable no-param-reassign */\n  return pcent\n}\n\ninterface Props {\n  attributes: Attributes\n  chartData: EasyPieChartData\n  chartMetadata: ChartMetadata\n  chartElementClassName: string\n  chartElementId: string\n  chartLibrary: ChartLibraryName\n  chartUuid: string\n  colors: {\n    [key: string]: string\n  }\n  chartWidth: number\n  dimensionsVisibility: boolean[]\n  isRemotelyControlled: boolean\n  legendFormatValue: ((v: number | string | null) => number | string)\n  onUpdateChartPanAndZoom: (arg: {\n    after: number, before: number,\n    callback: (after: number, before: number) => void,\n    masterID: string,\n    shouldNotExceedAvailableRange: boolean,\n  }) => void\n  orderedColors: string[]\n\n  hoveredRow: number\n  setGlobalChartUnderlay: (arg: { after: number, before: number, masterID: string }) => void\n  setMinMax: (minMax: [number, number]) => void\n  showUndefined: boolean\n  unitsCurrent: string\n  viewAfter: number\n  viewBefore: number\n}\nexport const EasyPieChart = ({\n  attributes,\n  chartData,\n  chartMetadata,\n  chartElementClassName,\n  chartElementId,\n  chartWidth,\n  hoveredRow,\n  legendFormatValue,\n  orderedColors,\n  setMinMax,\n  showUndefined,\n  unitsCurrent,\n}: Props) => {\n  const chartElement = useRef<HTMLDivElement>(null)\n  const [chartInstance, setChartInstance] = useState()\n\n  const valueIndex = hoveredRow === -1\n    ? 0\n    : (chartData.result.length - 1 - hoveredRow) // because data for easy-pie-chart are flipped\n  const value = showUndefined ? null : chartData.result[valueIndex]\n\n  const {\n    // if this is set, then we're overriding commonMin\n    easyPieChartMinValue: min = chartData.min, // todo replace with commonMin\n    easyPieChartMaxValue: max = chartData.max, // todo replace with commonMax\n  } = attributes\n\n  // make sure the order is correct and that value is not outside those boundaries\n  // (this check was present in old dashboard but perhaps it's not needed)\n  const safeMinMax = pipe(\n    map((x: number) => +x),\n    sortBy(identity),\n    ([_min, _max]: number[]) => [Math.min(_min, value || 0), Math.max(_max, value || 0)],\n  )([min, max])\n\n  useEffect(() => {\n    setMinMax(safeMinMax as [number, number])\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [safeMinMax])\n\n  const pcent = getPercentFromValueMinMax({\n    value: showUndefined ? 0 : (value as number),\n    min: safeMinMax[0],\n    max: safeMinMax[1],\n    isMinOverride: attributes.easyPieChartMinValue !== undefined,\n    isMaxOverride: attributes.easyPieChartMaxValue !== undefined,\n  })\n\n  useEffect(() => {\n    if (chartElement.current && !chartInstance) {\n      const stroke = cond([\n        [(v) => v < 3, always(2)],\n        [T, identity],\n      ])(Math.floor(chartWidth / 22))\n\n      const {\n        easyPieChartTrackColor = window.NETDATA.themes.current.easypiechart_track,\n        easyPieChartScaleColor = window.NETDATA.themes.current.easypiechart_scale,\n        easyPieChartScaleLength = 5,\n        easyPieChartLineCap = \"round\",\n        easyPieChartLineWidth = stroke,\n        easyPieChartTrackWidth,\n        easyPieChartSize = chartWidth,\n        easyPieChartRotate = 0,\n        easyPieChartAnimate = { duration: 500, enabled: true },\n        easyPieChartEasing,\n      } = attributes\n\n      const newChartInstance = new EasyPie(chartElement.current, {\n        barColor: orderedColors[0],\n        trackColor: easyPieChartTrackColor,\n        scaleColor: easyPieChartScaleColor,\n        scaleLength: easyPieChartScaleLength,\n        lineCap: easyPieChartLineCap,\n        lineWidth: easyPieChartLineWidth,\n        trackWidth: easyPieChartTrackWidth,\n        size: easyPieChartSize,\n        rotate: easyPieChartRotate,\n        animate: easyPieChartAnimate,\n        easing: easyPieChartEasing,\n      })\n      setChartInstance(newChartInstance)\n    }\n  }, [attributes, chartData, chartInstance, chartWidth, orderedColors])\n\n  // update with value\n  useEffect(() => {\n    if (chartInstance) {\n      const shouldUseAnimation = hoveredRow === -1 && !showUndefined\n\n      if (shouldUseAnimation && !chartInstance.options.animate.enabled) {\n        chartInstance.enableAnimation()\n      } else if (!shouldUseAnimation && chartInstance.options.animate.enabled) {\n        chartInstance.disableAnimation()\n      }\n\n      setTimeout(() => {\n        // need to be in timeout to trigger animation properly\n        chartInstance.update(pcent)\n      }, 0)\n    }\n  }, [chartInstance, hoveredRow, pcent, showUndefined])\n\n  const valueFontSize = (chartWidth * 2) / 3 / 5\n  const valuetop = Math.round((chartWidth - valueFontSize - (chartWidth / 40)) / 2)\n\n  const titleFontSize = Math.round((valueFontSize * 1.6) / 3)\n  const titletop = Math.round(valuetop - (titleFontSize * 2) - (chartWidth / 40))\n\n  const unitFontSize = Math.round(titleFontSize * 0.9)\n  const unitTop = Math.round(valuetop + (valueFontSize + unitFontSize) + (chartWidth / 40))\n  // to update, just label innerText and pcent are changed\n\n  return (\n    <div ref={chartElement} id={chartElementId} className={chartElementClassName}>\n      <span\n        className=\"easyPieChartLabel\"\n        style={{\n          fontSize: valueFontSize,\n          top: valuetop,\n        }}\n      >\n        {legendFormatValue(value)}\n      </span>\n      <span\n        className=\"easyPieChartTitle\"\n        style={{\n          fontSize: titleFontSize,\n          top: titletop,\n        }}\n      >\n        {attributes.title || chartMetadata.title}\n      </span>\n      <span\n        className=\"easyPieChartUnits\"\n        style={{\n          fontSize: unitFontSize,\n          top: unitTop,\n        }}\n      >\n        {unitsCurrent}\n      </span>\n\n    </div>\n  )\n}\n","import React, {\n  useRef, useEffect, useState,\n} from \"react\"\n// @ts-ignore \"declare module\" doesn't work properly when importing dashboard in cloud\nimport { Gauge } from \"gaugeJS\"\n\nimport { Attributes } from \"domains/chart/utils/transformDataAttributes\"\nimport { ChartMetadata, EasyPieChartData } from \"domains/chart/chart-types\"\nimport { ChartLibraryName } from \"domains/chart/utils/chartLibrariesSettings\"\nimport {\n  identity, sortBy, map, pipe, always,\n} from \"ramda\"\n\nconst isSetByUser = (x: undefined | number): x is number => (\n  typeof x === \"number\"\n)\n\ninterface Props {\n  attributes: Attributes\n  chartData: EasyPieChartData\n  chartMetadata: ChartMetadata\n  chartElementClassName: string\n  chartElementId: string\n  chartLibrary: ChartLibraryName\n  chartUuid: string\n  colors: {\n    [key: string]: string\n  }\n  chartHeight: number\n  chartWidth: number\n  dimensionsVisibility: boolean[]\n  isRemotelyControlled: boolean\n  legendFormatValue: ((v: number | string | null) => number | string)\n  onUpdateChartPanAndZoom: (arg: {\n    after: number, before: number,\n    callback: (after: number, before: number) => void,\n    masterID: string,\n    shouldNotExceedAvailableRange: boolean,\n  }) => void\n  orderedColors: string[]\n\n  hoveredRow: number\n  hoveredX: number | null\n  setGlobalChartUnderlay: (arg: { after: number, before: number, masterID: string }) => void\n  setHoveredX: (hoveredX: number | null, noMaster?: boolean) => void\n  setMinMax: (minMax: [number, number]) => void\n  showUndefined: boolean\n  unitsCurrent: string\n  viewAfter: number\n  viewBefore: number\n}\nexport const GaugeChart = ({\n  attributes,\n  chartData,\n  chartMetadata,\n  chartElementClassName,\n  chartElementId,\n  chartUuid,\n  chartHeight,\n  chartWidth,\n  hoveredRow,\n  legendFormatValue,\n  orderedColors,\n  setMinMax,\n  showUndefined,\n  unitsCurrent,\n}: Props) => {\n  const chartCanvasElement = useRef<HTMLCanvasElement>(null)\n  const [chartInstance, setChartInstance] = useState()\n\n  const valueIndex = hoveredRow === -1\n    ? 0\n    : (chartData.result.length - 1 - hoveredRow) // because data for easy-pie-chart are flipped\n  const value = chartData.result[valueIndex]\n\n  const {\n    // if this is set, then we're overriding commonMin\n    gaugeMinValue: minAttribute,\n    gaugeMaxValue: maxAttribute,\n  } = attributes\n\n  const min = isSetByUser(minAttribute) ? minAttribute : chartData.min\n  const max = isSetByUser(maxAttribute) ? maxAttribute : chartData.max\n  // we should use minAttribute if it's existing\n  // old app was using commonMin\n\n  // make sure the order is correct and that value is not outside those boundaries\n  // (this check was present in old dashboard but perhaps it's not needed)\n  const [safeMin, safeMax] = pipe(\n    // if they are attributes, make sure they're converted to numbers\n    map((x: number) => +x),\n    // make sure it is zero based\n    // but only if it has not been set by the user\n    ([_min, _max]: number[]) => [\n      (!isSetByUser(minAttribute) && _min > 0) ? 0 : _min,\n      (!isSetByUser(maxAttribute) && _max < 0) ? 0 : _max,\n    ],\n    // make sure min <= max\n    sortBy(identity),\n    ([_min, _max]: number[]) => [Math.min(_min, value), Math.max(_max, value)],\n  )([min, max])\n  // calling outside \"useEffect\" intentionally,\n  // because it should update the values first, and only then render the chart in useEffect()\n  useEffect(() => {\n    setMinMax([safeMin, safeMax])\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [safeMin, safeMax])\n\n  const pcent = pipe(\n    always(((value - safeMin) * 100) / (safeMax - safeMin)),\n    // bug fix for gauge.js 1.3.1\n    // if the value is the absolute min or max, the chart is broken\n    (_pcent: number) => Math.max(0.001, _pcent),\n    (_pcent: number) => Math.min(99.999, _pcent),\n  )()\n\n  useEffect(() => {\n    if (chartCanvasElement.current && !chartInstance) {\n      const {\n        gaugePointerColor = window.NETDATA.themes.current.gauge_pointer,\n        gaugeStrokeColor = window.NETDATA.themes.current.gauge_stroke,\n        gaugeStartColor = orderedColors[0],\n        gaugeStopColor,\n        gaugeGenerateGradient = false,\n      } = attributes\n\n      const options = {\n        lines: 12, // The number of lines to draw\n        angle: 0.14, // The span of the gauge arc\n        lineWidth: 0.57, // The line thickness\n        radiusScale: 1.0, // Relative radius\n        pointer: {\n          length: 0.85, // 0.9 The radius of the inner circle\n          strokeWidth: 0.045, // The rotation offset\n          color: gaugePointerColor, // Fill color\n        },\n\n        // If false, the max value of the gauge will be updated if value surpass max\n        // If true, the min value of the gauge will be fixed unless you set it manually\n        limitMax: true,\n        limitMin: true,\n        colorStart: gaugeStartColor,\n        colorStop: gaugeStopColor,\n        strokeColor: gaugeStrokeColor,\n        generateGradient: (gaugeGenerateGradient === true), // gmosx:\n        gradientType: 0,\n        highDpiSupport: true, // High resolution support\n      }\n\n      const newChartInstance = new Gauge(chartCanvasElement.current).setOptions(options)\n\n      // we will always feed a percentage (copied from old dashboard)\n      newChartInstance.minValue = 0\n      newChartInstance.maxValue = 100\n\n      setChartInstance(newChartInstance)\n    }\n  }, [attributes, chartData, chartInstance, chartWidth, orderedColors])\n\n  // update with value\n  useEffect(() => {\n    if (chartInstance) {\n      // gauge animation\n      const shouldUseAnimation = hoveredRow === -1 && !showUndefined\n      // animation doesn't work in newest, 1.3.7 version!\n      const speed = shouldUseAnimation ? 32 : 1000000000\n      chartInstance.animationSpeed = speed\n      setTimeout(() => {\n        chartInstance.set(showUndefined ? 0 : pcent)\n      }, 0)\n    }\n  }, [chartInstance, chartHeight, chartWidth, hoveredRow, pcent, showUndefined])\n\n  const valueFontSize = Math.floor(chartHeight / 5)\n  const valueTop = Math.round((chartHeight - valueFontSize) / 3.2)\n\n  const titleFontSize = Math.round(valueFontSize / 2.1)\n  const titleTop = 0\n\n  const unitFontSize = Math.round(titleFontSize * 0.9)\n\n  const minMaxFontSize = Math.round(valueFontSize * 0.75)\n  return (\n    <div\n      id={chartElementId}\n      className={chartElementClassName}\n    >\n      <canvas\n        ref={chartCanvasElement}\n        className=\"gaugeChart\"\n        id={`gauge-${chartUuid}-canvas`}\n        style={{\n          width: chartWidth,\n          height: chartHeight,\n        }}\n      />\n      <span\n        className=\"gaugeChartLabel\"\n        style={{\n          fontSize: valueFontSize,\n          top: valueTop,\n        }}\n      >\n        {legendFormatValue(showUndefined ? null : value)}\n      </span>\n      <span\n        className=\"gaugeChartTitle\"\n        style={{\n          fontSize: titleFontSize,\n          top: titleTop,\n        }}\n      >\n        {attributes.title || chartMetadata.title}\n      </span>\n      <span\n        className=\"gaugeChartUnits\"\n        style={{\n          fontSize: unitFontSize,\n        }}\n      >\n        {unitsCurrent}\n      </span>\n      <span className=\"gaugeChartMin\" style={{ fontSize: minMaxFontSize }}>\n        {legendFormatValue(showUndefined ? null : safeMin)}\n      </span>\n      <span className=\"gaugeChartMax\" style={{ fontSize: minMaxFontSize }}>\n        {legendFormatValue(showUndefined ? null : safeMax)}\n      </span>\n    </div>\n  )\n}\n","import {\n  cond, identity, map, pipe, replace, splitEvery, T, toString,\n} from \"ramda\"\n\ntype NormalizeHex = (hex: string) => string\nexport const normalizeHex: NormalizeHex = pipe(\n  toString,\n  replace(/[^0-9a-f]/gi, \"\"),\n  cond([\n    [(str) => str.length < 6, (str) => str[0] + str[0] + str[1] + str[1] + str[2] + str[2]],\n    [T, identity],\n  ]),\n)\n\nexport const colorLuminance = (hex: string, lum: number = 0) => {\n  const hexNormalized = normalizeHex(hex)\n\n  // convert to decimal and change luminosity\n  const rgb = pipe(\n    // point-free version generates ts error\n    (str: string) => splitEvery(2, str),\n    map(\n      pipe(\n        (str: string) => parseInt(str, 16),\n        (nr) => Math.round(\n          Math.min(\n            Math.max(0, nr + (nr * lum)),\n            255,\n          ),\n        ).toString(16),\n        (str) => `00${str}`.substr(str.length),\n      ),\n    ),\n    (x) => x.join(\"\"),\n  )(hexNormalized)\n  return `#${rgb}`\n}\n","/* eslint-disable indent */\n/* eslint-disable operator-linebreak */\n/* eslint-disable comma-dangle */\n/* eslint-disable react-hooks/exhaustive-deps */\n/* eslint-disable consistent-return */\nimport \"jquery-sparkline\"\nimport React, { useRef, useEffect, useState } from \"react\"\n\nimport { Attributes } from \"domains/chart/utils/transformDataAttributes\"\nimport { ChartMetadata, EasyPieChartData } from \"domains/chart/chart-types\"\nimport { colorLuminance } from \"domains/chart/utils/color-luminance\"\nimport { MS_IN_SECOND } from \"utils/utils\"\nimport { TimeRange } from \"types/common\"\n\nconst convertToTimestamp = (number: number) => {\n  if (number > 0) {\n    return number\n  }\n  return new Date().valueOf() + number // number is negative or zero\n}\n\ninterface TimeWindowCorrection {\n  paddingLeftPercentage?: string\n  widthRatio?: number\n}\nconst getForceTimeWindowCorrection = (\n  chartData: EasyPieChartData,\n  viewRange: TimeRange\n): TimeWindowCorrection => {\n  const requestedAfter = convertToTimestamp(viewRange[0])\n  const requestedBefore = convertToTimestamp(viewRange[1])\n  const after = chartData.after * MS_IN_SECOND\n  const before = chartData.before * MS_IN_SECOND\n\n  const currentDuration = before - after\n  const requestedDuration = requestedBefore - requestedAfter\n  // don't do overrides when current (available) duration is bigger or only slightly lower\n  // than requested duration\n  const DURATION_CHANGE_TOLERANCE = 1.03\n  if (currentDuration > requestedDuration / DURATION_CHANGE_TOLERANCE) {\n    return {}\n  }\n\n  const widthRatio = currentDuration / requestedDuration\n\n  const visibleDuration = requestedBefore - requestedAfter\n  const paddingLeftPercentage = `${100 * ((after - requestedAfter) / visibleDuration)}%`\n\n  return {\n    paddingLeftPercentage,\n    widthRatio,\n  }\n}\n\ninterface Props {\n  attributes: Attributes\n  chartContainerElement: HTMLElement\n  chartData: EasyPieChartData\n  chartMetadata: ChartMetadata\n  chartElementClassName: string\n  chartElementId: string\n  dimensionsVisibility: boolean[]\n  isRemotelyControlled: boolean\n  orderedColors: string[]\n  unitsCurrent: string\n  viewAfterForCurrentData: number\n  viewBeforeForCurrentData: number\n}\nexport const SparklineChart = ({\n  attributes,\n  chartContainerElement,\n  chartData,\n  chartMetadata,\n  chartElementClassName,\n  chartElementId,\n  orderedColors,\n  unitsCurrent,\n  viewAfterForCurrentData,\n  viewBeforeForCurrentData,\n}: Props) => {\n  const chartElement = useRef<HTMLDivElement>(null)\n\n  // update width, height automatically each time\n  const [$chartElement, set$chartElement] = useState()\n  const sparklineOptions = useRef<{ [key: string]: any }>()\n\n  const { paddingLeftPercentage = undefined, widthRatio = 1 } = attributes.forceTimeWindow\n    ? getForceTimeWindowCorrection(chartData, [viewAfterForCurrentData, viewBeforeForCurrentData])\n    : {}\n\n  const updateSparklineValues = () => {\n    if (!$chartElement) return\n\n    const { width, height } = chartContainerElement.getBoundingClientRect()\n    // @ts-ignore\n    $chartElement.sparkline(chartData.result, {\n      ...sparklineOptions.current,\n      width: Math.floor(width * widthRatio),\n      height: Math.floor(height),\n    })\n  }\n\n  // create chart\n  useEffect(() => {\n    const { sparklineLineColor = orderedColors[0] } = attributes\n    const defaultFillColor =\n      chartMetadata.chart_type === \"line\"\n        ? window.NETDATA.themes.current.background\n        : colorLuminance(sparklineLineColor, window.NETDATA.chartDefaults.fill_luminance)\n    const chartTitle = attributes.title || chartMetadata.title\n\n    const emptyStringIfDisable = (x: string | undefined) => (x === \"disable\" ? \"\" : x)\n\n    const {\n      sparklineType = \"line\",\n      sparklineFillColor = defaultFillColor,\n      sparklineDisableInteraction = false,\n      sparklineDisableTooltips = false,\n      sparklineDisableHighlight = false,\n      sparklineHighlightLighten = 1.4,\n      sparklineTooltipSuffix = ` ${unitsCurrent}`,\n      sparklineNumberFormatter = (n: number) => n.toFixed(2),\n    } = attributes\n\n    const sparklineInitOptions = {\n      type: sparklineType,\n      lineColor: sparklineLineColor,\n      fillColor: sparklineFillColor,\n      chartRangeMin: attributes.sparklineChartRangeMin,\n      chartRangeMax: attributes.sparklineChartRangeMax,\n      composite: attributes.sparklineComposite,\n      enableTagOptions: attributes.sparklineEnableTagOptions,\n      tagOptionPrefix: attributes.sparklineTagOptionPrefix,\n      tagValuesAttribute: attributes.sparklineTagValuesAttribute,\n\n      disableHiddenCheck: attributes.sparklineDisableHiddenCheck,\n      defaultPixelsPerValue: attributes.sparklineDefaultPixelsPerValue,\n      spotColor: emptyStringIfDisable(attributes.sparklineSpotColor),\n      minSpotColor: emptyStringIfDisable(attributes.sparklineMinSpotColor),\n      maxSpotColor: emptyStringIfDisable(attributes.sparklineMaxSpotColor),\n      spotRadius: attributes.sparklineSpotRadius,\n      valueSpots: attributes.sparklineValueSpots,\n      highlightSpotColor: attributes.sparklineHighlightSpotColor,\n      highlightLineColor: attributes.sparklineHighlightLineColor,\n      lineWidth: attributes.sparklineLineWidth,\n      normalRangeMin: attributes.sparklineNormalRangeMin,\n      normalRangeMax: attributes.sparklineNormalRangeMax,\n      drawNormalOnTop: attributes.sparklineDrawNormalOnTop,\n      xvalues: attributes.sparklineXvalues,\n      chartRangeClip: attributes.sparklineChartRangeClip,\n      chartRangeMinX: attributes.sparklineChartRangeMinX,\n      chartRangeMaxX: attributes.sparklineChartRangeMaxX,\n      disableInteraction: sparklineDisableInteraction,\n      disableTooltips: sparklineDisableTooltips,\n      disableHighlight: sparklineDisableHighlight,\n      highlightLighten: sparklineHighlightLighten,\n      highlightColor: attributes.sparklineHighlightColor,\n      tooltipContainer: attributes.sparklineTooltipContainer,\n      tooltipClassname: attributes.sparklineTooltipClassname,\n      tooltipChartTitle: chartTitle,\n      tooltipFormat: attributes.sparklineTooltipFormat,\n      tooltipPrefix: attributes.sparklineTooltipPrefix,\n      tooltipSuffix: sparklineTooltipSuffix,\n      tooltipSkipNull: attributes.sparklineTooltipSkipNull,\n      tooltipValueLookups: attributes.sparklineTooltipValueLookups,\n      tooltipFormatFieldlist: attributes.sparklineTooltipFormatFieldlist,\n      tooltipFormatFieldlistKey: attributes.sparklineTooltipFormatFieldlistKey,\n      numberFormatter: sparklineNumberFormatter,\n      numberDigitGroupSep: attributes.sparklineNumberDigitGroupSep,\n      numberDecimalMark: attributes.sparklineNumberDecimalMark,\n      numberDigitGroupCount: attributes.sparklineNumberDigitGroupCount,\n      animatedZooms: attributes.sparklineAnimatedZooms,\n    }\n    sparklineOptions.current = sparklineInitOptions\n\n    if (!chartElement.current || $chartElement) return\n\n    set$chartElement(() => window.$(chartElement.current))\n  }, [\n    $chartElement,\n    attributes,\n    chartContainerElement,\n    chartData.result,\n    chartMetadata,\n    orderedColors,\n    unitsCurrent,\n    widthRatio,\n  ])\n\n  const { sparklineOnHover } = attributes\n  useEffect(() => {\n    if (!$chartElement || !sparklineOnHover) return\n\n    const onLeave = () => sparklineOnHover(null)\n    const onChange = ({ sparklines: [sparkline] }: any) => {\n      const { x, y } = sparkline.getCurrentRegionFields()\n      sparklineOnHover({ x, y })\n    }\n\n    // @ts-ignore\n    $chartElement.bind(\"sparklineRegionChange\", onChange).bind(\"mouseleave\", onLeave)\n    return () => {\n      // @ts-ignore\n      $chartElement.unbind(\"sparklineRegionChange\", onChange).unbind(\"mouseleave\", onLeave)\n    }\n  }, [$chartElement, sparklineOnHover])\n\n  // update chart\n  useEffect(updateSparklineValues, [$chartElement, chartData.result])\n\n  const style = paddingLeftPercentage\n    ? {\n        textAlign: \"initial\" as \"initial\", // :) typescript\n        paddingLeft: paddingLeftPercentage,\n      }\n    : undefined\n\n  return (\n    <div ref={chartElement} id={chartElementId} className={chartElementClassName} style={style} />\n  )\n}\n","import * as d3 from \"d3\"\n\nwindow.d3 = d3\n","let fetchPromise: Promise<string>\n\nconst GOOGLE_JS_API_SRC = \"https://www.google.com/jsapi\"\n\nexport const loadGoogleVisualizationApi = () => {\n  if (fetchPromise) {\n    return fetchPromise\n  }\n  fetchPromise = new Promise((resolve, reject) => {\n    setTimeout(() => {\n      const script = document.createElement(\"script\")\n      script.type = \"text/javascript\"\n      script.async = true\n      script.src = GOOGLE_JS_API_SRC\n\n      script.onerror = () => {\n        reject(Error(\"error loading google.js api\"))\n      }\n      script.onload = () => {\n        resolve(\"ok\")\n      }\n\n      const firstScript = document.getElementsByTagName(\"script\")[0] as HTMLScriptElement\n      (firstScript.parentNode as Node).insertBefore(script, firstScript)\n    }, 1000)\n  }).then(() => new Promise((resolve) => {\n    window.google.load(\"visualization\", \"1.1\", {\n      packages: [\"corechart\", \"controls\"],\n      callback: resolve,\n    })\n  }))\n  return fetchPromise\n}\n","// @ts-ignore \"declare module\" doesn't work properly when importing dashboard in cloud\nimport \"jquery-sparkline\"\nimport React, {\n  useRef, useEffect, useState,\n} from \"react\"\n\nimport \"../../utils/d3-loader\"\n// @ts-ignore \"declare module\" doesn't work properly when importing dashboard in cloud\nimport d3pie from \"vendor/d3pie-0.2.1-netdata-3\"\n\nimport { Attributes } from \"domains/chart/utils/transformDataAttributes\"\nimport {\n  ChartMetadata,\n  D3pieChartData,\n} from \"domains/chart/chart-types\"\nimport { seconds4human } from \"domains/chart/utils/seconds4human\"\nimport { useDateTime } from \"utils/date-time\"\nimport { tail } from \"ramda\"\n\nconst emptyContent = {\n  label: \"no data\",\n  value: 100,\n  color: \"#666666\",\n}\n\ntype GetDateRange = (arg: {\n  chartData: D3pieChartData,\n  index: number,\n  localeDateString: (date: number | Date) => string,\n  localeTimeString: (time: number | Date) => string,\n}) => string\nconst getDateRange: GetDateRange = ({\n  chartData, index,\n  localeDateString, localeTimeString,\n}) => {\n  const dt = Math.round((chartData.before - chartData.after + 1) / chartData.points)\n  const dtString = seconds4human(dt)\n\n  const before = chartData.result.data[index].time\n  const after = before - (dt * 1000)\n\n  const d1 = localeDateString(after)\n  const t1 = localeTimeString(after)\n  const d2 = localeDateString(before)\n  const t2 = localeTimeString(before)\n\n  if (d1 === d2) {\n    return `${d1} ${t1} to ${t2}, ${dtString}`\n  }\n\n  return `${d1} ${t1} to ${d2} ${t2}, ${dtString}`\n}\n\ninterface Props {\n  attributes: Attributes\n  chartContainerElement: HTMLElement\n  chartData: D3pieChartData\n  chartMetadata: ChartMetadata\n  chartElementClassName: string\n  chartElementId: string\n  dimensionsVisibility: boolean[]\n  hoveredRow: number\n  hoveredX: number | null\n  isRemotelyControlled: boolean\n  legendFormatValue: ((v: number | string | null) => number | string)\n  orderedColors: string[]\n  setMinMax: (minMax: [number, number]) => void\n  showUndefined: boolean\n  unitsCurrent: string\n}\nexport const D3pieChart = ({\n  attributes,\n  chartContainerElement,\n  chartData,\n  chartMetadata,\n  chartElementClassName,\n  chartElementId,\n  hoveredRow,\n  hoveredX,\n  legendFormatValue,\n  orderedColors,\n  setMinMax,\n  unitsCurrent,\n}: Props) => {\n  const chartElement = useRef<HTMLDivElement>(null)\n\n  const legendFormatValueRef = useRef(legendFormatValue)\n  legendFormatValueRef.current = legendFormatValue\n\n  const [d3pieInstance, setD3pieInstance] = useState()\n  const d3pieOptions = useRef<{[key: string]: any}>()\n\n  const { localeDateString, localeTimeString } = useDateTime()\n\n  // create chart\n  useEffect(() => {\n    if (chartElement.current && !d3pieInstance) {\n      // d3pieSetContent\n      // todo this should be set in chart.tsx, when creating hook\n      setMinMax([chartData.min, chartData.max])\n      // index is ROW! it's !== 0 only when selection is made\n      const index = 0\n      const content = tail(chartData.result.labels).map((label, i) => {\n        const value = chartData.result.data[index][label]\n        const color = orderedColors[i]\n        return {\n          label,\n          value,\n          color,\n        }\n      }).filter((x) => x.value !== null && x.value > 0)\n      const safeContent = content.length > 0 ? content : emptyContent\n\n      const defaultTitle = attributes.title || chartMetadata.title\n      const dateRange = getDateRange({\n        chartData,\n        index: 0,\n        localeDateString,\n        localeTimeString,\n      })\n      const {\n        d3pieTitle = defaultTitle,\n        d3pieSubtitle = unitsCurrent,\n        d3pieFooter = dateRange,\n        d3pieTitleColor = window.NETDATA.themes.current.d3pie.title,\n        d3pieTitleFontsize = 12,\n        d3pieTitleFontweight = \"bold\",\n        d3pieTitleFont = \"arial\",\n        d3PieSubtitleColor = window.NETDATA.themes.current.d3pie.subtitle,\n        d3PieSubtitleFontsize = 10,\n        d3PieSubtitleFontweight = \"normal\",\n        d3PieSubtitleFont = \"arial\",\n        d3PieFooterColor = window.NETDATA.themes.current.d3pie.footer,\n        d3PieFooterFontsize = 9,\n        d3PieFooterFontweight = \"bold\",\n        d3PieFooterFont = \"arial\",\n        d3PieFooterLocation = \"bottom-center\",\n\n        d3PiePieinnerradius = \"45%\",\n        d3PiePieouterradius = \"80%\",\n        d3PieSortorder = \"value-desc\",\n        d3PieSmallsegmentgroupingEnabled = false,\n        d3PieSmallsegmentgroupingValue = 1,\n        d3PieSmallsegmentgroupingValuetype = \"percentage\",\n        d3PieSmallsegmentgroupingLabel = \"other\",\n        d3PieSmallsegmentgroupingColor = window.NETDATA.themes.current.d3pie.other,\n\n        d3PieLabelsOuterFormat = \"label-value1\",\n        d3PieLabelsOuterHidewhenlessthanpercentage = null,\n        d3PieLabelsOuterPiedistance = 15,\n        d3PieLabelsInnerFormat = \"percentage\",\n        d3PieLabelsInnerHidewhenlessthanpercentage = 2,\n\n        d3PieLabelsMainLabelColor = window.NETDATA.themes.current.d3pie.mainlabel,\n        d3PieLabelsMainLabelFont = \"arial\",\n        d3PieLabelsMainLabelFontsize = 10,\n        d3PieLabelsMainLabelFontweight = \"normal\",\n\n        d3PieLabelsPercentageColor = window.NETDATA.themes.current.d3pie.percentage,\n        d3PieLabelsPercentageFont = \"arial\",\n        d3PieLabelsPercentageFontsize = 10,\n        d3PieLabelsPercentageFontweight = \"bold\",\n\n        d3PieLabelsValueColor = window.NETDATA.themes.current.d3pie.value,\n        d3PieLabelsValueFont = \"arial\",\n        d3PieLabelsValueFontsize = 10,\n        d3PieLabelsValueFontweight = \"bold\",\n\n        d3PieLabelsLinesEnabled = true,\n        d3PieLabelsLinesStyle = \"curved\",\n        d3PieLabelsLinesColor = \"segment\", // \"segment\" or a hex color\n\n        d3PieLabelsTruncationEnabled = false,\n        d3PieLabelsTruncationTruncatelength = 30,\n\n        d3PieMiscColorsSegmentstroke = window.NETDATA.themes.current.d3pie.segment_stroke,\n        d3PieMiscGradientEnabled = false,\n        d3PieMiscColorsPercentage = 95,\n        d3PieMiscGradientColor = window.NETDATA.themes.current.d3pie.gradient_color,\n\n        d3PieCssprefix = null,\n      } = attributes\n\n      const { width, height } = chartContainerElement.getBoundingClientRect()\n\n      const initialD3pieOptions = {\n        header: {\n          title: {\n            text: d3pieTitle,\n            color: d3pieTitleColor,\n            fontSize: d3pieTitleFontsize,\n            fontWeight: d3pieTitleFontweight,\n            font: d3pieTitleFont,\n          },\n          subtitle: {\n            text: d3pieSubtitle,\n            color: d3PieSubtitleColor,\n            fontSize: d3PieSubtitleFontsize,\n            fontWeight: d3PieSubtitleFontweight,\n            font: d3PieSubtitleFont,\n          },\n          titleSubtitlePadding: 1,\n        },\n        footer: {\n          text: d3pieFooter,\n          color: d3PieFooterColor,\n          fontSize: d3PieFooterFontsize,\n          fontWeight: d3PieFooterFontweight,\n          font: d3PieFooterFont,\n          location: d3PieFooterLocation,\n        },\n        size: {\n          canvasHeight: Math.floor(height),\n          canvasWidth: Math.floor(width),\n          pieInnerRadius: d3PiePieinnerradius,\n          pieOuterRadius: d3PiePieouterradius,\n        },\n        data: {\n          // none, random, value-asc, value-desc, label-asc, label-desc\n          sortOrder: d3PieSortorder,\n          smallSegmentGrouping: {\n            enabled: d3PieSmallsegmentgroupingEnabled,\n            value: d3PieSmallsegmentgroupingValue,\n            // percentage, value\n            valueType: d3PieSmallsegmentgroupingValuetype,\n            label: d3PieSmallsegmentgroupingLabel,\n            color: d3PieSmallsegmentgroupingColor,\n          },\n\n          // REQUIRED! This is where you enter your pie data; it needs to be an array of objects\n          // of this form: { label: \"label\", value: 1.5, color: \"#000000\" } - color is optional\n          content: safeContent,\n        },\n\n\n        labels: {\n          outer: {\n            // label, value, percentage, label-value1, label-value2, label-percentage1,\n            // label-percentage2\n            format: d3PieLabelsOuterFormat,\n            hideWhenLessThanPercentage: d3PieLabelsOuterHidewhenlessthanpercentage,\n            pieDistance: d3PieLabelsOuterPiedistance,\n          },\n          inner: {\n            // label, value, percentage, label-value1, label-value2, label-percentage1,\n            // label-percentage2\n            format: d3PieLabelsInnerFormat,\n            hideWhenLessThanPercentage: d3PieLabelsInnerHidewhenlessthanpercentage,\n          },\n          mainLabel: {\n            color: d3PieLabelsMainLabelColor, // or 'segment' for dynamic color\n            font: d3PieLabelsMainLabelFont,\n            fontSize: d3PieLabelsMainLabelFontsize,\n            fontWeight: d3PieLabelsMainLabelFontweight,\n          },\n          percentage: {\n            color: d3PieLabelsPercentageColor,\n            font: d3PieLabelsPercentageFont,\n            fontSize: d3PieLabelsPercentageFontsize,\n            fontWeight: d3PieLabelsPercentageFontweight,\n            decimalPlaces: 0,\n          },\n          value: {\n            color: d3PieLabelsValueColor,\n            font: d3PieLabelsValueFont,\n            fontSize: d3PieLabelsValueFontsize,\n            fontWeight: d3PieLabelsValueFontweight,\n          },\n          lines: {\n            enabled: d3PieLabelsLinesEnabled,\n            style: d3PieLabelsLinesStyle,\n            color: d3PieLabelsLinesColor,\n          },\n          truncation: {\n            enabled: d3PieLabelsTruncationEnabled,\n            truncateLength: d3PieLabelsTruncationTruncatelength,\n          },\n          formatter(context: any) {\n            if (context.part === \"value\") {\n              return legendFormatValueRef.current(context.value)\n            }\n            if (context.part === \"percentage\") {\n              return `${context.label}%`\n            }\n\n            return context.label\n          },\n        },\n        effects: {\n          load: {\n            effect: \"none\", // none / default\n            speed: 0, // commented in the d3pie code to speed it up\n          },\n          pullOutSegmentOnClick: {\n            effect: \"bounce\", // none / linear / bounce / elastic / back\n            speed: 400,\n            size: 5,\n          },\n          highlightSegmentOnMouseover: true,\n          highlightLuminosity: -0.2,\n        },\n        tooltips: {\n          enabled: false,\n          type: \"placeholder\", // caption|placeholder\n          string: \"\",\n          placeholderParser: null, // function\n          styles: {\n            fadeInSpeed: 250,\n            backgroundColor: window.NETDATA.themes.current.d3pie.tooltip_bg,\n            backgroundOpacity: 0.5,\n            color: window.NETDATA.themes.current.d3pie.tooltip_fg,\n            borderRadius: 2,\n            font: \"arial\",\n            fontSize: 12,\n            padding: 4,\n          },\n        },\n        misc: {\n          colors: {\n            background: \"transparent\", // transparent or color #\n            // segments: state.chartColors(),\n            segmentStroke: d3PieMiscColorsSegmentstroke,\n          },\n          gradient: {\n            enabled: d3PieMiscGradientEnabled,\n            percentage: d3PieMiscColorsPercentage,\n            color: d3PieMiscGradientColor,\n          },\n          canvasPadding: {\n            top: 5,\n            right: 5,\n            bottom: 5,\n            left: 5,\n          },\n          pieCenterOffset: {\n            x: 0,\n            y: 0,\n          },\n          cssPrefix: d3PieCssprefix,\n        },\n        callbacks: {\n          onload: null,\n          onMouseoverSegment: null,\n          onMouseoutSegment: null,\n          onClickSegment: null,\n        },\n      }\n      // eslint-disable-next-line new-cap\n      const newD3pieInstance = new d3pie(chartElement.current, initialD3pieOptions)\n      d3pieOptions.current = initialD3pieOptions\n      setD3pieInstance(() => newD3pieInstance)\n    }\n  }, [attributes, chartContainerElement, chartData, chartMetadata, d3pieInstance, legendFormatValue,\n    localeDateString, localeTimeString, orderedColors, setMinMax, unitsCurrent])\n\n  // update chart\n  useEffect(() => {\n    if (d3pieInstance && d3pieOptions.current) {\n      const dateRange = getDateRange({\n        chartData,\n        index: 0,\n        localeDateString,\n        localeTimeString,\n      })\n      const {\n        d3pieSubtitle = unitsCurrent,\n        d3pieFooter = dateRange,\n      } = attributes\n\n\n      const isHoveredButNoData = !!hoveredX && (hoveredRow === -1)\n      const slot = chartData.result.data.length - hoveredRow - 1\n\n      const index = (slot < 0 || slot >= chartData.result.data.length)\n        ? 0\n        : slot\n\n      const content = tail(chartData.result.labels).map((label, i) => {\n        const value = chartData.result.data[index][label]\n        const color = orderedColors[i]\n        return {\n          label,\n          value,\n          color,\n        }\n      }).filter((x) => x.value !== null && x.value > 0)\n      const safeContent = (content.length > 0 && !isHoveredButNoData)\n        ? content\n        : [emptyContent]\n\n      d3pieInstance.options.header.subtitle.text = d3pieSubtitle\n      d3pieInstance.options.footer.text = d3pieFooter\n\n      d3pieInstance.options.data.content = safeContent\n      d3pieInstance.destroy()\n      d3pieInstance.recreate()\n    }\n  }, [attributes, chartData, d3pieInstance, hoveredRow, hoveredX, localeDateString,\n    localeTimeString, orderedColors, unitsCurrent])\n\n  return (\n    <div ref={chartElement} id={chartElementId} className={chartElementClassName} />\n  )\n}\n","// @ts-ignore \"declare module\" doesn't work properly when importing dashboard in cloud\nimport \"peity\"\nimport React, {\n  useRef, useState, useLayoutEffect,\n} from \"react\"\n\nimport { Attributes } from \"domains/chart/utils/transformDataAttributes\"\nimport { ChartMetadata, EasyPieChartData } from \"domains/chart/chart-types\"\nimport { colorLuminance } from \"domains/chart/utils/color-luminance\"\n\ninterface Props {\n  attributes: Attributes\n  chartContainerElement: HTMLElement\n  chartData: EasyPieChartData\n  chartMetadata: ChartMetadata\n  chartElementClassName: string\n  chartElementId: string\n  orderedColors: string[]\n}\nexport const PeityChart = ({\n  attributes,\n  chartContainerElement,\n  chartData,\n  chartMetadata,\n  chartElementClassName,\n  chartElementId,\n  orderedColors,\n}: Props) => {\n  const chartElement = useRef<HTMLDivElement>(null)\n\n  // update width, height automatically each time\n  const [$chartElement, set$chartElement] = useState()\n  const peityOptions = useRef<{\n    stroke: string,\n    fill: string,\n    strokeWidth: number,\n    width: number,\n    height: number,\n  }>()\n\n\n  // create chart\n  useLayoutEffect(() => {\n    if (chartElement.current && !$chartElement) {\n      const $element = window.$(chartElement.current)\n\n      const { width, height } = chartContainerElement.getBoundingClientRect()\n\n      const {\n        peityStrokeWidth = 1,\n      } = attributes\n      const peityInitOptions = {\n        stroke: window.NETDATA.themes.current.foreground,\n        strokeWidth: peityStrokeWidth,\n        width: Math.floor(width),\n        height: Math.floor(height),\n        fill: window.NETDATA.themes.current.foreground,\n      }\n\n      set$chartElement(() => $element)\n      peityOptions.current = peityInitOptions\n    }\n  }, [attributes, $chartElement, chartContainerElement])\n\n  // update chart\n  useLayoutEffect(() => {\n    if ($chartElement && peityOptions.current) {\n      const getFillOverride = () => (\n        chartMetadata.chart_type === \"line\"\n          ? window.NETDATA.themes.current.background\n          : colorLuminance(orderedColors[0], window.NETDATA.chartDefaults.fill_luminance)\n      )\n      const updatedOptions = {\n        ...peityOptions.current,\n        stroke: orderedColors[0],\n        // optimizatino from old dashboard, perhaps could be transformed to useMemo()\n        fill: (orderedColors[0] === peityOptions.current.stroke)\n          ? peityOptions.current.fill\n          : getFillOverride(),\n      }\n      $chartElement.peity(\"line\", updatedOptions)\n      peityOptions.current = updatedOptions\n    }\n  }, [$chartElement, chartData, chartMetadata, orderedColors])\n\n  return (\n    <div\n      ref={chartElement}\n      id={chartElementId}\n      className={chartElementClassName}\n    >\n      {chartData.result}\n    </div>\n  )\n}\n","import React, {\n  useRef, useState, useLayoutEffect,\n} from \"react\"\n\nimport { Attributes } from \"domains/chart/utils/transformDataAttributes\"\nimport { ChartMetadata, EasyPieChartData } from \"domains/chart/chart-types\"\nimport { loadGoogleVisualizationApi } from \"domains/chart/utils/google-visualization-loader\"\n\ninterface Props {\n  attributes: Attributes\n  chartData: EasyPieChartData\n  chartMetadata: ChartMetadata\n  chartElementClassName: string\n  chartElementId: string\n  orderedColors: string[]\n  unitsCurrent: string\n}\nexport const GoogleChart = ({\n  attributes,\n  chartData,\n  chartMetadata,\n  chartElementClassName,\n  chartElementId,\n  orderedColors,\n  unitsCurrent,\n}: Props) => {\n  const chartElement = useRef<HTMLDivElement>(null)\n  const googleChartInstance = useRef<\n    google.visualization.AreaChart |\n    google.visualization.LineChart>()\n\n  const [hasApiBeenLoaded, setHasApiBeenLoaded] = useState(false)\n  loadGoogleVisualizationApi()\n    .then(() => {\n      setHasApiBeenLoaded(true)\n    })\n\n  const googleOptions = useRef<{[key: string]: unknown}>()\n\n  // update chart\n  useLayoutEffect(() => {\n    if (googleChartInstance.current && googleOptions.current) {\n      const dataTable = new window.google.visualization.DataTable(chartData.result)\n      googleChartInstance.current.draw(dataTable, googleOptions.current)\n    }\n  }, [chartData])\n\n  // create chart\n  useLayoutEffect(() => {\n    if (chartElement.current && !googleOptions.current && hasApiBeenLoaded) {\n      const dataTable = new window.google.visualization.DataTable(chartData.result)\n\n      const {\n        title = chartMetadata.title,\n      } = attributes\n      const chartType = chartMetadata.chart_type\n      const areaOpacity = new Map([\n        [\"area\", window.NETDATA.options.current.color_fill_opacity_area],\n        [\"stacked\", window.NETDATA.options.current.color_fill_opacity_stacked],\n      ]).get(chartType) || 0.3\n      const initialGoogleOptions = {\n        colors: orderedColors,\n\n        // do not set width, height - the chart resizes itself\n        // width: state.chartWidth(),\n        // height: state.chartHeight(),\n        lineWidth: chartType === \"line\" ? 2 : 1,\n        title,\n        fontSize: 11,\n        hAxis: {\n          //  title: \"Time of Day\",\n          //  format:'HH:mm:ss',\n          viewWindowMode: \"maximized\",\n          slantedText: false,\n          format: \"HH:mm:ss\",\n          textStyle: {\n            fontSize: 9,\n          },\n          gridlines: {\n            color: \"#EEE\",\n          },\n        },\n        vAxis: {\n          title: unitsCurrent,\n          viewWindowMode: (chartType === \"area\" || chartType === \"stacked\")\n            ? \"maximized\"\n            : \"pretty\",\n          minValue: chartType === \"stacked\" ? undefined : -0.1,\n          maxValue: chartType === \"stacked\" ? undefined : 0.1,\n          direction: 1,\n          textStyle: {\n            fontSize: 9,\n          },\n          gridlines: {\n            color: \"#EEE\",\n          },\n        },\n        chartArea: {\n          width: \"65%\",\n          height: \"80%\",\n        },\n        focusTarget: \"category\",\n        annotation: {\n          1: {\n            style: \"line\",\n          },\n        },\n        pointsVisible: false,\n        titlePosition: \"out\",\n        titleTextStyle: {\n          fontSize: 11,\n        },\n        tooltip: {\n          isHtml: false,\n          ignoreBounds: true,\n          textStyle: {\n            fontSize: 9,\n          },\n        },\n        curveType: \"function\" as \"function\",\n        areaOpacity,\n        isStacked: chartType === \"stacked\",\n      }\n\n      const googleInstance = [\"area\", \"stacked\"].includes(chartMetadata.chart_type)\n        ? new window.google.visualization.AreaChart(chartElement.current)\n        : new window.google.visualization.LineChart(chartElement.current)\n\n      googleInstance.draw(dataTable, initialGoogleOptions)\n\n      googleOptions.current = initialGoogleOptions\n      googleChartInstance.current = googleInstance\n    }\n  }, [attributes, chartData.result, chartMetadata, chartElement, hasApiBeenLoaded, orderedColors,\n    unitsCurrent])\n\n\n  return (\n    <div\n      ref={chartElement}\n      id={chartElementId}\n      className={chartElementClassName}\n    />\n  )\n}\n","import React from \"react\"\n\nimport { Attributes } from \"domains/chart/utils/transformDataAttributes\"\nimport { EasyPieChartData } from \"domains/chart/chart-types\"\n\ninterface Props {\n  attributes: Attributes\n  chartData: EasyPieChartData\n  chartElementClassName: string\n  chartElementId: string\n}\nexport const TextOnly = ({\n  attributes,\n  chartData,\n  chartElementClassName,\n  chartElementId,\n}: Props) => {\n  const { textOnlyDecimalPlaces = 1, textOnlyPrefix = \"\", textOnlySuffix = \"\" } = attributes\n\n  // Round based on number of decimal places to show\n  const precision = 10 ** textOnlyDecimalPlaces\n  const value = Math.round(chartData.result[0] * precision) / precision\n\n  const textContent = chartData.result.length === 0 ? \"\" : textOnlyPrefix + value + textOnlySuffix\n\n  return (\n    <div id={chartElementId} className={chartElementClassName}>\n      {textContent}\n    </div>\n  )\n}\n","/* eslint-disable no-param-reassign */\n// @ts-nocheck\n\nexport const defaultCellSize = 11\nexport const defaultPadding = 1\nexport const defaultAspectRatio = Math.round(16 / 9)\n\nexport const getCellBoxSize = (cellSize = defaultCellSize, padding = defaultPadding) =>\n  cellSize - padding\nexport const getRows = (data, aspectRatio = defaultAspectRatio) =>\n  Math.sqrt(data.length / aspectRatio)\nexport const getColumns = (rows, aspectRatio = defaultAspectRatio) => rows * aspectRatio\n\nexport const getXPosition = (columns, index, cellSize = defaultCellSize) =>\n  Math.floor(index % columns) * cellSize\nexport const getYPosition = (columns, index, cellSize = defaultCellSize) =>\n  Math.floor(index / columns) * cellSize\n\nexport const getFullWidth = (columns, cellSize = defaultCellSize) => Math.ceil(columns) * cellSize\nexport const getFullHeight = (rows, cellSize = defaultCellSize, padding = defaultCellSize) =>\n  Math.ceil(rows) * cellSize + padding\n\nexport const getOffsetPosition = (offset, cellSize = defaultCellSize) =>\n  Math.floor(offset / cellSize)\n","/* eslint-disable object-curly-newline */\n/* eslint-disable no-param-reassign */\n// @ts-nocheck\nimport { getCellBoxSize, getXPosition, getYPosition, getOffsetPosition } from \"./utilities\"\n\nexport default (\n  el,\n  columns,\n  total,\n  { onMouseenter, onMouseout },\n  { cellSize, cellPadding } = {}\n) => {\n  let hoveredIndex = -1\n\n  const getEvent = index => {\n    const rect = el.getBoundingClientRect()\n    const offsetX = getXPosition(columns, index, cellSize)\n    const offsetY = getYPosition(columns, index, cellSize)\n    const left = rect.left + offsetX\n    const top = rect.top + offsetY\n    const cellBoxSize = getCellBoxSize(cellSize, cellPadding)\n\n    return {\n      index,\n      left,\n      top,\n      right: left + cellBoxSize,\n      bottom: top + cellBoxSize,\n      width: cellBoxSize,\n      height: cellBoxSize,\n      offsetX,\n      offsetY,\n    }\n  }\n\n  const mouseout = () => {\n    onMouseout(getEvent(hoveredIndex))\n    hoveredIndex = -1\n  }\n\n  const mousemove = e => {\n    const { offsetX, offsetY } = e\n    const x = getOffsetPosition(offsetX, cellSize)\n    const y = getOffsetPosition(offsetY, cellSize)\n    const nextHoveredIndex = y * columns + x\n\n    if (nextHoveredIndex === hoveredIndex) return\n\n    if (hoveredIndex !== -1) mouseout()\n\n    if (nextHoveredIndex >= total) return\n\n    onMouseenter(getEvent(nextHoveredIndex))\n    hoveredIndex = nextHoveredIndex\n  }\n\n  el.addEventListener(\"mousemove\", mousemove)\n  el.addEventListener(\"mouseout\", mouseout)\n  return () => {\n    el.removeEventListener(\"mousemove\", mousemove)\n    el.removeEventListener(\"mouseout\", mouseout)\n  }\n}\n","/* eslint-disable object-curly-newline */\n/* eslint-disable comma-dangle */\n/* eslint-disable implicit-arrow-linebreak */\n/* eslint-disable no-param-reassign */\n// @ts-nocheck\nimport { scaleLinear, extent } from \"d3\"\nimport {\n  getCellBoxSize,\n  getRows,\n  getColumns,\n  getXPosition,\n  getYPosition,\n  getFullWidth,\n  getFullHeight,\n} from \"./utilities\"\nimport registerEvents from \"./events\"\n\nexport const getWidth = (data, { aspectRatio, cellSize } = {}) => {\n  const rows = getRows(data, aspectRatio)\n  const columns = getColumns(rows, aspectRatio)\n  return getFullWidth(columns, cellSize)\n}\n\nconst getCanvasAttributes = (data, { aspectRatio, cellSize, padding } = {}) => {\n  const rows = getRows(data, aspectRatio)\n  const columns = getColumns(rows, aspectRatio)\n  const width = getFullWidth(columns, cellSize)\n  const height = getFullHeight(rows, cellSize, padding)\n\n  return { width, height, columns: Math.ceil(columns) }\n}\n\nconst defaultColorRange = [\"rgba(198, 227, 246, 0.9)\", \"rgba(14, 154, 255, 0.9)\"]\n\nconst makeGetColor = (values, colorRange = defaultColorRange) =>\n  scaleLinear()\n    .domain(extent(values, value => value))\n    .range(colorRange)\n\nexport default (el, { onMouseenter, onMouseout }, options = {}) => {\n  const { cellSize, cellPadding, cellStroke = 2, lineWidth = 1, colorRange } = options\n  const canvas = el.getContext(\"2d\")\n\n  let activeBox = -1\n  let deactivateBox = () => {}\n  let activateBox = {}\n  let clearEvents = () => {}\n\n  const clear = () => {\n    deactivateBox()\n    clearEvents()\n    canvas.clearRect(0, 0, el.width, el.height)\n    canvas.beginPath()\n  }\n\n  const update = ({ data }) => {\n    const { width, height, columns } = getCanvasAttributes(data, options)\n    el.width = parseInt(width)\n    el.height = parseInt(height)\n    clear()\n    clearEvents()\n    const getColor = makeGetColor(data, colorRange)\n\n    const drawBox = (value, index) => {\n      canvas.fillStyle = getColor(value)\n\n      const offsetX = getXPosition(columns, index, cellSize)\n      const offsetY = getYPosition(columns, index, cellSize)\n\n      if (lineWidth && cellStroke) {\n        canvas.clearRect(\n          offsetX - lineWidth,\n          offsetY - lineWidth,\n          getCellBoxSize(cellSize, cellPadding) + cellStroke,\n          getCellBoxSize(cellSize, cellPadding) + cellStroke\n        )\n      }\n\n      canvas.fillRect(\n        offsetX,\n        offsetY,\n        getCellBoxSize(cellSize, cellPadding),\n        getCellBoxSize(cellSize, cellPadding)\n      )\n    }\n\n    data.forEach(drawBox)\n\n    clearEvents = registerEvents(\n      el,\n      columns,\n      data.length,\n      {\n        onMouseenter,\n        onMouseout,\n      },\n      options\n    )\n\n    deactivateBox = () => {\n      if (activeBox !== -1) drawBox(data[activeBox], activeBox)\n    }\n\n    activateBox = index => {\n      deactivateBox()\n      activeBox = index\n\n      const offsetX = getXPosition(columns, index, cellSize)\n      const offsetY = getYPosition(columns, index, cellSize)\n\n      if (lineWidth && cellStroke) {\n        canvas.lineWidth = lineWidth\n        canvas.strokeStyle = \"#fff\"\n        canvas.strokeRect(\n          offsetX + lineWidth,\n          offsetY + lineWidth,\n          getCellBoxSize(cellSize, cellPadding) - cellStroke,\n          getCellBoxSize(cellSize, cellPadding) - cellStroke\n        )\n      }\n    }\n  }\n\n  return {\n    clear,\n    update,\n    activateBox: index => activateBox(index),\n    deactivateBox: () => deactivateBox(),\n  }\n}\n","/* eslint-disable arrow-body-style */\n// @ts-nocheck\n\nexport default (el) => {\n  return el.getBoundingClientRect().top / window.innerHeight > 0.5 ? \"top\" : \"bottom\"\n}\n","/* eslint-disable operator-linebreak */\n/* eslint-disable implicit-arrow-linebreak */\n/* eslint-disable object-curly-newline */\n/* eslint-disable react-hooks/exhaustive-deps */\n/* eslint-disable react/jsx-fragments */\n// @ts-nocheck\nimport React, { useRef, useLayoutEffect, Fragment, useState, useCallback } from \"react\"\nimport { Drop } from \"@netdata/netdata-ui\"\nimport drawBoxes from \"./drawBoxes\"\nimport getAlign from \"./getAlign\"\n\ninterface GroupboxData {\n  data: number[]\n  labels: string[]\n}\n\ninterface GroupBoxProps {\n  data: GroupboxData[]\n}\n\nconst aligns = {\n  top: { bottom: \"top\" },\n  bottom: { top: \"bottom\" },\n}\n\nconst GroupBox = ({ data, renderTooltip, ...options }: GroupBoxProps) => {\n  const dataRef = useRef()\n  const canvasRef = useRef()\n  const boxesRef = useRef()\n\n  const [hover, setHover] = useState(null)\n  const dropHoverRef = useRef(false)\n  const boxHoverRef = useRef(-1)\n  const timeoutId = useRef()\n\n  const close = () => {\n    boxesRef.current.deactivateBox()\n    setHover(null)\n    dropHoverRef.current = false\n    boxHoverRef.current = -1\n  }\n\n  const closeDrop = () =>\n    requestAnimationFrame(() => {\n      setHover(currentHover => {\n        if (\n          !dropHoverRef.current &&\n          (boxHoverRef.current === -1 || boxHoverRef.current !== currentHover?.index)\n        ) {\n          close()\n        }\n        return currentHover\n      })\n    })\n\n  useLayoutEffect(() => {\n    boxesRef.current = drawBoxes(\n      canvasRef.current,\n      {\n        onMouseenter: ({ index, ...rect }) => {\n          boxHoverRef.current = index\n          boxesRef.current.activateBox(index)\n          timeoutId.current = setTimeout(() => {\n            setHover({\n              target: { getBoundingClientRect: () => rect },\n              index,\n              rect,\n            })\n          }, 600)\n        },\n        onMouseout: () => {\n          boxHoverRef.current = -1\n          clearTimeout(timeoutId.current)\n          closeDrop()\n        },\n      },\n      options\n    )\n    return () => boxesRef.current.clear()\n  }, [])\n\n  useLayoutEffect(() => {\n    if (\n      hover &&\n      dataRef.current &&\n      dataRef.current.labels[hover.index] !== data.labels[hover.index]\n    ) {\n      close()\n    }\n    dataRef.current = data\n    boxesRef.current.update(data)\n  }, [data])\n\n  const onMouseEnter = useCallback(() => {\n    dropHoverRef.current = true\n  }, [])\n\n  const onMouseLeave = useCallback(() => {\n    dropHoverRef.current = false\n    closeDrop()\n  }, [])\n\n  const align = hover && getAlign(hover.target)\n\n  return (\n    <Fragment>\n      <canvas data-testid=\"groupBox\" ref={canvasRef} />\n      {hover && renderTooltip && (\n        <Drop\n          align={aligns[align]}\n          target={hover.target}\n          onMouseEnter={onMouseEnter}\n          onMouseLeave={onMouseLeave}\n        >\n          {renderTooltip(hover.index, align)}\n        </Drop>\n      )}\n    </Fragment>\n  )\n}\n\nexport default GroupBox\n","/* eslint-disable operator-linebreak */\n/* eslint-disable object-curly-newline */\n/* eslint-disable arrow-body-style */\n/* eslint-disable react/jsx-props-no-spreading */\n/* eslint-disable react/jsx-one-expression-per-line */\n// @ts-nocheck\nimport React, { useRef, useMemo } from \"react\"\nimport styled from \"styled-components\"\nimport { Flex, TextMicro, Popover } from \"@netdata/netdata-ui\"\nimport GroupBox from \"./groupBox\"\nimport { getWidth } from \"./drawBoxes\"\nimport getAlign from \"./getAlign\"\n\ninterface GroupBoxWrapperProps {\n  data: any\n  title: string\n}\n\nconst Title = styled.span`\n  white-space: nowrap;\n  text-overflow: ellipsis;\n  overflow-x: hidden;\n`\n\nconst Label = styled(Flex).attrs({\n  as: TextMicro,\n  gap: 1,\n})`\n  cursor: default;\n  &:hover {\n    font-weight: bold;\n  }\n`\n\nconst GroupBoxWrapper = ({\n  data,\n  label,\n  groupIndex,\n  renderGroupPopover,\n  renderBoxPopover,\n}: GroupBoxWrapperProps) => {\n  const ref = useRef()\n  const align = ref.current && getAlign(ref.current)\n\n  const style = useMemo(() => ({ maxWidth: `${getWidth(data.data)}px` }), [data])\n\n  const boxPopover =\n    renderBoxPopover &&\n    ((index, boxAlign) => renderBoxPopover({ group: label, groupIndex, align: boxAlign, index }))\n\n  const groupPopover =\n    renderGroupPopover && (() => renderGroupPopover({ group: label, groupIndex, align }))\n\n  return (\n    <Flex data-testid=\"groupBoxWrapper\" column alignItems=\"start\" gap={1} margin={[0, 4, 0, 0]}>\n      <Popover content={groupPopover} align={align} plain>\n        {({ isOpen, ref: popoverRef, ...rest }) => (\n          <Label\n            data-testid=\"groupBoxWrapper-title\"\n            ref={el => {\n              ref.current = el\n              popoverRef(el)\n            }}\n            strong={isOpen}\n            style={style}\n            {...rest}\n          >\n            <Title>{label}</Title>\n            {data.data.length > 3 && <span>({data.data.length})</span>}\n          </Label>\n        )}\n      </Popover>\n      <GroupBox data={data} renderTooltip={boxPopover} />\n    </Flex>\n  )\n}\n\nconst GroupBoxes = ({ data, labels, renderBoxPopover, renderGroupPopover }: any) => (\n  <Flex data-testid=\"groupBoxes\" flexWrap overflow={{ vertical: \"auto\" }} flex>\n    {labels.map((label, index) => {\n      return data[index].data.length ? (\n        <GroupBoxWrapper\n          key={label}\n          label={label}\n          groupIndex={index}\n          data={data[index]}\n          renderGroupPopover={renderGroupPopover}\n          renderBoxPopover={renderBoxPopover}\n        />\n      ) : null\n    })}\n  </Flex>\n)\n\nexport default GroupBoxes\n","import React from \"react\"\nimport styled from \"styled-components\"\nimport { Flex, TextNano } from \"@netdata/netdata-ui\"\n\ninterface LegendProps {\n  children?: React.ReactNode\n}\n\nconst LinearColorScaleBar = styled(Flex).attrs({ width: \"120px\", height: \"12px\", round: true })`\n  background: linear-gradient(to right, #c6e3f6, #0e9aff);\n`\n\nconst Legend = ({ children }: LegendProps) => (\n  <Flex data-testid=\"groupBox-legend\" gap={4} alignItems=\"center\">\n    <TextNano strong>{children}</TextNano>\n    <Flex gap={2} alignItems=\"center\">\n      <TextNano>0%</TextNano>\n      <LinearColorScaleBar />\n      <TextNano>100%</TextNano>\n    </Flex>\n    {/* <Flex gap={2} alignItems=\"center\">\n      <TextNano strong>Alarms</TextNano>\n      <Flex width=\"12px\" height=\"12px\" background=\"error\" round />\n    </Flex>\n    <Flex gap={2} alignItems=\"center\">\n      <TextNano strong>Warnings</TextNano>\n      <Flex width=\"12px\" height=\"12px\" background=\"warning\" round />\n    </Flex> */}\n  </Flex>\n)\n\nexport default Legend\n","// @ts-nocheck\n\nconst labels = {\n  k8s_cluster_id: { icon: \"cluster\", title: \"Cluster Id\" },\n  k8s_node_name: { icon: \"nodes_hollow\", title: \"Node\" },\n  k8s_namespace: { icon: \"cluster_spaces\", title: \"Namespace\" },\n  k8s_controller_kind: { icon: \"controller_kind\", title: \"Controller Kind\" },\n  k8s_controller_name: { icon: \"controller_name\", title: \"Controller Name\" },\n  k8s_pod_name: { icon: \"pod\", title: \"Pod Name\" },\n  k8s_container_name: { icon: \"container\", title: \"Container\" },\n}\n\nexport const labelIds = Object.keys(labels)\n\nexport default (id) => {\n  if (id in labels) return labels[id]\n  // k8s_custom_label -> Custom Label\n  const title = id.replace(/_./g, (word) => ` ${word[1].toUpperCase()}`).replace(/^k8s /, \"\")\n  return { title, icon: \"node\" }\n}\n","/* eslint-disable react/jsx-props-no-spreading */\n/* eslint-disable react/prop-types */\n// @ts-nocheck\nimport React from \"react\"\nimport { Flex } from \"@netdata/netdata-ui\"\n\nconst Separator = () => <Flex height=\"1px\" width=\"100%\" background=\"separator\" />\n\nexport default Separator\n","/* eslint-disable react/jsx-props-no-spreading */\n/* eslint-disable react/prop-types */\n// @ts-nocheck\nimport React from \"react\"\nimport { H5 } from \"@netdata/netdata-ui\"\n\nconst Header = props => (\n  <H5 color=\"bright\" wordBreak=\"break-all\" data-testid=\"k8sPopover-header\" {...props} />\n)\n\nexport default Header\n","/* eslint-disable indent */\n/* eslint-disable implicit-arrow-linebreak */\n/* eslint-disable react/jsx-props-no-spreading */\n/* eslint-disable react/prop-types */\n// @ts-nocheck\nimport React from \"react\"\nimport { Flex, Button, getColor } from \"@netdata/netdata-ui\"\nimport styled from \"styled-components\"\n\nexport const TabButton = styled(Button).attrs(({ active }) => ({\n  flavour: \"borderless\",\n  neutral: true,\n  themeType: \"dark\",\n  className: \"btn\",\n  disabled: active,\n  \"data-testid\": \"k8sPopoverChart-tab\",\n}))`\n  &&& {\n    height: initial;\n    width: initial;\n    padding: 2px 20px;\n    ${({ active, theme }) => active && `border-bottom: 3px solid ${getColor(\"bright\")({ theme })};`}\n    color: ${({ active, theme }) => getColor(active ? \"bright\" : \"separator\")({ theme })}\n  }\n`\n\nconst Tabs = ({ value, onChange, ...rest }) => (\n  <Flex data-testid=\"k8sPopoverChart-tabs\" {...rest}>\n    <TabButton label=\"Context\" active={value === \"context\"} onClick={() => onChange(\"context\")} />\n    <TabButton label=\"Metrics\" active={value === \"metrics\"} onClick={() => onChange(\"metrics\")} />\n  </Flex>\n)\n\nexport default Tabs\n","/* eslint-disable object-curly-newline */\n/* eslint-disable react/prop-types */\n// @ts-nocheck\nimport React from \"react\"\nimport { Flex, Button, H6, makeFlex } from \"@netdata/netdata-ui\"\nimport styled from \"styled-components\"\n\nconst ExpandButton = styled(makeFlex(Button)).attrs({\n  icon: \"chevron_right_s\",\n  label: \"More\",\n  flavour: \"borderless\",\n  neutral: true,\n  themeType: \"dark\",\n  className: \"btn\",\n  alignItems: \"baseline\",\n  gap: 1,\n  direction: \"rowReverse\",\n})`\n  &&& {\n    padding: 0;\n    margin: 0;\n    font-weight: normal;\n    height: initial;\n    width: initial;\n\n    svg {\n      height: 6px;\n      width: 6px;\n      position: initial;\n    }\n  }\n`\n\nconst Section = ({ title, onExpand, children, noBorder }) => (\n  <Flex\n    gap={3}\n    padding={[0, 0, 3]}\n    border={!noBorder && { side: \"bottom\", color: \"separator\" }}\n    column\n    data-testid=\"k8sPopoverSection\"\n  >\n    <Flex justifyContent=\"between\" data-testid=\"k8sPopoverSection-header\">\n      <H6 color=\"border\" wordBreak=\"break-all\">\n        {title}\n      </H6>\n      {onExpand && <ExpandButton onClick={onExpand} />}\n    </Flex>\n    <Flex gap={4} column data-testid=\"k8sPopoverSection-content\">\n      {children}\n    </Flex>\n  </Flex>\n)\n\nexport default Section\n","import { LOCALSTORAGE_HEIGHT_KEY_PREFIX } from \"domains/chart/components/resize-handler\"\n\nimport { LEGEND_BOTTOM_SINGLE_LINE_HEIGHT } from \"domains/chart/utils/legend-utils\"\nimport { Attributes } from \"./transformDataAttributes\"\nimport { ChartLibraryConfig } from \"./chartLibrariesSettings\"\n\ntype GetPortalNodeStyles = (\n  attributes: Attributes,\n  chartSettings: ChartLibraryConfig,\n  shouldAddSpecialHeight: boolean,\n) => {\n  height: string | undefined,\n  width: string | undefined,\n  minWidth: string | undefined\n}\n\nconst getHeightFromLocalStorage = (heightID: string, isLegendOnBottom: boolean) => {\n  const persitedHeight = localStorage.getItem(`${LOCALSTORAGE_HEIGHT_KEY_PREFIX}${heightID}`)\n  if (persitedHeight) {\n    if (Number.isNaN(Number(persitedHeight))) {\n      return null\n    }\n    return `${isLegendOnBottom\n      ? Number(persitedHeight) + LEGEND_BOTTOM_SINGLE_LINE_HEIGHT\n      : persitedHeight\n    }px`\n  }\n\n  return null\n}\n\nexport const getPortalNodeStyles: GetPortalNodeStyles = (\n  attributes,\n  chartSettings,\n  shouldAddSpecialHeight,\n) => {\n  let width\n  if (typeof attributes.width === \"string\") {\n    // eslint-disable-next-line prefer-destructuring\n    width = attributes.width\n  } else if (typeof attributes.width === \"number\") {\n    width = `${attributes.width.toString()}px`\n  }\n  let height\n  if (chartSettings.aspectRatio === undefined) {\n    if (typeof attributes.height === \"string\") {\n      // eslint-disable-next-line prefer-destructuring\n      height = attributes.height\n    } else if (typeof attributes.height === \"number\") {\n      height = `${attributes.height.toString()}px`\n    }\n  }\n  const isLegendOnBottom = attributes.legendPosition === \"bottom\"\n\n  const heightFromLocalStorage = attributes.heightId\n    ? getHeightFromLocalStorage(attributes.heightId, isLegendOnBottom)\n    : null\n\n  if (heightFromLocalStorage) {\n    // .replace() is for backwards compatibility -  old dashboard was always doing\n    // JSON.stringify when setting localStorage so many users have '\"180px\"' values set.\n    // We can remove .replace() after some time\n    height = heightFromLocalStorage.replace(/\"/g, \"\")\n  }\n\n  if (shouldAddSpecialHeight) {\n    const heightOverriden = isLegendOnBottom\n      ? window.innerHeight * 0.5\n      : window.innerHeight * 0.4\n    height = `${heightOverriden}px`\n  }\n\n  const chartDefaultsMinWidth = window.NETDATA.chartDefaults.min_width\n  const minWidth = chartDefaultsMinWidth === null\n    ? undefined\n    : chartDefaultsMinWidth\n  return {\n    height,\n    width,\n    minWidth,\n  }\n}\n","import {\n  useEffect, useRef, useState, MutableRefObject,\n} from \"react\"\n\nconst globalIntersectionOptions = {\n  root: null,\n  rootMargin: \"0px\",\n  threshold: undefined,\n}\n\ntype IntersectionCallback = (isVisible: boolean) => void\ntype Listener = {\n  element: HTMLElement,\n  callback: IntersectionCallback,\n}\nconst createGlobalIntersectionObserver = () => {\n  let listeners: Listener[] = []\n  const globalHandler = (entries: IntersectionObserverEntry[]) => {\n    entries.forEach(({ isIntersecting, target }) => {\n      const callback = listeners.find(({ element }) => element === target)?.callback\n      if (callback) {\n        callback(isIntersecting)\n      }\n    })\n  }\n  const globalObserver = new IntersectionObserver(globalHandler, globalIntersectionOptions)\n\n  return {\n    subscribe: (element: HTMLElement, callback: IntersectionCallback) => {\n      globalObserver.observe(element)\n      listeners = listeners.concat({ element, callback })\n    },\n    unsubscribe: (elementToUnsubscribe: HTMLElement) => {\n      listeners = listeners.filter(({ element }) => element !== elementToUnsubscribe)\n    },\n  }\n}\nconst globalIntersectionObserver = createGlobalIntersectionObserver()\n\n\n// this hook is created for 2 reasons:\n// 1) to use the same IntersectionObserver for all charts (contrary to use-intersection from\n//    react-use, which creates new observer for every hook)\n// 2) to update the isVisible state only when necessary (contrary to what \"use-in-view\" hook from\n//    https://github.com/thebuilder/react-intersection-observer does)\nexport const useCommonIntersection = (\n  element: HTMLElement,\n  clonedChildrenRef: MutableRefObject<HTMLElement | undefined>,\n) => {\n  const [isVisible, setIsVisible] = useState(false)\n  const isVisibleRef = useRef(isVisible)\n  // the ref is just to prevent most updates on init - charts are not visible on first intersection\n  // observer callback, but it still tries to set the state. UseState does not bail out when\n  // state doesn't change\n\n  useEffect(() => {\n    if (typeof IntersectionObserver === \"function\") {\n      globalIntersectionObserver.subscribe(\n        element,\n        (newIsVisible) => {\n          if (isVisibleRef.current !== newIsVisible) {\n            if (clonedChildrenRef.current) {\n              // eslint-disable-next-line no-param-reassign\n              clonedChildrenRef.current.style.visibility = newIsVisible ? \"visible\" : \"hidden\"\n            }\n\n            isVisibleRef.current = newIsVisible\n            // we need to mirror it in `use-state` to cause react update\n            setIsVisible(newIsVisible)\n          }\n        },\n      )\n    }\n    return () => {\n      globalIntersectionObserver.unsubscribe(element)\n    }\n  }, [clonedChildrenRef, element])\n\n  return isVisible\n}\n","import React from \"react\"\n\nimport { Attributes } from \"domains/chart/utils/transformDataAttributes\"\n\ninterface Props {\n  attributes: Attributes\n}\n\n// rendered on init (for example when chart is not visible)\n// and when it's rendering after being hidden previously\nexport const InvisibleSearchableText = ({\n  attributes,\n}: Props) => (\n  <span style={{ position: \"absolute\", opacity: 0, width: 0 }}>\n    {attributes.id}\n  </span>\n)\n","import React, {\n  useEffect, useLayoutEffect, useState, useRef,\n} from \"react\"\nimport { useDebounce } from \"react-use\"\nimport { forEachObjIndexed } from \"ramda\"\n\nimport { useDispatch, useSelector } from \"store/redux-separate-context\"\nimport { isPrintMode } from \"domains/dashboard/utils/parse-url\"\nimport { selectDestroyOnHide, selectIsAsyncOnScroll, selectAlarm } from \"domains/global/selectors\"\nimport { getPortalNodeStyles } from \"domains/chart/utils/get-portal-node-styles\"\nimport { Attributes } from \"domains/chart/utils/transformDataAttributes\"\nimport { chartLibrariesSettings } from \"domains/chart/utils/chartLibrariesSettings\"\nimport { useCommonIntersection } from \"hooks/use-common-intersection\"\n\nimport { clearChartStateAction } from \"../actions\"\n\nimport { InvisibleSearchableText } from \"./invisible-searchable-text\"\n\nconst SCROLL_DEBOUNCE_ASYNC = 750\nconst SCROLL_DEBOUNCE_SYNC = 100\n\nconst cloneWithCanvas = (element: HTMLElement) => {\n  const cloned = element.cloneNode(true) as HTMLElement\n  const clonedCanvases = cloned.querySelectorAll(\"canvas\")\n\n  element.querySelectorAll(\"canvas\")\n    .forEach((oldCanvas, index) => {\n      const newCanvas = clonedCanvases[index]\n      const context = newCanvas.getContext(\"2d\")\n\n      newCanvas.width = oldCanvas.width\n      newCanvas.height = oldCanvas.height\n\n      if (context) {\n        context.drawImage(oldCanvas, 0, 0)\n      }\n    })\n  return cloned\n}\n\nconst shouldCleanChartStateAlways = localStorage.getItem(\"wipe-chart-state\")\n\ninterface Props {\n  attributes: Attributes\n  chartUuid: string\n  children: any\n  portalNode: HTMLElement\n}\nexport const DisableOutOfView = ({\n  attributes,\n  chartUuid,\n  children,\n  portalNode,\n}: Props) => {\n  /* when unmounting, clear redux state for this chart */\n  const dispatch = useDispatch()\n  useEffect(() => { // eslint-disable-line arrow-body-style\n    return () => {\n      dispatch(clearChartStateAction({ id: chartUuid }))\n    }\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [])\n\n\n  /* separate functionality - adding custom styles to portalNode */\n  const chartSettings = chartLibrariesSettings[attributes.chartLibrary]\n  const [hasPortalNodeBeenStyled, setHasPortalNodeBeenStyled] = useState<boolean>(false)\n  const isShowingAlarmOnChart = useSelector(selectAlarm)?.chartId === attributes.id\n  useLayoutEffect(() => {\n    if (hasPortalNodeBeenStyled) {\n      return\n    }\n    const shouldAddSpecialHeight = isShowingAlarmOnChart\n      && attributes.chartLibrary === \"dygraph\"\n      && chartSettings.hasLegend(attributes)\n    const styles = getPortalNodeStyles(attributes, chartSettings, shouldAddSpecialHeight)\n    forEachObjIndexed((value, styleName) => {\n      if (value) {\n        portalNode.style.setProperty(styleName, value)\n      }\n    }, styles)\n    // eslint-disable-next-line no-param-reassign\n    portalNode.className = chartSettings.containerClass(attributes)\n    setHasPortalNodeBeenStyled(true)\n  }, [attributes, chartSettings, hasPortalNodeBeenStyled, isShowingAlarmOnChart, portalNode,\n    setHasPortalNodeBeenStyled ])\n  /* end of \"adding custom styles to portalNode\" */\n\n\n  const destroyOnHide = useSelector(selectDestroyOnHide)\n\n  const clonedChildrenRef = useRef<HTMLElement>()\n  const isVisibleIntersection = useCommonIntersection(portalNode, clonedChildrenRef)\n\n  // todo hook to scroll (observe on visible items) instead of changes in intersectionRatio\n  // because intersectinnRatio maxes out on 1.0 when element is fully visible\n  const isAsyncOnScroll = useSelector(selectIsAsyncOnScroll)\n  const debounceTime = isAsyncOnScroll ? SCROLL_DEBOUNCE_ASYNC : SCROLL_DEBOUNCE_SYNC\n\n  // \"should hide because of debounced scroll handler\"\n  const [shouldHideDebounced, setShouldHideDebounced] = useState(!isVisibleIntersection)\n  useDebounce(\n    () => {\n      // start rendering, when intersectionRatio is not 0 and it hasn't changed for 1500 ms\n      setShouldHideDebounced(!isVisibleIntersection)\n    },\n    debounceTime,\n    [isVisibleIntersection],\n  )\n  const shouldHide = isVisibleIntersection ? shouldHideDebounced : true\n\n  const previousIsVisibleIntersection = useRef(isVisibleIntersection)\n  if (clonedChildrenRef.current\n    && previousIsVisibleIntersection.current !== isVisibleIntersection\n  ) {\n    previousIsVisibleIntersection.current = isVisibleIntersection\n  }\n\n  useEffect(() => {\n    if (!isPrintMode && shouldHide && shouldCleanChartStateAlways) {\n      dispatch(clearChartStateAction({ id: chartUuid }))\n    }\n  }, [chartUuid, dispatch, shouldHide])\n\n\n  if (isPrintMode) {\n    // we should show everything in this case\n    return children\n  }\n\n  if (shouldHide) {\n    // todo perhaps loader should be added here to both scenarios\n    if (destroyOnHide) {\n      return (\n        <InvisibleSearchableText attributes={attributes} />\n      )\n    }\n\n    if (!clonedChildrenRef.current) {\n      const newClonedChildren = Array.from(portalNode.children)\n        .map((child) => cloneWithCanvas(child as HTMLElement))\n\n      const clonedChildrenContainer = document.createElement(\"div\")\n      clonedChildrenContainer.style.visibility = \"hidden\"\n\n      newClonedChildren.forEach((child) => {\n        clonedChildrenContainer.appendChild(child)\n      })\n\n      clonedChildrenRef.current = clonedChildrenContainer\n    }\n\n    return (\n      <>\n        <InvisibleSearchableText attributes={attributes} />\n        <div\n          ref={(nodeElement) => {\n            if (nodeElement && clonedChildrenRef.current) {\n              nodeElement.appendChild(clonedChildrenRef.current)\n            }\n          }}\n        />\n      </>\n    )\n  }\n\n  if (!destroyOnHide && clonedChildrenRef.current) {\n    clonedChildrenRef.current = undefined\n  }\n\n  return children\n}\n","import React from \"react\"\n\nimport { Attributes } from \"../../utils/transformDataAttributes\"\nimport { ChartMetadata } from \"../../chart-types\"\nimport { ChartWithLoader, RenderCustomElementForDygraph } from \"../chart-with-loader\"\nimport { DisableOutOfView } from \"../disable-out-of-view\"\nimport { DropdownMenu } from \"../chart-dropdown\"\n\nexport type Props = {\n  attributes: Attributes\n  // warning! this is not the same as chartId in old dashboard\n  // here, the chartID must be unique across all agents\n  chartUuid: string\n  uuid?: string\n  portalNode: HTMLElement\n  chartMetadata?: ChartMetadata | undefined\n  dropdownMenu?: DropdownMenu\n  renderCustomElementForDygraph?: RenderCustomElementForDygraph\n  onAttributesChange?: any\n}\n\nexport const ChartContainer = ({\n  attributes,\n  chartMetadata,\n  chartUuid,\n  dropdownMenu,\n  portalNode,\n  renderCustomElementForDygraph,\n  onAttributesChange,\n  uuid,\n}: Props) => (\n  <DisableOutOfView attributes={attributes} portalNode={portalNode} chartUuid={chartUuid}>\n    <ChartWithLoader\n      attributes={attributes}\n      chartUuid={chartUuid}\n      renderCustomElementForDygraph={renderCustomElementForDygraph}\n      onAttributesChange={onAttributesChange}\n      dropdownMenu={dropdownMenu}\n      externalChartMetadata={chartMetadata}\n      portalNode={portalNode}\n      uuid={uuid}\n    />\n  </DisableOutOfView>\n)\n","/* eslint-disable max-len */\nimport { ChartsMetadata } from \"domains/global/types\"\nimport { AnyStringKeyT } from \"types/common\"\nimport { ChartEnriched } from \"domains/chart/chart-types\"\n\nexport interface Submenus {\n  [submenus: string]: {\n    charts: ChartEnriched[]\n    height: number\n    info: string | null\n    priority: number\n    title: string | null\n  }\n}\n\nexport interface CorrelationMetadata {\n    scoredCount?: number\n    totalCount?: number\n    averageScore?: number\n}\n\nexport interface Menu {\n  // eslint-disable-next-line camelcase\n  menu_pattern: string\n  priority: number\n  submenus: Submenus\n  title: string\n  icon: string\n  info: string\n  height: number\n  correlationsMetadata?: CorrelationMetadata\n}\n\nexport interface Menus {\n  [menu: string]: Menu\n}\n\nexport const options = {\n  menus: {} as Menus,\n  submenu_names: {} as {[family: string]: string},\n  data: null as (ChartsMetadata | null),\n  hostname: \"netdata_server\", // will be overwritten by the netdata server\n  version: \"unknown\",\n  release_channel: \"unknown\",\n  hosts: [],\n\n  duration: 0, // the default duration of the charts\n  update_every: 1,\n\n  chartsPerRow: 0,\n  // chartsMinWidth: 1450,\n  chartsHeight: 180,\n}\n\n\n// netdata standard information\nexport const netdataDashboard = {\n  sparklines_registry: {} as {[key: string]: { count: number }},\n  os: \"unknown\",\n\n  menu: {},\n  submenu: {} as {\n    [family: string]: {\n      info?: string | ((os: string) => string)\n      title?: string\n    }\n  },\n  context: {} as {\n    [id: string]: {\n      valueRange: string // examples: \"[0, 100]\", \"[null, null]\"\n      height: number\n      decimalDigits: number\n  }},\n\n  // generate a sparkline\n  // used in the documentation\n  sparkline(\n    prefix: string, chart: string, dimension: string, units: string = \"\", suffix: string,\n  ) {\n    if (options.data === null || typeof options.data.charts === \"undefined\") {\n      return \"\"\n    }\n\n    if (typeof options.data.charts[chart] === \"undefined\") {\n      return \"\"\n    }\n\n    if (typeof options.data.charts[chart].dimensions === \"undefined\") {\n      return \"\"\n    }\n\n    if (typeof options.data.charts[chart].dimensions[dimension] === \"undefined\") {\n      return \"\"\n    }\n\n    let key = `${chart}.${dimension}`\n\n    if (typeof this.sparklines_registry[key] === \"undefined\") {\n      this.sparklines_registry[key] = { count: 1 }\n    } else {\n      this.sparklines_registry[key].count += 1\n    }\n\n    key = `${key}.${this.sparklines_registry[key].count}`\n\n    return `${prefix}<div class=\"netdata-container\" data-netdata=\"${chart}\" data-after=\"-120\"\n      data-width=\"25%\" data-height=\"15px\" data-chart-library=\"dygraph\"\n      data-dygraph-theme=\"sparkline\" data-dimensions=\"${dimension}\"\n      data-show-value-of-${dimension}-at=\"${key}\"></div>\n       (<span id=\"${key}\" style=\"display: inline-block; min-width: 50px; text-align: right;\">\n       X</span>${units})${suffix}`\n  },\n\n  gaugeChart(\n    title: string, width: string, dimensions: string = \"\", colors: string = \"\",\n  ) {\n    return `${\"<div class=\\\"netdata-container\\\" data-netdata=\\\"CHART_UNIQUE_ID\\\"\"\n      + \" data-dimensions=\\\"\"}${dimensions}\"`\n      + \" data-chart-library=\\\"gauge\\\"\"\n      + \" data-gauge-adjust=\\\"width\\\"\"\n      + ` data-title=\"${title}\"`\n      + ` data-width=\"${width}\"`\n      + \" data-before=\\\"0\\\"\"\n      + \" data-after=\\\"-CHART_DURATION\\\"\"\n      + \" data-points=\\\"CHART_DURATION\\\"\"\n      + ` data-colors=\"${colors}\"`\n      + \" role=\\\"application\\\"></div>\"\n  },\n\n  anyAttribute(obj: AnyStringKeyT, attr: string, key: string, def: unknown, domain?: string) {\n    if (typeof (obj[key]) !== \"undefined\") {\n      const config = obj[key]\n      const configWithDomain = domain ? {...config, ...config[domain]} : config\n      const x = configWithDomain[attr]\n\n      if (x === undefined) {\n        return def\n      }\n\n      if (typeof (x) === \"function\") {\n        return x(netdataDashboard.os)\n      }\n\n      return x\n    }\n\n    return def\n  },\n\n  menuTitle(chart: ChartEnriched) {\n    if (chart.sectionTitle) {\n      return chart.sectionTitle\n    }\n    if (typeof chart.menu_pattern !== \"undefined\") {\n      const type = chart.type || chart.id.split(\".\")[0]\n      return (`${this.anyAttribute(this.menu, \"title\", chart.menu_pattern, chart.menu_pattern)\n        .toString()\n      } ${type.slice(-(type.length - chart.menu_pattern.length - 1)).toString()}`)\n        .replace(/_/g, \" \")\n    }\n\n    return (this.anyAttribute(this.menu, \"title\", chart.menu, chart.menu)).toString()\n      .replace(/_/g, \" \")\n  },\n\n  menuIcon(chart: ChartEnriched) {\n    if (typeof chart.menu_pattern !== \"undefined\") {\n      return this.anyAttribute(this.menu, \"icon\", chart.menu_pattern,\n        \"<i class=\\\"fas fa-puzzle-piece\\\"></i>\").toString()\n    }\n\n    return this.anyAttribute(this.menu, \"icon\", chart.menu, \"<i class=\\\"fas fa-puzzle-piece\\\"></i>\")\n  },\n\n  menuInfo(chart: ChartEnriched) {\n    if (typeof chart.menu_pattern !== \"undefined\") {\n      return this.anyAttribute(this.menu, \"info\", chart.menu_pattern, null)\n    }\n\n    return this.anyAttribute(this.menu, \"info\", chart.menu, null)\n  },\n\n  menuHeight(chart: ChartEnriched) {\n    if (typeof chart.menu_pattern !== \"undefined\") {\n      return this.anyAttribute(this.menu, \"height\", chart.menu_pattern, 1.0)\n    }\n\n    return this.anyAttribute(this.menu, \"height\", chart.menu, 1.0)\n  },\n\n  submenuTitle(menu: string, submenu: string) {\n    const key = `${menu}.${submenu}`\n    // console.log(key);\n    const title = this.anyAttribute(this.submenu, \"title\", key, submenu)\n      .toString().replace(/_/g, \" \") as string\n    if (title.length > 28) {\n      const a = title.substring(0, 13)\n      const b = title.substring(title.length - 12, title.length)\n      return `${a}...${b}`\n    }\n    return title\n  },\n\n  submenuInfo(menu: string, submenu: string) {\n    const key = `${menu}.${submenu}`\n    return this.anyAttribute(this.submenu, \"info\", key, null) as (string | null)\n  },\n\n  submenuHeight(menu: string, submenu: string, relative: number) {\n    const key = `${menu}.${submenu}`\n    return this.anyAttribute(this.submenu, \"height\", key, 1.0) * relative\n  },\n\n  contextInfo(id: string, domain?: string) {\n    const x = this.anyAttribute(this.context, \"info\", id, null, domain)\n\n    if (x !== null) {\n      return `<div class=\"shorten dashboard-context-info\"\n        role=\"document\">${x}</div>`\n    }\n    return \"\"\n  },\n\n  contextValueRange(id: string) {\n    if (typeof this.context[id] !== \"undefined\"\n      && typeof this.context[id].valueRange !== \"undefined\"\n    ) {\n      try {\n        return JSON.parse(this.context[id].valueRange)\n      } catch (e) {\n        return [null, null]\n      }\n    }\n    return [null, null]\n  },\n\n  contextHeight(id: string, def: number) {\n    if (typeof this.context[id] !== \"undefined\" && typeof this.context[id].height !== \"undefined\") {\n      return def * this.context[id].height\n    }\n    return def\n  },\n\n  contextDecimalDigits(id: string, def: number) {\n    if (typeof this.context[id] !== \"undefined\"\n      && typeof this.context[id].decimalDigits !== \"undefined\"\n    ) {\n      return this.context[id].decimalDigits\n    }\n    return def\n  },\n}\n\n// @ts-ignore\nwindow.netdataDashboard = netdataDashboard\n","/* eslint-disable object-curly-newline */\n/* eslint-disable react/jsx-props-no-spreading */\n/* eslint-disable react/destructuring-assignment */\n/* eslint-disable operator-linebreak */\n/* eslint-disable arrow-body-style */\n/* eslint-disable react/prop-types */\n// @ts-nocheck\nimport React, { memo } from \"react\"\nimport styled from \"styled-components\"\nimport { Flex, Text } from \"@netdata/netdata-ui\"\nimport { useSelector } from \"store/redux-separate-context\"\nimport { useFormatters } from \"domains/chart/utils/formatters\"\nimport { netdataDashboard } from \"domains/dashboard/utils/netdata-dashboard\"\nimport { selectChartData } from \"domains/chart/selectors\"\n\nconst Title = styled(Text)`\n  text-overflow: ellipsis;\n  max-width: 120px;\n  overflow-x: hidden;\n`\n\nconst getUnitSign = unit => {\n  return unit === \"percentage\" ? \"%\" : ` ${unit.replace(/milliseconds/, \"ms\")}`\n}\n\nconst aggrMethods = {\n  avg: \"Average\",\n  sum: \"Sum\",\n  min: \"Min\",\n  max: \"Max\",\n}\nconst getAggregation = value => `${aggrMethods[value]}` || \"\"\n\nconst ChartValueContainer = memo(({ id, units, aggrMethod, displayedIndex }) => {\n  const chartData = useSelector((state: AppStateT) => selectChartData(state, { id }))\n\n  const value =\n    typeof displayedIndex === \"number\"\n      ? chartData.result[displayedIndex]\n      : chartData.view_latest_values[0]\n\n  const { legendFormatValue, unitsCurrent } = useFormatters({\n    attributes: {},\n    data: chartData,\n    units,\n    unitsCommon: null,\n    unitsDesired: null,\n    uuid: id,\n  })\n\n  const aggregation = getAggregation(aggrMethod)\n\n  return (\n    <Text\n      wordBreak=\"keep-all\"\n      color=\"bright\"\n      margin={[0, 0, 0, \"auto\"]}\n      data-testid=\"k8sPopoverChart-chartValue\"\n    >\n      {aggregation && (\n        <Text margin={[0, 1, 0, 0]} color=\"separator\" data-testid=\"k8sPopoverChart-chartValue-aggr\">\n          {aggregation}\n        </Text>\n      )}\n      {legendFormatValue(value)}\n      {getUnitSign(unitsCurrent)}\n    </Text>\n  )\n})\n\nconst ChartValue = ({ id, ...rest }) => {\n  const chartData = useSelector((state: AppStateT) => selectChartData(state, { id }))\n\n  if (!chartData || chartData.result.length === 0) return null\n  return <ChartValueContainer id={id} {...rest} />\n}\n\nconst ChartOverview = ({ id, chartMetadata, aggrMethod, displayedIndex }) => {\n  const { units, context } = chartMetadata\n  const title = context.replace(/cgroup\\./, \"\")\n  const icon = netdataDashboard.menuIcon(chartMetadata)\n\n  return (\n    <Flex gap={2} data-testid=\"k8sPopoverChart-overview\">\n      <Text color=\"bright\" dangerouslySetInnerHTML={{ __html: icon }} />\n      <Title color=\"bright\" data-testid=\"k8sPopoverChart-title\">\n        {title}\n      </Title>\n      <ChartValue id={id} units={units} aggrMethod={aggrMethod} displayedIndex={displayedIndex} />\n    </Flex>\n  )\n}\n\nexport default memo(ChartOverview)\n","import React, { useMemo } from \"react\"\nimport Anchor from \"@/src/components/anchor\"\nimport { Text } from \"@netdata/netdata-ui\"\nimport { useLocalStorage } from \"react-use\"\nimport { utmUrlSuffix } from \"utils/utils\"\nimport { utmParametersToString } from \"domains/global/selectors\"\n\nexport type UserStatus = \"LOGGED_IN\" | \"EXPIRED_LOGIN\" | \"UNKNOWN\"\nexport type NodeClaimedStatus = \"NOT_CLAIMED\" | \"CLAIMED\"\nexport type UserNodeAccess = \"NO_ACCESS\" | \"ACCESS_OK\"\ntype UserPreference = \"AGENT\" | \"CLOUD\" | \"UNDEFINED\"\nexport type NodeLiveness = \"LIVE\" | \"NOT_LIVE\"\ntype CTATYPE = \"NAVIGATE\" | \"REFRESH\"\n\nexport enum MigrationModalPromos {\n  PROMO_SIGN_IN_CLOUD = \"PROMO_SIGN_IN_CLOUD\",\n  PROMO_SIGN_UP_CLOUD = \"PROMO_SIGN_UP_CLOUD\",\n  PROMO_IVNITED_TO_SPACE = \"PROMO_IVNITED_TO_SPACE\",\n  PROMO_CLAIM_NODE = \"PROMO_CLAIM_NODE\",\n  PROMO_TO_USE_NEW_DASHBAORD = \"PROMO_TO_USE_NEW_DASHBAORD\",\n  FALLBACK_TO_AGENT = \"FALLBACK_TO_AGENT\",\n  NO_INFO_FALLBACK_TO_AGENT = \"NO_INFO_FALLBACK_TO_AGENT\",\n}\n\ntype MigrationModalActions = {\n  text: string\n  action: CTATYPE\n  toPath?: string\n  userPreference?: UserPreference | \"DONT_SHOW\"\n}\n\ntype MigrationModalContent = {\n  title: string\n  text: {\n    header: ((props: any) => React.ReactNode) | string\n    bullets?: Array<string | ((props?: any) => React.ReactNode)>\n    footer?: ((props: any) => React.ReactNode) | string\n  }\n  tickBoxOption: { text: string; preferenceID: MigrationModalPromos }\n  CTA1: MigrationModalActions\n  CTA2?: MigrationModalActions\n}\n\ntype MigrationModalInfo = {\n  [key in MigrationModalPromos]: MigrationModalContent\n}\n\nconst campaign = \"agent_nudge_to_cloud\"\n\nconst makeUTMParameters = (modalPromo: MigrationModalPromos) =>\n  `${utmUrlSuffix}${utmParametersToString({\n    content: modalPromo,\n    campaign,\n  })}`\n\nexport const migrationmodalInfo: MigrationModalInfo = {\n  [MigrationModalPromos.PROMO_SIGN_UP_CLOUD]: {\n    title: \"Learn about Netdata Cloud!\",\n    text: {\n      header: () => (\n        <Text strong>\n          Netdata Cloud is a FREE service that complements the Netdata Agent, to provide:\n        </Text>\n      ),\n      bullets: [\n        \"Infrastructure level dashboards (each chart aggregates data from multiple nodes)\",\n        \"Central dispatch of alert notifications\",\n        \"Custom dashboards editor\",\n        \"Intelligence assisted troubleshooting, to help surface the root cause of issues\",\n      ],\n      footer: \"Have a look, you will be surprised!\",\n    },\n    tickBoxOption: {\n      text: \"Remember my choice\",\n      preferenceID: MigrationModalPromos.PROMO_SIGN_UP_CLOUD,\n    },\n    CTA1: {\n      text: \"Wow! Let’s go to Netdata Cloud\",\n      toPath: \"path/signup/cloud\",\n      action: \"NAVIGATE\",\n      userPreference: \"CLOUD\",\n    },\n    CTA2: {\n      text: \"Later, stay at the agent dashboard\",\n      action: \"NAVIGATE\",\n      toPath: \"path/agent-dashboard\",\n      userPreference: \"AGENT\",\n    },\n  },\n  [MigrationModalPromos.PROMO_SIGN_IN_CLOUD]: {\n    title: \"Sign-in to Netdata Cloud or get an invitation!\",\n    text: {\n      header: () => (\n        <>\n          <Text strong>\n            This node is connected to Netdata Cloud but you are not. If you have a Netdata Cloud\n            account sign-in, if not ask for an invitation to it.\n          </Text>\n\n          <Text>\n            Netdata Cloud is a FREE service that complements the Netdata Agent, to provide:\n          </Text>\n        </>\n      ),\n      bullets: [\n        \"Infrastructure level dashboards (each chart aggregates data from multiple nodes)\",\n        \"Central dispatch of alert notifications\",\n        \"Custom dashboards editor\",\n        \"Intelligence assisted troubleshooting, to help surface the root cause of issues\",\n      ],\n      footer: \"Have a look, you will be surprised!\",\n    },\n    tickBoxOption: {\n      text: \"Remember my choice\",\n      preferenceID: MigrationModalPromos.PROMO_SIGN_IN_CLOUD,\n    },\n    CTA1: {\n      text: \"Sign-in or get a Netdata Cloud account\",\n      action: \"NAVIGATE\",\n      toPath: \"path/signin/cloud\",\n      userPreference: \"CLOUD\",\n    },\n    CTA2: {\n      text: \"Later, stay at the Agent dashboard\",\n      toPath: \"path/agent-dashboard\",\n      action: \"NAVIGATE\",\n      userPreference: \"AGENT\",\n    },\n  },\n  [MigrationModalPromos.PROMO_IVNITED_TO_SPACE]: {\n    title: \"Get an invitation to this Node’s Space!\",\n    text: {\n      header: () => (\n        <Text strong>\n          This node is connected to Netdata Cloud but it isnt available on one of your Spaces.\n        </Text>\n      ),\n      bullets: [],\n      footer: \"Ask for an invitation to this Space!\",\n    },\n    tickBoxOption: {\n      text: \"Don't remind me of this again\",\n      preferenceID: MigrationModalPromos.PROMO_IVNITED_TO_SPACE,\n    },\n    CTA1: {\n      text: \"Thanks, stay at Agent dashboard for now\",\n      toPath: \"agent\",\n      action: \"NAVIGATE\",\n      userPreference: \"AGENT\",\n    },\n  },\n  [MigrationModalPromos.PROMO_CLAIM_NODE]: {\n    title: \"This node isn’t connected to Netdata Cloud\",\n    text: {\n      header: () => (\n        <Text strong>\n          For you to be able to see this node on Netdata Cloud you will either need to:\n        </Text>\n      ),\n      footer: \"Have a look, you will be surprised!\",\n      bullets: [\n        () => {\n          return (\n            <Text>\n              {\" \"}\n              Connect this node directly (documentation on{\" \"}\n              <Anchor\n                target=\"_blank\"\n                rel=\"noopener noreferrer\"\n                href={`https://learn.netdata.cloud/docs/agent/claim?${makeUTMParameters(\n                  MigrationModalPromos.PROMO_CLAIM_NODE\n                ).substring(1)}#how-to-connect-a-node`}\n              >\n                how to connect a node\n              </Anchor>\n              ) , or\n            </Text>\n          )\n        },\n        () => {\n          return (\n            <Text>\n              Αctivate streaming to a parent node that is already connected (documentation on{\" \"}\n              <Anchor\n                target=\"_blank\"\n                rel=\"noopener noreferrer\"\n                href={`https://learn.netdata.cloud/docs/metrics-storage-management/enable-streaming?${makeUTMParameters(\n                  MigrationModalPromos.PROMO_CLAIM_NODE\n                ).substring(1)}`}\n              >\n                how to configure streaming\n              </Anchor>\n              )\n            </Text>\n          )\n        },\n      ],\n    },\n    tickBoxOption: {\n      text: \"Remember my choice.\",\n      preferenceID: MigrationModalPromos.PROMO_CLAIM_NODE,\n    },\n    CTA1: {\n      text: \"Wow! Let’s go to Netdata Cloud\",\n      action: \"NAVIGATE\",\n      toPath: \"path/node/cloud\",\n      userPreference: \"CLOUD\",\n    },\n    CTA2: {\n      text: \"Later, stay at the Agent dashboard\",\n      action: \"NAVIGATE\",\n      toPath: \"path/agent-dashboard\",\n      userPreference: \"AGENT\",\n    },\n  },\n  [MigrationModalPromos.PROMO_TO_USE_NEW_DASHBAORD]: {\n    title: \"Use the Old or the New dashboard?\",\n    text: {\n      header: () => (\n        <Text strong>\n          This node is available in your Netdata Cloud account. So, you have full access to the NEW\n          dashboards, charts, intelligence-assisted troubleshooting and many more!\n        </Text>\n      ),\n      bullets: [],\n    },\n    tickBoxOption: {\n      text: \"Remember my choice\",\n      preferenceID: MigrationModalPromos.PROMO_TO_USE_NEW_DASHBAORD,\n    },\n    CTA1: {\n      text: \"Wow! Let’s go to Netdata Cloud \",\n      action: \"NAVIGATE\",\n      toPath: \"path/dashboard/cloud\",\n      userPreference: \"CLOUD\",\n    },\n    CTA2: {\n      text: \"Later, stay at the Agent dashboard\",\n      action: \"NAVIGATE\",\n      toPath: \"path/agent-dashboard\",\n      userPreference: \"AGENT\",\n    },\n  },\n  [MigrationModalPromos.FALLBACK_TO_AGENT]: {\n    title: \"Oops! This node has lost connection to Netdata Cloud!\",\n    text: {\n      header: ({ date = \"\" }) => {\n        return (\n          <>\n            <Text strong>\n              Unfortunately, it seems that this node is not currently connected to Netdata Cloud.\n              So, the old agent dashboard is the only option available.\n            </Text>\n            {/* <Text>\n              The node lost its Netdata Cloud connection at <Text strong>{date}</Text>.\n            </Text> */}\n            <Text>\n              To troubleshoot Netdata Cloud connection issues, please follow this{\" \"}\n              <Anchor\n                target=\"_blank\"\n                rel=\"noopener noreferrer\"\n                href={`https://learn.netdata.cloud/docs/agent/claim?${makeUTMParameters(\n                  MigrationModalPromos.FALLBACK_TO_AGENT\n                ).substring(1)}#troubleshooting`}\n              >\n                this guide.\n              </Anchor>\n            </Text>\n          </>\n        )\n      },\n      bullets: [],\n    },\n    tickBoxOption: {\n      text: \"Don't show this again\",\n      preferenceID: MigrationModalPromos.FALLBACK_TO_AGENT,\n    },\n    CTA1: {\n      text: \"Check again please\",\n      action: \"REFRESH\",\n      userPreference: undefined,\n    },\n    CTA2: {\n      text: \"Thanks, stay at Agent dashboard\",\n      toPath: \"path/agent\",\n      action: \"NAVIGATE\",\n      userPreference: \"AGENT\",\n    },\n  },\n  [MigrationModalPromos.NO_INFO_FALLBACK_TO_AGENT]: {\n    title: \"Oops! We aren't able to get information of this node in regards to Netdata Cloud!\",\n    text: {\n      header: () => {\n        return (\n          <>\n            <Text strong>\n              Unfortunately, it seems we aren't able to get information on this node in regards to\n              Netdata Cloud.\n            </Text>\n            <Text>\n              This could be from internet connectivity issues from your end or some temporary issue\n              with our services. So, the old agent dashboard is the only option available.\n            </Text>\n          </>\n        )\n      },\n      bullets: [],\n    },\n    tickBoxOption: {\n      text: \"Don't show this again\",\n      preferenceID: MigrationModalPromos.NO_INFO_FALLBACK_TO_AGENT,\n    },\n    CTA1: {\n      text: \"Check again please\",\n      action: \"REFRESH\",\n      userPreference: undefined,\n    },\n    CTA2: {\n      text: \"Thanks, stay at Agent dashboard\",\n      toPath: \"path/agent-dashboard\",\n      action: \"NAVIGATE\",\n      userPreference: \"AGENT\",\n    },\n  },\n}\n\nexport type PromoProps = {\n  userSavedPreference?: UserPreference\n  userStatus?: UserStatus\n  nodeClaimedStatus?: NodeClaimedStatus\n  userNodeAccess?: UserNodeAccess\n  nodeLiveness?: NodeLiveness\n}\n\nconst isPromoSignUp = ({\n  userSavedPreference,\n  userStatus,\n  nodeClaimedStatus,\n}: PromoProps): boolean =>\n  userSavedPreference !== \"AGENT\" && userStatus === \"UNKNOWN\" && nodeClaimedStatus === \"NOT_CLAIMED\"\n\nconst isPromoSignIn = ({\n  userSavedPreference,\n  userStatus,\n  nodeClaimedStatus,\n}: PromoProps): boolean =>\n  userSavedPreference !== \"AGENT\" && userStatus === \"UNKNOWN\" && nodeClaimedStatus === \"CLAIMED\"\n\nconst isPromoInvitedToSpace = ({\n  userSavedPreference,\n  userStatus,\n  nodeClaimedStatus,\n  userNodeAccess,\n}: PromoProps): boolean =>\n  userSavedPreference !== \"AGENT\" &&\n  (userStatus === \"LOGGED_IN\" || userStatus === \"EXPIRED_LOGIN\") &&\n  nodeClaimedStatus === \"CLAIMED\" &&\n  userNodeAccess === \"NO_ACCESS\"\n\nconst isPromoToClaimThisNode = ({\n  userSavedPreference,\n  userStatus,\n  nodeClaimedStatus,\n}: PromoProps): boolean =>\n  userSavedPreference !== \"AGENT\" &&\n  (userStatus === \"LOGGED_IN\" || userStatus === \"EXPIRED_LOGIN\") &&\n  nodeClaimedStatus === \"NOT_CLAIMED\"\n\nconst isPromoToNewDasboardOnCloud = ({\n  userSavedPreference,\n  userStatus,\n  nodeLiveness,\n  userNodeAccess,\n}: PromoProps): boolean =>\n  !userSavedPreference &&\n  (userStatus === \"LOGGED_IN\" || userStatus === \"EXPIRED_LOGIN\") &&\n  nodeLiveness === \"LIVE\" &&\n  userNodeAccess === \"ACCESS_OK\"\n\nconst isNoInfoFallbackToAgent = ({\n  userSavedPreference,\n  userStatus,\n  nodeClaimedStatus,\n  nodeLiveness,\n  userNodeAccess,\n}: PromoProps): boolean =>\n  userSavedPreference === \"CLOUD\" &&\n  !userStatus &&\n  !nodeClaimedStatus &&\n  !nodeLiveness &&\n  !userNodeAccess\n\nconst isFallbackToAgent = ({\n  userSavedPreference,\n  userStatus,\n  nodeClaimedStatus,\n  nodeLiveness,\n  userNodeAccess,\n}: PromoProps): boolean =>\n  userSavedPreference !== \"AGENT\" &&\n  (userStatus === \"LOGGED_IN\" || userStatus === \"EXPIRED_LOGIN\") &&\n  nodeClaimedStatus === \"CLAIMED\" &&\n  nodeLiveness === \"NOT_LIVE\" &&\n  userNodeAccess === \"ACCESS_OK\"\n\nexport const goToAgentDashboard = ({ userSavedPreference }: PromoProps) =>\n  userSavedPreference === \"AGENT\"\n\nexport const goToCloud = ({\n  userSavedPreference,\n  userStatus,\n  nodeLiveness,\n  userNodeAccess,\n}: PromoProps) =>\n  userSavedPreference === \"CLOUD\" &&\n  (userStatus === \"LOGGED_IN\" || userStatus === \"EXPIRED_LOGIN\") &&\n  nodeLiveness === \"LIVE\" &&\n  userNodeAccess === \"ACCESS_OK\"\n\nconst modalStatusWithPromoFunctions: Record<\n  MigrationModalPromos,\n  (props: PromoProps) => boolean\n> = {\n  [MigrationModalPromos.FALLBACK_TO_AGENT]: isFallbackToAgent,\n  [MigrationModalPromos.NO_INFO_FALLBACK_TO_AGENT]: isNoInfoFallbackToAgent,\n  [MigrationModalPromos.PROMO_TO_USE_NEW_DASHBAORD]: isPromoToNewDasboardOnCloud,\n  [MigrationModalPromos.PROMO_CLAIM_NODE]: isPromoToClaimThisNode,\n  [MigrationModalPromos.PROMO_IVNITED_TO_SPACE]: isPromoInvitedToSpace,\n  [MigrationModalPromos.PROMO_SIGN_IN_CLOUD]: isPromoSignIn,\n  [MigrationModalPromos.PROMO_SIGN_UP_CLOUD]: isPromoSignUp,\n}\n\nconst useMigrationModal = ({\n  userStatus,\n  nodeClaimedStatus,\n  userNodeAccess,\n  nodeLiveness,\n}: PromoProps) => {\n  const [userSavedPreference, setUserPrefrence] = useLocalStorage<UserPreference>(\n    \"USER_SAVED_PREFERENCE\"\n  )\n\n  const migrationModalPromo = useMemo<MigrationModalPromos>(() => {\n    return Object.keys(modalStatusWithPromoFunctions).find(modalStatus => {\n      return modalStatusWithPromoFunctions[modalStatus]({\n        userStatus,\n        nodeClaimedStatus,\n        userNodeAccess,\n        userSavedPreference,\n        nodeLiveness,\n      })\n    }) as MigrationModalPromos\n  }, [userStatus, nodeClaimedStatus, userNodeAccess, nodeLiveness, userSavedPreference])\n\n  return {\n    migrationModalPromoInfo: migrationmodalInfo[migrationModalPromo],\n    migrationModalPromo,\n    setUserPrefrence,\n    userSavedPreference,\n  }\n}\n\nexport default useMigrationModal\n","/* eslint-disable comma-dangle */\n/* eslint-disable react-hooks/exhaustive-deps */\n/* eslint-disable object-curly-newline */\n/* eslint-disable react/prop-types */\n// @ts-nocheck\nimport React, { useRef, useContext, useLayoutEffect, useState, memo, useMemo } from \"react\"\nimport { throttle } from \"throttle-debounce\"\nimport { ChartContainer } from \"domains/chart/components/chart-container\"\nimport { ThemeContext } from \"styled-components\"\nimport { Flex, getColor } from \"@netdata/netdata-ui\"\nimport ChartOverview from \"./chartOverview\"\n\nconst Chart = ({ groupLabel, postGroupLabel, id, attributes, relatedIndex }) => {\n  const theme = useContext(ThemeContext)\n  const chartContainerRef = useRef()\n  const [displayedIndex, setDisplayedIndex] = useState()\n  const setDisplayedIndexThrottled = useMemo(() => throttle(400, setDisplayedIndex), [])\n  const [, repaint] = useState()\n\n  useLayoutEffect(() => {\n    repaint(true)\n  }, [])\n\n  const { chartMetadata, attributes: relatedChartAttributes } = attributes.relatedCharts[\n    relatedIndex\n  ]\n\n  const chartAttributes = useMemo(\n    () => ({\n      id: chartMetadata.id,\n\n      width: \"100%\",\n      height: \"60px\",\n\n      chartLibrary: \"sparkline\",\n      sparklineLineWidth: \"2px\",\n      sparklineLineColor: getColor(\"border\")({ theme }),\n      sparklineFillColor: getColor(\"disabled\")({ theme }),\n      sparklineSpotRadius: 0,\n      sparklineDisableTooltips: true,\n      sparklineOnHover: (event) => setDisplayedIndexThrottled(event?.x),\n\n      httpMethod: \"POST\",\n      host: attributes.host,\n      nodeIDs: attributes.nodeIDs,\n      dimensions: relatedChartAttributes.dimensions,\n      aggrMethod: relatedChartAttributes.aggrMethod,\n\n      labels: {\n        k8s_cluster_id: [chartMetadata.chartLabels.k8s_cluster_id[0]],\n        [attributes.groupBy]: [groupLabel],\n        ...(postGroupLabel && { [attributes.postGroupBy]: [postGroupLabel] }),\n      },\n    }),\n    [chartMetadata, attributes]\n  )\n\n  return (\n    <Flex gap={2} column data-testid=\"k8sPopoverChart\">\n      <div\n        ref={chartContainerRef}\n        style={{ height: \"60px\", width: \"100%\" }}\n        data-testid=\"k8sPopoverChart-container\"\n      >\n        {chartContainerRef.current && (\n          <ChartContainer\n            chartUuid={id}\n            attributes={chartAttributes}\n            chartMetadata={chartMetadata}\n            portalNode={chartContainerRef.current}\n          />\n        )}\n      </div>\n      <ChartOverview\n        id={id}\n        aggrMethod={chartAttributes.aggrMethod}\n        chartMetadata={chartMetadata}\n        displayedIndex={displayedIndex}\n      />\n    </Flex>\n  )\n}\n\nexport default memo(Chart)\n","/* eslint-disable object-curly-newline */\n/* eslint-disable react/prop-types */\n// @ts-nocheck\nimport React from \"react\"\nimport styled from \"styled-components\"\nimport { Text, Flex, Icon } from \"@netdata/netdata-ui\"\n\nconst ExternalButton = styled(Icon).attrs({\n  margin: [0, 0, 0, \"auto\"],\n  color: \"bright\",\n  width: \"10px\",\n  height: \"10px\",\n  alignSelf: \"center\",\n  name: \"nav_arrow_goto\",\n  role: \"button\",\n  title: \"Go to node\",\n  \"data-testid\": \"k8sPopoverItem-externalButton\",\n})`\n  cursor: pointer;\n`\n\nconst Item = ({ icon, title, secondary, onClick }) => (\n  <Flex gap={1} alignItems=\"start\" data-testid=\"k8sPopoverItem\">\n    <Flex width=\"22px\" height=\"22px\" data-testid=\"k8sPopoverItem-icon\">\n      <Icon name={icon} color=\"bright\" margin={[0, 1, 0, 0]} width=\"22px\" height=\"22px\" />\n    </Flex>\n    <Text color=\"bright\" data-testid=\"k8sPopoverItem-title\">\n      {title}\n    </Text>\n    {secondary && (\n      <Text color=\"border\" wordBreak=\"break-all\" data-testid=\"k8sPopoverItem-detail\">\n        {secondary}\n      </Text>\n    )}\n    {onClick && <ExternalButton onClick={onClick} />}\n  </Flex>\n)\n\nexport default Item\n","/* eslint-disable react/prop-types */\n// @ts-nocheck\nimport React from \"react\"\nimport { useDateTime } from \"utils/date-time\"\nimport Item from \"./item\"\nimport Section from \"./section\"\n\nconst DateItem = ({ date, title }) => {\n  const { localeDateString, localeTimeString } = useDateTime()\n\n  return (\n    <Item\n      icon=\"around_clock\"\n      title={title}\n      secondary={`${localeDateString(date)} | ${localeTimeString(date)}`}\n    />\n  )\n}\n\nconst DateSection = ({ before, after }) => (\n  <Section title=\"Time\">\n    <DateItem title=\"From\" date={after} />\n    <DateItem title=\"To\" date={before} />\n  </Section>\n)\n\nexport default DateSection\n","/* eslint-disable object-curly-newline */\n/* eslint-disable react/prop-types */\n// @ts-nocheck\nimport React from \"react\"\nimport { Flex } from \"@netdata/netdata-ui\"\nimport Section from \"./section\"\nimport Chart from \"./chart\"\nimport DateSection from \"./dateSection\"\n\nconst Metrics = ({ groupLabel, postGroupLabel, attributes, viewAfter, viewBefore }) => (\n  <Flex gap={3} column width=\"100%\" data-testid=\"k8sPopoverMetrics\">\n    <DateSection after={viewAfter} before={viewBefore} />\n    <Section title=\"Metrics\" noBorder>\n      <Flex gap={3} column data-testid=\"k8sPopoverMetrics-container\">\n        {attributes.relatedCharts.map(({ chartMetadata }, index) => (\n          <Chart\n            key={chartMetadata.id}\n            id={[groupLabel, postGroupLabel, attributes.id, chartMetadata.id].join(\"|\")}\n            attributes={attributes}\n            relatedIndex={index}\n            groupLabel={groupLabel}\n            postGroupLabel={postGroupLabel}\n          />\n        ))}\n      </Flex>\n    </Section>\n  </Flex>\n)\n\nexport default Metrics\n","/* eslint-disable no-param-reassign */\n/* eslint-disable comma-dangle */\n/* eslint-disable object-curly-newline */\n/* eslint-disable react/jsx-props-no-spreading */\n/* eslint-disable react/prop-types */\n// @ts-nocheck\nimport React, { memo } from \"react\"\nimport { Flex } from \"@netdata/netdata-ui\"\nimport Item from \"./item\"\nimport Section from \"./section\"\nimport getLabel, { labelIds } from \"./getLabel\"\n\nconst LabelsSection = ({ labelId, items, onExpand, onItemClick, ...rest }) => {\n  const { title, icon } = getLabel(labelId)\n  const sliced = items.slice(0, 3)\n  const expandable = items.length > 3\n\n  const text = expandable ? `${title} (${items.length})` : title\n  return (\n    <Section title={text} onExpand={expandable && onExpand} {...rest}>\n      {sliced.map((item) => (\n        <Item\n          key={item}\n          icon={icon}\n          title={item}\n          onClick={onItemClick && (() => onItemClick(item))}\n        />\n      ))}\n    </Section>\n  )\n}\n\nconst getLabelIds = (chartLabels) => {\n  chartLabels = { ...chartLabels }\n  const predefinedLabelIds = labelIds.reduce((acc, labelId) => {\n    if (!(labelId in chartLabels)) return acc\n\n    delete chartLabels[labelId]\n    return [...acc, labelId]\n  }, [])\n\n  return [...predefinedLabelIds, ...Object.keys(chartLabels)]\n}\n\nconst Context = ({ chartLabels, onExpand, onNodeClick }) => {\n  const ids = getLabelIds(chartLabels)\n\n  return (\n    <Flex gap={3} column width=\"100%\" data-testid=\"k8sPopoverContext\">\n      {ids.map((id, index) => (\n        <LabelsSection\n          key={id}\n          labelId={id}\n          items={chartLabels[id]}\n          onExpand={() => onExpand(id)}\n          noBorder={index === ids.length - 1}\n          onItemClick={id === \"k8s_node_name\" && onNodeClick}\n        />\n      ))}\n    </Flex>\n  )\n}\n\nexport default memo(Context)\n","/* eslint-disable object-curly-newline */\n/* eslint-disable react/prop-types */\n// @ts-nocheck\nimport React from \"react\"\nimport { Flex, Button, makeFlex } from \"@netdata/netdata-ui\"\nimport styled from \"styled-components\"\nimport Separator from \"./separator\"\nimport Header from \"./header\"\nimport Item from \"./item\"\nimport getLabel from \"./getLabel\"\n\nconst StyledButton = styled(makeFlex(Button)).attrs({\n  flavour: \"borderless\",\n  neutral: true,\n  themeType: \"dark\",\n  className: \"btn\",\n  alignItems: \"start\",\n  gap: 1,\n})`\n  &&& {\n    padding: 0;\n    margin: 0;\n    height: initial;\n    width: initial;\n\n    svg {\n      height: 18px;\n      width: 18px;\n      position: initial;\n    }\n  }\n`\n\nconst List = ({ labelId, items, onBack, onItemClick }) => {\n  const { title, icon } = getLabel(labelId)\n\n  return (\n    <Flex height=\"100%\" gap={3} data-testid=\"k8sPopoverList\" column>\n      <Header>\n        <StyledButton\n          label={`${title} (${items.length})`}\n          icon=\"chevron_left\"\n          onClick={onBack}\n          data-testid=\"k8sPopoverList-back\"\n        />\n      </Header>\n      <Separator />\n      <Flex\n        gap={3}\n        overflow={{ vertical: \"auto\", horizontal: \"hidden\" }}\n        column\n        data-testid=\"k8sPopoverList-container\"\n      >\n        {items.map((item) => (\n          <Item\n            key={item}\n            icon={icon}\n            title={item}\n            onClick={onItemClick && (() => onItemClick(item))}\n          />\n        ))}\n      </Flex>\n    </Flex>\n  )\n}\n\nexport default List\n","/* eslint-disable object-curly-newline */\n/* eslint-disable react/jsx-props-no-spreading */\n/* eslint-disable react/prop-types */\n// @ts-nocheck\nimport React, { useState } from \"react\"\nimport { Flex, DropContainer } from \"@netdata/netdata-ui\"\nimport Separator from \"./separator\"\nimport Header from \"./header\"\nimport Tabs from \"./tabs\"\nimport Metrics from \"./metrics\"\nimport Context from \"./context\"\nimport List from \"./list\"\n\nconst Container = (props) => (\n  <DropContainer\n    background={[\"transparent\", \"popover\"]}\n    padding={[2, 4]}\n    width=\"322px\"\n    height=\"422px\"\n    {...props}\n  />\n)\n\nconst TabsContainer = ({ label, value, onChange, children }) => (\n  <Flex height=\"100%\" column>\n    <Header>{label}</Header>\n    <Tabs value={value} onChange={onChange} margin={[4, 0, 0, 0]} />\n    <Separator />\n    <Flex gap={3} overflow={{ vertical: \"auto\", horizontal: \"hidden\" }} margin={[4, 0, 0, 0]}>\n      {children}\n    </Flex>\n  </Flex>\n)\n\nconst Popover = ({\n  title,\n  groupLabel,\n  postGroupLabel,\n  chartLabels,\n  attributes,\n  viewBefore,\n  viewAfter,\n  ...rest\n}) => {\n  const [view, setView] = useState(\"context\")\n\n  const isLabelView = view !== \"context\" && view !== \"metrics\"\n\n  const { onNodeClick } = attributes\n\n  return (\n    <Container data-testid=\"k8sPopover\" {...rest}>\n      {isLabelView && (\n        <List\n          labelId={view}\n          items={chartLabels[view]}\n          attributes={attributes}\n          onBack={() => setView(\"context\")}\n          onItemClick={view === \"k8s_node_name\" && onNodeClick}\n        />\n      )}\n      {!isLabelView && (\n        <TabsContainer label={title} value={view} onChange={setView}>\n          {view === \"context\" && (\n            <Context chartLabels={chartLabels} onExpand={setView} onNodeClick={onNodeClick} />\n          )}\n          {view === \"metrics\" && (\n            <Metrics\n              groupLabel={groupLabel}\n              postGroupLabel={postGroupLabel}\n              attributes={attributes}\n              viewAfter={viewAfter}\n              viewBefore={viewBefore}\n            />\n          )}\n        </TabsContainer>\n      )}\n    </Container>\n  )\n}\n\nexport default Popover\n","/* eslint-disable arrow-body-style */\n/* eslint-disable react-hooks/exhaustive-deps */\n/* eslint-disable comma-dangle */\n/* eslint-disable react/prop-types */\n// @ts-nocheck\nimport React, { useMemo } from \"react\"\nimport { Flex } from \"@netdata/netdata-ui\"\nimport { ChartMetadata } from \"domains/chart/chart-types\"\nimport { Attributes } from \"domains/chart/utils/transformDataAttributes.ts\"\nimport { ChartTimeframe } from \"domains/chart/components/chart-legend-bottom\"\nimport GroupBoxes from \"domains/chart/components/lib-charts/group-box-chart/groupBoxes\"\nimport Legend from \"domains/chart/components/lib-charts/group-box-chart/legend\"\nimport getLabel from \"./getLabel\"\nimport transform from \"./transform\"\nimport Popover from \"./popover\"\n\ninterface Props {\n  chartData: any\n  chartMetadata: ChartMetadata\n  attributes: Attributes\n  viewAfter: number\n  viewBefore: number\n  hoveredRow: number\n  hoveredX: number | null\n  showUndefined: boolean\n}\n\nconst Kubernetes = ({\n  chartData,\n  chartMetadata,\n  attributes,\n  viewAfter,\n  viewBefore,\n  hoveredRow,\n  hoveredX,\n  showUndefined,\n}: Props) => {\n  const { filteredRows } = attributes\n  const { data: groupBoxData, labels, chartLabels } = useMemo(\n    () => transform(chartData, filteredRows),\n    [filteredRows, chartData]\n  )\n\n  const {\n    id,\n    result: { data },\n    groupBy,\n    postGroupBy,\n  } = chartData\n\n  const renderBoxPopover = ({ groupIndex, index, align }) => {\n    const label = groupBoxData[groupIndex].labels[index]\n    const { title } = getLabel(postGroupBy)\n\n    return (\n      <Popover\n        align={align}\n        title={`${title}: ${label}`}\n        groupLabel={labels[groupIndex]}\n        postGroupLabel={label}\n        chartLabels={groupBoxData[groupIndex].chartLabels[index]}\n        attributes={attributes}\n        viewBefore={viewBefore}\n        viewAfter={viewAfter}\n      />\n    )\n  }\n\n  const renderGroupPopover = ({ groupIndex, align }) => {\n    const label = labels[groupIndex]\n    const { title } = getLabel(groupBy)\n\n    return (\n      <Popover\n        align={align}\n        title={`${title}: ${label}`}\n        groupLabel={label}\n        chartLabels={chartLabels[groupIndex]}\n        attributes={attributes}\n        viewBefore={viewBefore}\n        viewAfter={viewAfter}\n      />\n    )\n  }\n\n  const groupedBoxesData = useMemo(() => {\n    return groupBoxData.map((groupedBox) => {\n      return {\n        labels: groupedBox.labels,\n        data:\n          hoveredRow === -1 || hoveredRow > data.length || !(hoveredRow in data)\n            ? groupedBox.postAggregated\n            : groupedBox.indexes.map((index) => data[hoveredRow][index + 1]) || [],\n      }\n    })\n  }, [data, groupBoxData, hoveredRow])\n\n  return (\n    <Flex column width=\"100%\" height=\"100%\" gap={4} padding={[4, 2]}>\n      <GroupBoxes\n        data={groupedBoxesData}\n        labels={labels}\n        renderBoxPopover={renderBoxPopover}\n        renderGroupPopover={renderGroupPopover}\n      />\n      <Flex data-testid=\"legend-container\" justifyContent=\"between\">\n        <Legend>{id}</Legend>\n        <ChartTimeframe\n          chartMetadata={chartMetadata}\n          showUndefined={showUndefined}\n          hoveredX={hoveredX}\n          viewBefore={viewBefore}\n          chartData={chartData}\n        />\n      </Flex>\n    </Flex>\n  )\n}\n\nexport default Kubernetes\n","/* eslint-disable arrow-body-style */\n/* eslint-disable object-curly-newline */\n/* eslint-disable react-hooks/exhaustive-deps */\n/* eslint-disable comma-dangle */\n/* eslint-disable react/prop-types */\n// @ts-nocheck\n\nexport default (chartData, filteredRows) => {\n  const { keys, labels: labelValues, groupBy, postGroupBy, aggrGroups, postAggregated } = chartData\n  const groupValues = keys[groupBy]\n  const postGroupValues = keys[postGroupBy]\n  const indexes = filteredRows || [...Array(groupValues.length)].map((v, index) => index)\n\n  const postGroupData = indexes.reduce((acc: any, index: number) => {\n    const groupValue = groupValues[index]\n    if (!(groupValue in acc)) {\n      acc[groupValue] = {\n        labels: [],\n        indexes: [],\n        chartLabels: [],\n        postAggregated: [],\n      }\n    }\n    const boxes = acc[groupValue]\n    boxes.indexes.push(index)\n    boxes.labels.push(postGroupValues[index])\n    boxes.postAggregated.push(postAggregated[index])\n\n    const chartLabels = aggrGroups.reduce((labelsAcc, label) => {\n      return labelValues[label][index]\n        ? { ...labelsAcc, [label]: labelValues[label][index] }\n        : labelsAcc\n    }, {})\n    boxes.chartLabels.push(chartLabels)\n    return acc\n  }, {})\n\n  const labels = Object.keys(postGroupData).sort(\n    (a, b) => postGroupData[b].indexes.length - postGroupData[a].indexes.length\n  )\n\n  const groupData = labels.map((label) => postGroupData[label])\n\n  const groupChartLabels = groupData.map((boxes) => {\n    return aggrGroups.reduce((acc, label) => {\n      const groupLabels = new Set(\n        boxes.chartLabels.reduce((accChartLabels, chartLabels) => {\n          return chartLabels[label] ? [...accChartLabels, ...chartLabels[label]] : accChartLabels\n        }, [])\n      )\n      return groupLabels.size === 0 ? acc : { ...acc, [label]: Array.from(groupLabels) }\n    }, {})\n  })\n\n  return { labels, data: groupData, chartLabels: groupChartLabels }\n}\n","import React, { useCallback } from \"react\"\nimport classNames from \"classnames\"\n\nimport { useDispatch, useSelector } from \"store/redux-separate-context\"\nimport { setGlobalChartUnderlayAction, setGlobalPanAndZoomAction } from \"domains/global/actions\"\nimport { selectSyncPanAndZoom } from \"domains/global/selectors\"\nimport { setChartPanAndZoomAction } from \"domains/chart/actions\"\nimport { useShowValueOutside } from \"hooks/use-show-value-outside\"\n\nimport { Attributes } from \"../utils/transformDataAttributes\"\nimport {\n  ChartData, ChartMetadata, DygraphData, EasyPieChartData, D3pieChartData,\n} from \"../chart-types\"\nimport { chartLibrariesSettings, ChartLibraryName } from \"../utils/chartLibrariesSettings\"\n\nimport { DygraphChart } from \"./lib-charts/dygraph-chart\"\nimport { EasyPieChart } from \"./lib-charts/easy-pie-chart\"\nimport { GaugeChart } from \"./lib-charts/gauge-chart\"\nimport { SparklineChart } from \"./lib-charts/sparkline-chart\"\nimport { D3pieChart } from \"./lib-charts/d3pie-chart\"\nimport { PeityChart } from \"./lib-charts/peity-chart\"\nimport { GoogleChart } from \"./lib-charts/google-chart\"\nimport { TextOnly } from \"./lib-charts/text-only\"\nimport { KubernetesGroupBoxes } from \"./lib-charts/group-box-chart\"\n\ninterface Props {\n  attributes: Attributes\n  chartContainerElement: HTMLElement\n  chartData: ChartData\n  chartMetadata: ChartMetadata\n  chartLibrary: ChartLibraryName\n  colors: {\n    [key: string]: string\n  }\n  chartUuid: string\n  chartHeight: number\n  chartWidth: number\n  dimensionsVisibility: boolean[]\n  hasEmptyData: boolean\n  isRemotelyControlled: boolean\n  legendFormatValue: ((v: number | string | null) => number | string)\n  orderedColors: string[]\n  hoveredX: number | null\n  onUpdateChartPanAndZoom: (arg: { after: number, before: number, masterID: string }) => void\n  immediatelyDispatchPanAndZoom: () => void\n\n  hoveredRow: number\n  setHoveredX: (hoveredX: number | null, noMaster?: boolean) => void\n  setMinMax: (minMax: [number, number]) => void\n  showLatestOnBlur: boolean\n  unitsCurrent: string\n  viewAfterForCurrentData: number,\n  viewBeforeForCurrentData: number,\n}\n\nexport const AbstractChart = ({\n  attributes,\n  chartContainerElement,\n  chartData,\n  chartMetadata,\n  chartLibrary,\n  colors,\n  chartUuid,\n  chartHeight,\n  chartWidth,\n  dimensionsVisibility,\n  hasEmptyData,\n  isRemotelyControlled,\n  legendFormatValue,\n  orderedColors,\n  hoveredRow,\n  hoveredX,\n  onUpdateChartPanAndZoom,\n  immediatelyDispatchPanAndZoom,\n  setHoveredX,\n  setMinMax,\n  showLatestOnBlur,\n  unitsCurrent,\n  viewAfterForCurrentData,\n  viewBeforeForCurrentData,\n}: Props) => {\n  const dispatch = useDispatch()\n\n  const isSyncPanAndZoom = useSelector(selectSyncPanAndZoom)\n  const setGlobalChartUnderlay = useCallback(({ after, before, masterID }) => {\n    dispatch(setGlobalChartUnderlayAction({ after, before, masterID }))\n\n    // freeze charts\n    // don't send masterID, so no padding is applied\n    if (isSyncPanAndZoom) {\n      dispatch(setGlobalPanAndZoomAction({\n        after: viewAfterForCurrentData,\n        before: viewBeforeForCurrentData,\n      }))\n    } else {\n      dispatch(setChartPanAndZoomAction({\n        after: viewAfterForCurrentData,\n        before: viewBeforeForCurrentData,\n        id: chartUuid,\n      }))\n    }\n  }, [chartUuid, dispatch, isSyncPanAndZoom, viewAfterForCurrentData, viewBeforeForCurrentData])\n\n  const chartSettings = chartLibrariesSettings[chartLibrary]\n  const { hasLegend } = chartSettings\n  const chartElementClassName = hasLegend(attributes)\n    ? classNames(\n      `netdata-chart-with-legend-${attributes.legendPosition || \"right\"}`,\n      `netdata-${chartLibrary}-chart-with-legend-right`,\n    )\n    : classNames(\n      \"netdata-chart\",\n      `netdata-${chartLibrary}-chart`,\n    )\n  const chartElementId = `${chartLibrary}-${chartUuid}-chart`\n  const showUndefined = hoveredRow === -1 && !showLatestOnBlur\n\n  useShowValueOutside({\n    attributes, chartData, chartSettings, hoveredRow, legendFormatValue, showUndefined,\n  })\n\n  if (chartLibrary === \"easypiechart\") {\n    return (\n      <EasyPieChart\n        attributes={attributes}\n        chartData={chartData as EasyPieChartData}\n        chartMetadata={chartMetadata}\n        chartElementClassName={chartElementClassName}\n        chartElementId={chartElementId}\n        chartLibrary={chartLibrary}\n        chartWidth={chartWidth}\n        colors={colors}\n        chartUuid={chartUuid}\n        dimensionsVisibility={dimensionsVisibility}\n        isRemotelyControlled={isRemotelyControlled}\n        // easyPieChart doesn't support resizing, so lets just create new one when\n        // container size changes\n        key={chartWidth}\n        legendFormatValue={legendFormatValue}\n        orderedColors={orderedColors}\n        hoveredRow={hoveredRow}\n        onUpdateChartPanAndZoom={onUpdateChartPanAndZoom}\n        setGlobalChartUnderlay={setGlobalChartUnderlay}\n        setMinMax={setMinMax}\n        showUndefined={showUndefined}\n        unitsCurrent={unitsCurrent}\n        viewAfter={viewAfterForCurrentData}\n        viewBefore={viewBeforeForCurrentData}\n      />\n    )\n  }\n\n  if (chartLibrary === \"gauge\") {\n    return (\n      <GaugeChart\n        attributes={attributes}\n        chartData={chartData as EasyPieChartData}\n        chartMetadata={chartMetadata}\n        chartElementClassName={chartElementClassName}\n        chartElementId={chartElementId}\n        chartLibrary={chartLibrary}\n        chartHeight={chartHeight}\n        chartWidth={chartWidth}\n        colors={colors}\n        chartUuid={chartUuid}\n        dimensionsVisibility={dimensionsVisibility}\n        isRemotelyControlled={isRemotelyControlled}\n        legendFormatValue={legendFormatValue}\n        orderedColors={orderedColors}\n        hoveredRow={hoveredRow}\n        hoveredX={hoveredX}\n        onUpdateChartPanAndZoom={onUpdateChartPanAndZoom}\n        setGlobalChartUnderlay={setGlobalChartUnderlay}\n        setHoveredX={setHoveredX}\n        setMinMax={setMinMax}\n        showUndefined={showUndefined}\n        unitsCurrent={unitsCurrent}\n        viewAfter={viewAfterForCurrentData}\n        viewBefore={viewBeforeForCurrentData}\n      />\n    )\n  }\n\n  if (chartLibrary === \"sparkline\") {\n    return (\n      <SparklineChart\n        attributes={attributes}\n        chartContainerElement={chartContainerElement}\n        chartData={chartData as EasyPieChartData}\n        chartMetadata={chartMetadata}\n        chartElementClassName={chartElementClassName}\n        chartElementId={chartElementId}\n        dimensionsVisibility={dimensionsVisibility}\n        isRemotelyControlled={isRemotelyControlled}\n        orderedColors={orderedColors}\n        unitsCurrent={unitsCurrent}\n        viewAfterForCurrentData={viewAfterForCurrentData}\n        viewBeforeForCurrentData={viewBeforeForCurrentData}\n      />\n    )\n  }\n\n  if (chartLibrary === \"d3pie\") {\n    return (\n      <D3pieChart\n        attributes={attributes}\n        chartContainerElement={chartContainerElement}\n        chartData={chartData as D3pieChartData}\n        chartMetadata={chartMetadata}\n        chartElementClassName={chartElementClassName}\n        chartElementId={chartElementId}\n        dimensionsVisibility={dimensionsVisibility}\n        hoveredRow={hoveredRow}\n        hoveredX={hoveredX}\n        isRemotelyControlled={isRemotelyControlled}\n        legendFormatValue={legendFormatValue}\n        orderedColors={orderedColors}\n        setMinMax={setMinMax}\n        showUndefined={showUndefined}\n        unitsCurrent={unitsCurrent}\n      />\n    )\n  }\n\n  if (chartLibrary === \"peity\") {\n    return (\n      <PeityChart\n        attributes={attributes}\n        chartContainerElement={chartContainerElement}\n        chartData={chartData as EasyPieChartData}\n        chartMetadata={chartMetadata}\n        chartElementClassName={chartElementClassName}\n        chartElementId={chartElementId}\n        orderedColors={orderedColors}\n      />\n    )\n  }\n\n  if (chartLibrary === \"google\") {\n    return (\n      <GoogleChart\n        attributes={attributes}\n        chartData={chartData as EasyPieChartData}\n        chartMetadata={chartMetadata}\n        chartElementClassName={chartElementClassName}\n        chartElementId={chartElementId}\n        orderedColors={orderedColors}\n        unitsCurrent={unitsCurrent}\n      />\n    )\n  }\n\n  if (chartLibrary === \"textonly\") {\n    return (\n      <TextOnly\n        attributes={attributes}\n        chartData={chartData as EasyPieChartData}\n        chartElementClassName={chartElementClassName}\n        chartElementId={chartElementId}\n      />\n    )\n  }\n\n  if (chartLibrary === \"groupbox\") {\n    return (\n      <KubernetesGroupBoxes\n        chartData={chartData}\n        chartMetadata={chartMetadata}\n        attributes={attributes}\n        viewAfter={viewAfterForCurrentData}\n        viewBefore={viewBeforeForCurrentData}\n        hoveredRow={hoveredRow}\n        hoveredX={hoveredX}\n        showUndefined={showUndefined}\n      />\n    )\n  }\n\n  return (\n    <DygraphChart\n      attributes={attributes}\n      chartData={chartData as DygraphData}\n      chartMetadata={chartMetadata}\n      chartElementClassName={chartElementClassName}\n      chartElementId={chartElementId}\n      chartLibrary={chartLibrary}\n      colors={colors}\n      chartUuid={chartUuid}\n      dimensionsVisibility={dimensionsVisibility}\n      hasEmptyData={hasEmptyData}\n      hasLegend={hasLegend(attributes)}\n      isRemotelyControlled={isRemotelyControlled}\n      orderedColors={orderedColors}\n      immediatelyDispatchPanAndZoom={immediatelyDispatchPanAndZoom}\n      hoveredRow={hoveredRow}\n      hoveredX={hoveredX}\n      onUpdateChartPanAndZoom={onUpdateChartPanAndZoom}\n      setGlobalChartUnderlay={setGlobalChartUnderlay}\n      setHoveredX={setHoveredX}\n      setMinMax={setMinMax}\n      unitsCurrent={unitsCurrent}\n      viewAfter={viewAfterForCurrentData}\n      viewBefore={viewBeforeForCurrentData}\n    />\n  )\n}\n","import { useEffect, useRef } from \"react\"\nimport { isEmpty } from \"ramda\"\nimport { useMount } from \"react-use\"\n\nimport { ChartData, DygraphData } from \"domains/chart/chart-types\"\nimport { Attributes } from \"domains/chart/utils/transformDataAttributes\"\nimport { ChartLibraryConfig } from \"domains/chart/utils/chartLibrariesSettings\"\n\n\ninterface UseShowValueOutsideArgument {\n  attributes: Attributes\n  chartData: ChartData\n  chartSettings: ChartLibraryConfig\n  hoveredRow: number\n  legendFormatValue: ((v: number | string | null) => number | string)\n  showUndefined: boolean\n}\n\n// example of the attribute:\n// show-value-of-iowait-at: \"system.cpu.iowait.1\"\n\nexport const useShowValueOutside = ({\n  attributes,\n  chartData,\n  chartSettings,\n  hoveredRow,\n  legendFormatValue,\n  showUndefined,\n}: UseShowValueOutsideArgument) => {\n  // a ref to store found elements, just once per lifetime of component\n  const showValueAttributesNodes = useRef<(HTMLElement | null)[]>([])\n\n  // find the nodes that will have populated values\n  useMount(() => {\n    const { showValueOf } = attributes\n    // showValueOf will be undefined if not used, but additional isEmpty check can prevent\n    // regression performance issue in the future\n    if (!showValueOf || isEmpty(showValueOf)) {\n      return\n    }\n    const dimensionNames = chartData.dimension_names\n    const dimensionIds = chartData.dimension_ids\n    dimensionNames.forEach((dimensionName, i) => {\n      const userElementId = showValueOf[`show-value-of-${dimensionName.toLowerCase()}`]\n        || showValueOf[`show-value-of-${dimensionIds[i].toLowerCase()}-at`]\n\n      // if element is not found, just add null\n      showValueAttributesNodes.current = showValueAttributesNodes.current.concat(\n        document.getElementById(userElementId),\n      )\n    })\n  })\n\n  useEffect(() => {\n    if (showValueAttributesNodes.current.length) {\n      const chartSettingCallOptions = chartSettings.options(attributes)\n      const isFlipped = chartSettingCallOptions.includes(\"flip\")\n\n      // \"objectrows\" is for d3pie, which has different data format\n      if (chartData.format === \"json\" && !chartSettingCallOptions.includes(\"objectrows\")) {\n        const { data } = (chartData as DygraphData).result\n        const valueIndex = hoveredRow === -1\n          ? (data.length - 1)\n          : (hoveredRow) // because data for easy-pie-chart are flipped\n\n        // yes, \"flipped\" value means chronological order (from oldest to newest) :)\n        const rowIndex = isFlipped ? valueIndex : (data.length - valueIndex - 1)\n        const row = data[rowIndex]\n\n        chartData.dimension_names.forEach((dimensionName, dimensionIndex) => {\n          const value = (showUndefined || !row)\n            ? \"\"\n            : legendFormatValue(row[dimensionIndex + 1])\n          const element = showValueAttributesNodes.current[dimensionIndex]\n          if (element) {\n            element.innerText = `${value}`\n          }\n        })\n      }\n    }\n  }, [attributes, chartData, chartSettings, hoveredRow, legendFormatValue, showUndefined])\n}\n","import { __, prop } from \"ramda\"\nimport React, { useEffect, useState, useCallback, useMemo, memo, useContext } from \"react\"\nimport { ThemeContext } from \"styled-components\"\nimport { useDebouncedCallback } from \"use-debounce\"\n\nimport {\n  requestCommonColorsAction,\n  setDefaultAfterAction,\n  setGlobalPanAndZoomAction,\n  setGlobalSelectionAction,\n} from \"domains/global/actions\"\nimport {\n  createSelectAssignedColors,\n  selectGlobalSelection,\n  selectSyncPanAndZoom,\n  selectSyncSelection,\n  selectUnitsScalingMethod,\n} from \"domains/global/selectors\"\nimport { useDispatch, useSelector } from \"store/redux-separate-context\"\nimport { TimeRange } from \"types/common\"\nimport { MS_IN_SECOND, isTimestamp } from \"utils/utils\"\n\nimport { setChartPanAndZoomAction } from \"domains/chart/actions\"\n\nimport { getPanAndZoomStep } from \"../utils/get-pan-and-zoom-step\"\nimport { Attributes } from \"../utils/transformDataAttributes\"\nimport { chartLibrariesSettings } from \"../utils/chartLibrariesSettings\"\nimport { useFormatters } from \"../utils/formatters\"\nimport { ChartData, ChartMetadata } from \"../chart-types\"\n\nimport { ChartLegend } from \"./chart-legend\"\nimport { LegendToolbox } from \"./legend-toolbox\"\nimport { ResizeHandler } from \"./resize-handler\"\nimport { AbstractChart } from \"./abstract-chart\"\n\ninterface GlobalPanAndZoomState {\n  after: number // timestamp in ms\n  before: number // timestamp in ms\n  masterID?: string\n  shouldForceTimeRange?: boolean\n}\n\ninterface Props {\n  attributes: Attributes\n  chartContainerElement: HTMLElement\n  chartData: ChartData\n  chartMetadata: ChartMetadata\n  chartHeight: number\n  chartUuid: string\n  chartWidth: number\n  defaultAfter: number\n  globalPanAndZoom: null | GlobalPanAndZoomState\n  hasEmptyData: boolean\n  isRemotelyControlled: boolean\n  viewRangeForCurrentData: TimeRange\n  viewRange: TimeRange\n  selectedDimensions: string[]\n  setSelectedDimensions: (newState: string[]) => void\n  showLatestOnBlur: boolean\n}\n\nexport const Chart = memo(\n  ({\n    attributes,\n    attributes: { chartLibrary },\n    chartContainerElement,\n    chartData,\n    chartMetadata,\n    chartHeight,\n    chartUuid,\n    chartWidth,\n    defaultAfter,\n    globalPanAndZoom,\n    hasEmptyData,\n    isRemotelyControlled,\n    viewRangeForCurrentData,\n    viewRange,\n    selectedDimensions,\n    setSelectedDimensions,\n    showLatestOnBlur,\n  }: Props) => {\n    const themeContext = useContext(ThemeContext)\n    const unitsScalingMethod = useSelector(selectUnitsScalingMethod)\n    const chartSettings = chartLibrariesSettings[chartLibrary]\n    const { hasLegend } = chartSettings\n    const {\n      units = chartMetadata.units,\n      unitsCommon,\n      unitsDesired = unitsScalingMethod,\n    } = attributes\n\n    // we need to have empty selectedDimensions work as {all enabled}, in case\n    // new dimensions show up (when all are enabled, the new dimensions should also auto-enable)\n    const dimensionsVisibility = useMemo(\n      () =>\n        chartData.dimension_names.map(dimensionName =>\n          selectedDimensions.length === 0 ? true : selectedDimensions.includes(dimensionName)\n        ),\n      [chartData.dimension_names, selectedDimensions]\n    )\n\n    const shouldDisplayToolbox =\n      hasLegend(attributes) && window.NETDATA.options.current.legend_toolbox\n\n    const shouldDisplayResizeHandler =\n      shouldDisplayToolbox &&\n      window.NETDATA.options.current.resize_charts && // legacy way of turning off for print mode\n      !attributes.hideResizeHandler\n\n    const dispatch = useDispatch()\n    const allDimensionNames = useMemo(() => {\n      // metadata and chartData dimensions match each other, but we need to first parse\n      // dimensions from metadata, to keep the same order (when browser parsers dimensions object,\n      // it sorts them in *some* way which is hard to reproduce). And people can get used to colors\n      // so let's keep them as they were before\n      const dimensionNamesFromMetadata = Object.values(chartMetadata.dimensions).map(x => x.name)\n      const additionalDimensionNamesFromData = chartData.dimension_names.filter(\n        x => !dimensionNamesFromMetadata.includes(x)\n      )\n      return dimensionNamesFromMetadata.concat(additionalDimensionNamesFromData)\n    }, [chartData.dimension_names, chartMetadata.dimensions])\n    useEffect(() => {\n      dispatch(\n        requestCommonColorsAction({\n          chartContext: chartMetadata.context,\n          chartUuid,\n          colorsAttribute: attributes.colors,\n          commonColorsAttribute: attributes.commonColors,\n          dimensionNames: allDimensionNames,\n        })\n      )\n    }, [\n      allDimensionNames,\n      attributes.colors,\n      attributes.commonColors,\n      chartMetadata.context,\n      chartUuid,\n      dispatch,\n    ])\n\n    const { legendFormatValue, legendFormatValueDecimalsFromMinMax, unitsCurrent } = useFormatters({\n      attributes,\n      data: chartData,\n      units,\n      unitsCommon,\n      unitsDesired,\n      uuid: chartUuid,\n    })\n\n    const [localHoveredX, setLocalHoveredX] = useState<number | null>(null)\n\n    const isSyncSelection = useSelector(selectSyncSelection)\n    const handleSetHoveredX = useCallback(\n      (newHoveredX, noMaster) => {\n        if (isSyncSelection) {\n          const action = noMaster\n            ? { chartUuid: null, hoveredX: newHoveredX }\n            : { chartUuid, hoveredX: newHoveredX }\n          dispatch(setGlobalSelectionAction(action))\n        } else {\n          setLocalHoveredX(newHoveredX)\n        }\n      },\n      [chartUuid, dispatch, isSyncSelection]\n    )\n    const globalHoveredX = useSelector(selectGlobalSelection)\n    const hoveredX = isSyncSelection ? globalHoveredX : localHoveredX\n\n    // time-frames for requested data (even when request is pending)\n    const viewAfter = isTimestamp(viewRange[0]) ? viewRange[0] : chartData.after * MS_IN_SECOND\n    const viewBefore = isTimestamp(viewRange[1]) ? viewRange[1] : chartData.before * MS_IN_SECOND\n\n    const viewAfterForCurrentData = isTimestamp(viewRangeForCurrentData[0])\n      ? viewRangeForCurrentData[0]\n      : chartData.after * MS_IN_SECOND\n    const viewBeforeForCurrentData = isTimestamp(viewRangeForCurrentData[1])\n      ? viewRangeForCurrentData[1]\n      : chartData.before * MS_IN_SECOND // when 'before' is 0 or negative\n\n    const netdataFirst = chartData.first_entry * MS_IN_SECOND\n    const netdataLast = chartData.last_entry * MS_IN_SECOND\n\n    // old dashboard persists min duration based on first chartWidth, i assume it's a bug\n    // and will update fixedMinDuration when width changes\n    const fixedMinDuration = useMemo(\n      () => Math.round((chartWidth / 30) * chartMetadata.update_every * MS_IN_SECOND),\n      [chartMetadata.update_every, chartWidth]\n    )\n\n    const isSyncPanAndZoom = useSelector(selectSyncPanAndZoom)\n\n    const setGlobalPanAndZoomDebounced = useDebouncedCallback(\n      newGlobalPanAndZoom => {\n        dispatch(setGlobalPanAndZoomAction(newGlobalPanAndZoom))\n      },\n      400 // corresponds to global_pan_sync_time in old dashboard\n    )\n\n    const immediatelyDispatchPanAndZoom = useCallback(() => {\n      setGlobalPanAndZoomDebounced.flush()\n    }, [setGlobalPanAndZoomDebounced])\n\n    /**\n     * pan-and-zoom handler (both for toolbox and mouse events)\n     */\n    const handleUpdateChartPanAndZoom = useCallback(\n      ({\n        after,\n        before,\n        callback,\n        shouldFlushImmediately = false,\n        shouldForceTimeRange,\n        shouldNotExceedAvailableRange,\n      }) => {\n        if (before < after) {\n          return\n        }\n        let minDuration = fixedMinDuration\n\n        const currentDuraton = Math.round(viewBefore - viewAfter)\n\n        let afterForced = Math.round(after)\n        let beforeForced = Math.round(before)\n        const viewUpdateEvery = chartData.view_update_every * MS_IN_SECOND\n\n        if (shouldNotExceedAvailableRange) {\n          const first = netdataFirst + viewUpdateEvery\n          const last = netdataLast + viewUpdateEvery\n          // first check \"before\"\n          if (beforeForced > last) {\n            afterForced -= before - last\n            beforeForced = last\n          }\n\n          if (afterForced < first) {\n            afterForced = first\n          }\n        }\n\n        // align them to update_every\n        // stretching them further away\n        afterForced -= afterForced % viewUpdateEvery\n        beforeForced += viewUpdateEvery - (beforeForced % viewUpdateEvery)\n\n        // the final wanted duration\n        let wantedDuration = beforeForced - afterForced\n\n        // to allow panning, accept just a point below our minimum\n        if (currentDuraton - viewUpdateEvery < minDuration) {\n          minDuration = currentDuraton - viewUpdateEvery\n        }\n\n        // we do it, but we adjust to minimum size and return false\n        // when the wanted size is below the current and the minimum\n        // and we zoom\n        let doCallback = true\n        if (wantedDuration < currentDuraton && wantedDuration < minDuration) {\n          minDuration = fixedMinDuration\n\n          const dt = (minDuration - wantedDuration) / 2\n          beforeForced += dt\n          afterForced -= dt\n          wantedDuration = beforeForced - afterForced\n          doCallback = false\n        }\n\n        const tolerance = viewUpdateEvery * 2\n        const movement = Math.abs(beforeForced - viewBefore)\n\n        if (\n          Math.abs(currentDuraton - wantedDuration) <= tolerance &&\n          movement <= tolerance &&\n          doCallback\n        ) {\n          return\n        }\n\n        if (isSyncPanAndZoom) {\n          setGlobalPanAndZoomDebounced.callback({\n            after: afterForced,\n            before: beforeForced,\n            masterID: chartUuid,\n            shouldForceTimeRange,\n          })\n          if (shouldFlushImmediately) {\n            setGlobalPanAndZoomDebounced.flush()\n          }\n        } else {\n          dispatch(\n            setChartPanAndZoomAction({\n              after: afterForced,\n              before: beforeForced,\n              id: chartUuid,\n              shouldForceTimeRange,\n            })\n          )\n        }\n\n        if (doCallback && typeof callback === \"function\") {\n          callback(afterForced, beforeForced)\n        }\n      },\n      [\n        chartData.view_update_every,\n        chartUuid,\n        dispatch,\n        fixedMinDuration,\n        isSyncPanAndZoom,\n        netdataFirst,\n        netdataLast,\n        setGlobalPanAndZoomDebounced,\n        viewAfter,\n        viewBefore,\n      ]\n    )\n\n    /**\n     * toolbox handlers\n     */\n    const handleToolBoxPanAndZoom = useCallback(\n      (after: number, before: number) => {\n        const newAfter = Math.max(after, netdataFirst)\n        const newBefore = Math.min(before, netdataLast)\n        handleUpdateChartPanAndZoom({\n          after: newAfter,\n          before: newBefore,\n          shouldForceTimeRange: true,\n          shouldFlushImmediately: true,\n        })\n      },\n      [handleUpdateChartPanAndZoom, netdataFirst, netdataLast]\n    )\n\n    const handleToolboxLeftClick = useCallback(\n      (event: React.MouseEvent) => {\n        const step = (viewBefore - viewAfter) * getPanAndZoomStep(event)\n        const newBefore = viewBefore - step\n        const newAfter = viewAfter - step\n        if (newAfter >= netdataFirst) {\n          handleToolBoxPanAndZoom(newAfter, newBefore)\n        }\n      },\n      [handleToolBoxPanAndZoom, netdataFirst, viewAfter, viewBefore]\n    )\n\n    const handleToolboxRightClick = useCallback(\n      (event: React.MouseEvent) => {\n        const timeWindow = viewBefore - viewAfter\n        const step = timeWindow * getPanAndZoomStep(event)\n        const newBefore = Math.min(viewBefore + step, netdataLast)\n        const newAfter = newBefore - timeWindow\n        handleToolBoxPanAndZoom(newAfter, newBefore)\n      },\n      [handleToolBoxPanAndZoom, netdataLast, viewAfter, viewBefore]\n    )\n\n    const handleToolboxZoomInClick = useCallback(\n      (event: React.MouseEvent) => {\n        const panAndZoomStep = getPanAndZoomStep(event) * 0.8\n        if (!globalPanAndZoom) {\n          dispatch(\n            setDefaultAfterAction({\n              after: Math.round(defaultAfter / (panAndZoomStep + 1)),\n            })\n          )\n          return\n        }\n        // if visible time range is much bigger than available time range in history, first zoom-in\n        // should just fit to available range\n        if (viewBefore - viewAfter > (netdataLast - netdataFirst) * 1.2) {\n          handleToolBoxPanAndZoom(netdataFirst, netdataLast)\n          return\n        }\n        const dt = ((viewBefore - viewAfter) * panAndZoomStep) / 2\n        const newAfter = viewAfter + dt\n        const newBefore = viewBefore - dt\n        handleToolBoxPanAndZoom(newAfter, newBefore)\n      },\n      [\n        defaultAfter,\n        dispatch,\n        globalPanAndZoom,\n        handleToolBoxPanAndZoom,\n        netdataFirst,\n        netdataLast,\n        viewAfter,\n        viewBefore,\n      ]\n    )\n\n    const handleToolboxZoomOutClick = useCallback(\n      (event: React.MouseEvent) => {\n        const panAndZoomStep = getPanAndZoomStep(event) * 0.8\n        if (!globalPanAndZoom) {\n          dispatch(\n            setDefaultAfterAction({\n              after: Math.round(defaultAfter * (panAndZoomStep + 1)),\n            })\n          )\n          return\n        }\n        const dt =\n          ((viewBefore - viewAfter) / (1.0 - panAndZoomStep * 0.8) - (viewBefore - viewAfter)) / 2\n        const newAfter = viewAfter - dt\n        const newBefore = viewBefore + dt\n        handleToolBoxPanAndZoom(newAfter, newBefore)\n      },\n      [defaultAfter, dispatch, globalPanAndZoom, handleToolBoxPanAndZoom, viewAfter, viewBefore]\n    )\n\n    /**\n     * assign colors\n     */\n    const selectAssignedColors = useMemo(\n      () =>\n        createSelectAssignedColors({\n          chartContext: chartMetadata.context,\n          chartUuid,\n          colorsAttribute: attributes.colors,\n          commonColorsAttribute: attributes.commonColors,\n        }),\n      [attributes.colors, attributes.commonColors, chartMetadata, chartUuid]\n    )\n    const colors = useSelector(selectAssignedColors)\n    const orderedColors = useMemo(\n      () => chartData.dimension_names.map(prop(__, colors)),\n      [chartData, colors]\n    )\n\n    if (!colors) {\n      return <span /> // wait for createSelectAssignedColors reducer result to come back\n    }\n\n    const isTimeVisible = hoveredX && hoveredX >= viewAfter && hoveredX <= viewBefore\n    const viewUpdateEvery = chartData.view_update_every * MS_IN_SECOND\n    const hoveredRow = isTimeVisible\n      ? Math.floor(((hoveredX as number) - chartData.after * MS_IN_SECOND) / viewUpdateEvery)\n      : -1\n\n    const isLegendOnBottom = attributes.legendPosition === \"bottom\"\n\n    const legendToolbox = (\n      <LegendToolbox\n        onToolboxLeftClick={handleToolboxLeftClick}\n        onToolboxRightClick={handleToolboxRightClick}\n        onToolboxZoomInClick={handleToolboxZoomInClick}\n        onToolboxZoomOutClick={handleToolboxZoomOutClick}\n      />\n    )\n\n    const resizeHandler = shouldDisplayResizeHandler && (\n      <ResizeHandler\n        chartContainerElement={chartContainerElement}\n        chartUuid={chartUuid}\n        heightId={attributes.heightId}\n        isLegendOnBottom={isLegendOnBottom}\n      />\n    )\n\n    return (\n      <>\n        <AbstractChart\n          // remount on theme change\n          key={themeContext.name}\n          attributes={attributes}\n          chartContainerElement={chartContainerElement}\n          chartData={chartData}\n          chartMetadata={chartMetadata}\n          chartLibrary={chartLibrary}\n          colors={colors}\n          chartUuid={chartUuid}\n          chartHeight={chartHeight}\n          chartWidth={chartWidth}\n          dimensionsVisibility={dimensionsVisibility}\n          hasEmptyData={hasEmptyData}\n          onUpdateChartPanAndZoom={handleUpdateChartPanAndZoom}\n          immediatelyDispatchPanAndZoom={immediatelyDispatchPanAndZoom}\n          isRemotelyControlled={isRemotelyControlled}\n          legendFormatValue={legendFormatValue}\n          orderedColors={orderedColors}\n          hoveredX={hoveredX}\n          hoveredRow={hoveredRow}\n          setHoveredX={handleSetHoveredX}\n          setMinMax={([min, max]) => legendFormatValueDecimalsFromMinMax(min, max)}\n          showLatestOnBlur={showLatestOnBlur}\n          unitsCurrent={unitsCurrent}\n          viewAfterForCurrentData={viewAfterForCurrentData}\n          viewBeforeForCurrentData={viewBeforeForCurrentData}\n        />\n        {hasLegend(attributes) && (\n          <ChartLegend\n            attributes={attributes}\n            chartUuid={chartUuid}\n            chartMetadata={chartMetadata}\n            chartLibrary={chartLibrary}\n            colors={colors}\n            hoveredX={hoveredX}\n            hoveredRow={hoveredRow}\n            legendFormatValue={legendFormatValue}\n            selectedDimensions={selectedDimensions}\n            setSelectedDimensions={setSelectedDimensions}\n            showLatestOnBlur={showLatestOnBlur}\n            unitsCurrent={unitsCurrent}\n            viewBefore={viewBefore}\n            legendToolbox={legendToolbox}\n            resizeHandler={resizeHandler}\n          />\n        )}\n        {shouldDisplayToolbox && !isLegendOnBottom && legendToolbox}\n        {!isLegendOnBottom && resizeHandler}\n      </>\n    )\n  }\n)\n","import styled from \"styled-components\"\nimport { getColor, Icon } from \"@netdata/netdata-ui\"\n\nexport const DropdownItem = styled.div`\n  display: flex;\n  flex-direction: start;\n  align-items: center;\n  color: ${getColor([\"neutral\", \"limedSpruce\"])};\n  white-space: nowrap;\n  & > svg use {\n    fill: ${getColor([\"neutral\", \"limedSpruce\"])};\n  }\n`\n\nexport const DropdownItemLabel = styled.span`\n  margin-left: 12px;\n`\n\nexport const DotsBtn = styled(Icon)`\n  width: 6px;\n  height: 10px;\n  cursor: pointer;\n  & use {\n    fill: ${getColor([\"neutral\", \"limedSpruce\"])};\n    & :hover {\n      fill: ${getColor([\"neutral\", \"regentgrey\"])};\n    }\n  }\n`\n","import React, { useState, ReactNode } from \"react\"\n\nimport { Attributes } from \"domains/chart/utils/transformDataAttributes\"\nimport { ChartMetadata } from \"domains/chart/chart-types\"\n\nimport { List, SimpleListItem } from \"@rmwc/list\"\nimport { MenuSurface, MenuSurfaceAnchor } from \"@rmwc/menu\"\n\nimport * as S from \"./styled\"\n\ninterface DropdownMenuCallbackProps {\n  attributes: Attributes,\n  chartMetadata: ChartMetadata,\n  chartID: string,\n}\n\nexport type DropdownMenu = {\n  icon: ReactNode,\n  label: string,\n  onClick: (dropdownMenuCallbackProps: DropdownMenuCallbackProps) => void,\n}[]\n\ninterface Props {\n  attributes: Attributes\n  chartID: string\n  chartMetadata: ChartMetadata\n  dropdownMenu: DropdownMenu\n}\n\nexport const ChartDropdown = ({\n  attributes,\n  chartID,\n  chartMetadata,\n  dropdownMenu,\n}: Props) => {\n  const [isOpen, setIsOpen] = useState(false)\n\n  const handleClose = () => {\n    setIsOpen(false)\n  }\n\n  return (\n    <>\n      <S.DotsBtn\n        name=\"dots_2x3\"\n        onClick={() => {\n          setIsOpen(true)\n        }}\n      />\n      <MenuSurfaceAnchor>\n        <MenuSurface open={isOpen} onClose={handleClose}>\n          <List>\n            {dropdownMenu.map(({ icon, label, onClick }) => (\n              <SimpleListItem\n                key={label}\n                text={(\n                  <S.DropdownItem>\n                    {icon}\n                    <S.DropdownItemLabel>\n                      {label}\n                    </S.DropdownItemLabel>\n                  </S.DropdownItem>\n                )}\n                onClick={() => {\n                  onClick({ attributes, chartMetadata, chartID })\n                  handleClose()\n                }}\n              />\n            ))}\n          </List>\n        </MenuSurface>\n      </MenuSurfaceAnchor>\n    </>\n  )\n}\n","import { prop } from \"ramda\"\nimport styled, { keyframes } from \"styled-components\"\n\nimport { getColor } from \"@netdata/netdata-ui\"\n\nconst circleAnimation = keyframes`\n  0% {\n   opacity: .1;\n  }\n  50% {\n    opacity: .5;\n  }\n  100% {\n   opacity: .1;\n  }\n`\n\n\nexport const SpinnerContainer = styled.div<{ top: number, right: number }>`\n  position: absolute;\n  top: ${prop(\"top\")}px;\n  right: ${prop(\"right\")}px;\n  display: flex;\n`\n\nexport const Circle = styled.div<{ size: number }>`\n  width: ${prop(\"size\")}px;\n  height: ${prop(\"size\")}px;\n  background: ${getColor(\"border\")};\n  border-radius: 50%;\n  animation: 1s linear infinite both ${circleAnimation};\n`\n\nexport const Circle2 = styled(Circle)<{ spaceBetween: number }>`\n  animation-delay: .3s; \n  margin-left: ${prop(\"spaceBetween\")}px;\n`\n\nexport const Circle3 = styled(Circle)<{ spaceBetween: number }>`\n  animation-delay: .6s; \n  margin-left: ${prop(\"spaceBetween\")}px;\n`\n","import React from \"react\"\n\nimport * as S from \"./styled\"\n\ninterface Props {\n  chartLibrary: string\n}\nexport const ChartSpinner = ({\n  chartLibrary,\n}: Props) => {\n  const top = chartLibrary === \"dygraph\" ? 33 : 0\n  const right = chartLibrary === \"dygraph\" ? 8 : 0\n  const size = chartLibrary === \"dygraph\" ? 10 : 7\n  const spaceBetween = chartLibrary === \"dygraph\" ? 4 : 2\n  return (\n    <S.SpinnerContainer top={top} right={right}>\n      <S.Circle size={size} />\n      <S.Circle2 size={size} spaceBetween={spaceBetween} />\n      <S.Circle3 size={size} spaceBetween={spaceBetween} />\n    </S.SpinnerContainer>\n  )\n}\n","import styled from \"styled-components\"\n\nimport { chartDropdownZIndex } from \"styles/z-index\"\n\nexport const ChartDropdownContainer = styled.div`\n  position: absolute;\n  top: 0;\n  left: 40px;\n  width: 20px;\n  height: 20px;\n  z-index: ${chartDropdownZIndex};\n`\n","export const notificationsZIndex = \"z-index: 50;\"\n\nexport const chartDropdownZIndex = 10\n\nexport const spacesBarZIndex = 8\n\nexport const spacePanelZIndex = 6\n\nexport const appHeaderZIndex = 5\n\n// the same as in cloud\nexport const portalSidebarZIndex = \"z-index: 35;\"\nexport const customDropdownZIndex = \"z-index: 45;\"\nexport const dialogsZIndex = 60\n","import { cond, always, T } from \"ramda\"\nimport axios from \"axios\"\nimport React, { useEffect, useState, useMemo, useLayoutEffect } from \"react\"\nimport { useThrottle, useUpdateEffect, useUnmount, useDebounce } from \"react-use\"\n\nimport { AppStateT } from \"store/app-state\"\nimport { useSelector, useDispatch } from \"store/redux-separate-context\"\n\nimport {\n  selectGlobalPanAndZoom,\n  selectGlobalSelection,\n  selectShouldEliminateZeroDimensions,\n  selectPanAndZoomDataPadding,\n  selectSnapshot,\n  selectSpacePanelTransitionEndIsActive,\n  selectDefaultAfter,\n} from \"domains/global/selectors\"\nimport { serverDefault } from \"utils/server-detection\"\nimport { CHART_UNMOUNTED } from \"utils/netdata-sdk\"\nimport { getCorrectedPoints } from \"utils/fill-missing-data\"\n\nimport { fallbackUpdateTimeInterval, panAndZoomDelay } from \"../../constants\"\nimport { getChartURLOptions } from \"../../utils/get-chart-url-options\"\nimport { chartLibrariesSettings } from \"../../utils/chartLibrariesSettings\"\nimport { Attributes } from \"../../utils/transformDataAttributes\"\nimport { getChartPixelsPerPoint } from \"../../utils/get-chart-pixels-per-point\"\nimport { useFetchNewDataClock } from \"../../hooks/use-fetch-new-data-clock\"\n\nimport { fetchChartAction, fetchDataAction } from \"../../actions\"\nimport {\n  selectChartData,\n  selectChartFetchDataParams,\n  makeSelectChartMetadataRequest,\n  selectChartPanAndZoom,\n  selectChartIsFetchingData,\n  selectChartViewRange,\n} from \"../../selectors\"\nimport {\n  ChartData,\n  ChartMetadata,\n  D3pieChartData,\n  DygraphData,\n  EasyPieChartData,\n} from \"../../chart-types\"\n\nimport { Loader } from \"../loader\"\nimport { Chart } from \"../chart\"\nimport { ChartDropdown, DropdownMenu } from \"../chart-dropdown\"\nimport { ChartSpinner } from \"../chart-spinner/chart-spinner\"\n\nimport * as S from \"./styled\"\nimport \"./chart-with-loader.css\"\n\nexport type RenderCustomElementForDygraph = (selectedChartConfiguration: {\n  attributes: Attributes\n  onAttributesChange: any\n  chartMetadata: ChartMetadata\n  chartID: string\n  chartData: ChartData | null\n}) => JSX.Element\n\nconst dimensionsAggrMethodMap = {\n  \"sum-of-abs\": \"sum\",\n}\n\nconst emptyArray = [] as any\n\nexport type Props = {\n  attributes: Attributes\n  chartUuid: string\n  uuid?: string\n  dropdownMenu?: DropdownMenu\n  externalChartMetadata?: ChartMetadata\n  portalNode: HTMLElement\n  renderCustomElementForDygraph?: RenderCustomElementForDygraph\n  onAttributesChange?: any\n}\n\nexport const ChartWithLoader = ({\n  attributes,\n  chartUuid,\n  uuid,\n  dropdownMenu,\n  externalChartMetadata,\n  portalNode,\n  renderCustomElementForDygraph,\n  onAttributesChange,\n}: Props) => {\n  /**\n   * fetch chart details\n   */\n  const { host = serverDefault, id, nodeIDs } = attributes\n  const dispatch = useDispatch()\n  const selectChartMetadataRequest = useMemo(makeSelectChartMetadataRequest, [])\n  const { chartMetadata, isFetchingDetails } = useSelector((state: AppStateT) =>\n    selectChartMetadataRequest(state, { chartId: id, id: chartUuid })\n  )\n  const actualChartMetadata = externalChartMetadata || chartMetadata\n  useEffect(() => {\n    if (!chartMetadata && !isFetchingDetails && !externalChartMetadata) {\n      dispatch(\n        fetchChartAction.request({\n          chart: id,\n          id: chartUuid,\n          host,\n          nodeIDs,\n        })\n      )\n    }\n  }, [\n    id,\n    chartUuid,\n    dispatch,\n    host,\n    isFetchingDetails,\n    chartMetadata,\n    externalChartMetadata,\n    nodeIDs,\n    uuid,\n  ])\n\n  // todo local state option\n  const globalPanAndZoom = useSelector(selectGlobalPanAndZoom)\n  const chartPanAndZoom = useSelector((state: AppStateT) =>\n    selectChartPanAndZoom(state, { id: chartUuid })\n  )\n  const panAndZoom = chartPanAndZoom || globalPanAndZoom\n\n  const isPanAndZoomMaster =\n    (!!globalPanAndZoom && globalPanAndZoom.masterID === chartUuid) || Boolean(chartPanAndZoom)\n  const shouldForceTimeRange = panAndZoom?.shouldForceTimeRange || false\n\n  // (isRemotelyControlled === false) only during globalPanAndZoom, when chart is panAndZoomMaster\n  // and when no toolbox is used at that time\n  const isRemotelyControlled = !panAndZoom || !isPanAndZoomMaster || shouldForceTimeRange // used when zooming/shifting in toolbox\n\n  const fetchDataParams = useSelector((state: AppStateT) =>\n    selectChartFetchDataParams(state, { id: chartUuid })\n  )\n  const viewRange = useSelector((state: AppStateT) =>\n    selectChartViewRange(state, { id: chartUuid })\n  )\n  const chartData = useSelector((state: AppStateT) => selectChartData(state, { id: chartUuid }))\n  const isFetchingData = useSelector((state: AppStateT) =>\n    selectChartIsFetchingData(state, { id: chartUuid })\n  )\n\n  const hoveredX = useSelector(selectGlobalSelection)\n\n  // periodical update of newest data\n  // default to 2000ms. When chartMetadata has been fetched, use chartMetadata.update_every\n  // if chartData has been fetched, use chartData.view_update_every instead\n  // todo add support to \"data-update-every\" attribute\n  const viewUpdateEvery = cond([\n    [always(!!chartData), () => (chartData as ChartData).view_update_every * 1000],\n    [\n      always(!!actualChartMetadata),\n      () => (actualChartMetadata as ChartMetadata).update_every * 1000,\n    ],\n    [T, always(fallbackUpdateTimeInterval)],\n  ])()\n  const [shouldFetch, setShouldFetch] = useFetchNewDataClock({\n    areCriteriaMet: !panAndZoom && !hoveredX,\n    preferedIntervalTime: viewUpdateEvery,\n  })\n\n  const panAndZoomThrottled = useThrottle(panAndZoom, panAndZoomDelay)\n  useEffect(() => {\n    setShouldFetch(true)\n  }, [panAndZoomThrottled, setShouldFetch])\n\n  const defaultAfter = useSelector(selectDefaultAfter)\n  // when after/before changes, don't wait for next interval, just fetch immediately\n  useUpdateEffect(() => {\n    setShouldFetch(true)\n  }, [\n    attributes.after,\n    attributes.before,\n    defaultAfter,\n    attributes.dimensions,\n    attributes.aggrMethod,\n    attributes.groupBy,\n  ])\n\n  const { before: initialBefore = window.NETDATA.chartDefaults.before } = attributes\n\n  // attributes.after should be now used only for old custom dashboard\n  // and in the future for setting timeframe per-chart\n  const liveModeAfter = attributes.after || defaultAfter\n\n  const chartSettings = chartLibrariesSettings[attributes.chartLibrary]\n  const { hasLegend } = chartSettings\n\n  // todo optimize by using resizeObserver (optionally)\n  const boundingClientRect = portalNode.getBoundingClientRect()\n\n  // from old dashboard\n  const chartWidth = boundingClientRect.width - (hasLegend(attributes) ? 140 : 0)\n  const chartHeight = boundingClientRect.height\n\n  const isShowingSnapshot = Boolean(useSelector(selectSnapshot))\n  const shouldEliminateZeroDimensions =\n    useSelector(selectShouldEliminateZeroDimensions) || isShowingSnapshot\n  const shouldUsePanAndZoomPadding = useSelector(selectPanAndZoomDataPadding)\n\n  const { CancelToken } = axios\n  // eslint-disable-next-line react-hooks/exhaustive-deps\n  const cancelTokenSource = useMemo(() => CancelToken.source(), [])\n  useUnmount(() => {\n    cancelTokenSource.cancel(CHART_UNMOUNTED)\n  })\n\n  /**\n   * spinner state\n   * show spinner when it's fetching for more than 2 seconds\n   * hide spinner immediately when it's not fetching\n   */\n  const [shouldShowSpinnerDebounced, setShouldShowSpinnerDebounced] = useState(false)\n  const shouldShowSpinner = shouldShowSpinnerDebounced && isFetchingData\n  useDebounce(\n    () => {\n      if (isFetchingData) {\n        setShouldShowSpinnerDebounced(true)\n      }\n    },\n    2000,\n    [isFetchingData]\n  )\n  useEffect(() => {\n    if (!isFetchingData && shouldShowSpinnerDebounced) {\n      setShouldShowSpinnerDebounced(false)\n    }\n  }, [isFetchingData, shouldShowSpinnerDebounced])\n\n  /**\n   * fetch data\n   */\n  useEffect(() => {\n    if (shouldFetch && actualChartMetadata && !isFetchingData) {\n      // todo can be overridden by main.js\n      const forceDataPoints = window.NETDATA.options.force_data_points\n\n      let after\n      let before\n      let newViewRange\n      let pointsMultiplier = 1\n\n      if (panAndZoom) {\n        if (isPanAndZoomMaster) {\n          after = Math.round(panAndZoom.after / 1000)\n          before = Math.round(panAndZoom.before / 1000)\n\n          newViewRange = [after, before]\n\n          if (shouldUsePanAndZoomPadding) {\n            const requestedPadding = Math.round((before - after) / 2)\n            after -= requestedPadding\n            before += requestedPadding\n            pointsMultiplier = 2\n          }\n        } else {\n          after = Math.round(panAndZoom.after / 1000)\n          before = Math.round(panAndZoom.before / 1000)\n          pointsMultiplier = 1\n        }\n      } else {\n        // no panAndZoom\n        before = initialBefore\n        after = liveModeAfter\n        pointsMultiplier = 1\n      }\n\n      newViewRange = (newViewRange || [after, before]).map(x => x * 1000) as [number, number]\n\n      const dataPoints =\n        attributes.points ||\n        Math.round(chartWidth / getChartPixelsPerPoint({ attributes, chartSettings }))\n      const points = forceDataPoints || dataPoints * pointsMultiplier\n\n      const shouldForceTimeWindow = attributes.forceTimeWindow || Boolean(defaultAfter)\n      // if we want to add fake points, we need first need to request less\n      // to have the desired frequency\n      // this will be removed when Agents will support forcing time-window between points\n      const correctedPoints = shouldForceTimeWindow\n        ? getCorrectedPoints({\n            after,\n            before,\n            firstEntry: actualChartMetadata.first_entry,\n            points,\n          })\n        : null\n\n      const group = attributes.method || window.NETDATA.chartDefaults.method\n      setShouldFetch(false)\n      dispatch(\n        fetchDataAction.request({\n          // properties to be passed to API\n          host,\n          context: actualChartMetadata.context,\n          chart: actualChartMetadata.id,\n          format: chartSettings.format,\n          points: correctedPoints || points,\n          group,\n          gtime: attributes.gtime || 0,\n          options: getChartURLOptions(attributes, shouldEliminateZeroDimensions),\n          after: after || null,\n          before: before || null,\n          dimensions: attributes.dimensions,\n          labels: attributes.labels,\n          postGroupBy: attributes.postGroupBy,\n          postAggregationMethod: attributes.postAggregationMethod,\n          aggrMethod: attributes.aggrMethod,\n          aggrGroups: attributes.aggrGroups,\n          // @ts-ignore\n          dimensionsAggrMethod:\n            dimensionsAggrMethodMap[attributes.dimensionsAggrMethod] ||\n            attributes.dimensionsAggrMethod,\n          nodeIDs,\n          httpMethod: attributes.httpMethod,\n          groupBy: attributes.groupBy,\n\n          // properties for the reducer\n          fetchDataParams: {\n            // we store it here so it is only available when data is fetched\n            // those params should be synced with data\n            fillMissingPoints: correctedPoints ? points - correctedPoints : undefined,\n            isRemotelyControlled,\n            viewRange: newViewRange,\n          },\n          id: chartUuid,\n          cancelTokenSource,\n        })\n      )\n    }\n  }, [\n    attributes,\n    actualChartMetadata,\n    chartSettings,\n    chartUuid,\n    chartWidth,\n    defaultAfter,\n    dispatch,\n    hasLegend,\n    host,\n    initialBefore,\n    isFetchingData,\n    isPanAndZoomMaster,\n    isRemotelyControlled,\n    liveModeAfter,\n    panAndZoom,\n    portalNode,\n    setShouldFetch,\n    shouldEliminateZeroDimensions,\n    shouldUsePanAndZoomPadding,\n    shouldFetch,\n    cancelTokenSource,\n    nodeIDs,\n    uuid,\n  ])\n\n  useSelector(selectSpacePanelTransitionEndIsActive)\n\n  const externalSelectedDimensions = attributes?.selectedDimensions\n  const [selectedDimensions, setSelectedDimensions] = useState<string[]>(\n    externalSelectedDimensions || emptyArray\n  )\n\n  useLayoutEffect(() => {\n    if (externalSelectedDimensions) {\n      setSelectedDimensions(externalSelectedDimensions)\n    }\n  }, [externalSelectedDimensions])\n\n  useLayoutEffect(() => {\n    setSelectedDimensions(externalSelectedDimensions || emptyArray)\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [attributes?.groupBy])\n\n  const customElementForDygraph = useMemo(\n    () =>\n      renderCustomElementForDygraph &&\n      renderCustomElementForDygraph({\n        onAttributesChange,\n        attributes,\n        chartMetadata: actualChartMetadata as ChartMetadata,\n        chartData,\n        chartID: id,\n      }),\n    [\n      onAttributesChange,\n      renderCustomElementForDygraph,\n      attributes,\n      id,\n      actualChartMetadata,\n      chartData,\n    ]\n  )\n\n  // eslint-disable-next-line max-len\n  const hasEmptyData =\n    (chartData as DygraphData | D3pieChartData | null)?.result?.data?.length === 0 ||\n    (chartData as EasyPieChartData | null)?.result?.length === 0\n\n  if (!chartData || !actualChartMetadata) {\n    return (\n      <>\n        <Loader\n          // Loader should remount when that flag is changed, because inside\n          // there's an oldschool bootstrap icon which doesn't handle updates well\n          key={`${hasEmptyData}`}\n          hasEmptyData={hasEmptyData}\n          containerNode={portalNode}\n        />\n        {shouldShowSpinner && <ChartSpinner chartLibrary={attributes.chartLibrary} />}\n      </>\n    )\n  }\n\n  return (\n    <>\n      {hasEmptyData && (\n        <Loader key={`${hasEmptyData}`} hasEmptyData={hasEmptyData} containerNode={portalNode} />\n      )}\n      <Chart\n        attributes={attributes}\n        chartContainerElement={portalNode}\n        chartData={chartData}\n        chartMetadata={actualChartMetadata}\n        chartUuid={chartUuid}\n        chartHeight={chartHeight}\n        chartWidth={chartWidth}\n        defaultAfter={defaultAfter}\n        globalPanAndZoom={globalPanAndZoom}\n        hasEmptyData={hasEmptyData}\n        isRemotelyControlled={fetchDataParams.isRemotelyControlled}\n        // view range that updates only when data is fetched\n        viewRangeForCurrentData={fetchDataParams.viewRange}\n        // view range that updates when requesting and fetching of data\n        viewRange={viewRange!}\n        selectedDimensions={selectedDimensions}\n        setSelectedDimensions={setSelectedDimensions}\n        showLatestOnBlur={!panAndZoom}\n      />\n      {shouldShowSpinner && <ChartSpinner chartLibrary={attributes.chartLibrary} />}\n      {dropdownMenu && dropdownMenu.length > 0 && (\n        <S.ChartDropdownContainer>\n          <ChartDropdown\n            dropdownMenu={dropdownMenu}\n            chartID={id}\n            attributes={attributes}\n            chartMetadata={actualChartMetadata}\n          />\n        </S.ChartDropdownContainer>\n      )}\n      {customElementForDygraph}\n    </>\n  )\n}\n","import { useEffect, useState } from \"react\"\nimport { useInterval } from \"react-use\"\n\nimport { useSelector } from \"store/redux-separate-context\"\nimport {\n  selectHasWindowFocus,\n  selectStopUpdatesWhenFocusIsLost,\n  selectGlobalPause,\n} from \"domains/global/selectors\"\nimport { BIGGEST_INTERVAL_NUMBER } from \"utils/biggest-interval-number\"\nimport { isPrintMode } from \"domains/dashboard/utils/parse-url\"\n\ntype UseFetchNewDataClock = (arg: {\n  areCriteriaMet: boolean\n  preferedIntervalTime: number\n}) => [boolean, (shouldFetch: boolean) => void]\nexport const useFetchNewDataClock: UseFetchNewDataClock = ({\n  areCriteriaMet,\n  preferedIntervalTime,\n}) => {\n  const hasWindowFocus = useSelector(selectHasWindowFocus)\n  const stopUpdatesWhenFocusIsLost = useSelector(selectStopUpdatesWhenFocusIsLost)\n  const globalPause = useSelector(selectGlobalPause)\n\n  const shouldBeUpdating = !(!hasWindowFocus && stopUpdatesWhenFocusIsLost) && !globalPause\n\n  const [shouldFetch, setShouldFetch] = useState<boolean>(true)\n  const [shouldFetchImmediatelyAfterFocus, setShouldFetchImmediatelyAfterFocus] = useState(false)\n\n  useEffect(() => {\n    if (shouldFetchImmediatelyAfterFocus && shouldBeUpdating) {\n      setShouldFetchImmediatelyAfterFocus(false)\n      setShouldFetch(true)\n    }\n  }, [shouldFetchImmediatelyAfterFocus, setShouldFetchImmediatelyAfterFocus, shouldBeUpdating])\n\n  // don't use setInterval when we loose focus\n  const intervalTime =\n    (shouldBeUpdating || !shouldFetchImmediatelyAfterFocus) && !isPrintMode\n      ? preferedIntervalTime\n      : BIGGEST_INTERVAL_NUMBER\n  useInterval(() => {\n    if (areCriteriaMet) {\n      if (!shouldBeUpdating) {\n        setShouldFetchImmediatelyAfterFocus(true)\n        return\n      }\n      setShouldFetch(true)\n    }\n    // when there's no focus, don't ask for updated data\n  }, intervalTime)\n  return [shouldFetch, setShouldFetch]\n}\n","import { Attributes } from \"./transformDataAttributes\"\nimport { ChartLibraryConfig } from \"./chartLibrariesSettings\"\n\ntype GetChartPixelsPerPoint = (arg: {\n  attributes: Attributes,\n  chartSettings: ChartLibraryConfig,\n}) => number\n\nexport const getChartPixelsPerPoint: GetChartPixelsPerPoint = ({\n  attributes, chartSettings,\n}) => {\n  const {\n    pixelsPerPoint: pixelsPerPointAttribute,\n  } = attributes\n  if (typeof pixelsPerPointAttribute === \"number\") {\n    return pixelsPerPointAttribute\n  }\n  const pixelsPerPointSetting = chartSettings.pixelsPerPoint(attributes)\n\n  return Math.max(...[\n    pixelsPerPointSetting,\n    window.NETDATA.options.current.pixels_per_point,\n  ].filter((px) => typeof px === \"number\"))\n}\n","import { prop, pick } from \"ramda\"\nimport { createSelector } from \"reselect\"\n\nimport { AppStateT } from \"store/app-state\"\n\nimport { storeKey } from \"./constants\"\n\nconst selectDashboardDomain = (state: AppStateT) => state[storeKey]\n\nexport const selectIsSnapshotMode = createSelector(\n  selectDashboardDomain,\n  prop(\"isSnapshotMode\"),\n)\n\nexport const selectSnapshotOptions = createSelector(\n  selectDashboardDomain,\n  pick([\"snapshotCharts\", \"snapshotDataPoints\"]),\n)\n","import React, { useEffect } from \"react\"\n\nimport { MS_IN_SECOND } from \"utils/utils\"\nimport { serverDefault } from \"utils/server-detection\"\nimport { selectIsSnapshotMode, selectSnapshotOptions } from \"domains/dashboard/selectors\"\nimport { selectGlobalPanAndZoom } from \"domains/global/selectors\"\nimport { useDispatch, useSelector } from \"store/redux-separate-context\"\nimport { TimeRangeObjT } from \"types/common\"\n\nimport { Attributes } from \"../utils/transformDataAttributes\"\nimport { fetchDataForSnapshotAction } from \"../actions\"\nimport { chartLibrariesSettings } from \"../utils/chartLibrariesSettings\"\nimport { getChartURLOptions } from \"../utils/get-chart-url-options\"\n\ninterface SnapshotLoaderProps {\n  attributes: Attributes\n  chartUuid: string\n}\nconst SnapshotLoader = ({\n  attributes,\n  chartUuid,\n}: SnapshotLoaderProps) => {\n  const host = attributes.host || serverDefault\n  const { snapshotDataPoints } = useSelector(selectSnapshotOptions)\n  const group = attributes.method || window.NETDATA.chartDefaults.method\n  const { chartLibrary } = attributes\n  const chartSettings = chartLibrariesSettings[chartLibrary]\n\n  const globalPanAndZoom = useSelector(selectGlobalPanAndZoom)\n  const after = (globalPanAndZoom as TimeRangeObjT).after / MS_IN_SECOND\n  const before = (globalPanAndZoom as TimeRangeObjT).before / MS_IN_SECOND\n\n  const dispatch = useDispatch()\n  useEffect(() => {\n    dispatch(fetchDataForSnapshotAction.request({\n      // properties to be passed to API\n      host,\n      context: attributes.id,\n      chart: attributes.id,\n      format: chartSettings.format,\n      points: snapshotDataPoints as number,\n      group,\n      gtime: attributes.gtime || 0,\n      // for snapshots, always eliminate zero dimensions\n      options: getChartURLOptions(attributes, true),\n      after: after || null,\n      before: before || null,\n      dimensions: attributes.dimensions,\n      aggrMethod: attributes.aggrMethod,\n      nodeIDs: attributes.nodeIDs,\n      chartLibrary,\n      id: chartUuid,\n      groupBy: attributes.groupBy,\n    }))\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }) // todo fetch based on state\n  return null\n}\n\n\ninterface SnapshotLoaderContainerProps {\n  attributes: Attributes\n  chartUuid: string\n}\nexport const SnapshotLoaderContainer = ({\n  attributes,\n  chartUuid,\n}: SnapshotLoaderContainerProps) => {\n  const isSnapshotMode = useSelector(selectIsSnapshotMode)\n  if (!isSnapshotMode) {\n    return null\n  }\n  return <SnapshotLoader attributes={attributes} chartUuid={chartUuid} />\n}\n","import React, { memo } from \"react\"\nimport { createPortal } from \"react-dom\"\n\nimport { getAttributes } from \"../utils/transformDataAttributes\"\nimport { ChartWithLoader } from \"./chart-with-loader\"\nimport { DisableOutOfView } from \"./disable-out-of-view\"\nimport { SnapshotLoaderContainer } from \"./snapshot-loader\"\n\nconst getNodesArray = () => Array.from(document.querySelectorAll(\"[data-netdata]\"))\n\nexport const Portals = memo(() => {\n  const nodes = getNodesArray()\n  return (\n    <>\n      {nodes.map((node, index) => {\n        const attributesMapped = getAttributes(node)\n        const chartId = `${attributesMapped.id}-${index}`\n        return (\n          createPortal(\n            <>\n              <DisableOutOfView\n                attributes={attributesMapped}\n                chartUuid={chartId}\n                portalNode={(node as HTMLElement)}\n              >\n                <ChartWithLoader\n                  attributes={attributesMapped}\n                  // todo change to uuid generator (when we disconnect dashboard.js)\n                  chartUuid={chartId}\n                  portalNode={(node as HTMLElement)}\n                />\n              </DisableOutOfView>\n              <SnapshotLoaderContainer\n                attributes={attributesMapped}\n                chartUuid={chartId}\n              />\n            </>,\n            node,\n          )\n        )\n      })}\n    </>\n  )\n})\n","import { useEffect, useState } from \"react\"\n\nimport { axiosInstance } from \"utils/api\"\n\nexport const useHttp = <T = unknown>(\n  url: string | undefined,\n  shouldMakeCall : boolean = true,\n  isExternal?: boolean,\n) => {\n  const [isFetching, setIsFetching] = useState(false)\n  const [isError, setIsError] = useState(false)\n  const [data, setData] = useState<T | null>(null)\n  useEffect(() => {\n    if (shouldMakeCall && url) {\n      const options = isExternal\n        ? { headers: null, withCredentials: false }\n        : {}\n\n      setIsFetching(true)\n      axiosInstance.get(url, options)\n        .then((r) => {\n          if (r.data) {\n            setData(r.data)\n            setIsError(false)\n            setIsFetching(false)\n          }\n        })\n        .catch((error) => {\n          // eslint-disable-next-line no-console\n          console.warn(`error fetching ${url}`, error)\n          setIsError(true)\n          setIsFetching(false)\n        })\n    }\n  }, [isExternal, shouldMakeCall, url])\n  // force triple instead of array\n  return [data, isFetching, isError] as [T | null, boolean, boolean]\n}\n","import { ReactNode, useEffect, useRef } from \"react\"\nimport { createPortal } from \"react-dom\"\n\nconst modalRoot = document.getElementById(\"modal-root\") as HTMLElement\n\ntype Props = {\n  children: ReactNode\n}\nexport const ModalPortal = ({ children }: Props) => {\n  const element = useRef(document.createElement(\"div\"))\n  useEffect(() => {\n    modalRoot.appendChild(element.current)\n    return () => {\n      // eslint-disable-next-line react-hooks/exhaustive-deps\n      modalRoot.removeChild(element.current)\n    }\n  }, [])\n\n  return createPortal(children, element.current)\n}\n","import React, { useRef, useEffect } from \"react\"\nimport classNames from \"classnames\"\n\nimport { useSelector } from \"store/redux-separate-context\"\nimport { ModalPortal } from \"domains/dashboard/components/modal-portal\"\nimport {\n  selectAmountOfCharts, selectAmountOfFetchedCharts, selectNameOfAnyFetchingChart,\n} from \"domains/chart/selectors\"\n\nimport \"./print-modal.scss\"\n\nconst TIMEOUT_DURATION_TO_MAKE_SURE_ALL_CHARTS_HAVE_BEEN_RENDERED = 1000\n\nexport const PrintModal = () => {\n  const printModalElement = useRef<HTMLDivElement>(null)\n  const isFetchingMetrics = true\n\n  useEffect(() => {\n    // todo replace bootstrap with newer solution (custom or react-compatible library)\n    if (printModalElement.current) {\n      const $element = window.$(printModalElement.current)\n      $element.modal(\"show\")\n    }\n  }) // render just once\n\n  const amountOfCharts = useSelector(selectAmountOfCharts)\n  const amountOfFetchedCharts = useSelector(selectAmountOfFetchedCharts)\n  const nameOfAnyFetchingChart = useSelector(selectNameOfAnyFetchingChart)\n\n  const percentage = amountOfCharts === 0\n    ? 0\n    : (amountOfFetchedCharts / amountOfCharts) * 100\n\n  useEffect(() => {\n    if (percentage === 100) {\n      setTimeout(() => {\n        // in case browser will not be able to close the window\n        window.$(printModalElement.current).modal(\"hide\")\n        window.print()\n        window.close()\n      }, TIMEOUT_DURATION_TO_MAKE_SURE_ALL_CHARTS_HAVE_BEEN_RENDERED)\n    }\n  }, [percentage])\n\n\n  const progressBarText = nameOfAnyFetchingChart\n    && `${Math.round(percentage)}%, ${nameOfAnyFetchingChart}`\n\n\n  return (\n    <ModalPortal>\n      <div\n        ref={printModalElement}\n        className=\"modal fade\"\n        id=\"printModal\"\n        tabIndex={-1}\n        role=\"dialog\"\n        aria-labelledby=\"printModalLabel\"\n        data-keyboard=\"false\"\n        data-backdrop=\"static\"\n      >\n        <div className=\"modal-dialog modal-lg\" role=\"document\">\n          <div className=\"modal-content\">\n            <div className=\"modal-header\">\n              <button\n                type=\"button\"\n                className={classNames(\n                  \"close\",\n                  { \"print-modal__close-button--disabled\": isFetchingMetrics },\n                )}\n                data-dismiss=\"modal\"\n                aria-label=\"Close\"\n              >\n                <span aria-hidden=\"true\">&times;</span>\n              </button>\n              <h4 className=\"modal-title\" id=\"printModalLabel\">\n                Preparing dashboard for printing...\n              </h4>\n            </div>\n            <div className=\"modal-body\">\n              Please wait while we initialize and render all the charts on the dashboard.\n              <div\n                className=\"progress progress-striped active\"\n                style={{ height: \"2em\" }}\n              >\n                <div\n                  id=\"printModalProgressBar\"\n                  className=\"progress-bar progress-bar-info\"\n                  role=\"progressbar\"\n                  aria-valuenow={percentage}\n                  aria-valuemin={0}\n                  aria-valuemax={100}\n                  style={{\n                    minWidth: \"2em\",\n                    width: `${percentage}%`,\n                  }}\n                >\n                  <span\n                    id=\"printModalProgressBarText\"\n                    style={{\n                      paddingLeft: 10,\n                      paddingTop: 4,\n                      fontSize: \"1.2em\",\n                      textAlign: \"left\",\n                      width: \"100%\",\n                      position: \"absolute\",\n                      display: \"block\",\n                      color: \"black\",\n                    }}\n                  >\n                    {progressBarText}\n                  </span>\n                </div>\n              </div>\n              The print dialog will appear as soon as we finish rendering the page.\n            </div>\n            <div className=\"modal-footer\" />\n          </div>\n        </div>\n      </div>\n    </ModalPortal>\n  )\n}\n","import styled from \"styled-components\"\nimport { getSizeBy, getColor } from \"@netdata/netdata-ui\"\n\nexport const SocialMediaContainer = styled.div`\n  width: 185px;\n  padding: ${getSizeBy(2)};\n  background: ${getColor(\"borderSecondary\")};\n\n  font-size: 12px;\n  margin-bottom: ${getSizeBy(3)};\n`\n\nexport const FirstRow = styled.div`\n  display: flex;\n  justify-content: space-between;\n`\n\nexport const GithubCopy = styled.div`\n\n`\n\nexport const GithubCopyLine = styled.div`\n\n`\n\nexport const SocialMediaLink = styled.a`\n  &, &:hover {\n    color: ${getColor(\"main\")};\n  }\n`\n\nexport const GithubStarQuestion = styled(SocialMediaLink)``\n\nexport const GithubIcon = styled(SocialMediaLink)`\n  font-size: 24px;\n`\n\nexport const TwitterIcon = styled(SocialMediaLink)`\n  font-size: 17px;\n`\n\nexport const FacebookIcon = styled(SocialMediaLink)`\n  font-size: 23px;\n`\n\nexport const Separator = styled.div`\n  margin-top: ${getSizeBy(2)};\n  border-top: 1px solid ${getColor(\"separator\")};\n\n`\nexport const SecondRow = styled.div`\n  margin-top: ${getSizeBy(2)};\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n`\n\nexport const SecondRowText = styled.span`\n  font-size: 10px;\n`\n","import React from \"react\"\n\nimport * as S from \"./styled\"\n\nexport const SidebarSocialMedia = () => (\n  <S.SocialMediaContainer>\n    <S.FirstRow>\n      <S.GithubCopy>\n        <S.GithubCopyLine>\n            Do you like Netdata?\n        </S.GithubCopyLine>\n        <S.GithubStarQuestion href=\"https://github.com/netdata/netdata/\" target=\"_blank\">\n            Give us a star!\n        </S.GithubStarQuestion>\n      </S.GithubCopy>\n      <S.GithubIcon href=\"https://github.com/netdata/netdata/\" target=\"_blank\">\n        <i className=\"fab fa-github\" />\n      </S.GithubIcon>\n    </S.FirstRow>\n    <S.Separator />\n    <S.SecondRow>\n      <S.SecondRowText>\n          And share the word!\n      </S.SecondRowText>\n      <S.TwitterIcon href=\"https://twitter.com/linuxnetdata/\" target=\"_blank\">\n        <i className=\"fab fa-twitter\" />\n      </S.TwitterIcon>\n      <S.FacebookIcon href=\"https://www.facebook.com/linuxnetdata/\" target=\"_blank\">\n        <i className=\"fab fa-facebook\" />\n      </S.FacebookIcon>\n    </S.SecondRow>\n  </S.SocialMediaContainer>\n)\n","import React, { useRef } from \"react\"\nimport { createPortal } from \"react-dom\"\n\ninterface Props {\n  children: React.ReactNode\n}\nexport const SidebarSocialMediaPortal = ({\n  children,\n}: Props) => {\n  const element = useRef(document.querySelector(\"#sidebar-end-portal-container\"))\n  return createPortal(children, element.current!)\n}\n","import React from \"react\"\nimport styled from \"styled-components\"\nimport { ToastContainer, ToastContainerProps } from \"react-toastify\"\nimport \"react-toastify/dist/ReactToastify.min.css\"\n\nimport { getColor } from \"@netdata/netdata-ui\"\n\nimport { notificationsZIndex } from \"styles/z-index\"\n\nconst WrappedToastContainer = ({\n  className,\n  ...rest\n}: ToastContainerProps & { className?: string }) => (\n  <div className={className}>\n    {/* eslint-disable-next-line react/jsx-props-no-spreading */}\n    <ToastContainer {...rest} closeButton={false} />\n  </div>\n)\n\nexport const NotificationsContainer = styled(WrappedToastContainer)<ToastContainerProps>`\n  .Toastify__toast-container {\n    position: fixed;\n    width: unset;\n    min-width: 400px;\n    max-width: 500px;\n    ${notificationsZIndex};\n    color: ${getColor([\"neutral\", \"limedSpruce\"])};\n  }\n  .Toastify__toast {\n    padding: 0;\n    padding-top: 5px;\n  }\n  .Toastify__toast--error {\n    background: ${getColor([\"red\", \"lavender\"])};\n    border: 1px solid ${getColor(\"error\")};\n  }\n  .Toastify__toast--warning {\n  }\n  .Toastify__toast--success {\n    background: ${getColor([\"green\", \"frostee\"])};\n    border: 1px solid ${getColor(\"success\")};\n  }\n  .Toastify__toast-body {\n  }\n  .Toastify__progress-bar {\n    bottom: unset;\n    top: 0;\n  }\n  .Toastify__progress-bar--success {\n    background-color: ${getColor(\"success\")};\n  }\n  .Toastify__progress-bar--error {\n    background-color: ${getColor(\"error\")};\n  }\n`\n","import React from \"react\"\nimport { Icon, Flex } from \"@netdata/netdata-ui\"\n\nconst Item = ({ icon, children, hasBorder }) => (\n  <Flex\n    gap={2}\n    border={hasBorder && { side: \"right\", color: \"separator\" }}\n    alignItems=\"center\"\n    padding={[0, 3, 0, 0]}\n    height=\"100%\"\n  >\n    {!!icon && <Icon name={icon} color=\"bright\" height=\"15px\" />}\n    {children}\n  </Flex>\n)\n\nexport default Item\n","import React from \"react\"\nimport { Text } from \"@netdata/netdata-ui\"\nimport Item from \"../item\"\nimport { useSelector } from \"@/src/store/redux-separate-context\"\n\nconst hostNameSelector = state => {\n  const snapshot = state.global.snapshot\n  const data = state.global.chartsMetadata.data\n\n  if (!snapshot && !data) return \"\"\n  return snapshot ? snapshot.hostname : data.hostname\n}\n\nconst Node = () => {\n  const hostname = useSelector(hostNameSelector)\n\n  return (\n    <Item icon=\"node_hollow\">\n      <Text data-testid={`header-nodename-${hostname}`} color=\"bright\" strong truncate>\n        {hostname}\n      </Text>\n    </Item>\n  )\n}\n\nexport default Node\n","import React from \"react\"\nimport { Flex, TextSmall } from \"@netdata/netdata-ui\"\n\nconst tooltipBackground = [\"neutral\", \"black\"]\n\nconst CustomTooltip = ({ children, isBasic }) => (\n  <Flex\n    padding={[1.5, 2]}\n    margin={[2]}\n    background={tooltipBackground}\n    round={1}\n    {...(!isBasic && { width: { max: \"300px\" } })}\n  >\n    <TextSmall color=\"bright\">{children}</TextSmall>\n  </Flex>\n)\n\nexport default CustomTooltip\n","import React from \"react\"\nimport CustomTooltip from \"@/src/components/tooltips/customTooltip\"\n\nconst getContent = (content, { isBasic }) => {\n  const contentNode = typeof content === \"function\" ? content() : content\n  if (typeof content === \"string\" || isBasic) {\n    return <CustomTooltip isBasic={isBasic}>{contentNode}</CustomTooltip>\n  }\n  return contentNode\n}\n\nexport default getContent\n","import React, { useCallback } from \"react\"\nimport { Tooltip as BaseTooltip } from \"@netdata/netdata-ui\"\nimport getContent from \"./getContent\"\n\nconst Tooltip = ({ children, content, isBasic, ...rest }) => {\n  const getTooltipContent = useCallback(() => getContent(content, { isBasic }), [content, isBasic])\n  return (\n    <BaseTooltip plain animation content={getTooltipContent} {...rest}>\n      {children}\n    </BaseTooltip>\n  )\n}\n\nexport default Tooltip\n","import React, { useCallback } from \"react\"\nimport { Flex, Button } from \"@netdata/netdata-ui\"\nimport Tooltip from \"@/src/components/tooltips\"\nimport { setGlobalPauseAction } from \"domains/global/actions\"\nimport { useDispatch } from \"store/redux-separate-context\"\n\nconst Options = () => {\n  const dispatch = useDispatch()\n  const onClick = useCallback(() => dispatch(setGlobalPauseAction()), [dispatch])\n  return (\n    <Flex gap={2} data-testid=\"header-options-button\">\n      <Tooltip content=\"Import a Netdata snapshot\" align=\"bottom\" plain>\n        <Button\n          flavour=\"borderless\"\n          neutral\n          themeType=\"dark\"\n          data-toggle=\"modal\"\n          data-target=\"#loadSnapshotModal\"\n          icon=\"download\"\n        />\n      </Tooltip>\n      <Tooltip content=\"Export a Netdata snapshot\" align=\"bottom\" plain>\n        <Button\n          onClick={onClick}\n          flavour=\"borderless\"\n          neutral\n          themeType=\"dark\"\n          data-toggle=\"modal\"\n          data-target=\"#saveSnapshotModal\"\n          icon=\"upload\"\n        />\n      </Tooltip>\n      <Tooltip content=\"Print the dashboard\" align=\"bottom\" plain>\n        <Button\n          flavour=\"borderless\"\n          neutral\n          themeType=\"dark\"\n          data-toggle=\"modal\"\n          data-target=\"#printPreflightModal\"\n          icon=\"print\"\n        />\n      </Tooltip>\n    </Flex>\n  )\n}\n\nexport default Options\n","import React from \"react\"\nimport { Button } from \"@netdata/netdata-ui\"\nimport Tooltip from \"@/src/components/tooltips\"\n\nimport { useHttp } from \"hooks/use-http\"\n\nconst NETDATA_LATEST_VERSION_URL = \"https://api.github.com/repos/netdata/netdata/releases/latest\"\nconst NETDATA_LATEST_GCS_VERSION_URL =\n  \"https://www.googleapis.com/storage/v1/b/netdata-nightlies/o/latest-version.txt\"\n\nconst transformGcsVersionResponse = data => data.replace(/(\\r\\n|\\n|\\r| |\\t)/gm, \"\")\n\nconst transformGithubResponse = data => data?.tag_name.replace(/(\\r\\n|\\n|\\r| |\\t)/gm, \"\")\n\nconst versionsMatch = (v1, v2) => {\n  if (v1 === v2) {\n    return true\n  }\n  let s1 = v1.split(\".\")\n  let s2 = v2.split(\".\")\n  // Check major version\n  let n1 = parseInt(s1[0].substring(1, 2), 10)\n  let n2 = parseInt(s2[0].substring(1, 2), 10)\n  if (n1 < n2) return false\n  if (n1 > n2) return true\n\n  // Check minor version\n  n1 = parseInt(s1[1], 10)\n  n2 = parseInt(s2[1], 10)\n  if (n1 < n2) return false\n  if (n1 > n2) return true\n\n  // Split patch: format could be e.g. 0-22-nightly\n  s1 = s1[2].split(\"-\")\n  s2 = s2[2].split(\"-\")\n\n  n1 = parseInt(s1[0], 10)\n  n2 = parseInt(s2[0], 10)\n  if (n1 < n2) return false\n  if (n1 > n2) return true\n\n  n1 = s1.length > 1 ? parseInt(s1[1], 10) : 0\n  n2 = s2.length > 1 ? parseInt(s2[1], 10) : 0\n  if (n1 < n2) return false\n  return true\n}\n\nconst VersionControl = ({ currentVersion, releaseChannel }) => {\n  const isStableReleaseChannel = releaseChannel === \"stable\"\n  const [githubVersion] = useHttp(NETDATA_LATEST_VERSION_URL, isStableReleaseChannel, true)\n\n  const [gcsVersionResponse] = useHttp(NETDATA_LATEST_GCS_VERSION_URL, !isStableReleaseChannel)\n  const [mediaLinkResponse] = useHttp(gcsVersionResponse?.mediaLink, Boolean(gcsVersionResponse))\n\n  const latestVersion = isStableReleaseChannel\n    ? transformGithubResponse(githubVersion)\n    : mediaLinkResponse\n    ? transformGcsVersionResponse(mediaLinkResponse)\n    : null\n\n  if (!latestVersion) {\n    return null\n  }\n  const isNewVersionAvailable = !versionsMatch(currentVersion, latestVersion)\n\n  return (\n    <Tooltip content={isNewVersionAvailable ? \"Need help?\" : \"Check Version\"} align=\"bottom\" plain>\n      <Button\n        data-testid=\"header-version-control-button\"\n        flavour=\"borderless\"\n        themeType=\"dark\"\n        small\n        neutral={!isNewVersionAvailable}\n        warning={isNewVersionAvailable}\n        name={isNewVersionAvailable ? \"update_pending\" : \"update\"}\n        icon={isNewVersionAvailable ? \"update_pending\" : \"update\"}\n        data-toggle=\"modal\"\n        data-target=\"#updateModal\"\n      />\n    </Tooltip>\n  )\n}\n\nexport default VersionControl\n","import React from \"react\"\nimport VersionControl from \"components/app-header/components/versionControl\"\nimport { useSelector } from \"@/src/store/redux-separate-context\"\n\nconst versionSelector = state => {\n  const { data } = state.global.chartsMetadata\n\n  if (!data) return null\n\n  const { version, release_channel: releaseChannel } = data\n  return {\n    version,\n    releaseChannel,\n  }\n}\n\nconst Version = () => {\n  const data = useSelector(versionSelector)\n  return (\n    data && <VersionControl currentVersion={data.version} releaseChannel={data.releaseChannel} />\n  )\n}\n\nexport default Version\n","import { useState, useCallback } from \"react\"\n\n/**\n * @example\n * const [value, toggle, toggleOn, toggleOff]  = useToggle(false);\n *\n * @param {Boolean} initialValue\n */\n\nconst useToggle = (initialValue = false) => {\n  const [value, setToggle] = useState(!!initialValue)\n  const toggle = useCallback(() => setToggle(oldValue => !oldValue), [])\n  const toggleOn = useCallback(() => setToggle(true), [])\n  const toggleOff = useCallback(() => setToggle(false), [])\n\n  return [value, toggle, toggleOn, toggleOff]\n}\n\nexport default useToggle\n","import { useEffect, useState } from \"react\"\n\nconst useLocalStorage = (key, defaultValue) => {\n  const [value, setValue] = useState(() => getValueFromStorage(key, defaultValue))\n\n  useEffect(() => localStorage.setItem(key, JSON.stringify(value)), [key, value])\n\n  return [value, setValue]\n}\n\nconst getValueFromStorage = (key, defaultValue = \"\") =>\n  JSON.parse(localStorage.getItem(key)) ?? defaultValue\n\nexport default useLocalStorage\n","import styled from \"styled-components\"\nimport { getColor, getSizeBy, Icon } from \"@netdata/netdata-ui\"\nimport { Menu } from \"@rmwc/menu\"\n\nexport const RootContainer = styled.div`\n  width: 100%;\n  height: 100%;\n  display: flex;\n  flex-flow: row nowrap;\n  align-items: center;\n`\n\nexport const StyledMenu = styled(Menu)``\n\nexport const DropdownContainer = styled.div`\n  cursor: pointer;\n  color: ${getColor(\"bright\")};\n  .mdc-menu-surface {\n    border-radius: 0;\n    .mdc-list {\n      padding: 0;\n    }\n    .mdc-list-item {\n      padding: 0 ${getSizeBy(5)} 0 ${getSizeBy(5)};\n      font-size: 14px;\n      height: ${getSizeBy(6)};\n    }\n  }\n`\n\nexport const ListContainer = styled.div`\n  padding: ${getSizeBy(3)} 0;\n`\n\nexport const OpenerIcon = styled(Icon)`\n  flex-shrink: 0;\n  flex-grow: 0;\n  margin-left: ${({ noMargin }) => (noMargin ? \"unset\" : \"16px\")};\n  fill: ${getColor(\"bright\")};\n  width: 10px;\n  height: 5px;\n`\n","import styled from \"styled-components\"\nimport { getColor, getSizeBy, Icon, Drop } from \"@netdata/netdata-ui\"\nimport { Dropdown } from \"@/src/components/mdx-components/dropdown\"\nimport { dialogsZIndex, customDropdownZIndex } from \"@/src/styles/z-index\"\n\nexport const PickerBox = styled.div`\n  display: flex;\n  position: relative;\n  min-width: ${getSizeBy(102)};\n  min-height: ${getSizeBy(43)};\n  flex-direction: column;\n  align-items: flex-end;\n  background-color: ${getColor(\"mainBackground\")};\n  color: ${getColor(\"text\")};\n  z-index: ${dialogsZIndex};\n  border-radius: 8px;\n`\n\nexport const StyledTimePeriod = styled.span`\n  margin-top: ${getSizeBy(3)};\n  cursor: pointer;\n  width: 187px;\n  height: ${getSizeBy(2)};\n  &:first-of-type {\n    margin-top: ${getSizeBy(1)};\n  }\n  &:last-of-type {\n    margin-bottom: ${getSizeBy(1)};\n  }\n  & > span:hover {\n    color: ${getColor(\"textLite\")};\n  }\n`\nexport const StyledCustomTimePeriod = styled.span`\n  margin: ${getSizeBy(1)} ${getSizeBy(3)} 0;\n  color: ${({ isSelected, theme }) => getColor(isSelected ? \"primary\" : \"text\")({ theme })};\n  cursor: pointer;\n  &:first-of-type {\n    margin-top: 0;\n  }\n  &:hover {\n    color: ${getColor(\"textLite\")};\n  }\n`\n\nexport const StyledDropdown = styled(Dropdown)`\n  width: 88px;\n  height: 32px;\n  padding-top: 8px;\n  padding-bottom: 8px;\n  padding-left: 8px;\n  padding-right: 7px;\n  border: 1px solid ${getColor(\"border\")};\n  box-sizing: border-box;\n  border-radius: 4px;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  color: ${getColor(\"text\")};\n  .mdc-menu-surface--anchor {\n    .mdc-menu-surface--open {\n      ${customDropdownZIndex}\n      margin-top: ${getSizeBy(2)};\n      background: ${getColor(\"mainBackground\")};\n      border-radius: 4px;\n    }\n  }\n  .mdc-list {\n    display: flex;\n    flex-direction: column;\n    justify-content: center;\n    align-items: center;\n  }\n`\nexport const DropdownIcon = styled(Icon)`\n  fill: ${getColor(\"text\")};\n  width: 12px;\n  height: 12px;\n`\n\nexport const CustomInput = styled.input`\n  border: 1px solid ${getColor(\"border\")};\n  color: inherit;\n  background: ${getColor(\"mainBackground\")};\n  box-sizing: border-box;\n  border-radius: 4px;\n  padding: 4px;\n  width: 32px;\n  height: 32px;\n  margin-left: 10px;\n  margin-right: 10px;\n  outline: none;\n  &:focus {\n    border: 1px solid ${getColor(\"primary\")};\n  }\n`\nexport const StyledDrop = styled(Drop).attrs({\n  background: \"mainBackground\",\n  round: 2,\n  margin: [4, 0, 0],\n  border: { side: \"all\", color: \"elementBackground\" },\n  animation: true,\n})`\n  box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);\n`\nexport const StyledHR = styled.hr`\n  border: none;\n  margin: 0;\n  border-left: 1px solid ${getColor(\"borderSecondary\")};\n  height: 284px;\n`\n","import React, { useRef } from \"react\"\nimport { List } from \"@rmwc/list\"\nimport { MenuSurfaceAnchor, MenuSurface } from \"@rmwc/menu\"\nimport { RootContainer, ListContainer, DropdownContainer, OpenerIcon } from \"./styled\"\n\nexport const Dropdown = ({\n  title,\n  children,\n  className,\n  renderTitle,\n  isOpen = false,\n  onMenuToggle,\n  anchorCorner = \"bottomStart\",\n  renderOpener,\n}) => {\n  const ref = useRef()\n\n  const handleOpenState = () => {\n    onMenuToggle(!isOpen)\n  }\n\n  const handleClose = () => {\n    onMenuToggle(false)\n  }\n\n  return (\n    <DropdownContainer className={className}>\n      <MenuSurfaceAnchor>\n        <MenuSurface ref={ref} open={isOpen} onClose={handleClose} anchorCorner={anchorCorner}>\n          {typeof children === \"function\" ? (\n            isOpen && (\n              <ListContainer>\n                <List>{children({ maxHeight: ref.current?.root.ref.style.maxHeight })}</List>\n              </ListContainer>\n            )\n          ) : (\n            <ListContainer>\n              <List>{children}</List>\n            </ListContainer>\n          )}\n        </MenuSurface>\n        <RootContainer onClick={handleOpenState}>\n          {title || (renderTitle && renderTitle())}\n          {renderOpener ? (\n            renderOpener()\n          ) : (\n            <OpenerIcon name=\"triangle_down\" noMargin={Boolean(renderTitle)} />\n          )}\n        </RootContainer>\n      </MenuSurfaceAnchor>\n    </DropdownContainer>\n  )\n}\n","import React, { memo, useCallback } from \"react\"\nimport { Text } from \"@netdata/netdata-ui\"\nimport { StyledTimePeriod } from \"./styled\"\n\nconst TimePeriod = ({ value, period, resolution, isSelected, setTimeRange, tagging }) => {\n  const onClick = useCallback(\n    () => setTimeRange(value, resolution),\n    [value, resolution, setTimeRange]\n  )\n  return (\n    <StyledTimePeriod\n      key={value}\n      onClick={onClick}\n      data-ga={`date-picker::click-quick-selector::${tagging}::${-value}`}\n      data-testid=\"timePeriod-value\"\n    >\n      <Text color={isSelected ? \"primary\" : \"text\"}>{period}</Text>\n    </StyledTimePeriod>\n  )\n}\n\nexport default memo(TimePeriod)\n","import { format, formatDistanceStrict, parse, getTime, getUnixTime, add, isMatch } from \"date-fns\"\n\nconst MINUTE = 60\nconst HOUR = MINUTE * 60\nconst DAY = HOUR * 24\nconst MONTH = 30 * DAY\n\nexport const maxTimePeriodInUnix = 94694400\nexport const dateResolutions = [\"minutes\", \"hours\", \"days\", \"months\"]\n\nconst resolutionsMapping = {\n  minutes: MINUTE,\n  hours: HOUR,\n  days: DAY,\n  months: MONTH,\n}\n\nexport const getCustomTimePeriod = (after, resolution) =>\n  Math.round(after / resolutionsMapping[resolution])\n\nexport const parseInputPeriod = (timeCorrection, resolution) => {\n  const customRange = add(new Date(0), {\n    [resolution]: timeCorrection,\n  })\n  return -getUnixTime(customRange)\n}\n\nconst focusTaggingMap = {\n  startDate: \"start\",\n  endDate: \"finish\",\n}\n\nexport const getFocusTagging = focusedInput => focusTaggingMap[focusedInput]\n\nexport const timePeriods = [\n  { period: \"Last 5 minutes\", value: -5 * MINUTE, resolution: \"minutes\" },\n  { period: \"Last 15 minutes\", value: -15 * MINUTE, resolution: \"minutes\" },\n  { period: \"Last 30 minutes\", value: -30 * MINUTE, resolution: \"minutes\" },\n  { period: \"Last 2 hours\", value: -2 * HOUR, resolution: \"hours\" },\n  { period: \"Last 6 hours\", value: -6 * HOUR, resolution: \"hours\" },\n  { period: \"Last 12 hours\", value: -12 * HOUR, resolution: \"hours\" },\n  { period: \"Last Day\", value: -DAY, resolution: \"days\" },\n  { period: \"Last 2 Days\", value: -2 * DAY, resolution: \"days\" },\n  { period: \"Last 7 Days\", value: -7 * DAY, resolution: \"days\" },\n]\n\nexport const formatDates = (startDate, endDate) => {\n  const formattedStartDate = format(startDate, \"MMMM d yyyy, H:mm:ss\")\n  const formattedEndDate = format(endDate, \"MMMM d yyyy, H:mm:ss\")\n  return {\n    formattedStartDate,\n    formattedEndDate,\n  }\n}\n\nexport const formatOffset = offset => {\n  if (!offset) return \"+00:00\"\n  const splitOffset = offset.toString().split(\".\")\n  const mathSign = splitOffset[0] > 0 ? \"+\" : \"-\"\n  const absoluteNumber = Math.abs(splitOffset[0]).toString()\n  const firstPart = `${mathSign}${absoluteNumber.padStart(2, 0)}`\n  return splitOffset.length > 1\n    ? `${firstPart}:${String(splitOffset[1] * 0.6).padEnd(2, 0)}`\n    : `${firstPart}:00`\n}\n\nexport const getDateWithOffset = (date, utcOffset) => {\n  const formattedDate = isMatch(date, \"MMMM d yyyy, H:mm\")\n    ? date\n    : parse(date, \"MMMM d yyyy, H:mm\", Date.now())\n  return parse(`${formattedDate} ${formatOffset(utcOffset)}`, \"MMMM d yyyy, H:mm xxx\", Date.now())\n}\n\nexport const getTimePeriod = (startDate, endDate) =>\n  formatDistanceStrict(getTime(startDate), getTime(endDate))\n","import React from \"react\"\nimport { Flex } from \"@netdata/netdata-ui\"\nimport TimePeriod from \"./timePeriod\"\nimport { timePeriods } from \"./utils\"\n\nconst TimePeriods = ({ handleTimePeriodChange, selectedDate, tagging }) => (\n  <Flex\n    column\n    justifyContent=\"start\"\n    alignItems=\"start\"\n    height={{ max: \"240px\" }}\n    overflow={{ vertical: \"scroll\" }}\n    data-testid=\"timePeriods\"\n  >\n    {timePeriods.map(({ period, value, resolution }) => (\n      <TimePeriod\n        key={value}\n        value={value}\n        period={period}\n        resolution={resolution}\n        setTimeRange={handleTimePeriodChange}\n        isSelected={selectedDate === value}\n        tagging={tagging}\n      />\n    ))}\n  </Flex>\n)\n\nexport default TimePeriods\n","import React, { useCallback, useEffect, useState } from \"react\"\nimport { isValid, add, getUnixTime } from \"date-fns\"\nimport { Flex, Text } from \"@netdata/netdata-ui\"\nimport {\n  getCustomTimePeriod,\n  parseInputPeriod,\n  dateResolutions,\n  maxTimePeriodInUnix,\n} from \"./utils\"\nimport { StyledDropdown, DropdownIcon, CustomInput, StyledCustomTimePeriod } from \"./styled\"\n\nconst CustomTimePeriod = ({ handleTimePeriodChange, value, resolution, tagging }) => {\n  const getInputValue = () => (value <= 0 ? getCustomTimePeriod(-value, resolution) : 0)\n  const [inputValue, setInputValue] = useState(getInputValue)\n  const [isDropdownOpen, toggleDropdown] = useState(false)\n\n  // eslint-disable-next-line react-hooks/exhaustive-deps\n  useEffect(() => setInputValue(getInputValue()), [value])\n\n  const onChange = useCallback(e => setInputValue(e.target.value), [])\n\n  const onBlur = useCallback(\n    e => {\n      const currentValue = Number(e.currentTarget.value)\n      const isValidInput =\n        !Number.isNaN(currentValue) && Number.isInteger(currentValue) && currentValue > 0\n      const timePeriod = add(new Date(0), {\n        [resolution]: currentValue,\n      })\n      const isValidTimePeriod =\n        isValidInput && isValid(timePeriod) && getUnixTime(timePeriod) <= maxTimePeriodInUnix\n      if (isValidTimePeriod)\n        return handleTimePeriodChange(parseInputPeriod(currentValue, resolution), resolution)\n      return value <= 0 ? setInputValue(getCustomTimePeriod(-value, resolution)) : setInputValue(0)\n    },\n    [resolution, value, handleTimePeriodChange]\n  )\n\n  const onChangeResolution = useCallback(\n    newResolution => {\n      return () => {\n        handleTimePeriodChange(parseInputPeriod(inputValue, newResolution), newResolution)\n        toggleDropdown(false)\n      }\n    },\n    [inputValue, handleTimePeriodChange]\n  )\n\n  const renderTitle = () => (\n    <Flex alignItems=\"center\" flexWrap={false} width=\"100%\">\n      <Text padding={[0, 4, 0, 0]}>{resolution}</Text>\n      <DropdownIcon name=\"triangle_down\" />\n    </Flex>\n  )\n  return (\n    <Flex\n      justifyContent=\"start\"\n      alignItems=\"center\"\n      height={8}\n      data-ga={`date-picker::click-last-integer::${tagging}`}\n      data-testid=\"customTimePeriod\"\n    >\n      <Text>Last</Text>\n      <CustomInput\n        value={inputValue}\n        onChange={onChange}\n        onBlur={onBlur}\n        data-ga={`date-picker::click-last-integer::${tagging}::${inputValue}`}\n        data-testid=\"timePeriod-timeInput\"\n      />\n      <StyledDropdown\n        isOpen={isDropdownOpen}\n        onMenuToggle={toggleDropdown}\n        renderTitle={renderTitle}\n        renderOpener={() => null}\n      >\n        {() =>\n          dateResolutions.map(dateResolution => (\n            <StyledCustomTimePeriod\n              key={dateResolution}\n              onClick={onChangeResolution(dateResolution)}\n              data-ga={`date-picker::click-last-time-${dateResolution}::${tagging}`}\n              data-testid=\"timePeriod-option\"\n            >\n              {dateResolution}\n            </StyledCustomTimePeriod>\n          ))\n        }\n      </StyledDropdown>\n    </Flex>\n  )\n}\n\nexport default CustomTimePeriod\n","import React from \"react\"\nimport DatePickerLib from \"react-datepicker\"\nimport \"react-datepicker/dist/react-datepicker.css\"\n\nconst DatePicker = ({\n  selected,\n  selectsStart = false,\n  selectsEnd = false,\n  startDate,\n  endDate,\n  onChange,\n  minDate,\n  maxDate,\n  dateFormat = \"MM/dd/yyyy\",\n  open = false,\n  startOpen = false,\n  inline = false,\n  selectsRange = false,\n  monthsShown = 1,\n  showPopperArrow = true,\n  calendarContainer = null,\n}) => (\n  <DatePickerLib\n    selected={selected}\n    onChange={onChange}\n    selectsStart={selectsStart}\n    selectsEnd={selectsEnd}\n    startDate={startDate}\n    endDate={endDate}\n    minDate={minDate}\n    maxDate={maxDate}\n    dateFormat={dateFormat}\n    open={open}\n    startOpen={startOpen}\n    inline={inline}\n    selectsRange={selectsRange}\n    monthsShown={monthsShown}\n    showPopperArrow={showPopperArrow}\n    calendarContainer={calendarContainer}\n  />\n)\n\nexport default DatePicker\n","import { getColor, getRgbColor } from \"@netdata/netdata-ui\"\nimport styled from \"styled-components\"\n\nexport const StyledDateInput = styled.input`\n  width: 100%;\n  text-align: center;\n  border: 1px solid ${getColor(\"border\")};\n  color: inherit;\n  background: ${getColor(\"mainBackground\")};\n  box-sizing: border-box;\n  border-radius: 4px;\n  padding: 4px;\n  height: 32px;\n  margin-left: 20px;\n  margin-right: 20px;\n  outline: none;\n  &:focus {\n    border: 1px solid ${getColor(\"primary\")};\n  }\n`\nexport const StyledCalendar = styled.div`\n  background: ${getColor(\"mainBackground\")};\n  border: 0;\n  .react-datepicker {\n    &__navigation {\n      top: 8px;\n      &-icon::before {\n        border-color: ${getColor(\"text\")};\n      }\n    }\n    &__header {\n      background: ${getColor(\"mainBackground\")};\n      border: 0;\n      .react-datepicker__current-month {\n        color: ${getColor(\"main\")};\n        font-weight: normal;\n      }\n      .react-datepicker__day-name {\n        color: ${getColor(\"textLite\")};\n      }\n    }\n    &__day {\n      color: ${getColor(\"main\")};\n      &:hover {\n        background: ${getColor(\"elementBackground\")};\n      }\n      &--disabled {\n        color: ${getColor(\"textLite\")};\n        &:hover {\n          background: inherit;\n        }\n      }\n      &--keyboard-selected,\n      &--keyboard-selected:hover {\n        color: ${getColor(\"main\")};\n        background: inherit;\n        border-radius: inherit;\n      }\n      &--selected,\n      &--selected:hover {\n        color: ${getColor(\"bright\")};\n        background: ${getColor(\"primary\")};\n        border-radius: 8px;\n      }\n      &--in-selecting-range,\n      &--in-range {\n        color: ${getColor(\"primary\")};\n        background: ${getColor(\"elementBackground\")};\n        border-radius: 0;\n      }\n      &--selecting-range-start,\n      &--range-start {\n        color: ${getColor(\"bright\")};\n        background: ${getColor(\"primary\")};\n        border-top-left-radius: 8px;\n        border-bottom-left-radius: 8px;\n        &:hover {\n          color: ${getColor(\"bright\")};\n          background: ${getRgbColor([\"green\", \"netdata\"], 0.8)};\n          border-radius: 0;\n          border-top-left-radius: 8px;\n          border-bottom-left-radius: 8px;\n        }\n      }\n      &--selecting-range-end,\n      &--range-end {\n        color: ${getColor(\"bright\")};\n        background: ${getColor(\"primary\")};\n        border-top-right-radius: 8px;\n        border-bottom-right-radius: 8px;\n        &:hover {\n          color: ${getColor(\"bright\")};\n          background: ${getRgbColor([\"green\", \"netdata\"], 0.8)};\n          border-top-right-radius: 8px;\n          border-bottom-right-radius: 8px;\n        }\n      }\n    }\n  }\n`\n","import React, { useState, useEffect, useCallback } from \"react\"\nimport { format, isValid, getTime } from \"date-fns\"\nimport { getDateWithOffset } from \"./utils\"\nimport { StyledDateInput } from \"../datePicker/styled\"\nimport { useDateTime } from \"@/src/utils/date-time\"\n\nconst DatePickerInput = ({\n  name = \"\",\n  value = \"\",\n  onDatesChange,\n  onFocus,\n  placeholderText = \"\",\n}) => {\n  const { utcOffset } = useDateTime()\n  const [inputValue, setInputValue] = useState(\"\")\n  const onChange = useCallback(e => {\n    const date = e.target.value\n    setInputValue(date)\n  }, [])\n  const setFormattedValue = useCallback(value => {\n    if (isValid(value)) {\n      const formattedDate = format(value, \"MMMM d yyyy, H:mm\")\n      setInputValue(formattedDate)\n    }\n  }, [])\n  const onBlur = useCallback(\n    e => {\n      const parsedDate = getDateWithOffset(e.target.value, utcOffset)\n      const isValidDate = isValid(parsedDate) && getTime(parsedDate) > 0\n      if (isValidDate) {\n        const timestamp = getTime(parsedDate)\n        onDatesChange(timestamp, () => setFormattedValue(value))\n      } else setFormattedValue(value)\n    },\n    [value, utcOffset, onDatesChange, setFormattedValue]\n  )\n\n  useEffect(() => setFormattedValue(value), [value, setFormattedValue])\n\n  return (\n    <StyledDateInput\n      type=\"text\"\n      name={name}\n      value={value ? inputValue : placeholderText}\n      onChange={onChange}\n      onBlur={onBlur}\n      onFocus={onFocus}\n      placeholder={placeholderText}\n      data-testid=\"datePicker-input\"\n    />\n  )\n}\n\nexport default DatePickerInput\n","import { useDateTime } from \"@/src/utils/date-time\"\nimport { useCallback } from \"react\"\n\nconst useLocaleDate = () => {\n  const { localeTimeString, localeDateString } = useDateTime()\n  return useCallback(\n    date => {\n      return `${localeDateString(date, { locale: \"en-us\", long: false })} ${localeTimeString(date, {\n        secs: false,\n      })}`\n    },\n    [localeTimeString, localeDateString]\n  )\n}\n\nexport default useLocaleDate\n","import { useMemo } from \"react\"\nimport { toDate } from \"date-fns\"\nimport useLocaleDate from \"./useLocaleDate\"\n\nexport const convertTimestampToDate = (timestamp, getLocaleDate) => {\n  if (timestamp > 0) {\n    return toDate(new Date(getLocaleDate(timestamp)))\n  } else if (timestamp || timestamp === 0)\n    return toDate(new Date(getLocaleDate(new Date().valueOf() + timestamp * 1000)))\n  return null\n}\n\nconst useConvertedDates = (startDate, endDate) => {\n  const getLocaleDate = useLocaleDate()\n  return useMemo(\n    () => [\n      convertTimestampToDate(startDate, getLocaleDate),\n      convertTimestampToDate(endDate, getLocaleDate),\n    ],\n    [startDate, endDate, getLocaleDate]\n  )\n}\n\nexport default useConvertedDates\n","import { Flex } from \"@netdata/netdata-ui\"\nimport React, { useCallback } from \"react\"\nimport { getTime, isBefore, format } from \"date-fns\"\nimport { useDateTime } from \"@/src/utils/date-time\"\nimport DatePicker from \"../datePicker/datePickerLib\"\nimport DatePickerInput from \"./datePickerInput\"\nimport useConvertedDates, { convertTimestampToDate } from \"./useConvertedDate\"\nimport useLocaleDate from \"./useLocaleDate\"\nimport { getDateWithOffset } from \"./utils\"\nimport { StyledCalendar } from \"../datePicker/styled\"\n\nconst DatePickerWrapper = ({\n  startDate,\n  setStartDate,\n  endDate,\n  setEndDate,\n  onDatesChange,\n  onInputFocus,\n}) => {\n  const getLocaleDate = useLocaleDate()\n  const [convertedStartDate, convertedEndDate] = useConvertedDates(startDate, endDate)\n  const { utcOffset } = useDateTime()\n  const setValidStartDate = useCallback(\n    (startDate, setPreviousValue) =>\n      isBefore(convertTimestampToDate(startDate, getLocaleDate), convertedEndDate)\n        ? setStartDate(startDate)\n        : setPreviousValue(),\n    [convertedEndDate, getLocaleDate, setStartDate]\n  )\n\n  const setValidEndDate = useCallback(\n    (endDate, setPreviousValue) =>\n      isBefore(convertedStartDate, convertTimestampToDate(endDate, getLocaleDate))\n        ? setEndDate(endDate)\n        : setPreviousValue(),\n    [convertedStartDate, getLocaleDate, setEndDate]\n  )\n\n  const onChange = useCallback(\n    dates => {\n      const [startDate, endDate] = dates\n\n      const startDateWithOffset = startDate\n        ? getDateWithOffset(format(startDate, \"MMMM d yyyy, H:mm\"), utcOffset)\n        : startDate\n      const endDateWithOffset = endDate\n        ? getDateWithOffset(format(endDate, \"MMMM d yyyy, H:mm\"), utcOffset)\n        : endDate\n\n      const startDateTimestamp = getTime(startDateWithOffset) || null\n      const endDateTimestamp = getTime(endDateWithOffset) || null\n\n      onDatesChange(startDateTimestamp, endDateTimestamp)\n    },\n    [utcOffset, onDatesChange]\n  )\n\n  return (\n    <Flex\n      column\n      justifyContent=\"center\"\n      alignItems=\"center\"\n      flex={{ grow: 1 }}\n      gap={3}\n      margin={[0, 0, 0, 7]}\n      data-testid=\"datePicker-wrapper\"\n    >\n      <DatePicker\n        selected={convertedStartDate}\n        onChange={onChange}\n        startDate={convertedStartDate}\n        endDate={convertedEndDate}\n        maxDate={new Date()}\n        minDate={new Date(\"1/1/2018\")}\n        inline\n        selectsRange\n        monthsShown={2}\n        dateFormat=\"MMMM d yyyy, H:mm\"\n        showPopperArrow={false}\n        calendarContainer={StyledCalendar}\n      />\n      <Flex justifyContent=\"around\" alignItems=\"center\" width=\"100%\">\n        <DatePickerInput\n          name=\"startDate\"\n          value={convertedStartDate}\n          onDatesChange={setValidStartDate}\n          onFocus={onInputFocus}\n          placeholderText=\"Select a start date\"\n        />\n        <DatePickerInput\n          name=\"endDate\"\n          value={convertedEndDate}\n          onDatesChange={setValidEndDate}\n          onFocus={onInputFocus}\n          placeholderText=\"Select an end date\"\n        />\n      </Flex>\n    </Flex>\n  )\n}\n\nexport default DatePickerWrapper\n","import React, { useMemo } from \"react\"\nimport { Flex, Icon, TextSmall } from \"@netdata/netdata-ui\"\nimport { formatDates, getTimePeriod } from \"./utils\"\nimport useConvertedDates from \"./useConvertedDate\"\n\nconst PeriodIndication = ({ startDate, endDate }) => {\n  const [convertedStart, convertedEnd] = useConvertedDates(startDate, endDate)\n\n  const { formattedStartDate, formattedEndDate } = useMemo(\n    () => formatDates(convertedStart, convertedEnd),\n    [convertedStart, convertedEnd]\n  )\n  const timePeriod = useMemo(\n    () => getTimePeriod(convertedStart, convertedEnd),\n    [convertedStart, convertedEnd]\n  )\n\n  return (\n    <Flex alignItems=\"center\" justifyContent=\"between\" gap={2}>\n      <Flex alignItems=\"center\" justifyContent=\"center\" gap={1.5}>\n        <TextSmall strong whiteSpace=\"nowrap\">\n          From\n        </TextSmall>\n        <TextSmall whiteSpace=\"nowrap\" data-testid=\"periodIndication-from\">\n          {formattedStartDate}\n        </TextSmall>\n      </Flex>\n      <Icon name=\"arrow_left\" size=\"small\" color=\"textLite\" rotate={2} />\n      <Flex alignItems=\"center\" justifyContent=\"center\" gap={1.5}>\n        <TextSmall strong whiteSpace=\"nowrap\">\n          To\n        </TextSmall>\n        <TextSmall whiteSpace=\"nowrap\" data-testid=\"periodIndication-to\">\n          {formattedEndDate}\n        </TextSmall>\n      </Flex>\n      <Flex alignItems=\"center\" justifyContent=\"center\" gap={2}>\n        <TextSmall whiteSpace=\"nowrap\">/</TextSmall>\n        <TextSmall color=\"textLite\" whiteSpace=\"nowrap\" data-testid=\"periodIndication-period\">\n          {timePeriod}\n        </TextSmall>\n      </Flex>\n    </Flex>\n  )\n}\n\nexport default PeriodIndication\n","import moment from \"moment\"\n\nexport const SECONDS = 1000\nexport const MINUTE = SECONDS * 60\nexport const HOUR = MINUTE * 60\nexport const DAY = HOUR * 24\nexport const MONTH = DAY * 30\n\nconst resolutionMap = [\n  { value: DAY, unit: \"d\" },\n  { value: HOUR, unit: \"h\" },\n  { value: MINUTE, unit: \"min\" },\n  { value: MINUTE, unit: \"min\" },\n  { value: SECONDS, unit: \"s\" },\n]\n\nexport const getStartDate = start =>\n  start < 0 ? moment(new Date()).add(start, \"seconds\") : moment(start)\nexport const getEndDate = end => (!end ? moment(new Date()) : moment(end))\nexport const getIsSameDate = (startDate, endDate) => startDate.isSame(endDate, \"day\")\nexport const getDuration = (startDate, endDate) => moment.duration(startDate.diff(endDate))\n\nconst getResolution = (value, resolution) => (value > 1 ? `${Math.floor(value)}${resolution}` : \"\")\n\nexport const getGranularDuration = duration => {\n  let seconds = Math.abs(duration)\n  const showSeconds = seconds < MINUTE\n  return resolutionMap.reduce((acc, { value, unit }) => {\n    if (value === SECONDS && !showSeconds) return acc\n    acc = acc + getResolution(seconds / value, unit)\n    seconds = seconds % value\n    return acc\n  }, \"\")\n}","import styled from \"styled-components\"\nimport { Flex, getColor } from \"@netdata/netdata-ui\"\n\nconst Container = styled(Flex)`\n  cursor: pointer;\n\n  &:hover * {\n    color: ${getColor(\"textLite\")};\n    fill: ${getColor(\"textLite\")};\n  }\n`\n\nexport default Container\n","import React from \"react\"\nimport { Flex, TextSmall, Icon } from \"@netdata/netdata-ui\"\nimport { useDateTime } from \"utils/date-time\"\n\nconst DateBox = ({ isPlaying, startDate, endDate, isSameDate }) => {\n  const { localeTimeString, localeDateString } = useDateTime()\n  return (\n    <Flex gap={2}>\n      <TextSmall color=\"text\" whiteSpace=\"nowrap\">\n        {localeDateString(startDate, { long: false })} •{\" \"}\n        <TextSmall color={isPlaying ? \"accent\" : \"textFocus\"} whiteSpace=\"nowrap\">\n          {localeTimeString(startDate, { secs: false })}\n        </TextSmall>\n      </TextSmall>\n      <Icon name=\"arrow_left\" color={isPlaying ? \"accent\" : \"textFocus\"} size=\"small\" rotate={2} />\n      <TextSmall color=\"text\" whiteSpace=\"nowrap\">\n        {!isSameDate && `${localeDateString(endDate, { long: false })} • `}\n        <TextSmall color={isPlaying ? \"accent\" : \"textFocus\"} whiteSpace=\"nowrap\">\n          {localeTimeString(endDate, { secs: false })}\n        </TextSmall>\n      </TextSmall>\n    </Flex>\n  )\n}\n\nexport default DateBox\n","import React from \"react\"\nimport { Flex, TextSmall } from \"@netdata/netdata-ui\"\n\nconst DurationBox = ({ isPlaying, duration }) => {\n  return (\n    <Flex gap={1}>\n      <Flex width=\"24px\" justifyContent=\"center\">\n        {isPlaying && (\n          <TextSmall color=\"text\" whiteSpace=\"nowrap\">\n            • last\n          </TextSmall>\n        )}\n      </Flex>\n      <TextSmall color=\"text\" whiteSpace=\"nowrap\">\n        {duration}\n      </TextSmall>\n    </Flex>\n  )\n}\n\nexport default DurationBox\n","import React, { useState, useMemo, useEffect, forwardRef } from \"react\"\nimport Tooltip from \"@/src/components/tooltips\"\nimport { useSelector as useDashboardSelector } from \"store/redux-separate-context\"\nimport { selectGlobalPanAndZoom } from \"domains/global/selectors\"\nimport {\n  getStartDate,\n  getEndDate,\n  getIsSameDate,\n  getDuration,\n  MINUTE,\n  getGranularDuration,\n} from \"./utils\"\nimport Container from \"./container\"\nimport DateBox from \"./dateBox\"\nimport DurationBox from \"./durationBox\"\n\nconst PickerAccessorElement = forwardRef(\n  (\n    { onClick, start = 15 * MINUTE, end, isPlaying, isPickerOpen, setRangeValues, tagging },\n    ref\n  ) => {\n    const [timeframe, setTimeframe] = useState()\n    const startDate = getStartDate(start)\n    const endDate = getEndDate(end)\n    const globalPanAndZoom = useDashboardSelector(selectGlobalPanAndZoom)\n    useEffect(() => {\n      const after = getDuration(startDate, endDate).as(\"seconds\")\n      if (!isPlaying && timeframe !== after) setTimeframe(Math.round(after))\n      if (isPlaying && timeframe && !!globalPanAndZoom) {\n        setRangeValues({ start: Math.round(timeframe) })\n        setTimeframe(null)\n      }\n      // eslint-disable-next-line react-hooks/exhaustive-deps\n    }, [startDate, endDate, timeframe, isPlaying])\n\n    const isSameDate = useMemo(() => getIsSameDate(startDate, endDate), [startDate, endDate])\n    const duration = useMemo(\n      () => getGranularDuration(getDuration(startDate, endDate).as(\"milliseconds\")),\n      // eslint-disable-next-line react-hooks/exhaustive-deps\n      [isPlaying, startDate, endDate]\n    )\n\n    return (\n      <Tooltip\n        content={isPickerOpen ? () => {} : \"Select a predefined or a custom timeframe\"}\n        align=\"bottom\"\n        plain\n      >\n        <Container\n          alignItems=\"center\"\n          justifyContent=\"center\"\n          gap={1}\n          height=\"100%\"\n          width={{ min: \"380px\" }}\n          onMouseDown={onClick}\n          padding={[0, 1]}\n          ref={ref}\n          data-ga={`date-picker::click-time::${tagging}`}\n          data-testid=\"datePicker-accessorElement\"\n        >\n          <DateBox\n            isPlaying={isPlaying}\n            endDate={endDate}\n            startDate={startDate}\n            isSameDate={isSameDate}\n          />\n          <DurationBox isPlaying={isPlaying} duration={duration} />\n        </Container>\n      </Tooltip>\n    )\n  }\n)\n\nexport default PickerAccessorElement\n","import React, { useState, useEffect, useMemo, useRef, useCallback } from \"react\"\nimport { Button, Flex } from \"@netdata/netdata-ui\"\nimport useToggle from \"hooks/useToggle\"\nimport useLocalStorage from \"hooks/useLocalStorage\"\nimport TimePeriods from \"./timePeriods\"\nimport CustomTimePeriod from \"./customTimePeriod\"\nimport DatePickerWrapper from \"./datePickerWrapper\"\nimport { getFocusTagging } from \"./utils\"\nimport PeriodIndication from \"./periodIndication\"\nimport AccessorElement from \"./accessorElement\"\nimport { PickerBox, StyledDrop, StyledHR } from \"./styled\"\n\nexport const reportEvent = (\n  eventCategory,\n  eventAction,\n  eventLabel,\n  eventValue,\n  event = \"gaCustomEvent\"\n) => {\n  if (window.dataLayer) {\n    const eventData = { event, eventCategory, eventAction, eventLabel, eventValue }\n    window.dataLayer.push(eventData)\n  }\n}\n\nconst DatePickerDrop = ({\n  onChange,\n  values: { start: initialStartDate, end: initialEndDate } = {},\n  defaultValue = -60 * 15,\n  tagging = \"\",\n  isPlaying,\n}) => {\n  const [startDate, setStartDate] = useState(initialStartDate)\n  const [endDate, setEndDate] = useState(initialStartDate)\n  const [resolution, setResolution] = useLocalStorage(\"resolution\", \"minutes\")\n  const [focusedInput, setFocusedInput] = useState(\"startDate\")\n  const [isOpen, toggle, , close] = useToggle()\n  const ref = useRef()\n\n  const setDates = useCallback(({ startDate, endDate }) => {\n    setStartDate(startDate)\n    setEndDate(endDate)\n  }, [])\n\n  useEffect(() => {\n    setDates({\n      startDate: initialStartDate,\n      endDate: initialEndDate,\n    })\n  }, [initialStartDate, initialEndDate, setDates])\n\n  // eslint-disable-next-line react-hooks/exhaustive-deps\n  const clearChanges = useCallback(() => setDates({ startDate: defaultValue, endDate: 0 }), [])\n\n  const onInputFocus = useCallback(e => {\n    if (!e.target.name) return\n    setFocusedInput(e.target.name)\n  }, [])\n\n  const togglePicker = useCallback(\n    e => {\n      e.stopPropagation()\n      toggle()\n    },\n    [toggle]\n  )\n\n  const applyChanges = () => {\n    onChange({\n      start: startDate,\n      end: endDate,\n    })\n    close()\n  }\n\n  const focusTagging = useMemo(() => getFocusTagging(focusedInput), [focusedInput])\n\n  const isValidTimePeriod = startDate !== null && endDate !== null && startDate !== endDate\n  const isApplyDisabled = startDate === initialStartDate && endDate === initialEndDate\n  // eslint-disable-next-line react-hooks/exhaustive-deps\n  const consistentDefaultValue = useMemo(() => defaultValue, [])\n  const isClearDisabled = startDate === consistentDefaultValue\n\n  const handleTimePeriodChange = useCallback(\n    (time, resolution) => {\n      setResolution(resolution)\n      setDates({\n        startDate: time,\n        endDate: 0,\n      })\n    },\n    [setDates, setResolution]\n  )\n  const onDatepickerChange = (startDate, endDate) => {\n    setDates({ startDate, endDate })\n    const date = focusTagging === \"finish\" ? endDate || startDate : startDate || endDate\n    reportEvent(\"date-picker\", \"click-date-picker\", tagging, String(date))\n  }\n\n  const pickerDrop =\n    ref.current && isOpen ? (\n      <StyledDrop\n        target={ref.current}\n        canHideTarget={false}\n        align={{ top: \"bottom\", left: \"left\" }}\n        onEsc={close}\n        onClickOutside={close}\n      >\n        <PickerBox data-testid=\"datePicker\">\n          <Flex justifyContent=\"between\" alignItems=\"center\" width=\"100%\" padding={[6, 6, 0, 6]}>\n            <Flex column gap={3} margin={[0, 7, 0, 0]}>\n              <TimePeriods\n                handleTimePeriodChange={handleTimePeriodChange}\n                selectedDate={startDate}\n                tagging={tagging}\n              />\n              <CustomTimePeriod\n                handleTimePeriodChange={handleTimePeriodChange}\n                value={startDate}\n                resolution={resolution}\n                tagging={tagging}\n              />\n            </Flex>\n            <StyledHR />\n            <DatePickerWrapper\n              startDate={startDate}\n              endDate={endDate}\n              setStartDate={setStartDate}\n              setEndDate={setEndDate}\n              onDatesChange={onDatepickerChange}\n              onInputFocus={onInputFocus}\n            />\n          </Flex>\n          <Flex\n            alignItems=\"center\"\n            justifyContent={isValidTimePeriod ? \"between\" : \"end\"}\n            width=\"100%\"\n            padding={[5, 6]}\n            gap={2}\n          >\n            {isValidTimePeriod && <PeriodIndication startDate={startDate} endDate={endDate} />}\n            <Flex alignItems=\"center\" justifyContent=\"center\" gap={4}>\n              <Button\n                label=\"Clear\"\n                flavour=\"hollow\"\n                onClick={clearChanges}\n                disabled={isClearDisabled}\n                data-ga={`date-picker::click-clear::${tagging}-${focusTagging}`}\n                data-testid=\"datePicker-clear\"\n              />\n              <Button\n                label=\"Apply\"\n                onClick={applyChanges}\n                disabled={!isValidTimePeriod || isApplyDisabled}\n                data-ga={`date-picker::click-apply::${tagging}-${focusTagging}`}\n                data-testid=\"datePicker-apply\"\n              />\n            </Flex>\n          </Flex>\n        </PickerBox>\n      </StyledDrop>\n    ) : null\n\n  return (\n    <>\n      <AccessorElement\n        onClick={togglePicker}\n        tagging={tagging}\n        isPickerOpen={isOpen}\n        isPlaying={isPlaying}\n        setRangeValues={onChange}\n        start={initialStartDate}\n        end={initialEndDate}\n        ref={ref}\n      />\n      {pickerDrop}\n    </>\n  )\n}\n\nexport default DatePickerDrop\n","import React, { memo, useEffect, useMemo } from \"react\"\nimport {\n  useDispatch as useDashboardDispatch,\n  useSelector as useDashboardSelector,\n} from \"store/redux-separate-context\"\nimport {\n  resetGlobalPanAndZoomAction,\n  setGlobalPanAndZoomAction,\n  setDefaultAfterAction,\n} from \"domains/global/actions\"\nimport { selectDefaultAfter, selectGlobalPanAndZoom } from \"domains/global/selectors\"\nimport { setHashParams } from \"utils/hash-utils\"\nimport DatePickerDrop from \"./datePickerDrop\"\n\nconst ReduxDatePickerContainer = memo(({ tagging, isPlaying }) => {\n  const dashboardDispatch = useDashboardDispatch()\n\n  const globalPanAndZoom = useDashboardSelector(selectGlobalPanAndZoom)\n  const isGlobalPanAndZoom = Boolean(globalPanAndZoom)\n\n  const defaultAfter = useDashboardSelector(selectDefaultAfter)\n  const pickedValues = useMemo(\n    () =>\n      isGlobalPanAndZoom\n        ? { start: globalPanAndZoom.after, end: globalPanAndZoom.before }\n        : {\n            start: defaultAfter,\n            end: 0,\n          },\n    [isGlobalPanAndZoom, globalPanAndZoom, defaultAfter]\n  )\n\n  function handlePickedValuesChange(params) {\n    const { start, end } = params\n    if (start < 0) {\n      // live mode\n      dashboardDispatch(\n        // changes the default value, so it becomes inconsistent\n        setDefaultAfterAction({\n          after: start,\n        })\n      )\n      if (isGlobalPanAndZoom) {\n        dashboardDispatch(resetGlobalPanAndZoomAction())\n      }\n    } else {\n      // global-pan-and-zoom mode\n      dashboardDispatch(\n        setGlobalPanAndZoomAction({\n          after: start,\n          before: end,\n        })\n      )\n    }\n  }\n\n  useEffect(() => {\n    const { start, end } = pickedValues\n    const after = start.toString()\n    const before = end.toString()\n    if (window.urlOptions.after !== after || window.urlOptions.before !== before) {\n      window.urlOptions.netdataPanAndZoomCallback(true, after, before)\n    }\n    setHashParams({ after, before })\n  }, [pickedValues])\n  return (\n    <DatePickerDrop\n      values={pickedValues}\n      defaultValue={defaultAfter}\n      onChange={handlePickedValuesChange}\n      tagging={tagging}\n      isPlaying={isPlaying}\n    />\n  )\n})\n\nexport default ReduxDatePickerContainer\n","import styled from \"styled-components\"\nimport { Flex, getRgbColor } from \"@netdata/netdata-ui\"\n\nconst getBackground = ({ theme, isPlaying }) => {\n  const { name } = theme\n\n  const background =\n    name === \"Dark\"\n      ? getRgbColor(isPlaying ? [\"green\", \"netdata\"] : [\"neutral\", \"tuna\"], isPlaying ? 0.3 : 1)\n      : getRgbColor(isPlaying ? [\"green\", \"frostee\"] : [\"neutral\", \"blackhaze\"])\n\n  return background({ theme })\n}\n\nconst Container = styled(Flex)`\n  background: ${getBackground};\n`\n\nexport default Container\n","import styled from \"styled-components\"\nimport { Pill, getColor } from \"@netdata/netdata-ui\"\n\nconst getHoverColor = ({ isPlaying }) =>\n  getColor(isPlaying ? [\"green\", \"chateau\"] : [\"neutral\", \"iron\"])\n\nconst StyledPill = styled(Pill).attrs(({ isPlaying }) => ({\n  flavour: isPlaying ? \"success\" : \"neutral\",\n}))`\n  &:hover {\n    background: ${getHoverColor};\n    border-color: ${getHoverColor};\n  }\n`\n\nexport default StyledPill\n","import React, { useMemo } from \"react\"\nimport { useDispatch } from \"store/redux-separate-context\"\nimport { resetGlobalPauseAction, setGlobalPauseAction } from \"domains/global/actions\"\nimport Tooltip from \"@/src/components/tooltips\"\nimport StyledPill from \"./styledPill\"\n\nconst getIcon = (isPlaying, isForcePlaying) => {\n  if (!isPlaying) return \"pauseSolid\"\n  return isForcePlaying ? \"forcePlay\" : \"playSolid\"\n}\n\nconst PlayPausePill = ({ isPlaying, isForcePlaying }) => {\n  const dispatch = useDispatch()\n\n  const onPlay = () => dispatch(resetGlobalPauseAction({ forcePlay: false }))\n  const onPause = () => dispatch(setGlobalPauseAction())\n  const icon = useMemo(() => getIcon(isPlaying, isForcePlaying), [isPlaying, isForcePlaying])\n\n  return (\n    <Tooltip content={isPlaying ? \"Click to pause\" : \"Click to play\"} align=\"bottom\" plain>\n      <StyledPill icon={icon} onClick={isPlaying ? onPause : onPlay} isPlaying={isPlaying}>\n        {isPlaying ? \"Playing\" : \"Paused\"}\n      </StyledPill>\n    </Tooltip>\n  )\n}\n\nexport default PlayPausePill\n","import React, { useCallback, forwardRef } from \"react\"\nimport styled from \"styled-components\"\nimport { getColor, Flex, Icon, Text } from \"@netdata/netdata-ui\"\n\nexport const PanelRowContainer = styled(Flex)`\n  cursor: pointer;\n\n  &:hover {\n    background: ${getColor(\"selected\")};\n  }\n\n  ${props => props.selected && `background: ${getColor(\"selected\")(props)};`}\n`\n\nconst MenuItem = forwardRef(\n  (\n    {\n      disabled,\n      children,\n      Wrapper = Text,\n      onClick,\n      testid,\n      icon,\n      padding = [2, 3],\n      margin = [0],\n      round = 0,\n      actions,\n      selected,\n      width = \"100%\",\n    },\n    ref\n  ) => {\n    const click = useCallback(() => {\n      if (disabled) return\n      if (onClick) onClick()\n    }, [onClick, disabled])\n\n    return (\n      <PanelRowContainer\n        ref={ref}\n        flexWrap={false}\n        justifyContent=\"between\"\n        alignItems=\"center\"\n        padding={padding}\n        margin={margin}\n        round={round}\n        onClick={click}\n        data-testid={testid}\n        width={width}\n        selected={selected}\n        disabled={disabled}\n      >\n        <Flex alignItems=\"center\" gap={3} flex basis=\"\">\n          {typeof icon === \"string\" ? (\n            <Icon name={icon} disabled={disabled} color=\"text\" height=\"16px\" width=\"16px\" />\n          ) : (\n            icon\n          )}\n          <Wrapper opacity={disabled ? \"medium\" : undefined} width=\"150px\">\n            {children}\n          </Wrapper>\n        </Flex>\n        {actions}\n      </PanelRowContainer>\n    )\n  }\n)\n\nexport default MenuItem\n","import React from \"react\"\nimport styled from \"styled-components\"\nimport { Flex, H4, Collapsible } from \"@netdata/netdata-ui\"\n\nexport const DefaultListHeader = styled(H4).attrs({ padding: [0], margin: [0] })`\n  cursor: pointer;\n`\n\nconst SectionHandle = ({ toggleOpen, label, testid, Header = DefaultListHeader }) => (\n  <Header data-testid={testid} onClick={toggleOpen}>\n    {label}\n  </Header>\n)\n\nconst ItemsList = ({ isOpen = false, toggleOpen, label, children, testid, Header }) => (\n  <Flex column>\n    <SectionHandle Header={Header} toggleOpen={toggleOpen} label={label} testid={testid} />\n    <Collapsible open={isOpen}>{children}</Collapsible>\n  </Flex>\n)\n\nexport default ItemsList\n","import React from \"react\"\nimport { Flex, TextSmall } from \"@netdata/netdata-ui\"\n\nconst PlayOptionsTooltip = () => (\n  <Flex\n    padding={[1, 2]}\n    margin={[1]}\n    background={[\"neutral\", \"black\"]}\n    round={1}\n    justifyContent=\"center\"\n    width={{ max: \"320px\" }}\n  >\n    <TextSmall color=\"bright\">\n      Play to refresh and have live content, pause to see historical, or force play to keep\n      refreshing even when the tab loses focus at the expense of some system performance.\n    </TextSmall>\n  </Flex>\n)\n\nexport default PlayOptionsTooltip\n","import styled from \"styled-components\"\nimport { Flex } from \"@netdata/netdata-ui\"\n\nexport const Divider = styled(Flex).attrs({\n  bacgkround: \"disabled\",\n  height: \"1px\",\n  margin: [2, 6],\n})``\n","import React, { memo, Fragment } from \"react\"\nimport styled from \"styled-components\"\nimport { useToggle } from \"react-use\"\nimport { Flex, Icon, Drop } from \"@netdata/netdata-ui\"\nimport { MenuItem } from \"@/src/components/menus\"\nimport { useDispatch } from \"store/redux-separate-context\"\nimport { resetGlobalPauseAction, setGlobalPauseAction } from \"domains/global/actions\"\nimport Tooltip from \"@/src/components/tooltips\"\nimport PlayOptionsTooltip from \"./playOptionsTooltip\"\n\nconst MenuButton = styled(Flex).attrs({ padding: [1], role: \"button\" })`\n  cursor: pointer;\n`\n\nconst Dropdown = styled(Flex).attrs({\n  column: true,\n  padding: [2],\n  background: \"dropdown\",\n  round: 1,\n  overflow: { vertical: \"auto\" },\n  margin: [2, 0, 0],\n  width: 40,\n})`\n  box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);\n`\n\nconst PlayOptions = ({ target }) => {\n  const dispatch = useDispatch()\n  const [isOpen, toggle] = useToggle()\n\n  const close = () => toggle(false)\n\n  const onPlay = () => {\n    dispatch(resetGlobalPauseAction({ forcePlay: false }))\n    close()\n  }\n\n  const onPause = () => {\n    dispatch(setGlobalPauseAction())\n    close()\n  }\n\n  const onForcePlay = () => {\n    dispatch(resetGlobalPauseAction({ forcePlay: true }))\n    close()\n  }\n\n  return (\n    <Fragment>\n      {!isOpen ? (\n        <Tooltip content={<PlayOptionsTooltip />} align=\"bottom\" plain>\n          <MenuButton onClick={toggle} width=\"auto\">\n            <Icon name=\"chevron_down\" color=\"text\" width=\"12px\" height=\"12px\" />\n          </MenuButton>\n        </Tooltip>\n      ) : (\n        <MenuButton onClick={toggle} width=\"auto\">\n          <Icon name=\"chevron_down\" color=\"text\" width=\"12px\" height=\"12px\" />\n        </MenuButton>\n      )}\n      {target.current && isOpen && (\n        <Drop\n          target={target.current}\n          align={{ top: \"bottom\", left: \"left\" }}\n          onEsc={close}\n          onClickOutside={close}\n          animation\n        >\n          <Dropdown>\n            <MenuItem round={1} icon=\"playOutline\" onClick={onPlay}>\n              Play\n            </MenuItem>\n            <MenuItem round={1} icon=\"pauseOutline\" onClick={onPause}>\n              Pause\n            </MenuItem>\n            <MenuItem round={1} icon=\"forcePlayOutline\" onClick={onForcePlay}>\n              Force Play\n            </MenuItem>\n          </Dropdown>\n        </Drop>\n      )}\n    </Fragment>\n  )\n}\n\nconst MemoizedPlayOptions = memo(PlayOptions)\n\nexport default MemoizedPlayOptions\n","import React, { useMemo, useRef } from \"react\"\nimport { useSelector } from \"store/redux-separate-context\"\nimport {\n  selectHasWindowFocus,\n  selectStopUpdatesWhenFocusIsLost,\n  selectGlobalPanAndZoom,\n  selectGlobalPause,\n  selectGlobalSelection,\n} from \"domains/global/selectors\"\nimport { ReduxDatePickerContainer } from \"components/date-picker\"\nimport Item from \"../item\"\nimport Container from \"./container\"\nimport PlayPausePill from \"./playPausePill\"\nimport PlayOptions from \"./playOptions\"\n\nconst tagging = \"global-view\"\n\nconst GlobalControls = () => {\n  const ref = useRef()\n  const hasWindowFocus = useSelector(selectHasWindowFocus)\n  const stopUpdatesWhenFocusIsLost = useSelector(selectStopUpdatesWhenFocusIsLost)\n  const globalPanAndZoom = useSelector(selectGlobalPanAndZoom)\n  const hoveredX = useSelector(selectGlobalSelection)\n  const globalPause = useSelector(selectGlobalPause)\n\n  const isPlaying = useMemo(\n    () =>\n      Boolean(\n        (hasWindowFocus || !stopUpdatesWhenFocusIsLost) &&\n          !globalPanAndZoom &&\n          !hoveredX &&\n          !globalPause\n      ),\n    [hasWindowFocus, stopUpdatesWhenFocusIsLost, globalPanAndZoom, hoveredX, globalPause]\n  )\n\n  return (\n    <Item hasBorder>\n      <Container\n        isPlaying={isPlaying}\n        padding={[2, 2]}\n        round\n        height=\"100%\"\n        alignItems=\"center\"\n        gap={1}\n        ref={ref}\n      >\n        <PlayPausePill isPlaying={isPlaying} isForcePlaying={!stopUpdatesWhenFocusIsLost} />\n        <PlayOptions target={ref} />\n        <ReduxDatePickerContainer isPlaying={isPlaying} tagging={tagging} />\n      </Container>\n    </Item>\n  )\n}\n\nexport default GlobalControls\n","import styled from \"styled-components\"\nimport { Flex } from \"@netdata/netdata-ui\"\n\nconst hollowColors = {\n  warning: \"#FFF8E1\",\n  error: \"#FFEBEF\",\n}\n\nconst StyledPill = styled(Flex).attrs(({ round = 999, hollow, background }) => ({\n  padding: [0.5, 2],\n  round,\n  border: hollow ? { side: \"all\", color: background, size: \"1px\" } : false,\n}))`\n  background: ${({ background, hollow }) => (hollow ? hollowColors[background] : background)};\n  cursor: pointer;\n`\n\nexport default StyledPill\n","import React, { forwardRef } from \"react\"\nimport { TextMicro } from \"@netdata/netdata-ui\"\nimport StyledPill from \"./styled\"\n\nconst Pill = forwardRef(({ children, background, color, hollow, ...rest }, ref) => (\n  <StyledPill background={background} hollow={hollow} ref={ref} {...rest}>\n    <TextMicro color={hollow ? background : color} strong>\n      {children}\n    </TextMicro>\n  </StyledPill>\n))\n\nexport default Pill\n","import React, { useMemo } from \"react\"\nimport { useSelector } from \"store/redux-separate-context\"\nimport { selectActiveAlarms } from \"domains/global/selectors\"\nimport Item from \"./item\"\nimport Pill from \"./pill\"\nimport Tooltip from \"@/src/components/tooltips\"\n\nconst pillProps = {\n  \"data-toggle\": \"modal\",\n  \"data-target\": \"#alarmsModal\",\n}\n\nconst Alarms = () => {\n  const activeAlarms = useSelector(selectActiveAlarms)\n\n  const alarms = useMemo(() => (activeAlarms ? Object.values(activeAlarms.alarms) : []), [\n    activeAlarms,\n  ])\n\n  const { critical, warning } = useMemo(\n    () =>\n      alarms.reduce(\n        (acc, { status }) => {\n          if (status === \"CRITICAL\") acc.critical = acc.critical + 1\n          if (status === \"WARNING\") acc.warning = acc.warning + 1\n          return acc\n        },\n        { critical: 0, warning: 0 }\n      ),\n    [alarms]\n  )\n\n  return (\n    <Item icon=\"alarm\">\n      <Tooltip\n        content={\n          critical\n            ? `${critical} critical alert${critical.length > 1 ? \"s\" : \"\"}`\n            : \"No critical alerts\"\n        }\n        align=\"bottom\"\n        plain\n      >\n        <Pill background=\"error\" hollow {...pillProps}>\n          {critical}\n        </Pill>\n      </Tooltip>\n      <Tooltip\n        content={\n          warning ? `${warning} warning alert${warning.length > 1 ? \"s\" : \"\"}` : \"No warning alerts\"\n        }\n        align=\"bottom\"\n        plain\n      >\n        <Pill background=\"warning\" hollow {...pillProps}>\n          {warning}\n        </Pill>\n      </Tooltip>\n    </Item>\n  )\n}\n\nexport default Alarms\n","import React from \"react\"\nimport { Button, News as AgentNews } from \"@netdata/netdata-ui\"\nimport Tooltip from \"@/src/components/tooltips\"\n\nconst News = () => {\n  return (\n    <AgentNews app=\"agent\">\n      {({ toggle, upToDate }) => (\n        <Tooltip content=\"News\" align=\"bottom\" plain>\n          <Button\n            data-testid=\"header-news-button\"\n            themeType=\"dark\"\n            name=\"news\"\n            icon=\"insights\"\n            flavour=\"borderless\"\n            neutral={upToDate}\n            warning={!upToDate}\n            onClick={toggle}\n          />\n        </Tooltip>\n      )}\n    </AgentNews>\n  )\n}\n\nexport default News\n","import styled from \"styled-components\"\nimport { Flex } from \"@netdata/netdata-ui\"\n\nconst Dropdown = styled(Flex).attrs({\n  column: true,\n  padding: [2],\n  background: \"dropdown\",\n  round: 1,\n  overflow: { vertical: \"auto\" },\n  margin: [2, 0, 0],\n  width: 80,\n})`\n  box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);\n`\n\nexport default Dropdown\n","import styled from \"styled-components\"\nimport { TextInput } from \"@netdata/netdata-ui\"\n\nconst SearchInput = styled(TextInput)`\n  & input {\n    background: transparent;\n  }\n\n  & > label {\n    margin-bottom: 0;\n  }\n`\nexport default SearchInput\n","import React, { forwardRef } from \"react\"\nimport SearchInput from \"./searchInput\"\n\nconst Search = forwardRef(({ value, onChange }, ref) => (\n  <SearchInput inputRef={ref} value={value} onChange={onChange} placeholder=\"Search\" metaShrinked />\n))\n\nexport default Search\n","import styled from \"styled-components\"\nimport { Flex } from \"@netdata/netdata-ui\"\n\nconst Container = styled(Flex).attrs({\n  column: true,\n  padding: [2, 0, 0],\n  overflow: { vertical: \"auto\" },\n  height: { max: \"320px\" },\n})``\n\nexport default Container\n","import styled from \"styled-components\"\nimport { Flex } from \"@netdata/netdata-ui\"\n\nconst Wrapper = styled(Flex).attrs({\n  justifyContent: \"between\",\n  alignItems: \"center\",\n  width: \"100%\",\n  gap: 2,\n})``\n\nexport default Wrapper\n","import React, { useCallback } from \"react\"\nimport { Text } from \"@netdata/netdata-ui\"\nimport { MenuItem } from \"@/src/components/menus\"\nimport Wrapper from \"./wrapper\"\n\nconst OffsetItem = ({ name, offset, utc, onSelect }) => {\n  const onClick = useCallback(() => onSelect(utc), [utc, onSelect])\n\n  return (\n    <MenuItem round={1} onClick={onClick} Wrapper={Wrapper}>\n      <Text color=\"text\">{name}</Text>\n      <Text color=\"textLite\" whiteSpace=\"nowrap\">\n        UTC {offset}\n      </Text>\n    </MenuItem>\n  )\n}\n\nexport default OffsetItem\n","export const timezones = [\n  {\n    value: \"Dateline Standard Time\",\n    abbr: \"DST\",\n    text: \"International Date Line West\",\n    utc: [\"Etc/GMT+12\"],\n  },\n  {\n    value: \"UTC-11\",\n    abbr: \"U\",\n    text: \"Coordinated Universal Time-11\",\n    utc: [\"Etc/GMT+11\", \"Pacific/Midway\", \"Pacific/Niue\", \"Pacific/Pago_Pago\"],\n  },\n  {\n    value: \"Hawaiian Standard Time\",\n    abbr: \"HST\",\n    text: \"Hawaii\",\n    utc: [\n      \"Etc/GMT+10\",\n      \"Pacific/Honolulu\",\n      \"Pacific/Johnston\",\n      \"Pacific/Rarotonga\",\n      \"Pacific/Tahiti\",\n    ],\n  },\n  {\n    value: \"Alaskan Standard Time\",\n    abbr: \"AKDT\",\n    text: \"Alaska\",\n    utc: [\n      \"America/Anchorage\",\n      \"America/Juneau\",\n      \"America/Nome\",\n      \"America/Sitka\",\n      \"America/Yakutat\",\n    ],\n  },\n  {\n    value: \"Pacific Standard Time (Mexico)\",\n    abbr: \"PDT\",\n    text: \"Baja California\",\n    utc: [\"America/Santa_Isabel\"],\n  },\n  {\n    value: \"Pacific Standard Time\",\n    abbr: \"PST\",\n    text: \"Pacific Time (US & Canada)\",\n    utc: [\n      \"America/Dawson\",\n      \"America/Los_Angeles\",\n      \"America/Tijuana\",\n      \"America/Vancouver\",\n      \"America/Whitehorse\",\n      \"PST8PDT\",\n    ],\n  },\n  {\n    value: \"US Mountain Standard Time\",\n    abbr: \"UMST\",\n    text: \"Arizona\",\n    utc: [\n      \"America/Creston\",\n      \"America/Dawson_Creek\",\n      \"America/Hermosillo\",\n      \"America/Phoenix\",\n      \"Etc/GMT+7\",\n    ],\n  },\n  {\n    value: \"Mountain Standard Time (Mexico)\",\n    abbr: \"MDT\",\n    text: \"Chihuahua, La Paz, Mazatlan\",\n    utc: [\"America/Chihuahua\", \"America/Mazatlan\"],\n  },\n  {\n    value: \"Mountain Standard Time\",\n    abbr: \"MDT\",\n    text: \"Mountain Time (US & Canada)\",\n    utc: [\n      \"America/Boise\",\n      \"America/Cambridge_Bay\",\n      \"America/Denver\",\n      \"America/Edmonton\",\n      \"America/Inuvik\",\n      \"America/Ojinaga\",\n      \"America/Yellowknife\",\n      \"MST7MDT\",\n    ],\n  },\n  {\n    value: \"Central America Standard Time\",\n    abbr: \"CAST\",\n    text: \"Central America\",\n    utc: [\n      \"America/Belize\",\n      \"America/Costa_Rica\",\n      \"America/El_Salvador\",\n      \"America/Guatemala\",\n      \"America/Managua\",\n      \"America/Tegucigalpa\",\n      \"Etc/GMT+6\",\n      \"Pacific/Galapagos\",\n    ],\n  },\n  {\n    value: \"Central Standard Time\",\n    abbr: \"CDT\",\n    text: \"Central Time (US & Canada)\",\n    utc: [\n      \"America/Chicago\",\n      \"America/Indiana/Knox\",\n      \"America/Indiana/Tell_City\",\n      \"America/Matamoros\",\n      \"America/Menominee\",\n      \"America/North_Dakota/Beulah\",\n      \"America/North_Dakota/Center\",\n      \"America/North_Dakota/New_Salem\",\n      \"America/Rainy_River\",\n      \"America/Rankin_Inlet\",\n      \"America/Resolute\",\n      \"America/Winnipeg\",\n      \"CST6CDT\",\n    ],\n  },\n  {\n    value: \"Central Standard Time (Mexico)\",\n    abbr: \"CDT\",\n    text: \"Guadalajara, Mexico City, Monterrey\",\n    utc: [\n      \"America/Bahia_Banderas\",\n      \"America/Cancun\",\n      \"America/Merida\",\n      \"America/Mexico_City\",\n      \"America/Monterrey\",\n    ],\n  },\n  {\n    value: \"Canada Central Standard Time\",\n    abbr: \"CCST\",\n    text: \"Saskatchewan\",\n    utc: [\"America/Regina\", \"America/Swift_Current\"],\n  },\n  {\n    value: \"SA Pacific Standard Time\",\n    abbr: \"SPST\",\n    text: \"Bogota, Lima, Quito\",\n    utc: [\n      \"America/Bogota\",\n      \"America/Cayman\",\n      \"America/Coral_Harbour\",\n      \"America/Eirunepe\",\n      \"America/Guayaquil\",\n      \"America/Jamaica\",\n      \"America/Lima\",\n      \"America/Panama\",\n      \"America/Rio_Branco\",\n      \"Etc/GMT+5\",\n    ],\n  },\n  {\n    value: \"Eastern Standard Time\",\n    abbr: \"EDT\",\n    text: \"Eastern Time (US & Canada)\",\n    utc: [\n      \"America/Detroit\",\n      \"America/Havana\",\n      \"America/Indiana/Petersburg\",\n      \"America/Indiana/Vincennes\",\n      \"America/Indiana/Winamac\",\n      \"America/Iqaluit\",\n      \"America/Kentucky/Monticello\",\n      \"America/Louisville\",\n      \"America/Montreal\",\n      \"America/Nassau\",\n      \"America/New_York\",\n      \"America/Nipigon\",\n      \"America/Pangnirtung\",\n      \"America/Port-au-Prince\",\n      \"America/Thunder_Bay\",\n      \"America/Toronto\",\n      \"EST5EDT\",\n    ],\n  },\n  {\n    value: \"US Eastern Standard Time\",\n    abbr: \"UEDT\",\n    text: \"Indiana (East)\",\n    utc: [\"America/Indiana/Marengo\", \"America/Indiana/Vevay\", \"America/Indianapolis\"],\n  },\n  {\n    value: \"Venezuela Standard Time\",\n    abbr: \"VST\",\n    text: \"Caracas\",\n    utc: [\"America/Caracas\"],\n  },\n  {\n    value: \"Paraguay Standard Time\",\n    abbr: \"PYT\",\n    text: \"Asuncion\",\n    utc: [\"America/Asuncion\"],\n  },\n  {\n    value: \"Atlantic Standard Time\",\n    abbr: \"ADT\",\n    text: \"Atlantic Time (Canada)\",\n    utc: [\n      \"America/Glace_Bay\",\n      \"America/Goose_Bay\",\n      \"America/Halifax\",\n      \"America/Moncton\",\n      \"America/Thule\",\n      \"Atlantic/Bermuda\",\n    ],\n  },\n  {\n    value: \"Central Brazilian Standard Time\",\n    abbr: \"CBST\",\n    text: \"Cuiaba\",\n    utc: [\"America/Campo_Grande\", \"America/Cuiaba\"],\n  },\n  {\n    value: \"SA Western Standard Time\",\n    abbr: \"SWST\",\n    text: \"Georgetown, La Paz, Manaus, San Juan\",\n    utc: [\n      \"America/Anguilla\",\n      \"America/Antigua\",\n      \"America/Aruba\",\n      \"America/Barbados\",\n      \"America/Blanc-Sablon\",\n      \"America/Boa_Vista\",\n      \"America/Curacao\",\n      \"America/Dominica\",\n      \"America/Grand_Turk\",\n      \"America/Grenada\",\n      \"America/Guadeloupe\",\n      \"America/Guyana\",\n      \"America/Kralendijk\",\n      \"America/La_Paz\",\n      \"America/Lower_Princes\",\n      \"America/Manaus\",\n      \"America/Marigot\",\n      \"America/Martinique\",\n      \"America/Montserrat\",\n      \"America/Port_of_Spain\",\n      \"America/Porto_Velho\",\n      \"America/Puerto_Rico\",\n      \"America/Santo_Domingo\",\n      \"America/St_Barthelemy\",\n      \"America/St_Kitts\",\n      \"America/St_Lucia\",\n      \"America/St_Thomas\",\n      \"America/St_Vincent\",\n      \"America/Tortola\",\n      \"Etc/GMT+4\",\n    ],\n  },\n  {\n    value: \"Pacific SA Standard Time\",\n    abbr: \"PSST\",\n    text: \"Santiago\",\n    utc: [\"America/Santiago\", \"Antarctica/Palmer\"],\n  },\n  {\n    value: \"Newfoundland Standard Time\",\n    abbr: \"NDT\",\n    text: \"Newfoundland\",\n    utc: [\"America/St_Johns\"],\n  },\n  {\n    value: \"E. South America Standard Time\",\n    abbr: \"ESAST\",\n    text: \"Brasilia\",\n    utc: [\"America/Sao_Paulo\"],\n  },\n  {\n    value: \"Argentina Standard Time\",\n    abbr: \"AST\",\n    text: \"Buenos Aires\",\n    utc: [\n      \"America/Argentina/La_Rioja\",\n      \"America/Argentina/Rio_Gallegos\",\n      \"America/Argentina/Salta\",\n      \"America/Argentina/San_Juan\",\n      \"America/Argentina/San_Luis\",\n      \"America/Argentina/Tucuman\",\n      \"America/Argentina/Ushuaia\",\n      \"America/Buenos_Aires\",\n      \"America/Catamarca\",\n      \"America/Cordoba\",\n      \"America/Jujuy\",\n      \"America/Mendoza\",\n    ],\n  },\n  {\n    value: \"SA Eastern Standard Time\",\n    abbr: \"SEST\",\n    text: \"Cayenne, Fortaleza\",\n    utc: [\n      \"America/Araguaina\",\n      \"America/Belem\",\n      \"America/Cayenne\",\n      \"America/Fortaleza\",\n      \"America/Maceio\",\n      \"America/Paramaribo\",\n      \"America/Recife\",\n      \"America/Santarem\",\n      \"Antarctica/Rothera\",\n      \"Atlantic/Stanley\",\n      \"Etc/GMT+3\",\n    ],\n  },\n  {\n    value: \"Greenland Standard Time\",\n    abbr: \"GDT\",\n    text: \"Greenland\",\n    utc: [\"America/Godthab\"],\n  },\n  {\n    value: \"Montevideo Standard Time\",\n    abbr: \"MST\",\n    text: \"Montevideo\",\n    utc: [\"America/Montevideo\"],\n  },\n  {\n    value: \"Bahia Standard Time\",\n    abbr: \"BST\",\n    text: \"Salvador\",\n    utc: [\"America/Bahia\"],\n  },\n  {\n    value: \"UTC-02\",\n    abbr: \"U\",\n    text: \"Coordinated Universal Time-02\",\n    utc: [\"America/Noronha\", \"Atlantic/South_Georgia\", \"Etc/GMT+2\"],\n  },\n  {\n    value: \"Mid-Atlantic Standard Time\",\n    abbr: \"MDT\",\n    text: \"Mid-Atlantic - Old\",\n    utc: [],\n  },\n  {\n    value: \"Azores Standard Time\",\n    abbr: \"ADT\",\n    text: \"Azores\",\n    utc: [\"America/Scoresbysund\", \"Atlantic/Azores\"],\n  },\n  {\n    value: \"Cape Verde Standard Time\",\n    abbr: \"CVST\",\n    text: \"Cape Verde Is.\",\n    utc: [\"Atlantic/Cape_Verde\", \"Etc/GMT+1\"],\n  },\n  {\n    value: \"Morocco Standard Time\",\n    abbr: \"MDT\",\n    text: \"Casablanca\",\n    utc: [\"Africa/Casablanca\", \"Africa/El_Aaiun\"],\n  },\n  {\n    value: \"UTC\",\n    abbr: \"UTC\",\n    text: \"Coordinated Universal Time\",\n    utc: [\"America/Danmarkshavn\", \"Etc/GMT\"],\n  },\n  {\n    value: \"GMT Standard Time\",\n    abbr: \"GMT\",\n    text: \"Edinburgh, London\",\n    utc: [\"Europe/Isle_of_Man\", \"Europe/Guernsey\", \"Europe/Jersey\", \"Europe/London\"],\n  },\n  {\n    value: \"GMT Standard Time\",\n    abbr: \"GDT\",\n    text: \"Dublin, Lisbon\",\n    utc: [\n      \"Atlantic/Canary\",\n      \"Atlantic/Faeroe\",\n      \"Atlantic/Madeira\",\n      \"Europe/Dublin\",\n      \"Europe/Lisbon\",\n    ],\n  },\n  {\n    value: \"Greenwich Standard Time\",\n    abbr: \"GST\",\n    text: \"Monrovia, Reykjavik\",\n    utc: [\n      \"Africa/Abidjan\",\n      \"Africa/Accra\",\n      \"Africa/Bamako\",\n      \"Africa/Banjul\",\n      \"Africa/Bissau\",\n      \"Africa/Conakry\",\n      \"Africa/Dakar\",\n      \"Africa/Freetown\",\n      \"Africa/Lome\",\n      \"Africa/Monrovia\",\n      \"Africa/Nouakchott\",\n      \"Africa/Ouagadougou\",\n      \"Africa/Sao_Tome\",\n      \"Atlantic/Reykjavik\",\n      \"Atlantic/St_Helena\",\n    ],\n  },\n  {\n    value: \"W. Europe Standard Time\",\n    abbr: \"WEDT\",\n    text: \"Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna\",\n    utc: [\n      \"Arctic/Longyearbyen\",\n      \"Europe/Amsterdam\",\n      \"Europe/Andorra\",\n      \"Europe/Berlin\",\n      \"Europe/Busingen\",\n      \"Europe/Gibraltar\",\n      \"Europe/Luxembourg\",\n      \"Europe/Malta\",\n      \"Europe/Monaco\",\n      \"Europe/Oslo\",\n      \"Europe/Rome\",\n      \"Europe/San_Marino\",\n      \"Europe/Stockholm\",\n      \"Europe/Vaduz\",\n      \"Europe/Vatican\",\n      \"Europe/Vienna\",\n      \"Europe/Zurich\",\n    ],\n  },\n  {\n    value: \"Central Europe Standard Time\",\n    abbr: \"CEDT\",\n    text: \"Belgrade, Bratislava, Budapest, Ljubljana, Prague\",\n    utc: [\n      \"Europe/Belgrade\",\n      \"Europe/Bratislava\",\n      \"Europe/Budapest\",\n      \"Europe/Ljubljana\",\n      \"Europe/Podgorica\",\n      \"Europe/Prague\",\n      \"Europe/Tirane\",\n    ],\n  },\n  {\n    value: \"Romance Standard Time\",\n    abbr: \"RDT\",\n    text: \"Brussels, Copenhagen, Madrid, Paris\",\n    utc: [\"Africa/Ceuta\", \"Europe/Brussels\", \"Europe/Copenhagen\", \"Europe/Madrid\", \"Europe/Paris\"],\n  },\n  {\n    value: \"Central European Standard Time\",\n    abbr: \"CEDT\",\n    text: \"Sarajevo, Skopje, Warsaw, Zagreb\",\n    utc: [\"Europe/Sarajevo\", \"Europe/Skopje\", \"Europe/Warsaw\", \"Europe/Zagreb\"],\n  },\n  {\n    value: \"W. Central Africa Standard Time\",\n    abbr: \"WCAST\",\n    text: \"West Central Africa\",\n    utc: [\n      \"Africa/Algiers\",\n      \"Africa/Bangui\",\n      \"Africa/Brazzaville\",\n      \"Africa/Douala\",\n      \"Africa/Kinshasa\",\n      \"Africa/Lagos\",\n      \"Africa/Libreville\",\n      \"Africa/Luanda\",\n      \"Africa/Malabo\",\n      \"Africa/Ndjamena\",\n      \"Africa/Niamey\",\n      \"Africa/Porto-Novo\",\n      \"Africa/Tunis\",\n      \"Etc/GMT-1\",\n    ],\n  },\n  {\n    value: \"Namibia Standard Time\",\n    abbr: \"NST\",\n    text: \"Windhoek\",\n    utc: [\"Africa/Windhoek\"],\n  },\n  {\n    value: \"GTB Standard Time\",\n    abbr: \"GDT\",\n    text: \"Athens, Bucharest\",\n    utc: [\"Asia/Nicosia\", \"Europe/Athens\", \"Europe/Bucharest\", \"Europe/Chisinau\"],\n  },\n  {\n    value: \"Middle East Standard Time\",\n    abbr: \"MEDT\",\n    text: \"Beirut\",\n    utc: [\"Asia/Beirut\"],\n  },\n  {\n    value: \"Egypt Standard Time\",\n    abbr: \"EST\",\n    text: \"Cairo\",\n    utc: [\"Africa/Cairo\"],\n  },\n  {\n    value: \"Syria Standard Time\",\n    abbr: \"SDT\",\n    text: \"Damascus\",\n    utc: [\"Asia/Damascus\"],\n  },\n  {\n    value: \"E. Europe Standard Time\",\n    abbr: \"EEDT\",\n    text: \"E. Europe\",\n    utc: [\n      \"Asia/Nicosia\",\n      \"Europe/Athens\",\n      \"Europe/Bucharest\",\n      \"Europe/Chisinau\",\n      \"Europe/Helsinki\",\n      \"Europe/Kiev\",\n      \"Europe/Mariehamn\",\n      \"Europe/Nicosia\",\n      \"Europe/Riga\",\n      \"Europe/Sofia\",\n      \"Europe/Tallinn\",\n      \"Europe/Uzhgorod\",\n      \"Europe/Vilnius\",\n      \"Europe/Zaporozhye\",\n    ],\n  },\n  {\n    value: \"South Africa Standard Time\",\n    abbr: \"SAST\",\n    text: \"Harare, Pretoria\",\n    utc: [\n      \"Africa/Blantyre\",\n      \"Africa/Bujumbura\",\n      \"Africa/Gaborone\",\n      \"Africa/Harare\",\n      \"Africa/Johannesburg\",\n      \"Africa/Kigali\",\n      \"Africa/Lubumbashi\",\n      \"Africa/Lusaka\",\n      \"Africa/Maputo\",\n      \"Africa/Maseru\",\n      \"Africa/Mbabane\",\n      \"Etc/GMT-2\",\n    ],\n  },\n  {\n    value: \"FLE Standard Time\",\n    abbr: \"FDT\",\n    text: \"Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius\",\n    utc: [\n      \"Europe/Helsinki\",\n      \"Europe/Kiev\",\n      \"Europe/Mariehamn\",\n      \"Europe/Riga\",\n      \"Europe/Sofia\",\n      \"Europe/Tallinn\",\n      \"Europe/Uzhgorod\",\n      \"Europe/Vilnius\",\n      \"Europe/Zaporozhye\",\n    ],\n  },\n  {\n    value: \"Turkey Standard Time\",\n    abbr: \"TDT\",\n    text: \"Istanbul\",\n    utc: [\"Europe/Istanbul\"],\n  },\n  {\n    value: \"Israel Standard Time\",\n    abbr: \"JDT\",\n    text: \"Jerusalem\",\n    utc: [\"Asia/Jerusalem\"],\n  },\n  {\n    value: \"Libya Standard Time\",\n    abbr: \"LST\",\n    text: \"Tripoli\",\n    utc: [\"Africa/Tripoli\"],\n  },\n  {\n    value: \"Jordan Standard Time\",\n    abbr: \"JST\",\n    text: \"Amman\",\n    utc: [\"Asia/Amman\"],\n  },\n  {\n    value: \"Arabic Standard Time\",\n    abbr: \"AST\",\n    text: \"Baghdad\",\n    utc: [\"Asia/Baghdad\"],\n  },\n  {\n    value: \"Kaliningrad Standard Time\",\n    abbr: \"KST\",\n    text: \"Kaliningrad\",\n    utc: [\"Europe/Kaliningrad\"],\n  },\n  {\n    value: \"Arab Standard Time\",\n    abbr: \"AST\",\n    text: \"Kuwait, Riyadh\",\n    utc: [\"Asia/Aden\", \"Asia/Bahrain\", \"Asia/Kuwait\", \"Asia/Qatar\", \"Asia/Riyadh\"],\n  },\n  {\n    value: \"E. Africa Standard Time\",\n    abbr: \"EAST\",\n    text: \"Nairobi\",\n    utc: [\n      \"Africa/Addis_Ababa\",\n      \"Africa/Asmera\",\n      \"Africa/Dar_es_Salaam\",\n      \"Africa/Djibouti\",\n      \"Africa/Juba\",\n      \"Africa/Kampala\",\n      \"Africa/Khartoum\",\n      \"Africa/Mogadishu\",\n      \"Africa/Nairobi\",\n      \"Antarctica/Syowa\",\n      \"Etc/GMT-3\",\n      \"Indian/Antananarivo\",\n      \"Indian/Comoro\",\n      \"Indian/Mayotte\",\n    ],\n  },\n  {\n    value: \"Moscow Standard Time\",\n    abbr: \"MSK\",\n    text: \"Moscow, St. Petersburg, Volgograd, Minsk\",\n    utc: [\"Europe/Kirov\", \"Europe/Moscow\", \"Europe/Simferopol\", \"Europe/Volgograd\", \"Europe/Minsk\"],\n  },\n  {\n    value: \"Samara Time\",\n    abbr: \"SAMT\",\n    text: \"Samara, Ulyanovsk, Saratov\",\n    utc: [\"Europe/Astrakhan\", \"Europe/Samara\", \"Europe/Ulyanovsk\"],\n  },\n  {\n    value: \"Iran Standard Time\",\n    abbr: \"IDT\",\n    text: \"Tehran\",\n    utc: [\"Asia/Tehran\"],\n  },\n  {\n    value: \"Arabian Standard Time\",\n    abbr: \"AST\",\n    text: \"Abu Dhabi, Muscat\",\n    utc: [\"Asia/Dubai\", \"Asia/Muscat\", \"Etc/GMT-4\"],\n  },\n  {\n    value: \"Azerbaijan Standard Time\",\n    abbr: \"ADT\",\n    text: \"Baku\",\n    utc: [\"Asia/Baku\"],\n  },\n  {\n    value: \"Mauritius Standard Time\",\n    abbr: \"MST\",\n    text: \"Port Louis\",\n    utc: [\"Indian/Mahe\", \"Indian/Mauritius\", \"Indian/Reunion\"],\n  },\n  {\n    value: \"Georgian Standard Time\",\n    abbr: \"GET\",\n    text: \"Tbilisi\",\n    utc: [\"Asia/Tbilisi\"],\n  },\n  {\n    value: \"Caucasus Standard Time\",\n    abbr: \"CST\",\n    text: \"Yerevan\",\n    utc: [\"Asia/Yerevan\"],\n  },\n  {\n    value: \"Afghanistan Standard Time\",\n    abbr: \"AST\",\n    text: \"Kabul\",\n    utc: [\"Asia/Kabul\"],\n  },\n  {\n    value: \"West Asia Standard Time\",\n    abbr: \"WAST\",\n    text: \"Ashgabat, Tashkent\",\n    utc: [\n      \"Antarctica/Mawson\",\n      \"Asia/Aqtau\",\n      \"Asia/Aqtobe\",\n      \"Asia/Ashgabat\",\n      \"Asia/Dushanbe\",\n      \"Asia/Oral\",\n      \"Asia/Samarkand\",\n      \"Asia/Tashkent\",\n      \"Etc/GMT-5\",\n      \"Indian/Kerguelen\",\n      \"Indian/Maldives\",\n    ],\n  },\n  {\n    value: \"Yekaterinburg Time\",\n    abbr: \"YEKT\",\n    text: \"Yekaterinburg\",\n    utc: [\"Asia/Yekaterinburg\"],\n  },\n  {\n    value: \"Pakistan Standard Time\",\n    abbr: \"PKT\",\n    text: \"Islamabad, Karachi\",\n    utc: [\"Asia/Karachi\"],\n  },\n  {\n    value: \"India Standard Time\",\n    abbr: \"IST\",\n    text: \"Chennai, Kolkata, Mumbai, New Delhi\",\n    utc: [\"Asia/Kolkata\"],\n  },\n  {\n    value: \"Sri Lanka Standard Time\",\n    abbr: \"SLST\",\n    text: \"Sri Jayawardenepura\",\n    utc: [\"Asia/Colombo\"],\n  },\n  {\n    value: \"Nepal Standard Time\",\n    abbr: \"NST\",\n    text: \"Kathmandu\",\n    utc: [\"Asia/Kathmandu\"],\n  },\n  {\n    value: \"Central Asia Standard Time\",\n    abbr: \"CAST\",\n    text: \"Nur-Sultan (Astana)\",\n    utc: [\n      \"Antarctica/Vostok\",\n      \"Asia/Almaty\",\n      \"Asia/Bishkek\",\n      \"Asia/Qyzylorda\",\n      \"Asia/Urumqi\",\n      \"Etc/GMT-6\",\n      \"Indian/Chagos\",\n    ],\n  },\n  {\n    value: \"Bangladesh Standard Time\",\n    abbr: \"BST\",\n    text: \"Dhaka\",\n    utc: [\"Asia/Dhaka\", \"Asia/Thimphu\"],\n  },\n  {\n    value: \"Myanmar Standard Time\",\n    abbr: \"MST\",\n    text: \"Yangon (Rangoon)\",\n    utc: [\"Asia/Rangoon\", \"Indian/Cocos\"],\n  },\n  {\n    value: \"SE Asia Standard Time\",\n    abbr: \"SAST\",\n    text: \"Bangkok, Hanoi, Jakarta\",\n    utc: [\n      \"Antarctica/Davis\",\n      \"Asia/Bangkok\",\n      \"Asia/Hovd\",\n      \"Asia/Jakarta\",\n      \"Asia/Phnom_Penh\",\n      \"Asia/Pontianak\",\n      \"Asia/Saigon\",\n      \"Asia/Vientiane\",\n      \"Etc/GMT-7\",\n      \"Indian/Christmas\",\n    ],\n  },\n  {\n    value: \"N. Central Asia Standard Time\",\n    abbr: \"NCAST\",\n    text: \"Novosibirsk\",\n    utc: [\"Asia/Novokuznetsk\", \"Asia/Novosibirsk\", \"Asia/Omsk\"],\n  },\n  {\n    value: \"China Standard Time\",\n    abbr: \"CST\",\n    text: \"Beijing, Chongqing, Hong Kong, Urumqi\",\n    utc: [\"Asia/Hong_Kong\", \"Asia/Macau\", \"Asia/Shanghai\"],\n  },\n  {\n    value: \"North Asia Standard Time\",\n    abbr: \"NAST\",\n    text: \"Krasnoyarsk\",\n    utc: [\"Asia/Krasnoyarsk\"],\n  },\n  {\n    value: \"Singapore Standard Time\",\n    abbr: \"MPST\",\n    text: \"Kuala Lumpur, Singapore\",\n    utc: [\n      \"Asia/Brunei\",\n      \"Asia/Kuala_Lumpur\",\n      \"Asia/Kuching\",\n      \"Asia/Makassar\",\n      \"Asia/Manila\",\n      \"Asia/Singapore\",\n      \"Etc/GMT-8\",\n    ],\n  },\n  {\n    value: \"W. Australia Standard Time\",\n    abbr: \"WAST\",\n    text: \"Perth\",\n    utc: [\"Antarctica/Casey\", \"Australia/Perth\"],\n  },\n  {\n    value: \"Taipei Standard Time\",\n    abbr: \"TST\",\n    text: \"Taipei\",\n    utc: [\"Asia/Taipei\"],\n  },\n  {\n    value: \"Ulaanbaatar Standard Time\",\n    abbr: \"UST\",\n    text: \"Ulaanbaatar\",\n    utc: [\"Asia/Choibalsan\", \"Asia/Ulaanbaatar\"],\n  },\n  {\n    value: \"North Asia East Standard Time\",\n    abbr: \"NAEST\",\n    text: \"Irkutsk\",\n    utc: [\"Asia/Irkutsk\"],\n  },\n  {\n    value: \"Japan Standard Time\",\n    abbr: \"JST\",\n    text: \"Osaka, Sapporo, Tokyo\",\n    utc: [\"Asia/Dili\", \"Asia/Jayapura\", \"Asia/Tokyo\", \"Etc/GMT-9\", \"Pacific/Palau\"],\n  },\n  {\n    value: \"Korea Standard Time\",\n    abbr: \"KST\",\n    text: \"Seoul\",\n    utc: [\"Asia/Pyongyang\", \"Asia/Seoul\"],\n  },\n  {\n    value: \"Cen. Australia Standard Time\",\n    abbr: \"CAST\",\n    text: \"Adelaide\",\n    utc: [\"Australia/Adelaide\", \"Australia/Broken_Hill\"],\n  },\n  {\n    value: \"AUS Central Standard Time\",\n    abbr: \"ACST\",\n    text: \"Darwin\",\n    utc: [\"Australia/Darwin\"],\n  },\n  {\n    value: \"E. Australia Standard Time\",\n    abbr: \"EAST\",\n    text: \"Brisbane\",\n    utc: [\"Australia/Brisbane\", \"Australia/Lindeman\"],\n  },\n  {\n    value: \"AUS Eastern Standard Time\",\n    abbr: \"AEST\",\n    text: \"Canberra, Melbourne, Sydney\",\n    utc: [\"Australia/Melbourne\", \"Australia/Sydney\"],\n  },\n  {\n    value: \"West Pacific Standard Time\",\n    abbr: \"WPST\",\n    text: \"Guam, Port Moresby\",\n    utc: [\n      \"Antarctica/DumontDUrville\",\n      \"Etc/GMT-10\",\n      \"Pacific/Guam\",\n      \"Pacific/Port_Moresby\",\n      \"Pacific/Saipan\",\n      \"Pacific/Truk\",\n    ],\n  },\n  {\n    value: \"Tasmania Standard Time\",\n    abbr: \"TST\",\n    text: \"Hobart\",\n    utc: [\"Australia/Currie\", \"Australia/Hobart\"],\n  },\n  {\n    value: \"Yakutsk Standard Time\",\n    abbr: \"YST\",\n    text: \"Yakutsk\",\n    utc: [\"Asia/Chita\", \"Asia/Khandyga\", \"Asia/Yakutsk\"],\n  },\n  {\n    value: \"Central Pacific Standard Time\",\n    abbr: \"CPST\",\n    text: \"Solomon Is., New Caledonia\",\n    utc: [\n      \"Antarctica/Macquarie\",\n      \"Etc/GMT-11\",\n      \"Pacific/Efate\",\n      \"Pacific/Guadalcanal\",\n      \"Pacific/Kosrae\",\n      \"Pacific/Noumea\",\n      \"Pacific/Ponape\",\n    ],\n  },\n  {\n    value: \"Vladivostok Standard Time\",\n    abbr: \"VST\",\n    text: \"Vladivostok\",\n    utc: [\"Asia/Sakhalin\", \"Asia/Ust-Nera\", \"Asia/Vladivostok\"],\n  },\n  {\n    value: \"New Zealand Standard Time\",\n    abbr: \"NZST\",\n    text: \"Auckland, Wellington\",\n    utc: [\"Antarctica/McMurdo\", \"Pacific/Auckland\"],\n  },\n  {\n    value: \"UTC+12\",\n    abbr: \"U\",\n    text: \"Coordinated Universal Time+12\",\n    utc: [\n      \"Etc/GMT-12\",\n      \"Pacific/Funafuti\",\n      \"Pacific/Kwajalein\",\n      \"Pacific/Majuro\",\n      \"Pacific/Nauru\",\n      \"Pacific/Tarawa\",\n      \"Pacific/Wake\",\n      \"Pacific/Wallis\",\n    ],\n  },\n  {\n    value: \"Fiji Standard Time\",\n    abbr: \"FST\",\n    text: \"Fiji\",\n    utc: [\"Pacific/Fiji\"],\n  },\n  {\n    value: \"Magadan Standard Time\",\n    abbr: \"MST\",\n    text: \"Magadan\",\n    utc: [\"Asia/Anadyr\", \"Asia/Kamchatka\", \"Asia/Magadan\", \"Asia/Srednekolymsk\"],\n  },\n  {\n    value: \"Kamchatka Standard Time\",\n    abbr: \"KDT\",\n    text: \"Petropavlovsk-Kamchatsky - Old\",\n    utc: [\"Asia/Kamchatka\"],\n  },\n  {\n    value: \"Tonga Standard Time\",\n    abbr: \"TST\",\n    text: \"Nuku'alofa\",\n    utc: [\"Etc/GMT-13\", \"Pacific/Enderbury\", \"Pacific/Fakaofo\", \"Pacific/Tongatapu\"],\n  },\n  {\n    value: \"Samoa Standard Time\",\n    abbr: \"SST\",\n    text: \"Samoa\",\n    utc: [\"Pacific/Apia\"],\n  },\n]\n","import { timezones } from \"./timezones\"\n\nconst digitizeOffset = parsedOffset => {\n  if (!parsedOffset) return \"+0\"\n  const splitOffset = parsedOffset.split(\":\")\n  return splitOffset.length > 1\n    ? `${splitOffset[0]}${(splitOffset[1] / 60).toString().substr(1)}`\n    : splitOffset[0]\n}\n\nconst normalizeOffset = parsedOffset => (parsedOffset ? parsedOffset.replace(\"−\", \"-\") : \"\")\n\nconst now = new Date()\nexport const timezoneList = () => {\n  const memoized = {}\n  return timezones.reduce((acc, timezone) => {\n    const { utc } = timezone\n\n    try {\n      // We use 'fr' locale because it is the only one that returns back the UTC offset (dd/mm/yyyy, UTC-x) \n      // so we can parse it later and digitize it.\n      const dateString = new Intl.DateTimeFormat(\"fr\", {\n        timeZone: utc[0],\n        timeZoneName: \"short\",\n      }).format(now)\n\n      const [parsedOffset] = dateString.match(/[−+].+/) || []\n      const normalizedOffset = normalizeOffset(parsedOffset)\n\n      if (memoized[normalizedOffset])\n        return acc.concat({ ...timezone, offset: memoized[normalizedOffset] })\n\n      const digitizedOffset = digitizeOffset(normalizedOffset)\n\n      memoized[normalizedOffset] = digitizedOffset\n      return acc.concat({ ...timezone, offset: digitizedOffset })\n    } catch (e) {\n      return acc\n    }\n  }, [])\n}\n\nexport const timezonesById = timezones =>\n  timezones.reduce((acc, { utc, ...timezone }) => {\n    utc.forEach(item => (acc[item] = { ...timezone, utc: item }))\n    return acc\n  }, {})\n\nexport const getDefaultTimezone = () => {\n  const dateFormat = new Intl.DateTimeFormat(\"default\", {})\n  const usedOptions = dateFormat.resolvedOptions()\n  return usedOptions\n}\n","import React, { useRef, useState, useEffect, useMemo, useCallback } from \"react\"\nimport { useToggle } from \"react-use\"\nimport { Drop, Flex, Text, Icon } from \"@netdata/netdata-ui\"\nimport { useDispatch, useSelector } from \"store/redux-separate-context\"\nimport { setOptionAction } from \"@/src/domains/global/actions\"\nimport { selectTimezoneSetting } from \"domains/global/selectors\"\nimport { MenuItem } from \"@/src/components/menus\"\nimport Item from \"../item\"\nimport Dropdown from \"./dropdown\"\nimport Search from \"./search\"\nimport Container from \"./container\"\nimport Wrapper from \"./wrapper\"\nimport OffsetItem from \"./offsetItem\"\nimport { getDefaultTimezone, timezoneList, timezonesById } from \"./utils\"\nimport { getHashParams } from \"utils/hash-utils\"\n\nconst timezones = timezoneList().sort((a, b) => a.offset - b.offset)\nconst byId = timezonesById(timezones)\n\nconst getTimezone = (selectedTimezone, timezoneHash) => {\n  const timezone = timezoneHash\n    ? timezoneHash\n    : selectedTimezone === \"default\"\n    ? getDefaultTimezone().timeZone\n    : selectedTimezone\n\n  return byId[timezone in byId ? timezone : getDefaultTimezone().timeZone] || {}\n}\n\nconst Timezone = () => {\n  const [value, setValue] = useState(\"\")\n  const [isOpen, toggle] = useToggle()\n\n  const ref = useRef()\n  const inputRef = useRef()\n\n  const { updateUtcParam } = window.urlOptions\n\n  useEffect(() => {\n    if (!inputRef.current || !isOpen) return\n    inputRef.current.focus()\n  }, [isOpen])\n\n  const dispatch = useDispatch()\n  const selectedTimezone = useSelector(selectTimezoneSetting)\n\n  const selectedOffset = useMemo(() => {\n    const { utc: timezoneHash = \"\" } = getHashParams()\n    const { offset = \"\", utc = \"\" } = getTimezone(selectedTimezone, timezoneHash)\n\n    if (timezoneHash !== utc) updateUtcParam(utc)\n    if (selectedTimezone !== utc) dispatch(setOptionAction({ key: \"timezone\", value: utc }))\n\n    dispatch(setOptionAction({ key: \"utcOffset\", value: parseFloat(offset) }))\n\n    return offset\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [selectedTimezone])\n\n  const zones = useMemo(() => {\n    if (!value) return timezones\n    return timezones.filter(\n      ({ text, offset }) =>\n        text.toUpperCase().includes(value.toUpperCase()) || offset.includes(value)\n    )\n  }, [value])\n\n  const close = () => {\n    toggle(false)\n    setValue(\"\")\n  }\n\n  const onSelect = useCallback(utc => {\n    updateUtcParam(utc)\n    dispatch(setOptionAction({ key: \"timezone\", value: utc }))\n    close()\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [])\n\n  const onChange = useCallback(e => setValue(e.target.value), [])\n\n  return (\n    <Item hasBorder>\n      <MenuItem round={1} onClick={toggle} ref={ref} Wrapper={Wrapper}>\n        <Flex gap={1}>\n          <Text color=\"textLite\" whiteSpace=\"nowrap\">\n            UTC {selectedOffset}\n          </Text>\n        </Flex>\n        <Icon name=\"chevron_down\" color=\"text\" width=\"12px\" height=\"12px\" />\n      </MenuItem>\n      {ref.current && isOpen && (\n        <Drop\n          target={ref.current}\n          align={{ top: \"bottom\", left: \"left\" }}\n          onEsc={close}\n          onClickOutside={close}\n          animation\n        >\n          <Dropdown>\n            <Search value={value} onChange={onChange} ref={inputRef} />\n            <Container>\n              {zones.map(({ text, offset, utc }) => (\n                <OffsetItem\n                  key={text}\n                  name={text}\n                  offset={offset}\n                  utc={utc[0]}\n                  onSelect={onSelect}\n                />\n              ))}\n            </Container>\n          </Dropdown>\n        </Drop>\n      )}\n    </Item>\n  )\n}\n\nexport default Timezone\n","import React, { useCallback, useEffect, useState, useRef } from \"react\"\nimport styled from \"styled-components\"\nimport { useSelector, useDispatch } from \"store/redux-separate-context\"\nimport { useLocalStorage } from \"react-use\"\nimport { Flex } from \"@netdata/netdata-ui\"\nimport { sendToChildIframe, useListenToPostMessage } from \"utils/post-message\"\nimport { getIframeSrc, NETDATA_REGISTRY_SERVER } from \"utils/utils\"\nimport useUserNodeAccessMessage from \"hooks/use-user-node-access\"\nimport { selectRegistry, selectCloudBaseUrl } from \"domains/global/selectors\"\nimport { LOCAL_STORAGE_NEEDS_SYNC } from \"domains/dashboard/sagas\"\nimport { setOfflineAction } from \"@/src/domains/dashboard/actions\"\nimport { SIGN_IN_IFRAME_ID } from \"components/header/constants\"\n\nconst IframeContainer = styled(Flex).attrs({ position: \"absolute\" })`\n  display: none;\n`\nconst Iframe = ({ signedIn }) => {\n  const [rendered, setRendered] = useState(false)\n  const signInMsg = useRef()\n  const ref = useRef()\n\n  const [lsValue, , removeLsValue] = useLocalStorage(LOCAL_STORAGE_NEEDS_SYNC)\n  const cloudBaseURL = useSelector(selectCloudBaseUrl)\n  const registry = useSelector(selectRegistry)\n\n  const dispatch = useDispatch()\n\n  const { origin, pathname } = window.location\n  const nameParam = encodeURIComponent(registry.hostname)\n  const originParam = encodeURIComponent(origin + pathname)\n\n  const signInIframeUrl = getIframeSrc(\n    cloudBaseURL,\n    `sign-in?id=${registry.machineGuid}&name=${nameParam}&origin=${originParam}`\n  )\n\n  useListenToPostMessage(\"hello-from-sign-in\", msg => {\n    signInMsg.current = msg\n  })\n\n  useUserNodeAccessMessage()\n\n  const onLoad = useCallback(() => {\n    setRendered(true)\n    setTimeout(() => dispatch(setOfflineAction({ offline: signInMsg.current === undefined })), 500)\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [])\n\n  useEffect(() => {\n    const handler = e => {\n      if (!e?.target) return\n      if (e.target.src === signInIframeUrl && !rendered) onLoad()\n    }\n\n    window.addEventListener(\"DOMFrameContentLoaded\", handler)\n    return () => window.removeEventListener(\"DOMFrameContentLoaded\", handler)\n  }, [signInIframeUrl, rendered, onLoad])\n\n  useEffect(() => {\n    if (!signedIn || !ref.current) return\n    if (!registry.registryServer || registry.registryServer === NETDATA_REGISTRY_SERVER) return\n    if (!lsValue) return\n\n    removeLsValue()\n\n    const { registryMachinesArray } = registry\n    if (registryMachinesArray && registryMachinesArray.length > 0) {\n      sendToChildIframe(ref.current, {\n        type: \"synced-private-registry\",\n        payload: registryMachinesArray,\n      })\n    }\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [signedIn, registry, lsValue])\n\n  return (\n    <IframeContainer as=\"iframe\" id={SIGN_IN_IFRAME_ID} src={signInIframeUrl} onLoad={onLoad} />\n  )\n}\n\nexport default Iframe\n","import { useCallback, useState } from \"react\"\nimport { useLocalStorage } from \"react-use\"\nimport { useDispatch } from \"react-redux\"\nimport { useListenToPostMessage } from \"@/src/utils/post-message\"\nimport { isSignedInAction } from \"@/src/domains/dashboard/actions\"\n\nconst useCheckSignInStatus = () => {\n  const dispatch = useDispatch()\n  const [value, setValue] = useLocalStorage(\"has-sign-in-history\")\n  const [hasSignedInBefore, setHasSignedInBefore] = useState(value)\n\n  const onMessage = useCallback(isNew => {\n    if (isNew) {\n      setHasSignedInBefore(isNew)\n      setValue(isNew)\n    }\n    dispatch(isSignedInAction({ isSignedIn: isNew }))\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [])\n\n  const [signedIn] = useListenToPostMessage(\"is-signed-in\", onMessage)\n\n  return [signedIn, hasSignedInBefore]\n}\n\nexport default useCheckSignInStatus\n","import React from \"react\"\nimport { useSelector } from \"react-redux\"\nimport { Button } from \"@netdata/netdata-ui\"\nimport SignInButton from \"components/auth/signIn\"\nimport SignInIframe from \"components/auth/signIn/iframe\"\nimport useCheckSignInStatus from \"components/auth/signIn/useCheckSignInStatus\"\nimport { selectIsCloudEnabled } from \"domains/global/selectors\"\nimport Tooltip from \"@/src/components/tooltips\"\n\nconst SignIn = () => {\n  const [signedIn] = useCheckSignInStatus()\n  const cloudEnabled = useSelector(selectIsCloudEnabled)\n\n  return (\n    cloudEnabled && (\n      <Tooltip\n        content=\"Sign in to Netdata to monitor all your nodes at once, have composite charts, custom dashboards, use intelligent features and more\"\n        align=\"bottom\"\n        plain\n      >\n        <div>\n          <SignInIframe signedIn={signedIn} />\n          {!signedIn && (\n            <SignInButton utmParameters={{ content: \"topbar\" }}>\n              {({ isRegistry, link, offline, onSignIn }) => (\n                <Button\n                  data-testid=\"header-signin\"\n                  label=\"Sign in\"\n                  disabled={offline}\n                  {...(isRegistry ? { as: \"a\", href: link } : { onClick: onSignIn })}\n                />\n              )}\n            </SignInButton>\n          )}\n        </div>\n      </Tooltip>\n    )\n  )\n}\n\nexport default SignIn\n","import { Text, NavigationTab, Icon } from \"@netdata/netdata-ui\"\nimport React from \"react\"\n\nconst CloudTab = ({ label, active, showBorderLeft, icon, onActivate }) => {\n  const handleOnActivate = () => {\n    if (active) return\n    if (onActivate) onActivate()\n  }\n  return (\n    <NavigationTab\n      onActivate={handleOnActivate}\n      icon={<Icon name={icon} size=\"small\" />}\n      fixed\n      closable={false}\n      showBorderLeft={showBorderLeft}\n      active={active}\n    >\n      <Text>{label}</Text>\n    </NavigationTab>\n  )\n}\n\nexport default CloudTab\n","import React from \"react\"\nimport {\n  Flex,\n  Button,\n  Box,\n  Text,\n  H3,\n  H4,\n  Modal,\n  ModalContent,\n  ModalBody,\n  ModalHeader,\n  ModalCloseButton,\n  ModalFooter,\n  Icon,\n} from \"@netdata/netdata-ui\"\n\nimport GoToCloud from \"components/auth/signIn\"\n\nexport const TITLE = \"Discover the free benefits of Netdata Cloud\"\n\nconst DiscoverCloudModal = ({ closeModal, text, header, handleGoToCloud, image, video }) => {\n  return (\n    <Modal borderShadow backdrop={false}>\n      <ModalContent background=\"modalBackground\">\n        <ModalHeader>\n          <Flex gap={2}>\n            <Icon color=\"white\" name=\"netdata\" />\n            <H3 margin={[0]}>{TITLE}:</H3>\n          </Flex>\n\n          <ModalCloseButton onClose={closeModal} />\n        </ModalHeader>\n        <ModalBody>\n          <Flex column width={189} height={130}>\n            <Flex padding={[0, 0, 4, 0]} column gap={4}>\n              <Flex alignItems=\"center\">\n                <H4 margin={[0]}>{header}</H4>\n                <Box\n                  sx={{ marginLeft: \"auto\" }}\n                  data-testid=\"go-to-cloud-cta\"\n                  margin={[0, 2, 0, 0]}\n                  width={{ min: 40 }}\n                >\n                  <GoToCloud utmParameters={{ campaign: \"discover_cloud\" }}>\n                    {({ link }) => (\n                      <Box\n                        label={\n                          <Text textTransform=\"none\" strong color=\"panel\">\n                            Sign in to Netdata Cloud!\n                          </Text>\n                        }\n                        width=\"100%\"\n                        onClick={() => handleGoToCloud({ link })}\n                        as={Button}\n                        small\n                        data-ga={\"go-to-cloud-button\"}\n                        data-testid=\"cta1-button\"\n                      />\n                    )}\n                  </GoToCloud>\n                </Box>\n              </Flex>\n              {text()}\n            </Flex>\n            {image && (\n              <Flex height=\"auto\" width=\"100%\" overflow=\"hidden\">\n                <Box\n                  sx={{\n                    width: \"100%\",\n                    height: \"auto\",\n                  }}\n                  as=\"img\"\n                  src={image}\n                ></Box>\n              </Flex>\n            )}\n            {video && (\n              <Flex height=\"100%\" width=\"100%\">\n                <Box sx={{ width: \"100%\", height: \"100%\" }}>\n                  <iframe\n                    title={`discover-cloud-iframe}`}\n                    width=\"100%\"\n                    height=\"100%\"\n                    src={video}\n                    frameBorder=\"0\"\n                    allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\"\n                    allowFullScreen\n                  />\n                </Box>\n              </Flex>\n            )}\n          </Flex>\n        </ModalBody>\n        <ModalFooter></ModalFooter>\n      </ModalContent>\n    </Modal>\n  )\n}\n\nexport default DiscoverCloudModal\n","import { Text } from \"@netdata/netdata-ui\"\nimport Anchor from \"@/src/components/anchor\"\nimport React from \"react\"\n\nconst TabsContentText = ({ children }) => <Text fontSize=\"16px\">{children}</Text>\n\nexport const TabsContent = {\n  Home: {\n    id: \"Home\",\n    label: \"Home\",\n    header: \"Home\",\n    text: () => (\n      <TabsContentText>\n        The Home view in Netdata cloud provides summarized relevant information in an easily\n        digestible display. You can see information about your nodes, data collection and retention\n        stats, alerts, users and dashboards.\n      </TabsContentText>\n    ),\n    icon: \"room_home\",\n    image: \"images/home.png\",\n  },\n  nodeView: {\n    id: \"nodeView\",\n    label: \"Node View\",\n    header: \"Node View\",\n    text: () => (\n      <>\n        <TabsContentText>\n          The single node view you are currently using will of course be available on Netdata Cloud\n          as well. In addition, the charts and visualization on Netdata Cloud will be more flexible\n          and powerful for troubleshooting than what is available on the agent.\n        </TabsContentText>\n        <TabsContentText>\n          Netdata Cloud also comes with the Metric Correlations feature that lets you quickly find\n          metrics and charts related to a particular window of interest that you want to explore\n          further. By displaying the standard Netdata dashboard, filtered to show only charts that\n          are relevant to the window of interest, you can get to the root cause sooner.\n        </TabsContentText>\n      </>\n    ),\n    icon: \"nodes_hollow\",\n    image: \"images/nodeView.png\",\n  },\n  Overview: {\n    id: \"Overview\",\n    label: \"Overview\",\n    header: \"Overview\",\n    text: () => (\n      <>\n        <TabsContentText>\n          The Overview tab is a great way to monitor your infrastructure using Netdata Cloud. While\n          the interface might look similar to local dashboards served by an Agent, or even the\n          single-node dashboards in Netdata Cloud, Overview uses composite charts. These charts\n          display real-time aggregated metrics from all the nodes (or a filtered selection) in a\n          given War Room.\n        </TabsContentText>\n        <TabsContentText>\n          With Overview's composite charts, you can see your infrastructure from a single pane of\n          glass, discover trends or anomalies, then drill down by grouping metrics by node and\n          jumping to single-node dashboards for root cause analysis.\n        </TabsContentText>\n        <TabsContentText>\n          Here's an example of a composite chart visualizing Disk I/O bandwidth from 5 different\n          nodes in one chart.\n        </TabsContentText>\n      </>\n    ),\n    icon: \"room_overview\",\n    image: \"images/overview.png\",\n  },\n  Nodes: {\n    id: \"Nodes\",\n    label: \"Nodes\",\n    header: \"Nodes\",\n    text: () => (\n      <TabsContentText>\n        The Nodes view in Netdata Cloud lets you see and customize key metrics from any number of\n        Agent-monitored nodes and seamlessly navigate to any node's dashboard for troubleshooting\n        performance issues or anomalies using Netdata's highly-granular metrics.\n      </TabsContentText>\n    ),\n    icon: \"nodes_hollow\",\n    image: \"images/nodes.jpg\",\n  },\n  Dashboards: {\n    id: \"Dashboards\",\n    label: \"Dashboards\",\n    header: \"Dashboards\",\n    text: () => (\n      <TabsContentText>\n        With Netdata Cloud, you can build new dashboards that target your infrastructure's unique\n        needs. Put key metrics from any number of distributed systems in one place for a bird's eye\n        view of your infrastructure.\n      </TabsContentText>\n    ),\n    icon: \"dashboard\",\n    image: \"images/dashboards.png\",\n  },\n  Alerts: {\n    id: \"Alerts\",\n    label: \"Alerts\",\n    header: \"Alerts\",\n    text: () => (\n      <TabsContentText>\n        The Alerts view gives you a high level of availability and performance information for every\n        node you're monitoring with Netdata Cloud. It also offers an easy way to drill down into any\n        particular alert by taking the user to the dedicated alert view from where the user can run\n        metrics correlation or take further troubleshooting steps.\n      </TabsContentText>\n    ),\n    icon: \"alarm\",\n    image: \"images/alerts.jpg\",\n  },\n  Anomalies: {\n    id: \"Anomalies\",\n    label: \"Anomalies\",\n    header: \"Anomaies\",\n    text: () => (\n      <TabsContentText>\n        The Anomalies view on Netdata Cloud lets you quickly surface potentially anomalous metrics\n        and charts related to a particular highlight window of interest using Anomaly Advisor.\n        Anomalies are detected using per metric unsupervised machine learning running at the edge!\n      </TabsContentText>\n    ),\n    icon: \"anomaliesLens\",\n    video:\n      \"https://user-images.githubusercontent.com/24860547/165943403-1acb9759-7446-4704-8955-c566d04ad7ab.mp4\",\n  },\n  Pricing: {\n    id: \"Pricing\",\n    label: \"Pricing\",\n    header: \"Pricing\",\n    text: () => (\n      <TabsContentText>\n        Netdata Cloud’s distributed architecture—with processing occurring at the individual\n        nodes—enables us to add any number of users at marginal cost. Couple this with our upcoming\n        paid plan with added functionality for enterprise customers, and it means we can commit to\n        providing our current functionality for free, always.\n      </TabsContentText>\n    ),\n    image: \"images/pricing.png\",\n    icon: \"pricing\",\n  },\n  Privacy: {\n    id: \"Privacy\",\n    label: \"Privacy\",\n    header: \"Privacy\",\n    text: () => (\n      <>\n        <TabsContentText>\n          Data privacy is very important to us. We firmly believe that your data belongs to you.\n          This is why we don't store any metric data in Netdata Cloud.\n        </TabsContentText>\n        <TabsContentText>\n          Your local installations of the Netdata Agent form the basis for the Netdata Cloud. All\n          the data that you see in the web browser when using Netdata Cloud, is actually streamed\n          directly from the Netdata Agent to the Netdata Cloud dashboard. The data passes through\n          our systems, but it isn't stored. You can learn more about{\" \"}\n          <Anchor\n            target=\"_blank\"\n            rel=\"noopener noreferrer\"\n            href=\"https://learn.netdata.cloud/docs/agent/netdata-security\"\n          >\n            the Agent's security design\n          </Anchor>{\" \"}\n          design in the Agent documentation.\n        </TabsContentText>\n        <TabsContentText>\n          However, to be able to offer the stunning visualizations and advanced functionality of\n          Netdata Cloud, it does store a limited number of metadata. This metadata is ONLY available\n          to Netdata and NEVER to any 3rd parties. You can learn more about what metadata is stored\n          in Netdata cloud in our\n          <Anchor\n            target=\"_blank\"\n            rel=\"noopener noreferrer\"\n            href=\"https://learn.netdata.cloud/docs/cloud/data-privacy\"\n          >\n            {\" \"}\n            documentation\n          </Anchor>\n        </TabsContentText>\n      </>\n    ),\n    icon: \"privacy\",\n  },\n}\n","import React from \"react\"\nimport {\n  Text,\n  Drop,\n  ModalContent,\n  ModalBody,\n  ModalHeader,\n  ModalCloseButton,\n  ModalFooter,\n  Flex,\n  Button,\n  Box,\n  H3,\n  H4,\n  Icon,\n} from \"@netdata/netdata-ui\"\n\nimport GoToCloud from \"components/auth/signIn\"\n\nexport const TITLE = \"Discover the free benefits of Netdata Cloud\"\n\nconst DiscoverCloudDrop = ({\n  parentRef,\n  isDropdownOpen,\n  closeDropdown,\n  text,\n  header,\n  handleGoToCloud,\n  image,\n  video,\n}) => {\n  if (parentRef.current && isDropdownOpen)\n    return (\n      <Drop\n        backdrop\n        data-testid=\"selectedNodesDropdown\"\n        onEsc={closeDropdown}\n        align={{ top: \"bottom\", left: \"left\" }}\n        target={parentRef.current}\n        onClickOutside={closeDropdown}\n      >\n        <ModalContent background=\"modalBackground\">\n          <ModalHeader>\n            <Flex gap={2}>\n              <Icon color=\"white\" name=\"netdata\" />\n              <H3 margin={[0]}>{TITLE}:</H3>\n            </Flex>\n\n            <ModalCloseButton onClose={closeDropdown} />\n          </ModalHeader>\n          <ModalBody>\n            <Flex column width={189} height={130}>\n              <Flex padding={[0, 0, 4, 0]} column gap={4}>\n                <Flex alignItems=\"center\">\n                  <H4 margin={[0]}>{header}</H4>\n                  <Box\n                    sx={{ marginLeft: \"auto\" }}\n                    data-testid=\"go-to-cloud-cta\"\n                    margin={[0, 2, 0, 0]}\n                    width={{ min: 40 }}\n                  >\n                    <GoToCloud utmParameters={{ campaign: \"discover_cloud\" }}>\n                      {({ link }) => (\n                        <Box\n                          label={\n                            <Text textTransform=\"none\" strong color=\"panel\">\n                              Sign in to Netdata Cloud!\n                            </Text>\n                          }\n                          width=\"100%\"\n                          onClick={() => handleGoToCloud({ link })}\n                          data-testid=\"cta1-button\"\n                          as={Button}\n                          small\n                          data-ga={\"go-to-cloud-button\"}\n                        />\n                      )}\n                    </GoToCloud>\n                  </Box>\n                </Flex>\n                {text()}\n              </Flex>\n              {image && (\n                <Flex height=\"auto\" width=\"100%\" overflow=\"hidden\">\n                  <Box\n                    sx={{\n                      width: \"100%\",\n                      height: \"auto\",\n                    }}\n                    as=\"img\"\n                    src={image}\n                  ></Box>\n                </Flex>\n              )}\n              {video && (\n                <Flex height=\"100%\" width=\"100%\">\n                  <Box sx={{ width: \"100%\", height: \"100%\" }}>\n                    <iframe\n                      title={`discover-cloud-iframe}`}\n                      width=\"100%\"\n                      height=\"100%\"\n                      src={video}\n                      frameBorder=\"0\"\n                      allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\"\n                      allowFullScreen\n                    />\n                  </Box>\n                </Flex>\n              )}\n            </Flex>\n          </ModalBody>\n          <ModalFooter></ModalFooter>\n        </ModalContent>\n      </Drop>\n    )\n\n  return null\n}\n\nexport default DiscoverCloudDrop\n","import React, { useState, useRef } from \"react\"\nimport { Text, Flex, NavigationTabs } from \"@netdata/netdata-ui\"\n\nimport CloudTab from \"./cloudTab\"\nimport { TITLE } from \"./discoverCloudModal\"\n\nimport { callAll } from \"@/src/utils/utils\"\nimport { TabsContent } from \"./contants\"\n\nimport DiscoverCloudDrop from \"./discoverCloudDrop\"\n\nconst Wrapper = Flex\nconst InnerPostioner = Flex\n\nconst DiscoverCloud = () => {\n  const [isModalOpen, setIsModalOpen] = useState(false)\n  const [seletedModalContent, setSelectedModalContent] = useState(null)\n  const dropDownParentRef = useRef()\n\n  const handleOpenModal = () => {\n    setIsModalOpen(true)\n  }\n\n  const handleCloseModal = () => {\n    setIsModalOpen(false)\n  }\n\n  const handleGoToCloud = ({ link }) => {\n    window.location.href = link\n  }\n\n  const handleSetModalContent = content => {\n    setSelectedModalContent(content)\n  }\n  const handleResetModalContent = () => {\n    setSelectedModalContent(null)\n  }\n\n  return (\n    <Wrapper padding={[0, 0, 0, 4]} position=\"relative\" height={15}>\n      <InnerPostioner\n        padding={[4, 2, 4, 2]}\n        gap={4}\n        height=\"100%\"\n        justifyContent=\"center\"\n        alignItems=\"center\"\n      >\n        <Text color=\"primary\">{TITLE}:</Text>\n        <Flex ref={dropDownParentRef}>\n          <NavigationTabs>\n            {Object.keys(TabsContent).map((key, index) => {\n              const { label, icon, id } = TabsContent[key]\n              const slectedContentId = seletedModalContent ? seletedModalContent.id : null\n              return (\n                <CloudTab\n                  key={key}\n                  icon={icon}\n                  active={id === slectedContentId}\n                  label={label}\n                  showBorderLeft={index === 0}\n                  onActivate={callAll(handleOpenModal, () =>\n                    handleSetModalContent(TabsContent[key])\n                  )}\n                />\n              )\n            })}\n          </NavigationTabs>\n        </Flex>\n      </InnerPostioner>\n      <DiscoverCloudDrop\n        parentRef={dropDownParentRef}\n        isDropdownOpen={isModalOpen}\n        {...seletedModalContent}\n        closeDropdown={callAll(handleCloseModal, handleResetModalContent)}\n        handleGoToCloud={handleGoToCloud}\n      />\n      {/* {isModalOpen && seletedModalContent && (\n        <DiscoverCloudModal\n          {...seletedModalContent}\n          closeModal={callAll(handleCloseModal, handleResetModalContent)}\n          handleGoToCloud={handleGoToCloud}\n        />\n      )} */}\n    </Wrapper>\n  )\n}\n\nexport default DiscoverCloud\n","import React from \"react\"\nimport styled from \"styled-components\"\nimport { Flex, Box } from \"@netdata/netdata-ui\"\nimport Node from \"./node\"\nimport Options from \"./options\"\nimport Version from \"./version\"\nimport GlobalControls from \"./globalControls\"\nimport Alarms from \"./alarms\"\nimport News from \"./news\"\nimport Timezone from \"./timezone\"\nimport SignIn from \"./signIn\"\nimport { CloudConnectionStatus } from \"./ACLK\"\nimport { DiscoverCloud } from \"@/src/components/discover-cloud\"\nimport { selectIsCloudEnabled } from \"domains/global/selectors\"\nimport { useSelector } from \"react-redux\"\n\nconst Wrapper = styled(Flex).attrs({\n  as: \"header\",\n  position: \"relative\",\n  justifyContent: \"between\",\n  background: \"panel\",\n  zIndex: 20,\n  width: \"100%\",\n  padding: [2, 4, 2, 4],\n})`\n  pointer-events: all;\n`\n\nconst Header = () => {\nconst cloudEnabled = useSelector(selectIsCloudEnabled)\n\n return <Wrapper>\n  <Flex alignItems=\"center\" gap={3}>\n    <Node />\n  </Flex>\n  <Flex justifyContent=\"end\" alignItems=\"center\" gap={3}>\n    <CloudConnectionStatus />\n    <Version />\n    <News />\n    <Options />\n    <Timezone />\n    <GlobalControls />\n    <Alarms />\n    <SignIn />\n  </Flex>\n {cloudEnabled&& <Box sx={{ background: \"#272B30\" }} position=\"absolute\" top=\"52px\" left=\"0px\" right=\"0px\">\n    <DiscoverCloud />\n  </Box>} \n</Wrapper>\n}\n\n\n\nexport default Header\n","import styled from \"styled-components\"\nimport { Button } from \"@netdata/netdata-ui\"\n\nconst ExpandButton = styled(Button)`\n&& {\n  > .button-icon {\n    width: 6px;\n    height: 9px;\n  }\n`\nexport default ExpandButton\n","import React from \"react\"\nimport { useSelector } from \"react-redux\"\nimport { Flex } from \"@netdata/netdata-ui\"\nimport { selectCloudBaseUrl } from \"domains/global/selectors\"\nimport { getIframeSrc } from \"@/src/utils\"\n\n\nconst SignOut = ({ flavour = \"default\", ...rest }) => {\n  const cloudBaseURL = useSelector(selectCloudBaseUrl)\n\n  return (\n    <Flex\n      alignItems=\"center\"\n      as=\"iframe\"\n      src={`${getIframeSrc(cloudBaseURL, \"sign-out\")}?type=${flavour}`}\n      border={{ side: \"all\", size: \"0px\" }}\n      width={{ max: \"128px\" }}\n      height={{ max: \"40px\" }}\n      {...rest}\n    />\n  )\n}\n\nexport default SignOut\n","import React, { useMemo } from \"react\"\nimport { ThemeProvider } from \"styled-components\"\nimport { createSelector } from \"reselect\"\nimport { useToggle } from \"react-use\"\nimport { Flex, Button, DarkTheme, Text, Layer } from \"@netdata/netdata-ui\"\nimport { useSelector } from \"store/redux-separate-context\"\nimport { MenuItem } from \"components/menus\"\nimport SignOut from \"components/auth/signOut\"\nimport SignIn from \"components/auth/signIn\"\n\nconst SignInItem = () => {\n  const onClick = (e, link) => {\n    e.stopPropagation()\n    window.location.href = link\n  }\n  return (\n    <SignIn utmParameters={{ content: \"userSettings\" }}>\n      {({ isRegistry, link, onSignIn }) => (\n        <Text onClick={isRegistry ? e => onClick(e, link) : onSignIn}>Sign in</Text>\n      )}\n    </SignIn>\n  )\n}\n\nconst isSignedInSelector = createSelector(\n  ({ dashboard }) => dashboard,\n  ({ isSignedIn }) => isSignedIn\n)\n\nconst UserSettings = () => {\n  const [isOpen, toggle] = useToggle()\n  const signedIn = useSelector(isSignedInSelector)\n\n  const menuItems = useMemo(\n    () => [\n      ...(signedIn\n        ? [\n            {\n              children: \"Operational Status\",\n              onClick: () =>\n                window.open(\"https://status.netdata.cloud\", \"_blank\", \"noopener,noreferrer\"),\n            },\n          ]\n        : []),\n      ...(signedIn ? [{ separator: true }] : []),\n      ...(signedIn\n        ? [\n            {\n              children: (\n                <SignOut\n                  data-testid=\"signout-button-sidebar\"\n                  flavour=\"borderless\"\n                  height={{ max: \"18px\" }}\n                />\n              ),\n            },\n          ]\n        : [{ children: <SignInItem /> }]),\n    ],\n    [signedIn]\n  )\n\n  return (\n    <ThemeProvider theme={DarkTheme}>\n      <Button\n        data-testid=\"avatar-button-sidebar\"\n        flavour=\"borderless\"\n        neutral\n        icon=\"user\"\n        title=\"User settings\"\n        name=\"userSettings\"\n        onClick={toggle}\n      />\n      {isOpen && (\n        <Layer\n          position=\"bottom-left\"\n          onClickOutside={toggle}\n          onEsc={toggle}\n          backdrop={false}\n          margin={[5, 18]}\n        >\n          <Flex column width={52} background=\"mainBackground\" padding={[3]} round>\n            {menuItems.map((item, i) => {\n              if (item.separator) return <Flex height=\"1px\" background=\"disabled\" key={i} />\n              return (\n                <MenuItem\n                  key={i}\n                  padding={[2, 4]}\n                  round={1}\n                  {...(item.onClick && { onClick: item.onClick })}\n                >\n                  {item.children}\n                </MenuItem>\n              )\n            })}\n          </Flex>\n        </Layer>\n      )}\n    </ThemeProvider>\n  )\n}\n\nexport default UserSettings\n","import React from \"react\"\nimport { Flex, Button } from \"@netdata/netdata-ui\"\n\nconst SpacesSkeleton = () => (\n  <React.Fragment>\n    <Flex\n      width=\"40px\"\n      height=\"40px\"\n      round={2}\n      border={{ side: \"all\", color: \"border\", size: \"2px\", type: \"dotted\" }}\n    />\n    <Flex height=\"1px\" background=\"separator\" width=\"20px\" />\n    <Button icon=\"plus\" disabled />\n  </React.Fragment>\n)\n\nexport default SpacesSkeleton\n","import React from \"react\"\nimport { Flex } from \"@netdata/netdata-ui\"\nimport { useSelector } from \"store/redux-separate-context\"\nimport { selectCloudBaseUrl } from \"domains/global/selectors\"\nimport { getIframeSrc } from \"utils/utils\"\n\nconst SpacesIframe = () => {\n  const cloudBaseURL = useSelector(selectCloudBaseUrl)\n  return (\n    <Flex\n      as=\"iframe\"\n      src={getIframeSrc(cloudBaseURL, \"space-bar\")}\n      title=\"Space Bar\"\n      height=\"100%\"\n      width=\"100%\"\n      border={{ side: \"all\", size: \"0px\" }}\n      overflow=\"hidden\"\n    />\n  )\n}\n\nexport default SpacesIframe\n","import React from \"react\"\nimport { useSelector } from \"react-redux\"\nimport { Icon, Flex, Button, Documentation } from \"@netdata/netdata-ui\"\nimport { selectIsCloudEnabled } from \"domains/global/selectors\"\nimport ExpandButton from \"./expandButton\"\nimport UserSettings from \"./userSettings\"\nimport SpacesSkeleton from \"./spacesSkeleton\"\nimport SpacesIframe from \"./spacesIframe\"\n\nconst Spaces = ({ isOpen, toggle, isSignedIn }) => {\n  const cloudEnabled = useSelector(selectIsCloudEnabled)\n\n  return (\n    <Flex\n      column\n      justifyContent=\"between\"\n      background=\"panel\"\n      padding={[3, 0]}\n      width=\"64px\"\n      alignItems=\"center\"\n      gap={6}\n      position=\"relative\"\n      overflow=\"hidden\"\n    >\n      <Flex column gap={4} alignItems=\"center\" height=\"100%\" overflow=\"hidden\">\n        <Icon color=\"success\" name=\"netdataPress\" height=\"32px\" width=\"32px\" />\n        {!isOpen && (\n          <ExpandButton\n            icon=\"chevron_right_s\"\n            onClick={toggle}\n            small\n            neutral\n            flavour=\"borderless\"\n            themeType=\"dark\"\n          />\n        )}\n        {cloudEnabled && isSignedIn && <SpacesIframe />}\n        {cloudEnabled && !isSignedIn && <SpacesSkeleton />}\n      </Flex>\n      <Flex column gap={4} alignItems=\"center\">\n        <Documentation app=\"agent\">\n          {toggle => (\n            <Button\n              flavour=\"borderless\"\n              neutral\n              themeType=\"dark\"\n              className=\"btn\"\n              icon=\"question\"\n              onClick={toggle}\n              title=\"Need help?\"\n            />\n          )}\n        </Documentation>\n        <Button\n          flavour=\"borderless\"\n          neutral\n          themeType=\"dark\"\n          className=\"btn\"\n          data-toggle=\"modal\"\n          data-target=\"#optionsModal\"\n          icon=\"gear\"\n          title=\"Settings\"\n        />\n        {cloudEnabled && <UserSettings />}\n      </Flex>\n    </Flex>\n  )\n}\n\nexport default Spaces\n","// some old functions here, i don't have time to comprehend them now\n\n// --------------------------------------------------------------------\n// natural sorting\n// http://www.davekoelle.com/files/alphanum.js - LGPL\n\nconst naturalSortChunkify = (t: string) => {\n  const tz = []\n  let x = 0\n  let y = -1; let n = 0 as boolean | number; let i; let j\n\n  // eslint-disable-next-line no-cond-assign,no-plusplus\n  while (i = (j = t.charAt(x++)).charCodeAt(0)) {\n    const m = (i >= 48 && i <= 57)\n    if (m !== n) {\n      // eslint-disable-next-line no-plusplus\n      tz[++y] = \"\"\n      n = m\n    }\n    tz[y] += j\n  }\n\n  return tz\n}\n\n\nexport const naturalSortCompare = (a: string, b: string) => {\n  const aa = naturalSortChunkify(a.toLowerCase())\n  const bb = naturalSortChunkify(b.toLowerCase())\n\n  // eslint-disable-next-line no-plusplus\n  for (let x = 0; aa[x] && bb[x]; x++) {\n    if (aa[x] !== bb[x]) {\n      const c = Number(aa[x]); const\n        d = Number(bb[x])\n      if (c.toString() === aa[x] && d.toString() === bb[x]) {\n        return c - d\n      }\n      return (aa[x] > bb[x]) ? 1 : -1\n    }\n  }\n  return aa.length - bb.length\n}\n\n\ninterface ObjectsWithPriority {\n  [key: string]: {\n    priority: number\n  }\n}\nexport const sortObjectByPriority = <T extends ObjectsWithPriority>(object: T) => {\n  const sorted = Object.keys(object)\n\n  sorted.sort((a, b) => {\n    if (object[a].priority < object[b].priority) {\n      return -1\n    }\n    if (object[a].priority > object[b].priority) {\n      return 1\n    }\n    return naturalSortCompare(a, b)\n  })\n\n  return sorted\n}\n\n\ninterface PrioritySortObject {\n  name: string\n  priority: number\n}\nexport const prioritySort = <T extends PrioritySortObject>(a: T, b: T) => {\n  if (a.priority < b.priority) {\n    return -1\n  }\n  if (a.priority > b.priority) {\n    return 1\n  }\n  return naturalSortCompare(a.name, b.name)\n}\n","import { naturalSortCompare } from \"domains/dashboard/utils/sorting\"\n\nconst getBaseUrl = hostname => {\n  let base = document.location.origin.toString() + decodeURI(document.location.pathname.toString())\n  if (base.endsWith(`/host/${hostname}/`)) {\n    base = base.substring(0, base.length - `/host/${hostname}/`.toString().length)\n  }\n\n  if (base.endsWith(\"/\")) {\n    base = base.substring(0, base.length - 1)\n  }\n\n  return base\n}\n\nconst getNodeUrl = (baseUrl, hostname) => `${baseUrl}/host/${hostname}/`\n\nconst getNodes = (hosts, hostname, hostsStatus) => {\n  if (!hosts || !hostname) return {}\n\n  // decodeURI, because pathname (which is hostname) can contain white-spaces\n  // or other characters which are URIencoded when user clicks the link\n  // and we need to match it with `chartsMetadata.hostname`\n  const baseUrl = getBaseUrl(hostname)\n\n  const [{ hostname: parentNodeHostname }] = hosts\n\n  const parentNode = {\n    hostname: parentNodeHostname,\n    url: `${baseUrl}/`,\n  }\n\n  const replicatedNodes = hosts\n    .slice(1)\n    .map(({ hostname }, index) => ({\n      hostname,\n      url: getNodeUrl(baseUrl, hostname),\n      status: hostsStatus.find(host => host.hostname === hostname)?.reachable || false,\n    }))\n    .sort((a, b) => naturalSortCompare(a.hostname, b.hostname))\n\n  return {\n    parentNode,\n    replicatedNodes,\n  }\n}\n\nexport default getNodes\n","import styled from \"styled-components\"\nimport { Flex } from \"@netdata/netdata-ui\"\n\nconst Anchor = styled(Flex).attrs({\n  as: \"a\",\n  gap: 2,\n  alignItems: \"center\",\n})`\n  & :hover {\n    text-decoration: none;\n  }\n`\n\nexport default Anchor\n","import React from \"react\"\nimport { Flex, Text, Icon } from \"@netdata/netdata-ui\"\nimport Pill from \"components/header/pill\"\nimport Anchor from \"./anchor\"\n\nconst Node = ({ hostname, url, status }) => {\n  return (\n    <Anchor href={url} justifyContent=\"between\" padding={[0, 0, 0, 2]}>\n      <Flex alignItems=\"center\" gap={2}>\n        <Icon name=\"node\" color=\"bright\" />\n        <Text color=\"bright\" truncate>\n          {hostname}\n        </Text>\n      </Flex>\n      <Pill background={status ? \"success\" : \"border\"} color=\"bright\" round={10}>\n        {status ? \"Live\" : \"Off\"}\n      </Pill>\n    </Anchor>\n  )\n}\n\nexport default Node\n","import React, { useState, useCallback, useMemo } from \"react\"\nimport styled from \"styled-components\"\nimport { Flex, Text, Icon, TextInput } from \"@netdata/netdata-ui\"\nimport { MenuList } from \"components/menus\"\nimport Anchor from \"./anchor\"\nimport Node from \"./node\"\n\nconst Search = styled(TextInput)`\n  & > label {\n    margin-bottom: 0;\n  }\n`\n\nconst StyledIcon = styled(Icon)`\n  transform: ${({ right }) => (right ? \"rotate(270deg)\" : \"none\")};\n`\n\nconst ReplicatedNodes = ({ parentNode, replicatedNodes }) => {\n  const [listOpen, setListOpen] = useState(true)\n  const [value, setValue] = useState(\"\")\n\n  const toggleListOpen = useCallback(() => setListOpen(o => !o), [])\n  const onChange = useCallback(e => setValue(e.target.value), [])\n\n  const nodes = useMemo(() => {\n    if (!value) return replicatedNodes\n    return replicatedNodes.filter(({ hostname }) =>\n      hostname.toLowerCase().includes(value.toLowerCase())\n    )\n  }, [replicatedNodes, value])\n\n  return (\n    <MenuList\n      isOpen={listOpen}\n      toggleOpen={toggleListOpen}\n      label={\n        <Flex alignItems=\"center\" justifyContent=\"between\">\n          <Text strong color=\"border\">\n            Replicated nodes\n          </Text>\n          <StyledIcon right={!listOpen} name=\"chevron_down\" size=\"small\" color=\"text\" />\n        </Flex>\n      }\n    >\n      <Flex column gap={4} padding={[4, 0, 0]}>\n        <Anchor as=\"a\" href={parentNode.url} justifyContent=\"start\">\n          <Icon name=\"nodes\" size=\"small\" color=\"bright\" />\n          <Text color=\"bright\">{parentNode.hostname}</Text>\n        </Anchor>\n        {nodes.length >= 5 && (\n          <Flex padding={[0, 0, 0, 2]}>\n            <Search\n              value={value}\n              onChange={onChange}\n              iconLeft={<Icon name=\"search_s\" size=\"small\" color=\"text\" />}\n              metaShrinked\n            />\n          </Flex>\n        )}\n        <Flex column gap={2}>\n          {nodes.map(({ hostname, url, status }) => (\n            <Node key={hostname} hostname={hostname} url={url} status={status} />\n          ))}\n        </Flex>\n      </Flex>\n    </MenuList>\n  )\n}\n\nexport default ReplicatedNodes\n","import React, { useRef, useEffect } from \"react\"\nimport { Flex } from \"@netdata/netdata-ui\"\nimport { useSelector } from \"store/redux-separate-context\"\nimport { sendToChildIframe, useListenToPostMessage } from \"@/src/utils/post-message\"\nimport { getIframeSrc } from \"@/src/utils\"\nimport { selectCloudBaseUrl } from \"domains/global/selectors\"\n\nconst SpacePanelIframe = ({ parentNode, replicatedNodes }) => {\n  const cloudBaseURL = useSelector(selectCloudBaseUrl)\n\n  const ref = useRef()\n\n  const [spacePanelMessage] = useListenToPostMessage(\"hello-from-space-panel\")\n\n  useEffect(() => {\n    if (!spacePanelMessage || !ref.current) return\n    sendToChildIframe(ref.current, {\n      type: \"streamed-hosts-data\",\n      payload: { parentNode, replicatedNodes },\n    })\n  }, [replicatedNodes, parentNode, spacePanelMessage])\n\n  return (\n    <Flex\n      ref={ref}\n      as=\"iframe\"\n      src={getIframeSrc(cloudBaseURL, \"space-panel\")}\n      title=\"space panel\"\n      width=\"100%\"\n      height=\"100%\"\n      border={{ side: \"all\", size: \"0px\" }}\n    />\n  )\n}\n\nexport default SpacePanelIframe\n","import React from \"react\"\nimport { TextSmall } from \"@netdata/netdata-ui\"\n\nconst promptContent = {\n  signIn: {\n    title: \"Welcome back!\",\n    content: [\n      <TextSmall key=\"1\" color=\"bright\">\n        Sign in again to enjoy the benefits of Netdata Cloud{\" \"}\n      </TextSmall>,\n    ],\n  },\n  signUp: {\n    title: \"Welcome to Netdata Cloud!\",\n    content: [\n      <TextSmall key=\"1\" color=\"bright\">\n        <TextSmall strong color=\"bright\">\n          A single place\n        </TextSmall>{\" \"}\n        for all your nodes.\n      </TextSmall>,\n      <TextSmall key=\"2\" color=\"bright\">\n        <TextSmall strong color=\"bright\">\n          Multi-node dashboards\n        </TextSmall>{\" \"}\n        out of the box.\n      </TextSmall>,\n      <TextSmall key=\"3\" color=\"bright\">\n        <TextSmall strong color=\"bright\">\n          Custom dashboards\n        </TextSmall>{\" \"}\n        for you to create, edit and share online.\n      </TextSmall>,\n      <TextSmall key=\"4\" color=\"bright\">\n        <TextSmall strong color=\"bright\">\n          Metric Correlations\n        </TextSmall>{\" \"}\n        to find the root cause of anything.\n      </TextSmall>,\n      <TextSmall key=\"5\" color=\"bright\">\n        <TextSmall strong color=\"bright\">\n          Centrally dispatched notifications\n        </TextSmall>{\" \"}\n        for all alarms of all your nodes.\n      </TextSmall>,\n      <TextSmall key=\"6\" color=\"bright\">\n        And... It is{\" \"}\n        <TextSmall\n          as=\"a\"\n          href=\"https://www.netdata.cloud/get-netdata/\"\n          target=\"_blank\"\n          rel=\"noopener noreferrer\"\n          strong\n          color=\"bright\"\n        >\n          free, forever!\n        </TextSmall>\n      </TextSmall>,\n    ],\n  },\n}\n\nexport default promptContent\n","import React from \"react\"\nimport { Flex, Text, Button } from \"@netdata/netdata-ui\"\nimport SignIn from \"@/src/components/auth/signIn\"\nimport promptContent from \"./promptContent\"\n\nconst SignInPrompt = () => {\n  return (\n    <SignIn utmParameters={{ content: \"sidebar\" }}>\n      {({ isRegistry, link, onSignIn, offline }) => {\n        const { title, content } = promptContent[\"signIn\"]\n        return (\n          <Flex\n            background={[\"neutral\", \"regentgrey\"]}\n            column\n            gap={4}\n            padding={[10]}\n            border={{ side: \"right\", color: \"panel\" }}\n          >\n            <Text color=\"bright\" strong>\n              {title}\n            </Text>\n            {content.map(el => el)}\n            <Button\n              width=\"100%\"\n              label=\"Sign in\"\n              disabled={offline}\n              {...(isRegistry ? { as: \"a\", href: link } : { onClick: onSignIn })}\n            />\n          </Flex>\n        )\n      }}\n    </SignIn>\n  )\n}\n\nexport default SignInPrompt\n","import React from \"react\"\n\nconst NoNetwork = () => (\n  <svg width=\"68\" height=\"68\" viewBox=\"0 0 68 68\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n    <path\n      d=\"M48.875 6.375H19.125C16.7778 6.375 14.875 8.27779 14.875 10.625V40.375C14.875 42.7222 16.7778 44.625 19.125 44.625H48.875C51.2222 44.625 53.125 42.7222 53.125 40.375V10.625C53.125 8.27779 51.2222 6.375 48.875 6.375Z\"\n      fill=\"white\"\n      stroke=\"#AEB3B7\"\n    />\n    <path\n      fillRule=\"evenodd\"\n      clipRule=\"evenodd\"\n      d=\"M41.0834 38.25C41.8658 38.25 42.5 38.8843 42.5 39.6667V41.0833C44.0648 41.0833 45.3334 42.3519 45.3334 43.9167V58.0833C45.3334 59.6481 44.0648 60.9167 42.5 60.9167H38.25V65.1667H31.8278V60.9167H26.9167C25.3519 60.9167 24.0834 59.6481 24.0834 58.0833V43.9167C24.0834 42.3519 25.3519 41.0833 26.9167 41.0833V39.6667C26.9167 38.8843 27.551 38.25 28.3334 38.25H41.0834Z\"\n      fill=\"#35414A\"\n    />\n    <path\n      fillRule=\"evenodd\"\n      clipRule=\"evenodd\"\n      d=\"M39.7954 12.75C40.5778 12.75 41.2121 13.3843 41.2121 14.1667L41.2108 16.7294L43.9166 16.7296C44.699 16.7296 45.3333 17.3639 45.3333 18.1463V34C45.3333 34.7824 44.699 35.4167 43.9166 35.4167L43.272 35.4152L43.2727 33.3403H41.2121L41.2108 35.4152H39.151L39.1515 33.3403H37.0909L37.0897 35.4152H35.0299L35.0303 33.3403H32.9697L32.9686 35.4152H30.9088L30.909 33.3403H28.8484L28.8475 35.4152H26.7877L26.7878 33.3403H24.7272L24.7265 35.4152L24.0833 35.4167C23.3009 35.4167 22.6666 34.7824 22.6666 34V18.1463C22.6666 17.3639 23.3009 16.7296 24.0833 16.7296L26.7877 16.7294L26.7878 14.1667C26.7878 13.3843 27.4221 12.75 28.2045 12.75H39.7954Z\"\n      fill=\"#35414A\"\n    />\n  </svg>\n)\n\nexport default NoNetwork\n","import React from \"react\"\nimport { Flex, TextNano, TextSmall } from \"@netdata/netdata-ui\"\nimport NoNetwork from \"./noNetwork\"\n\nconst OfflinePrompt = () => (\n  <Flex alignItems=\"center\" background={[\"neutral\", \"regentgrey\"]} column gap={1} padding={[10]}>\n    <TextSmall color=\"bright\" strong textAlign=\"center\">\n      Can't connect to Netdata Cloud\n    </TextSmall>\n    <NoNetwork />\n    <TextNano color=\"bright\" textAlign=\"center\" margin={[2, 0, 0]}>\n      Maybe you are behind a firewall or you don’t have connection to the internet\n    </TextNano>\n  </Flex>\n)\n\nexport default OfflinePrompt\n","import styled from \"styled-components\"\nimport { getSizeBy, getColor, Icon, TextNano, Text } from \"@netdata/netdata-ui\"\n\nexport const NodesContainer = styled.div`\n  .mdc-list-item {\n    padding: 0 0;\n    padding-left: 0;\n  }\n  .rmwc-collapsible-list__children {\n    .mdc-list-item {\n      padding: 0 0;\n      padding-left: 0;\n      height: ${getSizeBy(4)};\n    }\n  }\n  .rmwc-collapsible-list__handle {\n    .mdc-list-item {\n      padding: 0 ${getSizeBy(2)};\n    }\n  }\n  .mdc-list-item__meta {\n    color: ${getColor(\"bright\")};\n  }\n  .mdc-list-item:before {\n    background: none;\n  }\n`\n\nexport const ListItem = styled.div`\n  width: 100%;\n  min-height: ${getSizeBy(3)};\n  display: flex;\n  flex-flow: row nowrap;\n  align-items: center;\n  cursor: pointer;\n  justify-content: space-between;\n`\n\nexport const TrashIcon = styled(Icon)`\n  fill: #35414a;\n  margin-right: ${getSizeBy(2)};\n  transition: opacity 0.4s ease-in;\n  &:hover {\n    opacity: 0.6;\n  }\n`\n\nexport const StyledIcon = styled(Icon)`\n  flex-shrink: 0;\n  flex-grow: 0;\n  margin-right: ${getSizeBy(2)};\n  fill: ${getColor([\"gray\", \"arsenic\"])};\n`\n\n// @ts-ignore todo extend interface in dashboard due to lack of types in netdata-ui\nexport const NodeUrl = styled(TextNano.withComponent(\"a\"))`\n  text-decoration: none;\n  margin-left: ${getSizeBy(5)};\n  color: #aeb3b7;\n  max-width: 145px;\n  word-wrap: break-word;\n  &:hover {\n    color: inherit; // overwrite bootstrap\n    text-decoration: none;\n  }\n`\n\n// @ts-ignore todo\nexport const NodeName = styled(Text.withComponent(\"a\"))`\n  flex: 1;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  min-width: 0;\n  white-space: nowrap;\n`\n","import React, { useCallback, useState } from \"react\"\nimport { CollapsibleList, SimpleListItem } from \"@rmwc/list\"\nimport \"@material/list/dist/mdc.list.css\"\nimport \"@rmwc/list/collapsible-list.css\"\nimport \"@rmwc/icon/icon.css\"\nimport { Box, Flex, Text } from \"@netdata/netdata-ui\"\n\nimport truncateMiddle from \"utils/truncateMiddle\"\nimport { naturalSortCompare } from \"domains/dashboard/utils/sorting\"\nimport { MASKED_DATA } from \"domains/global/constants\"\nimport { MenuList } from \"components/menus\"\n\nimport { NodesContainer, ListItem, StyledIcon, NodeUrl, NodeName, TrashIcon } from \"./styled\"\n\nconst Node = ({ name, alternateUrls, machineGuid }) => (\n  <CollapsibleList\n    handle={\n      <SimpleListItem\n        text={\n          <>\n            <StyledIcon name=\"node\" />\n            <NodeName\n              color=\"bright\"\n              href=\"\"\n              onClick={event => {\n                event.preventDefault() // prevent navigating to url\n                event.stopPropagation()\n                window.gotoServerModalHandler(machineGuid)\n              }}\n            >\n              {name}\n            </NodeName>\n          </>\n        }\n        metaIcon={alternateUrls.length && \"chevron_right\"}\n      />\n    }\n  >\n    <Box margin={[2, 0, 0]}>\n      {alternateUrls.map(url => (\n        <ListItem key={url}>\n          <NodeUrl href={url}>{truncateMiddle(url, 50)}</NodeUrl>\n          <TrashIcon\n            name=\"trashcan\"\n            size=\"small\"\n            onClick={() => {\n              window.deleteRegistryModalHandler(machineGuid, name, url)\n            }}\n          />\n        </ListItem>\n      ))}\n    </Box>\n  </CollapsibleList>\n)\n\nexport const VisitedNodes = ({ machinesArray }) => {\n  const sortedMachines = machinesArray\n    .sort((a, b) => naturalSortCompare(a.name, b.name))\n    .filter(({ url }) => url !== MASKED_DATA)\n\n  const [listOpen, setListOpen] = useState(true)\n  const toggleListOpen = useCallback(() => setListOpen(o => !o), [])\n\n  return (\n    <MenuList\n      isOpen={listOpen}\n      toggleOpen={toggleListOpen}\n      label={\n        <Flex alignItems=\"center\" justifyContent=\"between\">\n          <Text strong color=\"border\">\n            Visited Nodes\n          </Text>\n          <StyledIcon right={!listOpen} name=\"chevron_down\" size=\"small\" color=\"text\" />\n        </Flex>\n      }\n    >\n      <NodesContainer column gap={2}>\n        {sortedMachines.map(({ name, alternateUrls, guid, url }) => (\n          <Node\n            alternateUrls={alternateUrls}\n            key={`${name}-${guid}`}\n            machineGuid={guid}\n            name={name}\n            url={url}\n          />\n        ))}\n      </NodesContainer>\n    </MenuList>\n  )\n}\n\nexport default VisitedNodes\n","export default (text, maxLength) => {\n  if (text.length <= maxLength) return text\n\n  const spanLength = Math.floor((maxLength - 3) / 2)\n  return `${text.substring(0, spanLength)}...${text.substring(text.length - spanLength)}`\n}\n","import React, { useCallback } from \"react\"\nimport { useSelector } from \"react-redux\"\nimport { createSelector } from \"reselect\"\nimport { Flex, Text, TextSmall, Collapsible, Button } from \"@netdata/netdata-ui\"\nimport { selectIsUsingGlobalRegistry, selectIsCloudEnabled } from \"domains/global/selectors\"\nimport getNodes from \"./nodes\"\nimport ReplicatedNodes from \"./replicatedNodes\"\nimport SpacePanelIframe from \"./spacePanelIframe\"\nimport SignInPrompt from \"./prompts/signIn\"\nimport OfflinePrompt from \"./prompts/offline\"\nimport VisitedNodes from \"./visitedNodes\"\n\nconst replicatedNodesSelector = createSelector(\n  state => state.global.chartsMetadata.data || {},\n  state => state.global.registry.fullInfoPayload.mirrored_hosts_status || {},\n  ({ hosts, hostname }, hostsStatus) => getNodes(hosts, hostname, hostsStatus)\n)\n\nconst visitedNodesSelector = createSelector(\n  state => state.global.registry,\n  registry => registry.registryMachinesArray || []\n)\n\nconst isSignedInSelector = createSelector(\n  ({ dashboard }) => dashboard,\n  ({ isSignedIn, offline }) => ({ isSignedIn, offline })\n)\n\nconst Space = ({ isOpen, toggle }) => {\n  const { parentNode = {}, replicatedNodes = [] } = useSelector(replicatedNodesSelector)\n  const visitedNodes = useSelector(visitedNodesSelector)\n  const globalRegistry = useSelector(selectIsUsingGlobalRegistry)\n  const { isSignedIn, offline } = useSelector(isSignedInSelector)\n  const cloudEnabled = useSelector(selectIsCloudEnabled)\n\n  const switchIdentity = useCallback(() => window.switchRegistryModalHandler(), [])\n\n  return (\n    <Collapsible width={74} background=\"panel\" open={isOpen} direction=\"horizontal\" persist>\n      <Flex\n        flex\n        column\n        overflow={{ vertical: \"hidden\" }}\n        margin={[3, 0, 0]}\n        border={{ side: \"left\", color: \"separator\" }}\n        style={{ pointerEvents: \"all\" }}\n      >\n        <Flex overflow={{ vertical: \"auto\" }} flex column gap={4} padding={[4]}>\n          <Flex alignSelf=\"end\">\n            <Button\n              neutral\n              flavour=\"borderless\"\n              themeType=\"dark\"\n              small\n              icon=\"chevron_left\"\n              onClick={toggle}\n            />\n          </Flex>\n          {!isSignedIn && (\n            <>\n              {!!replicatedNodes.length && (\n                <ReplicatedNodes parentNode={parentNode} replicatedNodes={replicatedNodes} />\n              )}\n              {!!visitedNodes.length && (\n                <Text strong color=\"border\">\n                  <VisitedNodes machinesArray={visitedNodes} />\n                </Text>\n              )}\n            </>\n          )}\n          {isSignedIn && (\n            <SpacePanelIframe parentNode={parentNode} replicatedNodes={replicatedNodes} />\n          )}\n        </Flex>\n        {globalRegistry && (\n          <Flex border={{ side: \"top\" }} justifyContent=\"center\" alignItems=\"center\" padding={[6]}>\n            <TextSmall onClick={switchIdentity}>Switch Identity</TextSmall>\n          </Flex>\n        )}\n        {!isSignedIn && cloudEnabled && <SignInPrompt />}\n        {offline && cloudEnabled && <OfflinePrompt />}\n      </Flex>\n    </Collapsible>\n  )\n}\n\nexport default React.memo(Space)\n","import React, { useCallback, useEffect } from \"react\"\nimport styled from \"styled-components\"\nimport { createSelector } from \"reselect\"\nimport { Flex } from \"@netdata/netdata-ui\"\nimport { useDispatch, useSelector } from \"react-redux\"\nimport { useLocalStorage } from \"react-use\"\nimport { selectSpacePanelIsActive } from \"@/src/domains/global/selectors\"\nimport { setSpacePanelStatusAction } from \"@/src/domains/global/actions\"\nimport Spaces from \"./spaces\"\nimport Space from \"./space\"\n\nconst Wrapper = styled(Flex).attrs({ height: \"100vh\", zIndex: 10 })`\n  pointer-events: all;\n`\n\nconst isSignedInSelector = createSelector(\n  ({ dashboard }) => dashboard,\n  ({ isSignedIn }) => isSignedIn\n)\n\nconst Sidebar = () => {\n  const [lsValue, setLsValue] = useLocalStorage(\"space-panel-state\")\n  const isOpen = useSelector(selectSpacePanelIsActive)\n  const signedIn = useSelector(isSignedInSelector)\n\n  const dispatch = useDispatch()\n\n  const toggle = useCallback(() => {\n    dispatch(setSpacePanelStatusAction({ isActive: !isOpen }))\n    setLsValue(!isOpen)\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [isOpen])\n\n  useEffect(() => {\n    dispatch(setSpacePanelStatusAction({ isActive: lsValue ? signedIn : false }))\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [signedIn])\n\n  return (\n    <Wrapper>\n      <Spaces isOpen={isOpen} toggle={toggle} isSignedIn={signedIn} />\n      <Space isOpen={isOpen} toggle={toggle} offline={true} />\n    </Wrapper>\n  )\n}\n\nexport default React.memo(Sidebar)\n","import React from \"react\"\nimport styled from \"styled-components\"\nimport { Flex } from \"@netdata/netdata-ui\"\nimport Header from \"components/header\"\nimport Sidebar from \"components/sidebar\"\n\nconst Wrapper = styled(Flex).attrs({\n  position: \"fixed\",\n  justifyContent: \"start\",\n  alignItems: \"start\",\n  width: \"100%\",\n  zIndex: 10,\n})`\n  top: 0;\n  left: 0;\n  pointer-events: none;\n`\n\nconst Layout = ({ children, printMode }) => {\n\n  if (printMode) return children\n\n  return (\n    <Wrapper>\n      <Sidebar />\n      <Header />\n      {children}\n    </Wrapper>\n  )\n}\n\nexport default Layout\n","import { DefaultTheme, DarkTheme } from \"@netdata/netdata-ui\"\n\nexport type DashboardTheme = any\n\nexport const mapTheme = (theme: DashboardTheme): any =>\n  ({\n    slate: DarkTheme,\n    white: DefaultTheme,\n  }[theme] || DarkTheme)\n","import React, { useState, useCallback } from \"react\"\nimport GoToCloud from \"components/auth/signIn\"\n\nimport {\n  Modal,\n  ModalContent,\n  ModalBody,\n  ModalFooter,\n  ModalHeader,\n  Text,\n  Flex,\n  H3,\n  Button,\n  Box,\n  Checkbox,\n} from \"@netdata/netdata-ui\"\n\nconst campaign = \"agent_nudge_to_cloud\"\n\nconst MigrationModal = ({\n  migrationModalPromoInfo,\n  setUserPrefrence,\n  closeModal,\n  savePromoRemindMeSelection,\n  migrationModalPromo,\n  requestRefreshOfAccess,\n}) => {\n  const [isRememberChoiceChecked, setIsRememberChoiceChecked] = useState(false)\n\n  const handleCheckBoxChange = e => {\n    setIsRememberChoiceChecked(e.currentTarget.checked)\n  }\n\n  const handleClickedCTA1 = useCallback(\n    ({ link, toPath }) => {\n      const { CTA1 } = migrationModalPromoInfo\n\n      if (CTA1.action === \"NAVIGATE\") {\n        if (isRememberChoiceChecked) {\n          setUserPrefrence(CTA1.userPreference)\n          savePromoRemindMeSelection(isRememberChoiceChecked)\n        }\n        if (toPath !== \"agent\") window.location.href = link\n        closeModal()\n      } else if (CTA1.action === \"REFRESH\") {\n        requestRefreshOfAccess()\n      }\n    },\n    [\n      migrationModalPromoInfo,\n      setUserPrefrence,\n      isRememberChoiceChecked,\n      requestRefreshOfAccess,\n      savePromoRemindMeSelection,\n      closeModal,\n    ]\n  )\n\n  const handleClickedCTA2 = useCallback(() => {\n    const { CTA2 } = migrationModalPromoInfo\n    if (isRememberChoiceChecked) {\n      setUserPrefrence(CTA2.userPreference)\n      savePromoRemindMeSelection(isRememberChoiceChecked)\n    }\n    if (CTA2.action === \"NAVIGATE\") {\n    } else if (CTA2.action === \"REFRESH\") {\n      requestRefreshOfAccess()\n    }\n    closeModal()\n  }, [\n    migrationModalPromoInfo,\n    setUserPrefrence,\n    isRememberChoiceChecked,\n    requestRefreshOfAccess,\n    savePromoRemindMeSelection,\n    closeModal,\n  ])\n\n  return migrationModalPromoInfo ? (\n    <Modal>\n      <ModalContent width={180} background=\"modalBackground\">\n        <ModalHeader>\n          <H3 margin={[0]}>{migrationModalPromoInfo.title}</H3>\n        </ModalHeader>\n        <ModalBody>\n          <Flex padding={[0, 0, 4, 0]} column gap={3}>\n            {typeof migrationModalPromoInfo.text.header === \"function\" ? (\n              migrationModalPromoInfo.text.header({})\n            ) : (\n              <Text>{migrationModalPromoInfo.text.header}</Text>\n            )}\n            {migrationModalPromoInfo.text.bullets.length > 0 && (\n              <Flex column gap={3}>\n                <Flex column gap={1} as={\"ul\"}>\n                  {migrationModalPromoInfo.text.bullets.map(bullet => {\n                    if (typeof bullet === \"function\") {\n                      return <li key={bullet}>{bullet()}</li>\n                    }\n                    return (\n                      <li key={bullet}>\n                        <Text>{bullet}</Text>\n                      </li>\n                    )\n                  })}\n                </Flex>\n              </Flex>\n            )}\n            {migrationModalPromoInfo.text.footer && (\n              <Text data-testid=\"body-footer\">{migrationModalPromoInfo.text.footer}</Text>\n            )}\n          </Flex>\n        </ModalBody>\n        <ModalFooter>\n          <Box margin={[0, \"auto\", 0, 0]}>\n            <Checkbox\n              data-ga={`${migrationModalPromo}::click-remind-me::ad`}\n              data-testid=\"remind-me-checkbox\"\n              checked={isRememberChoiceChecked}\n              onChange={handleCheckBoxChange}\n              label={migrationModalPromoInfo.tickBoxOption.text}\n            />\n          </Box>\n          <Box data-testid=\"cta1\" margin={[0, 2, 0, 0]} width={{ min: 40 }}>\n            <GoToCloud utmParameters={{ content: migrationModalPromo, campaign }}>\n              {({ link }) => (\n                <Button\n                  data-ga={`${migrationModalPromo}::click-ct1::ad`}\n                  textTransform=\"none\"\n                  data-testid=\"cta1-button\"\n                  onClick={() =>\n                    handleClickedCTA1({ link, toPath: migrationModalPromoInfo.CTA1.toPath })\n                  }\n                  width=\"100%\"\n                  label={migrationModalPromoInfo.CTA1.text}\n                />\n              )}\n            </GoToCloud>\n          </Box>\n          {migrationModalPromoInfo.CTA2 && (\n            <Box\n              data-ga={`${migrationModalPromo}::click-ct2::ad`}\n              onClick={handleClickedCTA2}\n              height={10}\n              className=\"btn btn-default\"\n              data-testid=\"cta2\"\n              width={{ min: 40 }}\n            >\n              <Box as={Text} sx={{ fontWeight: \"500\", lineHeight: \"25px\" }}>\n                {migrationModalPromoInfo.CTA2.text}\n              </Box>\n            </Box>\n          )}\n        </ModalFooter>\n      </ModalContent>\n    </Modal>\n  ) : null\n}\n\nexport default MigrationModal\n","import React, { useState, useEffect, useMemo } from \"react\"\nimport { useLocalStorage } from \"react-use\"\nimport { useSelector } from \"react-redux\"\n\nimport { selectUserNodeAccess } from \"domains/global/selectors\"\nimport {\n  MigrationModal,\n  useMigrationModal,\n  PromoProps,\n  goToCloud,\n  goToAgentDashboard,\n} from \"@/src/domains/dashboard/components/migration-modal\"\nimport { selectSignInUrl } from \"domains/global/selectors\"\nimport { useRequestRefreshOfAccessMessage } from \"hooks/use-user-node-access\"\nimport { selectIsCloudEnabled } from \"domains/global/selectors\"\nimport { selectRegistry } from \"domains/global/selectors\"\n\n// const PROMO_SIGN_UP_CLOUD: PromoProps = { userStatus: \"UNKNOWN\", nodeClaimedStatus: \"NOT_CLAIMED\" } //CLOUD\n// const PROMO_SIGN_IN_CLOUD: PromoProps = {\n//   userStatus: \"UNKNOWN\",\n//   nodeClaimedStatus: \"CLAIMED\",\n// } //CLOUD\n// const PROMO_IVNITED_TO_SPACE: PromoProps = {\n//   userStatus: \"LOGGED_IN\",\n//   nodeClaimedStatus: \"CLAIMED\",\n//   userNodeAccess: \"NO_ACCESS\",\n// } //CLOUD\n\n// const PROMO_CLAIM_NODE: PromoProps = { userStatus: \"LOGGED_IN\", nodeClaimedStatus: \"NOT_CLAIMED\" } //CLOUD\n// const PROMO_TO_USE_NEW_DASHBAORD: PromoProps = {\n//   userStatus: \"LOGGED_IN\",\n//   nodeLiveness: \"LIVE\",\n//   userNodeAccess: \"ACCESS_OK\",\n// } //UNDEFIND\n\n// const FALLBACK_TO_AGENT: PromoProps = {\n//   userStatus: \"LOGGED_IN\",\n//   nodeLiveness: \"NOT_LIVE\",\n//   userNodeAccess: \"ACCESS_OK\",\n//   nodeClaimedStatus: \"CLAIMED\",\n// } //CLOUD\n\n// const NO_INFO_FALLBACK_TO_AGENT: PromoProps = {\n//   userStatus: undefined,\n//   nodeLiveness: undefined,\n//   userNodeAccess: undefined,\n//   nodeClaimedStatus: undefined,\n// } //CLOUD\n\n// const GO_TO_CLOUD: PromoProps = {\n//   userStatus: \"LOGGED_IN\",\n//   nodeLiveness: \"LIVE\",\n//   userNodeAccess: \"ACCESS_OK\",\n//   nodeClaimedStatus: \"CLAIMED\",\n// } //CLOUD\n\nconst MigrationManager = () => {\n  const cloudEnabled = useSelector(selectIsCloudEnabled)\n  const registry = useSelector(selectRegistry)\n\n  const cloudUrl = useSelector(state =>\n    selectSignInUrl({ content: \"agent-auto-redirect\", term: registry.machineGuid })(state as any)\n  )\n\n  const linkToCoud = useMemo(() => {\n    const { href } = window.location\n    const redirectURI = encodeURIComponent(href)\n    return `${cloudUrl}&redirect_uri=${redirectURI}`\n  }, [cloudUrl])\n\n  const userNodeAccess = useSelector(selectUserNodeAccess) as PromoProps\n  const [isModalOpen, setModalOpen] = useState(false)\n  const { migrationModalPromoInfo, setUserPrefrence, userSavedPreference, migrationModalPromo } =\n    useMigrationModal({\n      ...userNodeAccess,\n    })\n\n  const preferenceID = migrationModalPromoInfo?.tickBoxOption.preferenceID || \"\"\n\n  /**\n   * There is seem to be a bug when we are using the useLocalStorage,\n   * the value to be returned does not change when preferenceID is changing.\n   * For that reason we acces the localStorage directly\n   */\n  const [, savePromoRemindMeSelection] = useLocalStorage(preferenceID)\n  const hasPromoSelectionSaved = localStorage.getItem(preferenceID)\n\n  const closeModal = () => {\n    setModalOpen(false)\n  }\n\n  const isPromoEligibleForShow =\n    cloudEnabled &&\n    migrationModalPromoInfo &&\n    isModalOpen &&\n    (!hasPromoSelectionSaved || hasPromoSelectionSaved === \"undefined\")\n\n  const requestRefreshOfAccess = useRequestRefreshOfAccessMessage()\n\n  /** We are delaying the show of modal because some time the userNodeAccess is equal to null\n   *  and only for a few seconds we are showing the NO_INFO modal an the the userNodeAccess\n   *  has a new value and we show a second modal on top of the other. We dont want this\n   *  behaviour\n   */\n  useEffect(() => {\n    let showModalTimer = setTimeout(() => setModalOpen(true), 4000)\n    return () => {\n      clearTimeout(showModalTimer)\n    }\n  }, [])\n\n  useEffect(() => {\n    if (goToCloud({ userSavedPreference, ...userNodeAccess })) {\n      window.location.href = linkToCoud\n    }\n  }, [linkToCoud, userNodeAccess, userSavedPreference])\n\n  useEffect(() => {\n    if (goToAgentDashboard({ userSavedPreference })) console.log(\"Lets go to Agent\")\n  }, [userSavedPreference])\n\n  useEffect(() => {\n    if (isPromoEligibleForShow) {\n      document.documentElement.style.overflow = \"hidden\"\n    } else {\n      document.documentElement.style.overflow = \"auto\"\n    }\n  }, [isModalOpen, isPromoEligibleForShow])\n\n  if (isPromoEligibleForShow)\n    return (\n      <MigrationModal\n        savePromoRemindMeSelection={savePromoRemindMeSelection}\n        migrationModalPromoInfo={migrationModalPromoInfo}\n        setUserPrefrence={setUserPrefrence}\n        closeModal={closeModal}\n        migrationModalPromo={migrationModalPromo}\n        requestRefreshOfAccess={requestRefreshOfAccess}\n      />\n    )\n\n  return null\n}\n\nexport default MigrationManager\n","import React, { useEffect, useLayoutEffect, useRef, useState } from \"react\"\nimport Ps from \"perfect-scrollbar\"\nimport { ThemeProvider } from \"styled-components\"\n\nimport \"@formatjs/intl-datetimeformat/polyfill\"\nimport \"@formatjs/intl-datetimeformat/locale-data/en\"\nimport \"@formatjs/intl-datetimeformat/add-all-tz\"\n\nimport \"@material/menu-surface/dist/mdc.menu-surface.css\"\n\n// intentionally loading before bootstrap styles\nimport \"./styles/main.css\"\n\n// needs to be included before bootstrap\nimport \"domains/chart/utils/jquery-loader\"\nimport \"bootstrap\"\nimport \"bootstrap-toggle\"\nimport \"bootstrap-toggle/css/bootstrap-toggle.min.css\"\n\nimport { useStore } from \"react-redux\"\nimport \"typeface-ibm-plex-sans\"\nimport \"@fortawesome/fontawesome-free/js/all\"\n\nimport \"styles/fonts.css\"\nimport { loadCss } from \"utils/css-loader\"\nimport { useDateTime } from \"utils/date-time\"\nimport { useSelector } from \"store/redux-separate-context\"\nimport { selectCloudBaseUrl, selectHasFetchedInfo, selectTheme } from \"domains/global/selectors\"\nimport { Portals } from \"domains/chart/components/portals\"\nimport { useChartsMetadata } from \"domains/dashboard/hooks/use-charts-metadata\"\nimport { PrintModal } from \"domains/dashboard/components/print-modal\"\nimport { SidebarSocialMedia } from \"domains/dashboard/components/sidebar-social-media\"\nimport { SidebarSocialMediaPortal } from \"domains/dashboard/components/sidebar-social-media-portal\"\nimport { isPrintMode } from \"domains/dashboard/utils/parse-url\"\nimport useAlarmFromUrl from \"domains/dashboard/hooks/useAlarmFromUrl\"\nimport { useRegistry } from \"hooks/use-registry\"\nimport { useAlarms } from \"hooks/use-alarms\"\nimport { NotificationsContainer } from \"components/notifications-container\"\n\nimport Layout from \"components/layout\"\n\nimport \"./types/global\"\n\nimport { useInfo } from \"hooks/use-info\"\nimport { serverStatic } from \"utils/server-detection\"\nimport { mapTheme } from \"utils/map-theme\"\nimport { netdataCallback, updateLocaleFunctions } from \"./main\"\n\nimport { MigrationManager } from \"@/src/domains/dashboard/components/migration-manager\"\nimport { isDemo } from \"./utils/is-demo\"\nimport { Box } from \"@netdata/netdata-ui\"\nimport { selectIsCloudEnabled } from \"domains/global/selectors\"\n\nconst FakeMargin = Box\n\n// support legacy code\nwindow.Ps = Ps\n\nconst App: React.FC = () => {\n  const cloudEnabled = useSelector(selectIsCloudEnabled)\n\n  const store = useStore()\n  useEffect(() => {\n    // todo\n    // @ts-ignore\n    window.NETDATA.alarms = {}\n    // @ts-ignore\n    window.NETDATA.pause = callback => {\n      callback()\n    }\n    netdataCallback(store)\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [])\n  const [refreshHelper, setRefreshHelper] = useState<number>()\n  // this is temporary, we will not need it when main.js will be fully refactored\n  const haveDOMReadyForParsing = refreshHelper !== undefined\n  // a workaround - each time parseDom is run, the portals are rerendered\n  const parseDom = useRef(() => {\n    setRefreshHelper(Math.random())\n  })\n\n  useEffect(() => {\n    if (haveDOMReadyForParsing) {\n      const loadOverlay = document.getElementById(\"loadOverlay\")\n      if (loadOverlay) {\n        loadOverlay.style.display = \"none\"\n      }\n    }\n  }, [haveDOMReadyForParsing])\n\n  const { localeDateString, localeTimeString } = useDateTime()\n  useEffect(() => {\n    updateLocaleFunctions({\n      localeDateString,\n      localeTimeString,\n    })\n  }, [localeDateString, localeTimeString])\n\n  useRegistry(true)\n  useAlarms(true)\n  useInfo(true)\n\n  const [hasFetchDependencies, setHasFetchDependencies] = useState(false)\n  useLayoutEffect(() => {\n    Promise.all([\n      loadCss(serverStatic + window.NETDATA.themes.current.bootstrap_css),\n      loadCss(serverStatic + window.NETDATA.themes.current.dashboard_css),\n    ]).then(() => {\n      setHasFetchDependencies(true)\n    })\n  }, [])\n\n  const chartsMetadata = useChartsMetadata()\n  const cloudBaseURL = useSelector(selectCloudBaseUrl)\n\n  // @ts-ignore\n  window.NETDATA.parseDom = parseDom.current\n\n  const hasFetchedInfo = useSelector(selectHasFetchedInfo)\n  const theme = useSelector(selectTheme)\n  useAlarmFromUrl()\n\n  return (\n    <ThemeProvider theme={mapTheme(theme)}>\n      {hasFetchDependencies && (\n        // this needs to render after dynamic css files are loaded, otherwise netdata-ui\n        // styling will have smaller priority than bootstrap css\n        <NotificationsContainer />\n      )}\n      <></>\n      {chartsMetadata && cloudBaseURL && hasFetchedInfo && haveDOMReadyForParsing && (\n        <>\n          <Layout printMode={isPrintMode}>\n            {isDemo ? null : <MigrationManager />}\n            {hasFetchDependencies && (\n              <>\n                <Portals key={refreshHelper} />\n                <SidebarSocialMediaPortal>\n                  <SidebarSocialMedia />\n                </SidebarSocialMediaPortal>\n                {isPrintMode && <PrintModal />}\n              </>\n            )}\n          </Layout>\n          {cloudEnabled && <FakeMargin height={15} />}\n        </>\n      )}\n    </ThemeProvider>\n  )\n}\n\nexport default App\n","import { useEffect } from \"react\"\n\nimport { useDispatch, useSelector } from \"store/redux-separate-context\"\nimport { serverDefault } from \"utils/server-detection\"\nimport { fetchHelloAction } from \"domains/global/actions\"\nimport { selectRegistry } from \"domains/global/selectors\"\n\nexport const useRegistry = (shouldUseRegistry: boolean) => {\n  const registry = useSelector(selectRegistry)\n\n  const dispatch = useDispatch()\n  useEffect(() => {\n    if (shouldUseRegistry && !registry.isFetchingHello && !registry.hasFetchedHello\n      && !registry.isHelloCallError\n    ) {\n      dispatch(fetchHelloAction.request({\n        serverDefault,\n      }))\n    }\n  }, [dispatch, registry, shouldUseRegistry])\n}\n","import { useEffect } from \"react\"\n\nimport { useDispatch, useSelector } from \"store/redux-separate-context\"\nimport { startAlarmsAction } from \"domains/global/actions\"\nimport { selectHasStartedAlarms } from \"domains/global/selectors\"\nimport { serverDefault } from \"utils/server-detection\"\n\nexport const useAlarms = (shouldUseAlarms: boolean) => {\n  const hasStartedAlarms = useSelector(selectHasStartedAlarms)\n\n  const dispatch = useDispatch()\n  useEffect(() => {\n    if (shouldUseAlarms && !hasStartedAlarms) {\n      dispatch(startAlarmsAction({\n        serverDefault,\n      }))\n    }\n  }, [dispatch, hasStartedAlarms, shouldUseAlarms])\n}\n","import { useEffect } from \"react\"\nimport { useDispatch, useSelector } from \"store/redux-separate-context\"\nimport { fetchInfoAction } from \"domains/chart/actions\"\nimport { selectRegistry } from \"domains/global/selectors\"\n\nexport const useInfo = (shouldUseInfo: boolean) => {\n  const registry = useSelector(selectRegistry)\n  const hasStartedInfo = registry?.hasStartedInfo || false\n  const dispatch = useDispatch()\n  useEffect(() => {\n    if (shouldUseInfo && !hasStartedInfo) {\n      dispatch(fetchInfoAction.request({\n        poll: false,\n      }))\n    }\n  }, [dispatch, hasStartedInfo, shouldUseInfo])\n}\n","import { useHttp } from \"hooks/use-http\"\nimport { serverDefault } from \"utils/server-detection\"\nimport { ChartsMetadata } from \"domains/global/types\"\n\nexport const useChartsMetadata = () => {\n  const [chartsMetadata] = useHttp<ChartsMetadata>(`${serverDefault}api/v1/charts`)\n  return chartsMetadata\n}\n","import { useMount } from \"react-use\"\n\nimport { useDispatch } from \"store/redux-separate-context\"\nimport { getHashParams } from \"utils/hash-utils\"\nimport { setAlarmAction, setGlobalPanAndZoomAction } from \"domains/global/actions\"\nimport { alarmStatuses } from \"domains/global/constants\"\n\nexport default () => {\n  const dispatch = useDispatch()\n  useMount(() => {\n    const params = getHashParams()\n    const alarmWhen = params[\"alarm_when\"]\n    if (alarmWhen) {\n      const alarmTime = Number(alarmWhen)\n\n      const alarmStatus = params[\"alarm_status\"]\n      const alarmChart = params[\"alarm_chart\"]\n      const alarmValue = params[\"alarm_value\"]\n      if (!alarmStatuses.includes(alarmStatus) || !alarmChart || !alarmValue) {\n        return\n      }\n\n      dispatch(setAlarmAction({\n        alarm: {\n          chartId: alarmChart,\n          status: alarmStatus,\n          value: alarmValue,\n          when: alarmTime,\n        },\n      }))\n      const PADDING = 1000 * 60 * 5\n      dispatch(setGlobalPanAndZoomAction({\n        after: alarmTime * 1000 - PADDING,\n        before: alarmTime * 1000 + PADDING,\n      }))\n    }\n  })\n}\n","import React from \"react\"\nimport ReactDOM from \"react-dom\"\nimport { Provider } from \"react-redux\"\n\nimport { store } from \"store\"\n\n// resolved in craco.config\n// @ts-ignore\nimport App from \"App\"\n\nimport \"./index.css\"\n\n// todo for static-dashboard:\n// 1) wait for the whole page to load, then render\n// 2) when the whole page is loaded, check window.NETDATA.options set by user and override initial\n//    options settings\n\nReactDOM.render(\n  <Provider store={store}>\n    <App />\n  </Provider>,\n  document.getElementById(\"root\")\n)\n","import { useCallback, useMemo } from \"react\"\nimport { useDispatch, useSelector } from \"react-redux\"\nimport { createSelector } from \"reselect\"\nimport { useLocalStorage } from \"react-use\"\nimport { showSignInModalAction } from \"domains/dashboard/actions\"\nimport { selectSignInUrl, selectRegistry } from \"domains/global/selectors\"\nimport { NETDATA_REGISTRY_SERVER } from \"utils/utils\"\n\nconst isRegistrySelector = createSelector(\n  selectRegistry,\n  ({ registryServer }) => registryServer === NETDATA_REGISTRY_SERVER\n)\n\nconst offlineSelector = createSelector(\n  ({ dashboard }) => dashboard,\n  ({ offline }) => offline\n)\n\nconst SignIn = ({ children, utmParameters }) => {\n  const [hasSignedInBefore] = useLocalStorage(\"has-sign-in-history\")\n  const signInUrl = useSelector(state => selectSignInUrl(utmParameters)(state))\n  const isRegistry = useSelector(isRegistrySelector)\n  const offline = useSelector(offlineSelector)\n\n  const dispatch = useDispatch()\n\n  const link = useMemo(() => {\n    const { href } = window.location\n    const redirectURI = encodeURIComponent(href)\n    return `${signInUrl}&redirect_uri=${redirectURI}`\n  }, [signInUrl])\n\n  const onSignIn = useCallback(\n    () =>\n      dispatch(\n        showSignInModalAction({\n          signInLinkHref: link,\n        })\n      ),\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n    [link]\n  )\n\n  return useMemo(\n    () =>\n      typeof children === \"function\"\n        ? children({ isRegistry, link, onSignIn, offline, hasSignedInBefore })\n        : children,\n    [children, isRegistry, link, onSignIn, offline, hasSignedInBefore]\n  )\n}\n\nexport default SignIn\n","/* eslint-disable comma-dangle */\n/* eslint-disable implicit-arrow-linebreak */\nimport { omit, pipe, mergeDeepLeft } from \"ramda\"\n\ntype HashParams = { [param: string]: string }\nconst fragmentParamsSeparatorRegEx = /[&;]/\nconst fragmentParamsSeparator = \";\"\n\nexport const getHashParams = (\n  hash = decodeURIComponent(window.location.hash.substr(1))\n): HashParams => {\n  if (hash.length === 0) {\n    return {}\n  }\n  const params = hash.split(fragmentParamsSeparatorRegEx)\n  const response = params.reduce((acc: HashParams, current) => {\n    const parts = current.split(\"=\")\n    const [param, value] = parts\n    acc[param] = value\n    return acc\n  }, {})\n  return response\n}\n\nexport const makeHashFromObject = (params: { [paramKey: string]: string }) => {\n  const entries = Object.entries(params)\n  if (entries.length === 0) {\n    return \"\"\n  }\n  return entries\n    .map(([key, value]) => (value === undefined ? key : `${key}=${encodeURIComponent(value)}`))\n    .join(fragmentParamsSeparator)\n}\n\nexport const getFilteredHash = (\n  excludedParams: string[],\n  hash = decodeURIComponent(window.location.hash.substr(1))\n) => {\n  const filteredParams = omit(excludedParams, getHashParams(hash))\n  return makeHashFromObject(filteredParams)\n}\n\nexport const getUniqueParamsHash = pipe(getHashParams, makeHashFromObject)\n\nexport const setHashParams = (params: { [paramKey: string]: string }) => {\n  const allParams = getHashParams()\n  const allParamsResult = mergeDeepLeft(params, allParams)\n  window.history.replaceState(window.history.state, \"\", `#${makeHashFromObject(allParamsResult)}`)\n}\n\nexport const getHashParam = (\n  param: string,\n  hash = decodeURIComponent(window.location.hash.substr(1))\n): string => getHashParams(hash)[param]\n\nexport const hasHashParam = (\n  param: string,\n  hash = decodeURIComponent(window.location.hash.substr(1))\n): boolean => getHashParams(hash)[param] !== undefined\n\nexport const removeHashParams = (params: string[]) => {\n  window.history.replaceState(window.history.state, \"\", `#${getFilteredHash(params)}`)\n}\n","import { useEffect, useCallback, useState } from \"react\"\n\ntype IframesMessageType =\n  | \"spaces\"\n  | \"workspaces\"\n  | \"hello-from-spaces-bar\"\n  | \"hello-from-space-panel\"\n  | \"hello-from-sign-in\"\n  | \"is-signed-in\"\n  | \"streamed-hosts-data\"\n  | \"has-focus\"\n  | \"iframe-focus-change\"\n  | \"synced-private-registry\"\n  | \"set-is-logout-dropdown-opened\"\n  | \"user-node-access\"\n  | \"request-refresh-access\"\n\ninterface IframesMessage<T = unknown> {\n  type: IframesMessageType\n  payload: T\n}\n\nexport const sendToChildIframe = (\n  htmlIframeElement: HTMLIFrameElement | string,\n  message: IframesMessage\n) => {\n  const iframeElement =\n    typeof htmlIframeElement === \"string\"\n      ? (document.getElementById(htmlIframeElement) as HTMLIFrameElement)\n      : htmlIframeElement\n\n  if (iframeElement.contentWindow) {\n    iframeElement.contentWindow.postMessage(message, \"*\")\n  }\n}\n\nexport const useListenToPostMessage = <T>(\n  messageType: IframesMessageType,\n  callback?: (newMessage: T) => void,\n  defaultState?: T | (() => T)\n): [T | undefined, () => void] => {\n  const [lastMessage, setLastMessage] = useState<T | undefined>(defaultState)\n  const handleMessage = useCallback(\n    message => {\n      const data = message.data as IframesMessage<T>\n      if (data.type === messageType) {\n        setLastMessage(data.payload)\n        if (callback) {\n          callback(data.payload)\n        }\n      }\n    },\n    [callback, messageType]\n  )\n  const resetMesssage = useCallback(() => {\n    setLastMessage(defaultState as T)\n  }, [defaultState])\n  useEffect(() => {\n    window.addEventListener(\"message\", handleMessage)\n    return () => {\n      window.removeEventListener(\"message\", handleMessage)\n    }\n  }, [handleMessage, messageType])\n  return [lastMessage, resetMesssage]\n}\n","export const isMainJs = process.env.REACT_APP_IS_MAIN_DASHBOARD\nexport const isTestingEnv = process.env.NODE_ENV === \"test\"\nexport const isDevelopmentEnv = process.env.NODE_ENV === \"development\"\n","export const storeKey = \"dashboard\"\n","import { createContext } from \"react\"\nimport {\n  createDispatchHook, createSelectorHook, ReactReduxContextValue,\n  useSelector as useSelectorOriginal,\n  useDispatch as useDispatchOriginal,\n} from \"react-redux\"\n\nconst shouldUseDefaultContext = process.env.REACT_APP_SHOULD_USE_DEFAULT_CONTEXT\n\nexport const dashboardReduxContext = shouldUseDefaultContext\n  ? undefined\n  : createContext<ReactReduxContextValue>(undefined as any)\n\nexport const useSelector = dashboardReduxContext\n  ? createSelectorHook(dashboardReduxContext)\n  : useSelectorOriginal\nexport const useDispatch = dashboardReduxContext\n  ? createDispatchHook(dashboardReduxContext)\n  : useDispatchOriginal\n","const isPrintMode = window.location.hash.split(\";\").includes(\"help=true\")\n\nconst getIsDemo = () => {\n  if (isPrintMode) {\n    return false\n  }\n  const { hostname } = document.location\n  return (\n    hostname.endsWith(\".my-netdata.io\")\n    || hostname.endsWith(\".mynetdata.io\")\n    || hostname.endsWith(\".netdata.rocks\")\n    || hostname.endsWith(\".netdata.ai\")\n    || hostname.endsWith(\".netdata.live\")\n    || hostname.endsWith(\".firehol.org\")\n    || hostname.endsWith(\".netdata.online\")\n    || hostname.endsWith(\".netdata.cloud\")\n  )\n}\n\nexport const isDemo = getIsDemo()\n","import { prop, path } from \"ramda\"\nimport { createSelector } from \"reselect\"\n\nimport { AppStateT } from \"store/app-state\"\nimport { utmUrlSuffix } from \"utils/utils\"\nimport { alwaysEndWithSlash } from \"utils/server-detection\"\n\nimport { GetKeyArguments, getKeyForCommonColorsState } from \"./reducer\"\nimport { storeKey } from \"./constants\"\nimport { OptionsKey } from \"./options\"\n\nconst NETDATA_REGISTRY_SERVER = \"https://registry.my-netdata.io\"\n\nexport const createSelectAssignedColors = (args: GetKeyArguments) => (state: AppStateT) => {\n  const keyName = getKeyForCommonColorsState(args)\n  const substate = state[storeKey].commonColorsKeys[keyName]\n  return substate && substate.assigned\n}\n\nexport const selectGlobal = (state: AppStateT) => state.global\n\nexport const selectCommonMin = createSelector(\n  selectGlobal,\n  (_: unknown, commonMinKey: string) => commonMinKey,\n  (globalState, commonMinKey) => (\n    globalState.commonMin[commonMinKey]\n  ),\n)\n\nexport const selectCommonMax = createSelector(\n  selectGlobal,\n  (_: unknown, commonMaxKey: string) => commonMaxKey,\n  (globalState, commonMaxKey) => (\n    globalState.commonMax[commonMaxKey]\n  ),\n)\n\nexport const selectGlobalSelection = createSelector(selectGlobal, prop(\"hoveredX\"))\n\nexport const selectGlobalSelectionMaster = createSelector(\n  selectGlobal,\n  prop(\"currentSelectionMasterId\"),\n)\n\nexport const selectGlobalPanAndZoom = createSelector(selectGlobal, prop(\"globalPanAndZoom\"))\n\nexport const selectDefaultAfter = createSelector(selectGlobal, prop(\"defaultAfter\"))\n\nexport const selectGlobalChartUnderlay = createSelector(selectGlobal, prop(\"globalChartUnderlay\"))\n\nexport const selectHasWindowFocus = createSelector(selectGlobal, prop(\"hasWindowFocus\"))\nexport const selectGlobalPause = createSelector(selectGlobal, prop(\"globalPause\"))\n\nexport const selectSnapshot = createSelector(\n  selectGlobal,\n  prop(\"snapshot\"),\n)\n\nexport const selectRegistry = createSelector(selectGlobal, prop(\"registry\"))\n\nexport const selectCloudBaseUrl = createSelector(selectRegistry, prop(\"cloudBaseURL\"))\n\nexport const utmParametersToString = (utmParameters = {}) =>\n  Object.keys(utmParameters).reduce((acc, key) => (acc += `&utm_${key}=${utmParameters[key]}`), \"\")\n\nexport const selectSignInUrl = utmParameters =>\n  createSelector(selectRegistry, selectCloudBaseUrl, (registry, cloudBaseURL) => {\n    const name = encodeURIComponent(registry.hostname)\n    const origin = encodeURIComponent(\n      alwaysEndWithSlash(window.location.origin + window.location.pathname)\n    )\n    // not adding redirect_url - it needs to always be based on newest href\n    // eslint-disable-next-line max-len\n    return `${cloudBaseURL}/sign-in?id=${\n      registry.machineGuid\n    }&name=${name}&origin=${origin}${utmUrlSuffix}${utmParametersToString(utmParameters)}`\n  })\n\nexport const selectIsFetchingHello = createSelector(selectRegistry, prop(\"isFetchingHello\"))\nexport const selectIsUsingGlobalRegistry = createSelector(\n  selectRegistry,\n  ({ registryServer }) => registryServer && (registryServer !== NETDATA_REGISTRY_SERVER),\n)\n\n// currently cloud-base-url is taken from registry?action=hello call, which returns error\n// if Agent+browser are configured to respect do-not-track\nexport const selectIsCloudEnabled = createSelector(\n  selectRegistry,\n  (registry) => registry.isCloudEnabled && !registry.isHelloCallError,\n)\nexport const selectHasFetchedInfo = createSelector(selectRegistry, prop(\"hasFetchedInfo\"))\nexport const selectFullInfoPayload = createSelector(selectRegistry, prop(\"fullInfoPayload\"))\n\nexport const selectHasStartedAlarms = createSelector(\n  selectGlobal,\n  path([\"alarms\", \"hasStartedAlarms\"]),\n)\nexport const selectActiveAlarms = createSelector(\n  selectGlobal,\n  (global) => global.alarms.activeAlarms,\n)\n\nexport const selectAlarm = createSelector(\n  selectGlobal,\n  (global) => global.alarm\n)\n\nexport const selectSpacePanelIsActive = createSelector(selectGlobal, prop(\"spacePanelIsActive\"))\nexport const selectSpacePanelTransitionEndIsActive = createSelector(\n  selectGlobal, prop(\"spacePanelTransitionEndIsActive\"),\n)\n\nexport const selectOptions = createSelector(selectGlobal, global => global.options)\n\nexport const createSelectOption = <T extends OptionsKey>(optionName: T) => (\n  createSelector(selectOptions, (options) => options[optionName])\n)\n\nexport const selectDestroyOnHide = createSelectOption(\"destroy_on_hide\")\nexport const selectStopUpdatesWhenFocusIsLost = createSelectOption(\n  \"stop_updates_when_focus_is_lost\",\n)\nexport const selectShouldEliminateZeroDimensions = createSelectOption(\"eliminate_zero_dimensions\")\nexport const selectIsAsyncOnScroll = createSelectOption(\"async_on_scroll\")\n\nexport const selectParallelRefresher = createSelectOption(\"parallel_refresher\")\nexport const selectConcurrentRefreshes = createSelectOption(\"concurrent_refreshes\")\nexport const selectSyncSelection = createSelectOption(\"sync_selection\")\nexport const selectSyncPanAndZoom = createSelectOption(\"sync_pan_and_zoom\")\n\nexport const selectTheme = createSelectOption(\"theme\")\nexport const selectShowHelp = createSelectOption(\"show_help\")\nexport const selectPanAndZoomDataPadding = createSelectOption(\"pan_and_zoom_data_padding\")\nexport const selectSmoothPlot = createSelectOption(\"smooth_plot\")\n\nexport const selectUnitsScalingMethod = createSelectOption(\"units\")\nexport const selectTemperatureSetting = createSelectOption(\"temperature\")\nexport const selectSecondsAsTimeSetting = createSelectOption(\"seconds_as_time\")\nexport const selectTimezoneSetting = createSelectOption(\"timezone\")\nexport const selectUTCOffsetSetting = createSelectOption(\"utcOffset\")\nexport const selectUserSetServerTimezone = createSelectOption(\"user_set_server_timezone\")\n\nexport const selectChartsMetadata = createSelector(\n  selectGlobal,\n  (global) => global.chartsMetadata.data,\n)\n\nexport const selectChartMetadataFromChartsCall = createSelector(\n  selectChartsMetadata,\n  (_: unknown, { chartId }: { chartId: string, id: string }) => chartId,\n  (allMetadata, chartId) => allMetadata?.charts[chartId],\n)\n\nexport const selectUserNodeAccess = createSelector(selectGlobal, global => global.userNodeAccess)\n","import React from \"react\"\nimport styled from \"styled-components\"\nimport { Text, getColor } from \"@netdata/netdata-ui\"\n\nconst BaseAnchor = styled(\"a\")`\n  && {\n    color: ${getColor(\"primary\")};\n\n    :hover {\n      color: ${getColor(\"primary\")};\n    }\n\n    :visited {\n      color: ${getColor(\"accent\")};\n    }\n  }\n`\n\ntype AnchorProps = React.ComponentPropsWithRef<\"a\"> & {\n  Component?: React.ElementType\n}\n\nconst Anchor = ({ Component = Text, ...rest }: AnchorProps): any => (\n  <Component as={BaseAnchor} {...rest} />\n)\n\nexport default Anchor\n","import { createAction } from \"redux-act\"\n\n// slightly simplified version of the creator used in the cloud\n// we will unify it when some typing issues will be fixed (cloud version didn't warn on bad payload)\nexport const createRequestAction = <RequestT, SuccessT = any, FailureT = any>(name: string) => {\n  const action = createAction<RequestT>(name.toUpperCase())\n\n  return Object.assign(action, {\n    request: action,\n    success: createAction<SuccessT>(\n      `${name.toUpperCase()}_SUCCESS`,\n      (payload) => payload,\n      (meta) => meta,\n    ),\n    failure: createAction<FailureT>(\n      `${name.toUpperCase()}_FAILURE`,\n      (payload) => payload,\n      (meta) => ({\n        ...meta,\n        error: true,\n      }),\n    ),\n  })\n}\n","import { mergeAll, mergeRight } from \"ramda\"\nimport { LOCALSTORAGE_HEIGHT_KEY_PREFIX } from \"domains/chart/components/resize-handler\"\nimport { DashboardTheme } from \"utils/map-theme\"\n\nexport const SYNC_PAN_AND_ZOOM = \"sync_pan_and_zoom\"\nexport const STOP_UPDATES_WHEN_FOCUS_IS_LOST = \"stop_updates_when_focus_is_lost\"\nexport const DESTROY_ON_HIDE = \"destroy_on_hide\"\nexport const THEME = \"theme\"\nexport const LEGEND_RIGHT = \"legend_right\"\n\nexport const themeLocalStorageKey = \"netdataTheme\"\n\n/* eslint-disable camelcase */\n\nexport interface Options {\n  // performance options\n  [STOP_UPDATES_WHEN_FOCUS_IS_LOST]: boolean\n  eliminate_zero_dimensions: boolean\n  [DESTROY_ON_HIDE]: boolean\n  async_on_scroll: boolean\n\n  // synchronization options\n  parallel_refresher: boolean\n  concurrent_refreshes: boolean\n  sync_selection: boolean\n  [SYNC_PAN_AND_ZOOM]: boolean\n\n  // visual options\n  [LEGEND_RIGHT]: boolean\n  [THEME]: DashboardTheme\n  show_help: boolean\n  pan_and_zoom_data_padding: boolean\n  smooth_plot: boolean\n\n  // locale options\n  units: \"auto\" | \"original\"\n  temperature: \"celsius\" | \"fahrenheit\"\n  seconds_as_time: boolean\n  timezone: string\n  user_set_server_timezone: string\n  utcOffset: number | string\n}\nexport type OptionsKey = keyof Options\n\n// those options have been created around 2015/2016 and some of them are not needed anymore\n// so we need to revisit them, test their impact, etc.\n\nexport const INITIAL_OPTIONS: Options = {\n  // performance options\n\n  // boolean - shall we stop auto-refreshes when document does not have user focus\n  [STOP_UPDATES_WHEN_FOCUS_IS_LOST]: true,\n  // do not show dimensions with just zeros\n  eliminate_zero_dimensions: true,\n  // destroy charts when they are not visible\n  [DESTROY_ON_HIDE]: false, // eventually apply slow device detection\n  async_on_scroll: false,\n\n  // synchronization options\n  // enable parallel refresh of charts\n  parallel_refresher: true, // eventually apply slow device detection\n  // when parallel_refresher is enabled, sync also the charts\n  concurrent_refreshes: true,\n  // enable or disable selection sync\n  sync_selection: true,\n  // enable or disable pan and zoom sync\n  [SYNC_PAN_AND_ZOOM]: true,\n\n  // visual options\n  [LEGEND_RIGHT]: false,\n  [THEME]: \"slate\",\n  // when enabled the charts will show some help\n  // when there's no bootstrap, we can't show it\n  show_help: Boolean(window.netdataShowHelp) && !window.netdataNoBootstrap,\n  // fetch more data for the master chart when panning or zooming\n  pan_and_zoom_data_padding: true,\n  // enable smooth plot, where possible\n  smooth_plot: true, // eventually apply slow device detection\n\n  // locale options\n  units: \"auto\", // auto or original\n  temperature: \"celsius\",\n  seconds_as_time: true, // show seconds as DDd:HH:MM:SS ?\n  timezone: \"default\", // the timezone to use, or 'default'\n  user_set_server_timezone: \"default\", // as set by the user on the dashboard\n  utcOffset: 0,\n}\n\nconst localStorageKeyToOption = <T extends string>(key: T) =>\n  key.replace(/^options\\./, \"\").replace(themeLocalStorageKey, THEME)\n\nconst getItemFromLocalStorage = <T extends string>(key: T) => {\n  const value = localStorage.getItem(key)\n  // \"undefined\" (deliberate as a string) to support \"options.setOptionCallback\", an old property\n  // used in old dashboard. users will still have it, so we need to support it for some time\n  if (value === null || value === \"undefined\") {\n    localStorage.removeItem(key)\n    return null\n  }\n  let parsed\n  try {\n    parsed = JSON.parse(value)\n  } catch (e) {\n    // todo fix after main.js refactor\n    // special case for netdataTheme, only this option is not boolean\n    if (key === themeLocalStorageKey && value) {\n      return value\n    }\n\n    console.log(`localStorage: failed to read \"${key}\", using default`) // eslint-disable-line no-console, max-len\n    // it was not present in old dashboard, but it probably makes sense to remove broken values\n    localStorage.removeItem(key)\n    return null\n  }\n  return parsed\n}\n\nexport const getOptionsMergedWithLocalStorage = (): Options => {\n  const optionsFromLocalStorage = Object.keys(localStorage)\n    .filter(key => key.startsWith(\"options.\") || key === themeLocalStorageKey)\n    .map(key => ({\n      [localStorageKeyToOption(key)]: getItemFromLocalStorage(key),\n    }))\n    .filter(o => Object.values(o)[0] !== null)\n\n  const overridenOptions = mergeAll(optionsFromLocalStorage) as unknown as Options\n  return mergeRight(INITIAL_OPTIONS, overridenOptions)\n}\n\nexport const optionsMergedWithLocalStorage = getOptionsMergedWithLocalStorage()\nexport const initialLegendRight = optionsMergedWithLocalStorage[LEGEND_RIGHT]\n\nexport const clearLocalStorage = () => {\n  const localStorageKeys = Object.keys(localStorage)\n  localStorageKeys.forEach(key => {\n    if (key.startsWith(LOCALSTORAGE_HEIGHT_KEY_PREFIX) || key.startsWith(\"options.\")) {\n      localStorage.removeItem(key)\n    }\n  })\n}\n"],"sourceRoot":""}                                           HLgAEW?  (  D$\$L    I$  %
   HtHʉA$    I$  1HT$(   fD$    D$f
9  H    H(\(HHHHL<   'Hs
Hߺ   L    t$L       tuA$`      H\$H       L|$Lm7He7LML5H5I   1IEp    HIEx    Iǅ       Iǅ       A)A   HHD$ eH+%(      H([]A\A]A^A_    (  D$qI$        A  @  H\$H6LXLH 5>B  L       @JH  Hv  kI$  HZ
  O    ff.     @     HcAVAUATUHHSL  HLX  L  L    MtLH    LH    Ae9MtAd$9I~HP         HtEw$HH tGH  Ht
  Hu0H     []A\A]A^OH  Ht؉  H  HtH
             1tUvv    ft;ft,ftIw1   ft f   f       u    f1fWE    ftpw+fptgv?   ftfqu\       f!t<v&fY!tfv    ftf
    ftfr           1    fD      AWAVAUATUSHHHeH%(   HD$@HF1C   HD$5    ft$D$<    H,    2      Aǅt*HD$@eH+%(   Q  HHD[]A\A]A^A_    H   HHH$    Aǅ    H<$H       H    H    H߉    Aǅ    H    H    H    } @       E      IH
  H$
    M	  M  I  Ip     M  w1t
   fA!  H  H  H	      I   Iȩ H
  Iǆ       L    Aǆ    L   Ml	     L    HH    HtHH    LL    HuD   I  HE A0  HU8I!  HE0Ht+HI!  HHI!  HHI !  H@I(!  HEM   LHI8!  HHI@!  HHIH!  HHIP!  HH IX!  HH(I`!  HH0Ih!  H@8Ip!  2    I  I    HL$  uIǆ@!      Hu    I  HI   LAǆ     I  Aǆ      M   Iǆ       Iǆ       P<fA!  P>fA!  PHA!  P@fA!  @BfA!  HE    A0  u  ?       Aǆ  @   
  fA@  H=    9AƆ Aǆ)     OЋ    A  fA  ?   Aǆ)     9Oº   fA  A  %
 A      I HT
  I H     D
  Iǆ      HIǆ@     )H 1HAX  H<H    Iȫ H	  IP  +	    @       I
  H	  A0  >	  E      I!  Lp      A0  Q  i     Aǆ)      Aǆ)      Aǆ`     Ap  Aq  It  DA  H @ @    PPAuAƆw  AƆ  AƆt  dAƆ|  dAƆ   AƆ   A  tAƆ  Ih  I  I  HIh  I  H)I  H)΁(  I  HL    LH      I  QAAƆ  LfE      D    A  E    A   LAǆ(     fE  Aǆ(     Aǆ            A A  =    @R  A0  j      Iǆ      I  H  L    A0    A  $  =     tAƆ!  AƆ!  I  L    AƆ!   AA!  -  .  A  ?AtA    E    A0    L    HE(?   HI!      5    L    A0  H	   Iǆ    I   I   IX F  H  H	 џ Hџ H	HH  @ H	I   I   HD   %  A  H
   I  	 I	  I    P  I   I   I      tIǆX         trIH  Ht&Ht$L    D$  I   I          9OfA   H       H	I   H    (   H	I   A  tI      tI      LdY    1I`!  L        I  I:  HH       H   1L    I0      R	    A:  LA4  A>  fA8  %E111I )  H        I        H   Iǆ@)      I()  I0)  I0)  I8)  A  @A  L    Aǅ>  L    A  Iȩ     I     Iȫ     IP     I
      HD$H(L@    H       H    H߉    @t	MDH    7HH)JL     A!  A    _  AƆ  A     I  AHD$w?I  HU_HA       H    H߉    I  Iǆ      HI   HX  H        I   IX H	   Iǆ    I   Hџ
 Hџ H	 џ H	HH H  H	   H	A   A  A   @A  A  7A  A  fA!  u̀1%Aǆ     fA  Iǆ)      A  AƆ  LA         L    A!      fA!  uA     A  -A  XA  I  H3IH=    (   
      H   HǾ(   HD$    HD$IP A  A  IH	   H  H	HARH   O>W<HuH   H        AA    1I ,   LAЩ I@!      SBs>L    A!  tAǆЩ    I  1M֩ AЩ  @H       1   Hl$ HHL    |$) N  HL    HL    |$.    L$ H        L    A!  f=J-  f=X#  I`  L    A0  L   k
O   Ht$5L    u!L        A  f   H    H|$5    A!  tA!  I  f=J  f=X       z    k
9O_T$2    LRDL$8H    DD$7L$(    ZL$*DL$,LH    DD$+        I  fD$  Aǆ!     H@H@HLx8MAl HT$   L    f|$t8|$    |$A!      A!  iHGH@Hx81HLfD$       f|$ uL/        AU   ATUHSH          AH  H0    H  @2HU HtHH  0A  S      AtVH  0H    ètH  HE HtHЉH             [D]A\A]    HE HtH  㿋HЉfH      HA    AHEH    H    H       @     USH  H0    H  HHtHЉ   H߃          H  HHtHЉ(H߾         []    f    AWAVAAUAATEA   USH  H0    AOIfE   EE1EEH  HHtHЉ(   H          H  HHtHЉ   H߃          H  HHtHЉ(   HAA          E9SH  HHtHЉ(H߾   []A\A]A^A_    f         USH  H0    ŃH  HHtHȉ   H߃          H  HHtHЉ(HH          |     []i  i           ATI&   UHSHH@      u*u Ffw&H@  LH    uA$8u	[]A\    ff.     @     AVE1AUIATAUE1SH  0    I  EIE HtHЉ   L          I  L0    DDEI  IE HtHЉ   L          D9o[D]A\A]A^        U1SHS      HMft,   Hߺ      Hu1[]    fHCH    H    H       ِ    AWAVAAUATUSHH HL$ft$fT$
D$tD$H []A\A]A^A_    H*D$D  AD$E1D$f   HEi      HHOf     t$   HD$  HB48HD$A  L$PD$AŉD$ D$DP!9t5IAfD;|$
sA4$   HEE~f.fuHE~    N  '      fD;|$
H%D$AT D$

   EHD$|D      AWAVAAUATAUHSHAŅt[D]A\A]A^A_    HAŅ   fE   EEA   Ntu IA   HH0  C4$HA   HIffEI9t%Htf  uA~   A   HD[]A\A]A^A_    HA!f.         AT    AII    USHH  eH%(   H$   1HL$   HHHH     H    HL!HHHHJH1L!H1I9u޸   EHHf     D1f  t#H$   eH+%(   uzH  []A\    H   DHuƸ   f+$f  fH$   eH+%(   u,HCH    H    H   H  []A\        f    U1SH#   d   2            H  Hߋp    u1#   d   2            H  Hߋp    H     v#I%
+4Xed$UfK#ift+GxڲPJR*
NG=A^=ɾgsDTyfdZU#	9}/d=$}:(z?xwrv.ގ:N'nĭukt7+8=O[/>~]<de4NiRQa1N&i;ʨ3y<ϞqgN'Q2D$O~&r&6GF>8؋I1fA΢a277sOovq|M헟_<qҗ{]z tq$M,e4qì,"c<Ft7.l7wZѤ O=	nʳu7eJŗ4/.b<L^eѸxJi>)>>˧e]ȐRH?T$덳[ydRM)`^=$/|rl/u/GiE[V4-y!W^sxuMDldy2dd2t1	K6wxsz~4nǼk,:2UVՈ/NiDg$in5w?}tۻ(Md.dO2Cxx\~Vd~b3.C;:gy4ß^=mGI?NR>񜎱^&ߌ~9N,D`QR.ȝElwc$?Ek瞬h,y#`M瞋6O?MƳ
l6WN6 Y_S<q1X>vr}}y:Msڛr$h(]O}YXŘ6}H2C"KqJ]ɂvL}?U3vIXm̾|G:9]ɤ'ƣ7&'?TAAd)bsDo W9l;;tʦ*Q1[Zԧ,e-L}	noV*/.{0wF2u1ڥQV*HųL'Z7Y_eX\_Vf`O2vwd؉oK^@,vO9q"Ȣޑialwekeew'n(W؟t&'
*'7Vrf/5O`a7bv00b4OG}ۘnǭ?͓[y*Y$K7X8MteHܦH7
f_Q?Oyzȝi"VW9>6c);-{[@rNdNgI_L@R>;N L!@1bԏ	ڢLAK,:
4do9˴ec>,
|6=TLeLC+/sqg$O._sou[bK6͇t<!̊m_wl1yp-)CWrbiL<9g,M2Lw9tAqָ\Mq^^<A9d|VrBve*'?&6<bIJ.9opm,3D.DK<&~BsJvckkSytZ_ptKepe^@a8*q
7Z	=Ye"ҁ=Rh^[.2$ڻzpkJxbaGrKٵʲ)ss/,eEe
+,4Ӥ!o)!ӡ|Lq=:={ܓ50^%P5ep[ZI!i5햘t4"tNtuvjoL^޸(kzYy:Oe:~xrR&cuǩ cx,6e}ћE$#JlolZ .!=!>P|Ȼ	!ȓaKxv̔qV)QaX^݅&p@KS\xel՝- ǓXv4|,߽<mEx*DЯ\?x2T(xě2Ob8mmuv>Vaa;nǭ¯kՌj+Hj"n5BNujxh;蠋af!>U.kl"'%cJ6$Av0yN_=mgN-$zr٣zn )z7Sy-Ay)]ьOuotAΔyL`mB6%Ūi\vN̶|wW"vR,dxay#?f%dϊj4a8?]45a!gxy`s	]r`:_Hg+nHTbm%sCXp7N|پgOiQ
7WЉ	]><˛u aZ~vw'VGہ<njեVXX3Hnκ%(̶EKw<	0 Ya=%9b)bF	AaFrJݻT
]h"':`PSadIMqh7"`[,=)0ā=gC_vLqդD6f{
V6!\7쎹srfSFk?X֒|
ErNޜ^v";7
>"׊deX4SN<0Ia.E}>O2[n@{m,NƩކROE$}RHwG{|+vzubyPfmf#cGJppypx2dBj֚KU߱QFkox5^hɚ.У٠Hii`ݛo{Rkr
]Gݧg9Y..{R8Tvi
jU!CLG|)nxr٧^t>[RGoXgx68n"YѰ\xX#_sۓ"#fظǓIeB'0^?$%[O$:k^:{hwn^޴yg|tkk EVrOT>hGr|OR97dSl]Θ;}qĻo:bDU|%eevі.C-8K"dJr.cQѴY9>U5hePYSa'2qaNߍawXʥd{qOĻI9\w'tymgii8_9ͷlף_ ǐܛX#	gtd*w˃Lo<pz-
ms=\IsѦL1[gG2"7}a}kp7"WMw7KO%.
&RJptrʹ_G-H_Ӽt"׹>9<??8>tO/N/7'kI,ʲh:
ih7܏ϹIJYם937$ﴔWŊȻuRt%0~fRI_KL%2E%RxCfT_nt:sfbCtQ_c8xض6	Kbf!I$x8oQyomk5ZI,]ZAIw:;bԎ@y	ˬ4a;ǴAﻪMxNފ0d#1hg9UQenxWuf*:=:np=12cVﱅ.WޑX,Wo9|4>Ȅj BU2yd > ro#98ųbjDiHțh#nb{7R\wbe	*1N$)Dwb ~>`nFk/;ϊ+:wKqܸ#>z?W.<CYXq?ğR/nLd<M׼+mZVyO&Q:57"Ƣz%:)Cz-,5?l]#	n]fKqM`Mʭ65jNeOb9y"3!|:W,(tĄ_YSI?AFZ90rRWY
YEQAՊ?{}:9P`sA{x?ga-7]]hv?̒s4;Zz8b\*2*lV%4ɻ撳jv4l̢o~䳚?8,==S]ӟ%uJ!-.:|2StOz"ø3L=
TBhvu{Ӓ<OJ Ӿ#u9V`on;&`xѽlL8?9ta]+GJ~rg3w7`Iփ!!RZ4{Ν*hU{;n4x'e1C[Q'X̝:y|zڼoFon'rC h(|ey9qƴz&P يZ&qYCk5FGlzZ7@`՘ȣD4}Wߨ!1[ss=pTY2WIpIg~{۝XaTXo) `ĝ1>?caGx7FoaXɸYS X_>\e]Vζ01)]&YxȊ=K|ɤoJ_TET˒#.XiE$R>7\CEH\>\Lč%Ner"Op$2^mJǾhϠQ%w8Gvpmww8f۲U ~.ToЕKaf
*$6+=9h~4,% <R `xYhH.	=ġ
#Wc>ZS@YLsX1.2K;<W0{FoKg. "{LKR2)/8U;@^O(\nhT8/gF)yq,q.ܨ79 zal
S:qB_MqZך>/Lӫc<t'ZNf3:ή!GbvFNjRLj@jcC=P^sݔs }
2 
	AJVIlMȕgKr R!UȰ4_u[v #T@x2s+ʌvlÅ9ÀF&/9I]1k};$?  JW;h*D
67lrR?^83}/y8Xqډ!iS"Q!`tW۝f~cؾ-I1!/q2\m'~!W?Ku)}\/+YYvv) 
Q\>o H"Q۫̐-݋NZ$F
SEpɊ2,iv>.Dv+n̿]˼c<iTa,=<2KYnIp`S0>ɧx:Z. ]ަ%gn?]:@5<Kad9Iv[~9)Ȃ7lju`
%'*9i`1Xn¨\XߏaaXKl{8Q
@ Ҁ:cb1u/gQ'gV1=W`n-zn6\u2?rx6	FQWo\`\;⩞Ϻ><ը-	QٮK<-ۺ9dcd_ yȆY1EAk)4Q10E7l).?XV^D5jd:Kݐѹ49-'p	-2l
ZV^i7( M=+AS3(FkE=jvB:V9:0TlX11X/ȶ7PM{Ϊ櫀$it30c\;S Ȕk=xL7MdҫF;[{QU"~<Hi`ҬN7xfNe,S;2IV4܍SF,dԚ	é]6[[-FѳA2)=*5KP@Ig9	uȸw{ 73MyM۝E2nD.W
vۍ oY)=<ۖ@զ/;xF
g1xq1i&VR*MGw06 _2S4&`O?\~?ߎw$#"72mf!l$=	gdUyǧGAJL|"ude%w9x$*[c O]#{zLYAPaZ<:K$r໊g</r:H6k;;-oӄi|<ԚٻsXrTjW؂뵣5L?iaZ`MYѠ[zů)оj<zm_0C#-Qn#tzٛ) aRr`v;6$)NrB'^n S-AMǥǲbarN][ь>țe(P֯oaA(\ضakccxb<>f4ó
BR7*Q1~MY[VmӈMb%%"C[ED/nwNz]ݸQЧ[,yF6Awߖ:0$I{K*7V&xYf|POU	x~&I?*d	\F)v6faw4.M3dǨP#Y:.߯֟< +^^~,L]=u8x5b,r׏].r'_J\nbWಶCJjBjAB&OEƓWd.gdfR/#d3g72wYϑ0DNvB1vhdÀbO\aomo;% k2,OuAaÔjjus<jM[7>L"KWCŬq7x::|q?k
T
{h`Sj7X"gIZ\*EqQ2c%ppB`{>guDb !F> o\s % PP~SB/&.ka^cyxt"wqcunϹuY8Fk|TACRFУw"Bܑ=;IKx ˽c2\v}Q9Ǜw
LU~n\a1$Uxgq?^<]M77lݫwN'Uy0Bj:dR+vcouҗE!Ehσl-AOV#ǵbUT&W^ӀHn~Ѭ\ǨEW	3J;v-oq1:e=1y	Ïq.f
;p>hAsţ.QB}UjшaWXd虒y?ثX
WǇ7ǧ
<zcMQ^g}?6	FjTjz
KAѯnۣZRHh$ EePz|@'
JWV-{hc7$("62p82\_dD^*d_э3z;} ײF 4^N'G\OOOC?=Ls\")V{7@U_MB,4D?l4ʫ"o/bQ18mAT$>F	ZՑr
԰R(bɅ ]=p(_y{,r9,UZ+6Fq1N>q;tGѺL{ڇxGSµ%9DP.L&~%5+X&F_HM+V<ߜF͉'3keEoV/Yܑf_3r|utxM-h]kĕ
^J8T5{,)Њꢃ)@ 
@`B`H}}LcY՝趂?%/z,a> 0>߭>XQ[8ï+^?b;瑭r\6N\$
	u	ܧy~+@c\N6(vnSn܁I
ۏh	k2V,JaM|K.-FNKщ0(Pb~T:}NpqM&u
@\	`JP֟B`>?Z^tƓK10DSL9AD6$>OHĽv?WCUetLm'(F
QrqFy
EPgt@8Npgwfpj͇hkFfkm}wL7v[qiG?ww,jQihVc[\
33+CGv}Qڔw(6&S}[Ф-C%CEnL+-YSk,qpP
oJJ`cts܍by.KPxgxZއoW[񎎳I/P\+NO>O:s?jЮZ9}1'hY4|1{YlFIA.lp u?	$\o!hQ쉑.OQ"x>ȗ~xWRk_,onj|EnVCm{т+_&d*}8:?ݧ+CM_d}.c4FՖXLxy1;\2T?̗.B";Ь7&d1xY}u,@]&o@Jy5y ͓_	Qe%j-hlw̱\:>V=7n:m3+82!*Pڥ5tr])0cgZeYk^ćMqΦ=$eyL'de<ge)V܃{nXN^  Y8i{
yRm0_f0şA?$n@h2U|z5I)Fl3+o{%6dVC^{<ŏGGREk`qM(5xŅM}@]l8zi/*3J7Z:Xz|PY!ڜUqm{B=/ݐwr+KK kߕ۽ʔ+u0uaw N
ޮ.5'|S
G٘A`E_b\`]r_tLd'}dzBN^bԗl''sםL/Q{{aj25''rEȔ]Kl!UJsJזph5㪅jnVd301Gf"[XE^q;u"^؛$⋿.JYWi>}_1\krH*!Qnяel.	&E.+.p!C}^ɨгA!?)_>7͟qj[tSm)dЛfޮǾ-g^ghSDf^HW껿^is|!vw纙0j4TȎ~<wYZvvVqZ*fbۀb-RT9׶{}Rx$go\pkdjvCgE+Zkbx
c5y/e.CCahz
̱?[vt`XXU΂,1kvyiڷ3~/|ؗť 
WwY.ZfYЊOLE!
@֞w0/䩢Ck<Jı|bK"!Lc0éhA\J>$Xq=j5",	oY`r@컳T;ed+bO8MWUtE=k1\k
7r>UlӠ}2BEo:
Ȋlg|2ZLeIŮ!;1F}m7jq'<Tq`ǇgGb' 5F?粧!\>+{8[|+V"DN1I noY~1)]<

k 6 .ۄFצ!&ƾjk^y>8`d͈!(l@l4Kr`
:U~{
eħ"OaIv}.}	G
A2WGtx9wol͗]}7g@TXh
.YlWp]rk	b۸<`~K2FvhYA:gu;.3hm;M8~mWOqcc	7^E=ojqJkxL|xh\[3V:[E@;^tj=f+nn/#te}
*ME}qꔮ%Q[gܜ/ʬ	xS6a{$NCyj*./IGݓH{drȭO MO+
Pm
.O=NG:J)RӂJ,.	q͌CEfD =2v'!`&"xK0sjnbc79Γ<7{9;><:h7-dwZ}~(_
-|A-;9rop7*XISyz_haNA
2ᲆ]7?? 5we{p	
%.0	4V[vG
wn4tX
n/hΥ;5W[տ
R)bm9(PRSgjAwώ7|d0<QT.͆s
'랒u]nph7OxW`'YJan
`T$]vs Eu&#ta.ûNҕuE5C0}ޱGs/7ִK:kf`?F;\VC֯%xB0K૮_[)Vde[ܗ).)uWc5œ/+O'ߋFe#=i͚evFq6(4kye7{%2]nG4}*Dlomdc&Sڻ4}^F+럢+7hs?([>SC<gCX7A@)lB@ށGrmmdfخ?8{,Uj
aVPVr6w LYRW#.)kH} b:IɋŠi?[yqwݒ%8_8F)4%s}|eȣG~82[;Y`W#6)=ܵMoo,G;8[xћ9
f7)b[[@2&R$޺v/dO8(ojz62D/8xc%Kp{#jts,ib~!q D~Չ1g
}a݊Cӥm*^D=Ė@F9VhZ
\!hl,n{=*QyO1J|q}!hfnwl	 ·5+˰[K7p)
ҌIpih2c	x[	oU'xr=Np4ZGʈ?`˿΁F[zxmm(;!O!):'BY&>% $lh9.\vTevB@EL2|UG	AQ*Е4(^.omBGؠDF03[U,O pc`<dUMLOAPQ
sܰ%π}-GQxc
=b䰵!>OlŸϭ;2 I݇Kc-U'j,IgqX1BRzʙ%-.G	dg˔0'!-I],,Ƚo{/ky( ₳ȃ(쨪juǚz:@N?ogEdǝs!HKE'QP%b*LmKen	s/}^l6U]bQO?TzSL
(lrfqN!]Ƅ	6`)eG<a%
Zَ'EK5"ң=%!s{g*WI~DkGʺ2ݺ.n.lB

-;UY1 $bk'рbղTPgusLwiۊ4UEFV2M,"K/(Efd:~$:=c85:STSg*9V{=3p$L1@"̈́ komUrĤLX
mT<TuF R6M݌GrB`}!NYr~8@/#%\ZT1D aV		5rÀb@3rH~ysrsiА2L)3r!o
d޾%>:mp"CHws՗*g z065Hogy1Urߺ0z^Nߐ&C;J/7mt`Z8rX" d	jkMa_q>'|^k8Uf@\m4L)Z	^"ݾG՝+JA{w[,uXL3Wpfz~%[uy..`jI`_%q.9축!-n#"is$nĝx&h	py.G={X!1gG^"Jw	4|vurOj(oeiXkڗ22\IVwWH9ÉrK#n=#TN]!'̧,2ŵJevk+K	E!J	3z2n'8MSNޯOۭ52Z̕=0 +о]-~?He-}~&T
}/ȊC.7vp谓=F6$.pk8"n_75c07T}
>s䨞-/ALۻ]+yDVY1,o4zyYW
U%[
]xk#Υ?n
#9*栏AۮMY
q5xk"/l:vӦJ:IrsvN RO0Z>`t[n㸅qWVd<ӭIQ4[-rǥS#ʙb0GqM X\GmVq%3tMa&M]IT**o	VeJp}~lVFbr&w⤔YBr9Dڑ#=aka2&yz


m1껣v3kMѨHXϞ=UTgBqɼpoSqvb9$i"Ȏ$fNnKj~l}|Zi[epmCa	|*đ/TCd!>(a
+m~ڲI)|P%HQYy~Eo ;gĴn(3*&
zj9Q lF˼mVȴxد*axpS3/ŚةT^K(=vcY9zL?<!`)ktPng4H9p8>.M]A<3# .t*٨C|'6&|E/}
^|N 3mz Ruz#P?}CF_شmFӈ[(K&yz
~y%1bgR5gqލa:R[scFd,:9e
jhiXrm=b-FyvYp
vYuvZEt@$u̅96:-G<k}u ʎ9cGp~;9v=i7@s\V {խYLթc[LPEXq7o}
VCgrs<R$8Ư-RСM%U<~	0-UTw+OrWEպ,J;F%
R)iTI-19~M،˒H7A6(uk0Β&G'(
Ծu6w٭m_.7>xק2Էc(\Y%lf$qk<}򧋆/ZJOy8y4&$XJ|
^HX'}6
	:)aW*g*CuwuoҬKmN>vԕ?W[[m "]|ddX!'|W}&f>>)Iq
:azɜ:c
5
OVr0
CLBeX?и[3SBz2м9>QmC«:oa}EmYw:2Pv\!6|WʄľehA.i^*ȸXEk=>@^CK.!٩[9RW^,{NvᜅkSGB-H")pQ8L;50Aer Mskj6҂NJK'rZK5$P;Ɠ~2#WԄP2DG+Y{FkƾQ
}bFkeiCKI|j&&5بg.=aP))qb
Z3=XyL%ԴG؁E;e*/d~hA'0)-.yog8Žb=8B{@~Riﵣ_/ Ty+G p.aA.;ݑ@O=;Leqd:EQ<[VJ{L(xF֋ޞGޚ,lGݿ-eNC+wȾ`{/uxtjKz
8}ϔ$"<Jjb̅>
timoR|(̲4o*OMe_wʐJ8
hsySWuu򎹞g_1Zo웰PZK"yWJ3sat-@Fwҗ<{sx}#ǃs6N'cɴ-|w`@E֮7fQOc%EBP˻Y~z}7_+
("2뼆ʘnw2O]_p$Zykt@Y@ml+G3knxNڊ*
H-u(bxT9BƗT !V.<Tn/Zc W:kg3$aP\Z g~.SE7 [QF⠅o=x(ƠˣH,X4\u }N}1=_%ՌN
UF(Ǚ4a R
,R|Pe!huSAKu̶@T|D;TIOe ߧ<9fʧP!45QP4wP.
0X`擎?~9ҜvfK$h}'OKw?^hyk~x\s`I0z{^[Y}w{-GgOzn:1K1<s'ֽzMU3 \۪ob5LV&r>cuieDMMٗ8
7w_C
hߦˮspBkI洐8/x&+IR1`ag
M!~dr7_4N8pyIRpYby	TGW$ldU*.SU󍤨8-bBci#*Ia㡏${JjJA&-nJ횯ngĎ0V$WqO;0Vܙ)ՆDfy~SoD4l±!x!@~B9%PkouXR+>z>4aݟ=>ݳ$\gsN6inBc)?gnTjr{}+T
7eQv!nk8]_3lω2232Gr˞uȯ*DtFF4#'5"ǈ[OKѻj6@.:aMX"L(jwujsr6&axMzNJðxnI\Y4cG
%֪iVz
w-z^~0X˛9>QK!#\qܬUM*l1_ȡ}
H%5纨
9ѹkW5*yчӛE>*[Uin Fnݣ|o슪Ca~iu|& _B3v~K
ѣ|ywp+oszk\#,WruU!QcѬM92JcI&JGE1<\B
R5B
VP-qseLX3UcK5'`&QN;2QW%Nm+U}!m[h(C6͗t_&7Vˍ 
%Aa,W:	$q=''8he˻;W@Ko
Q#z\!Itׁ3F$TcJܐ2ow7b1N!Gb^ik412%<6m}Om2;6v媥4\AUUϵ}
QY]og oLK,4Ό2r>*Fjtnj8<5HD[ZSWf"9tt[
9+xx8WE4.m_,<uՁB	P&
R-M?<y~([9l<rbhh\@Y+Sq"}Mpu}Umfi7
$S!bXYXחGѺu4흭ݶx>G-/` "n*%DҒy߄5NIAZـՈl0r5s~
/z"[sMP$Y.H!z U|37lz1G~xvuQi9᡿C.^|<b/|\\]4tc1OT W]X+ }4O5{KݛMۯȜjk8O?խYB'Ul+	+A6ZxVkkxU{Rsr~_g
H*#~`mmKVj, ܨ	2<06	hUuO>óյR'Mj99\ǥ_]?z/E^n5tBdMSG_Glb9jQs0V(C^e74
U SB?myOza4tہJYF5Lnb{ʥ4|.ٰ#jmhH{ldTU?rz@<3<Z}=9{ŷ.Tu*ȢaeM#y @݇/{M(6L@U-456J`bI}>o2a/AEdӭ|XjU<6Ido=S/#)XU%!{`
̀>
8+vG&숫Uܒ9]ߨ,kD11gG'Rާ?U]ȇRivs\j\I$8muCNtSeAS
p,2&?<{M #|PULt<+F`0`"9hK@Q.,6@*U6@	WjeEXk!sd[jSBqãj+Y#{-Χ	=PВo8L\*8ԥ`x=v%<ϷHa.qtSܳA15&^wcL(Ĉ$:PJe.ULV'
;>LybLKrTwws2̾Wצy
rd*QhJ&sY~hHZ,6ha9د{Vm
.@Z!b[[Q& c]:%ˋW7"k	
zFv
U)Yj<T.htGbjj	BЧ; U_ ptm4@PvJH~8w:014#fآ^àN]>7v`.rC1M}ΑHŤUe!c2%&[
)r9?׏*̇e )lA=s#Ք!}KbHd~&_\g(juV+>;T91~"ѓǞLv+n9
eƽ~|pN%k/Q:ΔqhhPA
NYҾBnzsHn:+>ʁ`l&^qn-,V+9G?y ֵZzQNWycRc`t9[_!> =(U9 F7k{vO1Y?OBTO˘fJ/63>zUJi|'0Y8<@!> "O9+Ot%9++49d\1LZ8ecL`ש;*iq<$6ʠF5"XQϕ;y*Rx[	-m^mPj쒶Lj8=w&A|S=h$(ji0yw>pR0K;jfLX ,1˸[R.LV$U ϟ,:!Mr3tXm64/y!:]!$6Y7s.vuhA>o$P
b]BM7椘0Bs^mHj}&,](6 x܃dEtXrVOH88ꬫl>r⩭2v{N_W!0P=%T]&+}]U)LӹFEieD
NDId^̝O5?@ Wwc:XA[V'6R C!4w`0 %8<&T<9y
WSC>rrQoge ֻP
1XL~dÕqҝOM2R;0TB^R`A7oyC^/<7#ÐrYu~TU>n^kF?1	bڐ(JBB#F
\<#\@k SdAlz`^GtX
h<u/n(Dqoz\D1mZ۠aLneڵ\d2Hq;܋[{V{w!E,䵚m^
.5c1`QK!+u 7O@,da`Xvw%n6hPUʒ]U
&յPa
PX@}6(՝VxUNN<z+4gg}2kD'O?,4Kq?(q{6|1߂$;!Ij8_,VjN!
e1>?9oJ5nY.[F͡5u_$b=zP$6*VsYD 񗟐)ɢ:,^̄KEVKuCjAXʳ
QZU,WS4O$U]S]^Th!(*[a)\,W7>M(#xDW̴CZ/Kj>#~}DcGk!2C'Rk4t]2TJ$#~j&:;hݿ{-IVCMKW[v|v+<ub00LnX̵5_9}@޺) ]8N('ݿN}>wKW뭶XtM2Szb$#_+]y0j)0@vG(g[QW=XvDy8`TH!?߇+s5kmpv2ht	[D`UUC$;S_rtQUlw#&ѡjrhf
GI-^ڇ<0ZKdƞI0ԯb&,DzTU5B0O=P-RgsK9,|7Pոi5jQIBsdYAG2*sZZ
68G22
7#pW~xIw9`\LaH$xC؋[w*7>'FkH.>CYk8<~
c'GeOFN	^id<ڈp{6_twN1~#X`eaSF8;]V|cY{(ӗ}h|.`lkՏj%PJ"gtmDsip	xSz3,DӴLrxS@2^BE8qE4S	]z$l0\U0mNjÄ	wR QRyZ5#ыMUsmC YNRZ+n؈NTB,Aê4W>lC%{(? 7kV%U+L*.VZ7<嬗u?
`)؀ uZހxLsC64\XT5'ɉk5=rkIhMxIv0H#BEuwTɫ,']O'֦	.
..m_&\??suԥ;z	GQ}q'$y'a1+Q	aodCme%<	!=gtzn2DB0$Aw|bh7no޾%
Gi=0xBN-4o_ʢ4Aym\N֝Ҝkll5`FbMaX":`h_`
g
SK*c{u6ۭoI';ߒO'
-]gemԉHM!b@eJn݂O<u+1 dn^/E1z=}sn<Ձ}tzubW
~n(OY
:NZN|EhKŖb[2}l6>wDMF(rt\1*LrI&;I`3'Płb	T14~:^1oYݶ ,kĤVVr.BS#x{ʠs,?G'~Cu",c;x!ؠ&G8YӋ{ZK0J7(0o6k9Aj_[-wnlG=kuƿaC^cE 
}2KajY*<h&lS8J`PXEPgRt%t(vuzQ	~'Bc+Z{	kh(|/p&gPz"~THb E1o	D
%SJr/GY!*|<}{YQ[<j]Q -IUjBrD|ҵ@ᅼ"W5:I7g_hC㺻A8]S9޶X9<߯/$NwwO\y+viaܞk%hMOgH=4<	F!ʸ8כ6մz?Q!j7Y3k+	gM7LWxi;$N  ⪖w03w2Bktǲ䑓Y(Z>WQfMZ1iBA-/KF̋uA`TrH2H>/(s27̈ ԅR'He@= ?K=:4a^Y蕍͎
aNp?48$r+wUpѰG8 @Y%bɠ褖
emqXw&F$/8Q,V"¸<RPYK[kdG"vv>AЃmv(0W|k$Fc Թ4R3$nXc(2*\Ѻ.
7/~
zI9:SuC]Й}cn'7}/2Օ~=wo#&%9<-9ή\Z{@} ME<?`j*=681TAx]Fme5]8_*sL';H??1[ebY#	0΅6V%Zb\Js=A
ɹ/<j\g#o9wݓKA-T
vx5!`í4֔|GcM1Ǫj 9rT#Q|TSKdfoy Oj,m*$PO#:=!M*]op+}1^WUo@I\HCʘ_LP>UDJ~c)6ՓJ<ZDQI|R{/Gu05IR6xX} =xp,,X3r]@JݼN!+ןnqPj-r$¾,+G
W'gO(S
ӷ`\[w6NEگ3GCKAAu	Vi3DBrx)]qj/4	x,4cu mFEI>-ʌhD[YmDhQ.7~2m6B %ht T¨9zsvTuX`:m)̏ոcW<'sVUD,w6əJT_UIl
ɾeϽjBB1y(U֗S3!n?7h@?>#hgeX
/{q]22m0>
d~J!Gwg?X9
Hu@p89(W>ݾ8I^HZ/`7Jg<L5tPK5W`j\0Ayުa]p^{>U՗@Gr u'o%-R
<B `^vg`ތ
X	^V!}-USUmFڹiy:[vHƙ6jԗuK)bnǖPϬ	*^qEARDvtcB~M|㐜&u"I^0HR[w͠eڵbS[SVO}"MMC٥3pTd}&
Gd,tLA
bG ҁ{r:<2׿Xa
.{`KnuĘXJPV
oWy[ltFdʴ	MjAyJ@GzmBFB@7!1djv8L㻐Mcfbk<8e9'(*E?HTQѢv]wrJY/sygcom3iUID:Գ 	)sCd0M넪]do ,NsݨV
O +bb>ZaK#d.Io.j8#@G7=D[*q1r%ȅUˤ4DAq!.яWe]Z?H`ЈΒ{$MkCPAs"I%L54n|Fr\s9k`O 3'ۡe\) PPsc5_KuGa3ZFR[y!e#淰WV++`t3gTx1q^9kbAPBA\͚K8ZylԲ~FkTsZ;Pk+Tک@dL&Ȣ-|ގK7ɢ|!/H"Ւ_.KS+ZGg&7yč5d $hV$DAũ5x?,qJ):D	0)t|DL7[SbAU.d>#+KkW1PpD \]_
!I>P
zFw%
SX+BBf3_ydQ)CK%,fDJPtM4MO!ߎiiD3Dq,IVn,j!|zn|?i8

378cCx=^7)Pf~m)iǶ-Gh֖!=,Az}{b;e~ױ۷9m{^\0Ϳf[&o+M_8y{{juۮ Fvm71V:ϧ@02T߉nZKd1!7w`CgֆIY~L>'cl_=ZkUe.!H݇c$`5bPwlV]\	y,i,9ʇɇ%6rzu/a;/* o !a*KX@AMb'FE
xG^-cߗ*h.Q#|
PufU%ݖx.(N[n3n0MhH
#G
kɨd9{ޓwUY}L yMɊv<#9_uUT n$5(UP8H
K3:Ob5]Qlaw[ s^6Tu"ļQ]h:k%أ^P1!X:|Lʻ/ªR_l~$A>5mWs*:%Qc~ֵG@c'zH h|!{S8@ΛY>گ]sbm}$%Ǜ9÷Ővoj~:W ]
NȚ')WY9bXAb2jk]Z$5c}KgR$_aQb'mgն;mn6&Eڲ H<1vWJ#!֎хZA%FMb8
G1H3}Y_:uґ&4%MJk5(#Uy6:G# 	$fUbL\bpoߜ09QR΀(G CA_֚Kg
ZF|qq kq	3
uz5xKh'K6"JC?(MOŢq2NCσ$ؙ΃
iF绪3L{.&s ېHLGbѨ_~}346M&tUs"M5Ik&&[:]Eɚi]hUhީ?+	SDGh8S$]4
BqK _gWZq&Gp4Skn А%|y+WUU*mW_lLxP65 'p`{@>]xjFrYGYBpJDZNlޤ<ӓuyWbovZ;Uz*j_n&l/⩾'w$g
6@E8e5
+&kk yYhj9b[;
x򷁊Q]P5<t>8;$d4Jl5u0&ŠK_dg͡2wZ5PF3euIu݇l=O]^Vv!F{NG[d|"pˑx%"_ͯ:s,!Kcbi"Ol ք N]ى!pjb~w}+Q\0P^,^6[{vE$4Ey	G(+īX'h<Wo=QuEp3r<m휶Ny᥸rU h#+cwC/SĪ4`+A-&aLO$]	E0MR*gl/Yem
RrGSъG+|.h!ć\@#1vղs#"\8F}\{$Ygɬz58Yb{rw?_Ge]
y*I2U5zE*pmf0GW'K,ze@YWB#ҙI
FU9<m0s}#σ)bE5Fŉ<"|_!cDt_i>
ZɢЌg/QNp7'9xۻKimcyd=ŝun㲁ʞe0p%ip'\d$zPL(ۙv5ֿњ/UT4ơR:Ј6h-K aN<V`H$Pwe13\&U{ެ_jdZZsb&W!ZjP~r3:JdatN
KTf
\{5)~ZPr4Hf&$Ƃ<ĖC+drJ* @)=W⾺&Zc,5P	-|TNgC8)
:M?"8-RX3nc,s|~yc.S|q5:yz`U%lbL!F,'0vk'-dW*)q0zwQU3Mƃ6j[ k KD.ܒ$F[HY~E|E!A4u?X-ѬpiQt'l?}R(=Q@@!9RlUӵ	;;bߨ$8*}!ꞣkh4@ ~b=fG}9d024%^wFF޼䚆pS Kݲ[9d$.Wԝ/P̍\i:@%!ؐ"X|2\H-?,f.HDw8d!#	a *Gf,yZq`Y}OtZ,YQі
b&[uxuԊ~
WElg8noDַTH
)vc.26m[&(n8
KjwgZ<|Č~6Vc$z[|'&)Ӣ;Na(1pΥF9sYeRۋ*4Fo~_l~(+>d8Hw<jcM(Ŭ@T⃑cpWv_刢*Zl36:m2eb*DATI(ҁg5w/QOffaU]gu 1*&8܇L\SWEg9#tX&K}ɋ* 8Mt|dqOF|q"s
B.+jXJѳWc 1>j8S0~Vuc#RPə8;XXhDog8֤@|Fmp5dA4(X7{ѨZf˼&NbW9`0%
b4=ǩ:u_sB^CSrD3fAk'M/ Etv?Fs/e gh6I9|XmI?3-ϲu"GIn]cN=x~U%eڝ߅D.аQ'%gu+/ڒUiV5R2ry7Qs*Oeyf&kʝ-*:׊dn^5W+]ȯ^Zqރuhd1L+|X[łЖ-U 
S{&$"
:;!?cfnSǎrCvQquCWXD>beck%%/>*&	Q	};=gt9C3fd9AIZg}tHۿ!.9~S{mZYFJXa&|7|Itug 'eXp̺"5fSbٟ͎z~\vOjlͰ|* %xψ ى(87⫾;^Ym^=(+ ̷4=ɉ{`Qw
݇Ȫ_4xO\/u\QX
:fyhs~\dNs4eEggGwvvvC(f_RN4qq!D8,4DԟEbJv  UI%/'Nsa/.m*-p'wԂė8_3sOjD̒Xdh[rDTW>L#A[ww;2ͨ7Xva=qK]/ht`f}E׺J}+c\>ǒ`6¿Gs |G&-N!8P<^vN}O)~}gos;
oa;,}:HJ	%){2D#P}Mz
?P+Aq_,w$A^<n jiJ4d e`-
WB%Y٩CC<ͪM`q=榥{V< a5tDzp]Y>y랓P&ַ24u]֪
+
?CtN^+RQ)i=dDT]*y4$R|]ȦMڃkdWe,!mV6FV6\Ya ΔH5O%yFp`2"g;%qޫˢ7~)ïjzaA	5J}g4clgr4kwg,!;Y5}2qr%m@X=R/;Wz@م4]`{t	$<~_鈍Oye@`ޓ&a nP9>M{7SɅs_A	n̈8|0cnsë"mD9|^i*KuMk	{ea^20B<Vc.Fm
 ʗ|Дe~*5aZ2|KOoxT ?%m1>ų8x'O
8t2ޫЛ.;У+<G$`	#Nf06+UT,/ϭ1³<|9
dZ
 a|tRn)S@[.8 #et
:BRѲ`5Yau>A!);|r~`X`Q fm+LT%3葀s^X㴯:Yekkse8
b 	zO9%, ZXm"yύcl.v,qNtK}o4_3otrrA:MS/:6
-bq!ͽm^/C
,c^ B+>_&m OP,78+ur0!pÈG8^w	xq)#J'"vzrbkD?hjV9ްk+k.5P\n)e-QX|"ȉ[EUKAi 	29/T),(K:}xo:`S`8k%VDxECe3[e0ãF*wd>3Z#kr{=i#
-|,+ǣLJ]'ݟ3B,rZCHM	rL`G'aa
V̇*yBex&/h%PoF'yuC0Zٝ}1ap`*E#GS%1F_X8Tr]Pj4Up%^5Lb9vpuui aQ{itGx}p`5㶌cp Qa8mGq<L>(EˣDDw+z\<y]}֠WQkUYՊ~#i\Sa_$87>;e?l|;mCwh5^:b-=DwXfWAJGf;;ۑjdh"6P^®}/ӫU}@].k2}!-\#B	N*L
q+2=$n
71$11ZA%ɭxtHДkw@ȪBdL0nJ(lj{PDU0j3#5V	$L
R|-1jDP"+diuӽl/(V34SGĶAa6@2HɴWC1VGECe\#0Fy NcCցpYp<b9;
HdM hSkLNK7woDy)r
Lg


8=̬
u:N~%c:_ur^Q؆?ƛr>k	2G9z;Kb"衋.`Z|oxY
ףcNj*d5(-?.н9
C	!9ͨU!X&qaR&&^sur^X> r{ʊ>Ls<1E-|.~5ZԞp^n9.]xmɓN.hJZmh=jQI9O\kwU&dHqLieSvFy2%`F&nlh1}
bl
\&X\]W+Mb^7-1^zJY|dwjm0ԉcr۽hvr__taLNml빮1]tpLxixq׮i9q;	ݶFS&j5+f*J̕3WM{Vm;Ffej^#[	վᡷڿNsu<U54&Q<
pßɵ&1̍`P7Fk֊j-*euWvW"ӽaE@;,\GgX(uG*V$D$ycm>n91O	{|eF0S~+c4Bx-/37:otm!ԇw^뒇a7,U'#cb!7{_I2)U%eE::mN{bh05hE6	Nz&Z8TՑGMo*dQ9FwQAqWrC;G&Q]^h~4hAK$ZE}3U#8nbV)pNU{HmY.=&ʹ^KF9F1su>S0#Q:%0r"dɖO:իѨ[q"`]HId.սlyurRν9&nD}Qh[#Q,:ayܒ6bDoIsP
OamyMDko~{xtŞ8^~8bXv8="70cm6xgH.?|r]t\PݩAh-O>?W+WVajPewJt\eP
O2$u?p^OXzNJ
_e"f\	ķ4ðV_k֝̰'絁(T-dJEj5+:J!Ȩ{uot+ey׆?SCNH7ps"{^sBx<XQ Nt{{]H:Y8eL&.:Bیy3o9̛"?ڎC5
ܥ:\f8#TK?T{ww^k8zfxhbJ-GT\цS*#jBy|d8GtP[P JhK#0	'ywWzO&cyeM`p/atG=~hHz"\GS0N#Ֆ}{?yI/V>g30!d@S2KW/pTϓQ4!]	"bZ;=82+DU룜H(>(&.O
ǳˣóëÛ?iSbMu99F?	-IΠ*-Y\cf0=?J_q'qIVJcT.9tⴚJfn5HNgao-~]Fۆ6*m<MW/+UTj<ä\{B]8&9hʙsbі-2M
.I_hЫ8BӺHR,%&_?-ݥe85t.@OZon!Ujmsg\ñ$+.Z
n
< d7×CvsǷ63}xU<;Cy2Q7X1?G5k%HHxNۨ9s]6⹘yH
/ >x+qeM
E}4mB8
QVXFM-%U&ڿ|n6ft9A6Ju^ϓx]ߨuLQ+_2?0iשө&_ms2 7bĸsz>jaf5<|7cl4',BՕ>.0,Ixh8:yFtqqsju*[

Hxu]Ke~tլU;XV9+^
Q
 n'!EZ5R!G^>O1"bk!
ðosd6\M;,szn>c(
7ylH[;CpEg`l>hd.S{sl"4UJ+y=(ٞ ,$Q/vUX,LYLo:Wh樃v;J9K#IpKBrf`}c:+ț^ZH#I1ͤBiZ79NyO|k}W6ofS'bODMѫJ*$
͇?eC[Վn}@޷C;{/bBI5X
Dli
ʹ}'xd:毴l_B狔pS
YxwoחޒnWYAϼsz_bbxF誚5'YPNXێ[8V`/JXD?UHo;B?VzEfD'kJOxEƱCSyؐ(lZ~F'/XVd\݃]#ĹLt07gIl<prqGщKI<c}RKkhlٱs&[V</޴<<,W²8Z}b8ś"X	: R%Se}8w9(ϮXSB?yHƧAd|Gh~߫˫wgQsPΊ[ßQJq3KFY4UjW1ׄӬ;
Sx3+6|-y/-ݕ\8{pLKpZ,}Y zRBN3Xս~&dQdA2_udFa[+}rPA|Vc2^Ȝ'R7)AQ=;["trK`NCF_YRkoB?]`Za~ga^40f
QNJq莸SdJU)_[޻Ib0AlT^"eKnNeƮXrr1Ut\q6V jr<sctX42Q-_X	,
ˌi# Ԝ,'V!q՟qFtOcֈ/ iT(|NcTj2`5Ze擯e%'63i')`sY+Xa*ZN^+OXqzZ3A+~yz (;֠<G}"Sbԣɸ? #Mǯ2j`QNIX4}=n߭dM95[2 @ dk SRr}tˇl5enw#¨F+f=%btұu.FوU؄/ s9[>S
<Ye~Ooݪa62ċ'c'2Q~*䅣*!ڹ?"r'dO
tOG8}HUK%	"Y
Ʊ
2Ω0.{\_BH/?Yjx(JxeXXyRh^NdeDole-+ǹ\,_!Ki'e"+FX4ML|"as c&p)Cu-Q3P݃fgg{_=:9tn7=whҊ8G+dGpgj
K˃֜
|+q]{?* C_tN?48E[e
ASzw]ωGl䢻P|Alb@SKkZֆ8OnJCl7(@]A$m ׌z;pY^nNa7SuBU}嵓|6JfyЎS<SP>d1js$>ضM.%!jEI JJvV?66yOfG$md	>" (Qf=\CYFbKm)^UvBPeOGO7G~:yxօ|uy}ݪ>B2sw05R|KceEUCz .
Bg1YKs˪u5+%M^r7O
we^^	I`$o6sUYmU&7S0קG%N9/lB)bғޡ^}_Y?9~A$e_"$,JKl2G:驒617<5wweJ4a\шD!s1z,^PXY"+ݼ	#Z{SV9;e?TG5*0>ؠ%
9gr?f)|5-;	QËCNHnD%rQui}[PԑWiR,? jU_5y8Tx0V\huj8Jh:hiwdcmY%1J8>\ c+5>iA0EK v&NtW0W쬐SUa\5({c:u*X
{/}Rȓy
`%iG\0~@QEojKo9AuS]P\)&T:^r;|%PpO㪕.	V5/FfNdO@2Ś:E<ip0 -{"ip3yG aݘB 4ޛ_Ͳlm}Baڎ6(6r"2@_NGX|<lU֖&vv'n)+kCanKKv'_κit{_\YġjǴT^Sq׀?)wՠY1Dv~hΠ#qU.~cvJT6ZFnin׳"vjAA$m?>;_[5<ӼLz!2M$#;S(~!q 0rBǝʹElbf\p0LNY}de>?m.նvteV<
c"a|ǁH %^DqŔ1_r߼dC] ^Vt!ߨY.&Y	u1H=?BUw9WΉosKHnW
h2qo8ׇ$Mn)^_9CU`i-ZQ%z+gfg"\T=4QN IR]x.P8W'zΉd<]MbaQWB?{9GIv`VKߖr?6sMrm/T"ȣxԋ.iZ֘I\٣lL^b ˹n"d-ZRpN&pr`;nر)2J 1b-%vq	҇Ƚ%u 1zKsn/Ȝfz*<Xf)Etd~@rYWUrGMV_/lu>I%!z`<c'ȕ^$jAiyLd7|S9Ϡ?<CĎ؊:>ѻW>M\Z{cnNe.)G']ON\xt|9u$|g.!7|g^*kAzH+}j0]l,\-eR2FeggYp/[7!@#O7NqdUC"}ׁ(Pf{{Ⴁ;7^39	=,=dGѱl	P
4<OY,<{gRuT,|Nڦ(_uoG:!Et\e(?ǾY`5$&r\v?X:]w@ftI|Ě:
DoGi2m|b1+Gt{TYF
Ǒ3KZLF%b5BoD-al{M8yXovw 8 li7jHppY3q&;nb:#:Vj7ctvSnu\0:Nr'm6ap'Oz9cqLHdoYڐn-z=đ:z1?Yu߻_>䄫-9K
k(.)x*
9$+z> GτQ4M	I]Y2Ls9l$cT1)S}i_Z
{Q%y*f 5W>	*^t>_EtB?N QΞ?8kic6Ʊa
2)W
LhQgwz[2k˂Z[~LT媻6
0zZE>
?
 KʄvNaOYNx5q}eql)=B$VXUUꂇj`)*ʧ[uc}V>	MMr	L2ͯUIRZb}\!/g,sv	%%K,G&ixQ,~F6>4JN o&ď#a[V5aڍ[%ߓ~ޗO_Kv|τ +P].>MMK\3kX?S2	Y4R/=+)ȂW>=~_ N"b:LA S٠;oΔOh/|9̮%#SNuЗ7L3 UiwB,tMR[&0#{S^kKWpYd鮇O[Nb\1d3Հ{y2ў\O涟2+l.\Fm.2W5!V77py|e׸g#_g͓"c61*,pb, 9uQ)pBS/NB?B#.I-UJP eUP
Fp	<2O#
9xӸrOr8VPPvqqopA4S&\u(#9*	@,8<e:-B&Towۍ]=殱@PT$t'cQEd0:rL
Td00ޭa1hDdZכY|В\ϟjKd#J/rʠPyr&ɪC +m\=mCL=&{Dhof+:ÏUYvgsӦg<w{4r_YYX76ē
ҕ
4D}?}?` ߽Қ:eEqV`d=~yCjN7qռAQACvLG/K^[Q ɍ(m7]3)ǤZ7ܹ#I$&cbwc=]]Re+^ǐ.p"tVodh&UH3>i!J #FR;zG- ~)ʍr+ߝϸhFޥJYviF.|λ pc pwI^<-^<7/dώ`s#Q	pTdR/0|JY4	9TR~Wt	#fA'Lwɓy&WcSNqqe	t2\{_ּڎoIށ''Ns;J<ⶺ']J{Π{{Պr6)sk(k=퐭nQCAT+.TPr'QN`ϘDiӟoT4{FE1?Zf ǬS3ꢧ,E2*;X xt55(nA,w"8ē,zY2<`4gѱdZ	췕w/91<%y*O2T!UuR?礟wa5l֤B&@CpŐv2 tn6;2({6kA8ܺq0d/5G>Pb;r:H dKG>%JbZI\S+IGa5l5U^*hW?lԁ0ű&m/SE_mi5Vk
#DSʲ
Dj[E>)Vf!)JЩ}`͹տ	vPV7&Hu+hv{cМz7)R>=q1	i&aؘ8 r^a̓?vJ*%P@'!zR?l܍ӔZ(l3UvbfU#ދW;ʅh8q"=K8|[XuwT~*rbBC!9J87#j!vJ,HJ	rWGW&#[}ۄ:f̰h=|k.V#SIG@C>Rg&CJC:i.;1ye4btMUWLfh>P574u%E1L{8r-ݙw'\*G+>Kx/bȐdl<?X"{*0.&fOĎy -VAjvU/.byfh[tk:wvKOͱ+}v:{d8ľ~I;tKϣHNED,	/T5:N%Tע􊖼TѾ\ܴR[E
yJxq'>4l.ەCEd@xvn\_\Yx*O:q ONQ400J1+DrMIc9U^Wiq;Ծ"L'7~rφzYy[gvߚW"@1rKsbRڐ?͠iUcD<RaҗWs0,iʫQݽWf߬*>>5ox^RM׃WX2u'*1@&F8c  QC\ur]=oKLn޲{[3Cy=KKVzYQ<*Z/7Skn>eF
RQO/ld?.6Yմc=tdaYuRĴG:+Uk4TMtb @Շ
"3ʟjN+UKLMImV՝!1#/'G	ĒL>*`E>!Y#[,zLzcl1?flN{r{ú3qܚOo;;;m]uZ;W'W7WW"TcM{>c>ﾶRﵽ%I^u#G6v]N):X$RCv.MOS%\ڮJ\Ys
m_k/eGs֮CuC:sPϔjaRL9uʗG8)rgRS44
;	t.f$8N7u})7M@9JFmW!_巉_5D6ׄn!0Xy%EVG
WRs/L
b^YW45H[;E.ެ|
uj(=j  *Nͪ4UUpډڎNLtf-*R:3xe(H5
xcDgέ$
%v߳SmgA\917N9,,,W
Խ=U%ƨ襽ܮu5+7ӆK})&
bl&$r1NzT_6blӚ26%[i02Edow~FK۹6J=UwI,ewm])]8]R4HU["Hq*k@.I2_ 1ݝ[ kl\R>+H-$j]-"Uk挊Rp:YugbP%_G2T֡CQ C;6NS.ݮ) i^DMѿ:;}s}xOW74ER2baRgz"øЗZ)A<%}aFg)\2f,=ιBd"_^-Nۻٮc{7@"H V+Ȣ{UE"⥯fbsǐ~k.%woW&\Pa)k%ilt< e0GxaV%Nօ/#K-a-Z	Qy[T|Ӝ~h,{;Dzہs.coiǠֹB"fAi::݃}żnaV&`R&/fݒ#g]$]l,4}`n0:sx7a)sGߠ#%~¥Ө[{-s|ַn2i;f0?V'i97z>XIpϯywb榷)E
],<@0b
6dh*g=3tCkРL(\<]2!/h+HA(J'ӆ{ܔp|Cy:Á<bix
O؎աNz6ͣHۤ@)/g.4sd>ƘGa-˿K!txYQ&t5#C1:?]
^([wOOݍrHuqɳ8fUϖGgE*DR,
0qDA-%#ff)dJylMTl~utʰ-L4VZ2'%c'.MϜ|~dhp2
4N6:)MÅȩpԡ,s8e^XS}̥cWXWh%'DJen	Js
|5.QoS*J0ƌfH::kghW%{G!0ڄ>O$|7}Mz#)VHQj ]ubC
Ә.h(^`<-k?	vM#t]SyW歃, ԺuUD%&$'kofy0y^W$$QjV緁qĩ۟ ғԂAપ;dU$zy.
'#`(7ˑgt`̃$;;ͫo5>^
!G=:<><=N;orrF
8*êcDu\\_)f;IG3ܺM8k?kyBӬ07RNg o^F0r]'lO
>muВ2Xgh-2ipXnqO=%$FtXkMA4_^v/pI>sƜP8ߌ4_E?^]l[e9w:ܽmb8ɚY6`
Qn͸(sLS.w5xSG)*eb{~DPn)Xng=A'"1Y;AA;KS*.vzviw%Y.37ԅqώtI)𯯑~;q{_ʉfg=._3Jj
}Hї: wwd;~g YFeg*i;o9RRZ޶	o8>׽|5F7qkPp{г㺉  :6ǏU*&gG]ηVǷVz#WC{4<l_D[q1c.oju`wt] vRy.@0[pp]sQX=z¶}Xҗw#e|UQdəHo:@G5UUp8XK݋󫧭SQ*R{ŉz։gs=ۼ9S#"VV'Bp~$̨(ֿ<>;a{&OW]okgwwcMya1Ζ8
秛oΏHmk՗i]V0mO]/yj4e<a)ٱuG9vЩ ؿ2aɖ8/I_חˆTnWargCHhEcWsi&=Ƕo$-!Oaڸ{ޫ-w~봹lW6i@^j#8GN֙$:Ȣ}P܉B^bybY4le{$quN	D ڵNgBp MO^5䶚}EHziQ![m|dĬi(v
4ahMR+jN/rא>kf	e1VVE"z\q.,S?ABh+zkF`춏V(XUu9zt~LGK5Mac]}[1`ـ/Qu*X)
0^~}hC iD77g&0o8dq$%#\PVTt^56{qP7 Uj|!%uD?\klCo>,Cx%%3ڹ<-tXs,'3e&k%0Q^,(M'E 0;6	HXL	39? ԩWk6MWwb bcX]V*y"9sU^5-$
,o@B?OȾ>Ƞ0=bˮy!f*_qDrzt&ѩĔG|wv<m\C[Jo(hC+|W`WyI6Vv^W띿 5nw( L`A-!ABHBYF3@ه{D`堙A\$Q
v2"I&N(rN4,?"x(WH>%V Jۜ*,&h<N4!8Y~iD ҫtHU>cnRq`|!ֱ.S`d`Nj]T&>/Gy!Ja;SVe 
-4{l7qH3&A>T[_N>]
-(R r5G;·:zcmaVZ5Du%C<uV)Vn{~%#so螿yRW<Y0n<@='dPWq}|]fra7; 6$>-Z7{t^j9"R֑QA.z
OsYF{=&`bVxk|
	抰_[0
;#g02AK.EbK+6\dPtʲR	E
>_ LEE[z@NUZzY|ݦC=CVF'kQo74Uf7&ԅ|DH^U/,m=IEYM@+j
}hiv}0rhٰ?=cG	,5·W_:=99ouOWקsG8RMg\ӏ/Lsӽ1Dr0eHG&LnY:ڛO_^GBƔ]U4~winnt6 >,ᦣ"`4xLk.*KO\0=Α[}?ҠUERX~ksY؆2hk3Hdϒ<U`-_04'8
PwAt>цl|U+f+&n3%U^{jdj~#kSڥ@Hq	AZz_F_S".۝L
_~_+n2ł`o(
g> 0bC]PE0sս೹O7%8OKT>csվtL~v]5 U+g9
`}J9!k: >WCZ$G$Apz*	(J_Y`j&:f?n7#cX|j&.	kШzUb^54cgHUM#ݍeQV*}w^ܣ@+S0φ/-^:º'tKF/>Ŏ9A3RÍG96^z#uf3sW
4]d':Fv<N4'X9ao`fͲNBb-ie9Z\L\^
2H_{}sf*=VM _qKT{8ւ0<
9e *+u/GinA;~QMnRNbА]s2JMoh(A@v#yLB5#9Ybr*(X=zNrq;ͭeKӈSJ."~e 8Y8JP:+-#V<OXLi4SasTy~l]+/%"JҮz6'HG5prQsх/{j9RspYj`u$IQߎUgZS ˚';>Vߦ=Rײǖw]--tl~&Iw|&V݊b϶j)掗%VjsnnsnrLi+`nloWWuJcFUdrev|隫f`:ͽ< á;-(1렀ikۈ悐+[yAQg\L^˔)e麓eQT.DW)it/zO!.{B6&\pYafc
9C[|e)W
;I5ƞeVlE@syhK2]T%cF'z}e\3C׈Ba12!J7$&K$aovnL0*s3UGX%Q(>*#o'Q'Wzy8{ҏ=hw)ψ޼ǟ>M:z,g:LGf	(^Y_TabFᏯWz=8n/~8t3@u_nJ[HpkeaMe:eUَ$Cy03	C&S>QTm#x2=	C;o=}^:n7}<kt0X.{s7_6"+>èҷRI۔vjfZppqordY"c{NV@=-zH6ɌN<8d
)O=;ƥ(:VZ؀!&ʊ,aQe!ӄqĒF(zdkIwXV#)RjaaijGNsEUhvu1YR'A3jRKѾCw<toeۡfE,#y7yI?OL,'Pg*h_&\#׻巕C?,wxf[j@lfVO:x;nj}!1Z橝OAfWbӁ#T9KJ%ܒȨyz
̫t%ȏVk%vIi̎vRJo()gQ:d&(gpE
QZWvnfD{4\jN龜TX>ɜFzyTя8ur*۾߽V
L'Nxۙ)\eRzhZth1eEX"{R`+9eBvW0^ Ij֣1*O-ϝxIs^:*:=u)w@-^(P\kgD\΀iE:eM n0M{yjU80hȑ)uښQo3w.N&IE	Bf7M^Jȯf.] ?puɮ"q;EgZ4jJJ
0#ť'"hdڡ\6A,-TJU8Bu$\U+ `tơiĶj-آ
qxbSYFt&(MlY h'FdW~8[a'rrHvt7P4m
BJ1E#S2c-0 )`AW֗w掓Q~fig{Ig[>ZͿ['>	TtZ@x\*o[Rr#t&v.cPŵz4-߲t)*t^{)
9W
s?c&HI{V=62;ߴqfŨ[7>k[?^X葻US{BE* M1Ѽ Lde!/-ϊiM/}CɓH٠-93[[VNoJMdE>T>ٵANOwSݏU/I(*}3TF{3۞݇]a+
raei6*&ʜ/y̿K9Q-enwcA?^ܻݒ>yJb\_߆yn1!޴d'QZ-);)%vnBfFBEe2(y3wʼM̠VčEݵ&کg}#$uTZzʸ(Pna!CH]N;,|⸖`)rC@m8-nQԿuA%0s
sgO^eïJ2R+݆Y_\qQc?;Omb`׋al"Pe_'h,x|}B)c1ؠ3㦿00úim#7$dHBOpVfV~ ~jpR^ӚK\5elKڐu% <QU+S;="S^2i.E޳/!hފZm44
2#3V}K6O)ei(#`M:ޞ.oZMyϥfPqQ*%k[v,=Vj Q<z9>;(в6!1:џ./.TP%=.ۼwٗwxT	S&daڵF	\ZP0>ާz8ƯGo"lthE}A.l}_ڒZw[gb	K4*'jjۊ]9,\,eoZdlmm7rY¥q FG냬5=xV|^3Kov]
y7tO[-|9ƫm
4gxUW'e+>?
_J&țC=~ߨ_U/Zztʕ%Z@-orX%cA1]ruDsg\}3w?lRkRh\gl#S\2уY
ٞMAl<.66?>ܿoi'y SqFI-irWGzeO*2ac7oۈU^4vq
.e9,վs1Gg;+ZjI3YE$6%
}N*ഢ7Ti ݼ.({[XPg]@J
0+	m(~JyurRD;4#->G[5J,tv9`lC	2_R6+95
sSf`э3R awMQPRcGEnA#.ЏHh:cD M)ԋJNVt!l{Oݓ_OJXv@OY|Q3HMemV%.W4)YcO(k/)AaPƹtyz_&Jm\H3W7XIpu*a%TGAa ٣6ywH=bݹ"c0,7ghΥ
FɊz_5±</k
kR 5z0/(:(6b!X*<	nٝft[ѺLwT(c,kµX]y5|{Eh8A?c:-.0g6[sȫ'1ʸ_rESR~AcEЊ-A&b|=fy]PFػ~zO?EwP݂foo?^t |PCEd}hFg#885{oxZéh9RI&=/x^nx[8X6Z/F?9CX4Qh}zP-.{
UWu'g.dx+}?xJ4/!h[GZUl@Lb'r5N.Ι5t;Ϫy7u<:YE埒ceͪwt(Қ-LŸOò!mErnR7/ί܎6R1+56KxV=4d
KI9Qvˉ%uƤЅdݨq2fnӋ8dBHbĳՁ0yo&P
,Dk#T>L{k;3][wnO _Ix_"eFoR	oW~
]rcr#Z1.vc:%
U*WldelJD
+Cv\lI.`/llGj&#:4t?vލO_˨uiIѩ1VH?M?GAy6c4 8FvWgVo4a\h0U:b߶@yO3E
u"c]SR,՛H(]z,Ί*R5C%pr"? uv81?ġ{_NsV1
J<Pfs^qu}z6,UBq5G7/o.˛O\]^o.
~-	68{hubE:*"h7mJJmݷ*?TpkpkQ7.,|Ֆ:&뎗D6MQ1<V3nm΁	rXvnDiY ;H,P5ajg;h_nӦݺV]~[b6ƦߩV<N{;)
g8Zb% #߼˯Hyg1c)Ou-H (YN8r8lafn-(N1Ft_ SHt8 JH?[='Y|}gىWc%J?:Uh
p:3fIk% U 	Hb.ޮS!I8(zZ[Es.	i 22v<})6ScD㽛^/I4dkay3{Y#<iIIu3MBəBp9&?XBB
CVY^]ؔÖ,xu+/e[mX0L}e1D,5Pz3gH^`w-îlxX|4P%힞'%ơt[b"_vVhdsQw*!#+͆iuU*XӸ)=Hل9.[MwKfՖq(7I_"F9)ɨL[+Q9yB# l·6JvmX?¡j`yL7f/kS)ߧӱJg\cv7jrZ˿s	&El@sofMխirp'|^:$+6k W*䐱(WjHvld	i^Z* +QRD<t4clC0gyB-sfs(ĂzNouNC$3% өSַ[Skp.h1.^RoaXLI=3mO=|.<har!фD*hF33U,ɫZxDX X|w<KZ@
8dRVh{['Z=5Vմi>YQ+9gũY]ףbC9};n4NNS2_^ZϞϿ MϫYme{*D~1[.C
'ZrÉ7տ;îÎ;R9:=0!zLW8O,J=
2[R%)g9H	Z#SɃK-tOn=>
2P<weLFvϫY*"*Ql=u|pGUswz37K1^anijOg8ml,RRX|}KtbtNoԈ::isO&ZPǕuͧZnTh٠Xhyap_l)V|KY]+t\2pu6ZH^YXHP>["#(5S8LQtCMfWhyJ> ;	f(`:eÀ	-Xh3qW<}.qYNd
U+J#b<XJQ2QdȭWt<ŻK_>FS5aN:		O!N':uUǑ'ϔ8F,QbƵxBg/[n˰vif}wκ$xYΓB3}2	t9N3w/`v_%,*X	=;ab2Y^>f
F2H1&umic諧T0,v@d=IrGnQЧo*k$MLlyJs&AUnŇ|,H6I!ˈɻ#2r)|'hbeKmVfZf<`FvVșM^j1IM}'oRLAa(suvg5AT!͛c6JYmH ]:dQ)9*
5a]'JS%s~CB6@{Nޝ/d0qxDBTRqa3LJ҇f}9&v^Kv@="#
+<"?P5Ύw8΀g:&K_2D>.1U;[+j~hD܀v0hsSHMhq*Ag׹'{S}38I8D*t0\}N_=@&rl%wXs*YL
j7+XpUg٪ ANh@$4
e ]iphX%b"oqwt4A`%҆D?)e4)Ք>zY&eNl
tѴ<?\vu=sx파c8%KT\) Jx˗$?MTq	ǎ* 0	ՃΓDpQ
f 30VF	0%Uvqx/g{Yu_ZH75G
⺓
̱z*Sc&  &&feql:(yc,ߢb?ɣ;4O}49sl<!&б4NH1%d*n/#wOc|OHF{=n8| 0-욖ըn\};|
3ǯUŧ/r+dbuQa3ydܵeX7?== =V@8=?
K1;ܴ$zci.]/N'*daOdM47b#{'!0AV!HvCaWNQi쪞m86t5*qخ:E,Rf+dR߾m1%a\%yiG&[͵۪Q1$<s>Mk4rvOTМEǇL]0KmJS()lq2Eϩɕ
h1%;~QwBa@B(xj8Ny'_f? 3˺|Y/_lz-OwEA|ъ\1AByc9_{9s$I	jWˣӷ'7Ī_TlЇ5el/4cwSN|FSY #z#G[VfǜbG7Wz|
i#E4>B.Nrk|aTDˣa~f	ΰ:Zq:[Nhֳ$/%3=DŲPj_3K`5OE6M~%! |&Y,-bx`AOQ'qDcZUSW[~ع$j 
k
s^p^§=gev41e
gK0	9	z{KD4%bUx B!ZNW9hReº+.M®z~*,{7RBIa~;bgpUۻG?PhPlj7ptƕ :Bt|5eK\NeT
@sct̾VtQJfjv|NjbT~񋃮ou<KxZZ	\Tl'_?윻g}>\$3|
Ē8P)5ue*HTv.gNI\V?bLC:~{!q^JN<s#"d
p@ev;ZgN6Iq	_/!Xp,wa$6<tJMv^Z;S	Srl@D%iJ7{VVhb7ח?uO]zCJ-@ST"ލDBb
	6mV/Y0AǞ g䚗G(4*rz$==KŕLذ0'h5dpfbI&	]_L+$e0ջk[OEθkw3j,;N>)]Q-jԮ:ZҜd"8G9r^趐hSW~*ghwEl=xeQp6
t.*r7@ԛM!5<5V.:v#I-WV^"d[f{9ݍ${ddPp(W^vWWQ$#;ggo$gP+U.9+		ⵦvxm|tosMԋlxF>w9GcM+P68CK
H0hgaSg]etT2]@AC!Al5R2M8pˤ#
јܷ/=M #d끺bE+Z]4	VǐH0naIâ5fDt[z!D@jT;.Gp}`ql5cAΣ?r l4uJ=%NbRIq)P瀖 ̢:A:+c"/MQi
$M"Zғlr؛:Qchu e#$?r>%}E")lSݻq[5-+uTDWCd]`|-1xT +/
҈DK=Q.A/$^~E%}
{$Z[xg>Ǌ,հŔ@ DY7J~8qfQ_q9`̕a:7<N[*
NG(u^<=	
P~"'d?jmƃȕMkBڪ,h	n)56׆T= :;0')`/PDE::-%̆;@b(]iD/W/	D Aٯ6r4/IJXT팩rZD)5 ;5GtpYױ	 
hu^0xD[H.~Ix;3y=jaqn
`,Kz;nsTTAz[z\HMFΫ1H;󍶈Y?d@kWAN%/)?vC[^&5R'n~ؓ/}ڲudU;7P1fK(,1 t&H_LrC2p/w>ݫvn}W~\s ="3'
`q.VpEd+=<6<ڭ,X`vWjgg6ºr⢊P"EB
%8#˳Hd"	gF,4RA<>,ua&"(	UgRM	 ZabN)}w's7ɓ$HK%pLT`YCUЗIF 59a.&^%/}}zzJZN^jnx=26j
	&h,J|Y
%VP
#b
Nlu&]>w栐z
ҿ@ Up~sۮe)ʗ[\ҿ!BCNcr-\PΊe7-kfr.1d-+ӳߊ̽FehGb>핷$vw5cabSIĄ}*cQtov"ÌqTA0kWd|8* 'fQZ#K	=ArgS9 %ঢ়ăy/ᵢ}G4 vPs91Q&]j t) hb<S#
Bx!qeS.RK0!@75Nz
"c%)pBDYX
5[#p"o킧*tfz@sSK1{CA7:S5B?a#%fuIc.3(Xf%<l
i=y(7m67oޚ٫ox;Zﮱ&M]PQ87#13ZWo5@Fق:bvgMet(Gmm
L|$QƂ޴#,p/@?9.鶎%>)05?]^u5|4"gw~@i/]	hFuHt^vVv@{w.y%w7^@{ ?@|P=z?(#, Qr}]XvzߑN Ťt~JqS7@,f
"4l +TF[SɜvVD:L]YCYcin߂GN^d!ڕw\ yC'~_^$|
LbGs̑P{FGvԋs
E<!u9a:<K$TȱBxwUÖR؋mWJ*WYsfܜmڸF6s@RYKgTg=eD˸W3Jvs; <LI:ڭğuE?=g4|G2s)ap f^W1cV_f\谌z
m[Uesi0v<;f
m$ƣ3zpzд3ɫqI$'<_n_?j羼+k2	dR@۱e,`?Zo^5Ih) QmH^|!G0PV/qd;EBs'MZ
T(Q`~#)@Q܍|@7Y3Zy\,tG#	/Z4r%]yN
jA.FRvvM+I͈j ̎+VύmﾋF*8 D.J[!
*.ϸlѫgc{Tƴ̍4X5="Kdt]
7hLԽі,a9V_0$)7E#*gŵSϫZ0q՘AH6bElu{GxXA#1vOW1,x̅׍1|1]ٵVgI_FTD+!
G
3U=״ZE TU'0"8|w:QD0#˨ mKM4zJ 	ϳx4`?`YaU˫=v?ow̒OG{?]ǃQ^x/_Sx=y=kѷö"{D)Ϥ}"J7HADRSޒqihަ׺ix7u.Na{+<͠nX7={kdr~,UOʙkwV~J1D~7&BBIӨ٦`~m_0Z|_vxԞ=eiф
@Cԟ@Y*eLsHQ}pډTUM]0!~H16\?=gWئӽ=LTQ]A|Jc:x6]xI&b>Sbٴ1WiB"Й5ZzDv(M(Gp
h4l+j>5/ai<@_uq
VB\~A{&T4JK-:'e_9րȟ*ΙGio6͂USU=WMX_<'2'>3v ݽJT+Ѥ쐉{z>uOl8"٦{ϜMK}9㿪Z]\|YFyT6p+Yl宀oSaB%֨q>jundTe;{Z*Feϱ0֌HDmn9`_$c%A{4Hn9hىhro_:]=㇖B?}-*@L~){}#<	j,#{L,HQn{x|pD'Og?%ݣGFC!&
ѭq~_ڶ^;o޽O²< F_(8φd9o,1h>1#&ȡlLn֢NiDewāZhBo){۠%ƞ'\AS
QhaHTxcƘ)yb^w-,3;Bt2I_O!
zG2\mJYTnmb+ˇ6@Ea@I0SknXZр,)Lg@L")xݣxJ!T$bk'bOdW'_jRcq}3(W'R=%©.0bexbr}_c^<9ܤݩJA{TOHv^T]>/	xfMd~XI+eY!1D8P/Yy\lz^j&*&kˡ{E%~ȏ*;5Tdϧgp,&6CWΏ0NҍD@-TkjjM'3&S͸oWj8>	v &ܑ,/In"̸amH{42M
,CK6lBYKeKmb:ӤL0/[S'Ƒ-0lnC◉|{STvl D"KkB|čqA0_;Ok]o!&Xw9AZHg~2Ehp-2b$I?DcwTmQc =IqIjO
¿tR)P]PfWy9V,οԁo]({/rF5Vd@Dd{Ya>bT1nBx/p՞kx)%XtB[;.еK>XsHgUY>eùt{bcZWaoM
hs'**ςO߼G$v:x=YPmݫYzgh.'hG|F(>0X\5=_*U7y<f["-'
0V7W2]VY+]^WI
̈́Ou$9ig|zw^<ow{~GwnuiH=D=c%s)6QjޅxIxgpRnF۟Bk  lEl$("E"OēInY2	V7ןQUWJ?*%П/2WuJ1zeCK"I{:ȕ<&UDiv7唖[B::jOi~9IJؐX	b|ge6cSizYɫ11?'^w>a7kkd'ǦSOU1S5ޒ%Gϱ<@&ҎG,|fDAU՚|;LLFϭdKvL=BGo
_[(W jyL9V!&ލƕ''/ϰĞ_z!2Dej3!-հztRՆ~&Iҵòdf
a^hw0?kzgoVtHW?gMܽ7 sSN5#jEE~ݕn4 (rdףw8-n-SS6ҰK#`|}eSEMfx{Mv\|z-@MƂ,[c<} Pi;O7ɺH۠Ђ*d-X.,Zsc0~!,`V7*}Q=n؉ו++ѰIW`Onk)0"B`Z$CufG8yț@&jnolj U	l[owߙ$?8#V8TYZ 83Elu]qE,П--l艽Kǵ d~:=q/Z!\}n`U(dY}Z;6zNSSӊ-	FZ_\b(V0ԀL=ubkD&~ _;;Q ]VT]
0
 CO1fn<<oi^QF]\+%AQT41pY=;xqȀl~ bVDxhO	MXse$3+$
1
mKaeʹc~	2hIAfIKVq錌\/)$CYϖ$)㡥?B>^Ԁ3F)-NiU?>>?OɬntFQ	gׄE{ަ?g}kr,~/h
%^Va-_.Te
K
[EDYГrM:
H嬞mDD-G38dZ@٢_/qt
rs<KЅ
mW
A'FFV:>:hIR|
GMFKr^C*<DW=6h<M-ci)#WcҀ~V~~<%+eh.<'e궗;g|2Hz#C, A2zX B5|WXأ|Uߦiqo_%Q $fpGJnaSalpatXm*؏(J5JEޘwyՅs?6UWIS5Xϲc1zji70߇";TZvqwWǏ6Silu,_(rs{pM@6QQeP>ƛ@ ~yVZUدj-fu
Gf  \&.\樚*%IWB5TksAAfeŹ<R9~xV";ܸ/^5@{j:\TD0M蠊K;lDF+nkSV,7aiRSTK/yIXJ5$xI5w[V8rd@5]ErdHm`m̲fT ɀ+B8T `>פMJcuoL4VчBz(<$=?{PYfhZN@z'yFK-_|.nɉ(B&/z \X_oŃvҝ흝PG[}CTG|=򭷓K!$V#vre퐪O)y#%ɱIm
Ig5hnﾭ[:Mf,H<ergOW.5lo_%ۛ;qj~(48yS>ĝ+CgoILɵt Hv%Ϻ#sΒԮ&Ng Q
~C/>}2FG	e]7!BW-JNwbyB]P%jS ܆^)v$ڳN_ʗUg:D"Dk\{Qx`%)
ݹ;̈́PRBUW6l 	?4(0Cul(TX*&) i5)j%#eh|ǝDbl6liy嶅rǈU^z8Q#"DtFl/P_6O_ve{p~T{zHělɉI[sId?-Mt*ԫFg[SWi~v+Vv!8X⇑?t	*e@uMX4cun 
)ׯV4"iYV]j}f>Y];:fu_5u~ҥj{0ڮw[vWlfi a4c(/PH/w=K=*zanS莸> 5HƷcؚ
LO2w"yYF?nG؇U:vS8zתZN/c-I54"G|`9+4`Ј[bMǺ_b⎏:KjOI04mzͽ*3Ԙl<=E0gyvWΌ&`{GYl0rsmo.I>	1N3p	285Vd{tvNؗKݛEARzvht
@ 럓Tj*WU=GeOtǗաҸ9֫*]9^@(	5fd-ME'òM%]Q#Lł9?9HNOu2;THh.zSDiU9u"n4h"Lz`8JX/@!:MD&XJe5t	g}8o,rp6züc)}tL(p79j T׌sP% \4^OW^țֶ	8N;7[5^!|)vùdQ,AF}Ojl
vO)K#*eп
8-3PEMךcDٴ|gO/6ngMZt9噌.LWUbfǙt
igCTVt&X))7 ~(

rzv  >N{mrrCS\y -䥹ޒC3@3!V{#d`
PՔg#)eXQ#Q`̾U>n#r&{H`Sw$1Qivq9*BV ef
QwҼ~L/
=ȰGFO87w_(nͶ`_& ZT|T&6r'vn"F8O` RyQxXQ}-+Xg̕Ʌ.}w!۝i*],\yUw6U'˨94tMdyҧ51T <^v>L(OX<sOӿ.ê&rw#	MC1ʀtd0Me@l{idyYyib^v UK\/'AZV\6ѷX,GDm4O?,I'ٗݽ}1^#I{)vEm/#
dw0![./ߥlx_w7߼y+m\j+ѻuo:j=,b샸#=nf/K@@MʢO2BWFr	}.tԃXnׅyquGyT2w*\FIwb1Xf}=;^`/IT|r+Jx-n60қGxi8X@Q){2swb/*qrZ\(z](ӏG> "%.HI{t T=sp;&?00i7`:V0 cAJ!__̆q?=z_\vҴ}x)v7LT0#੓I.*rΪOBgD(l0A]\!7PNAЭ!̍?yOHƢiĐHuBy1#pbC~:*ߴtA9m*ŘXHf$
%ƨc$nrGkջ-s*؂Ev5n]r?nl
<I~U*6ty[4ÆZZ4RbSUطX__߳&FB<`$f#w	J ژ~08L/1-=OVlu˙]&-!f<9\w9`vCZ+c[%nae)o^ݛoo^5tٻ7WS8f*HuK!pؕZl0RʯHu8 (cPy5#ǏՍg{xwvu-amǭcinѠj'C~wr7gQcxLv~tpXN?)h?i^W-ÕO{E`ܨ}(A?/]:f6j/ =^5R4ODu6w2CT[TSiMIrf#vWCߣ
Ok6+@mJ	j%ݝ?5	EZlevo&Vs:-$Eˤ!)L6`lP0ԻYpaVOgeJ(:.= 7)2s֕%io8p=^! b #eKVE<Jݛc[ <X:T೷aZW/t}@
)VĞ{;
YyCp@k8qύ#NDweeYV(RHɽ"
J!E\k՗V!x6֝ a:m/
;w(rfk!-bD_$J%[9YPH%qm{~DxL& ֪ʧ
vXX)*N$~'zJnF}[VdI'ͲBѫ,y,Ž7K՞]P9^
zrX
Wyc̑?CrỵBYb!fjaQU:l7̰9Q\"@HdJjKw8%r`gY#nD: FPGj3$q*U pqyCe:zr]黣O6@&A,UZX{@4`ǛònSqkv0}MUT*%	ÈXVC
Gbd+k$sA;Br5;A5 J-7m䤠WՂN9OkۃgH:`:S{McZ;%:{XRXDe&
}mXKopn)o݌DIde(JrW 5>B
 &)4+4T%rsNͳ@K^}r:lzedG,˛T]3}TU[^AoU7$-5uD6|pRyJofPJ,U0K&?8e4-q-ZX~o˛:~'<NJ+>09ՏZ7CK;0O,e^b\ {3C]''QX%씸Ez'*~iwLQ-e>l\Qdgk];4^tsQ^,}O'_I:bCj\.pXHsGbeNdzʜa|nM"
	ʹF8Ѿt̰*BǦڰ-Df*.ےt9 1`yB2A^1eՍіWn)ZhJ7`>βaIkҽ1."ăQnl5ro*ĸYLM)ҭQE- BeH_U˂m$LYpf8&)OݽӣEzԗ ? N$MYel4Wcf-zaw᪓U-zda
?Rʞ~6IӢq'@%6K;k`	3G؁+`l*cdl-ߜ滋t>g-;
u+j92$S'>L}k<F]$v֘q:Q\Y.!KfZWN6լtRPԁ$iL@C5!|Dj&Hn\Ke<|Ox GANѿ (fb3	4[H>#/Q^Ubh>D&͊j.+&+S^l%#ޯqkg	{yC]p,KkwjԫM6)K`VfӈnRUx36[ZXv&.f:QPС`͍fLiND^paTds}T<* ڒ5=Z4A_$}wmpKڢD%#vIB{C6qR<_YL.<:w$;v!pt?
^PͳNϒ&{(뢽]Hyѫ'*	+(;>hlIS&,	$ݬTfJ'WPdԿhdp\[ĵF2i>ǳy=hM6ǋZCu`ĺU!DmjVj̤?
f7>|g	,'KO5P}%{@51
ha7 jMϙ2B[=go'xwzkAԜr=$`,$r&:o5DMN}sm@[o7-lIR'ߤ[j R0*gCVJ!3h$4uo_n:jS,M5z+u7Mй yUmm7xsRܣzfs
yI6@=_|<tʻ> DٟY3. e8lҙgGړrn~631P˲7ζC-VVt\
A7[qs2|۩лok8+ksB#g!fV8592aU	˔񒲶\{oyD㳋Nloo'uPIOCIHXC}tљa(ٚqp+L{AI<D0JhM,?E9rs [c_\}WO߾[H^
tP%IJ|^
FQ!ipD53ϫzLLe&[,TQ6}漨*(C-N+@B(vAj9t p{A7ao"m؍BpOL}<R@3P+~`\dI{)3d^RSHz= Pт(sBRdg}d ̎lԣZ_(J?HVG
k>D%	WrTݲu-9K1bz</B05.s| 1ސI>c<dM|o$BB鶷o6%-- ؐۿ.PORJ
Ek^ΖZJ-z_|WmyBҾMAEI AHGT//!ʿeɜ@WZ'z(x 51b:sݭ6G=U18[?~⭋QטfUu)ԙ*	@Tp,S4>N
I_֪v{~~NІmCm9Qy'Gf{N-1bAݽ=(UgEggA[$k|z!Q.'j"ΧNrEbuPZ鎕4KTJ I ZYs GC2"v:b'7p%_*_OY;(.u86ԝ-Bl<];weP~1/ي:ϙEU*泺<|aVV2@yl405IeK)9ݻxLp񨟈!Fd0oVkXVsgjyqf0,9+\K-XS@|.x ky$)9ˢa*zqsB}|i
r'W{|I=9ɽ4?^I3~W8>:,Q[v?ؙeăyEA&+9¸|
L3]ް!Lk:!2YDn-~o&\ǝo}}sv/m׉`fWRxp|,q[09IS7SHٸ̤qe>
PFw_#Eq OMiT|E[<>PK?
MJkNa-1뺁tA<[>JK|[nɨE-:7ed=̘B0W7Zz֧ba$:Y8ௐ5%^ƈʟ#UH?
r	Rag
OB6d_BX~ɪ~QH~s*
62G`FK9(h7[|U^#
*8$hi*A#IAp:Te]3p[!\9Aʷv,uTq 'D	R3$^> TNy:LQvQZ}BeO:D엖!r<a`)Mϋ&ES*3-˄2Xjl6
|x)oUtfX2]tw#ιG?&(?X2y0zspۋQX
sW3">Jl/h	WHLa|EтEQ[~a,]Qa틣kswӭ%knv !KѠ}ƺ,<a@YSÜE^%
n}||
&6*v!yF\W$npj^*T.@u(Sqe&
Au{kl~0y#>m÷s~t~h{$8_שZ&Lժ)K+WO;9Aգk,fیǭֈ}FDQW۫rUJA
͘
/=!r&I)8i$9OI+5.	OȍWno^֫Ċ\wovEڒFQ
Ɲ. ÇæG=/Ⱥ'6ȪdK8^%́?/8" -cLe5,7]DE;FpgXLxM|=7-	%X^tkxcGęh@5UWsɨiw(.;3QLFAvr<[]3@JSHc`[W5bIi*wHޚ-h'h!guG\٢!5(8gJZ\<&y/Ċz|fӝi6)׳OХs9NR!.aKw(&BPlI)5؄-%,ndݑWBC׊o,7-
Xwg)K4ܹ2Fux+mpm:<h/El} 7Rʧ&a_>LnA\Zƽ LL)&19FnA}@S܎$˜Tp(l3Q7F'Ar29.SSwTj$RCb?`oP؍\+Nj]3qF`y! A}xUt>j͠^͝'<-?
dVOsk.blϬMȠxR}sxgnr?|`；4/gm)^Ǩ((MB` gʾ[ڃ:	4(T3q釃YU4` 
{];-iAwqu6n'
ZNp7rJapݟ>0| 5%Y=&h x$QTZ-QߦHQf |]9=w69c*F G͏@i?Jӎ:d`wV=CɤWuFMm Ts%NԤoϸ[U7/e1aDǪ3sF:?HfjL#cMGޟoVzXѭ*icRw<c,|NmqYCɫ魻4fQj`]ڈ⇷)@"̟O:pEȐY~ҺlOlW\O]_롟
VFуJׂ"!1prCfH6zn[%lf0j/(š̟.ə	T],?NվR5rZ\tlC :#i0Ԗ57҂I(Ľ!2.m,),2bU5xxE5}t$&`Z*nf+ .*pЃk&1kh-EV#D~vϿ6(˨4߮+ h4e5-^IL*wBk`nrC9%3R|uY,lF#s9A 0?XO@gMdL2
Ec ݪI$q/GoL*qLn=_+tJaiD*u_O y&0S`QK$VL;sY"WmH,zڶE}76H[2Vh_fSIS8Ɲ ڳDq&ƂtdX0?F~y=ΪemP/!Aٮb:>t7^ǃ%f3Ի\ӽn32[ߧ߿
RI&)$5n?=Gfj;6Fλ7A1ٝLfԲ#
>yM51,fOm4<eIp^XD	J@v	j>ait7ҍlήOne^:th,*;7G؟A9_3s"M=騷yn#J 2R
QTC@j"~6}\-L@D$:SFɫLrlb3kH:Ӵ	sFn2L'I
z*؟(Up'xCa$Ś	ه}זطww!|'|si>׭˚(b	32h|K",rN32_$LU(?ߧ["ynd4 {7}BH񅞹NXKh*Bww46lzNߪ,9Z _*{:AOnq{[Ali7}(7A4]^ɹk{4+艗߷۾bp6Lwh>R,El1ȃ*}X{4UN`eNV7Jlbac'/uF|W_]
yFt+	~O_}b+qn"UH	oĢU!Оߤ~e.C#k2jMƴk*\BC}[2DQĎ-
v-'εE9wP^J6)V Fo>zݎbߜzp{)b-(αyf;wfЕ~X}sa/NSJ찑St!ѝhOMڤv6@
&tUG9jm֨}'|Ȅ 
ʝ57l^#l2~ڑsz-ꌳŨ14|uRG5Uo}VBQIHt-L1/K,l{lƎLXP=Y[xAmz	S~4K,S38/*!o8ɛMqf8zjg#aN}>2U5zN0$Q)B
Cu	Bi
ҌM 7fXXwXh>9^w{>c	QU5a62avn Bü'2=R X"%H!+zi䕵dK_bQ܋
^K$*m'<z.SGMss{D(ChDFZi4n3?`B(u%y!V9䤖CiƊpDP{?ʟJHuߚ+ꚯDa
ƌ׆6cgfȃAFg^"է<!dkUB͹/Dh\VN8׆ܓ|XܸRʢ@[R
xee_f]G!'~C,~|l?=^EW.:	~U]u'CA)+cmܝ3%_+
y$d
ϵZD6^\S9dGbN}nx--;J)ӚY6Qf\nd2J+s[-QMS+=8SB`gkI;U.e,b_?
Gcܠ*}gF`|r=H."+ H?壃)AޫaJÃoqE)c~AxT/ɣ|-'Qm{9-"jrBa=#n"*87zŬ_U,$QU_pI<7xMd}EE 8-TfЄ0ۥF|Vc.M!I%1@c:F6̉3HӴ|{T4\3h>l7d-R})W8xcP2c&mqhv|Os,~_t^mmoǘ=:x!jlRZ ἂu
oB\@ҷu
60?*#`ڮJ	"+J
1(9퇊^5(I.nOC.M.
ª{֨?L>~ǆ0
6d	I͚fISXRA?p  (UJ-wb!5AF.	Ivq1nwԭ]&W"&Ƽ1=&ܱi(!ߤJB^-#tq%6 av#[wo+#7k~s{D_F5AwS"Uz
6{$}iϻ{gw_$1u[Zdؤ(̸LFH^\fh5NܑYKCznH6NH{Ч¹zUj_[<"\o3Sag׭|(uݑTAָ~D{a;  Mi

~k[(nv#@	sW}kjP9b+zU:zeBB+,ɏaqS>YJ9U
-E	rN l!ǹuH6uP976=
z5碢%T2;iu8
`JTo^T#mA$$s2|,9֩37Swo}ng+,GTNnVpd)zV5^;
r
DǬ~.`D)*^w>nk_S#Rd_Ƚ`a/˛g[@*ԋrbbef
D DY-WB|s+[{Po~&^k Z+^]k#I-@8j,A U&l]ub[FayOm@c_߁f++FslF2k#;Ui'w;{Jr|ǩht8~(2҆UzNλ"ΧhsʫX%}m+JM
n?!`RLXij:p06?'ۻK2~ ]6Q)ŅXUerl˲9qvXEUivG:Vm!\3bD,-*%#gp-t\wh.stOs7MEoM\ˬxuTXSϐiVe/.JrϜ	qx،
)	ƤvzaPƱVD
1C۾G;]>!U+r}c[o-#M6]1L6vLiiLqn;gvȉ':_O
w
#(oՃ;_ws1P{_knųu^@l9k)L"HfrC46,bAU+)6jpIigߐgGRG@Jp٨lLjw;Ah)F!毯6_+5`}̢@m
q-DØXzhX=wU
jox~#(96B~^_RFٮ[pW~+sk~]NR@TIivlpxޖ/ ZhFvaHH6b
rlxKE)~*%dceP:j= 	ebh9946ԽCbi^o?xƏ~UwXa%;,pdhH(>.AOP;}Ӛ.4$U9*VV(
jփ)C |BD6Q*3`"Sqi,rBKa~8OZަ<i4gIcЍ4 ?tNS$H<Čq!Xy9͗n)ȆZgTu;"AgJynV~:qAz,U
J;ER%2Z?vցcaK\*Uպ_VkXIU٪Ad/	狠5a2ٞѝ;縧+jĕ90'BOhl~FŬ:?}I497szyg<~xßge w8>i{z jć|eiW1"&QSUOcFITn9}T~(/@B,SBXW)jY2޽WƢ
~p3kV@ݰ/!{_6E~|9'Y^mUB1GrnZ^CeX]q9km@,Y0h a ;9+:*8,i֤Y@k_~qG9FȾP.$c{z*J2=w*Hv`?fYu]
09Gtϐ(IzkLu ?ҘGKv:ίJgn
=yimژB-5_}s8X`Oj75QHd+*Qn
bծEn\VirD*"'UYe";&Z' CrÙ:=Mzo4Gڠ۽즥4^NwYT=Ti1`ZVUzrĔ@"g]#S0#OH3$cn4ܬPDP;z:_$bٌXօ٣ヘdٕSC ?,k1VٔAfF+gX#7Q5"z
La=2n3')Y1,a߽e[E,-$eto:^^a+zEx1¶hu1+`1]eZypK9, TrQ6vFg%ɧr*(p^9B"Fn廌>WmBbE9a˅6FL+*RS9#P!@(C?6c';SM$9S[/[[Xwg|/oʒ]T"|;Dn3ј][:vmlbB#y^N<=d3J(>#~\+c)LÓzs;]."Hkkڞ%沖<9^V>?׫a 	_Udt?gKWk	WVW4#ZTL8)`js >/Lsq(9DZqtVRM#w2Y_<Gˁbbți﵈:`JU#zC;dFX_ :,neޢrH|C'}T$S#4''1"7T-_FOlAT &qp%''mI1u>FgN)@!h(bb "l2-fpo֝EM~'۴;3CiU|Ī	2ۏ#B8'kEI,@bvĖYXP e[ͪMA7u{;{Z|o2XKm	&J#aL9s%p/PcEh`hp&I_i!/?K{J<2ΧGv
^gtcVwP$E"_ԗ@jr~ѕ-ymيe`  r4ܻB'(խ,ee~J2?Xpn!l._r(Ӹ?}R
Bk1s=r]?2B1k-e-ahzA_&L	0"P
2*=%	v`"xm!$TRR:̭V) y63zAvSĘ`U>w-.=d?4y!\#;*P';;m¿m5=i)*NL"%SǺ֝B<S`s$rtpD {!1'&2j:u5ϵ?'?:'2a$f	V;Vޣk=	.Fe*٭a3ӏG>b WZ%5و#Aϊ?q@u=|j% 
r0߫3qt:N,^)F)=/t)eZ&%\@QKV'B맳d\|#ђ}
 -jV5eou^83LUIl${;gk:V	z<~zNCRD##aŞ@_٥䊳Z/eR6(Yo'*#VRAԧb!Ȳ@AЋ-%^/FKc~Nl eij%+
ĺ:q	_۔Wc!3Y^qCD$exǊ'1|=!Qg5 6CT7򳋃Npk
 /2/?[q7eFݸ,Or !ރxUJݺ0r&;jv֦,v*`9sUƭV6Ai)sd>R@[#c>-b "zvn5V44#55Τ'})lgnY1xhS,0-/j-FE'B:la\,!Q2In"qgr'xzkrif%[?j}G-WDQiU$"DMܴhT-δv02h`YUJW-TE$㣬U1񂈻oK;-(f>0jp4NiS󾦺"st۰(頽¸LmEA,D:O?XR*]A?#:<}=:W]B*򣣺C=Euov넅$/\L6*g0UEebA5Ek0S-[2}:[Rh1H(VhĵQʹi%5R_	BeݬE^ʟF kWB$g,Vrï]~,f-wNτc?-˦La㭻ucfFnݪ/(+e}_9Sp`%Չ$i7Ϗ0$Ԗa5HzM`嵖YEʀXeT_CEu W7خ$.Ѱ8gYv֋(B/!CAXӔ~@!d>u-s8"EX!?+|؂]pB"($1>OPY4KO5uWq@Fs7V70ӳI ygӛ15J3Z,g\*b;_JyS`za/9y!JEV
]A(L/+<̽<.^>%#riܓU+8+: g*!U
8gF	<;${fJ\y9<K@K7Yq\"%I##lUgÞbuBMw&9KA3eDPܷJd5gWAFR++SjZvM>tрvOwSv""k:mmmmy_IGIR:miPk-DcD%O˻AUE5.-6a!dn˒BToD2-* p/C9l+S}Kنזлs3i,Ev|=Xlm!͖ԺXvEȅ˟p/*#[KlsK/TYsQc68)Zoc'!6K{ZVp+<.KѨ~.>YեGNM%Y_*p<Ы̗>}2, x!C0`b#aWMOCJI54b~8޸EŔ~w-εQVOGTp$`_ֶB\Xɬo>{ݝz~xlf(\٪)h.6? ndOz3is"+n!>DSEVU{'N>o:_HcW"	zyD?˖9?t:a<5X޹)I#@9I	|9{5P<	w >
vt-:{U	4f!3&KxJ
Z%}jr+Э=`%rE,b;2VP_VPUeB.H!QŌ:ٯ$ .pKQ-gne<x: wH,LrJq	׉\ji{h`EٸbDN38zs[uq 1 T ɔLh
j/JqӣqGXv)QQ$E~yv{I%gē+(uH5zǹ<5z_GU2,fXl6*e2HnRC#7c@,uzayYe1CU޿Q>֡:٠ݛI3ȣ*ly>Z
Wof)Eo'f|Z9u,d>ѵ^M>8Yu{UNqOqiy[X:W+.^VAV$K#!b@f5֜.io~g7z'6|cB%mS+dwƆ^j1^s#dtXUp#S\S;'[DQLg`Rr7m0HJ'J_	|YKNpB%,bYuK{qjy{]	G+9'lFBt7hI|HnWmY!o7]J<:Ԯ^mjJY3߽KʅmbwGOњLy07y[w{ACdY>Myx\2u'6X^\;+1Mǔw\SjSH Hns%ȴnZi'?n_!Q*9>*DD<۫lwpӼCfMr@~St1j&O0ٔja
>=aע
lLN9w#ǟN.xV-.vZ59@><JN<عW:Lud5p,#q9j1Qm3͹b"l=zG.ԁ7c\6NGKEDbɄmJ'-I--%&[wFQ4).tG}9.G$޲R/R若Hܧ2/`q["`,H7	pmȽ#y-*8%oa* x/p.Af~
jZwoڻgKEePt ސ1hhe >7Gja04̪2rzN7&0_R4>y@[Z cXbX动Oy!pR.#/ѕJRN?9H;?]Lv [nŮ[n{6i|Ύs[[I/
`U+sO.w[[)ylwkwRtw	y!ܟ{3qlmԭI6U2IaFfPmӞ`J<0dzrHȎsh$+3JS/k*Q/c	X􊶃A\:y2-|Lr?t=EP
v(r (fv\eZps"NM*;Hot'3Bf`(fiBZBq{yf
~(S8OKjH 5焄Ho"BPgqQE}\!lB˙MdoqZ½fʣtd#-ZG)砰1ȕYp>djq>G
"v1RxKOՍz>hJw?XwUȔ& __WDio>d΍\*ZEĞ	]|dt@DУ:Y8"^Y¥FzTݒ3G>\tm"ۣlFtXNwT+%{7K6J<b֤mko(/>5x7C>E$o?V.E?=XemH#9d99uC^XL|c6"O#XHqCV<ݫ:
+1|7(}i(`K(4v5nBaA|+'{˘%~#/sw敐8ƛi(N#2
P C~ޮ*1X8^Wn4κ_ommRg`I"W5ǞH &G}?	a|/M|i^}#֏F Qx3lcL*GTƅ\HgH2-Kd=K!R6)| 8Gvs_r*MBe
f:s0 ?eOmkr,GZ9']^rGx<Rƀ#2i<f],"ijjol&rX<Ni5F8<	)(ٽZ(vvwwںQ澕O|12H
Q;CKl(ÛjQGT[HK.S᫕Q5C+'ū4Ct3i?@9_0	[9,ʟ>$
LM8&	e/$WXV i,%?'@@
a	=gqqEs0%SV:;adus]%+ֺײ=춷?]8s'EQ
⑧.ct8'Jc=i+|Nғ];xԽ17O/>xg8vO.%k1]{t_0fj:ZvH`%Ǝ@	PoO>#\tM3yMIi޿.-p	'wN*Udn\^n\b6.WoئCP--G<O|p(Ƅ->c(Za&S,v3*C8&΀/״5|!=+*Ez1̷䟧ccZ
9b*G9^9)=u>+Ӻ~;hFݏ&t~ߞԜXB?d^>m"A,G%?B(OP?\hBφ$鴼)$WEz]_
;Y`$++1}U2&0]YU{ݳкYS|[E;al%CDʹ$m~+AHgë5%
DKh@-
c5MF_ɥ.c-+Y5ZSd!H""$d/Sga×wd
AF/,~B&w^Yw|zE氼	YӜ=	&M
8-kCf^k@R3fŌ	E)ϵy)9@(\%ci0<
-kYR3	$0mH9Ը_	5*4"ni#ܽFfrl@KI{fa(V
RߤqmՕ6ԅSM<,<yȻ"l!B5
NU{z	Dօ*Ald9xE{ ]݃8dcOc"ʗ,\-xx`;Z&WhQPCMlTE>OO|gyR0LkWn`_Q{GlcF"&;P[Mi'^UYne)_Zc}(MF1ӽ\2P]Hbl=%(3JuZ/fq"2D8ȌJ9VڞubuJV
`msG]	Q|X]t2{ȧZ1/+<2	fzT-q)ũlNzd]v.ʸ:[{Qg'9|$TYJӫye3oMD 	%;b@/̀w$|	|v'%
=p,1WODJV[@or>lcrb_k	+
%k1H F2vDn(kbkvdIA7Nء@	尶~c0<#>^}BM7_9n2K^m-}nMibW:RaiXL:2h%ͫ- hv|lE wVWn榔U:l ˃|w6ܾlt߬+l٠UQtX -="Qd)b-	>/CUf:) :8M]h:*|tŦN*źQ#H	tXF	)O]GyEh$q>\e	ͅ39zr}iѸL nZxQ׃k*Ao>] O]oV9kAdW~K󾬼B/Yke*a3ln&	JAK8$6[m
ku/Ǖ	+TF'3m/x"MM e0Ȯ!^8f-O|rBbZ9zXEe1PCo<ł!m1H;Hk6mO' !;r[ZCg(b}Lz<.fTx*?ͯCeyY0 wiomI%Y@Q4hq)䂌iS	${^4qљuU+鏀굑j0}?F=6	3WD3]yY~&c(RYP8DL0
Hh =b9WnFKōQ^S >pk9jJ*=̓,U3J./^$:WЋ-j6/͡5rU	MDajj"(kޏeI?;=dIGOl$rFiS­X]vnA1ExMP[	T m쫛$ܡQE0ǄmD$QET`ޠS_;( CDKɈ3*FG;x:tPRxY3?r,[4W*M>Wv㻊ɆyiV]y-t^4
(<;B[۝5><M!`kRb"M3n|Z
esdDz@ HMH7sgm_XJi!#\
;<;d2AElÂlSV+\t-<N课3IDJح*Ef#~6	|!  *B!P&so^׬L{pёK*VD\iy0Wͳ)bbN9cY%oxrу-04}?}ˍ+_ Q(uW}O|9.~`E->=
P֪>QWhJ5+TpUBa6=,Qb~;m.ճqr&Xu~hzb,XمjaoșNNfqت }<s/JTXE8*֭lv{dot5`%&qWZ0B:|&t\E=wBz2wC_zЧwc%&#'mأ@n~F|ycYr	!ʘ٨>=)GnȋqJ{шn{!W3XکC)ij9XSOМD/0%^V[@.e+=M]kj4RWn^$1B%e*S'lsCsXfc/TP?SYpnUl/pǓźE'rIǻ}k.0lv?)1&_]}&QfzL9|Rc)0rfqmBij#|V= v
7~`}V <GV\|>Ixa jÿ hYن|!VU6;
A%tS>箋ޘP/!D,%#JrAfk-={*&G=_:yĀR;=g\X2k{!¥Q$rtJMwjyhMQƼu2wx]\';Lk0Uwܘfv=S h@mw"B\l
R*&I
W|ֳD '[/
ZW	[oVDϜ[5bXo;m(!%BMM19⪩?M)ΏtP	Gx[Ư1)/zh0c%b%:g.}C<Rv~Z -c>2s?g4P=;MxJꉤu)~Xl^%V\Lt\ZL`z5B=Rd7B>unxyd$@1ēt2_Vؾu&QJ?(*44^("b{xŞ{xcLa֓%ooPLd5f"ep%sBXrLnˡ{{s%8'σ\]KgҒՏh^MK7bEJde8/Eϩ~74JU$mŊf6ʇ	BY쮋tPwZ jqY
b㡈G7ug^>ӢT{rш-[[;k݃@P?ʠp`]ɍ!?RjnA
0-$k8Y6~";ecp~*>VA#֒VߺXc	lQrX|&Sub$>
5+Eǀ0>XAы%/Hzb*TxjNUt;?1yˀ	UO/b01'nq޹v@XQ>
VXGSv`Ȟޟ&"FM_, b y"LˎHR\UJf1SgZ'6Q%3!DH8]dw`9SaV17~{>*k_wKX
#
[r!҄!_]]y7fbA`Yh?fW>9ja}IM	bov'(b:V/fZ+WWq쫍p
`.nmgGB#h41;wr=I>9S5tܗOX1d~]wj#
ilھ%Lu
#<uj9*w_+efrXrM
NQٽp|nmJ`?O.,R*2Z;úu`˲R{AE]
	(@E%MVB~ wj2P@C0?jIK3LNbk)뭱姐GG>{C<ú].ö@Yg?ٯzx"-(

a	v:;|.gKG),ea1bVqm63+=AlH\؄4DaR|MF7p5$@1P.8cDT3^zIm8käqw\tb«["q3?
6^ȝ3ՏZ95@ห-nLqڙ7L:+
y˾$4Rtͤv43G۝#kZԉ'+,E.O6OJסqG^m4κr~n]LhU]t<!G AsxDlkI>6lg'cjM@d{rnh!#۔MzżQM߱̉vPmg<TnXT+}nجȺnDbJ;66g2kH[C7SkRpWa5^pPsy%!v]Eàɤm]-R
,er't _XSAËH_;ՠ/)_+hhG,4vq)&;6ooֆC@"4x^$\YOƥPUTce]QQߜWT$j:ԥ=
^Qݐď"9?Is<Odw$;	1}=a+} }B:y֝mdp_ě#ܬ!+U/O-Ħ_*}@}6bg
>r$̙^t^ՠ/˰$9-v6돮,*F8zTVRHBtOyc!Z-LtfV4ngo-"&s#.Ά
 bCY}CCDYEW:I2J}C>rP k]،(_^Ç{I*>nҚڅN5?d44`hM~!]Yү),S^̚c7@к6
nz&@0NMrZܔìG鞱2)#Rc?x3Ua|1E
"r[|5{< B`k	7(EKkx)
чR2zKˡJE7Bz08	frr6ꑪ s	`,)?l1$B/%:jU[o/r8l[5B%"s{V&pZʣ,=!iu|V$,b~3V^1`Ezr=`\UeOˏPJ</]{9_Dϳ$9norV
KgQ"lhlz%>ZRKhy2RԝNT1y6Lɡ
B
őgT8_5hA?&Kv_!H;J<7HBP
w_v6_kPӆ.U,@)lǌq1\73ȿdBsDYe/]
!x1n{QhmUq˫
q"Zj87}%KV|T3l}bx+
1a3P 'hqd4f4%)SWg)!U"&תi!H|!cW<]S5u0^GxE5#u{*4 R5vb^b͘
%R3vVPܱTM+0kv@7*O^)8S#nBuj K)YKl9 Dp%COj/pƽS
dH{7rMoxe3i0^&%a(~e6rAܢrpݧ8*Rw5J#|ƌ +`Kd 66-9
\wѾÎzLp}gvwXoY`Mp,&QJ]?	E{vV		rf5CI۔{녜8}g9ޙKu%I!I׺ _q·p	eDh
-k
߮3=2o(MUx5
:vSg>=
9ߺi*K*޺
CdGsb>)Zh]| S면u$VgZ?Ca%Te(=$75
wQ*qsײ5&@,ٟP1ߜ8#)ˇ,P~w)
OG)Z942*߹)f]^Vx}:*oSx$ mț·VZ.N	WJe^v^r}A2GHdN@ H*paG>H$.ш!ܿhR`2A$px>QѠfB䙀Dnc{*|\"ZloN2t~X	$0H<-Λ,m{YNre$}n#j0Ve$/_mԢ3nOOG"/A_ض1 l(ʌ*$2D"BHtUEFVBzY(V3V./XMV׍5+3!H #죮`Ȣ:
/._?ƨka(F*УfëoD_J%*u35"iߜY7&ϲ!g5[זL/sS#X6MI"r2<jS7r})}x /ӭidW9L2acDh/1-%ԉUI,|{d<#kyMOZ>ʒ hERw3R#/'j"\
?ݽ"lJXPs&8YCĂH|}8^
:
"˃gBQc
V1P5hn}AlĚs!j"H<NlBo_݅7ntgܝ7.8n0AjRR)Yƣ߈D=P'UvjɈ5
/rY,TWTb/
 R y}#pV,)/;ޮd?l?Bu!%Jr<? Ѵ
@7dˇJzKyqi^`g{E 'mVi,)̴Mc1WJͮ=k"<60ٱ_y D8Sm1 5;AiqnnFrݓ$:{*MVRI&w6JU0	^Ug7wi	4RxKUAq!H 9SUSQl%VATdyz]5^
RDX8zt`V
E_;l;ƏzjsspS9GHxDnXVY3Nj"g;9MX& Cr.H& sTpFbI5\f )Iz~-kyǌ̉Y v~ג8
r=f4rs(4,vf`7ngjz{{pJ$$0gGI~8WoqD="r}Y_O,
L[W-awkX.  $Q'I+t
*ݠs^؃5Y<EZ#z=QqWv
icab/}8m'?ռZrJi\oo貥QJ5N4z&&<Rɦ
]k۩jTfgN'o5/@BF0f"qSYv/ZɮYCNa(kܔ[p&NKzx"r߹ŐV0%T"98Cڴ	DI.s#*#py
gdlY	n?cd>ٜWMg
nNatOIDOtQ7D5sQwG1rZ'
#m@4E(2	W@yޡLIWI>Ա ?DGUZYDsrlނaH˱@W3HU Te:p壼e5߁ab@zhOH:7TXRƯFmeͿ_y_7E.p"9r7̜)9հW2|yI(/
Gc7fHqGf)N$9\skZh'q9ϊiѬt(ˏY8nBL\tN?F!+un׽.>eOgf۬6AT)#Y}RTOųv0;<&$b:~JV_f.իϚ
{7ko Q
bDWb	4Cyҡ][5bSD{8Z۪$arj<+L\ɧɫyz:K$Jp!XU9Adի9}|*f;wZĦ$C("{!XT9]
MgL{ͳܙj[9+$#P*0nU$wQ}1dI9ǑCW(s"AI:rhz^j_sAJI;_w:!3:x$תJJ5LGܶ_21&]ja9`B
Wt!h[r"-g:-e`qk˭\b
DpMo\Px\`R͆@9ǔ(1n>&AE_OTPŝWKӝWWo\#ؔvW*;
'9xbX=pscWޫ)݃ŷӽ#BߺCJL8miAηoUYT憊ÅX_%h52|4pjLnS]& [1~	*Zݭ*~{mӫ3Ipی^ޣO^U>	?ϭ;q1#3ƫf0u-Y09ck/i
c9gC{h6A&n 5F4Q>8Ty*S{эƕr1La7ݕH>ְ>xOWr֑2qԢEA/%`j,8m*lJ8D`\RRIYA< G:WtZ=W[޹ǵJW&~/I} Eju@s.R(|?P~gp<vŜǪpLzK6M.l2Ize9CJ8cwέPYj<m	?M=?j[1커ڃU]؃w 9h'gH:j
-ye:		YH@vVJ9]
wlgޖyλuܱ$߸"8b̹.*9AP8iLDdb!2vd^aSJLs_w54B鼗dS{Vc`᜹ůsqd-@ 4wxZ`
mC؆,]bzJ.2+]U"B8=`*p?co[DQI{)&!y@*8a,#%ny#a|DGR>oH+lc_
"vI-p{Ne<ʍ/@)_soes<Ŗw:sTbcsGS1zF+pL=_
Mu%ݕiPjuI*yFOIO1=l8`j҈Z|4i$4L6-V)B1bԫ4]X<TPDI.V[n=wwE%Cq\P7AΦVr>wE҇b8ΗFO~:\E04XvTIoJ9ACezfvKg	t|hqӌK_L@巔i^{y16H('X;}-*!s$g+J?reXZY.e8`
7HH!lf}6E򪽕h0zp(-IE9z4/-z5X}
rC5_>'CDR^!'OWq*G/GnO;->pWQbOޱ?w}7.:
EiqT4"*'m>N;-sBp9'_gjX/8>Ԃރۊr7w9%"V&EC$&+٘..>BtUW*XEv=ɫY89islwۻR:mp?uCov5tQx#LW`  AןSgYp8eE=r3mJo/L|ǢHюѤl׼:+#wvLM69^r0F"H0nxݢ
;YhA{"̧y3vDAo&s38LY5;B-Ļx:θsܒ&괍`]޴7GO	'h{ť8PM^0|:zf\jΧR'0q&}9@(Gw%CƩJX+%_Ceu(pX!iG"KpK3P*4Tn)D#D`0?ZU"{Ȕr++*KB$'߱6
I[.=oOHbw=un&jr6P2T֏Ha9	w'FWE{,.q<jQ>c|FG	UI`p/9/jB1sی&317`Y82`e6K't"E#>/S3<&a{RI%!L*R`}KC?1eKac%s NH+6riWhU}d>?[ jsDV)|w&D˸*;̀t+ro*Bǫ2-af[s#&0督gAMc-+]E#.cWhW_YiUVe\*|mJN	rLvm _
@Kh6:.kgZ9O@m f!] fh* !yr%hFPBKym
_wYdH
iT2~O4p&) cNFXI
d&;	hP((F뽱Gzǥe˛Pq`֗>,jv~rl%^pDцd.y$C,TxqrX/ٜFE2_tUYyO	@!eT	P$}#vA2RzWòG+=vK{lo-0Qll{L>m|jiƏ@1b?F鈨|ܵ3VَԞ\ZXUIP+9t}ui-X|^LKI6#y~*Q!Nxͭ(<Ll/j6wG\*'W v5`Uh/)Xߕk@88Ĩnh;aP3O#Fޓ{:]bh J%qɍ+\ھF0h49qӃgâ	ZXF,%kSt
e&.SؔlKx[rZ40'&#n?R[!Vz%u ROIo!
T䣴B\թ\n?ɔaik~جu-6a޿\us~Y~K%DFU[N[ڙ}K8,uB~HuNx|k9dpٸ&r]ҝd.m]@u?Gh<ZJ5ȅ_5¥:V1%#DS}yǗ-1ZP"AF}HɺG7 s]TXìeQS<by${pc9b
-KdPX˔3@k[,A+A@3wRáU0Bwƴ{J'VJ&D|p9;<gQ(+g&+aZTIk6(0.(ӹ5{5Ejo?Q'2&XCx>+m|*IVygNM{mtKjﴷ[mpWj_|<|ٸ Ϯ+a$uN@UmDdϞQ77z~|vo8h 1޶v_b_dZӸL 8-fS1o_*80yՖfz}7l8Z*~W:ˀ;ZRR
NE<߾^Y.B ~]FPzp"=fJWWتJJۣa+Z,[SC#뼨z(BPuɯ%v˒|k<n)m1eZݕ(Gj"UugqC$F
LY!SFKV$
6KVqfPRst&qd4*ap_@9oX,ĽdLrpxTY5|x0sWZ]Y8dBiMmKa
ڤ]"'neӺ++7eBBKħ`;\r22n"}4+!⽍!%w<G(3R+BrB\p6
>}&ֹ4-p~&gpb>
Fު@8tE\߄GZj?Ię)LP&/n~a*}Iޯz#B@%:jV&/}5L^r@rgŊk9fͼknt3V>e²V31i.E(tﲱLr$̌o'0c_d?Vad]`[R8:TC,خ_KPlHwe$vpbӴ͗_w#	`}-c|x@LjvMVzҙB$>	x|Jq{\^]Q[#ɦ ǵHFu3?xw0'3m,&[GiƒĊ5r_.D4WW6d-bA
u]F٢qvN H>u^L/yE9v@)Ᏺ E҃Jp.
 x7K57!YɺH&=r#H<M$Sz+J@nx2̖nBtt\7|B=Ws=܆T:>fw7T`7tOo_5%irR7zAoѾPXa}Q^>i9јXn~ƆwwN#qS$fr{<#OnRѴIC\V#)TpT 1}F+ٰ/6`oaq_Gux9WoQ9|*o>ݐuLQ^7ĽHK3`g2mD7JҩfJc!Hom@@Ϩ|\h"JG6ٙYa,JP 5-ϊRRϳO3|-9
7fDpy)ptV:JtoFx[{gqs
!NLi'f$̠9/!|OwDgͧWzHn[rIr-0Մkl,@[mVY?|Py6"=S|LpFR 	7;֙]Rj`,(^9hP3{2i
(.;5fs33deot`ϙ3b烳ޅ-`aPӥ)0w;sb:5( *\
@]]00N?441;괍]×	I2|֑55<4-u=Y&k8/v7wkrLȝZQ8 (1G 
,h!EqF*iy#2GX뻵}*VL|i5Ϗn}
n֮f~~[wBLVy tVJ6nA3d_SF<r5%)D-Z=TJfSxMrK{aγ0s/O mE=I	|sLsV?j;(#5yOm0|xgg,3K[

|6[yrc|Q%7T_UCs;{seOgRh?3y!1>|p?]!lGEZw1x`9*KT#)"h+X-tZҷz|Qጴ0 ð'J}L+OlQ}?t=+:d!PPCu2
w8rG8_r,+1_|SjO`dqרAH(M8{TeBl(r+	gR:,Bj
jAW<s(8ǛM5p)b{zVc"Vij*w^h]+Y AoN|Oy܇j⓶͑P2-}L?t;njwڞϒKvJ  gG ZvM-KFY~sA4n7
Ju
3}3F!k:'fyj<Tm/)b&9Q8sQbS"$Rq &
3>ݫdxqɠ _?gniJ.	M	TAJ̳ňÑFDd>bm'@Wŀ}LK e8Uz	a::|v&8y榋tmTRh(!ZV;7^B7W<F3Þw@GSngaQ颻XU_;4L4NFeٵ&G<k9o_rwT*"_Nѐ*[!)_İ,K?%!8_o%
YQnJz}1ok,?DaN{h y[cV8?n	ϸ?=RNT4
	2·Mo$-!:w$oj>F\o8f	uoi#(K e!^+Tl;zGLӼUɾj>R%&@%|eD1ǏNULPMfH4$Jh!yp&(;ükT!Efki)2@ҀqLjvӆ笘${cfoJFlM]-U{, <Xj%HW/ތ:k>/%#ׄ@A2<~\br@^}CqSҶy_X牦JLQ5<G(鴓ÒT{zK-е<bpPugryzY'm-"rs3)cDcزبʈ}ך]
ۑno88MZ#V
d@Ӑ*O6t\Dw/[sS􎔔WwRhk)k6t'أ(>D_ q}0Ho԰ڲW
_LÚOF*zgD Q|B?(_T}O4YPpD$J:c'Nc}wJ F(K[D`-پ6z ޠ8[Pf_)|W >ǠY`TTM}57J?vv3L> /=ԕF/V˘36{y.XP˄RQ|
ӮaWN
<FҰڌѳYM6GtFnWhIjqp&Y[2WʳCjjOPbf3ㅑޙ"Os/5A:FJE8
B DY/)q[<2d	,Fyhv{iNrYAIcJhʥP;L!lRl
@~PyH0 ZB%!"EB
XP,~gS$ɷ||s=-Y07@)㙅C`"M־n[T
ݻJb(E0݃N{&x)2]yCRrZ?-[5S\y!Jjȋ3rHk |ۧѐ2z},'tjb)/g#Oe
C%fy!ڙ~crlVRZճDvKIе#-ެHk;fDͣ[9eFa__[c[/?$^nM;]c%z奀12T6DRB!VvVZ 8DJyWu%h|JfuXQOv42(_~.m#gVR+GB&D
{U;UXx¯Jn-7RyZ(OKQvsHu3pF;·Fy|sBcgQ]	(G~uwn@D◬/!7	?wcvvıuW
tsZL6~E3@֛B6 l$xg
1G@>;a	
u
|4(ƳY`/Q/L&Sd^LMÝZɎ})E3AKfX|8U{z4-ԫ"oWJ;"FhT]o5 YR+u&	7qI.UŌ74y&iRXMBos0ZR{E89$wLJҁ85i]['HZN7/j	y6)~E3!wqu:?e7JPdDVFg17& Hi,(^Y:_[ϾX2_Lao{aQ/$C]a!b&eLꈮ`Š~O	4Ud
~Cz4cpg76Ŷi8rk*)q%F[T|m'OՖ/;'{FvRI/ń89A_ɣ7p#$T0Zj996uhr]G6ԓ
l	ޛyRfpΔ XId^Lo/T$m"6[ REհ)eJ4ZI1_K
R=ї"k/pDV5օ$s$^uDۙj9FAuE"&H/0-w93LDO-
;ETʈKG!/u{ivCBEh5rxJi0h93e574^[kU*C'_F4tpi0\-6M-ΆnibcFUØ䃹 g^*oU
=
R%i GABrJ98=`]/#{|-q*ooNY`Jn#gRg?hclAG=ת5鰢06DL`l}vÈ[ѽظM*$%+<qk&rPKS惥2&?*ỷKLuL1P擜`kdL:BE{IYhD`&gzEBcXM&W%0O}"B/eQn/Ǝ=[jgJH+fY[H0lSˏZsAz9WJ}!ótSȋƅ6qbYfdAɬx1nТ]ex,ވ'6`
)fs݉XԦ9+$z Y׭ӌG&k5sbZ^{ MNRk
;VJDH|j-+->vpvz/>_Io[γ%p\>Tf56Ug{&
^w;d/5 󠜣_N>TVCmQi݈%jJ0Ϋ+GjEhȖvC{swu[6\S,FpE|}<	EZ!rZZ$,h=Х:YM`l]_LuhTQi+znYzw<+F~dଲ=4	{i糏Rd =[2)*LxIgZr"\ 蕓²zzbOc7PC2Ȱ!$Ӄg wГ(]tj_O p*ұPZ7$c>W"APzzW'jiPPzΛ"K,ˋx^%LLZX,=VP1zdɐ\"V,'݁;IU|\3UN/Y	"Ռ_ycgs睔oW(*ߴ,߻)rE^،yo#	aR2I
R3BPM#{6Ir30p VjϐjY,DFۺq}=JRZ<Gsg~<؍`J=fW9RwHid߲(@pN?tA FJ`-FZ=?ŔIRP%g[dŴ.]CJźA8uջ2asIKntc)b|#w|384O)Cyn\yÐ@G+㈓`EϷ1D4n-Oq[r,y+Bk>KY+OWhmT*%3&Z|'@AC>n_:Yy5s=x1Mo<nL#H^PH5_]xx.c~$:%wܝfJfGs1tU>#SSӆ	<K-B*cU{ȧRܒw!eF	sf*
 qX
uiWou~8-Fwvu]zS'ݭ7#[􄉳j-,Q%G侩fJ\oܚNpqϷ=y촜H2.HR	;UvoD04Օ`.7Hd78;,fέϽ9$ԫ:H%^ ^RIH)l~@XyY5וk(E
څBLڌ*my̽.5f3Qp\12ʖS,R=Uy)I|0 B`k*Q] 4
%)u܉?oG]=b͠wH
n_{F|Ij+x9^-L}\?et?'x?ի';[T~1NxgۣpwT`wC7| V<TMR>}fmRNnvp51PYܑOIc맄xx6Cx^,>'VPÁgϭ!iCZ5͐l]&SX4;w|JLTQ	?98>Ilr?"ӱ*W@"DBx2GLsL;h#urK]X]0Y$
d}[a>t^=hw
4+?1*rQ%o0i`d?ӭH
͸sMWIvAq,:>%9̿qs?gRSwRh)Fm&ipYm!O!OA] B06:0$2Qq<XC>05(E{hs|YtOA֣N]@R~6c滝7Xr:f7e[/
y1.0Q,}zO˩D:)$53T:2Mɶjȧs]Gs-W\t<sdɳ{8?>bqAh$m2Z:ޯOjvgM8it<ք9eUCFwWø;fCk8J!UtG/z~)r.<+t;\&	F/|-wqgH~Da]3#]@RQp|Rh*
Y}	P`xt >\D)JnTp+iAҫB5-*Jn~pw^~uU}Z__de_Jݧõ	-
&TnKEORhHI۵wHKǹHo`;yzPKΡOݪZ9|:+ަngPTl5H3];:j+	W3	('Ex
zO28
BHPDW u
8XVzY8yS^٬&b=qT
YOL1^ƴaW4jVw	Uel6<-1\342_	^`idYZ:CAH<Divsuc0[˦(
L7rYF
!pjMpnPv9Cl*D 
ݖ{e:}X'PVIq,tF;K7$Mmqc4QӚZC&ekq%"*H~+6s	on[ƕUO}O~>8۹蠉*73!uIxD d{32O5CprTrORow+;a
{;vm?pRQjS.hNnLl 2@sհFq)~6

 % {j-ow#M:wU>-Xz9NWP\f\r-I?PM<mZ"[t biտ2

 ĸ$9}K6*=ؓ[a.ְ=;:L[Ire#n7YSu CHµsq|v7^뷂ԑ@8)9=HG Xe26+%#eڞP_;,2d
q8JO:φE+Q^w.v厞9phv׍f4*doٺP86H4JO%#
O@
zI*Kg`*TMR"Do!¢Y,a)Z\ *|tA8	*
O(бDE1\cxzµ^.0]HWg7I0^U3ƌ>ޛÜ:؃Oyu-]*7*#C%%kuAAjSŌdOӎnl-nia.NwU؂`1E2	$pceHdY+B}	&zeX-z"V.6}-_THPTs=q!^dh޸3Bu2N6ny]ED|e[/㱺{5䐩+5!iWF\ESOe<\16|.&
6$B~i.g.Ar-)Tut"_R"J8+p%r޳n'~>B|zΞ%oq"m[5m
5Ph.O@GtH2wŐ PL|׳*Tx] AGYc?H:RV"ׯLWk%88t~pᲬ`!J
q'sS)X6p	LudA9jwH.KЬ]Df we|t	b'" FZGF;+RxBQY1gdrfmjP^})EQpt̛H3Uf2(<(쳦Ȳvw.)n\=\"g-jenڐVgʾ@%!SuA%}5KJSq'z~Kr{}xnYyeOlXo8oj'f5Pn3TPQ_(Ĳ@""Nsaυ>P܅Fa_2#Hw]GI+U+_Ɍd (98ȒWlboP{r%:O18NPXZ*v*ʚ+wMtXʅ@@FR`4$xiȔ;ٕ[FP5QXJɾH/)<y[暊8]HתJ+RR@b(wޗNs%>
|ϓ'OP}=gSuoOvnE:=&Tc>fw _-=0Н67.蘩?maXei]3M	GZޝzד
xJog{{(H/qIDw06@ɭ0
%4u|pjL aN\Ip/ـ>K>8Q~PH`fy[dJj:{S(4dFㆊ-]|widE :7B@r{p1
0 Wj.>>2-HG{f<	|oT;br&J>o8Z-2f6StqhJTt\<5xG#U;g@'|aG.}v ?eE5![51 SIny󀈷;:p cc: ٘:iR7][
bn}$JO{tau.<Ւ3MpO4۞'R+zhSZ&S&iydS>5eڿ	7m~u_D#"/oG{$A'{NkkbIVE'@utXyLRD1z̼97Ry4>L7ȒV,Aa_X`H1z3Jڠ;uكTb5iEf3;̖h
;6 _`{5slďn]K -Z]Ȕ1z.@:
3 >Igi
2I1L,q`bD@4btz+QޖP|	>|!%ᤌtO98J&V<!VY	o
LCޖ!Q[TR^VCV4J.H*H DjeJc#Lwc@̮+Y
+zxAH_4{}Hү(7<r/
tT\՛$g}+ ch/Eft8uՊo=cq#SgVUⷿq6ZhPŚFj Y7z5
WY(@6gaq7{Izռb7n壼B>ht'4@^2эkAkdꪑ&|?A-|2ܺI?H)jV`(h6.UcSCEn
|5䌗5LT6<p$5?4X}?<}Αڙ]q\U/,L1&rmH_1)tkCte~
i~tooFB񋐺
"	Z~$(ߵw7UWunp=|ˉ)]k&XDUcAD!!<BEHJ7d@Я2N[S
O'nx1!aE\|ѾThPda➖'2qhj/\X-0]кB'(JS*}#,E$#c2r?fXdҁ;B{ωBkW*>p²p/DBq*		mW%e
+T:'lG3Ee2-Ts"yvno[[OhԨD0U7Gn	;BOz
-&ߊ)V,iZYD@gI	%jV1jݧspj>qq$ebfHc߄XNjG3D!NQp;u~<؝빦ΝE2$""WLh)LgĀ0MɬQ﹋^6q#_?o?̐u87r){X7U:kBKtؑVeny2P3h5}|5?"ut44QUhȴRvLfaJ]b`告Jd#71ȉ{usez5{Fڐ^~{{}\[Qt1/Q֒e\8V30;[r׉񏏎$Wq{
)kQG%G:(h8؀ ڡ~Ȋ;Qy	K\
&wXki+*[)oI}
}aG<9&Wd1d7F1(~h#V@Wòj,la{w7N%yAiin
#&1ə8&QG(mEyD#HQShdv}@>am * &$%yD3<0MpxDyl/(OA۫o߶ۿ7 z1teb=:ܰ4SyZU.@\uVmu+C`TeS8?X+g:Ts:HH4$ה-\1\hAЊ;/*%0"euι)$7XXDLbޛd?@'ڐz49|]V/xe 0hG *Ltxi
+W΂H>"u鍲CHdcꫲǮ1@^NMs&h$
DCVp=ќ'ET^aSFޒ7'Q*n4l'_`g%i?b`-zJf3mdOq?o+i# A[?nGP:b|JtӲ
;<ӝE@qu <P.<}^F;P2u\Ϗ%[n
l01gq";H\G0𧆣ۻb{\.L5Lo]
'J5vZ"%EJuƘfmNKLG׻ֆrI&>z"Z=$<am}|K.ף)E<PKNTeX828-8fr;=*}<Aw
N%bY\%Z.*d`uY3Yp_n`k \}c .+HJ̦8?ͥ½8#
Os'NkM MviQ"&)L`&瞒S$/IZzn6)w7}MGUήWRsݯUG:btxMb@"_cB5QG9
hc Mrccug "0-畱{>eLG&46
@PC[1P&Iy$6(|v:ajngZC.jd!^JP㸣KQY|z׈E}K"MSY5CSsUM>=OF9qBJr&LZ
zJS	Urb8DtJn(ci͆tY1BqCJ=^nڝ*+9,d1r5&ֽ`E[=D盤|Kg*fyYn! [-Ow.S{+li}xk$@V^ioDHuXزqQE.i0*ym0c, ,\aO$!ognVߡk:&랶},aXXiQ ˂󪁂`ZBf8c%&Bl9ڵ
ڗ%| ۍ̓EwJt	w7y;L^F쌿Rwo,!
jȴ%7Y{ |]0wvwͮfc|O5]POpHc
JXϲ|`k ˹vVre3J06<1=mf%b"Q(XCR'<(=^6ΫE.'yn}usiugm</$?u*|ek!^WqMpx:CIJ< J{h<sjvz[k0S]!!V2БsDX\R#dN^X|n 2ĬTPK1j)5{+N\Rk~??cc1hC:/qw-uo/faٗ7l\\-&ͱE;I9x3,^e.}LRg޷-㘞Fjݣ)!K7E':J@3C3L>Mu{_Y!jV@> F??;k0W^ommR(R	k
`|ȇF3Z	d4ߦq
VIM59̊Gn&X1c^6~BxH rGUSu΀%%iwgmnjmovwwQevWv[[[vggtW׭-KIW&Lr<_pSL5@HNASWu SIҪiT9OJC6HN6f|d7P
>\K=a$STu9"ran7U^Ùgg&MiiiOK FE\+ٸ&b3Rr5Ձ2!h,QEICD:|6E
̀(TyЪλ[(ʈ@hf:ǝ0"q⢔Vӗ  dT|ǈhb3BQbfVapd)VVR6(DoXݴ	WV<ҡ/:Gt8NYț9ɜRzn@b^F pyRӈv?o^|fg7}Z@J"m zN]c{m]KxΑ^L͉(6I2;[rONRt͸>Veݻs+Z5ˑgurI/%uD4:wې+V*dcLa@bQ YU˝HsgRϹO&Uf#0ss&,$cy+dAR>Z$@S3gz':Y^-)W9S&{򆭖,eZRko^-A7ɼ*QoH3=ň	dnԛuOa	ڎi[6rb(\¹jufdZ1k!/GqІJ'<^
X33+0M^(%y#^5u:Az9uIfaF.J5ej(rt.Z)^,@59a#/6mշJ XiB2c(/Gꍢ䈷k1cHz8XSWbೠC.	y6.#?0k	e|
@Czo/JڤdK+"ďʧׂp̄ހ*a~d/]a/B< WTfP
|xBQ+*Gj{atQ_h8Zzk&#&@"^ 麉xϥt;5d+Gv0kE:kr&0,`tMb
KnY~d,BLD#|QR~#S7([ڵh6ފ+6P	RtӘ6"ܪ Jo^UHPv@܅z+evF]s~JRg֎	0x,1䄭3uZu4+e$U6M<
;b(aTF[0$Ĝ)87ӰD\{(fKڨLfV?E-Έ!|
2of2{XihW3|&n־Dݰ	d5zѩ /)+1RGmM{/1Rnt3!=Æ HbB(W%8A78f';,ƆO9@́ḫͫZ6D/Sa+UaT (VIC1"r7MֈꭗBC?bzh5.k@#Gǌ  ֖G؍uROt1pEd$B!?36j߷\Z7
G8`Qʛ+Ӑ(`ָMQQHOf{?Rc_-SDy\k*}Tco:}bO=1lu6qq>* 𥻏5hوTK ·f1~v3h&*qΧd v_%My@p5C2euT%:C9hex([a=_!LbHϿ%%:n2mXO{rd!{v^O(&N[~j08qkx]i&ad#S\/$ɷlC)"Sւۗ;_kmX#BfC?TcD1u9Fjh{^	XqX	SHsf/4SŒӚs|zqta਻[_OgMluL/pV6dwΫ$o>I/o??.[qG.~ݑ.϶~_۸tQY S;:EɌ%-RvqW(({?~bgcY (˽9c@]e\ˆ^}!W(ƻD tfw9S^[ugn3kO%gTfC"2Ѯ!On1܆[Rm;6aEN90|ؔ/45'ۨ0}O
IF95=CH1qmoy'Tp!'4G˓2e`*|L] == $s|6[0,KEAe{"yV(-Tv5LA.~X+S-aAm(6f/vWS~S?ᐧ~Q1cj^y*8Y\V\4ٛN_!_Ba5U+́"FOOONdƍslɡ]VpNE̻S8Q03'ʚ5D6lSE⑺m?Xnfz,b@ZdX๯-@C|T}L{B^78j+ɺ1G$WAi	uD-!>EkBH
<؃ۻa;v@h]aw18B>\!e(<V$wkt̂2e&M2u^<A-V)ظʕ*P8	F.2N.ENq
$v+ls1_<>x*koBaUv |1L-r6=IGj<2aw^+
1 ZW))'e2i1ܫTYQ67!ݵ})Q`p/QU_q9,١jV76pAզ"|KJ<|ˤ.

\* 6c"9/1|ٌE2
e+`GV3*o¥]{ťpzyūǔ6m2] 
v#D*<TV1F$k</cщ+8Elzgغ[Mm:k"ky:B^8jE"<iN0'[. 
XBSt]!b"esQ\)#յZ/վUUy9}|ḨBvG`싺J$?hMa9N/B8SغWFp W9Z4ݶv_ʜؐ'z 3}VQ/jCxް_φ2yR
	!8l5* {}kãⰐk˒&m=}
EQpn

.DTBw`}+&h^ܫl #gYn L#ymXyODt\f7>.bPQz"Y %@::i.-[RV.!p;oAI(>?>voBrIqNXviE<5LAkqȃ_ﲣ8i7R9Y͟X:aW?2k˦\tr׈ "cx(j%CYaѡ׵
	b4+HoBsjjH
DaG%.Ź
՜=bW3rpP&@$?C<rkzkD.cOnm
sߝ$Q#kV;B*;c֕?WS)f,˃'(`7)Rfjae/9G~|m짲Cz+sqH精Qqu.\jHDf%3URRcJ)^ataS8n\s|&_+6<c蚷qL)|ƕ(p~8!
G4uG]TĻVmB}z^!,߇1ۻ􆪇l9F`ٻ
tKʒvbH7QFgЈ"ʷnu8C4@K׋?^|8!@Xy	atWp珦;֫,kKFE<֐Ɣj5'e1a}tWWb>7w{SD^rw=,+<;{R;K>&[Хp%wO-Fzb?QR$D m5e",,z=QVe-.SJ}=)R"-[mIsGu-WH8'?JYnZH
_YOf1 B16:rص!UXeEXUm׮&R퍲ޝH![c.ɢo
}0D ޓ'aBmu&-Q ئhxKM=DHѬ5hȩ<9`̔+CA CI=V[?J/}fƅb#^TjAxu*L맸֡š*,iq{>F	@>#iRV+;M~ &X\Fp{Kݏ!7t'@j] ϣR)SHAu0G4j(<T/=ܧ_пj>JfCʂ	B"TDHt	UeJTQ5@t hZa<hv74ЪҍG,[4P(m W7R9/_#Ӧ	jP]鴙tSMjaߵZ͡dg/qs/EȲzEJPm~JXu!AFU)TR11=CpgW0m[4K}
Rnф`Щ	wRK%۷+j23=$;%}Uz#K2\dsY@~XrW
ދnr!]
\'A/-/.J?k/j5^\a	[e% UrS'\C~'nYĹAnZhɢqWd[ vX
oǥ2	m]ِ
,kAD{m"\0&52(*:Ő2U?|z4O.ipU$jչv=> ߳sq)Uu	IQu>)Ԟ >Fٻ`P&J=kE}^h6Lk"E'_[~>|si1daَ_H	oVa9|(uwFio$ؤZJXU<-BLV_G	3N܉Px ;@+RR},;KzIK»1.r$(
<d3Ebf8|!|r6u8dL"*Nɀwuprza lJ N!葰ż傿MMiGC6XO!9=sl9,i<5Ҙ:Պ[68vW21lk_,
ˇ~1OhA{4D]7o	/TWOX6CpGMRk>9ij}kԵ8/֎dkz`6gZ_9%UqƐ-~G¤S$]# 8"b;"bih'5280hךQ {y*L;uBYJc	]\Cw+MعyL5>LgQAF+ץ9 Ԍ4X'°Bk]-ʙ;I,i*õbål#դjYle!;fx.L7o41|Iz=rR#d_!x'ŴHRІ|v3s+NyyOj"(䏙m;7zG?d$ɘsdi [I04xx*(¶;bIg}/[}«OZ*_ZӼ'70~Gs*=#]Q# 'E݊LH[M?+:R/
[A	TbAVq,րi!ѭCxZlN¾5`%J@{t^}^>}Km8,B*}4h	Aa8%yF8=)E.@mw~;|Ő~\#
XYfaws_~}Pb+13RdVXĊɹĚȕi2	3]i-/b;6$Uٽe
tDC1.avXm~{I^c@ɓͭb'{ި-=^Mwc ZDL	=@u(<]NeN^GؙfRAZazBl$i.uq\Lke
yC͔p[ݗ{ss֍T7Eȓe#	9hTNf KEXcww76
;~,c-wE1!Tz%ɽ$i#~"wO(\&O"DoF,Ă}YT*2Y4^ǔ|gd,RѱQfhpn\è3өծ:\+!ƏxͺWV=cOyc]4Mb%
g9Kn T". V/0|h@eh.e`Edagg.\26C`kdbkn!$,g-ˍNbC(goD ٌ*Y[I
FItꂠg8lkș\cmɯH4s8+2{.|a 4lȗ}:<jPރ{sr?`%Eע(WgOJClk}&M㙐L#ܧԤ2hZ Ϊn&.i{W+;%
zÏξ;`M9-'ǎ@4k6	]4RĨ?}{ߠ'o	zEH*s,a& z{`HBE3Z:cS6i40495.>ީt{?7(>}l;\V	LH4&UĚ[a5yJJݾ\5<Y_b]{>
jJgLgR{c޽nN Fbl*;p­i9)>&	SUƈnGzJ|v8
lɏo
mjZw.jJMlgmT
M_A1%[-	KxS	>H4}iJDPnx{k`C'6
bab0[_0wImGuN7C@_kk$Vj"JԼdHӿ8.CD73Y_M1`08l
ǻH<6kT:Y;B-.ԁi1>7DZgsΘ[T/c"P&d6]{fciꇰ}c}oG)WR5
Gˊ¨,9lB!<1 ^ISѫpIR;N^ZR@rF*_ 8U- q#ںr+Fb1:"@gp./^/1+ ۛa|e
CioNbDoV~;}R/I5Q9_9ӣ_gHjgrx6=rq@AU+dbrI59jCӍA/H;gy\g!١N߿fyE'DPd:#ܓz_sQjP00& 8s{5>5xQԐY:DXY㠚@Vff{,͓Z9pFAʬ)Ŕtq.I @WWs*"kjYFDwUj/Lм)Oi\zx,nuiH gaU
ny2I]%S^'2( ó2۝@xp JV͗DDZRiq6"Xղno
smwm_rȳ=ZI;+K"'[9kix?S	Sp_H
.Wa¾ #$$L% zᑽ._	׻aA+Q죎q5$BvUɧCd}j|zS~&V"0'x2rh<{{|9BFhaX_`e݀iꋯ
(U4/NBm2կt
(V2E<.0R$(In
[\<~_3=vxj%YۄK=:.:ĵҫ/[ʽ/{[dpۺT;S JaxQ#8""`w>{QFyuL|F
sKc$F}5Y뺨0KiY	:BTWAU88ׇ5di	r:9ڴ$
Z$IyZ#i7<-$KtLѠOcof'8RvvBi=yyhPmle&szZҦ)Ia)):Zm;L%rD/=1k{"?)HVrT+S(y$E߳ΓχLa	,UKfxe>VmcTʍ*%5$ec/,5FMUoe|Ō:L&ȯEwRpeld+,{_BA]b1}lʳQ롽}f%fԞT_H6^sg}oo{a\ĵuZHqʉǬ8CUʘ\;N}?;KTz˚}8_å+F$gx"-7mzJ>rajH||W}f)g)щ(|Kvc	`H|p+PJBHeg7Sho04d~bѕ\>{76: z^}K,36MD0] =sUd-'dstR~R,fy媛űM$Kٔ
9(?wpD9OX.@3
SQuvU[cE!Vt7V*9aof]Ag\ju-Х~(Jx`إɤ<2P\p3D(Ie&J|ɑ$|8Yg#{SS󬽻V3ٞ	ř#ϝۣ #Yf
{]r<Mlmm쬇{Ɲ샖;;2)M猃PeINj9de5+RfAaYe
"26:Ġ\/>2;ޟ/y?݅V渆5ޢL&dp,&_{_:'"5lc:)>["Vӿ>ӈ#maTl(ŝg  ?R>·sc-tca -I:bĠeD#0VZ+N0!JB Cφbui>VUGݖNxxLTJnck=^`Ds82	~5̶[~i ^@f^Y:wU"WcGlTD	!BDJXSa%Rh
'ƘsV	|;Wyo6;[&ca
n/#ˑ cbm8|y}8*>o|`LգW%=du"E?|(,jo
Lп,IP|;.*YSe:#5o ԇTYx)TDJ(;zNʹ7!^j Gzukjf c{_fQh̅1bvdih?ZxIZgC	=09)!5ۉǧXRL@y_$
<$7]a˚˜_ brU$or4먧Rhv.!
z7~	ɸ"JLBv
i#*>sI0	";BLg`79r 6R2jvMnNS4iW,tzYu4A{wBE.6-X3*vs[ǼG@A!BL(Y	̶(j0U
>Mr;	6wP_Q} 	1',2=֬ƯD!0^xU<7NlӵuF`
y-%#׬ҫ Zͥ4
DňvRe,t^XhώrZanfn,YDsԗ+>]G!&w-q}ezU0P其 >!uFu5+UHygR؞0|&ZTȇBju2* 'Ÿ䌲.Y׾Ɠ%x
 k'(**F}ʞiMBQ
5}:)=tETd"?L:X2QONl6{>E
K>%j/zɖJÊr(jRaesz.SymZX%/4qeJ㾻HwƔ[W>cp_ŸR/SEXK` c
)|Ir%KD9osUYz2Pod!Tcbfĭ~Sd^ba4-
H@]UQ>l`Js״mp	Qk'qPX8M}VCUW3CEa
09xNRQt߰Z=!_cQ<p7-Yv7<~_'B1q%Q+c|yZq?J(V+E PoQe@1낃]
˩&֭ NJxR!'y|pA$ pwg}52-Wq~hw~[Loo]?F=	ؖe`m)V򀐪EqY\Y?ieHeOٟNY3ߟU{KWmr$%slz&XAG&j$Zou	shNaϭBS_U2+ot/ CvW{"@Ii>qKx93Ѭp)-]r'b"nԊw"3M$v,Epj⓲˩շ$kV!oK't#YU%	Tۤwݧӕ븎gDW=iyҭ<febHGCba0RW[0Q"
R,*v8ꤸ.(u7Ϋ䡸訔Qg*9Z3v5bPc-Di4UX->OښWe@j.܈9<Q(Q$rh8^pZ{^{r/vMAUdHW^nW^4ߣ}:<.
z
%y,+B6k#Lh<UH/ɏHzu2QŧM<%J|~a'|<73kNQ9l:>m][opX,b3	Lc56|BIߔB
: tRsV7GgIM6^}ԣI'Ҷ	1(Q%
流X^n10[1:ShRuI݆M}Ls{hg7g`}vdQwGc&D.S @tjbņo9CJ4	oD,\G"c|0/܂S#Q7F*`^+8Ae7W_0Lev61asv{:\@~
&KVY0/pvYwi+A.monIwXkakce8ŵ{w[.Ki}͍p\JyZ(RDJpF[tJjϊMwa{gv~ͣX j{_
UuCqXÆ	T{A8uo2e2+A˫ 6l	6¶($v_1VC;)םԆ[!%cr[m<uey #EebM@hA&}&$\Ֆ,$e-Q~HDԪ:f5*IH@RP5jsW`ZY{ffrɣ$GI]}"OIu{}zNȆ,d>ɧ CPK/mfv_
pt
)+R{Brb|	 yf~n77;W9&rܻC*	)b R7]KV׫z(o%@+s_|`Kg;\o}q/7͵͗/T!v͹DG{-%QO0m;ͽ-Hetӯ	(Z=KbcGè.I=ݯ
Rm}Ur7
h,Ǯ|x,=RE\a K^P8vنA*Y8f
ߕBQQkԵ,c7}2
ؐڈgTkW/(X^YZwDYS_`G{!@=_>3)lI{7R;YQNtefi0t_%1!C@Z\郕S#W!x>2/
z .7æMg\k$LZ!kdOB5ΐP:C>$۱ʥ7X=T*x:#p(b
*#¼/:*|'Ol;U7U"@.jR=򧯉%Vn
#
;52qZ2\p7"5ldFޒzO65-xpRKqTz!Ht[#.fjzO&)?9o@tdeM'HD&|pWhr
S՟ΥTwJឲ׫˪jl>7J{MTGUڟ8Xx}JelZiʁ^%,mvJv╠7"
MXn>N@	Kd'rɊ^MHW
j/0
g+ft[J\K
b\(|G(`S muUZ(Vt +`?UN(ِe<F1G%-asccDU^SŊd󣓓LReH?zMoZk+_~]ءw5?~^<ngY'ACčqT]U5G?Wkz=CN8e6y~w)<^ʨ*A?5Wcg[@rǓa
 jnpO+C
5kpm3/ԙu?Ӹxˠ׋zTD1 m8&N=DykHcEۡg3iZf! _!|bfl:ub!M5*Њz:@Od;] ?s=uͨ;,O	(rPlp[|8qs*Yr9UuTȲƩb\O4.'gB_z.2̰MDtHP<&|# DdSXkQY	cgݓv]*ćwm~) rVD:R@71B&7
mR|P0;f9
lBV﷕.Es~5PUCe'	>Cqzr١YZ&+GuKq12v7;.5`!Ȗ b.ErXcPy}g!7~	1L̀'ViaRJ8t:,ACv8{@|)xW46sZfp>Kམ"PV#4:Uv 	nԛ-/mmP	%wu9Aa BP7-P;,o&A*Ϛ>[T앬0Ư!jZ,`&]W~R@j?J@8צN1(lzN/bRe/VNɁ+A;Y"h0ǌ[sgPI/Y tjQ@x@mO2rwdRyiOL1lFc;ȯb]&ZDxCJ8TMx]Ocj0WiQ&R4ҊE>P)ZELA*#z{iNx|_gmV2Է㬘M|6_.|
e81Y*Yqu?QzWaA	R!aZ=seq'miQ)Q9#
3 3'a>좯:"NR](ّ)	Ksi3.:^WnѲ'qYy"V4߰cjo1̩#迤
Ml~	w@Jګ*jW?g#B y*M3
RHM=Yu-JƎqT&ZT.ϑ`ںQ|qa˛1 Ȟ݅	=xn#µhb&"?-z
H&]߇ri:䠈P4F34rHgyɣde]I_to^0zD
?K:݆#/>Y?5FGZ;ѢOxC,7M#)G	Жm
 mP¾=kp
F)C.PB4{WI^c{+V /@R'^֬Bv,vow_ZZC>DB{g<nMV٩鬻 $P`;^z]|n>;J#.tA "$]7W\ WΥZ9ͅY'uM)1>
%kf$CT`ɔ2;{RTwcjM,/2FOݒ~=~g%dށJSB2%gZf\
e08^loQs=kf..gߪuVNm;b-DVyۉ'YBҮ6bP͓FW`[DD) <įE|WH`bY^PGU(  >L 	kW @4\F2e=# LT
/
pMR#ȹ
'@NX2xmYv:WkD˟![76W=2I`E4|uM%
Yy1a|D
 XT̠-$yDQ:?$QT
̭
0ƖVJ2n{Cx 㕎+f|<%8tɥ}e_m{~֌
>B[Cp"=k熲8tƉQu7n(rn/\kƚ6p	{V ת}kd@4"Mbf[.(BL4Bp
SXȿ^<=/ԉgvkT&_8Qs̙'S[V`U,wQ,<%4d3ϻXW"j:l::LxhLz@al_`Q.kJSiJ!6s>1;ZST}:VTQJkb^M5[Q_f\|ؓm!Vu5Q}5v]e_^H0,K\%T޽q-*3]ji`wzR1͓A+ĖܧjpғL)CL{"ũX8R㥠sC\
uӭWɖ$ @x\ѽ=V^|Q0$E1pdP 
UDk۾~4OT}T0D2Lr8(F<_K{tO2]EV($?|0B<.bxb)T8~:YdN8m'OU0;l^
/JtQ{^lrj%!a8§$}-zaZGb/xmsɈo5Y	CHMvΜCn[+Ct%5Z"}mtS#E+jq@nP~{"Y
>s~:QacP
 Nd9ڍ  \7/Ϗ/O&6U+|a⬹JS_!UYZڂZ/|+~o8(h0K@+dodQ)]ekMXcfFhvĜeŞLt2)jV]H8iڼt[7Tcp?OaٌTf;j$^ʫ\6&ب]u') <._6-OyII]cR;1r\'ؽ ;:g&V3``Ma;:Øa3-2nE{KOn$KD<\׳nDci)pƈY?shLEnXX͙$~x_xI>5fI(~<C޴EҳsA¥F\+		4`ъ4{)d%&x|HR!!aF*iah Q$zW:f1	YLk8%M{VR0ƎkNz-|r0>lA֡d_"akc-HE|h?\KQ
!l}(ojc+WΣ&k!#WN hKСðq ҝ7T:ΌT^Qj@,B/H} H#ZZp
mV1?|*

 Q$7d[
1	ai4a<:Ϗ"iPG?-6)w)ءrڳTuuޥF
Qy"9<}wЇqPPCeS=Ӝ3y Ar=B)YbhTAmݍO/U9(Y#;=DPht"|,C]0Υ rgaQG#VI%ơՆ	zm
SNz`fa\XSs1*t^
=JB&sK5I/)#]*iv$D3TNN,݋;Am(#;$,_||n@A#rlzQQsqNCt-FIjP{AI}i5BT?jؑLCiBF(!u\8Yk*Eh8҅GRudT*I"7k@660*) 8kdv30	3"uJ>{CT`SrԁH]KHBڂ2=9/|vt<
Fڧ@[ȓ|YpRpQ=RV8酰S9mr:	Nr7[꨹h ":V=ϏO,[#]P}C@xKSaeLs޵
nzҽ$4<iY?wة}dֺcbBՄ1rW|# _o*<Qtx(EG;BG7-xءm)zJ6檫JgzNVX](۾ZBL"	(>MoYA
~#蔚H|VA?N֟K>:c}8R-n
7_bz2@=;:Y_o;ʱԙP
P.H[ΞH[W<wf9B(I=#DHZ$<HgTAir-\˓du%0/-OHތ(<-t OGWކ߱JUAUM4F^&Wivn\ՌR:PúH8b)O`eAԃA?b#Zt/{ZRɱxhS*\ި>ѕIBАHSe<
ч <R:Dg-_KvY
pWvR0CP6\:~cxV$=k0I_ސ:^AGD<d ڰ@.Y
R?

T
vG}V>zS4BȎr4@]*[YG{*зkPp.6
XHWXhD>?<dǏp,ckZ#Ώz/["b
b.g7*oi?\zf,Pf #Fo]>|̕P#ק)~8\./ۇƯUexU"yQ?~kgsx,gXhnf(F,qM-JY`Zphޡc#o1wʾ t14<ku׺z.l[69<RQZYGp ELE>{o=@b)o8TW 79n'(<w⓱a~W*(aQ	Sz&+f[J}2e 	a=.ޝՆ3vQrl嶣0)Cj\:8ԖtkDka0C_CW4PNpɌ1-+˂|*2^`dleZ
QY%e͗00xW42':B
<"3УS*"Q6A7*g\sa)IH&D3}EWx Iʭ~$qFdčÞ}mCGݕ/D9vLHM[Sq#+sgxB&0DA--K8(72XV<R0u=2J`7sZ
'a@mB$15mytyr	Џ-T$j˟hI8Jn~"XsdqHd%[t*l,9B7e0ZfbXnGs^?<9jZ}>/~W#
:౱>b	W=!f=l{n Vj
.5[6^!xt4BQZ]ǷK'%u莎%+rNk!lARkeѳ pDƇ|:&5oS
u<)Gjm+HJంهd e.h`Eqz2 W:<IMU1>	eL3io,Y̧6u,|i^sL/2IsD)PÕD'B>D;(3=k/	םTjXRͯ<ʹ&Op쒳%(lq.JW{׉,9Lk: oiKH!7`p`$BDx}"_ }R֖ q\2 ֎ M5Q=Bƃ
Y'=@'̼c! -
!
&i)L=4"#?dH@X1F2}1$ #Gi$z4	Q$
 dFґ>l㬑_uAFbPF.8rHta|%kixJ?^*q
RqZ4?r OkQ EދVe~h1wbӂ'L4&M`CC~y͟S7Ms![>a=!PjST hU7pE3R	9<VG30A%_^7/jhFNb܄,oU]U3)E{9
e	jOBz֎/M=5!ɯF#-LS,Xh$k	K^8&֬sMiJl!W8z6GKi[mhIf,n.#M#.C&f+`Mȟ`szH~~wHnǵI;#2
^P!  9HXҗj~U9bv# 
Sm͒ry
K*776~}~rLF.ы@51'jBn,b-dذ`%y>Bʪ;e0m !gFmNS_Ekf8}srw^,bPo-C/Z?!1

QM+3[a)V^	QNx=6)\Il&bd:Gxac<>_dPd(Kz]L3olqGDQLvخGtRy"<+)*zx0+]4򯍆>Õ1E^ΫE>5%%<6L{lZ\Whۈf!<2o8a#ۄD"c;LVYbiŀۅNVV-_ln&r+mp#u(PEn(3x=_RiT)e+K mߛLAjh/k:L=+'sB9Y*,়O
Fr6^!n^_
*AE'C-	F27%WհV~"̭Z!
YŊt#\"U4c.K8\=jL6Ό/~ugzMnl¥!Xlh꿪\IcZ$ֺ5J*$3c2j~!2-PE$Nk$GjmX$^";
hE&DqfCIa C=ܱ(&ݻrb-ap<S$:wz\
58۩렚<ŲP|3kf+>;>K܇hW78ʯڽYq=¡,C	< TIc	V6%(飺zF|,@cʹ9NlojsxPPa?HEJ4SPۊE@+'b\!߶,IU$@SY@ݿ=K^~T79BI,%ߗO
_j4xH};񢷹{545HŐ5VL!eүF[xH+L	P)㔌4"a MZ!L"rew7z[fbE>ØV	a6)~s݆q^L|4[[&.840/?b4 fћKG}ԹE,R&6ue{&:>.FN
+o_MǰT/MV#*Jgdˇs-
D^7{mlT*z[!"gD[Lc$hV)O@M_V/{4
wwlOuQ(
yD1@GSKb|BTm%H;eui9VsB5%{)]t
:8܈`sJAm:MLU׏xw^SؔGֳZJl9@dqBsA
!ps/7hLКyQlP)C{p,)\k.o5$ּAHpm76hPٻ~c'G<Im9cn+\;\&f촞u\XTZCġhR2nV3H"w- 0\jKoGT"~
͟
xg;tB(fON_J.XtRWP0J-RS9H-](sB{(>|ԃ&hy80j>BvIT|!F?^ч?by$T:K;I/sIchHVW=(I;D3b6F$Njܝ,Jys{+B.fQ|smHGw"rQ`gAnJO8a$e-7ބwP}H*IBLSxg|hJ:j?>m1HXyHYvzxa$%{mτ2[ye?KlHSP1FZB
un÷<l꿄h0_FDُg
5AJ8~*^(*TqPI<DǛ{h[&UIx:
	ո|i\Ag
nQ^PDpy9LLK3g9~KnPExigU^L>AwN4K?k2rX7ܪL8^ĵ	(HM 
wi<k5RGgrDRԽ6T`&dS=k/
oI": -K[ݵmG?k_v_r'FK&qe҉aj (wk-}8==>PM>xM6/J[EcK8UT*e V09uЂ`!}|Ye9>a݃9Z
4PPEnS%7l_0D߽
%cq<"RqY_1ym; /ÛZG:,!ƴSu GD"-M.7Innsx"%dd5뉚FՂz{#]Hh"xCG[ʿBe1[GkX\F!r7bGz/wL^mohX~޽!~iAO*r>/DLrXPGi>mvE,STm/:ڣrUȒaK`Lvh#T5JlG$8#c
QU/H3p&*gѕ)Ή?+u'\:N"G^#z(}^W'ItxMf?OSڐHwJ
WL%(P_:lm{uݠ_B8@qWYKX՞$pwl	y~B9jذ8
7AK>~	47C6wAfgiDWx 1Jk'q8pYsQq,,bڔHjR'Nhs)p0Z[2g<|gk!}J8[`+<gp<O{bѪ69Rߴ;
V0Ǣ/ugCL3W#LBgj
9פL6)2X,C+4uR$[Yz<GgNޝ\/^=	)Nem4=Gi4:ʪ_\.ݕG)bILZ%he,|Bha2t3j	za%@qY^EE\^mLR\z7:]}:^˻\&JkhGƛTW6qe`+v=`tgx3Ŗoz4 @V-t:crͩh(i]B xŘ#}VuUe.dI㊭bst"/vIb->Z)'<VzZ5O'Br3UgM* ջ"PԼ%g,Zb
؊h*oZP:`FHDj"aR$s$Jc|YNuo<JH1@^-GojuLZw|AY\ʟk2΀<]20S(-PsJ`OCEn[ȏ"LjPrs.C1<. AU7 ɍWX2Wx h:^zz)XQBh&oFql^iR#X'0OKS]ʌy/YZ~{"Hf'	Cd5-zsWdF<Sbfki5CO7&)0G CDɅ-<éi<VW$
uѰp~1M'5^*!KIP2ybqP2Ž'd)uOUls !$@,._xGMi#9F=@v`'	;W4A݈p95kV.wo󢿹>gǂAf*vcaW6d[W;/㝗ً׻W/כ×Wы,5fq
kLE&[(]YU;O=O$)v	ͦ*

+Xɢh&l*Շ{k߈F=|HB^xӑ4*TE\j1T/

D0f/$9J!Oz0XiЕ+56W_@1I+b/)<+Uoq|ڝ,CNyZѺ||bSAH&
3w	+%{>1'g}]&(b<T_V@mܒ;Uj!P}v  o	Fp`]
aX?!:YyBTk 8߆g#pjU?\IM* Zw*a4:B|YVP bY ,}9U}q]UgYJ3b_!ƜP>tXG	OG
,7H?!~եT>O*FT㼧*kl(k]t)X[ՓW25\#?܀CSI,@_D6mdTs:hSMI?EV:jx\"qG-H%bd|ksQIi]<eԇ>,͏$`'QCGQMMDv%vܝ5jeX=j<	<\ovlo|M@MU5
:Zt	Jێj0۪ZݸSW.Y8H(Yo%dVtU}ðQ#G*dL)n%¤Ax 
5N(a:*6YHT琋%˳ jxfeW4>_Z.
3H+{?jZUeFnuђWg]2<s.l-j$W[Ep%h:xRpq<Tew?:D Gw5ZK0uhj0?Ͱ/2ۆYWp%˅1׆NHJ,۬߶"I5NS	GF0	Ϙ4K)]+ڙR~f: Tϕnؚ-dc"ي3YiaEY#0ē`D^Zn~['4_P$*uv.D	FgLJZC:/SQZ8eq-%j]4sQzWs[%9%U
GRF{נHϟ.4:Wsq	
(!_(DEVBU@Z,򁖧f<)LA@x8J;BL0ƷLh/U"~%	({Ys0Wl+D!`ϐX׷1]PF	Rg+(kB&<mEmՍ3QΥ>
@`a*x ü DLWrjQ7f3jBԇ4W05K5VQHfIX5Yyv-j9MZs_l;_;#.\lK37;7_|YՎb݅^
.8$U4L8 O
Ybdo,R#=˄re%v5$lI	/-w~Y|`=Kq%#k(h7[i.y:#':wY.JK'R_E2zς}P$׾M"Tb_ɝ:*m59v[Q0<#H|k/TM8FЭ|[ӛw4\8}Zհ,V_E7Qw͍hvyХIRwE-0=-|BوX|!HCmM^Gi>TQSf3Kj?9U"m->U<2OԒEE,VhV?eP鋝VVm΁ɗf&Jꎉ;ngWp*&*JRm6+Sdxm>9=8|
&@E?
bMơ!]"kׇ_Zp>*GF	sZoZwƟg@Ǔӷ'ǗZVpMV}:|}Qo J+aH2曓w죇sەu
/Hhv0_M>!6͓sReaw6^Pŝc;3.b9E$\Kb\1eь:"?
YHY<\.~ `Z?~<=9}{ Lj.GHH[,hŅQ&I
bc$H>V7.wyc-w {rKeZGup|&0B- \"W׵(`klyD5vV!mfYrAsgΎqC:wX3%ka~l}i) tud`RE0<<8p@qglFAmkiD &X;,|=C8ayxI*,8rriQˢ(K>]\Wۯol>D'.-'uPC9It
['^⇆gLFQo%ZRܰ=AOE[Htjvnq
zɪOR u	u(73M	E`NN-jK~d L#<÷'-E gM:;?y{vrvsFP$OzBF
@Ҷ<eT2W+uLB`cBiU#xy3u7PsS"Vj4`Kх-]o*
0,+wFC	7{Sd/=3[AE=<-QC79OƇTT!̒TBjUIQcwpGʷZgS&^rPW᭝m#z@%RW,z@owͯ\oT]!uCI}p
M
6kw`̪yӂvIi	J~8dⓐG`M`;	!#Z
U-0nUxT"Sv! ץۑ KAWԀ@ڪBd~iIT1'ծ)o.
[S% 'KX@9l7$E<)OU"<聅Z @þ@ʐ"/|#,X.ܝYDiJLŋ
RupQҸKa~itAc<3
N21)LW]}@0LbXD0?Z4BVM4} \p9EB j J&8*]WUP~f꿣`n"2.!@5mJ
JIj+bf$Z6{ez[`v4_mP9;M>]dnw#S0J-	ԁ mVkfMe$n{s))+?
`g[&vE*k͋$}(}TLVv߭ Ԡݨ46Q4pE TP<N$+a\R󞵨qCW:5#Nqzgڸ!-/)N޹/E4fRޫ>޴r}i٦# OhFr"aG>PVZZU(ju(ϨWR#ַ22XJe@dZ	҅נl5<NF:Tަw'mym47_<wV&w-ƨ1Y1U[0Hrs,(JW߫mWͼ1,\GrGQo\
amRR0#Azr+YWQ34bum~5
Y[@ُgG$1bWfS3{TيtީrKL?PcU.񴓦1ֳ=vDk("a_T]VS@5 /M@i(@8KErn833ՄI.'ΫuCI6:w"7SrajF.]Ky5E4F8>©?,Xvxo?:i(=V]I*CCយ
sFUn	eylcU+fn Ԣ346jF 3tDJxZ&čȵ\(9` PؔPUi*E7ܠ742uD\Ѩ`謸	1p8QFu%"TL*?,Ǉ)^؟d>Gy?>I&z)+8Ó-}JGቆCD%[AZF΃
r]ZKh?<T#ߜp*'|WW4:E|lwTB\*uP/h^D.\kN
MO;K{'ܥxJH|㤦MXoGΘO䙁NLdP0nZW`U?<RK+ؓ1T{GJ5
8vc'JГ6~o`oJ-!YR<we7?6;ͽ>!XxlKUU ]!:~ZLJ@`Sb}n>%u4עF2pMQSs6)
b(|FmYe[}937Qc/lSJd/*Ke	.Pjd-%*[r~M7<54a]_L_%K4qOÍd!G'')Ķ=b3.nX}hļJr){v0"l]3
vrKhDUr4bddpѺTZbf d߰_=uZW^phK˙䬀q{IAB8jum LRiwh[f}:-4ʡw|хK]gΣ\nJqt	fcQG1xCj54I!\N3E)4<>sO\>!Q'Rt	rǏX DuO"!IُXmICN1}N
M
YlY]Kޕo7rS. u^_L%>w0Zy\Kt9J>Gڔr^B{AAR\o˛[P
nB;)Q:FΗ]K]5޿}Q>G_]
s1q.WZK/fb9ᚤD48Sm*NPz~e&YrZYrgiLFzX=NjxĴ[+:ݬ5SՖ{ť(Fz9v,NV2PԶ]5;8eOWW!ӺV&T7P&ΗZأK&YDEρYUf,^8cp
	
1Ty"&W̞n]n]P_:-,#QZc,u~!{=``ۆ۔7JQmie'ώ+8
ΟB<[޿.^ոv
!IlBELHZ2:=DTڒBYgX7VfJ"DPf0@ƹCeϭ$Lk)iH6̸To4Gf6Q7aD?fRo
W|`]6+ؼbW_5fuxħ(c'hD#VI1Ekc6r(BcϏxY3W+S:tdsewgg7zrRh3-nJͬw`Ϫ=kiq+
CaMtp&c=w#a-x<s^űCZYp-i*
,ޖñ5~}s㥚fs[/m/XuOۛ#(xw0 D
`5;J\qA
 dQXUaUrWW]WT>{\z|Ijl`PVgO5F&;JQP6š(&X=)$S*듛kğSe3RJ-Ķp>Θ)c_z|W>y[=	N=kV1͌	QY8Jyσ0kxW `CSٜ

}RqW䓗
r5]PHKOci4ԷS¿ c\Q#6x/R|gqk+ZX/D^<bT/&~V
lX Zn?Zh|"ZM;8z@oTq.0C0*1`Pwl]uD@%:2vA.h6 @?ǃ\=;hoiXP<캺J[$7b\Y3rT_g<|&jCZxj07WLt(my0#pU=S<z[q
ty
\295*duHkL쐚}Wַ {p`H{L)NA\&p8
1*0ǵ#_K@nnmL|4LEC!3Vz-M
vFbdnltώ>_?8/U;GnV$2XŨ&U>-o.ec:SL
)fvG DdQU_DYn2UV;% XkzBҭ'^u["]	~%<(!{p,E'G"w#\m+dsu4
i[%{{ps	-t*Evf>bl7=l?nt9>>7%
SH7OYnzVh`!=~>Hu3
f&kk:"%[iuϵumS[1Ze'et梧Ҋoϧ!8fRX./śq];l*.~(<?_;XkYK530LnbYq+UxJM6_mҝQm˹=XJj L:DW5m-*F)aT(FC+0d(E㖼t1gjJ/
it4EtB[ձJv;6vۦІPfiÚ'{R
6P&]<K7C0	^E-e#RT|1uk|$'R@}&nb3vLHMt}i5p8DD.,O[++PD;EaoiKTDC"cܫYyrY[{ksBd1b((S&g &W(J,UVj*nH6ƺq>WB$RThuWc
ZWӋËO症GG痯OO_5x"8&q?zw|x櫆
4C
_	u&~,uAXs4W,|nKZc=>>J	Hj===%kE>B'mQ#pV?ҥ<B_!+
S?ki=:8ޅWhd^釋7>^T Jnb[l!ͤngGz7<qJqH~ݜmv{I0O 	v
|"z5S>zrkA{N[c7YY=!)KbkR!u̯F!h{pnHD>5=.e3c=Ϩt/N|iv:P7&"S6TɚO#'Sڵ[	AiunfdcF
̈́{K!FbCZ;6}=m<%O%@bIW&",|mo&Dg+:<޾HOׂ6tRL%[>.qUd4'9jS$&ls).˶ՎƱ^h?m콠[χ?T(!<<lk"i陕7=~.F	Cb~eIXLt=|!Uw<\0Ksm|k#8:	Sjsr(ԟ &d:U1R4F!LnR5NUStfR,A~:bDe	yg*(6G7d[&G),+aʙ8PHmUŋ21g6"X6ӺtNG>Cj&NZģ_aů3ڗch	k$$|V+y3?o*(3~X44z
d(٧%pĕ)NfEM`-xӻ
'LC
9v	kVf-Ր#p^\0 kIǎn?Bh(ƹ98HB-kq9LPnܮNpqWJ/e8`,F 8kGZD	 %2^F}_|')mparkx64H'.<<s+vpՕi/PhϏ~dkǏQU%M:6!Eu_}M"Q᳄N&5TH`G
CQ \$fzȟqOD!Dcj/A5jWh-zIu(DJ<B(^^SuEGu+Ǯ t|DY!G.ziA',bkո!j@$3vl@B(ȸYBB8OKcS|
2nHzH (YN+oFVhǘx7eXI. TCUA76E87dG aV:xyn.,#/`g"28{%wi/9ޠ6SUR,`yY'ƀzIp|K,Cnm>_L(pXТIKDKe.(\QVm1JuGj-Ax,`s?]s{(CbU x&SXb"_su$`%hV)j}iIgdШW9b!^ 犢BL}
!Hp`

\a&-U%Pth O|_y]^/CNDJ[}\%|Sפ`&25pL]C)JRF	JﵑW1-)2
/U%5#WS'9XRD_t]@PGl!p 5%3Z&<r2?ߓ!o1RG8ğ:$of7F)sd$`>wI] RTޣ7^\]XED߯ouٶ@#Ŧ @r	\Va@HzJs$-}DD2<ʎL/
,*U9Xb:pZ24Vi0pp(D=saF}֔%%$z6ҷ(?fVrǙ+Ʀc@q%֨/i*qT
:JƜf{^,J#D3-\Dr|YvPE!e-
+,Sp4~5r	ͮECbn6uyDu*v}+{Ἓ_;p0
:g>_um:?Q4ѷDe
^`R'\0ˡ,uI|@Z95Oj	~}#)~g=NjlŰ_TlnKfE!Hɕu#e4KzH
b5r.(ur֍"wN&G8q@e5*>S=MĢj7|;Er,rؤa!EE`ܪ Cq̣G[G0"tTPCׯ ]1$_BOdJ5/Vç:c!@F[$11+ok|ײwV#SOP]"2_+R"fp.RCW9H(FGjV[{\"	6`?q<K]1Y%1RpZ#mUǒp-!w-cIsdU:RJoKl5Mb2%%GTfØU\|D`PXe8ڂ[˔Ys!Қj)IMVŭcHUUmV6BT]»r{ţ[RHp.MF2b) L9]PR*JkKx,apo҂ExS>~V\$@'2٤HKw4-mKˇWlamUPN3 WRW
| ®Hgi>e1=FC9dwvpfd`V~铇~kTCFT{Y#pOí_Noy!7J"**ћ^(A1Lǫ0oaYY+KR>C#ӳ鿕 J8@9X?.9rŦN,9He`Y)\SrXVXra~]"چG@3u#ي.LP!2ԡpbgoQ19O	cm(15>"L!lT^VvbH`lA1Pb^>
d[x(|{,)WvP?PdT's'lv/^@*zSM(5׎)bGϭ{OP(b!U&_HQ_	ިvd2$[Җ<0e!aaV\Z@ω@Oc w>b9n;V(HʺK^ܬauJ74I~)'QW7	Af8
b z7y
Ϻsx(`@@fh^XhN'TSTKX^iW+Ϗ`yn1lp2LrB-si:RG@K@F]X	Ç'l&MvCu-i퉜k& ${rV0+ek-k={dY1jY!0,'6U½G7=
b1:J\?  b+"ǅ\S/Ϊd#J^]<C800@Yse-a5p}#1+
%U@a8`ӚUsB!Ib)/<ʪvj1rn쩯YR2RW}
B|OWU>e5=MNOM l:JH<WcE]-^rb!*910>jm}k	dqmFթ)"QUt6BC:M#ԡsY͈7Ɏ
eԅ#k
)ߥs(5=ᕅAQItH`<%7%mgݳH(
b=<UTuB䯉lޚ<rVkENΚWp.P.n-b~/<	aΉLqcPI?s9qbf zXcDBz,".9^\$.DHIP.&Jf_$ᶚipەX8p6E9Yl
\)</h>|'r@vUyz_.~#XuT0ڤ)OI[6CҺ->yku_2\wnx4H׆y8lЦ-zĶ^"})&lD.
u^dze+^mUO3^u'x	(BAn\Qkٹ K0lG?Mm1Ӫ<Sn2eY75c,6`UqL`J`J=X%K7[)  oQd\%N!ۏQtF.i5XAs$XNJ[gJ, z:Q\$VVtPdx!p
/Rpz}'CꗺLFHF느%Quc`}տD$(ҋiY]˵e4[đlF>uA?E႖CQFѾe=lP+߬$lGI+u~9*`lLsѬ,<?L#lZm䵭	*TфZ?O{gFע
M(HA  u
ulw
Z))^e`]f}_`TSyG.quN>/_:-Cn%
>ҵffn7ӆy<CNЧQ\we*vv*:Fqa_Zgj{tW+fW⥭o䦒rmGE=-ʹzH݊_m"[@~v8(2Q&]~5H!grHu$nsLI/K7d-⦚'RGFIb!\CP5cTO ,H03y`	m50R
-[~c>QWԭ$Ri(`~4,2	|^<ے'W!\ERIm-CHdRҳSA"USů,aR]u).AP*ZV=խ67_\c;Sܶ eShC@{Ai
{R<̊VR	3(c`ihk^8mx96"QUrO\Tc'Ƣ\&T6Lװq`=y\xN+(̪1R%h;f:u]r&>0%B4kU~5LJkgrlg3-lhV 9*9}**fQ?ZMae!`gED>MP}5oURiYXYlЪe
>(	H'HHu0*AK-2'\C֣8(ȬȪ+7'M7DUW~i{jd"S5SZMem񪺹ئBecӵR9C0R#@kW?7l6#Xi	Lr>,I60eaOqFC:#N>9z:t.Mçup$&9)%J[~i˱ZghG6U[".7Ά|e8a<Dcp'o!$1iBpYn5,uBhMŴqM	o*!le?;U-f<*Pq%NpmJpxj	bIº"lEJBȍ.6j_VrlsC?ޏ_l8>%&66L*0_Mbt҅<T=u4 ǵʘ=BOu5*z֮1|d! _QnOFsrt7Z~E ?e-JE(y4d{CEg[bU<'Ev+PE9]d>ѬL挋KL"Ǫ*$@ ;Ko_d[YVX"g
3ýUr>_baeӽWc"^O `m[YTOfsxL2R-Bu@؎fi㻪qH8ۢ\
M>A
:qtHˑ#ghX!fC`q(L
{^Zuez=uUzl
7!
8YcE5W'Į`
i	˸P<.XAj"
OM+"4y5+odRB|6LX5l=Лy(MP)
IUҕ^m) 4T^N |n<φbCh}/1Qk
d5V*Gi]dhzn6vPEy
?7Md&/A}ȇ%^j* yM}*9
ࠩnvc7A$a}{kb0O4`@$VDy/ڶCM~c u=
c"0vyʪ0<LDlH6cgg9QBo!E3"Qà"۪rܯVDHRkkn ({`MKO3$sr
orW^H{]v3=k!͹')-l[m,_e!1gHA?'o]7|U^ss%LIR1āTX,v{+CFO7E7jQލr8:s"P!x@K_T61<~ƭ~f"<` 9qKvw, Io+HzA]:ޥ]tssK{4ȖcKӺB{Pjh$LNd׉b0
1m 냺!,t?{5	-eZ$065&VEoLx1w96xMyU$[ n
%Ze,t0:BJK Hqy(~&W٪vdqAlXaP\	-[;I:_w^SSKd㿄eS2MGOSQ.|4cJY:dcbGC+Vna%q.Q:|+?,/W{M]|\S.ݧ,Qv-87ۙM׆D0-O\]i $X&4Cx.y2W4٩v6)WrTCjRKpa:67ÙRLh!
yTW""@}Y
{RMxBao^{rcGO.ӷ'Y{wcSw~4	
0U{BtDf˗FL+::@mvEMeSI^N뵬ͱQ<N)Hԃf݄8-'@g.uSZeԕ=泥foeQ|;>XZKUt<h=z_%6MӘwFpOմ|#k8Bc=a
8sRHԅʑ@@?i6aGaؘ+7ed$~uH1wMtKoDxvp6]'a(̓]\I
DV<b}}h/[u@>`"dFI
{WB
PMA\6LNflS"!3Qjg[ӐѨ}5!Zmd28:ʠ68qڳcX:w*uwٰa"^JS0$w2X=tTN۠(nkvKӅܓM|5Acvxeaf)n%e~}NkT^}fVC#M5{wmY);HhzryRzVܯ>i';X/tۥhPec7
|>/ÂSW0r:wMܨi1C0=IE0I&e9-[j2w	Y>!ҘV۳R`<)r	%j\9>ոX]x<v@bV&sI
*C;^!;E1n#x:Z=~vC[,l^i챤괯_GQM[J J/B+U|a0J|4eף180|Ř	g)w_WMmըrX2MMMWE(b}QT9v4YZCg]c4d:  T8ike-A@ZDRˏLi+GiWH+\lg7
OqgƮ7Ȭ]-t\4u,+HA;pݰ]+WaV}C.3pR+ /bՎE%~c*,y+.^Vy` 6]OV"+̗3@Pf뗊לa^gޔ->ݦ|Ia4G}ٔ8RQB>2Lm4ْ R[t&|ֈ;֭
?$a)|8K2[U!4*P,JXz>i~[wLJ+嵽m>_z4\̳
`;h0U}Ik3vQ21_;5#,3<C3ԹqJ'h"G+)Wiح!Z/lC$¢0_0>6BB,ڛ?(6#	R1yPKwKkM0jcTl$.xu4ɠĥ:i~
9IvCv¨>7ф:l{kyTBR0nɗNx˔xd+lX"0aZtqm<nmt?w
.o޸>iFw.6һma6T%hCbdv ߲4G5qOΠKkI$ܹCpxn3F{ygbZ^1Q?Î(15A$I_XLyGv91e,q67T8a%ERWҢ,<yQ$BX(a|Y3̢J¶(ΥesZlv6RvcЮܡj`QXu}*؈3l辄" r'yK&Vʳ[igo79R)wYx*?V'O%LڒOFSA@)HU
Cߙ, 8W)C#B`v_:	7Hܐ_uPV{S~O]`o~T7\K8*+Ev٥
FO
Kx1F+مܞk
q[OVBpx/`g!~4j]@ci&G0\Q:{X.fHr8M;KjwM'F%=a4tH<I$*44)Sj'
87>?<=:|}xq݆Y`#/ޫgoEw
դm~"ԋߠ25MShe<F_uV&MoՑ'cYia%@.\&##qB"W,Ĳdt4-OrRNRt:Ȋx4<<FWKsWnAHDdX^LgѪ#~s?d(֎N5X1MN<ێVqYX	;<%ypO"Y(poBH0CdhN#>ɷ6ڷ7غf#DpMMGQa#əz궢lpM@OCbT&ދb_>HF@⺠4fM$3hR`ѥn9;؅^)Y1`Ҩ'QN,CSlvT<"c
/e^M"EAnRNP&,-1h6K3SNkx\HU,'P-I_z!ʁK}D~!x<i^*A+vVm{U^3W؜{|8]	6$uRRV*MF0?]ߖBܑԪnWa[;|U\6ջ%
Z*]b6CZN5,z5u	7{*k")BOMcS~+x#?|);NnC!|!yogƞs?d%DpV9~h4%d.yyLlY)\z-|{(b=qdXig--s.@&ChF$F֕MSH	jPh桧;Edhm Ԃ.]cCK
47ٲ;:@>Ø+\VIQ? V89ͷc{ڔ).&TP]\BDګ'[-^CvE˘ Ihe*Q!)Vo~+ 9DxTPQRr$E EIwe/fL㟆)\b+41yIԡ~	gS>*t\i@ѭ28^qE;@>y8_ E9Rj#G{{Ye\&]c?@X>׶<U`U`a(蚘}#PB u.V7^_{
ġLWOj
E
μ:C!ni@r",X[9!q2ӻ($;Q'(U>JÊX!c&K	t2 eѧFIO0׹5^(,%2j1Y'{8o0bQy,mhn7W{>BoI9b[!Wd+O* F0)tSvig'_E$/ ָcr"gQx~L*(4?j5zܒCj|v~)odOw m<Ҡ:G
C}4dQMLDP
HWDLdT[D"BZ#q⅔uɡυTQe]NHnGx~sw18ׅ@!,@djB)k	zqv7dTjj}S"{;sY"h\L=sqfr,ˣqY~D"GwqIrh< ~it
	X#17_GD_ˍ-nڣNa>L~v³Z	8{͆
V7&$NBp mX'nRԲO-"1ac,|`QeM82ut@?rgg)rM6(@D>Mhc?TMvּA7=;.Er1	F%z]HeH*τ^bS]B@"?=>/e~LjEIhWZI>W'tYK
lp	?]VbBs#Բnp;zrS۬4J<BMS)
}B|-J1LK
Wtz؆5#zY
T[HNqo
E:>)h7J3ylCT8t7b"4
䪹oூZyEW1B˅K
33ІkEP~c!Vŋt%21וYѸۺk
{lP^@"\`Q
uDw|_*PVqB?/NçGmL)95԰W82g\@Ĵ).T8R6D`пϩ;{dm\o66vw	އhAG"~ra1?jDE_E1c5
ֹ+gFˇ*1L#RMMGy3kB@`s]/x~;&\UPw]ǫMP4~
DVmxqda-)LR-YF=)F̟nO?\xx":,6x馮saiIE}gcRVkw7k4iu+)F~8m1<6hz:9*kcxrw5O\Y"uwhD0Q_	?;b^BjlV1'aul%1Y=$lͶ-jgMx[B-%wV\c FղvXqҦFFԂg'jQ(HTs
x(Ur9-kgP22+ƟWgjV-9
<	ߓ9
{djYHRܸK|қU=$̋F߲2aHRN%tJ=}
[gj3k$CT<$wY=*'16;oHF̍!U
0^aQoxF֣SXOQ##1TSwPv%P8(]3"xx51fF#a9s	VS[@<_i[:%>'y/,FXjwpwb3h0K̯F OT00wϩL$-RXժ(;@/xqܰR@'UrJ7r0ޟކK򡸒A!*M|,^=i mgn#aQZPxQ8i[_>Oh tVv>	X{OyoVFdu#-6R!?ɺbtUFndpbUΘ]i-u 64'4ԫ~.XH4;"Caݘ֠Nk}޸D;X<_a~B幸sX1%d&A^Я}-S
Zh0tN+ԯ ;S&f)I-?<.FVm>C\DGLlIt҄5+x)Q#8cf$N˗TopjkG+^e-eVÄXk9~Sax"vhv%D$YxVӧ%V&͆tgY1.HGl2_\N//)^@%h~,.=q`-L{>OVUerUׅtAQir}FL-?VCvK=T+͍Rt&,En"
,5t6/dF=XTL47k0±e+%)$`ipdRm$!Ij'ZRϤpG,"BXQI5Nd3zOyp	KII=P'ܨhTOF^WVPUxgmMxނsqJ5|#OW[T4jI/|L5:y-\j:H{Zn,emQRdӬ=a\)#4=]Qm$Γ%"H"+G9^fkO^g2g_@9EȕzO~NZ{p?	-$b	q>`B}Uy5vQEP)fI52@߻ch*u)*AXX
&4S%HbAE>teueȡ"IgL+yC֨P\@|`Nmֹj}h6|v8mm+ڈ_@%%(wR)BY/E-Y%#Y*<Ƈpu5{U[c'F/DXiVs,@my>%XB[x5f{<i0*]DMA12Zq-
L/&4W
oKK?r?c?Kə[3gu}
式ՕH7Yfpc LCsH`V]}>e!4/7a0x[£rr9Z#mk/!`Yu[<;d5_0:
),yE;"_ː鯦ިyC{h֗9:X 5![j7YW?{%:- ˘IZkqvT/AͳOu1岤yvDt#U
'̶ol˼(ѣS8
];[_z=fϲ3i?NGQ?,7b_ UPw39ީCR
5_q2t=}	QCMK	F/!>XlW@dH*|HݛQx64 y{EIczdU[_Mp<qN\/6nM'JwOI l.^7WL}*Dg
fN4l$Y mJŐ<nCRZ,4!@ޞ7k阴րe|BuF6+V*aZ C?u\r"p49XBdSь,յ2Ѹ3FX~e!eH1(򑲶#tɐ5N"ʫV:	KLV)'#EQ7@3 "Hp(Bz;ʸMNsh{k0<+Gy%70	f~3--?/9{UӐ)Pj`W)^g\rjN1SL\`0W$y"2{J D&1v78?8LM-,HĮcUjj5´yo(3G4ᰟzVZ7FC{eBk ;Qm }ߠhٔR]g\DD1&?7SCdIG?\'o/_?>9}56}Z)'fy.oYCx~vLxK,hOBf^UaeX%uo2AxܣνzLY_}
-6CWfc#%,a"2SH pC1"֪&46NJ]$09J|GHLnMO璄߼iǋX)J0֙\AvQ 	:!,i5AlcF>zFx(jB&1}{PTkJ8alz3LYjXuj~fq?<|sqCaEO.>]HkZ
Zf#YD C̤0>Q+.̧ؑ+*
I&"°һ:\X)B aVqq}~WXb^SxE^$K}*p3sGGLM~v(4YyMx~NNukUm%MHPZǧլEmm[%|uFC DץSFkBg0ilJDYrgFFsY6eIbVJ S_+R}b'$
,un+)R?ԔJ8o4xi%k<yZ& 	O[QµDtp
:|
bX85lX=_Gcz=7}͢cxͼ3=iq%(fRy:T9H?y3F $Ghǎ]i?r@a^l	?N#<Ёford%.u+\'3J[1"Uz
EoK$R]=s1R.3jm? #]JWEX.8|۹T-mǄ
dr.x&Vgc&}k|RaC7?ngFe":He`	T[mSMU3h0@xnnӃr$e}9|P?ZwfXLtΞ4I;g"Ғ_SnU$p|i
2#
"O(DQ
3 NpGFsOLB G6iגt:=^6N%iAZ^3%	eд`*tؓ"Ȟ)"K{}+^fYs}d\Jɖq\u)c+"sϳr6@U%T(UҚс˯R&a  M{G''kJVr)t4.ez->#5lgv@0x|9<|l0eK PMö i23ArWKNW?p$Gd.7T|(@6L֞=xJmG+]N~~!
7yzk2ѰO^\BN&[yY#6~#JX67vKnReYEYDҲqI.a|K
y̽ҔLj`-ɚܫ]PF>z9I<W
/ {aO=ERyS2(ϷKgԗTPESv"/`":T4ɫ౬㑧ހ6DF|Ȗg6pO4U%l,x;Ds=<b:5L	fۉ-'KzXMo3QqփJLŊ%
xأ8'=^|v[`N(5W }ŽF>`bEu4JgG<e<'),Wԥ6^T~:M9eKUo1_5ύyL|'$6B1ziPh3e
#;Id,[tYwwO-"37$̠oʴ_P߷]~3ߗ궛8}:?IA}}8==><?:<]S:UN.	;ͱ𺸷4;DU+>ZϷdj[)UVUrWb%	 `xHiH?K'`>^}xvxq+%`o^E\(
"#٨c˛ۑ TLʈ}s9W26uTɊeIa%Rh^c,swXϯfJ֯ѰKHl9CdexLj>U-RJgIkJEJ
ȶA;ۄWMŞMY.aJM]^U^#p#{pFiO"D8e}~zfX8fUj{#2 6azb5>T,Z[QBs"te밚i:xS)QBIl@K<3
D4w4V$h<Zk9h
DUCPYfp!ƅ<PʣhS
T`'V C85]?Q}1ͪԔ`>nP
;C	!QSOUTg^̩4
Srr$(ṫ5BWՅh8Lϫ#1G(})=3 |/pK,Y)U1U+xP~@ƥiAuJ8F`nӔ9bWVCF2zXE"ۨH@;#T1]n	e9㽬y'<5g+w8y:.%)z3y*J*)c+<$WhBE0=ec	N^2=g_*N/yF% Ta
gM0ZrS\*&$.Pt~^A+K,a4ə:WV27亨uYbIR;Bht+8}DfWIĆe.uwvY[<0/=SXe6,5_7/6wpfwgseOD+7uL/K@6 p팲6RT@u `T34C#ht o`LMg9O> ãz|a2ypgԅ~#GV/t&ևq96wJRCCݷd/Bd!(e$zt(hPDlGDWZ7"'`TXH(S[yua,<DcĮN8;Ao@gHGkDnww{/Ѽ}pcxQ/wY*>Vo g5%0V~67Ul<I˨˰o*S=G:Z3,2ނI"}YSlM*j:S_
s:>!rI(LM;YdBǕVv
Rx
Ŝ'1b[@
)VZFsS92y-,4FDe&w/q~ݗ6_6%\CUC*áB~aawDE4tի
4qZ٭/3ǄX~uWfL3^g*h2\ɉ!M\C^dBg)&!wPT:9Ƌ^~r1M|
NuS(ȭqP*t	n0ԢAC/0/!ŗq֒ӶH%@֩l56£QyZlKO$-70ԔSh62LZi3~*)2 >})?LZejLQr"0Q7u6,ni
Egʗ{;n.n|4]Ĵ_8!6c/NUD
zHKE')YZq8/٣8ȾH9UƠa:26`wȵAlRD.GŨK<	.	7kD*#i9&xUX莘SjO&4[ݭfoliX`{±C&q1kJ♌p
9$Q~_uXX(h[Z?HSJU*9*̒UqCnI?<'P8^
V$F`%^UJbCxNř"xҵs!fX>D\FQa)o5ְĩ/9dA΄6K8n<M\xLy]
Iep쓉pnt.]fgE.
ۢ'PoN?^+9vL+YXSΘŗ*c{z,_pcQ[8؉]}X{:dN|J25fcP!m3.Ӗ^2D׀
5vZ_v[2zZ\ZCFO&~2FbYĹsi> \KWMn#dd/ﺟ۫c@O;_]^L	DzRJ	WMS	p&X& Fxݫ8=̕qϚ%/#3I#Qf-G$gm2R 2s`.0z)Nv80Oߟ1k<piN}ܰe/vk{;>)mRMw'qKȍS}`nG48P*7$$H9mPϻ0b7!2eF?O6x8unwtDڵ)AqC?x2;eڵQMg+	璓^N/Hؤnð2\O)  '@r!zl+ZCwߗU6"ď௽ە+קaxOǩt'!2:h;mueHV*ФߡKDKkw !oV$$t7ҾDycUX:CDn+2vuri]2OPQrfxvKë&=k&
FiO jNaQk.S^yǄ]:v"(iI1zSYT?:JTrKI.--[tY>M~ʕӼ})ui܈ݨ	H{Ef)rp}La
JSMպ.//-d}9яQ~nNΤV6/}Xy&~]uM0t}xs kt+:ٙ40h)4rD* +%^miI=j	fE~5
if,ka	 !a_QSVڲlפ(ثIRyO<s50.cqMЌj|4:WfPyR'{53c$?Cbők~jծwr,/1v7Ȍ$if\WI£`6QPK(f?A\BK^ qlj||21e4cY'	h{4rd0Kt3)-Vȃ<ZMiͻn[wM;iQMTtb,
0U)F"d4/g1_u(M_Rb8EKN6Z۱Ҙj(-_Y@k'zֲv9~ӂ$W-JN/b?&l55`=% P,!W2:"M g"<1,ѼD#AI0+Ǫ:hQ1>dZp+!CT\}4	`%a*}ڔI}&<M̅#Pp@
j$Bȯߺߴ~&U#s%UpkY
O=t$ey볣v|FrLQ/CIph"/t:qaʮtm1&-"&tJ9{!sLv̂݊/L>'n%>TjPp~";>}{rz|.7*sA#}]N/q"4
Iiu
lKae'd3Xǡ
 1Z*@8P 7q䇃6F*~ؑmQc⡾+jyBE!q&c3L`|ZLqJC\^ZHkDLAIs9xU3B\%܋(
+}%O6oKZ~I1Aw\ʚ̘'%kb7LC3/ƌDp`Ywo
9*RA}4 Kih/g4߫;RfţAWh)c)R]..$r:E9B^0֧Xy1DGLҸ:UGY-?5sR qD;s$իx*$rD`mFT"A(%ruc'SzެhRG.~\?x.׍da`ٿE"~@p?ju]T@A q؞cU*̠	b!<(wf<zz<u^QXZoS<!6q'}f0ZO%rNoFU>Zgi VZ'P 0x3!Y)C 2Hrvb1,%B
<ʼJvq1˵\SVzp{E Z%jzD@2_A)D|#rg
dHPGLPKEwD	G,*+Ox'ffN:S
G&^}
nI7cзdi&j4oE5SZj;2xm;dj!Q SΣ!Ц|\-w_"not5]x2v	J'R>zzbRXW!AN-rnͮvB@b<Ejq-,[tmle-iY$U:VCzIx+_-+UrnPTZU5}z؛J?2[Ne(\,5R:|Fv `(W#>G> ,PW nwk"(m8X>DUy=c#0|!j+v.u!Z}CjDo&ş/(N"\GCUݩ-̈́^$("72l,$kҌWqt-}k[}:O/+8pk| B0.n<͜8N$V`ڴXx
h8Բz>/Wc~z3bh-Ѩ'Y~LULDa'0,cDE{jfBFtߨi*I Xmh~,7Nu[jKPYh%0
Eڢa2gPB;H/`eZ
`$4+}Hkz/!83%0>sy8υa%5-	B"am3壆g/ 4{kOJ@$ãMTX,}NecTD4*2Q ie"qtEuEpǙtװ1z5atqR(M2efPNC0LoyJ#c)
OuIʤ֭8n
vD.A)'tKFL-_Y]uCԦ/<<ozo	

$vTFA
Pt82
 +/
b2%})uGPh_hgXE<7 v@ʄcblsskgz@r'	,xDr81?z( 0"âƓT@RV)N,:Ya(?oK\@;;mpR\,X%<
.GU"b>J
/|aQDvRT'XcJjM܀(`Œ67 9՟0^5gBn'L̚UE r3y=;mbk>o[}6}I`̣Q&sR>-ЎO#hWORTMq'C~
!xׇ$ڡ82'EB/q
.Nn0׺'ѬĆ%
佚Դ
wh@0ߔ;w+5۩TIxKW
l-k%7/Aj(.@gV1}0 5*녱~ډ4A".l
$9bDMb.NVa-
8v3ڀa1$@gg7/MBK(nǚ׋"ώK5cep$bHnafE)PݝOSFK(K+_U6_Q
6YJA3ĬU]SLu[Ъo!V4_Ĺ]K/tfG`G{+<m0l	e-mTC1<>Na]TdEVogi2{n|U}4LFjPK:W˓Vff 0
%$
]	
U֧Oˑ;sI+Uju LߴuX$qhs"Xd־
< Wh8Y ?,ҺvX?m|7)9B^[ɖ
aQX,v*d*`
]WX)=@%QRuqT[?
*SDDeo|twEEͅSFkI?d5FOl*-9m7}+9r=/vc4RAGO:wFj\" ri THx˾G #2Q1ǱpCMBQ-iV+
m&A}Oo*IϏo׋C1z,XolUS)̳.1런1Sq~'KL[3,
݇\d o+m'еź?KCƛr
#Q	h4ϣIŤI7
B8L%T>h<*z<ޏ8Np\M&1N f|AjjjuO[<_".u1粖óQح }us5O%T3AD#-'|jx$Sf."Ve,GQ'׼7H-$1ؐ*F*:o;m\QU>%N*V*,wJ&nB'1)!~d*}7UJmǖȻpէv>(k=BMqH	_%~	xtx&US
TڗS騆ݐ҃ˠ0)gsMZ)bI3ۆA7dxc+Ҟ"܏K;:T}-xϮ\/U=:uwGr=6a{d'!2t-ٕg1c8vc}VȀN(v+RheSNWhs\b yE}o&/j=/r
YZAro_PuYSZ}!W+JX"}M]v2	)Rvb'4}]tqLB:#'
$"<O{W$9BA麺=D@+t˨n<-aS9zp"h>'mo<6DicoP%wC/a*L?uILxt7׍"أ!$G]q6>@XRJsU~sf
u:p0F:7~4XuUl'DO%V0mu`ý4GXc
+YO)ж*Ab*:*5/yuQuQO4v_Oa^dܶB
Wgy}]p5`
h5}\8ţgf{~^?B)]Z#<&oA-
;bI&
-b3<Ѵ^f{hI+2q4ޑdCU|V+%__PL%͹"A!REU+w$c'F\F6\`\ްtnI껊"p2-]=ERrqIXp;<ڋ۰٫|(vDAHn`iv SC+uJme9TO7m_lc#|oӇpJa/B}^x *$GBK`]WLs`!Yf@=5яb*|rsi"}ފQ.FAYXQAH3	l}^$E?W雎: m,X:jbT+W͟U8`,#wUĴZ.1٠MiAe
̇I=u_ EclTHxw'/
[YF+P9l&IQO' ]æ7T7N@uW_]QPkA`Ȋ<bzPugJ\3eI-&*Y@@]ϟ+Rx3
0SҚ1kHdcS Y+Z.ISEI1ٙ$vND#B6Qג2kjBs3r٢pL)̯rP+LT)KVkZn7(aҿlx\FSbyvѩqIN8)(Sh6'f

 Iێ\+GSRBdyA7"cJɴyNL氇&~&$#L<qx`;
yoLý60P
iIO<weo.ON/ZfXpgDjkS-1:"Є8(=@.'R{)$Pz-m=
Cyoۿ8}_,X˩p#!u+)6ZsGe7zlܖ!9@#ކkEAXfV9W<Gs.|NbVC:AbThB)HS]*I^C/A:`ҩ>ޑvn(!u`gi*d'[EȝM+#yozRS75%Q))`FMb~ku}l?|vS:MUz9}RXŔjr<jx=X {GNF>un )2w%p1aXg] -sB$-øĘMS~ŴH7JfJL
ܰ=6yHH%;%{q]O@͑a&!:1ҘP(
g@NDuϤtR>s4)Ņ~X|L 1+q|%rXHÖFRh_#~i._zVAd·nA^ggӞ|X'(fY<sd%%gHa rg5$p4Sgi-A4ҕVխt[iX{ϗ044	Hy}	;_zs DXPX=+E8s~5ͳ?y6ʙ.5SvOHN'&<az[kyRNZfn:Ne[...)~Cp4l5~zA15,uX|860
NFÛgQQ0=uyY\#1\K톬CuN:O	Js"+| _g0Fx:?KBkC$8 IMB3.FzKLA)	%W-Tg}9V*wӁ@SSga@f@9M(^ҀvICjhL%_3Q4e E{Tf
.!J[ja`$ݻŮqT!b	zytΰ9+lFZrzr$Y8IX<Ő͕XFev)5r/z	X%?0$M*WB-iE{˰Fa؇`;hYh?N!) -I _wc~_FM-T9T<IVlX@ivI9Z!<dk2rq^+3*P|~{(p
9{?&{XX~acA4)It\پmXuG9HmJo JA7]̤l:lوu̦o#높ƞUc؈.󐡇o^SR73a@F	n?h<UTCt1DQL5&CMdggG hXB{J_wԑ82vx"$
S~rwcxGW`x]0;K@]S-iUɡT0#	^Xi2WXx??<$x6GcnuI+@@61B]FuNa6lMϧE  	$YEL[Hvl $" )ޥ/b}W/1 ee{)<0p2~n0Ū,-'I\gY<6`t}8Y;^dS,/{ŋ(ЊE:WA^EfX$iW%+y29 B֝ÃS>r2Ml|pM?k͊+.{nuۮh{HuI=ZRmtmXz*ŀOkDώmOf7weA?π!誫ዷx%{Bʷr\8ȍ{eQg֔3}
ϙ֞tFř9OD
0(n4BD~ڟO:Է.=.x~JrCFl_Xظ~C'S*X;Gn#'EW;}zK"6FPs{@sS./rQ?$S&9;2 _IMԅ_?\]%,wȜ}ZL,zLT-8{w\$.#f/Ǆj]DĭuWW-tP\2]ilP7uӨ҅KˢcIqђt.ojiŷTlT-A@(7͸_^e("k	>^d%$vti=JBATŬnY,sUo_&@g-C7+".vOKA+uQ:bwniwo-Mh
Ss*#E)_E\0)x04S#@ۡw
/P1ǶFGA19QlFJ+udL
CA٩ŗ.tDfJ(bhv|Ϲ)]|tQ
;b]B
)7RQaOp{O aK)onJu/
6wAI!M"k%7*U΅pQ6,TSr]`,k|R멑<ARy&3Z]^.*Hu_4W	B7#T5f65ڍgw.f}*i.Y uI]3A-_sٺ^vSq16b*P9FSrU㯔L	|rBEUs^W܇X>ۃYd5Aβ5T\B	
b
)@(=]}vg
$ѮZ@\e3w7iG#K"a!
5T̡<pp鍰7@BPzo8J!FO^'0ko%_к2QŎL½tT#XMm4%=0IoʸxYsD//ϋM1NLX IqG,_	φU:Q4vl5.{bg-9Fϛ ZXVcxITԌt2&8<G7I"X%7[(*ޠ%I%wugI?ڋ\C EJI\*ExE%D2uXMyCWٷS#Ae1~UC+X\/DYBfiZټ$ C\uFTcRP_G*2'o!KI*0C
;(NΗPFOI|ｹxGrȻ= 4{I;xuʍ
Nqqsi,ufn_nVۗ<>:o\ 
tkLeӟt	.]OЉO7rQ''w^ݟԊKPV<Xϊ5q ۮPIa~g.^c-U,zck@0Ph!ӣ%O{DٗpY#XN6k֪+DBAc26iԽB dGv*I>n8*pNuVqmʣϢyKk
^ظ;
 :bjۤ7f{Hz-#vn"(oc$gX4':7)-9	`1ܲ ͛}n#b=V4/
2-]]VVB#z)4wuO@p$9Da'R/|}-=+$TdY< Niwde(sǖ<C unڛuXsSp]ޞoئx6Ս%D @-G/6-Mm-|b9nY9[Jt<'ҿ!#6'GzZX=Iwv^s*R1ra#:;eч[]ոd/L,-
TRHtcX3
#@}NP+3AE="T6{sts{4ܹ=FG/5Z>Eo)`#6.xIQ[I$_Fg}Ƥ}STfZ899	rbՁeYӒ7_е|wV7O8FnEC%Uc!ᾗ;n`1,xRfUʶx89FַUڮW#x"wm*d(bOST7EuU	[ )o.&
)^jweh s>/D11,Ua(ʪrsQ
U-EIQQ{=Q0ݞ5؉	R2>ɸTSin'fFt3?B]B@?eiqx)qrW^{)ٵZj7T\IVן&qhR4B~zL_bV^K!lCԊgLw#yL%vjˎW_WeЖ]zuthrpwW9@YĢo
+5Y_^&h?Cr0L
u\MԻ'K&Uk.["5g
Sp}S^.gy}i2ċ	!&	-XϢZ8'HRQ
|.@=$cV:W2U+ |L>b=1С "+\Y]`O16+Ŗiv/6Ќ6 ѻ@9ᶉ{~d3`{'&[pgruMEѭ
 5TݝCf?ȶ8$<$h&i&#+b*JQ'
g)ܿFHwAdOAGq1WM/bLtru=T*V}T9O_>Sx~rY+)ю1Sy82k\biYPF	ӘȎٮٮq!{z$;5'^/$8Vt*F7vIXtgx=)q_.#H+THԗ>RW:#uµGvu|2>1+8|nɆVDȻVSm=lK9TJǫ#DS+z`^j"67vo6KdQ5uzAXJЊMaH\epw.<P	L05 V"ַXԆ<hÔapQC>.gZLsL."*]6S`L]*#f¬R]7[;h$ո _3vN<N\K9NdD+Umέbg2]`yr٥6WkUX^GXK$ |0E<><?r@1EG')Ծ_ m7XZSU1<pzs~Kċf;&QyL_?RjCt߃ME] 7??>=_
D~XU g4%.liѻZ# KS=#JSp8h,R&BjzHԒ{?ݼ;~܈8x pkVE"ӰF+87jŧvÜauQ;VC=rV`ƅM+DGъK\Lք&MH5ݺfӯ?:<J0/woWί]Tq<MeW􏝣؎龵5Y}i7W;8d[jiuloR3讉yB3UܻXjIK_iLЕ8)#6jJ_'&bVs^A,"M~STG0bR<#ݻI֖2q%DCxESQ;(""~m@p?`@ߢc+
h7$ ^Of*R9FrŌ"[nˠOnkb&0C∜IX:r2m>۬ao,Hdr.oU.G`-\;HjIz_>{?DK!<W7ri!ncFKkdqio8keAj+27_}HLgbY6ԁrv}v7߼x~u%PR6L:bL[I]̈Oܖu&	VF"ʪʅ?C2^,DHq{{OU YA?q#@fԦSKk`KMKwh	ǅE0r
`j=(=ixaqrުt'Gܗ3eOPj׆znE]ށjZ}Mcʼ.Zں'X7Y)>rm#Z87.-8ZZa>NB4K|\j\[eZl]A%.^k=\a^{nVx$rj高s/ϩ-G^j\p㟄\wljb2bu37=Ƭii=k/Rc"7JxDWs*@E~b
"FG>3ͽAbu6IhYMhs6{FoHk&(ʀH)P
"6	Z(hÓEkI	lpP;"7ȯΟ)2NfQ+bwdߕ)_0|{-_GP8\ !w)2G	ޑxȑ,}RbI$RY' 1Jc494|8pE|Vpz^Pj#Cl'nryO../oaJĝ筃78_8ŉK{4ݎ=(eZN@#4ꦖR~P-ooXhwmTp:&.{HY2DQ/elZyh@͡)/(r7㉤gwVGYFvϨ^/<L-dL*73,7:AeŬGF[B;h%.4PDyf@jbDVIfwns^=}dԭv{ٿœyEk6й+nX*uG.VBK^rxm~!VSlbeHebr0*6eYX).z!
͈vEw}T v`ɄpI'[vRT$kQ=/z?(4a)
2O+2Nb85d*:N32uu=>A_I!qE,@8b+AdIa L]`OԠtxo_]n]ENl (0`783*$gn{@&eծH4e:V>1JU5Q7"sϠg\xBD^ZpD.6R	H||\
*9c1&:zyT]7ήn1 tӻ+GDwe9*r 8ݩj2n8Irtzq)ժd5X!4R<_@ݗfwՂU~GUBV2GTAGd$Vyc	η C_%:i;(uB#}-veN;]QwU:2~WYh+c֖Y2+WTxΏLKIsKo4{Fn,\dF.j/#uqQ\2Ed8$(hsȫ|'Icoa:vkwX3jl#݈r-I
U-=q'Zd*	ݎPSAYU

BW!#dsϫ	[m4`pg4ĳ;<Gi$.a-wȜ1BB2m2=UL˻|]{:jcnW]JE=A"&x5#ψ;z=_x5xSJ_GW4}k$mw 75:(j1R#6q;NJ +XF'wz}xQ'+	mvzwa}|,~߳{Җn"Ei)L1iXDm
A3}e=1f{Z.7Rv`7ץ^uiB>Ǎ
~Z7TO˼`8ŕ{	zV]ut b @gۀD-ƨ@"&ڄxwtc|ĺ򝖦ɪ6G&/49AJȷqUqUjYuBgCއƑt"kcp')
QfS
!7)R
'ʛJ#LHJIɷEDI/&Yx$?#G>)wsb)qHHHKkH
cy?h
E!Rq6MD-B
OgJ;f;<MPt0&9PH^xߕgܥI#ʵ29{l9=	Ug-Y"ZUU]Ӿ͙ǯ*cWW~Qp,Qy>J9/	dsX
l&H_DV5Zz뒸a'ϯLPf|Mw{>{
nGuNS<4R?햴ٖ߯G+).jog:L[҇\ׅ^7% z:D>$oy1,?Cc\/*~3t"r*n\T[x蟜>=#n4~iuE\7&aDt\M	4j.*wdT鈫y׷'}1p+<
 ~81eFK<sKt0c)WǸTĮZX~}1.5!ɗih5R/[NL`Ms-oYc1ٴq,R Kb[%:ꨫ K̹@Gn1kK!ݫ>uI(N@CON&JOU3Qu
0	k؂/xY"fM=}7Դ!lU%U
ۍitefG@x#5t<ˋD=_(8nھk
Y${&F Bu} \^Ks)j1@/1DOyCTy
*
jl㳬'bm8pb*z8|Dd4[SNإL;1DŽcsNMTC6*^X^ARX;eEno{p!",Pp[|jeP-E:+#a_}G8J&z6׺/r"bR-d+۲K~&IJT3-RT*V(&"hM!uNP@º5gfC;1Z3eZ&.;;ڜXƬڞVSW6 VEnɢ)c}S=m[hLmРwZ^R}+yTUgIwPZO$OTE4VXfj8hmDPweMSNI(!V\(UL4dmFGc&V Q@uV)<gxVY0{{
nPrT=$N?\۔hgik`O[l`5
@>(!dJGWE(FAVK4We$WID`&5"=#qw{
`>X%tQ\Wƛٺ8Uw<@gq(ϵ} 3xc,)O[⭰57BJAN1'c{<H,;XdRC~!hT $mz/QrU/&]e.]*wb?lKSoXu_3ED<)aZ\NV^;ALMұ*$j?~\HLƫ
HˋPGyyBuCZ+I'I#Ӑ}
EW+?`maxO6(pLtm.ȏӺlm~vaxޝ^mnjv(
k'l |g~1ZLV^'Gۛ҈JؔmLӃU:
icb,Ry"sRZ
Q."5Js
P_o8T/]<
'^
Y"΁SIz
Rw O[l1J3'Y)3u*d]/!`7 DuKVU	:o9_ݽ+3_zk.AIU$#2%^2<u}GB38AG}h'm?	cqDA%G~m}ۻdnhoDg<3ys]l0}t/q)5ChvOEN7$UHmܚ.H;w-h*]a1J!^yMz/VD$r[,|NO_{AtLn+3}ۿ,w%FOx<85b~9y^t{oߊӷ.	hy04ty	j%M(,Kt=deo{ߞ"jIJn|3M3f`oZ_	7aʹnmZVn4ď)lS2VI2dG\oUI⅁,%E#Eo73ǰQc- u8ElWuIv\oNɤmpfщZ}>~ ~T)Z-\D{A'"X 46⤗ӠlvY@W㩯] 18q`~+Re$^"+gv~oe>wիΑuO\bv+mirkz=,3vxʹL5~}e~2ok`ֺqZ<kg櫹OnJ0OM˻&bEP5EK!?ˑCcp>5lvN;{U^i-dcmnQ\
i<E=v%#ь%X4_@[v`$Ϙkqhb
	S-Jw}Y)Km kM#c6ʞx/
MdXnlȟ&T'Π;u(#1,.1:|Alf;n2{w)nUdNk"z5-
;7)wu85=XCWm_KgZ<y]܎6NCsW(Ӟsp d!6}FB_)$kuR)kʻOg۸.@ߟ6
&/;WV"wo;dѣ=e{h	T$:[Ψ|cV1<U]Ѫ}xg
f"#2qC{RD$|y7--W-hW[ 7XK:vdުCFp$[K(TCɬ?ؤur7Е{{}!nTɿ4!jRo1hy.RsjT.y5.U-16ګ˳͇wg%mֻi9L0sw o*$Iu#N>%u-N[M|v|*
UMSaHhIԡRfWoK\zY4d&gv-*._ʅn1 s1QŹЦlm{0B<`0j6]!{C_ad#6(LU(^k)7qR.@qR
bDMst dCK#e^,kGãavVE.REz[=vpIB:ǂ~jWq5Dm7&wgV AB\k噢n}gB/=T|MGbB>"WES͋G
ʞ1"WLaO_JnWse7Y3JhejtL{'Wj
b"*uڏW51>$"	
8ֲgO,!:[BY8^ݡź]V!)+io[q
8k68e0]+2F?-foRu"JkDƵu]_WI᠛9<pį7>:H%+"y5gB\6f-řat}hVBu?!zF)*pIW+03Ŵ7*_B1K]U6'DKEbbjެ_IOLc_J},f%~>ȊNL:{̨N"8?U
Ⱦלד4Tn.b>	shBқ=sJYW(OC5[bo蠉4s_z wf0g]^s,7g"_;Fm/x}AS"-rU
٨jO5USnz,~Ȗ>^q_CG\Lʄ`)}}Y> m5~rs!QQxy4
Ӹ͕T4BsX>CcZ3D9c/]G\+ߗ;&ooz/wv̭7[¼4OV^F8dMӨ5g Lhh'bw^
6b EDt$bGCBPJC'Ѻ&τ+4~ENYb[hb_g@6d]q5T'(lh$t|տ?+B孮1DrQFǍjmc2b/`eXK^O숴xmS_y/A̷SĮ3SW]=ֈV/^WU6Q뭦-R~H6ڃD}&nZ UMtpI&fP}w&7Yo*Hظ|`$uBjB$G-[=ZBV8fc0,8^Kɰxzw~iϛZAI*x+'ј||n!
c*(S}x1ӬhwZ/u5J;¢EUytR!Qcs5[HK4sGy m>8bqf\CѩSwK?0\KU}8_f	^<!9[fb}PM#Zϲog((&VxRăI(V*WWZsX4Eݻ<ٳm}ќZaBU'R oMU8DC
ɑp҅#x1rC`
XK9RT}'b8jdu".VϥRECOT:KLm_6/dT (Orc ]tK!G0
lѕؾReQјOyqLvFEf޴:*Z:4O+y7|f3,G#ZN r{ꊋ.O"-{nCH.we!τ%["BQǱ};NW?FQx,z{u^)Jaj%Hݧty`e"<\*tSUӺ:7*(S^YxA].Eduafs]E4ꃪ0
CXou&Rnۆc?Ț	^u!`4*/gzX{(hU&y
ofJVbWhB)3Uٱ
NiFXP
bO7u%CpݵG3:B0l
;E7.X![d'Mv/7WSˡZR*xXa-1A!CH:H@߷Z|yj4
G(e[CuK7F_󕛂^Sv|lƪEsÒOcjܾz֖*t(
F՚Ld-xP委nƾfA>jxKUښm(w7韖KF!=5 !T=9ٻOO.Qp..dnXVMO`i*EY9nN[5Y#r6wHe؍x o^COhĈK;/bI7^\
SBc>} ׄ@jh)p~mjՇUA+[y^N`s::nSz M+tF½JtTQoœ.A)%C:\Xoph4PN/ߕ7ȭVeKo%}o{}w$2/:
rU.X%¹ۉΑ\;-m9j',VX8,Bb0l 
/-W+OpY%\Pj
K/ZQ29e?)$gӛ{VHڟ_vŅps "ejXzXi2$b7J;8#\8>bujֺF*&\Sؽ5,ݼoyX {5w]L=~ ZIslNľejȼ\װO#LAp_׺nIP60m0pfz\)۲t6) а 
vkι"EiҲfl*bG-e%a\?&˧&
SԊ?v
[
?i#}m9TQƐp16Xua?Ktf*i	k*MPqK[|5#.9|YߥjzD
'Ji<!uQDvRuUNZ'lF=Dr4sĶ̹*Z5]8HT]GlajB.ct lfHkED`Ehƌ^,gQ?xtj6Na^P=Vu.3|J̉ªb#s;׸A2xb+GI|FS}:_,'q,0I{Pebz<+tK>n*('X*Y'dz0RSm,cxd$14M{rlXNq|~I\? SOD7	ŕ[V
d,+WGZNYѥxu˝_wA`ŏ"[J)D*J4XeQjEL9=+Ba?6*e#̴C!%hsҲ;7}y(e*~|PhRRohHA?JȤ^;ʦ]Fc1ړ&WnנGeCpT`_3[RYwӎCIz iƪWgYQz
&?vŬ,J
 ֜f%S4,dR͉ J$v|W3u¶hT5ݧ3U
গS)V[^Nzk~,1Cѭespl@*ʼPU2gNF1ġx8*P èL+X捅m@jQڝ0o}cK))YV
vӾQ#~D02Cz<c	&5s	<'4${)/eX
{E_+yS?mw0"e]\_Rq=rۣQsFK JD?e0'c:Jߖםj@"
G˒ݾ+\F	vE:,p!՜XRY 'w.D
98\`$Fk%V:SL?9Q7td\ڋ#I/MHňi tO8]\a	c,V]6ֿ^_Lʁt[\V]@
"B.Rx[je!r2|O.e
<fޛԭm3<.
tU_&MVqg?C͊ph#-[<np[;巽9^9թ< 4j6 ~@qRpδ#wGr9c>vv?y\ZuKt1v%jcc	${ߑDǥya<Βuìsvw>{rK^+"5y|1ȣyrr)U -okg%jP.˲L~\]N..0J6-	gaHhxJFsR-x7&g)
x)\2+j2Tdnfýb~
WDSbnߣnL}.-MtǄ
Nd_TIe}b5YZZri`0W?s'P3윷+$|݈-MY&XHE3>w0_EÏx_EsMm ]j CK;#\BAF},}~Q e~bjHJ4ώjjb5@؏oqqqڕ훦HhrTbQǎYkŚ[	4DAK$Q5j^/M0wyu$7'wO^ƾDz
]v_dӎw,Q1֯c=0R.-gGB]4x.b$Op
5	GYk/>!1;"dN"@뭐UE#޻I|Wz[U
k^ǎz|JyAB{4@w}zr&[.):vi P-bɖytF|*'Ǟ^$*c) RjHq	xtܴ6a#7$Cew>mUux5Pbiټ~\W=s);l?e"(k{R4W;Ik&Yot:Cxjiaq~<U3Іqe;@ԯݱNAEj@xAɌŲ.tPܖ"[]HM|3wc=Q5DG*UyN)Sefa-b
8JВ:(3X~p'+H˷[
(&G28m2R~C=
ABlQo|kdUu݋X`Tx:*r^r~DJ8e/
X0m(Ēf-"1fi&#KkQʒuv
PRl.@vEAٹO>2>dKs.[l:'@U&3key,X"[]mN`r/-<Ӧ,F"8.L\,^hCA,R?Y,4>C`&ڂZ/nNI6vXQT21jbrm>dMyZ]CᬛMU(_YjOU먺f"'*a! ;i`*||o~:?t|bݟ52;`Qh U Ii)	p܈,8{<⪱/l1)Vj0U0*P;8{l]ڍDՙ2+VMd8cE۰(#Aբ2\z_xQ4
,YQOc<[k[)pM]݋҈*Maeu7ay&>bya>b-'g	hSsZdĩ,S^la*8nU7[ 3^<b!I9'z'dc^3b
yA xin<׎UN"Ϧ夨WV9

6p>#`^5Zӈ pxl	)]=x
XKr'BE*#ŞX^FsFgz{Sz!9nIHv|E~'Q^>CX>
08SA<6*k mBr-i73oUvmZ!>kcR[8]~{nоk6$RmHsY>$3hM#2^_H4d"I1Yw߇o׈y9|H12_J L&j}J	@Z_ˡxN,2Opi[-uU̅bq鬚ٯWXWRyEcRiVYUV,JQ//
|ܗ<4B봖U>*Hx-x*ֳC{ WI*ܺE1,|"n@MDQ*ߧЖZ!Eן)st [)tFOX*JTYo@AJqt$%Ǎ9IAX^Zbv.x!I-ho[KEM=YĢEhUS3H+TcY2ނEl\7X7uN-w
WJ$NK;1rh/Oj'1kQ,0Xqg	/56I'|궐"!R.~/ή>Kaj&͑tn4-J̋ -VH}2oyȧʐtnb_sWslHV~S qS9v!y%Vgf
#ra{ψ,JOLEL}.(y+*
N2SE	⥛`F5ޝT%y2GU3aj닷S?{QHë :"	5 STZMfYX[Tx1/^蚡!NI<Sɇ޺9tu%yҶz0^H>Dw@{mnƸlr>%78+|P;5IW4Le @22dRsԉU#E'|3BBn&P}~Xi2&W;/^$+[+B/۔;N7E^,R6UAd {A R=𽬸{7D6x{?XVg	SaPVrBA½ƿcK?
)x$zL?cDs
sw8@"|@7c<7x2ѐ+7BzRƢ刺wj<
,&g܆>gS1`=V~z8yIa{g4!Х|\+(MQ]hXT pT 76urar=j=HyEiJE4b,\(?cVwQT0ֲ@F7HC
G>4^xˇOq{
2_a>0A3?zpr!Q=%.ILbmqQB]45xV:^o%hݲ{&H*`/YZUb>zY7'3Sp$;Ei,nGbtbh;<8v&}?6YO)EWj:0d!AYp!*T-fܝ `:)Dū9v5L+0]#2j*)cXta'iDK<*o30ls
u|qCsUj[*)_ s~""/Lk
!ȫk52vq]h[o~,_ǥXĳy.!Æz;.lܲQTEO++ڑ}NuGdJεde@a؍
 hZa]JN(<t*A=hs&s9.9xK̆$
1SUDe(JWj^t?0Ցh;t"MD&:tpV_[RCc,ʪr-TJ0ʞe(O'^ܑM32I-+o
E0{qUs*bT%d}׈;8aX}C<o)w@o@51ʹrty/MNb dv盡G`jnߜ0mO_:12GM5s[q1=sV6d>f}Y7YlׯcZ%p
ε$m
p:_VF'#pj/z}w.K{uzPV2S!^?EG*f^f뽂elKj@nzrJQ>vu9jk놭1'bȻΘ);A25e.Sv@փ0l5b$=44@
yݵHeEj'_'(t
lXE4~/UP!S{?ϞzL0<%(u#j#ܼ㇛:ApaM%B
ήM-bD临~PFMͽమu	TGJ^^"3z] wTK03t|BrI.jDSE@$D< VÓ;>e Cxyq^%:[EnDkNw?j ,*Z+@102$dMHUF
<E%ǻ&|[Bzǎ%jY'Nζ<8WmqZb*}oU{;Ten,:=@(T.ql0yMߏ1IgiSpȟ!ܤ݀u]}8pSݐpE}rCHF<,[ͤiiIKg뇏aME7j08j\&#IT'Ҷ{פb^8.`o(݄Rlb!ֽg uoig2Tl(yFY&VAn5p	bիu|8P5`[|QϧstɥӍV_7MS>ezvw @<J>68ySoYsqLJ"xZ|:ooiY#{P?2.9Q'Vx1{ v7alXS񁖙$&>lUqC5K1wӪ%-N*Jߨũz]u*pn1~3=BҟlO|`S_^K{SQC%v0.31vE1H)qs/.7k|n1+0ګi? ^_s@["+WĈN]v- G<Sw Cc=.DKZL\17J9Q/j(9&Ai]ZQmM*S?^JW=uPnf9iw ]
|H'Z[^TSfP?|(gBe5;rKOh`@(37@xA.BVx+n;x8k*j́lp	UbXQ`O,"75 Y5SQ<n
ʡc<d!ƛ[ԋ#Sm*1eC$6txV@hRݓ^U*ʅ9?E+i}A!KˤC>MfĜ{u[Cm5ȶFSXhn=6\#q
8FUtXVrZSJ*uH5ҳ$r9|VNuf֊tPXv֣׎Rs̜*kD

(9=ac9^hX+zvYcLF
buQlH	"ޡT4j"*#e_IVf讕FuS6=!:-QbD2Mr,e4ԧׁyrv_x( y(5s_kp۝gdb\<tywk2NR`vg|4duRW\+lx8{wu~YY3GsBXѦC&\F
OR15>ٸ*3YHru-rE&<nbb'nT~pX;v7Y,\e;e/ dVEs#CDV>Drgm>ͱUEYv!ѫmay5
l2 W._SrOL,hQ NhDZ==4#
J1F]&pBGnhYm^	zVvQ?va Xgw&
m0ucKy-iqn]wb_Ǵ@tl^n+,_`U=t6m(mr7nΌ*Ĺ_ʺ@.p;^?2
k2ϕq

#ecGh#dQwَ\SHQ%J%z5Mu:hNH{연5+UeGޚuU'/5l%сmڸ[>ĶoCg˦DrxG3rs`3:T<VY9ѲN·dkQ[az%/A«EMr
MZxjcXz"`V)36B5E_`3*wpM(匝
=0\ZjMݫ~Jz-xh>v<bj2G^G OBQXy":E-eKs0ZT+<˓Tk?t/@'Jhlk}s՟bU,V S*;6?xѬWWW2ˆ
RJ09xVf(xY.5
fqȝi
l`ߚ0EWr ^e5YpNtl8k si:-uN8nP
n]H4ku5!&8l"fR>0T#=#RgKwq1H錳k
2БXݝ.L3E?o5Kp
o`X'U솫8Xz)"{M%$Fm]'1?
NΚ+g9<@IiO?5
z(ormHYKnWb&?aG5Mo~¡m<Oז
4{yف?8pXX=/⮿Dwe^npf-]-٘W|#8k
kIJgU}vtJ7b`٭
qA)^$M>u%	N@]|ʀR
?hZq^SDAtuɵx*Lwx<chdPF D#,'+y*S-G!CBҮˆj#חS/0y\6W?}X(uprg0eRl`OȮWI"V,th پ)e>e8dupJv_!s!zi-
B:% Snufeq!좡(B )ж(1Iqpʭsb/$V6aܕ4_nUIqY\:OE͏5e
Hm(F+
p* -uKF>O ws'V^eb&ÄJ
S;WRs'9qmXl5xB|]kDBVTK4~g"@$cR_;fk3R8F@L,WJMtU["(R?Q}sYotM)7KQf\Sq&0tqQAh'װ,cD#;.x㕊[!WP$@IT^a͘zp
Ja KCNT%PʐނPxv܀	[x\JNW'2Hmm9vVuemn)Aכ$©uYu0w+aMNrG Qzg285Z=hU\YlܙߜwK{5Ûz~-J}ƴ0L1!{q9]3]=D,Lu[Zh{욋KxP!!$ZJIcK}H r,k/),Wnwlqv@BFu`U1b*obw</Yo"ADib|DƥtH6>8sOu첔\/+EMp-7/EEk)C
3dV*Y&Evr@hGVNc/6
1/'(0wjuDqC]e/!Hd?*:t%W݀VHű"\v	yb
G74mЏ;Y˥Wd	8ޥQΌ}h~6#%t+|d>A3;I꿖Yv_JRcH\} s=JmL- QN,7}Bg&Rw20RzՂ-FLkѢa i$mpsXyɣN솾4fyzqI`'_OW#XHޤe/̙W]u$]}&g`]d}$]gWѐoRMo@}1\ka]:/+3B؈(ffmdfؐ!!ʹ,PǥN+ļlpD~8KHƤA҅($go\/J^<~Yv*n.0yp8-iGEvsc~Sݳ^^bb/)#|nWyFb_~K!צ&Kw}n>fq<ܪ%xfN$m'Ȋs|NzR.wYe>,|U'c
(ӊ&[Sj4Dsw
Fr{oYs~]skMt{	xt/8m(ۺW'm*	*ueJLCn>Sl+<|.l5`@kpI~_4FYOe6ndgr	8$juw2+XAktq+̦Tg~iÌd_r5+Of&,a[A7.|e>񚿿}%qc4b̤U9e<^M%c|bxPa% 7绿һMg9SE;)}AW"tCMVRb KfXc|TMWOV>LѴ6c9ĽHң>t7k5 1`Hc(bX՘zϫ26a@qp`2,It{*4w*`oSvn̆{q1h܊,\F1y_|{a3ǘo@βa&MJE!,*8V3U%BLr^ZcVE(Hr(@F^ЩU6YGq&80xVwK]AIs֬n՚T.x=  )GLU$5zߎ*0mÝl7+5<u"mCʋp
1#1K?ʪ'7A:ӛ tVy 5vժ@
F^/|FO Wl	2h5g9 B)IquWCỚ߆T GnvXNyO(J̳wRꢖūU@6$*c1c\(hExo3olw1R "kͅ.z{;ԺypȵroO]VZX*hrj>V.Mqnʕr#	{6|^I<B;9xv'0F|`v0yBv*+T]kQ
)RA) 8]5SFۓ@ܽlYQo,\S.CN"rS`8Rq|蝁mUa[G\E8DI(jiQr8r<H@oYSUk?|}]UasBD4+'N)NEy[`"%U^e=%lUloB2"H.N,UŃ㌔Co./1<47Mxlϝ-h Tj3/<;>#R*NȂ J}Mbjx`|1$xHڭ:NElTiMrv1ڴo7\e.sb$qHXz1# CӲtqq<H&I}.\Fho`'I0.W\OΘBq̾1Ģfm
 L:'h6S,KV3:1UvD߰vR$Upc<˻U%mtbdZ
US4k3	xIyl֘V{<'K?E рG.s4L ^YkׯO_R5ר)oM
i4ˬ"V3]VY1H"+w
(B
,7t[?:%M_$zB
fJ{E;"lDϭFgF7#?&&Ԫ[$_>:'_d]䣥a2KSJ>o@2IjZ?&tZ~LE(SA!潸xW^T_9p{̠l0F2S7xGcܺCŘ_q
Ѓk8JMt_JS6uX6Bpkn{'NHonb/bBE/k&nV^(x!z܄~q.u鲃07@XWn%D$淪NT"kvnc4ȞyfX30~8?9xzBW߯mtמ	<e!rń9xG-HH\+jU$sjEAsb0)**ϙ	G}Qr4J+V^Cj]!,kM˻b{ʛ7Vq`T0Em
9:^7FY>\ K>L-7fYy
`J_2maPvJ|:6QNt؛bUlwj7Yn3zJJ3K+/`xGL]N%@xW	إ:BsX?
8`-Vi˷ nVTnEDb44+nط_G>ϯ78z_gt <yBʨ&A\'g`ƺ\*sΖ#,-f=ۈ	)` ]U4=xb'A֚yVCKw?}׺ө[mHߵ×d/1|շ_b6<[1}jÃ^Lw/ZPGx[;Foݯ9kqY_/|_
3xΒX վut^(|>6XwUe:	e\.<JR9c+@QjX	SR@'7m_UG7 N.Y6zU1쭕)J1ni[ȓ)SDZ`ƪpWp.ilvaS)eP
۔pXfRD	W4EjhimTԫ밝?}G=)B]KRjr E;A3@jtV.hrȶRIcWAQVT0E󻇠B:
C5}Y0//N;Ǒ}w:i3/.xX%
B:)/UIY7n<5]=eџgL\ xyyo{Uk&MK#JᢙS*@C)Ď]6G0.^vTU[TO>77* `ŲN  'DRpDd}NeKʼ Vc&X^dײ!{r絷~ݐ7f)9O_wb0(0?&a,Ŷ?U~6^x"I$lBXX9\~[Y>`y\f|/A6;J}[wk
T}I_ZM8ziJFDկ+niR.w!DDʒdM!%[vU˭2lÚX}ѳۋ#ȧ 8TR~%OLq-V5փ]eVnȴ@BshY
_Vޫ5~;U7i{'CU?Q!_fg+J)-+iҟȟ!(BevJ8o(0ҍMum}9~/=/Ve
>KJ>ibu\	j߳8rև	T!yܥ7ҍrei>,HZfc?;SxiX瞫iӹ6>?wRƐ,HŬY/<w?ۿͲ'7wvE>|6$\f%:
v>4_l*W	ogO`c+ۓ+jlwv0/o?q.G&.#[*eF r҅%RuG	L.$? I(5Yز2JsUC+zYn*}wv0꾂ky
VP Td/_MrY*WG E>so	Y#/*=#u<>%ǚ[~WPBb2_\.`|zǒ-FfkV8އaDV)8FwhLo
IJ>e%T_22|JבBh
FJ D-O[0'U4jc:(͸	̾ihuⰖz'irÐ~P4;GMܤf%/%Eσm)bz?r}yDD2ʠ"t/&2s6^k̲{&o ⪁|
ƹ/KDk}⼳͵HF%Pt7Nm`&}<eQ؊[ (^wpNTʂD^kP5ww1Tw$*
AT}UUu46O9E/v*J}NlWK!}irxc׷8]xcb}dq/
D.JR'WjV
,V<gS\~hѲk]*C=v\Ѥd@t3o?x_qԯDqxXFJrdjxO ܝ}YVW9lكiv/O[)!{a1g̍ڑ=r75[fkq.'Hx%ˍzt`rQU{z2 hJX︰;4.Do&Vv;MUϷrn-sOMőbl뙻/jCYd}O5mE3Ɗ	P+=KogMRGȼonI:t}=1u`+rUYw	Gܸ/C2*b-{#``Z(KemUs
+գ%0c4;c쨦:3_qYh=sO]c8 q.eg2'n;H{o@xVnzWK Y칍ʇj0l*LfS!ǍOꚯYnyUgԿ7V%yv	-&C'nvynF/3¤3AmXzdJIJs@CyD<[2T=OG=|@B!fw$|Gu5e8ae
k5t<sìS-ҭaԐ#KN'dFÒ"#g4gl:2sr`e6E^jȭ1THqR֖"HZ6  BydWJHڹ/:KSm
g>ڸŚ+exK)d,PF3	V< Q4W3*9͌\WH=>ɨ\Rʤ\L'3t].@.9`/6£2&܎eU%'Y.I7R^?'M N޽y}U%՚4%Qy$MtI3xBb#ȰպD-`!z&ަ&qs"ljڍxaMU܋X|.mj.$l  u[W]}JzA` eZjamC\Ć7S̚	3RŪ7Ǘ>;nJc)k}>k́/8UWVs(φ^m
9MqԸ[k[uyYhƤjg0@"'TVT>I+90UDHV`&d"s~;`f2.Dj<+jT̲ݾןQɮ(ޢ$lKMɧ"gQrTʒ4Խ٧anP8pA[_݂7bOU9)#r 4] &j$@;WE	.`s.]{!BDv0#"OGՒuWn[=ؑw?*푀h]-KbJww=8ٙ*Jx'oj #ow;iQTݛQ$պ᪊:3&/GK
K
'-[ѺFꚖz@+.`:|h$nY6^{ak9~6A x]<dQ%HQʍ݃.?ه1\N0Mt3\:grF=9B%ݤ6^v,|vm4a	ʦG2'B.h	"p	LOgFXO+oi#WOrA(Kؽ@eX:Ě]GҴb}sXq"$}Sc4sY7a]ǄϝfV:tsq!ª,t£f 76{(PKo*m~;Or*l`f<5T>:9;K҅B(I^~,M.HN9ʘ!cTʒX5hh']mȤ
eFZhA2@Rx:&۵)e5
Pc({	ҍޯ<-AQMZQ+4=IE_*umڔBe=vmx`6߮"q	{VFSBLOV㿯O(uKEq?M]ƍ}EvG"o=ZϪ@Cٽ64k~W~(|?^+;4o:>Ϝ[nT[q
n6ayWCkORe][[Zj'2AXtpD;<PwIwD({K"j~E7ýhh\^b#JYMGaoZBM_G!d:	ydm_$b5M[)cW0c{A\nKVafuzGvjVzURѡVeuh]FK8!y]fED6cWy:ĄH{jhOOq
f06¾XIsXύCThG+½[ASqܴhf
t%<nz{_″7.[Mm9~aH9:hT;e.|2ZgQtl e@4CJ\of	b̏7_m:$
Nu|X:!g^~wwߺe=V{wvJو *|%΀1^7-r4O5P:,BڦDKQ$)L%ޗn]6ǂnx\UEX58^e=*es{>u;\&*R"35Ef1=VFg6~\xW2Џ_ݛ}quV/y$ ]P|]j.YD`,S!ZvOslwjy4Z¡}sLH2Âҧ!a$-(r	wǄ.r>&edˊ׻jVqv:]NruU}&m4 .VN+,Vz.fz^4-\p)wpy3{Skem.t[ ;I⫧h62$W{Rhm,墰g5({iRba*WC џB&b 4!ĥc"U`k%ĪQIn6"#Ul\k]-R[jh)	kΑKCl{阹,;R9E?2jcE^̱JTɬȧe%$ܪmMnnb
0l:+ǬwToH|~Dyc&޵Gؿ"4{ &p=bʹS&,3ۮau@`qI;}/[{є"KwȅфgSMENXz롮XIksU=y֠yM,(pK7䓥J-N$P5ȕ,c/2;&jy;Zhx;M~0L܌bx;/4CM[ÌOSHTrj{bd!#qXn"F:^Cn~eYT?߬v&ϑ?Xnn8%.a  Df2b[Q6d"ɾ|,+}L.r!1E*9{fJHE#7Ekd<U0l/x;EМ|;@5Jvfc-}ٸeIHPF&xe0(?
-8~r!$J-HU>3t+ik>n;M}yĽxn&~-&U!tЛX	tMf떜J>QuLj28 ĸ
yE irY؅SHgQeM:vmZӂEvVEDcs*X.OرYL\k&Y@e(-cK)_TOMYBL^DPOa7bXGTpɍL_zC>zrZ01܅d
v4V,&jPF0> aU n1LZ+nuHSܕսSdo=D!T_k]	u,-q*OK/Ʃ
hSg_9
8N'?e= Sk1=jvvfй#}Qr\U̹ok[ioOF"sĽ
|Tm~:_G-	8Y}
X!}CXE<tԾ͉S^ꪓ,GPSjj92H7ċ˶lLđ^᮰x|Ư+IdyHnE@XeybPTuPAת9)۰o]L `mG!ߑ~d%LPh.lrjEPnX76{jQ4vk/#÷BWX}_^xzxFP^3s=]C)6~
_(Vcwd1VHٶRzCϖ7CԷ@d=xZHmK9{4GZW|nc		{Gz&nJĿ;nW
/bt<vewkGw<c490'	T.L!?xא)}`۾SHgMħV@^$%vt+-+ez<Fb42t~P{FSe㜰乴ծ5Qxn]] )X܎\SSHG|A6aF udL`&d4DRm-#5'oك,E×upmOTؙT&C~ެ;C#4f'q˖˙|aY<RH 2
URoh7ig06vn-T|#rOg5<ж,pڋ"-u	KNŷ@ϵlk!Ne=+kܥ30@%ަsrp
q)"5_@3'd#:nTRbТ*_7u-LᯢQeGyOEqaEneڳ>\!jpmwZyŝ^Ģ2S%k.'j#Ƃ5{-
bڣٸ.MHu`{ЋB*wʫmE»wG&]6;?RbZAPGȈa?k)"5X^KcU啲X.o7~̼*$[7,!퇲䴟[3PWu-Ja`.n4@A*"x8n֌C3Z/caΜmzAӽD`k9[f)$7^[[14 g1U?aF֜roYD쿋Z:ϗaxiV
D3-ҕfёކDҕ
i+jJvڜyq0K0[(y̼ćr
([E!l!
ܤq.OK]rc(WUWnHNԀK$FB%uǿN[7#dbLZ	Z&*f3O@X,	&L)@A֣SBHބ0?NQ;7@p=^dC9[WdCٴnǞ>.
И7sIC/w"_Z#+{n[) b]l8z?L
oGs@x@)6ZLb(R֝[З6hF<n+J/>&VnLW]>L[;b@-Gz}$Iùc_$~m@/zfYtE2Za0F bQ"Ulyq}Y-/Ǣ-*5S"\fՇ>ʍNQ3fvޗHOIk=l#;>;KD#<ܠ8lCӃ`&')>9,shRhRr'%?nlwz;wFOb +P*rP_`#PgC^]W,**?[K@X<_
y(:Ph䞡O0ArF3nF].0ɖ.û4fޫI$g'm{ۚnFmss8.P} .}5$"wO}Jl̝}kwǰclJZ_
91-v1׎|(V"[+p@gB¾]T͍danQyǘ/50hVfr~bl1I*BM 	
V=e֧9 M紿*آn7^儐2#]"j!X-/2?|u8CyKo77wf.\i 1i5e~Aq\/GOep=D"LL\\αmI<-@Șl|mS.J2tE'V,4<iHDXM舫a aESf?mS!$֙UR3U"=e*U}siՕƓ2<pcm\JD"]@%jɩ
Uw=5t&-Uʔ
E&-zoOnO`U
ͭTؖaֻb=ަ͗lʭQ0n]\JOAw탈h/V{2I.0.]zskBt;IBz%c]#d^Hp=yu/zb<J:z2}HMAAn}bwd0KEY-z(pQ.'%#UTwYzA9wģ8 aqb1Tlr5zB>9ݿ*{Ͷh5ٲ~dD^ңj
/~˽Eޯ5a-	Q\sGgO#}gw_mͶT=2$"fV(?G ߲EY%d%jV78Z>dnH|;+q,v 
NrHD{'sL8`>><򰐼,U]'Qrφ$	ם1מMtq)QsJA$Ӏxm=ub~h'gs2j &f骈Edf|iq:rg#7	C (J\̦s<1}{5F˖iRDT|_arR0sHZ]D+elֻ.Ѓ*66mzfPr(pQ*T]$WZ}'z0vd[M
d9wJDXӢYHM]F2F
)4XxB1lj5ΩV_V]bRgL$<#\'S<,0c7mYԒMH5;c5g1|#K0⦶vTS{$?1F	 .ϭaes?ҎpN( ߫z_촑9$2{@kN`<FE/#!aj5%
Ǻ.NU&˸͞yVA)<.!ތݏ9dzrC7<*au
TiH!ќ]q>+MYW0f(8Km2U4sIaSmbYK3wO@LP~81nn fIWw[,-Ij᪇x\6d>cNjeW4oO~>k]atqinuNT)غV8\ HF*S?ϼ*=؃brrڝOZUn*,_hh.ň̗r2_OAydӇEy{dleYePMe*RVj5"cU5 a
*Z@9PLS"/Or*=on*7ݚx_ǃ7F6ir6fĻdLfw Dk7C\S\:qW_Gƶlŉadkj3TH<+T''J
.QT4z.oDf8эZcu|_
<>^,T5Z@CA%@}u^ Q݋!8{60#[4Z\"e9g05@Np52h8AuZi7ʏH .ukL;Vn{=)gHs%xUQ!_#3n"ejO(.=0\cb}K@
EUM2DT"Q!^01.m)BVOB,K2`?7RFa6ϼ[/\nf2rf^cf߮;&yڌuME:]\'*I[XSU.Cj,Fss^);;a_%1u..Fm~u4̗ux!/sYv5cHx7x}DBNZO{vcR#	l15KqUORc!ҋє>'0u~-&6_[v
%s<#[vc
M.HYv/w^r/b˱iS7x95r`^P~'}-rn:
A4g0&dJKmU}.XUmQӗBkE3{17r)EЗSz͚Dj{])Į\^]ur:*Y(ΆHteLou.muG;%M.d9
16 !JͲH,`.q^շLP?
j\g'퇊Lӌ|xskbh.ͼtSfHkn~BAG
}׎N(鰇A7޺A{
W4Kh-J?$-?SJPVvi
eB|YwU!yKe
.	Vy[g9y KX=SJiޫŶ]-F.
+wvP/])匃])<\;cb;
j+H
վ<鴼ZއKmi PM:l^S7]v_p imPLewLE6*\7fj̴ο}"`<hOK	BUD3 i[tΜA4`0USĴI?gբ{FnYmC!HJcaXʕ:"]H%gj,
DC%֌s.&i{ГN\*ًl&I㸰u(͈M4`/aLi-3qkoi(BfCWeH @\$]۲:Y}HeDps28-TQǔs
Dm|aq/#K7hj `,4e/_=
1R{/!6	>rK~ͱh+nJ	yfؖEZbS9=a1HI2{JU KӘl1JYaRr1Eq_|X˖"$VeH;}|O@+BXLS
uJq,̔T*,(l,EQkpӭ\5#ؠZ,K@B.9M@&l܅ ZK\U0"
/ff^I2UIo%%vQ޲)u&+2	'qEJFhn*0:wOV7-xU#tsPߗYZ0
ީG#Xɧ#UǬ8{+PYj:YבG
IJM
'BRL6	]#-{V% {'aJ]&A".U's+Ь	jϤ~dBz=6 ?d-3rTC7:;8}RO
l""!D۳ƹ;⯃(S5XqU
/)DìN"ʠH)7{V
EZߊ X_Ea`A#u<iWxVeo`M?uYQ7j^*rc)ze4|*Pu	_rNbW}~n K
pX߄#qT%`bP풗y~͜;{ƾjVρqUzf`1:-{VY.i:CuQjn5I|ܲKI4x.SJbTOn9+=U*^=	n_$L(~jX?˭7l}=VU"/n5ZRU6n?>|/Gٺ;]PSN>"_擅I:wH 6_ho0	;rU^^Ee*z"9'6]|#4OX@;(uY&rl"PG^Ӳ,.]MY>R,o|@]OzGpFP`FJ_V;DHE=Z{HaZrwz^MA@
|.w#K@+Kxs7kSJh! ir3ƨþ/oNy/z'\h"I	|w3I'oNC[UĢ}{g7Ez?ӛ`/f, {	+tjK3+;t| E?U*(
Ð hZ7Dkr.8s5hm>e=2&{95@jv	SΕfR.bu;X'=a,ǓE~7\	1|t
2Z
}4㋛goN>~h&a	d"Nk%qNXS *VMOg7A{rܬ[&_1˖ֺAƅ85thܗWHA}/ړߘyB-R9]WI%j#9/E#y0 VBرItPyUǗ]\PzgZ;I4+!`(v
?wkoA_ʿp/`ʀ8_iSTC#\sU@:
i@|[SmC~LB/xTTM?ɻְ-hV|7F
𤹠t&y\YI㳫õ8WT>)  nIJGr
,J!TM })d%/\}F!\u&kDn-5
,Ŝ`l~|EP0wz iD2By$U#U}4VU
19;d.W
Vf<W9*uSf=f.dF`=}vk]Ή`S|7`w^vӑ'H_RMR|Q4mKuCd[4QKzZZ]̈́<%		8<qIE@7ٰ^EZ!lGt[^눂$b()uSř?tsxÙ\&^zgrE5Sr#f
OT*'@ڳۢ!y~!~	aݎ[ݖRN),fhn*ҵ7Y0`XVB&{;1vGS]@SݗW{ߐ=HC%ILFHJMR9i['m*+>QNJLaNDSIi52tj&x?pNyq(HR1x8=|[}Y޴s8'{|;u>s9}ͅ]hKba"2g59' fK1/]]b7AʫO돰)YQ%;n0{Hܹ8Ӎ9NBc&1]vq]{tDx*ȎnrXCtAyˌK7>dKn\޻;(_qNp ޏ`R1*C6$tc!5mO?_TgK3,Jܐ;2\#0$Zw㛰oMٕz)Av}vu|xȤ»)|}f زT)TTҌnCKJWl
ނ9sՖ n~Ѐ|T]zM՞ 'BM"	Qf!WC$-rP0'l{T妥KJ:˧$MtN|Tev/)3BnGDJ$uYg,Ta6Dp|}rm;dbܶbR6n`LAJǬ\I1GX(O&I@Cxhhk$ܡ[Y+|y}nmRѹaNoYac[zO^Йkt^W9'Ux̍fȝUTYC>a).^FB}n(ͬ>YUR
z+8@TX[M)&`d*5+ĩ8<Z>pĘG-c8"'1V+/KV&Mdʿ<qAN49^NVu⒍5q˃U-<[H7C#$583]돋cUSM|Msj?7]bQ2u~w2I49|jϷO%IꥅRk5jsQݫo
4"Kq(1i*;83ŔsAiXboW.AmD:|
n~;5?5Aݬk16j8GbTdNy3-X&[uEDmH~ow/'
0K^>$;/ɛɛ{;II28~{9q!%<%?6|X}/X:_mNo^C7B_'_J^$oO{2q7z"O7N |y[;Ǉǯ]kX2#|s҅KUCv'	
ry4tF9??FFtCd"%:I{M:΂u_QILXOμ*
YmtG9~oH8FD
oW+^5EdՊ>c?ZꭰT
ybu=L쇾lɦRQߩev[*L-
J
[.mkɫWr/yjzΏ8Fy2
[nD}5?Ze*vԃ*TA{QŲ
,B(!QLQ/4zXVC	E*Q!{ly5kV4̃Oٟ7bDRqުڑ܄%aPܥ5A\I.IAE o	uk£{xK*ȅ^W"6.˖{ذ/y7xR//YJN]}y O;_S8:3S~Pǂ[c:r z*m.jV^OV>ǍcrxtF܄SPfpUeO:
u/n6S /;(Q<\WI#MpuhY;|F5][1fFO^bM)-ϼcC٭?&Ȅ/BªݢrڟeWY_UR]W[y2|٪F+AYUh!I"Cdwq___|a7ʪWWﯤ-zq{y9g,KLfXsG
ofϥr/ZKX1uZ<_m !ǵgwz.UDFIgfNu.>fh%j2(P8%ubr4ƓƵDaE{X=q|Oiwz<f._Q(@6il,g"
1&p>nw:48[$Jv@]ʝwQy3,SJo3 Q"B]-Fev%\ӡA&_[e.*}q `8\Aeի5I&QmlR=Xpuj0r%M6nNߛEkp׃LB^b(`,)F2Ծ:ơث>vR4*	)GqTeivyҞPǿ~
쩯ٜlo[F]S}3 WonzQƢ}Yp\ξ?e1ީM*zs]E(Z#
lZט:Wyuk
Y\>y{
iVYqj%&q}xkimBo!j."ψ'B,Nʏ*bz4*T
F;J4:{W>5?`̀[
xɀ̊-NjGQekr=}#Foz\rL&`f;wHh͙JH@2JVmVWZ ͨߙP2^BED/C,D$}Q?Hs@œf)<\z|w	
-/+캆Ytx	$l"f|d	T]$S
;85$QR+6v"C'&l-.2IF_Ú cFwn&@(=(IMfϚ
b
Qiu2>Ul])^Lep7;KY[Rb*v֭>RI}>74VQ2j5'T^8Nn#5畸ԅX9Au~yK$)N~A#Tl^(.kCvɆW3<{BnZh!|%!hFVdx +C5HcypGO^
[$ p	Ad?ɝ=ߠ:S[*TkmiWQϗ&ج)G%.Olp튊eϘ
7!ֶ_\DW
ot-tsC)3qkk /OgMk>^=eY|#(2٭9mA`nd*۵R
DQēgu&y<"?*qHk#+-vJkؘx}umO M}&ki]"ζ7ZW	EtmTsj91qD%YqFJGy[?4\EmF
JfrɹXHAy?2\`Dp	q+>?3Y'P2|lXJmlxM
rfSDKcfUS٪`
G_o3SJh
:Jb*6Ci?4F|aV5LȓC~Oxq	dV3FLWtϧk?P~FE8ѫή۔kDdhȯrnb&s[)px5S:t%DN7JJ<i@J'Pc":XFJ
wCRXg̈[?~nvti2ċq0.j5yy;ML~eEsc5-$ ۶qxa7Dl|zUAu^Vvvt{czKTִѾIuA{Hz:)R#DnN eDuE$SRaD≑vbkH~yuP/q^/帿=̗_Ȧ:ǈlˑ:.O,9 ~{4IzҺ:R&,))IS	94ں%:3nnyC!@q><KZ DAYz_`FYwoܣsSir·bu(gק]FsS+nVE5MOq짳o޽90%ku8s;% -D=$E^.Y'\
߹xe]J	.!]Aφ\-XV56UP<gj9'=u@ .,վqL\ȋJ_$5v|" 퍩4RO
lc'wWݑ]t/l@D)xtSE[mQ1i}2W^d-3
}J_Co˦3B_>s'WqkE
v IPI
ʽQ*[˞.>'?B;wh""U2|</v4/͝7B~Q#n'$f׺Ju5U!{#`++QYp|KObq!eň>Bq:]-Ɔ基,޺M1yR:
ˆX u(VC-X(=,Z݃QֳC-dd[AsMtpQ3V1:n]Hl$
`Ew^pYsV]Jf~iGY#y*mx=ʃtb{ JYT@	/K`#.-̱a쐱3AOf̡?5ibѶ
Ak~WW)+MOtf/$gT׮7ֱ뻯s1%nSNܢc6n}m	s﮺"~>6!em6fmr:%`>ae?;[?ͅ|S,Y)ߜ}}
lFl򁠒 뤍z0E}0ʸ;s~ސT̴̗+*6bY몲@-oJ4l*ϛv 3u./@{_nO{L `D
9YЀzipeWHV	C{'}
ȁҍe}Q!X$aˊ_ә7a棌`Ck%q&`}>4|e|n; 0ށcso*R'&݋''4>Ϧq#5hij+p$piM	/>i#g0F~Yng b
ǻSwfǄ܉i:E\yrЁ{M@/^]Y_k,%pGN?Y]ɂVí^27'"̀og@+42xށ~gni"BFCEJSy'z@f[&H:WV@%7u1=
Rֶ֖'\z=#4xZJHeD+iCH1Ti 
5mEvq.XQG-cȫd:C:zR1A66LTp E?NSI`0ZXFI~Qŋlh'?`|럓荂FZ{݆݃,:5* .Э[=P?LQDxU{z⹺{ji2s 8NSpg\i	RaȲg|k47樓etZ+&z}tC:v.MqտUGRiUsRdT*u
u,ÞP-R
-pL=ji2?NFY`T|Wꞵ)z"*a4`~R'Q{pX&=MAVU)?5'@62N/v_rNxjwӬ}<%ޕXOCkao~bi/F,j&`qt\XG]x:[bmT{#oX_oIj=Px?2	"ԂG{'ם釅A܏ptL]!3ab+o<#yBVW?EɅpZ@|{^@CK(3n~[kJZ@D;V"㏭dJ:m5udT<ywn;̅oPߝnHތO2Sfw}kĽuJW+?O&Ȓ23-WEsou4~2HJLXwaú v	m7[4ZBnT0QM\08Q_FIZEuС*ɋp);Uv﹎
1xݿ?,Ho.{DfqcEgHW _[+XIji@ߨP%F6d)J	>}1*hzc.i-ǆh#kkPwYl}X0n0ڼ1n_~&lĺZ	h5WGPQ똮E)tb:nOrO."u5ߪL-3DEnd=ReNMvwT7QhpI ZO9T!Ԓ'Y{ WYձ+F\]Tڄ=Tx:(ʬ"z.UU$%/<KMS7BgR콱T$-
}ՠ>lKb[?S?}ݢIκB38+ho,~|nPXEFat
	Pm
JӦqmUoQz	}|yK@h;2lc(
7ӈڃ n؀g@Y3AAH>!0xO PDYTfTgW=CubrQ$FՌ阻Jt!.Z#5ܴӅ?$f`J\EYa"R7d% 'Kt.,
tkF"dAB2
&b
gZ(%TA7#f齆X
srjƠASi^f!؜=Y^Tp%3j4.Гr/g:ojT.Pv;E_.h%Avz%0{ܦ֜zY3Q6
OnrrjV!TןD/j[	BD
ʘDUxK"] 6x"ߤ{s	:;2y~?Aݽ
әn_ᚴƍ
q*zNe2gtĒ]|Ѷ.Vá昿=sn4dKf+cms V@kl6h\fd ISQBz.cǆìm]edɁW	ad[ uLC7/ϳ<ڃΠ#iKc/tŎDfxSU?_˻ȲgRE/`5(`Mew,+`luki')">TLyVC2~_pvU3^kTVŀQ[M$mXsdq}BیNo grY~ڣC;;j^Z
VJمXs$N-WH714tw}
t4-^uw Ӹ(-q}^_5`^d23F <vg>(5Ryu_Fw,hQ"ISqjLُ)=\USnR.~°JF1EM]~1s7z/`5a͐*U5{}6	C"P2fA,&{+6uFZ:/<ɯv"")`]x=r
k*j*	6Zj

4oGu=
":&[<*d8,z[zyZS[ԌG_0XHt$Fxb5%)ʹ,~T"G|wwS},-epɧ'U`4.'n샷p*7``E\]5Y`j|QUΖ|qݤcXWi׀v)v,6v<:jdl3ʩC-~2aAeXja~&fMɁ;jSbea1Ku~I	e\ǋbciXb1"3	LFDdZ/zfCyҹt}Ntуg>p,%$5N-sXF@\W^I̻yncQ/PI:b;,/rFޯ@wSny-Rj7im9ѢKSX)lLV3ERw}mrIK	z;4vxlZu!gZ㣥KIJb5z~Wt͒z9:.+YS1SpH:w0%םڝkS>~Ɔ2:yWx!ؽ)hyẽ EVo3K0!4gV&Q@l`lơu%cF*8ʗD &hmB.jIE"cqє	MYE_RgbR8PX=E璫g :	he+eʚBWi /a!׿ZI[z#@-TSض
2^3͞af\џ!iGfX_D#!H{kZ3̄~V@]8끵/7E:.KdJv?DwST!HV.,sh᱐";RMK^Pv$(I[mݻ½%uo
i˟'챮x$8:ɯ7LL"2
i|JL޹@1{-r{9tj]K[DASPw^?ŽG̷{D !vV\Oƹ[?sy/l/F뉠j 5ZhE=.jAxliM?<ՕDjEMG)
yJ%xWi%löTJI9$i*\l](d\ʍ>s-w@	s$.Z6e.:Ϸ)Vor$p\qrgXqmQe݆f.-/'ŸgE [tr!Ҋ
?qqXXVmZ8W޲!enc!j'V..l$ٿf#KYEdLY~7݋FtHcvƣ\S(FF Ys!m(ٙn)W0F>Mٓ"*V]6SQ.+[TQ*vQ)ʋjE<@bcHL*{n$E|= Ex`)Jǁ8s{_.ՋW
OgQ0@*dw~|qz|}|zvɘ
{d;[XF6
6rйݙr`%8AH
.4|jƄ}wM^/+P:$Bk%$@f4iM҉ۥJҾ0J<cP	/V
iݭH0_	vYgrg0EZaE׬$\⹐!IfVySev)̕?q#̭)Lտ=uFoLߛ)0%=޹b9Ub]W*/qY:8Qs\ WXUQTzbjWHf;+`#K0h5zr;vFJt%!9gPҏ3HsU{4HlǖKE\*>-c8Qr(5p땧;KS_`gGnj<LstXk%dH".X!-,kǖ5MWTvkXnTN%0RJPmy^^hYSL2yڗf`OY& y0.U"X`+6F||^f}ywϫ{W@T
(rS 5÷y5	/""|-[Kյ9vY(7? cxPo_+RA뇮pܥ/o_UAzby
FnE7:+QB@wˉ>7o|ޏDIGjވK#rÄ <7Nr(!6GXt,"8ȵ*҃[>hwW`;nh	ft#]0TB@u̺zh$/kE%{hw.ps坽1<`U\R_e=ceʱE&#$a="o.,.`3s%)WˣDp7c,M;2O!밢mj[qk=nbUa$9)
H)N&XW5D:<}=PL0n@N݈^3;2Kם[bQ28<O;G~?phiJǱT6+ +{wfk=ofR+npDpTl9Q/IօNR70rN>Ⳬ.e$ъ([G[)ʣמ p2g\8Aiñ	XDa%BlPڟg#hU@2UHoOV;,X))g@,p<|T0ِJ\B)O3.nO,P3;\.qhT;֓R2Y0
%רIlR9aM)Bz˦i#>+Y:׷ yVZׅtI̞S+=eM<*h:ugJ/1JYPDH>Zߨe	I{A*dn
^z""GѠ78!2.U/>\IKN7ݕY֡gT3gĖAKeKuCFߪ+*:y
;<Z_ԅ^0IPyp(^o5 DN)	s|rULD:	G'ZީXxx,.M!k  Hkgoל5aekcQAGZ8;r.)t\Vh%'u7UECk,*nEɴZFʝq=鉿
{D|_p
ƢP5Cِ3.@7Lbk=.O>OKhx1Ra"
3h9}5@Qqs<RnyF\ gZp:>3Ѹ5BHSF0WH+wi%+R!FJ7/~YKYD4}Ǜ)q!
X=1EPˠZ#srq@wU^ <ZjKqdi'ą`lDIJ[()Tj3jywrTU&Yr#u!x($	*d
nkW$%^mdKE;H79pYM5|c5MPF&Lѕڇ1(;]^~R:Dl
EgkY'"B)v1Gu13xtLeZpD$b5x٣@ldp7N|2Q'93v%rmE6*=,r2A$>Ӌ*%Y	/4x&KyVOa"b
\?>Լ|~JPSyg<i).1wCpx.>k'.(oqHpo
'&=Ev~476>oGXnWbtmi3g{t\|pIE.S=BB	%.BR~DHnfhcH̏(j☠uچj+!#"Zlbfݛ;`v-5/[=qd%QM)wѵש7;wUjC[blښgX1Z*PAe?\='-t-B豐ϛt@dUn2'@RBJ{&49=ɟT Q*mmUBXAi:FVDo"'#Re0]UESt.`ؓAǝez-9rvt,E"W7KվCwm~AGj[2pkB1PS֨Y!;>[z	YCa&V)!1'IsR҅ԋ>:&4[	wpY^ⷲ^ĩn_b,0_t"Ks.N̙`/ 29E0	֍"jޞY~"ZԵhR~b;LN:/tRh
߬WMPT7C~`$?~%rKcު_窰qPZ%%
NH;Q@FE_h$`>~rQe0	ڬr1Ji9T=jD/_`wCyЅ3Pyt08kWWM[[E{d/D$/*)=Tr\aaWٜU&kwYOeܭūR}<cJ,Hޒw$Up״J6,VU
NҼkWrSMxoe#1.;;q||np xLZ/|+kWPLfq+R-l<e
~4?}vt>x906K^]]/~Oq2
%:Oޘ~@D~5փ%vn5`S}|-KVal͛ X؝,UnSnRTT|n 50}dBj02)4ڬFO.@S40Џ"xx	It2Ճ,OwR^9Vlr4}3zHmJe$Ԏ--2U:4列x,/(yyssݷI)&6k<8]g{Ii*Tjxlߵ`k%iJG:-
nO0k,!UNDٟ"=fwrKFܨ!}	w]:*a
ZF( =Xn?cպCwZpY>2?o	}/ܾw[S	~VZ=么#piM7_dw0sJXI	FB0syk#YٹHZW\
4_^e~2F.O7Hk?uů3'4w)'hu<^渀1\E2µO<ݽmҞpe\s/	6;(AJfre O&P=Þ/t =-'/_j,;xw\eD4\(C 팓>V9 ;{bxS4 D/qkӸi8cX6<p]71Rf2Y(6#4|h嶛#lhLGeŇ	<񛼸-ۍƏhS'*vuog|7G$;faQ, w@cY
b5 Ƥ*]|$(`;]jT;.jX$-Ĝ}W_bM|ٲOIukq?W"q9Vb|'"q0MlT>!vv`5VmqVCMG@TX0ae'(,μ	Ouu|י#P&=Tٰ*s}al<daR2R40tJll9Ňi>ڝ~pGvdQp/TBJ	'Tk)~!v0]/,c8s)R-Bqԓ̯%WntёASIXerۦ\*4˘IQrX;
ee4KP.wt|e%~]).'qKKfVryy^{{%.;>9ysu%.!tRKa&c." #IH..)֑By5/n}@=%wМ\*0ZE&͒m5a1>}}~![`Kr
*]$["f⎂.ڟH
!2۳
XPPC;HSPhѪ 2DA7kA1Gj*t9a<e:5nTQWy\''{dM(*&LFKEzn-'6WveY R
iЈmcބݴSA{`[YG qҽqQu('l/7Aӷ{Usw8r__Ztٴ
Rܥm<?؄9WN-gqan0a?G~+i\5x],?gp.)%-qs_4}ryQ&/񸁨b~C!NN߲%q&fyJ5Ƅް!ΘR	KV#f#=`S5l
|v=!pp^Z6cz޼9݋VHa<`9Z@a-3'C=w|[;l5eaۤi)eM⚻ro{|_J}(h-nǅ/j׆ 0gOVIڈhf
2OdTExbԭ{	X\*b22q(赒Rd]E-PrZ
e jUujڰ!wu	@j|2#JB%xrT,-{.pU[.B,2JؾaH;,J;&RZxIUe
7&rE:"^$*;J#Gn.twvvԾEh;2xT2xU
]tvni] 6r
YuZ_'!$#:S_w=8gQHx#j
^[vT&-*<"$!aօ<dIST//~I!XIc$kh$prmvMS![aYd;{$D'i
Zܕ\h*SYHYύqG58\4C8A ׍?;>~!RwRk	%}Ӯ;~ÍsF*p~X	* K:/S55,Qc1a +3ƺ^der覶fk ,Uo竾yTTn/'OjU5͝[E_5-ӱ$x4aP{,8V:1`C ۴*0<:"&:8XHkr#pv=۶Fm.֥ö[pF-nSimWͷ%5*5vKn᪚loK=V i{'AիvÞSұs6,q
1iCͥStDce18;#<bn{.)^]Sֶ,`ly6^x=Z)#5mmBx<oh(ᦼ]&ux⭴DdhTUU$PonE=7*?\ŻYlxa4(-ubݾ{۽gj+S]SH
PV.wo%u#*-h 
"+ϕm
ýB	
b4&zU9Lޖd@N˲x慴cq0:n0	jҁ\xw.#k_LsVd4#/iF=U5{2My\sDƨ#cEal0v8U^M̭"X	NKAjrjz,4|a򡘙s/-nSCyU#XlÍ(ֲ}

cq⩃WnZݻ_t69P߃{dB]SQб?duګlY2bARk䙹rE.t~ف{ 2ϧWRJ6?8"P:Fk޺CPcWH'y:@!+o1wjn]O	"pzOAy1z+	p;֬.tł3/tb"Vx jxS~ͪWd
FPQyW=ᵼ(Pǒt
^.!ݰT!JCm(ʉlZ\)
(
BUOQ-h,
jV|醬SlK7E-l
R;*Z[Yi}aTVc{èUl<c-c:ŚLZu	R٣X`(3/e/wThLF}nqwմx=`#.pOn='Ń)	so[RT{Jdqx1"{Yh7eB#)_s$,p%Gy^eZ3Ϩ3o
4t^!03x'Əi#Fba)p&O):F@ߪy[.RC˷ؚ~xiܡOrzc_{=O[hG?w[?l_;K.aůoo(W^_]ݤcζdg)_D3׆y#Vdn,R*r5Tm
g<ýzyȗv,9Ip| {Y
uTwܞ4*
=r
˨i(=AIHFE>Qʯl q5w],[dEIut'`b$Y^<LfSQ!Le\dO	n[ 2r?${w]"[Mm9q!:+2UǹV(G|j	7Ȓ_8$OK*I*+w#޽+?N"
/=}N(JhQHt<	(53^k}) ݙK"HPssF%SԙMgiZרLl_Y6+
<R4@ZObb9Z ĵfuNP͇b%OO{rxyxEW\nKn?_J*H#ۏu=K:/v*:r3R|C)55aٮo!R楱!+MD%Pŵ.dQ-#?`L"/{Vȕʏr8AǤaW+r
+}0*p`ZQ,
\.WTѬf5Vh=@L=)ory
DJ3iĴL@5*_Lo=cK(Ur#._r7CZq.p$*C3\oz=L)OOl.ܛ?A˹L3˾1[ЂMH ۑMBmZ2&C֥4#)VŞ\dP>aXiWjq~J* \F,pjT6*ɷ"&1ZYuqTd}J?m2ԙ~+ /7[^$y\-EI8tN_ŗ9:%?Շ?lR['x;PN*ьpz`͞,CVŔOcCc>q'G]NXOϮ@{/O>mTK}@t"S67?kt#>_nx~TVK3r _hHÚ?rp!F$cɕp	<ojΤ¢Ơ~@p
q)s-N;HNƫ@P%T%n#n;@.Ubh:-kReZ˄b1"\l XGJM'gpYڈtn7	9P
6uVKm!ܫǌ3įZUAkGxk{nyFx>5ݐ/g|xI^A @bIpcQ w"r&a!pU/21JL| MJ\ٟs)A#nk\'d1c[ Wt}]å<;%Se;UZ\
BrJ8w7Gyه^&Kh/*w%i߈-aC椪kA!O=	PQ@ாˎxIVҟb}'2v ,;6G9d)B<nV/a )$E'UKe8VyN.PD=3j+)ZXRzƹ!MǁxuߤMLሩisFV`qF+F:]}rN{ITGM>-G)uBSH
rL/ԑ5oݾM$<=;ٗM6,b*҃5@۩@-2wVSjC2:/l`@xYa=%r
raZ3&
H6gjF,ӹM?\t -7MW;&lv
v5
3k`¾hL:kOxQr[C&F#6DMe3P4Dv{d?wK1+Bo^_MzJRc>H*կ~B}Mljԉ
{b,u^#ێ}Y;tx]ap\	~v$VYYfŏyc5
P v s*V UuƗ ,]/*nG~U*%AycPk/L5Ynt
@  )I|>J8Gfq	a,wGO&7FP0Oߋ=KU__^7<Ns&B?귙V]pvS6A1U^mUp3Ӊ[b6X-48M0~ƱUpNU0Q&9ktN%(l
nQ5SϓB	qx{6<Հt$bqH$IV;Q]d
iOb%S5QJL&,
-eY٠GBi@]9yJ駑,I\b´?r 4O16e|mc e yrEezLUnFL/߆BO2sN=mBfDvP
.<~@chR.bOݶ1pp/)J6׶THY~vV܀{:adb5q)KXb%pz݂-ʣ J
ǉOc9c(|6p/'Q;CazPU{7OV)HybS
@B\kI#4kYq;%QN;	G3Xٸn/&HЪ[V8dx¨]BB#+?0Y#@3"d2oQ;y?L+Jz"Kp=]N"M=dVγGѣamovvn
sIY7fjI!jǵPqv\DuZEn5yQS|DGk{}Wת] 20ׁ6dHqP.kRU)[(T`@T\i!iMB
3w÷'QMvYMiU	 <:%jOu[p_#TYذ)~ᴂja*E^'øG4hsbvL7O{D[,
5Ps {_7(He\.I;Q7`S"5WU]$Z[8l"}5elfC3K=9ڞ9-{C%$Up'o9s6.ot ҢL+YP
A@ ܜ\r^UTm.^"jL${U{6
=G;?NTIWlcClq>I FQ)h9TތvJ9M֜Vzِ/f|9cEZjkψ(U/	)euok8d;\A@
!\qI9qg@%skV#iv+g>)놵wԢ9f#3fBJ^F!Odu8rLT^D<4̼e%ُ0U k>lm܁;C]_]Mc{UzPY~!WE#k%MuyO!XXЄq3bXMR(T;P?%IMl,Xh\1L}ecO-%`M-PƎhX@(0@%u8 B.LRZs#yrţGUtڤ1$]5O'fc:ʝ)1GXzoL;a!mz:rIF++ywptBgtZ"' a
$pFQxlw(ޣ>v%Ew@+CTE4kk
>($_`fU䳄)S5I˿<]QC!勹^>FNvo
m<
GjydԼzU-uTND-};Ibٷ-Eƙ~RtT+ΕH#GhS(U,ut`bXl&Si%E_#BeѣǿvzKy>x=-1:hМyX~MԊ܊:QBɗCG"y)wKP4XD_ZŶzÄJWW坢Ҩs=)؃du
 i)ق!ϓ3!w FtロqCVO;huXg5R(o/{fp0t
~Ǐm+GmӋABXيT3bp-#N*.4QSiF؛9lwz0 h'zݐ)b=M8t
B|>	/O
o`h?nRHwd&[!YXhb	ލn6|6ҙ_<}N^^GJ))I)=A͇'1,T!6z{Pg?ەT햀VgO;b
rZ˳ߋE|^5!|#J$oTjvh0s0,(M%E?Dܕ	al1[OC.@-wd3m$]IdƖ;MĿ_|RoGHk)3{0^ݦk,It5YE3
|51oYNaQ:+,jlI1P)s+%5Ʈrpئڊ=7J(_d\$9Rװ`$WDGm/訣b͛k>3\EXo@M\K
LQ(%ݨ|5Q#]g#!Io !nbBu"gt`zN
빸%&|mphtX Tn	ٰ hU+| O>SSJn5{r: sϖC.Cr3AAP4jC!]pYxA-g\t p ,o6.8}EgNyo/KnTi@cU'x.9ArMDx]uݗn%lP<E*WEVqSN[A݃;_\+DP6zJة/L[OG
p6f q~-2W\<mR,r3Ά:4w))оƢ#b};m+x?c}/
65A
'G؏ +vb(S.uKhCiAƏ	ܤ,n1_=YUɆ~3-+k?t\%K	hJr/rOGerr%QY:*$(g[ȢppN+|R@b߁{6oLBܘҚ]F\6T\8'^1'6(_#3U|yb8_KG~
ض_.ª5بQWhTGyθ|3$i:]6hVR`՝b.@NlM[(+sV!&X@q]L
huek	Ge(t0tl-[Fg}8hx߷\#Ո8>ujU䋝	,fr7AO߷`ovWOT)k9
6g\TQ$|¦n9vLaz
,bz,
 `-nPQH`,=sB3dpb~ZfI.CSns
[U:3M>䑕RC
)TYL ,+"w9.BVȴOаs1N-)_*uapYS~VΙ0"Ť̾;=isě0ҺX/\ztC<!'/L Ո@Mʆ<eM]aTrD",W}n-o˂}M`e<:];__}J*QW1iUY-j{nF#b:\׈Tm&T~ΩeЈC`ıZ1h!Pۧl"eZ"^BLM7%BoÏ̫ "Ηǩ(f vXSh09
?<]i!aUu6̢a:ܣTZIA7Lt:ZZ׃rFա@*gU-b`R(YbN*J
r	I:JJA=}jXEmC )oi8xU~6`V.-·Oچrrvn\$s1VŘ{[A/tބO_ēެwU0}K`4jwU݅U Xr ORͽ|imZfa龵4dh`2 1dB'sFU߸<0ch1-%9<lAFBITHzM#q@8=ǃ$wsY;:_Y={t!&s/O޿{?]|a:*D
FvOs'f56˻^-Z
6gy^+avCT\܅avR?_"x}5@Fx˷Ǘ'?]\_8LN3nF`&XjUS0G4{)gLa:.N`Dĳ@<ĞG<SŚgD4J!<!(2$j$+E݆͚+7IٮTat	.p)}		bzQd}6-Lw5+.=kc6&	U@n(2AF6Y# W" v{"v>',;H"m-|^5X+?IP/n,ܧqED?i¤Cf3:[4K˗;m5+Ƴu<2P4֓ꖪcʛia󈗡$\JSC3^noy?
<;Ǩz=ϾI#P
fs";c!&1d˞ <~(ֹ$LroE#KUPzuՋ4;z(_vp~"V2FLC-ދpR^Cӽ gUD)]D]ϦqRMӯԾjoF'qTT(`"gЃmf0ЌA!)%l֒łѕd8z\U(	:\*|P^R$0U^4z$h7WJL)˫Xx̰109\RY.kNN }pZV_2H]h7&:LM-h޵VbY.f|j7SArZ8t<'XDl3F,5,
JGG݃c2Δդ"q$	XZAtaώ+xŨzp<0{keMg_e%vpͶD	D9mFO|m\əs5DcPaחiR(3K!Af6q>lM;	xJBy̽,s6
@V>rH)ާ27d/$ruuqX?o.$ZMxH78--|4fYǥn
QF YM8Rx+v=`rYܚ};ԽyE];V2!m^uJ.}ZaܚsB[6TgpBt&ybՙa49D}LNd-5PMi~8;
~*7o#};}CەK@9Vu~iF]M- CUk~wa/
86Ӈ,|0PPhãP.L&BXzQL=mrj鐴56
͓/O	|a׃|<-B8ӭxP4;kQzsz<S@T葧olƴAnEQ(&1#Ewur;K>N`Yבҩks5@K.vÌP尤>''
s60N|P搔<A	_YLn)VGmZu⼂Ee_~8}.&bˮ	#Ry vl]qLm=U5oEvH|w^,=a1;`h,*Zɏ
|4е~T	!ۀ|hy՘֮>+N!%S;UdU׬f/*|yt(3@dUQtfoд
?Xꔚ9Drz됮v84ҹE1V͔DLCÜo'6T",L;]oqQmL%=x(P\(VHLյ1Y:H`4MB;}wrӋw:@iQAg5^Yш s3jA;UWr։`13Y|4WNiUyǚt7;u6{Ma913vȱ*_.mG
hf
X]4(An6<ZƩJ-iI81otF>ץW/n#"oiwi}gk.s6ϮIi}}+nA.[R'FtalUn|$&0;ey(m85t
\iAC fƱc~krLv
LcJyOç,`qJ05?8OPsPF9MiI2QLӠ&.ɀ,RsU.[rjs3X}Mv*x? j).cq5z֗}"e+0b_
ag7ʡ;2'·ʀ!Y;Q!r=@^XM+{b ?K2:A*wX'N'o,뮺ң:.NN)1XI+fW:⧌[Wyy<(SRhMQ0z;%o nLpZ^"1HK9N+|.}e1]Q'9p
53&F)'2ƚ!d7XǨJSPսtoiigc(ˬ}ŗzF6弣Kw9</Bi>qň&"P&F &#M^
$è-mʛZoɗS-Q{φ&_RIc)vb&qxǞ׫v*#'dE͢45د؛U# ,ʚHlm_G&y˳mRMl_Y<*u-nd9wD#FKzRp]b84I B{4NR3J-((3jA:γ7n_Vk9Us弍'x]Ӈ3Cz'n'U
U¥Bֳ?s!"8S4O!6kԜ[jxea;K+^~<'WTiw)/c\z9Pl;ƶy){
X;[N1^J5ɱ8231S2tM°Ipˢ#<R%JdJqW lN-!hve߸X;]mM˖h)fZ vK6L^B]ݣ8J-nqN2967cH{	E[]wQ%FMzD@;
^XfF叱|D"uֆh*ԡzrKޡB$1Qґ#H2\}kzi\ݸ%T+9%&6ZA?~2}gY1sP܍Y!x<}7\?%G-q6	P&I^ 2O\[cI] OB&=;#vd!^ȏbtiVYhl#ŮۂTn )$\IYX7rҽ58F3qwT3
$Sʑ
2kKt
橚YkEH̀l6w;	,,;+szFC,kvXXXXkơY9-w)!ɈG[iQx&(	ZJE5)X}
e./c-Pr44X;Xz`6Վk4}dUv8T6t-(Y#	 .3
ɬVnW5Vv#X Ԭ6 xZ?D=/Kvp?d/McĴ~tK.	<GP ;p=ơ5$U-h<
E?"OTpo<TGP+|bCCS-Bs'&?x
HtOlM+l,AvtD6
%k|ؚM/2Lޙgc8HKW.*lL'2}|P5	&Ӵ[Y
)	C;[=.
rct@-qnЇkz e|r6( "@yv<{pT.[<!>*8lS;+Mq"
h%Twcc'Ӻ($9V)^ 1M+¬ `o֢q
X),Y~~a|8mk5t"Iw!ţ{d]>l]f8Q g5 ZǮ&_4@7/??׳_V5wZ;!v[!tD0r݆&\
QPYD#p
MׅX؇a__ߠ+]ahC*YPXB ,Z&2[{q7h	ϞXԅ5ȍB;z,q`۩bu=5NcOͩ0zw@Y5{y?_~T/ŧU5_մbB;p}rK<$y̿7r?Y$و -匹:7[`qbk8-V<wڜtx].lO	Ѱd\?n$eARcE<0;Zg
{Ǟ?q@I_Rr=S?NK,YKŗy97!m<k!.;`P*`hB>TnPAKyga	H/Q<4էz<QL QVY+gbN1Ep,tÄ5cbzh_a/z'%\y&76i)
qek	\Z06ֵ.$X}@*Ǒ4 ol '펣ZO
J/2%=zadU{DAZ
$JrXӣTSAMJ"7qoTuo䎀G؃v6܅qWX|5gV)RWqW+z_ܰc55P6ۆ9g$QzUTQs_su|Y]]5z3uJxa_~V$,qcÅG$;|`mDm^^gWE9MRTz*q1+.\|usX╌Z4Kئ2R*K+y{om(׫*'!6{1:~I,n>[qZ+l\bO&5B+8()fht\objd&vHG>ۛ-YTFi<]>Ҭ	U`}		PbFf	b8Wj5i~czh,UPrLiM
xrE+1O..KyutvH5̞3Bgh1Y2'~F! 
Tհ?n-HQ=^ZBjsUr։D5lL|"=^r8IE>	p>Eg oe1F0p">1R
OیxH ZgOP^bHȀ |
>w̉7=oAUkcJ[k֥ly㇪*cBNz|/24
ԙާ%;[KJB/A[Lvi?pTt1q	f=Iv"bWt^ı+ܖd hcffX$**~9WV6Tl$(R
j)T<:k\}p	$nBPdBbbF<۽1
1\RoMY+նFm"dX]>*zɻ\G|DWn-(αի)QXp\{
k-HNwCFUy yMه],L'ѢEWv1
)t6VQq:^&zT!^14k~SKG193s\8(?@"(`zĠq]}0VŴQEyֻT!@rȮ4dŪ(|_4bJ/~,P5eCYnˀ>rJ_?KrvUlg52&Vl@NNRY4N;ge)bژ$D[`󬕇DT!?}M>TW<~Æ]Jr}W	돇r|
_ģ?ҿ&s9F[lKY6lA"']]2G6^O*KmյתbN:ƸG>6-ǅŕa/Hw׈K_⼱ Ue1*ncn.<6a8$CikI$x>\\,qH,B1Q>8?6a_tEQ<@{uBu
w*O9n1u/upLޠWz/[oa(5-K}C0CoT6h߿jfFX[NlrdQJw&ig)_7- j*N2b'oH+R<Ng5eNb*՛>$P~X9~EVVCƨ
lJyL[/VplD7A_
Bi@n_B\&F봀DU۹˴4&lq-,rK$T|b&(T V6~ _!ޒpo5MBZ-(wnmEy-.E8SxrMUF7
=?0%r#44a7\dy=ɜB=2oӾfaI=9I|s
ݻj
E@Gkky˒KϠ	>:$mq;QwBU.)Q"GD&#tẒ^wE(ɇϫ$U8p"j)P G#29ǉ
Κ>V㝏]ӀKB^tX+ts1,ȓq͟]z^ZkAz#E!r$\1v^CvrV'խnB˶"aY^E}Yi; d*:\n
ɆlkHf~VB欽8/lx[
hJs&;|E֞E9̋X2ūMdMƆubyt/<5OH[.~z;j¨@
t5Na o/+|( l;Fөimt=lXfǲA*j}i#}B=u{x>t5i-rCt]̂_E,tRt1/,lC:.'L}{V֛^(4a&MvhZ\+m&<HXcŅk\]<9Q.\`	ȱcQSC@AAOwsԁK ^2Lmc!X!UI |uҊsdϮYOrz a=#h1WX6O1A{,sxWhwrX.+y%+<,MAHPh)_9.@VH,APO&\%lz8H,-w}A؛Ϸ+'+R^ϞKL*nդ0eZ:>UΧT}]XZTtrH,Y!o~hŇ@_hlR#,iG{޿E7RR[軍A7}+:Wa;$|=o	Rl7ujD-OǹWEZ6ci$Ё"=oqnfF
ڧ3|YzT^v`I+@(;&z3NA;zbeo b.5fZM`BgS^B,a񹭤)s)4Vag8q FR{n&;aI/em܋VRR;QbZbmkp|g'g_F[mĹPOH[ώߩR|wgGÃ@_}]dGeMe>z+pçp<@1Y=8|_ާ|fӊI/XC	_G
$Y->@`tq:
6gD7c5V4a-&u#ƉleR,Rե%vFlML6W`vbucyerY7hA*|s7ti('ryASpnvvxaVOd1ϺBK|ǪIʨ1
c:o+Gu3HֈiT,?70Z&\,Ofhtn"o$K?b:Ff57InؼPʱW~bqhNX_]aN`qI](Q@@q*L0Al9 %)օĺt%&{ѪWd7V򤰱$ӆ:G\̕.zV"&Bgڣl[W~-D#kd>3hGus$ަl՘xEgXD9TLymT,NW_Z_Ð֫e՗F-U~`pƎ,z6l^T
dr*TYQ'j?,X^Os[q5UL:6Fx!^q7G_<8o=4u(sLe$%{e<C0P_=f'd%;8Z'9vK8;t~
cưwڕp?[بR|Od=>gz񽄱6@MԦ'(3p[̢,xpc3j彀QOV1߸*MXʶK6$hMm.aL/G\݂ࡣD~?jd66G= Ȁ*5@큵!2:9ԎZi-7[^Zi[oϭ[*s;W,*f{"iQ ~zYٶazދ,bV@Pu4tټQ)*[YE{d6ƚyTq}R8*Cos)BjJf)p%NFؒX^)?"{+F:5Vg篏UUBl$~;2 B[CG7IXI굯^}1ckˮcm9"'#Z#Aq0	$>6&I(r<
)č༃y
cZtVHZc	ru5E) rVZIM֢SKR}-G;KzwheFdfWĝ	EJl?o)eq<#	"T\v-/[;u+۫v=KLX7t6:;6+{قtF@ \GCG1Wك%7~hǵQ?kpE#0O/2m9u{ls3LrWWCf_xd3/(*m_Ɔxj1 K6ŭmt~[h}JP!Bj<C?_y<`K 0"-	9+j&V
 )V\:d9esvdɲ]x:o'_~IH{Rz||ϒiFY.Y{-_(6)Nk|yl_W=e"'HS@'TcI
V,ٱXFA1}ޚШnGr#YOƀ#Y+ID1I̚edvst,?ղ&_zMZ|IHGKH.A AN}>0hտc-\;da'K^5],AԽ76VuQ0kS2y98M9doOk㋙RU[F1Sxyr.{ٳC_*YOXD\S
7GM)ZБ_JQ2;; Ԑ18Wl)kb{#(QC ]eH{[wa['4o%;)}{m~S앻hʭ%`
6yV^c7:(lT
;?tJ𓆙PV7zNG,P*om#oq▇}Q7K
↺ գ4!fβi2=MՉU6Kdy* ^m#G6vWهuqG$/KyAbFK ]4IH/|l١!'{ݱ@	><3a'vQ cMsh1PQFHNj5ߨץPc.\1?6EϿq 
V;YWɩ~k?|:}}G{!9Ovخ=]w(1l˙
[h602V{kBZeʨ̣Hr
a0!
E9Tz<f[45>8$I~]	!W~hN0nIxf#1L%qc ؓz0'Q*$S7QϨafx֖ZI̾F4}D>/
&qo*E{x6HaK(g"	 zwSZ 4קf
7xQQ9hՖ8gs@%&{=P>k`9S͢'n75i,N35l	Jz-Q;&7NA&V *I	8yHIQ-VxC{Y/cuv_\H+۫u$B'@{U-ZFWC3;O{Gtnj$cmmkwqyeC֨F8xIc)iluwI*̞ ;y(KcgYu}V^3r}]vV`2ظ-a("lӍ-^)rnM`VgoL!m{=D<G˷xw40j׌Grĩ0g.NDSV5Q>|ZDum@=DE1Ŗ|:SE,̵w# 
TF	B
s+VV<KJ!݃P( s; *DAJ$W5.2c7UQ|C}vi6C9EK2dAceUxG>~>v
G8X~⠂6ou(QpH]9yrǨ+jzZ.7*VIkW6 zJۗ=1gD]jH	1W<osޙF!XN"uS EJe`
.;c/l-QVhǫ7bQ"%_tj 5 -dGA"k&"T|ije>?JіaFiZVmv˓~;BW:7pK*<e:˯:Z$]&v}ByxPdd?-&x>VP6e25~峁<Y7j^:0S
f?qgsUMj]g>7>Th*acفM/&6	T#whgqHy%i]1^)|VĖe1wT7 ;iihhpٳo@jdB7/Cn4&AQ/O)x{aJMW&\w<8E>V@y#$.M[;6vUp>;Q.'ܜ:6+ݫAkV0Z,-s-
x0q*#9"gu)ço<L	;OD'1(M⬵#g#5NR;8m Y8qfJ\1T5M5\|e:YpإP773dY7g7Ɍ+ڂ{rM8coQԔxald8Q2s%fAO$:Afd:Ȗ]<q8r~)QSHr.&,D6u-NVqJyHO1Iq}gcB|j:P<fB[$UltGs
,
+Fi`bKMw0"6\RVE>]M	HG9&2Q-VT)=x!޾9&Qzև|2ĜPU{MBV'  {,͋ے}yDxR
p{18=lڈ<??߻8ۛd
0jmCִap7m8j8ȧh!:XǲdZv+{(fns^r*?*ɒPxĩ*_iG{mM*._NRz;5FA`}#xUjbveSv{FҘ;J(&SO97nxW.nl=JUa-	ǝ6Kp^_X{ȯ?3/((C
9յX]5|dݰJ"
Nu
ϜBJtF#w9T#	nsnBQ֐0wʩ߬Cr8=+0:GaƙM^!N_/gh1(m=NŨVayTrQ'"B&fRJ"^qֆV1rr_~M
g](1^ּZc׈&<Ј\t$@{Quyf}Xr~Mo%x2M-wyHMY;cw{GI3
QKIxG.Mc`(&| T'cpgo `?JS;]SiǫP[&pV횸vaD1O91 ~bc*L@Elͧ螧ԤH/WK6RW 1#[<_E&D
eҟqRdnMYA:;moOV~X~[BНˮwv˵7=s;2wi{WR,u`~:b@
g*8)u{ķX) $#5ovǌZ'R r4j(nts;zbQoTgsJNbhYX[BNt%(,/9~1($*@7Oc(С#+<暭~Fx84vAG
 hDS{@(
+lQM]~ÛywŒfG{ڼRJ=iQRAhS^@jw^z&r2U5ֿga|3%8%~<,'rR)QRCsy9>={-	=[eZmxh/|xNKSWΫ[MM7/S_Lw#'kObRm\Zj?iwy/Rye+bG6A ښm`AF?a=&2յ/!1
"Yp}~aࢁP8QdlH$td=k*uP k_z	oY0iw`
aH-֮?~W󅆼|!6=a|=&B7W/pei~KEbcecb$)2/U~eL`k%l\\_To%.&Ӭ}}L>TɴTCbjG^sצqμhK.dXύmi5DPl<:LUbs8ϩq-48?8j[NɬK䟬t9iIJp_J#aA/H4Pūi Ta'ѿWNs+XCwY(`q%~
H'@h&]qZ%O7ڴq%(ϺzIi͋
"X;Ż	9sW*jutEN?!V$Ϳ$;%j\=(.{ݤu30ڸXr^dPă-ԺwCѧ[3Kei}&%2Ov:ZʙrTk3B..Tj2s%M'wBw#Q";=~HW"8~u5&G:. r`u^L6FiӆAԘEU.oɒ9[fGǊ
N^+h+Udg(ԌVr)uT:@naJ$Owۊh<M	Cs2+?U>s ڤ
|oZHXx
Çڣvq@8le?.O.1W"w{q"
p
\>atION駡Z[L̛_`\%W,-q@HQ4ꉽ?XjQ@T[ɗt\e/_`$xNisA)fUsLcB/<_muqaT#F<p<Pq·D9aNVI~ Nbw bJժQg>UcO$3䷻G~6-a[)QW:~tw'ed6.:Χ$ͻR
!#;+^[
V)"}PRth%lT,B	Y,XMV?^ZawaRC);{=/RC)'!n*־L1nS8wٯј"p^2ի3vZn|pA*)]/;2Gznn\9avda	v$cl;P҃Ov[Us@ ]5W].pإg#ٛ 
C[~ߠV.O߾x|ɧ_~8>l+ 	X),6(kp]v,
's<|fq'xP'ttt[$_`#yP>Bd:Y1N1IGG݃9zbxV\*wx;|}=8z}tx_^?WV.B,µo=^~(
of%v][!8
	#	oBq"TQ4gŨUB0q ]#Kb+S%T9?vC1,T:I區%5]Obր?ZaaI7{':B?RwÐ3kEMrȥwj+@Lod\Mh(  .Nhmag	]Eu؁la2	7 
VB%u"{[ṵ'LURsMbil~{+6?Z81=*Ow 6ylcO~䨈%'Ӡ!ɲ|$ɅM_V%օB
Bd\IQK#ے	5D"
W_++fzk<
Vx,TrN309VJۧvGғ~xN1\HS`:u[-ZZYo6#AʹoGvdZ 	v'O嶢j;OnQ͟\Y{('i
;'aFG֭
gZ?тVFeܰՐJqy
&wP
d1S ߩ3W{^M=!m92n~I(COZno"PuayaF)a}~~N/^^%;ƝWfe9gu3{w'cJ}h՛rS6@Zy,_ngjQ=uQvtL>h'Ũl^[#TgCH6)QXl?LlQ |FDMA2>$b
6$]bbTm혠1nG$ДZuSXR[d+ߵcft,Q쎲WKĄJ{'3<ڝN{즬%}f衼-RO_,	:̤[P
ghoIY Jv
KdV5}5!hlT ]'ifmq?K;YaM躈bewMMs%?&`l9ɬBMN
Aag؎btOws̚罔?rrҚePK@!RL7r2MԔѣ1XcY!DU]dLbXݥ|5u 9
$ʀeY|pJ}F~\J>_tVbHﲃUc颬JhÈv';츣uswp:{Gjгִ(/NMk*>}qK%ྵl=i+21Lb	d;&z ~xJ|hZz
'yrq2_6ʈ9n|RCT"
* {blr'Tʙbp
%p@$M%?4<l
ocXE!2Qܠ%^NPE/DE>RQ,=kYn'j<RlsNE%Vz׀oJ,nBѶTxnadϘ}X%{C!4)fEB6~?¹@DYQLQIoL}ӍB?[arHڼX`t_%%j^~/;C?Cy2uU7$Trm%Q74  ؅5LWަ|!Q-|$Iܛ3\bPy7#X,DN=e_bǼ<āT%9ьm+4z	½?o2-G=PPslxdp848KU-rz%߄l5M@uXqC5l<FNcLޱ6X$:o
$ƘFz[^S	s*SOF	rY*j.WqU^Xa}QcqWKnbk=Je2T/j`&Z^BՍ$ih6-B(&u$gb;VFf'IcPc@nى(b[>+μ:_8r*IuE68L:slDׇUZr?C\w/*eqk<2o8KB 5-$
}.:4p63q*kNCJ1ܻlm7q'0>ӫ\UA;zRkH%HV mU~!mY̱rrr* SbnVɊ8>:ܫ{X4R2BQPR'd7ren;
ջd= >v˔O@y{qsGT7[W]f9	HsB%X,(sd[9~r)7^3tsa SI)Fg>
CV_`vhLeN06,{:V]>sh^&*#r';gPj=_*J?'\RLJ#ېo	\F:C" y^^- Q:BPl<
<Bm=xzz1-$`A׻m2&zP;:Y26Yg 4R$w#ALYlX
y<G[^/j7<WPRb$iF{/XWatYzL$elJ]X`gGNG=XtEsf1&KGQfhJQR$dqbF	!q5-<Ƙ4%]7h;ӷZ1\I>&1cfFR\"gP>УiF D<u6`йrXd&hjv0E]^"^2h#^؇
BY]$M,?;
پʒۘ_PS-N5SnZԡwOa80Ƹe'h9Բ5a0*WU5[7(T6r<D!u	efC)waSe xlmHCjbɯIsTژ̳
i5vTw}p9+&&vSw1 FrsgAg/1ӎۘDwjcHP-!u>Ai,Yid.(8Y;TC_*c
"c2[ؠ$*+|7E\*	b~YR`gڿbVaI«Dq)|k
%ϓΎ_]4Ք}}LF:SF1#I5IVyX<{_~]+p"oʠeAKQ|ovI50)>@.xITBWe:F;A\9s|1䯐ˡčRWO7jaF*~Mj%VXoO|zzz?wKrd_0i5uj"ٻ`6W'ɰI8t~~[aoeʃ+/:"@Sob=cH[8Zz9Q"vm%['}UL2sLUIij|cg?L|O'YWWapQ.ᕕs-`2:ST}gQ#ʉK5,%o(>guuWldYEXx{Ur[%`{@&ecsVޫIA*2T5XU
.hO[lnWfR.-	>ʏ(ibO$B͑NWV-Aiu֫f]"Q;%W0'WY%!Ж/G24_p^@
eJ4k]NOxLyզ%1KY{1h"ig]%lUd-/eKCFgx%2u
;/0hY֐tf&mNsmZǵ"&E0\b|n,ĕ6:N3;J^C0GyIC.[-]UUT	9clA.l?<1E`zм=>	dø1fڜՙ|]fD5cW&\J:Yrpd*OĎ}4"e4&H} K8EMN.c
c5!<Br
'RLmbky
 9?JTF\M0`u@y7 R33#OXoo2'KFF]7h!{xmBp(у jƕ{g|WW%RzgMmXb7H0םgN
~vV$_Y;7	U`MXLbcq|BKb
Ap#pfV hR*V
~bejJ6qUjV1
}I )"xɛ1J_ZR~0_xw,kLIG{`817(k!˙@4h~KZ4bz{[`	Eꐪ]>V.`e3^04 &(|\EArg8	jόeZ'dgR`OjL\rZ0<FKH"겦4M
lZW$)L0ЏeY
IGb$pA'gǃm1rH1]Oݕ.X!u]OyNIe\`>C7	}b8;&m9lNFvӾqa9+da4hUEs
? Ne3EVkZ<ks[)>aϝ_2:T̴+!aXRd#V#?&7 mIlcٝN5=r$"iz4?(XR<k]Jƭ4Oч;G
( %;;kyOCpן{_^|lῠ OO'tnM cv`۶Wޮ>䗶|o8jb䟴g?Z![R4S` !+PBVСyjmh[7Eq]kf3uqXNgW"r1ʊWsŔ5!	VV"6ԉzDDyP-<^""Iv!&6/bn4<uzr(hϥiâ.a~5A5'J-N4KSۗ)\:̩slmX絃#gۻ7^Zm~bDvJ)kdF? ^Jzїf:e}vvu3K!| vhQIB<6z	@VuBI^-o+Lg8^CaՐ V6s4N1h޾9K\
׫3V\Akc_īHNS7f5Dt7);W)O/F[/"`[%6Hv16U>+ư /[o7>g8"A3'QJbGȟT#G]y@0Q9u/h>J[R+W^Dk2ƫG~rO`II
IP]Qj.y
÷Z*}:{A)
~IDPPg
5As1X~8
ں_+'p-R[:mjd}g S]rM=:#~%7 Y<hR+*sTSE&L'Ԉқ,31Gg3>99MIdKp6?έ
V'9ZbX2jt6wnF+aH?q
OρJt`wJ*3vA7 |
zG8܍oʖApA4ܾWO_(jg55 qJnaiv^P-kLM9?!9ה*PAy[X1z|9d)QMcuA'J}bgSr]Se֡b9JVw= ʄ!,fI^s {A~eGe$h9[˅BW&Z! &ұ/T+K	]BU`PrG[k"(OeZ#!*+r4ӉI{PІ\^'*
D92ۂD9rcףK2af#WM</BKa;l,xz824rvCW>[$$B.|BjRIz1ISQ.*FݟöE1@~ny_^'W%-gö+su"/KO)ϵ5ϧMnQnm!o)X7{X~P']	pɩ*R]1GY)lOI7efl|w2o61g GZ7Q)L#AAջ!?MÖ1~XzZA
52wu9ϫ&50>4D:J|5U[r,s2I_@: d ϋi;۬x"<|_$-ʱjqg3A&CAKj] 	ֵH!1gguAk7q:{T3lU}}+W 1Krl]hLUs2RNfHֺM5lQ]t6&5U$ ~
վ-p]U%1֌Ŗt4WTB5|,kqم塇.zlǓ%Ւ8L7A{/1f5q/2,e3ZBYlSyZ]pLΰtxstZG,O84IEPNQ\y皡:6w6	Y{~oXzJ;M@"eYM6G-C&g?N~[_O8۳MvО%&ps{!OӦގD	iBYs^3ԉل%%ШJ~'VCX"Mc J
	;{y6NNEhMOVŌ1*q
ެ_M)4(Vh^"TRd|]q;@0zД!s^Y
V0[OLE1*⨭%k(҅dIm*1s))7R}<16jr6][ձuLX`(c:b	tXRl>sIӺRKE7-nLa](i%&dV6ae;6gɩyڧ]/[/ZF`o;p}&8\˝>jh"6|w|j\wG[G-`nW\FcT׎ʋ|%nYhX
qQV.W6ܿkL!_ħ%?"Y3
rwwxc!_1,pQTmXt(SLg7r!W).<JՔW15&C6"8\Yo::Q5F%Z]rU"	x)Fi0#uR4)o{)nD!S?%kjV4*#{~r^H1|0DS+uϨZ;P6MFݽj_w` x44ep{2Eշ^-.YJ
d0j"-9%VBcኵDRk@ޯ"kd$9ޛ>}6ph"(	K8zjPQ_SFTSL\'O$r7Ŭ(Z͝Gy$ChFo5ZS:n!_d*hmMt?-S4ppz6^dbt:Ih1\%AxpoM/I7`E#MEٺ.k(СaoaFih5?hiCƓػu] !QA5乍L>U]BQn@'U]oѲaHI	=H;8/'V"'b8^oFIb<Mۗ
cqxfN ƔֳMFjxA=Ξj"ÁTlR1hun!o?p|zqJ攣2(-
rE*6 Sv#5F /ҧ`Sp>BBl*DG/Lwj@dg20Z_rp
A]$_w0D>G쐒[<(X>CD:4Q2SsVǭ>LK}B}m) )K0]=!`ɝ4bN.I"aQ:3z?!o--)m7Ky]{TNܲF6p	2P|.jRRΩK=@aZW9Ѫ$f!X+%^2A4*W1(OWe'Gu ,ϟ[q%vaİ?̵V
a(ǩz/;-=i}]Wu(́YSֻSĘ8QW?:bNN-g'Y~QqԸٲs[9)YXaM2oM֓V )u,EВ%a/JE{T1,@n6
/_;gfJٚd;U,Ni3bB,ai$eV&4U/$p]j"7tZjsXUXh
u )6}߆vuqYb
\%JWTvFMވn®Êc	ҶʽaV+RVZ8oNάIUC>f=U2FְSYPQٕyKik)uJR\;l33]QW_RstH/((_Zf'b%{ūOW+I5HHuzLvl+J_nBZ+贾k?9YL%/4R31^BɄʴqMQmV{^#CE8ArqRUāq@3+VbE~	CB7DןW69!7 JțdLFcEMB]#~Tr8ʆ&0e`9Ng
X`@4UލCOG;әp]!+ W,d-W_BY||&E(ܓhn
_Wp֡4<ͽv2+KS򻷐
IB*| '%(2)̃ۂ}
5sQl:+r#S.ʣycZpqiS|!7X^`d	bs_,¥0NzqGB (;&mqKKglg\rzeh`g_,B۾V,(&ʋ9Gala}*UB`ŌvF8ܲ3CiV)2yz+(C:E3UfHSfM[fGw6+@F6|+tZ\!Jd6^(;EFThYΐL4[JG:^嶜%XX$Z^#l2,CJ`FH[/6D0 Τb
dCIT۰&Kj
s?Ux
ۡm'|>LieB#U#5"3N2fYxgRW˹Aƚ?}hV#nÇ^5K*{T~d"Ǣ-]|"B3ROS]rՐl	<$1n jhA+ݓm5-UG#iznq+rtŚ+hnotVKRVV;8>C ShGj,SaAɝlTuoPs&9n*r+z3Xɴ?S$.B-8XQ.9ϔ<	W
j4Mc3tA8+ϊ5ʴ&TaLJ7Q`ww͟8.e\e֊x#GQj`:Dhzlӓ0
|;4Gbe6 B9aMíjFIRɔ=;쭣Һ/>Mռd٪TPs%NY"S3Lkvܪ.%VP\qV# >B,_(*ucD>5t53~JҌjIĉ&CQۉ9UlP͡XƂȮѨvo0xOjY[dLذM2V9 ZicR_z52kE&
<^I5~(meU<W˳'<9E.&B҅^V3VwpRr䰞SZtu+x,)7՗z%A	pFeؔO:'V5FXSƷAFE(ѫ;n"nTs[l܎v;aC>K
,,g:PGO!F87m-3Rʅ.#[O?O<)eH6/t*&T:5 R3>N毙;XYpyNFO%~)^w_f9GtvNmna߅Ee?7|v-2O)r&]@uze7**<>?|-+oq\J8qيĦp5+ћp09rUe)x^%L:@y`:AYWYIm+me%Lu*6FhsTIvL PYv,bH{zIF̩9$XST"Zqq5&gYOõ1Z!hT@/@0/2G+OvV7Z?]j<<7%A6Fš)e1\tcR9u7}A	O~\..(Պb߀1Z9k<rTZ%MsYKFb.zeBh`)}O81mWÖ*)E|yh,Gfο17J-޷oD߻{xt'pzY#g	+w^ FT"@G\Aa9\eWOrsJV~S)9}5B>aZ6^B{ź}MN$m@&Y5!AKb4f[BcA	W>.Fz}sG$|#Aʛ Ũ<חFьR|RX^uY?
tUJO5B{bLʞdp-JT*Y\VƢ;`(~F$!=>4sꕣSЃFK֑h51|mZK,<&?!7=ڰ0 >P=z1X<)G<GËC)AsCg,ݩ8@;1
ZIWHjajdtXrڬdź;枃p86^ȕa(
>m𙤒IT/+D<ޜft&E:w{=└C݌	I{ڬfE-8[v
Cz6(0͔UpUnp G=|Yf3-v3TLId4cd\{0$ܦMXvqٻKe1H^۬ҏuYTk1u\HDJ֊o}})yBbjBDʡ$#.q?݈J7o0?1:Ͼsњt'6Ra@H3=bʕXb}z
"9p/@3\0b_yuOzi
G-cn	ϨJ]EXْ4n LA	R$^)%SrRUAHQE2fL|-~jnV!`8:?e^#ZC͙9_5&yڔKD:>>=x<
sǦoYX[B1/qfܵyoͫA)c?{Wv[WǢiuhswFV9c0x%z0h)Es9sTZ,)HԵFdC0îͳ.LUi*,p-j^(^hbZk|MI@begC2PQ%ҥqC"'+&cVs\kzn
vP) )d׻=♜gZ65yCS]&K<Wu<g@S	pdtNej1BL9W\@"AWfZSEF8Nt1֭ј]0Iq@G@8B{{E1r ,	uhMIC _ՐTgӀx@6,?*:=@@9jLWI55~<zkb5fJnjm.jĆ<D
cԯbQ_ypbL\Q6L%q^;ǡSĵL[0qn%k^z_W 3@Ġm]g*
^cd?+A"O 8D
xm$E_"K-YJg= 7杔swp*8a7EƱ7u2*%Lr
6:v{u}3+琶Xu
G M?.I,ݫB",*a.6'آب>&ς\`CPKMv Fϗ%g ?ɞy/Q#UbQc׈7f8NZwr/\:-1 6Z4)w1oI%+4tPS9ZY~J^:s,+5;ҮMӾ&kr܇|ģsg{v@ޣ?Nq}%6 >?w'5qq1Ko{[dNzXE8X7K;Ma8NK{⚿WdGM~nۄ̘Q)}+/B2v$	7K[~ Y'GJqB!y!D0~ UD_Wi5ҸxYq)pr@E)FՐ{ih?s}=y<Xv,gW.}, @y	Aw-"SN;.bDY4Kܰd}"A k5-|C,(9eU<ks\;Ea9V~})
	"F`l?lXr+&7{Z}3E1q5<kڬsϗg+3?^-KQoT&C8>z3-fྣJV>SЮl~bIkΡ7RL65<2J@U@W,z.XZK>ꔕe%6iyf:,v^FH6$5y$-)?Һ7-`d&]ܸC~:)UhCbEͱ}M ː:6bKQXP;𦖘uvbgfCK LVEyAU]ɝOViJH5``n[
-+Z
tc\ Q=0Teց?zQ>'ߞ|y쯇oK?E.B,*>#9--LPej2oi{e暴Frdg⹦h^bdtmLŘjڵAfPXb'":=/VypU<>=uQ^iq0PNM aƓ6'܂ɇޚq4y||SOьw" c肩ܧ:wRC6!>A5vyjMZ`b(0Ķ'Ap(:FPJǑi.YN-mI>_bx
7˝0Tǧydˍ.N
dXnhzL:A	}v6QRQ̅܋~RR<4*tԢpFba,5XWжm}&TWR!8>FJZN N䭝UwLq☽"psZqmhfF,NӒxZ
ig

C\KܙVWa}>nb4T~[$7!e7-["k&`jFsg1;Nf׫RoG{CMnT+}hm|,%{=dILC+\Qb	\~ZΤZyU@Kxgi	T7ԿCPۘP$koZ36%ǵ&BQ6I8D8ކ&CF͞jz뛪tm-:w
0%^Z\0
~զ
=CIE5gniNu
vEuk>vM$Yq7iL?[q]o6%C;Ty$ʟr<x[GH8}uGC1oqhq׼$l@h \&yN{pxe vVâi]0UK3h
<Œʓ.%Wr0%R送@cYA]a&ad]j
zXUop*ȯUc@&̜cϓʘg37ou/nz(I87\GWE6ڈwO4w`!tf8K.盳ԤnJM\n#h"g_0!Wsvq|yNUX\@O		dɇDL-gSd,ȳjyEI\nXڦJ<r4VHцζ3df	9?&{(U=r\\u
io{87Cp)wj&.0^]-I[1H{e#Tԉ!5/ri?=`u&u$FWd*OĔ.IR+="	2޲HށN?'Uz+-O--)I2sQH<DySA)@ov*xu4}1e,ƖOADiIG/"Ϝ(>
>z0fX8-CR3GqTjx$րfnD*/,ZSdT>DZ;;fuŶ5x!3Orhką!a|h͝&_-Ow[=
jR뢔
<x7s=W(S^:śJ-ZeuXSx4=W!|*fhj>tr,J$4GUX&>G[҆7+,Z|4àGB6O
]dKtEr*`Jرl=c̚ZLϝ2)l<0[lyAܾ&'
Bo=|}qD?W%ФBB񍡎|Y'N`;H +h!6XԟZ<s)6((=ׄ1O?ܗ0l"=O[0{Yԧi҂A^sl=K>7ӻz)]ӑ舱wGs+؅9YWEoU/(b@zwmA=eZ
oqV@o 8
1i;Fryy~#W'~tf3M"$.\|h~fu}c
z{>Vd5?"1F̝)(6mzf'oUgo3x/ޟ8n:,U\7jbޔUKf
Ci3]cf~qݪq4
Y蘎pոX6&O>RH&wN[IP SS:{K̗Y%N*</G3-+78捪a[=!lObT.>=E
c4Nu >sמc8A10|8$
(+@BY-POx?S|ʚ?)Go`1a]i4ĚX$
I06q
^P 7X;)ٓ|$shZ3CF]'߽zyބw?[                                                                                                                                                                                                                  

    3Gb                     d   d dl Z d dlZd dlmZ ddlmZmZ ddlmZm	Z	 ddl
mZmZm
Z
mZ ddlmZ d dlmZ  ed	          Zej        d
k    Zej        dk    Zej        dk    Zg ZeZe                    d
           e	Z	e                    d           erdZ ej        ee            G d de          Ze                    d           eserd Ze                    d           d Z G d de           Z! G d dej"                  Z" ee"          Z"e                    d           eser5 G d dej#        e"          Z# ee#          Z#e                    d           ne Z# G d dej$                  Z$ ee$          Z$e                    d           eser G d dej%                  Z% ee%          Z%e                    d            G d d ej&                  Z& ee&          Z&e                    d             G d! d"ej'                  Z' ee'          Z'e                    d"            G d# d$ej(                  Z( ee(          Z(e                    d$            G d% d&ej)        e#          Z) ee)          Z)e                    d&           eser3 G d' d(ej*                  Z* ee*          Z*e                    d(           eser3 G d) d*ej+                  Z+ ee+          Z+e                    d*           eser3 G d+ d,ej,                  Z, ee,          Z,e                    d,           d- Z- G d. d/ej.                  Z. ee.          Z.e                    d/           er
ej/        j0        Z1n e
ej/        j0        d0ed12          Z1 G d3 d4ej/                  Z/ ee/          Z/e                    d4            G d5 d6ej2        e#          Z2 ee2          Z2e                    d6           eser4 G d7 d8ej3        e2          Z3 ee3          Z3e                    d8           eserf G d9 d:ej4                  Z4 ee4          Z4e                    d:            G d; d<ej5                  Z5 ee5          Z5e                    d<           eser3 G d= d>ej6                  Z6 ee6          Z6e                    d>           eser3 G d? d@ej7                  Z7 ee7          Z7e                    d@            G dA dBej8                  Z8 ee8          Z8e                    dB           eser3 G dC dDej9                  Z9 ee9          Z9e                    dD            G dE dFej:                  Z: ee:          Z:e                    dF            G dG dHej;                  Z; ee;          Z;e                    dH            G dI dJej<                  Z< ee<          Z<e                    dJ            G dK dLej=                  Z= ee=          Z=e                    dL            G dM dNej>                  Z> ee>          Z>e                    dN            G dO dPej?                  Z? ee?          Z?e                    dP           eser3 G dQ dRej@                  Z@ ee@          Z@e                    dR            G dS dTejA        e>e?          ZA eeA          ZAe                    dT            G dU dVe           ZBe                    dV            G dW dXe           ZCe                    dX            G dY dZejD                  ZD eeD          ZDe                    dZ            G d[ d\ejE        e>e?          ZE eeE          ZEe                    d\            G d] d^ejF        e#          ZF eeF          ZFe                    d^            G d_ d`ejG                  ZG eeG          ZGe                    d`            G da dbejH                  ZH eeH          ZHe                    db           eser G dc ddejI        e#          ZI eeI          ZIe                    dd            G de dfejJ                  ZJ eeJ          ZJe                    df            G dg dhejK                  ZK eeK          ZKe                    dh            G di djejL                  ZL eeL          ZLe                    dj           eserg G dk dlejM        e#          ZM eeM          ZMe                    dl            G dm dnejN                  ZN eeN          ZNe                    dn           eserf G do dpejO                  ZO eeO          ZOe                    dp            G dq drejP                  ZP eeP          ZPe                    dr           eser3 G ds dtejQ                  ZQ eeQ          ZQe                    dt           eser G du dvejR                  ZR eeR          ZRe                    dv            G dw dxejS                  ZS eeS          ZSe                    dx            G dy dzejT                  ZT eeT          ZTe                    dz            G d{ d|ejU                  ZU eeU          ZUe                    d|           er3 G d} d~ejV                  ZV eeV          ZVe                    d~           er3 G d dejW                  ZW eeW          ZWe                    d           eser@ejX        ZY eejX                  d             ZXejZ        Z[ eejZ                  d             ZZeser% eej\                  Z\e                    d           ere]                                Z^dS e]                    e j_                  \  Z^Z_ e`e_          e __        dS )    N)GObject   )wakeup_on_signalregister_sigint_fallback)Template_extract_handler_and_args)overridestrip_boolean_resultdeprecated_initwrap_list_store_sort_func)get_introspection_module)PyGIDeprecationWarningGtkz2.0z3.0z4.0r   r   aB  You have imported the Gtk 2.0 module.  Because Gtk 2.0 was not designed for use with introspection some of the interfaces and API will fail.  As such this is not supported by the pygobject development team and we encourage you to port your app to Gtk 3 or greater. PyGTK is the recomended python module to use with Gtk 2.0c                       e Zd ZdS )PyGTKDeprecationWarningN)__name__
__module____qualname__     2/usr/lib/python3/dist-packages/gi/overrides/Gtk.pyr   r   <   s        Dr   r   c                     g }| D ]D}t          |t          j                  st          j        j        | }|                    |           E|S )zCreate a list of TargetEntry items from a list of tuples in the form (target, flags, info)

        The list can also contain existing TargetEntry items in which case the existing entry
        is re-used in the return list.
        )
isinstancer   TargetEntrynewappend)targetstarget_entriesentrys      r   _construct_target_listr    D   sX      	) 	)EeS_55 
4+U3!!%((((r   r    c                     t          ||          \  }}|t          j        j        z  }	|&|	r |j        |||g|R   d S  |j        |||g|R   d S |	r |j        ||g|R   d S  |j        ||g|R   d S N)r   r   ConnectFlagsAFTERconnect_object_afterconnect_object
connect_afterconnect)
buildergobjsignal_namehandler_nameconnect_objflags
obj_or_maphandlerargsafters
             r   _builder_connect_callbackr3   T   s    -j,GGMGTG(..E 	J%D%k7KO$OOOOOODWkIDIIIIII 	6D{G;d;;;;;;DLg5555555r   c                        e Zd Zd Zd Zd ZdS )_FreezeNotifyManagerc                     || _         d S r"   )obj)selfr7   s     r   __init__z_FreezeNotifyManager.__init__e   s
    r   c                     d S r"   r   r8   s    r   	__enter__z_FreezeNotifyManager.__enter__h   s    r   c                 8    | j                                          d S r"   )r7   thaw_child_notify)r8   exc_type	exc_value	tracebacks       r   __exit__z_FreezeNotifyManager.__exit__k   s    ""$$$$$r   N)r   r   r   r9   r<   rB   r   r   r   r5   r5   d   sA          
 
 
% % % % %r   r5   c                        e Zd Z eej        j                  Zerd Zd Z	e
ser fdZe
ser fdZ
e
ser fdZe
serddZ xZS  xZS )	Widgetc                 $    |t          |           v S r"   )listr8   childs     r   __contains__zWidget.__contains__t   s    DJJ&&r   c              #   n   K   |                                  }|r|V  |                                }|d S d S r"   )get_first_childget_next_siblingrG   s     r   __iter__zWidget.__iter__w   sW      ((**E 
1..00  
1 
1 
1 
1 
1r   c                 p    t          t          |                                            t          |           S r"   )superrD   freeze_child_notifyr5   )r8   	__class__s    r   rP   zWidget.freeze_child_notify~   s-    &$33555'---r   c                     |Ft          |t          j                  s,t          j                            t	          |                    }t          t          |                               |           d S r"   )r   r   
TargetListr   r    rO   rD   drag_dest_set_target_listr8   target_listrQ   s     r   rT   z Widget.drag_dest_set_target_list   s\    '*[#.2Y2Y'!n001G1T1TUU&$99+FFFFFr   c                     |Ft          |t          j                  s,t          j                            t	          |                    }t          t          |                               |           d S r"   )r   r   rS   r   r    rO   rD   drag_source_set_target_listrU   s     r   rX   z"Widget.drag_source_set_target_list   s\    '*[#.2Y2Y'!n001G1T1TUU&$;;KHHHHHr   Nc                     |F|                      |          }|t          d| d|d          t          j        |j                  }t
          j                            | ||           |                                S )NClass "z#" does not contain style property "")	find_style_property
ValueErrorr   Value
value_typer   rD   style_get_property	get_value)r8   
property_namevalueprops       r   r`   zWidget.style_get_property   s    }//
>><$*&*ddMMM&; < < <
do66J))$
uEEE??$$$r   r"   )r   r   r   r
   r   rD   translate_coordinatesGTK4rI   rM   GTK2GTK3rP   rT   rX   r`   
__classcell__rQ   s   @r   rD   rD   o   s        001QRR 1	' 	' 	'	1 	1 	1  .t .	. 	. 	. 	. 	.  Gt G	G 	G 	G 	G 	G
  It I	I 	I 	I 	I 	I
  
%t 
%		% 		% 		% 		% 		% 		% 		% 		%
% 
% 
% 
%r   rD   c                   h    e Zd Zd Zd Zd Zd ZeZd	dZd Z	d Z
 eej
        j                  ZdS )
	Containerc                 D    t          |                                           S r"   )lenget_childrenr;   s    r   __len__zContainer.__len__   s    t((**+++r   c                 .    ||                                  v S r"   )ro   rG   s     r   rI   zContainer.__contains__   s    D--////r   c                 D    t          |                                           S r"   )iterro   r;   s    r   rM   zContainer.__iter__   s    ))++,,,r   c                     dS NTr   r;   s    r   __bool__zContainer.__bool__   s    4r   Nc                     |F|                      |          }|t          d| d|d          t          j        |j                  }t
          j                            | |||           |                                S )NrZ   z#" does not contain child property "r[   )	find_child_propertyr]   r   r^   r_   r   rl   child_get_propertyra   )r8   rH   rb   rc   rd   s        r   ry   zContainer.child_get_property   s    }//
>><$*&*ddMMM&; < < <
do66M,,T5-OOO??$$$r   c                 $      fd|D             S )z<Returns a list of child property values for the given names.c                 <    g | ]}                     |          S r   )ry   ).0namerH   r8   s     r   
<listcomp>z'Container.child_get.<locals>.<listcomp>   s)    PPPTD++E488PPPr   r   )r8   rH   
prop_namess   `` r   	child_getzContainer.child_get   s!    PPPPPZPPPPr   c                     |                                 D ]2\  }}|                    dd          }|                     |||           3dS )z=Set a child properties on the given child to key/value pairs._-N)itemsreplacechild_set_property)r8   rH   kwargsr}   rc   s        r   	child_setzContainer.child_set   sU    %||~~ 
< 
<e||C--''tU;;;;
< 
<r   r"   )r   r   r   rp   rI   rM   rv   __nonzero__ry   r   r   r
   r   rl   get_focus_chainr   r   r   rl   rl      s        	, 	, 	,	0 	0 	0	- 	- 	-	 	 	 		% 		% 		% 		%	Q 	Q 	Q	< 	< 	< /.s}/LMMr   rl   c                   L     e Zd Z fdZ eej        j        d          Z xZS )Editablec                 X    t          t          |                               |d|          S N)rO   r   insert_text)r8   textpositionrQ   s      r   r   zEditable.insert_text   s%    Xt$$00r8DDDr   r   fail_ret)	r   r   r   r   r
   r   r   get_selection_boundsri   rj   s   @r   r   r      sS        E E E E E 0/0Q\^___r   r   c                   >    e Zd Z eej        j        de          ZdS )Actionr}   labeltooltipstock_id	arg_namescategoryN)r   r   r   r   r   r   r9   r   r   r   r   r   r      s4        "?3:#6-U,CE E Er   r   c                   >    e Zd Z eej        j        de          ZdS )RadioActionr}   r   r   r   rc   r   N)r   r   r   r   r   r   r9   r   r   r   r   r   r      s4        "?3?#;-^,CE E Er   r   c                   V    e Zd Z eej        j        de          ZddZddZ	ddZ
dS )	ActionGroup)r}   r   Nc                      	 t          |           n# t          $ r t          d          w xY wd fd	}|D ]} ||  dS )a  
            The add_actions() method is a convenience method that creates a number
            of gtk.Action  objects based on the information in the list of action
            entry tuples contained in entries and adds them to the action group.
            The entry tuples can vary in size from one to six items with the
            following information:

                * The name of the action. Must be specified.
                * The stock id for the action. Optional with a default value of None
                  if a label is specified.
                * The label for the action. This field should typically be marked
                  for translation, see the set_translation_domain() method. Optional
                  with a default value of None if a stock id is specified.
                * The accelerator for the action, in the format understood by the
                  gtk.accelerator_parse() function. Optional with a default value of
                  None.
                * The tooltip for the action. This field should typically be marked
                  for translation, see the set_translation_domain() method. Optional
                  with a default value of None.
                * The callback function invoked when the action is activated.
                  Optional with a default value of None.

            The "activate" signals of the actions are connected to the callbacks and
            their accel paths are set to <Actions>/group-name/action-name.
            entries must be iterableNc                     t          | |||          }|0|                    d|           n|                    d|                               ||           d S Nr   activate)r   r(   add_action_with_accel)	r}   r   r   acceleratorr   callbackactionr8   	user_datas	          r   _process_actionz0ActionGroup.add_actions.<locals>._process_action  so    TRZ[[[' (z8<<<<z8YGGG**6;?????r   )NNNNNrs   	TypeErrorr8   entriesr   r   es   ` `  r   add_actionszActionGroup.add_actions   s    4
<W



 
< 
< 
< :;;;
<
@ 
@ 
@ 
@ 
@ 
@ 
@  
$ 
$###
$ 
$    .c                      	 t          |           n# t          $ r t          d          w xY wd fd	}|D ]} ||  dS )a  
            The add_toggle_actions() method is a convenience method that creates a
            number of gtk.ToggleAction objects based on the information in the list
            of action entry tuples contained in entries and adds them to the action
            group. The toggle action entry tuples can vary in size from one to seven
            items with the following information:

                * The name of the action. Must be specified.
                * The stock id for the action. Optional with a default value of None
                  if a label is specified.
                * The label for the action. This field should typically be marked
                  for translation, see the set_translation_domain() method. Optional
                  with a default value of None if a stock id is specified.
                * The accelerator for the action, in the format understood by the
                  gtk.accelerator_parse() function. Optional with a default value of
                  None.
                * The tooltip for the action. This field should typically be marked
                  for translation, see the set_translation_domain() method. Optional
                  with a default value of None.
                * The callback function invoked when the action is activated.
                  Optional with a default value of None.
                * A flag indicating whether the toggle action is active. Optional
                  with a default value of False.

            The "activate" signals of the actions are connected to the callbacks and
            their accel paths are set to <Actions>/group-name/action-name.
            r   NFc                     t                               | |||          }|                    |           |0	|                    d|           n|                    d|	                               ||           d S r   )r   ToggleAction
set_activer(   r   )
r}   r   r   r   r   r   	is_activer   r8   r   s
           r   r   z7ActionGroup.add_toggle_actions.<locals>._process_action?  s    ))t5'\d)ee!!),,,' (z8<<<<z8YGGG**6;?????r   )NNNNNFr   r   s   ` `  r   add_toggle_actionszActionGroup.add_toggle_actions  s    :
<W



 
< 
< 
< :;;;
<	
@ 	
@ 	
@ 	
@ 	
@ 	
@ 	
@  
$ 
$###
$ 
$r   c                     	 t          |           n# t          $ r t          d          w xY wd}d fd	}|D ]} ||g|R  }||}|5|5||                    d|           dS |                    d||           dS dS dS )a  
            The add_radio_actions() method is a convenience method that creates a
            number of gtk.RadioAction objects based on the information in the list
            of action entry tuples contained in entries and adds them to the action
            group. The entry tuples can vary in size from one to six items with the
            following information:

                * The name of the action. Must be specified.
                * The stock id for the action. Optional with a default value of None
                  if a label is specified.
                * The label for the action. This field should typically be marked
                  for translation, see the set_translation_domain() method. Optional
                  with a default value of None if a stock id is specified.
                * The accelerator for the action, in the format understood by the
                  gtk.accelerator_parse() function. Optional with a default value of
                  None.
                * The tooltip for the action. This field should typically be marked
                  for translation, see the set_translation_domain() method. Optional
                  with a default value of None.
                * The value to set on the radio action. Optional with a default
                  value of 0. Should be specified in applications.

            The value parameter specifies the radio action that should be set
            active. The "changed" signal of the first radio action is connected to
            the on_change callback (if specified and not None) and the accel paths
            of the actions are set to <Actions>/group-name/action-name.
            r   Nr   c                     t          |||||          }t          r|                    |            	|k    r|                    d                               ||           |S )Nr   T)r   rh   
join_groupr   r   )
group_sourcer}   r   r   r   r   entry_valuer   r8   rc   s
           r   r   z6ActionGroup.add_radio_actions.<locals>._process_actionq  ss    $$eWW_grsss 4%%l333K''%%d+++**6;???
r   changed)NNNNr   )rs   r   r(   )	r8   r   rc   	on_changer   first_actionr   r   r   s	   ` `      r   add_radio_actionszActionGroup.add_radio_actionsN  s    8
<W



 
< 
< 
< :;;;
<  L

 

 

 

 

 

 

  
* 
*(::::'#)L'I,A$ ((I>>>>> ((IyIIIII	 (',A,Ar   r"   )NNN)r   r   r   r   r   r   r9   r   r   r   r   r   r   r   r   r      s~        "?3?#;-6,CE E E+	$ +	$ +	$ +	$Z/	$ /	$ /	$ /	$b9	J 9	J 9	J 9	J 9	J 9	Jr   r   c                       e Zd Zd ZddZdS )	UIManagerc                     t          |t                    st          d          t          |          }t          j                            | ||          S Nzbuffer must be a string)r   strr   _get_utf8_lengthr   r   add_ui_from_stringr8   bufferlengths      r   r   zUIManager.add_ui_from_string  sJ    fc** 
; 9:::%f--F=33D&&IIIr   r   c                 D    t           j                            | ||          S r"   )r   r   insert_action_groupr   s      r   r   zUIManager.insert_action_group  s    =44T66JJJr   Nr   )r   r   r   r   r   r   r   r   r   r     s@        	J 	J 	J	K 	K 	K 	K 	K 	Kr   r   c                   8    e Zd Z eej        j                  ZdS )ComboBoxN)r   r   r   r
   r   r   get_active_iterr   r   r   r   r     s#        **3<+GHHOOOr   r   c                   >    e Zd Z eej        j        de          ZdS )Box)homogeneousspacingr   N)r   r   r   r   r   r   r9   r   r   r   r   r   r     s4        "?37#3-G,CE E Er   r   c                   X    e Zd Z eej        j        ddej        j        ie	          ZdS )	SizeGroup)moder   )r   deprecated_defaultsr   N)
r   r   r   r   r   r   r9   
SizeGroupModeVERTICALr   r   r   r   r   r     sB        "?3=#9-68>@Q@Z7[,CE E Er   r   c                   >    e Zd Z eej        j        de          ZdS )MenuItemr   r   N)r   r   r   r   r   r   r9   r   r   r   r   r   r     s4        "?3<#8-7,CE E Er   r   c                     t          | t                    sJ t          | t                    s|                     d          } t	          |           S )Nzutf-8)r   r   bytesencodern   )strings    r   r   r     sG    fc"""""fe$$ (w''v;;r   c                   X     e Zd ZerddlmZ  e            Zd fd	Znd Zd Z	d Z
 xZS )	Builderr   )define_builder_scopeNc                     t          t          |                                            |r/|                     t                              |                     d S d S r"   )rO   r   r9   	set_scopeBuilderScope)r8   scope_object_or_maprQ   s     r   r9   zBuilder.__init__  s[    '4  ))+++" 
Jw334GHHIIIII
J 
Jr   c                 <    |                      t          |           dS )a  Connect signals specified by this builder to a name, handler mapping.

            Connect signal, name, and handler sets specified in the builder with
            the given mapping "obj_or_map". The handler/value aspect of the mapping
            can also contain a tuple in the form of (handler [,arg1 [,argN]])
            allowing for extra arguments to be passed to the handler. For example:

            .. code-block:: python

                builder.connect_signals({'on_clicked': (on_clicked, arg1, arg2)})
            N)connect_signals_fullr3   )r8   r/   s     r   connect_signalszBuilder.connect_signals  s!     
%%&?LLLLLr   c                     t          |t                    st          d          t          |          }t          j                            | ||          S r   )r   r   r   r   r   r   add_from_stringr   s      r   r   zBuilder.add_from_string  sJ    &#&& 	75666!&)){**4@@@r   c                     t          |t                    st          d          t          |          }t          j                            | |||          S r   )r   r   r   r   r   r   add_objects_from_string)r8   r   
object_idsr   s       r   r   zBuilder.add_objects_from_string  sL    &#&& 	75666!&)){224TTTr   r"   )r   r   r   rf   _gtktemplater   r   r9   r   r   r   ri   rj   s   @r   r   r     s         M777777++--	J 	J 	J 	J 	J 	J 	J	M 	M 	MA A AU U U U U U Ur   r   )type   )r   r   
stacklevelc                       e Zd Zd ZdS )Windowc                 R    t           st          d          t          | g|R i | d S )NzRGtk couldn't be initialized. Use Gtk.init_check() if you want to handle this case.)initializedRuntimeError_window_initr8   r1   r   s      r   r9   zWindow.__init__  sL     	IHI I 
I 	T+D+++F+++++r   N)r   r   r   r9   r   r   r   r   r     s#        , , , , ,r   r   c                       e Zd Zeser=dZ eej        j	        ddddde
          Zd Z	d	 Z e
d
           Z e
d           Zd Zd
S )Dialog)titleparentr.   buttons_buttons_property)r  
transient_forr.   add_buttonsr  )r.   r  r  r  )r  r  )r   ignoredeprecated_aliasesr   c                    |                                 }t          t          | j        |                    }|                    |           d}| j        t          k    r| j        j        t          j        k    r|dz
  }|                    dd           }|Mt          |t          j                  s3t          j
        dt          |           |                    dd            nd }|                    dd          }|rNt          j
        dt          |           |t          j        j        z  rd	|d
<   |t          j        j        z  rd	|d<    | j        |i | |r | j        |  d S d S )Nr      r  zThe "buttons" argument must be a Gtk.ButtonsType enum value. Please use the "add_buttons" method for adding buttons. See: https://wiki.gnome.org/PyGObject/InitializerDeprecationsr   r.   r   zThe "flags" argument for dialog construction is deprecated. Please use initializer keywords: modal=True and/or destroy_with_parent=True. See: https://wiki.gnome.org/PyGObject/InitializerDeprecationsTmodaldestroy_with_parent)copydictzip_old_arg_namesupdaterQ   r  r9   getr   r   ButtonsTypewarningswarnr   popDialogFlagsMODALDESTROY_WITH_PARENT_initr  )r8   r1   r   
new_kwargs
old_kwargsr   r  r.   s           r   r9   zDialog.__init__  s   Jc$"5t<<==Jf%%% J~''DN,Cv,V,Va

 %..D99K&z+s/W/W&
 ^ 6*N N N N y$////"NN7A..E 

=
 ^ 6*N N N N
 3?00 /*.Jw'3?>> =8<J45DJ+
+++ 
/  +....
/ 
/r   c                     t          | j                  5  t                      5  t          j        j        | g|R i |cd d d            cd d d            S # 1 swxY w Y   	 d d d            d S # 1 swxY w Y   d S r"   )r   destroyr   r   r  runr   s      r   r!  z
Dialog.runF  sQ   )$,77 
A 
A%'' A A:>$@@@@@@A A A A A A A
A 
A 
A 
A 
A 
A 
A 
AA A A A A A A A A
A 
A 
A 
A 
A 
A 
A 
A 
A 
A 
A 
A 
A 
A 
A 
A 
A 
As3   A0AA0A	A0A	A00A47A4c                 *    |                                  S r"   )get_action_areadialogs    r   <lambda>zDialog.<lambda>K  s    f.D.D.F.F r   c                 *    |                                  S r"   )get_content_arear$  s    r   r&  zDialog.<lambda>L  s    v'>'>'@'@ r   c                 Z    d } ||          D ]\  }}|                      ||           dS )a  
        The add_buttons() method adds several buttons to the Gtk.Dialog using
        the button data passed as arguments to the method. This method is the
        same as calling the Gtk.Dialog.add_button() repeatedly. The button data
        pairs - button text (or stock ID) and a response ID integer are passed
        individually. For example:

        .. code-block:: python

            dialog.add_buttons(Gtk.STOCK_OPEN, 42, "Close", Gtk.ResponseType.CLOSE)

        will add "Open" and "Close" buttons to dialog.
        c              3      K   | r@	 | dd         \  }}n# t           $ r t          d          w xY w| dd          } ||fV  | >d S d S )Nr   r   z%Must pass an even number of arguments)r]   )btrs      r   _buttonz#Dialog.add_buttons.<locals>._button\  s       
NQqS6DAqq! N N N$%LMMMNabbEd



  
 
 
 
 
s   
 .N)
add_button)r8   r1   r.  r   responses        r   r  zDialog.add_buttonsN  sO    	 	 	 &gdmm 	, 	,ND(OOD(++++	, 	,r   N)r   r   r   rg   rh   r  r   r   r  r9   r   r  r!  propertyaction_areavboxr  r   r   r   r  r    s         :Bt :BU
 3+E'?EM?R4T 4T)@
B B B(	/ (	/ (	/T	A 	A 	A
 hFFGGx@@AA, , , , ,r   r  c                   R    e Zd Z eej        j        dddde          Zd Zd Z	dS )	
MessageDialog)r  r.   message_typer  message_formatr7  r   )r   r6  r   r	  r   c                 ^    |                      dd           |                      d|           d S )Nsecondary-use-markupFsecondary-textset_propertyr8   r7  s     r   format_secondary_textz#MessageDialog.format_secondary_textv  s6    4e<<<.?????r   c                 ^    |                      dd           |                      d|           d S )Nr:  Tr;  r<  r>  s     r   format_secondary_markupz%MessageDialog.format_secondary_markupz  s6    4d;;;.?????r   N)
r   r   r   r   r   r5  r9   r   r?  rA  r   r   r   r5  r5  n  sv        "?3#4#=.K?OGM7O 7O,CE E E	@ 	@ 	@	@ 	@ 	@ 	@ 	@r   r5  c                   >    e Zd Z eej        j        de          ZdS )ColorSelectionDialogr  r   N)r   r   r   r   r   rC  r9   r   r   r   r   rC  rC    s5        "?3#;#D-7,CE E Er   rC  c                   >    e Zd Z eej        j        de          ZdS )FileChooserDialog)r  r  r   r  r   N)r   r   r   r   r   rF  r9   r   r   r   r   rF  rF    s5        "?3#8#A-U,CE E Er   rF  c                   >    e Zd Z eej        j        de          ZdS )FontSelectionDialogrD  r   N)r   r   r   r   r   rH  r9   r   r   r   r   rH  rH    s5        "?3#:#C-7,CE E Er   rH  c                   D    e Zd Z eej        j        dddie          ZdS )RecentChooserDialog)r  r  recent_managerr  rK  managerr8  N)r   r   r   r   r   rJ  r9   r   r   r   r   rJ  rJ    s?         #?3#:#C-]7G6S,CE E Er   rJ  c                       e Zd Zeser eej        j        de	          Z e
ej        j                  Z e
ej        j                  Z e
ej        j
                  Z
dS )IconViewmodelr   N)r   r   r   rg   rh   r   r   rN  r9   r   r
   get_item_at_posget_visible_rangeget_dest_item_at_posr   r   r   rN  rN    s         Et E"?3<#8-7,CE E E +*3<+GHHO,,S\-KLL//0QRRr   rN  c                   >    e Zd Z eej        j        de          ZdS )
ToolButton)r   r   N)r   r   r   r   r   rU  r9   r   r   r   r   rU  rU    s4        "?3>#:-:,CE E Er   rU  c                   8    e Zd Z eej        j                  ZdS )	IMContextN)r   r   r   r
   r   rW  get_surroundingr   r   r   rW  rW    s#        **3=+HIIOOOr   rW  c                   8    e Zd Z eej        j                  ZdS )
RecentInfoN)r   r   r   r
   r   rZ  get_application_infor   r   r   rZ  rZ    s&        //0STTr   rZ  c                   p    e Zd Zd
dZddZddZddZd Zd	 Zdd
Z	 e
ej        j
        d          Z
dS )
TextBufferNc                 z    t          j        dd|i|}|                                                     |           |S )a  Creates a tag and adds it to the tag table of the TextBuffer.

        :param str tag_name:
            Name of the new tag, or None
        :param **properties:
            Keyword list of properties and their values

        This is equivalent to creating a Gtk.TextTag and then adding the
        tag to the buffer's tag table. The returned tag is owned by
        the buffer's tag table.

        If ``tag_name`` is None, the tag is anonymous.

        If ``tag_name`` is not None, a tag called ``tag_name`` must not already
        exist in the tag table for this buffer.

        Properties are passed as a keyword list of names and values (e.g.
        foreground='DodgerBlue', weight=Pango.Weight.BOLD)

        :returns:
            A new tag.
        r}   r   )r   TextTag
get_tag_tableadd)r8   tag_name
propertiestags       r   
create_tagzTextBuffer.create_tag  sC    0 k66x6:66  %%%
r   Fc                 F    t           j                            | |||          S r"   )r   r]  create_mark)r8   	mark_namewhereleft_gravitys       r   rg  zTextBuffer.create_mark  s    ~))$	5,OOOr   r   c                 H    t           j                            | ||           d S r"   )r   r]  set_textr8   r   r   s      r   rl  zTextBuffer.set_text  s"    dF33333r   c                     t          |t                    st          dt          |          z            t          j                            | |||           d S Nztext must be a string, not %s)r   r   r   r   r   r]  insert)r8   rs   r   r   s       r   rp  zTextBuffer.insert  sQ    $$$ 	J;d4jjHIIIdD$77777r   c                     |                                 }|                     ||           |sd S |                     |          }|D ]}|                     |||           d S r"   )
get_offsetrp  get_iter_at_offset	apply_tag)r8   rs   r   tagsstart_offsetstartrd  s          r   insert_with_tagszTextBuffer.insert_with_tags  sw    ((D$ 	F''55 	- 	-CNN3t,,,,	- 	-r   c                     g }|D ]R}|                                                      |          }|st          d|z            |                    |           S | j        ||g|R   d S )Nzunknown text tag: %s)r`  lookupr]   r   rx  )r8   rs   r   ru  tag_objsrd  tag_objs          r   insert_with_tags_by_namez#TextBuffer.insert_with_tags_by_name
  s     	% 	%C((**11#66G 
? !7#!=>>>OOG$$$$dD48444444r   c                     t          |t                    st          dt          |          z            t          j                            | ||           d S ro  )r   r   r   r   r   r]  insert_at_cursorrm  s      r   r  zTextBuffer.insert_at_cursor  sO    $$$ 	J;d4jjHIII''dF;;;;;r   r   r   r"   )Fr   )r   r   r   re  rg  rl  rp  rx  r}  r  r
   r   r]  r   r   r   r   r]  r]    s           8P P P P4 4 4 48 8 8 8
- 
- 
-	5 	5 	5< < < < 0/0S^`aaar   r]  c                   b    e Zd Z eej        j                  Z eej        j                  ZdS )TextIterN)r   r   r   r
   r   r  forward_searchbackward_searchr   r   r   r  r  "  s8        ))#,*EFFN**3<+GHHOOOr   r  c                       e Zd Zd Zd ZesereZd Z fdZ	d Z
d Zd Zd Z
d	 Z eej        j                  Z eej        j                  Z eej        j                  Z eej        j                  Z eej        j        ed
          Z fdZ fdZ fd
Zd Zd Zd Zd Z fdZ fdZ  fdZ! fdZ" fdZ# xZ$S )	TreeModelc                 ,    |                      d           S r"   )iter_n_childrenr;   s    r   rp   zTreeModel.__len__,  s    ##D)))r   c                     dS ru   r   r;   s    r   rv   zTreeModel.__bool__/  s    tr   c                 `   t          |t          j                  r|S t          |t                    rE|dk     r?t	          |           |z   }|dk     rt          d|z            |                     |          S 	 |                     |          }n # t          $ r t          d|z            w xY w|S )Nr   zrow index is out of bounds: %dzcould not find tree path '%s')r   r   TreeIterintrn   
IndexErrorget_iterr]   )r8   keyindexaiters       r   _getiterzTreeModel._getiter6  s    c3<(( 	J
S
!
! 
	cAggIIOEqyy !AC!GHHH=='''
H

c** 
H 
H 
H !@3!FGGG
HLs   8B B+c                     t          t          |           }t          |d          r|                                S t                              |           S )Nsort_new_with_model)rO   r  hasattrr  
TreeModelSortnew_with_model)r8   super_objectrQ   s     r   r  zTreeModel.sort_new_with_modelE  sK    Y--<!677 	633555 //555r   c                 X    t          |t          j                  r|S t          |          S r"   )r   r   TreePath)r8   paths     r   _coerce_pathzTreeModel._coerce_pathL  s'    dCL)) 	"KD>>!r   c                 L    |                      |          }t          | |          S r"   )r  TreeModelRowr8   r  r  s      r   __getitem__zTreeModel.__getitem__R  s#    

c""D%(((r   c                 L    | |         }|                      |j        |           d S r"   )set_rowrs   )r8   r  rc   rows       r   __setitem__zTreeModel.__setitem__V  s'    3iSXu%%%%%r   c                 Z    |                      |          }|                     |           d S r"   )r  remover  s      r   __delitem__zTreeModel.__delitem__Z  s+    

c""Er   c                 F    t          | |                                           S r"   )TreeModelRowIterget_iter_firstr;   s    r   rM   zTreeModel.__iter__^  s    d&9&9&;&;<<<r   zinvalid tree pathc                     |                      |          }t          t          |                               |          \  }}|st	          d|z            |S )Nzinvalid tree path '%s')r  rO   r  r  r]   )r8   r  successr  rQ   s       r   r  zTreeModel.get_iterh  sX      &&y$//88>> 	>5<===r   c                     |                                 }t          t          |                               |          }|r|S d S r"   )r  rO   r  	iter_next)r8   r  	next_iterr  rQ   s       r   r  zTreeModel.iter_nexto  sE    JJLL		4((229== 		 	r   c                     |                                 }t          t          |                               |          }|r|S d S r"   )r  rO   r  
iter_previous)r8   r  	prev_iterr  rQ   s       r   r  zTreeModel.iter_previousu  sE    JJLL		4((66yAA 		 	r   c                 r   t          |t                    rt          d          |                                 }t	          |          |k    rt          d          g }g }t
          |          D ]F\  }}||                    |                     ||                     |                    |           G||fS )Nz%Expected a list or tuple, but got strz1row sequence has the incorrect number of elements)	r   r   r   
get_n_columnsrn   r]   	enumerater   _convert_value)r8   r  	n_columnsresultcolumnscur_colrc   s          r   _convert_rowzTreeModel._convert_row{  s     c3 	ECDDD&&((	s88y  PQQQ'nn 	$ 	$NGU}MM$--gu==>>>NN7####  r   c                 z    |                      |          \  }}|D ]}|                     ||||                     d S r"   )r  	set_value)r8   treeiterr  
converted_rowr  columns         r   r  zTreeModel.set_row  sP    !%!2!23!7!7
w 	: 	:FNN8VS[9999	: 	:r   c                     t          |t          j                  r|S t          j        |                     |          |          S )z5Convert value to a GObject.Value of the expected type)r   r   r^   get_column_type)r8   r  rc   s      r   r  zTreeModel._convert_value  s=     eW]++ 	L}T11&995AAAr   c                 &   |                                  }g }|D ]j}t          |t                    st          d          |dk     s||k    rt	          d          |                    |                     ||                     kt          |          S )Nzcolumn numbers must be intsr   zcolumn number is out of range)r  r   r  r   r]   r   ra   tuple)r8   r  r  r  valuescols         r   r  z
TreeModel.get  s    &&((	 	9 	9Cc3'' 
? =>>>Qww#** !@AAAMM$..3778888V}}r   c                 |    t          t          |                               |                     |          |          S r"   )rO   r  row_changedr  r8   r  rs   rQ   s      r   r  zTreeModel.row_changed  s1    Y%%11$2C2CD2I2I4PPPr   c                 |    t          t          |                               |                     |          |          S r"   )rO   r  row_insertedr  r  s      r   r  zTreeModel.row_inserted  s1    Y%%2243D3DT3J3JDQQQr   c                 |    t          t          |                               |                     |          |          S r"   )rO   r  row_has_child_toggledr  r  s      r   r  zTreeModel.row_has_child_toggled  s;    Y%%;;D<M<Md<S<S<@B B 	Br   c                 z    t          t          |                               |                     |                    S r"   )rO   r  row_deletedr  r8   r  rQ   s     r   r  zTreeModel.row_deleted  s/    Y%%11$2C2CD2I2IJJJr   c                 ~    t          t          |                               |                     |          ||          S r"   )rO   r  rows_reorderedr  )r8   r  rs   	new_orderrQ   s       r   r  zTreeModel.rows_reordered  s=    Y%%44T5F5Ft5L5L599F F 	Fr   )%r   r   r   rp   rv   rg   rh   r   r  r  r  r  r  r  rM   r
   r   r  r  
iter_childreniter_nth_childiter_parentget_iter_from_stringr]   r  r  r  r  r  r  r  r  r  r  r  r  ri   rj   s   @r   r  r  +  sb       * * *    t 
 
 
6 6 6 6 6" " ") ) )& & &  = = = *)#-*FGGN(()DEEM))#-*FGGN&&s}'@AAK//
0R0:<OQ Q            ! ! !(: : :
B B B
 
 
$Q Q Q Q QR R R R RB B B B BK K K K KF F F F F F F F Fr   r  c                   Z     e Zd Z eej        j        d          Zd fd	Zd fd	Z xZ	S )TreeSortable)NNr   Nc                 \    t          t          |                               |||           d S r"   )rO   r  
set_sort_func)r8   sort_column_id	sort_funcr   rQ   s       r   r  zTreeSortable.set_sort_func  s+    
lD!!//	9UUUUUr   c                 Z    t          t          |                               ||           d S r"   )rO   r  set_default_sort_func)r8   r  r   rQ   s      r   r  z"TreeSortable.set_default_sort_func  s)    
lD!!77	9MMMMMr   r"   )
r   r   r   r
   r   r  get_sort_column_idr  r  ri   rj   s   @r   r  r    s        --c.>.Q\hiiiV V V V V VN N N N N N N N N Nr   r  c                   z    e Zd Z eej        j        de          Z eej        d          s
e	d             Z
dS dS )r  rO  r   r  c                 6    t                               |          S r"   )r  r  )r8   child_models     r   r  zTreeModelSort.new_with_model  s     44[AAAr   N)r   r   r   r   r   r  r9   r   r  classmethodr  r   r   r   r  r    s        "?3#4#=-7,CE E E ws(*:;; 	B

B 
B [
B 
B 
B	B 	Br   r  c                       e Zd Zd Z eej        d          s
ej        j        Zn eej        d          sej        j        Zd Z	d
dZ
d
dZd
dZd
d	Z
d
d
Zd Zd ZdS )	ListStorec                 n    t           j                            |            |                     |           d S r"   )r   r  r9   set_column_typesr8   column_typess     r   r9   zListStore.__init__  2    
t$$$l+++++r   insert_with_valuesvinsert_with_valuesc                     |0|                      |          \  }}|                     |||          }n t          j                            | |          }|S r"   )r  r  r   r  rp  )r8   r   r  r  r  s        r   
_do_insertzListStore._do_insert  sS    ?,,S11LC..x#FFHH}++D(;;Hr   Nc                 p    |r|                      d|          S t          j                            |           S r   )r  r   r  r   r8   r  s     r   r   zListStore.append  s6     	.??2s+++ =''---r   c                 .    |                      d|          S Nr   r  r  s     r   prependzListStore.prepend  s    q#&&&r   c                 .    |                      ||          S r"   r  )r8   r   r  s      r   rp  zListStore.insert  s    x---r   c                     |H|d}n-|                      |                                          d         }|                     ||          S t          j                            | |          S r   )get_pathget_indicesr  r   r  
insert_beforer8   siblingr  r   s       r   r  zListStore.insert_before  sa    ?==11==??C??8S111}**4999r   c                     |K|d}n0|                      |                                          d         dz   }|                     ||          S t          j                            | |          S Nr   r   r  )r  r  r  r   r  insert_afterr  s       r   r  zListStore.insert_after  sf    ?==11==??CaG??8S111}))$888r   c                 v    |                      ||          }t          j                            | |||           d S r"   )r  r   r  r  r8   r  r  rc   s       r   r  zListStore.set_value  9    ##FE22
h>>>>>r   c                      fd}|rt          |d         t                    r  ||d d d         |dd d                    d S t          |d         t          t          f          r<t	          |          dk    rt          d           ||d         |d                    d S t          |d         t                    r9 |t          |d                   |d                                                    d S t          d          d S )Nc                    t          |           t          |          k    rt          d          g }g }t          | |          D ]g\  }}t          |t                    st          d          |                    |           |                                        ||                     ht          j        	                    ||           d S Nz7The number of columns do not match the number of valuesz0TypeError: Expected integer argument for column.)
rn   r   r  r   r  r   r  r   r  setcolsvalsr  r  col_numrc   r8   r  s         r   
_set_listsz!ListStore.set.<locals>._set_lists      4yyCII%% YZZZGF"%dD// 
C 
C!'3// X#$VWWWw'''

d11'5AABBBBMdHgv>>>>>r   r   r   r  Too many argumentsArgument list must be in the form of (column, value, ...), ((columns,...), (values, ...)) or {column: value}.  No -1 termination is needed.)r   r  r  rF   rn   r   r  r  r8   r  r1   r
  s   ``  r   r  z
ListStore.set  s7   
	? 
	? 
	? 
	? 
	? 
	?  
	o$q'3'' 	
o
4!9d14a4j11111DGeT]33 
ot99>>#$8999
47DG,,,,,DGT** 
o
4Q==$q'..*:*:;;;;;  !n  o  o  o
	o 
	or   r"   )r   r   r   r9   r  r   r  r  r  r  r   r  rp  r  r  r  r  r   r   r   r  r    s       , , , 73="788 ?!m>
WS]$8
9
9 ? ]>  . . . .' ' ' '. . . .: : : :9 9 9 9? ? ?o o o o or   r  c                       e Zd Zd Zed             Zed             Zed             Zed             Zd Z	d Z
d Zd	 Zd
 Z
d Zd Zd
S )r  c                    t          |t          j                  s$t          dt	          |          j        z            || _        t          |t          j                  r|                    |          | _	        d S t          |t          j
                  r	|| _	        d S t          dt	          |          j        z            )Nz expected Gtk.TreeModel, %s foundz/expected Gtk.TreeIter or Gtk.TreePath, %s found)r   r   r  r   r   r   rP  r  r  rs   r  )r8   rP  iter_or_paths      r   r9   zTreeModelRow.__init__?  s    %// 	W>eAUUVVV
lCL11 	F|44DIII
cl
3
3 	F$DIII ')-l););)DE F F 
Fr   c                 @    | j                             | j                  S r"   )rP  r  rs   r;   s    r   r  zTreeModelRow.pathK  s    z""49---r   c                 *    |                                  S r"   )get_nextr;   s    r   nextzTreeModelRow.nextO  s    }}r   c                 *    |                                  S r"   )get_previousr;   s    r   previouszTreeModelRow.previousS  s      """r   c                 *    |                                  S r"   )
get_parentr;   s    r   r  zTreeModelRow.parentW  s       r   c                 r    | j                             | j                  }|rt          | j         |          S d S r"   )rP  r  rs   r  )r8   r  s     r   r  zTreeModelRow.get_next[  s=    J((33	 	7
I666	7 	7r   c                 r    | j                             | j                  }|rt          | j         |          S d S r"   )rP  r  rs   r  )r8   r  s     r   r  zTreeModelRow.get_previous`  s=    J,,TY77	 	7
I666	7 	7r   c                 r    | j                             | j                  }|rt          | j         |          S d S r"   )rP  r  rs   r  )r8   parent_iters     r   r  zTreeModelRow.get_parente  s=    j,,TY77 	9
K888	9 	9r   c                     t          |t                    rj| j                                        k    rt	          d|z            |dk     r                     |          } j                             j        |          S t          |t                    r{|	                     j                                                  \  }}}g }t          |||          D ]5}|                     j                             j        |                     6|S t          |t                    r fd|D             S t          dt          |          j        z            )N!column index is out of bounds: %dr   c                      g | ]
}|         S r   r   )r|   kr8   s     r   r~   z,TreeModelRow.__getitem__.<locals>.<listcomp>x  s    )))DG)))r   z0indices must be integers, slice or tuple, not %s)r   r  rP  r  r  _convert_negative_indexra   rs   sliceindicesranger   r  r   r   r   )r8   r  rw  stopstepalistis   `      r   r  zTreeModelRow.__getitem__j  sR   c3 	2dj..0000 !Ds!JKKKq22377:''	3777
U
#
# 
	2 #DJ,D,D,F,F G GE4E5$-- 
A 
ATZ11$)Q??@@@@L
U
#
# 	2))))S))))N"3ii01 2 2 
2r   c                    t          |t                    rm|| j                                        k    rt	          d|z            |dk     r|                     |          }| j                            | j        ||           d S t          |t                    r|	                    | j                                                  \  }}}t          |||          }t          |          t          |          k    r.t          dt          |          t          |          fz            t          |          D ],\  }}| j                            | j        |||                    -d S t          |t                    rkt          |          t          |          k    r.t          dt          |          t          |          fz            t          ||          D ]
\  }	}|| |	<   d S t!          dt#          |          j        z            )Nr   r   z9attempt to assign sequence of size %d to slice of size %dz<attempt to assign sequence of size %d to sequence of size %dz2indices must be an integer, slice or tuple, not %s)r   r  rP  r  r  r#  r  rs   r$  r%  r&  rn   r]   r  r  r  r   r   r   )
r8   r  rc   rw  r'  r(  	indexListr*  vr"  s
             r   r  zTreeModelRow.__setitem__}  s   c3 	2dj..0000 !Ds!JKKKq22377J  C77777
U
#
# 	2 #DJ,D,D,F,F G GE4eT400I9~~U++ O5zz3y>>234 4 4 "),, 
= 
=1
$$TY58<<<<
= 
=
U
#
# 		23xx3u::%% R5zz3s88,-. . . C 
 
1Q
 
 P"3ii01 2 2 
2r   c                 n    | j                                         |z   }|dk     rt          d|z            |S )Nr   r   )rP  r  r  )r8   r  	new_indexs      r   r#  z$TreeModelRow._convert_negative_index  s=    J,,..6	q==@5HIIIr   c                 j    | j                             | j                  }t          | j         |          S r"   )rP  r  rs   r  )r8   
child_iters     r   iterchildrenzTreeModelRow.iterchildren  s+    Z--di88

J777r   N)r   r   r   r9   r1  r  r  r  r  r  r  r  r  r  r#  r2  r   r   r   r  r  =  s        
F 
F 
F . . X.   X # # X# ! ! X!7 7 7
7 7 7
9 9 9
2 2 2&2 2 28  8 8 8 8 8r   r  c                   ,    e Zd Zd Zd ZesereZd ZdS )r  c                 "    || _         || _        d S r"   )rP  rs   )r8   rP  r  s      r   r9   zTreeModelRowIter.__init__  s    
			r   c                     | j         st          t          | j        | j                   }| j                            | j                   | _         |S r"   )rs   
StopIterationr  rP  r  r  s     r   __next__zTreeModelRowIter.__next__  sC    y 	 4:ty11J((33	
r   c                     | S r"   r   r;   s    r   rM   zTreeModelRowIter.__iter__  s    r   N)	r   r   r   r9   r7  rg   rh   r  rM   r   r   r   r  r    sW              t     r   r  c                   b     e Zd ZddZ fdZd Zd Zd Zd Zd Z	d	 Z
d
 Zd Zd Z
d
 Z xZS )r  r   c                 t   t          |t                    rt          |          }n4t          |t                    sd                    d |D                       }t	          |          dk    rt          d|z            	 t                              |          S # t
          $ r t          d|z            w xY w)N:c              3   4   K   | ]}t          |          V  d S r"   )r   )r|   vals     r   	<genexpr>z#TreePath.__new__.<locals>.<genexpr>  s(      55CHH555555r   r   z-could not parse subscript '%s' as a tree path)r   r  r   joinrn   r   r  new_from_string)clsr  s     r   __new__zTreePath.__new__  s    dC   	6t99DDD#&& 	6885555555Dt99>>KdRSSS	T++D111 	T 	T 	TKdRSSS	Ts    B B7c                 V    t          t          |                                            d S r"   )rO   r  r9   r8   r1   r   rQ   s      r   r9   zTreePath.__init__  s%    
h&&(((((r   c                 .    |                                  pdS )N )	to_stringr;   s    r   __str__zTreePath.__str__  s    ~~%2%r   c                 <    |d uo|                      |          dk     S r  comparer8   others     r   __lt__zTreePath.__lt__  s#    D <T\\%%8%81%<<r   c                 <    |d uo|                      |          dk    S r  rJ  rL  s     r   __le__zTreePath.__le__  #    D =T\\%%8%8A%==r   c                 <    |d uo|                      |          dk    S r  rJ  rL  s     r   __eq__zTreePath.__eq__  rQ  r   c                 <    |d u p|                      |          dk    S r  rJ  rL  s     r   __ne__zTreePath.__ne__  "    }8U 3 3q 88r   c                 <    |d u p|                      |          dk    S r  rJ  rL  s     r   __gt__zTreePath.__gt__  s"    }7U 3 3a 77r   c                 <    |d u p|                      |          dk    S r  rJ  rL  s     r   __ge__zTreePath.__ge__  rV  r   c                 D    t          |                                           S r"   )rs   r  r;   s    r   rM   zTreePath.__iter__  s    D$$&&'''r   c                 *    |                                  S r"   )	get_depthr;   s    r   rp   zTreePath.__len__  s    ~~r   c                 6    |                                  |         S r"   )r  )r8   r  s     r   r  zTreePath.__getitem__  s    !!%((r   )r   )r   r   r   rB  r9   rH  rN  rP  rS  rU  rX  rZ  rM   rp   r  ri   rj   s   @r   r  r    s        T T T T) ) ) ) )& & &= = => > >> > >9 9 98 8 89 9 9( ( (     ) ) ) ) ) ) )r   r  c                   N    e Zd Zd Zd ZddZddZddZddZddZ	d	 Z
d
 ZdS )	TreeStorec                 n    t           j                            |            |                     |           d S r"   )r   r`  r9   r  r  s     r   r9   zTreeStore.__init__  r  r   c                     |1|                      |          \  }}|                     ||||          }n!t          j                            | ||          }|S r"   )r  r  r   r`  rp  )r8   r  r   r  r  r  s         r   r  zTreeStore._do_insert  sW    ?,,S11LC..vx#NNHH}++D&(CCHr   Nc                 0    |                      |d|          S r   r  r8   r  r  s      r   r   zTreeStore.append  s    vr3///r   c                 0    |                      |d|          S r  r  rd  s      r   r  zTreeStore.prepend  s    vq#...r   c                 0    |                      |||          S r"   r  )r8   r  r   r  s       r   rp  zTreeStore.insert  s    vx555r   c                    |`|d}nD||                      |          }|                     |                                          d         }|                     |||          S t          j                            | ||          S r   )r  r  r  r  r   r`  r  r8   r  r  r  r   s        r   r  zTreeStore.insert_before  s}    ?>!--g66F==11==??C??68S999}**4AAAr   c                    |c|d}nG||                      |          }|                     |                                          d         dz   }|                     |||          S t          j                            | ||          S r  )r  r  r  r  r   r`  r  rh  s        r   r  zTreeStore.insert_after  s    ?>!--g66F==11==??CaG??68S999}))$@@@r   c                 v    |                      ||          }t          j                            | |||           d S r"   )r  r   r`  r  r   s       r   r  zTreeStore.set_value#  r  r   c                      fd}|rt          |d         t                    r  ||d d d         |dd d                    d S t          |d         t          t          f          r<t	          |          dk    rt          d           ||d         |d                    d S t          |d         t                    r> ||d                                         |d                                                    d S t          d          d S )Nc                    t          |           t          |          k    rt          d          g }g }t          | |          D ]g\  }}t          |t                    st          d          |                    |           |                                        ||                     ht          j        	                    ||           d S r  )
rn   r   r  r   r  r   r  r   r`  r  r  s         r   r
  z!TreeStore.set.<locals>._set_lists(  r  r   r   r   r  r  r
  )	r   r  r  rF   rn   r   r  keysr  r  s   ``  r   r  z
TreeStore.set'  s9   
	? 
	? 
	? 
	? 
	? 
	?  
	o$q'3'' 	
o
4!9d14a4j11111DGeT]33 
ot99>>#$8999
47DG,,,,,DGT** 
o
47<<>>47>>+;+;<<<<<  !n  o  o  o
	o 
	or   r"   )r   r   r   r9   r  r   r  rp  r  r  r  r  r   r   r   r`  r`    s        , , ,  0 0 0 0/ / / /6 6 6 6
B 
B 
B 
B
A 
A 
A 
A? ? ?o o o o or   r`  c                       e Zd Zeser eej        j        de	          Z e
ej        j                  Z e
ej        j                  Z e
ej        j
                  Z
eser fdZeser fdZd fd	Zd
 fd		Zd fd
	Zd Z xZS )TreeViewrO  r   c                 z    t          |          }t          t          |                               |||           d S r"   )r    rO   ro  enable_model_drag_source)r8   start_button_maskr   actionsr   rQ   s        r   rq  z!TreeView.enable_model_drag_sourceS  sJ    3G<<N(D!!::;L;I;B
D 
D 
D 
D 
Dr   c                 x    t          |          }t          t          |                               ||           d S r"   )r    rO   ro  enable_model_drag_dest)r8   r   rs  r   rQ   s       r   ru  zTreeView.enable_model_drag_destZ  sF    3G<<N(D!!889@
B 
B 
B 
B 
Br   NF        c                     t          |t          j                  st          |          }t          t          |                               |||||           d S r"   )r   r   r  rO   ro  scroll_to_cell)r8   r  r  	use_align	row_align	col_alignrQ   s         r   rx  zTreeView.scroll_to_cell_  sO    $-- 	"D>>D
h,,T69iQZ[[[[[r   c                     t          |t          j                  st          |          }t          t          |                               |||           d S r"   )r   r   r  rO   ro  
set_cursor)r8   r  r  
start_editingrQ   s       r   r}  zTreeView.set_cursord  sJ    $-- 	"D>>D
h((v}EEEEEr   c                     t          |t          j                  st          |          }t          t          |                               ||          S r"   )r   r   r  rO   ro  
get_cell_area)r8   r  r  rQ   s      r   r  zTreeView.get_cell_areai  sB    $-- 	"D>>DXt$$224@@@r   c                     t                      }|                    |           |                    |d           |                     ||            |j        |fi | d S NF)TreeViewColumn	set_title
pack_start
insert_columnset_attributes)r8   r   r  cellr   r  s         r   insert_column_with_attributesz&TreeView.insert_column_with_attributesn  sn    !!$&&&68,,,d--f-----r   )NFrv  rv  r  r"   )r   r   r   rg   rh   r   r   ro  r9   r   r
   get_path_at_posrR  get_dest_row_at_posrq  ru  rx  r}  r  r  ri   rj   s   @r   ro  ro  H  st        Et E"?3<#8-7,CE E E +*3<+GHHO,,S\-KLL..s|/OPP Dt D	D 	D 	D 	D 	D  Bt B	B 	B 	B 	B 	B
\ \ \ \ \ \
F F F F F F
A A A A A A
. . . . . . .r   ro  c                   \     e Zd Z	 	 ddZ eej        j                  Zd fd	Zd Z	 xZ
S )r  rF  Nc                     t           j                            | |           |r|                     |d           |                                D ]\  }}|                     |||           d S )NrD  T)r   r  r9   r  r   
add_attribute)r8   r  
cell_renderer
attributesr}   rc   s         r   r9   zTreeViewColumn.__init__{  s     	##D#666 	1OOM4000'--// 	; 	;MT5}dE::::	; 	;r   c                 \    t          t          |                               |||           d S r"   )rO   r  set_cell_data_func)r8   r  func	func_datarQ   s       r   r  z!TreeViewColumn.set_cell_data_func  s+    
nd##66}dIVVVVVr   c                     t           j                            | |           |                                D ]'\  }}t           j                            | |||           (d S r"   )r   
CellLayoutclear_attributesr   r  )r8   r  r  r}   rc   s        r   r  zTreeViewColumn.set_attributes  sf    ''m<<<'--// 	K 	KMT5N((}dEJJJJ	K 	Kr   )rF  Nr"   )r   r   r   r9   r
   r   r  cell_get_positionr  r  ri   rj   s   @r   r  r  z  s        #; ; ; ; -,S-?-QRRW W W W W WK K K K K K Kr   r  c                   2     e Zd Z fdZ fdZ fdZ xZS )
TreeSelectionc                     t          |t          j                  st          |          }t          t          |                               |           d S r"   )r   r   r  rO   r  select_pathr  s     r   r  zTreeSelection.select_path  sF    $-- 	"D>>D
mT""..t44444r   c                 n    t          t          |                                           \  }}}|r||fS |d fS r"   )rO   r  get_selected)r8   r  rP  r  rQ   s       r   r  zTreeSelection.get_selected  sA     %mT : : G G I I 	!5>!4= r   c                 `    t          t          |                                           \  }}||fS r"   )rO   r  get_selected_rows)r8   rowsrP  rQ   s      r   r  zTreeSelection.get_selected_rows  s,    M400BBDDet}r   )r   r   r   r  r  r  ri   rj   s   @r   r  r    sj        5 5 5 5 5
! ! ! ! !        r   r  c                       e Zd Z eej        j        dded          Zd Z e	ej
        d          rd Z e	ej
        d          rd	 Zd
S d
S )Button)r   stock	use_stock
use_underline)r  r   )r   r  r   r   c                     d|v ri|d         rat          j        dt          d           |                                }|d         |d<   d|d<   d|d<   |d= t	          j        j        | fi | d S  | j        |i | d S )	Nr  zKStock items are deprecated. Please use: Gtk.Button.new_with_mnemonic(label)r   r  r   Tr  r  )r  r  r   r  r   r  r9   r  )r8   r1   r   r  s       r   r9   zButton.__init__  s    
 &  VG_ 
 P5!E E E E $[[]]
&0&9
7#*.
;'.2
?+w'
#D77J77777
D+F+++++r   set_focus_on_clickc                 6    t          j        j        | g|R i |S r"   )r   rD   r  r   s      r   r  zButton.set_focus_on_click  '     z4TKDKKKFKKKr   get_focus_on_clickc                 6    t          j        j        | g|R i |S r"   )r   rD   r  r   s      r   r  zButton.get_focus_on_click  r  r   N)
r   r   r   r   r   r  r9   r   r  r  rD   r  r  r   r   r   r  r    s        
 3*Z'1)@+,	. . .	, 	, 	,$ 73:344 	L
L 
L 
L
 73:344 	L
L 
L 
L 
L 
L	L 	Lr   r  c                   >    e Zd Z eej        j        de          ZdS )
LinkButton)urir   r   N)r   r   r   r   r   r  r9   r   r   r   r   r  r    s4        "?3>#:-=,CE E Er   r  c                   >    e Zd Z eej        j        de          ZdS )Labelr   r   N)r   r   r   r   r   r  r9   r   r   r   r   r  r    s4        "?39#5-7,CE E Er   r  c                   V    e Zd Zeser eej        j        dddde	d          Z
d ZdS )	
Adjustment)rc   lowerupperstep_incrementpage_increment	page_size	page_incr	step_incr)r  r  r   )r   r	  r   r   c                 X   t           st          r` | j        |i | d|v r|                     |d                    d S t	          |          dk    r|                     |d                    d S d S t          j        j        | g|R i | d|v r|                     |d                    d S d S )Nrc   r  r   )rg   rh   r  r  rn   r   r  r9   r   s      r   r9   zAdjustment.__init__  s     	04 	0DJ'''' &  vg/////TatAw'''''   
N#D:4:::6:::
 &  vg///// ! r   N)r   r   r   rg   rh   r   r   r  r9   r   r  r   r   r   r  r    sm         .t . 7+\FQFQ4S 4S)@+,
. . .0 0 0 0 0r   r  c                       e Zd Z eej        j        dddde          Zej        j	        ej        j
        z  ej        j	        ej        j
        z  ddfdZdS )	Table)n_rowsr  r   r  r  )r  r  r8  r   c
                 V    t           j                            | |||||||||	
  
         d S r"   )r   r  attach)
r8   rH   left_attachright_attach
top_attach
bottom_attachxoptionsyoptionsxpaddingypaddings
             r   r  zTable.attach  sH    IT5+|ZQ^`hjrt|  G  
H  
H  
H  
H  
Hr   N)r   r   r   r   r   r  r9   r   
AttachOptionsEXPANDFILLr  r   r   r   r  r    s        "?39#5-SAGV_6`6`,CE E E
 `c_p_wz}  {L  {Q  `Q  \_  \m  \t  wz  wH  wM  \M  XY  de 	H 	H 	H 	H 	H 	Hr   r  c                   >    e Zd Z eej        j        de          ZdS )ScrolledWindowhadjustmentvadjustmentr   N)r   r   r   r   r   r  r9   r   r   r   r   r  r    s5        "?3#5#>-K,CE E Er   r  c                   >    e Zd Z eej        j        de          ZdS )
HScrollbar
adjustmentr   N)r   r   r   r   r   r  r9   r   r   r   r   r  r  !  4        "?3>#:-<,CE E Er   r  c                   >    e Zd Z eej        j        de          ZdS )
VScrollbarr  r   N)r   r   r   r   r   r  r9   r   r   r   r   r  r  )  r  r   r  c                   ,     e Zd Zd fd	Zd fd	Z xZS )PanedFTc                 \    t          t          |                               |||           d S r"   )rO   r  pack1r8   rH   resizeshrinkrQ   s       r   r  zPaned.pack14  +    %$$UFF;;;;;r   c                 \    t          t          |                               |||           d S r"   )rO   r  pack2r  s       r   r  zPaned.pack27  r  r   )FT)TT)r   r   r   r  r  ri   rj   s   @r   r  r  3  s[        	< 	< 	< 	< 	< 	<	< 	< 	< 	< 	< 	< 	< 	< 	< 	<r   r  c                   >    e Zd Z eej        j        de          ZdS )Arrow)
arrow_typeshadow_typer   N)r   r   r   r   r   r  r9   r   r   r   r   r  r  ?  s4        "?39#5-J,CE E Er   r  c                   &     e Zd ZddZ fdZ xZS )IconSetNc                     |<t          j        dt          d           t          j                            |          }nt          j                            |           }|S )NzXGtk.IconSet(pixbuf) has been deprecated. Please use: Gtk.IconSet.new_from_pixbuf(pixbuf)r   r  )r  r  r   r   r  new_from_pixbufrB  )rA  pixbuficonsets      r   rB  zIconSet.__new__H  sc    !
 D5!E E E E +55f==+--c22Nr   c                 R    t          t          |                                           S r"   )rO   r  r9   rD  s      r   r9   zIconSet.__init__R  s    $''00222r   r"   )r   r   r   rB  r9   ri   rj   s   @r   r  r  G  sL        	 	 	 		3 	3 	3 	3 	3 	3 	3 	3 	3r   r  c                   >    e Zd Z eej        j        de          ZdS )Viewportr  r   N)r   r   r   r   r   r  r9   r   r   r   r   r  r  X  s4        "?3<#8-K,CE E Er   r  c                   &     e Zd Zd fd	Zd Z xZS )TreeModelFilterNc                 Z    t          t          |                               ||           d S r"   )rO   r  set_visible_func)r8   r  datarQ   s      r   r  z TreeModelFilter.set_visible_funcb  s)    
ot$$55dDAAAAAr   c                     |                      |          }|                                                     |||           d S r"   )convert_iter_to_child_iter	get_modelr  )r8   rs   r  rc   s       r   r  zTreeModelFilter.set_valuee  s=    ..t44""477777r   r"   )r   r   r   r  r  ri   rj   s   @r   r  r  a  sR        B B B B B B8 8 8 8 8 8 8r   r  c                   8     e Zd Zedd            Zd fd	Z xZS )CustomSorterNc                 j    |t          |          }nd }t          j                            ||          S r"   )r   r   r  r   )rA  r  r   compare_funcs       r   r   zCustomSorter.newq  s5    $8CC##''i@@@r   c                 ~    |t          |          }nd }t          t          |                               ||          S r"   )r   rO   r  r  )r8   r  r   r  rQ   s       r   r  zCustomSorter.set_sort_funcz  s=    $8CC#t,,::<SSSr   r"   )r   r   r   r  r   r  ri   rj   s   @r   r  r  o  si        		A 	A 	A 
	A	T 	T 	T 	T 	T 	T 	T 	T 	T 	Tr   r  c                       e Zd Zd ZdS )Menuc           	      <    |                      d ||||||           d S r"   )popup_for_device)r8   parent_menu_shellparent_menu_itemr  r  button
activate_times          r   popupz
Menu.popup  s-    !!$(9;KTSWY_anooooor   N)r   r   r   r  r   r   r   r  r    s(        	p 	p 	p 	p 	pr   r  c                  "    t                       d S r"   )_Gtk_main_quit)r1   s    r   	main_quitr    s    r   c                      t          t          j                  5  t                      5  t	          | i |cd d d            cd d d            S # 1 swxY w Y   	 d d d            d S # 1 swxY w Y   d S r"   )r   r   r  r   	_Gtk_main)r1   r   s     r   mainr
    s   
%cm
4
4 	2 	2!## 
2 
2 $1&11
2 
2 
2 
2 
2 
2 
2	2 	2 	2 	2 	2 	2 	2 	2
2 
2 
2 
2 
2 
2 
2 
2 
2	2 	2 	2 	2 	2 	2 	2 	2 	2 	2 	2 	2 	2 	2 	2 	2 	2 	2s3   A'AA'A	A'A	A''A+.A+stock_lookup)asysr  
gi.repositoryr   _ossighelperr   r   r   r   r   	overridesr	   r
   r   r   moduler
   gir   r   _versionrg   rh   rf   __all__r   warn_msgr  RuntimeWarningr   r    r3   objectr5   rD   rl   r   r   r   r   r   r   r   r   r   r   r   r   r9   r   r  r5  rC  rF  rH  rJ  rN  rU  rW  rZ  r]  r  r  r  r  r  r  r  r  r`  ro  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r
  r	  r  
init_checkr   argvrF   r   r   r   <module>r     s  , 


  ! ! ! ! ! ! E E E E E E E E > > > > > > > >4 4 4 4 4 4 4 4 4 4 4 4 - - - - - - % % % % % % u%%
|u
|u
|u
  z    6  * + + + ,#H HM(N+++	 	 	 	 	4 	 	 	 ( ) ) )  -4 -   NN+,,,
6 
6 
6 % % % % %6 % % %)% )% )% )% )%SZ )% )% )%X 
&		 x     ,4 ,&N &N &N &N &NCM6 &N &N &NP ##INN;I` ` ` ` `s| ` ` ` 8H z      4  E E E E E E E E
 Xf

FNN8E E E E Eco E E E
 (;''KNN=!!!\J \J \J \J \Jco \J \J \J| (;''KNN=!!!
K 
K 
K 
K 
KCM 
K 
K 
K ##INN;I I I I Is|Y I I I 8H z     4 E E E E Ecg E E E
 (3--CNN5   4  E E E E ECM E E E ##INN;  4 E E E E E3< E E E
 x!!HNN:  'U 'U 'U 'U 'Uck 'U 'U 'UT (7

 y     1:&LL"?3:#6-6,C./1 1 1L, , , , ,SZ , , , 
&		 x   U, U, U, U, U,SZ U, U, U,p 
&		 x     $4 $@ @ @ @ @)6 @ @ @  H]++MNN?###  (4 (E E E E Es7 E E E
 $8$899NN)***E E E E EC1 E E E
 !!233NN&'''  *4 *E E E E Ec5 E E E
 #(#677NN()))  
*4 
*E E E E Ec5 E E E #(#677NN()))S S S S Ss| S S S 8H z     !4 !E E E E ES^ E E E
 *%%JNN<   J J J J J
 J J J 
HY	 {   U U U U U U U U Xj
!
!
 |   Gb Gb Gb Gb Gb Gb Gb GbT Xj
!
!
 |   I I I I Is| I I I
 8H z   QF QF QF QF QF
 QF QF QFh 
HY	 {   N N N N N3# N N N x%% ~     $4 $B B B B B) B B B H]++MNN?###Uo Uo Uo Uo Uo
y, Uo Uo Uop 
HY	 {   d8 d8 d8 d8 d86 d8 d8 d8N ~       v   * ! " " ".) .) .) .) .)s| .) .) .)b 8H z   Mo Mo Mo Mo Mo
y, Mo Mo Mo` 
HY	 {   +. +. +. +. +.s|Y +. +. +.\ 8H z   K K K K KS' K K K. .))           C%   * ''
      74 7#L #L #L #L #LY #L #L #LJ Xf

FNN8E E E E ES^ E E E
 *%%JNN<   E E E E E	 E E E
 
HUOOENN70 0 0 0 0 0 0 0< Xj
!
!
 |     %4 %H H H H H	9 H H H 
HUOOENN7E E E E E+ E E E
 Xn--NNN#$$$  !4 !E E E E ES^ E E E
 *%%JNN<   E E E E ES^ E E E
 *%%JNN<     	4 	< < < < <	 < < < 
HUOOENN7   4  E E E E E	 E E E
 
HUOOENN73 3 3 3 3#+ 3 3 3 hwGNN9E E E E E3< E E E
 x!!HNN:8 8 8 8 8c) 8 8 8 (?++   ! ! ! #T T T T Ts' T T T& 8L))LNN>""" p p p p psx p p p 8D>>DNN6 
24 
2]N
Xcm   I
Xch2 2 2  #4 #''(899LNN>""" ..""KKKsx00KtDzzCHHHr                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              
 T              l        @        @        ?        ?        (?        (?                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             }>  f      +       i          +       ~          +                 +                 +           =      +           h      +                 +                 +                 +       &          +       ;    ?      +       P    j      +       e          +       z          +                 +           	      +           A	      +           l	      +           	      +           	      .          %         O          	             5    	      	       K    
             _    
      <       w                              $                                                                                 
                  
                          %    @       	      6    P      H       C                 X    0      {       o          /                e           P      M                   ,                 [                  T           `      D          7                                 5    ,       7       O    c       8       b          c       s    @                 `                       R           p                	      X           	      *         0                          ;           P             -                  M    @             `    @      =       t                                              /                 +          *         (         * P                       7           P                       j           `                	         <                        3          6       L          J       d           "           0                                         V                  k                w                 L           `            	                  	          t       5	           R       V	    @      	      n	           %       x	   *       `       	    @      (       	    )      L       	    P!             	     "      ;       	    u             	    @"      2      
    #             
    $             -
     %      	      C
    &             _
                 
                 
    '            
          (       
    j             
                
    /      	                            2      I      *    P3            ;    05      j       K    5      l      V                 f    7            w                      9      4           @9                3      8          P;      d           k      I           ;      )                  p       
          6       $    D            6          P       M   1 p       8       e    :                 W      
          0i      -           U      5           g               1 8       8                 G           i             
          6       
                 3
          >       D
    q             X
    ]             q
    v             
   1         8       
    u      0       
          +      
                  
    	      !       
          o       
    	                 0                 Ђ            )    Є      e           @             7    Ѕ            O    
             l                   w          O          
                `      0         0                          (                }                            &                                   @            .    F             A    0      G      T    ^             l          N       }   * 0                                   v                 p                 p                       @       {   * @                *       H       [   7                         H          7                          H       *          #      ;                  H                  U                  b                  o    p             {   ,                   .                                    `      0                                                      ;       /    J       1       E    {       +       Z                  s            (           P                        _                 $           (       (           `             !    <             8                 S    P       (       c    x             w   * `      0          *        0            P                    +                                                           `
                        ,         1       8           @      ~                 '                       ,          a       ?                  Z                 m   1 
      8           P                                       N         1 @      8           p            ,   1 `      8       E   1 (      8       ^   1 
      8       w   1       8          1 P      8          1       8          1       8          1       8          1        8          1 8      8       '   1 p      8       @   1       8       Y   1 
      8       r   1 
      8          1       8          1  
      8          1 X
      8           `                1       8                 $          1       8          1 `      8       7   1       8       P   1 x      8       i   1       8          1       8          1 
      8          1 	      8          1 H
      8          1 
      8          1 	      8          1 h	      8       1   1 0	      8       J   1       8       c   1       8       |   1 P      8          1       8          1       8          1       8          1       8          1        8          1       8       +   1 8      8       D   1       8       ]   1 p      8       v   1        8          1 X      8          1       8          1       8          1 @      8          1       8          1 x      8       %   1 (      8       >          R       b   1       8       {   1       8          1       8          1 H      8                 A          1       8          1       8          1       8       3   1 0      8       L   1 h      8       e                 y                     ?      &          1       8          1       8          1       8          1        8       !   1 P      8       :   1        8       S   1       8       l          !       w           &           0                 P                 p                                                                                                      0                 P                 p                              
          "                        "                 .                 :    @      %       F          '       R          #       ^                   j                  v    @                 `                       &                 !                                             0                 `      !           $                     $                     E     -           PE                pE                E            1    E            F    E     4       Z     F     S       m    `F                pF     4           F               G               e                 I                                  pJ            /                 I    @K     '       Z    pK     ?      h    `      0       u          (           L     V                H           N     ;          PO                pO                 P               0Q                T           
    U                V           -                 <                  R    W     +       b    W           q    k                                 m               `                        `            p               q     w            pr     t           x     J      .           <       F     @{            X     Y             w                 l                  o     @|     o            |                 }                 ~     r                  6            @                                               !                !    q            %!                8!               Q!    @     -      a!                 v!           0      !    $                !               !         U       !                !         ^      "    @     ]       "         q       ?"   1        8       X"          6       v"    `     T       "                "   1       8       "    p            "   1 h      8       "    `           #   1 0      8       +#    p           F#   1       8       _#         P      z#   1       8       #    Ы           #   1       8       #    Э     9       #                
$    Ю     c       #$    @     )       B$    p     &       Z$         R       $                 $         0      $          *       $    б            $   1 X      8       %    в     h       -%                K%    p            d%    @           %   1       8       %         W       %    p           %   1 x      8       %   1 @      8       %   1       8       &   1 `      8       /&   1       8       H&   1 (      8       a&   1       8       z&   1       8       &   1       8       &   1 H      8       &   1       8       &   1       8       &   1       8       l     '             w     '                 @'                 `'      !           '                  '                 '                  (                  (                 @(      &           (      &           (                  (      &           )             
    0)             '    *             '    `*      @       V%    )             /'                 J'                l'                '    `            '                '   1       8       '   1 P      8       '                '   1        8       (    P            $(   1 8      8       <(               U(   1       8       m(                (                (                (    p     6       (                (   1       8       )                ,)               G)   1       8       `)          6       ~)    `     T       )               )   1 p      8       )                )    p            *         [        *    p            l    ,             w    ,                  -                  -                 @-                 `-                 -             6*    .             D*    .      @       (    -             U*                 w*                *         d       *         N      *   1       8       *    `     P       *                *    `     "       +   1       8       +                1+   1       8       J+    `            c+                r+                +         +      +   1 0      8       +               +    p            +                 +                ,         &       ,,                 G,                Z,         !       v,    @     H       ,               ,         w      ,   1       8       ,   1 h      8        -   1 p      8       -   1 8      8       2-   1        8       K-   1       8       d-   1       8       }-   1        8       -   1 X      8       -   1       8       -   1       8       -   1       8       -                 !.   1 H      8       :.                 ].   1       8       v.   1       8       .          B       .   1       8       .   1 `      8       .   1 @      8       /   1       8       /   1 (      8       5/   1       8       N/   1       8       g/   1 x      8       /   1       8       /    /      !       l     1      !       w    @1                  `1      $           1      "           1      #           2                 02                 P2                 p2                 2                 2      %           2                 3                 03             
    `3      $       /                /    `            /    `           /    p            /    `            
0         ,      %0          L      :0    P      3       T0    p           i0          "       <0          I       0          K       0    $                0   1       8       0    9            0          [      0    1              1           '       &1    !      /       l    3             B1    $                G1    $ 
               M1    9            d1    :     a       {1    :            1    ;            1    P<     a       1    <            1    =            1    0@     n       2   1 (       8       2   1 `       8       82    pD            V2   1       8       n2   1       8       2    G           2   1       8       2   1       8       2   1       8       2   1 H      8       
3    I     D      .3   1       8       G3   1       8       `3    J     \       u3    PK     ]       3    K     g       3     L     g       3   1       8       3     M           3   1 h      8       4   1       8       14   1       8       J4   1       8       c4   1 P      8       {4   1 0      8       l     4      $       w    04                 `4      "           4                  4                 4                  5             4    6             4    6      @       4    5             4    `5      \       4     P            4    P            5     P     *       5    PP           =5    i     Z      S5    T            s5               5    0           5                5    Y           5    `            5    f            6    Pr           ;6    @k     @      ]6                }6    @            6    [           6    R            6     S            6    S            6    @T            7     U            27    U     {       K7    0V     9       g7    pV     !       7    V     2       7    V           7   1  "      8       7   1 !      8       7    pX     E      8    ]            38    @^            B1    $                P8    ^           q8   1 $      8       8   1 $      8       8    `            8    a           8   1 p#      8   ---
-- This library was written by Patrik Karlsson <patrik@cqure.net> to facilitate
-- communication with the Apple AFP Service. It is not feature complete and
-- still missing several functions.
--
-- The library currently supports
-- * Authentication using the DHX UAM (CAST128)
-- * File reading and writing
-- * Listing sharepoints
-- * Listing directory contents
-- * Querying ACLs and mapping user identities (UIDs)
--
-- The library was built based on the following reference:
-- http://developer.apple.com/mac/library/documentation/Networking/Reference/AFP_Reference/Reference/reference.html
-- http://developer.apple.com/mac/library/documentation/Networking/Conceptual/AFP/AFPSecurity/AFPSecurity.html#//apple_ref/doc/uid/TP40000854-CH232-CHBBAGCB
--
-- Most functions have been tested against both Mac OS X 10.6.2 and Netatalk 2.0.3
--
-- The library contains the following four classes
-- * <code>Response</code>
-- ** A class used as return value by functions in the <code>Proto</code> class.
-- ** The response class acts as a wrapper and holds the response data and any error information.
-- * <code>Proto</code>
-- ** This class contains all the AFP specific functions and calls.
-- ** The functions can be accessed directly but the preferred method is through the <code>Helper</code> class.
-- ** The function names closely resemble those described in the Apple documentation.
-- ** Some functions may lack some of the options outlined in Apple's documentation.
-- * <code>Helper</code>
-- ** The helper class wraps the <code>Proto</code> class using functions with a more descriptive name.
-- ** Functions are task-oriented. For example, <code>ReadFile</code> and usually call several functions in the <code>Proto</code> class.
-- ** The purpose of this class is to give developers easy access to some of the common AFP tasks.
-- * <code>Util</code>
-- ** The <code>Util</code> class contains a number of static functions mainly used to convert data.
--
-- The following information will describe how to use the AFP Helper class to communicate with an AFP server.
--
-- The short version:
-- <code>
-- helper = afp.Helper:new()
-- status, response = helper:OpenSession( host, port )
-- status, response = helper:Login()
-- .. do some fancy AFP stuff ..
-- status, response = helper:Logout()
-- status, response = helper:CloseSession()
-- </code>
--
-- Here's the longer version, with some explanatory text. To start using the Helper class,
-- the script has to create its own instance. We do this by issuing the following:
-- <code>
-- helper = afp.Helper:new()
-- </code>
--
-- Next a session to the AFP server must be established, this is done using the OpenSession method of the
-- Helper class, like this:
-- <code>
-- status, response = helper:OpenSession( host, port )
-- </code>
--
-- The next step needed to be performed is to authenticate to the server. We need to do this even for
-- functions that are available publicly. In order to authenticate as the public user simply
-- authenticate using nil for both username and password. This can be achieved by calling the Login method
-- without any parameters, like this:
-- <code>
-- status, response = helper:Login()
-- </code>
--
-- To authenticate to the server using the username 'admin' and password 'nimda' we do this instead:
-- <code>
-- status, response = helper:Login('admin', 'nimda')
-- </code>
--
-- At this stage we're authenticated and can call any of the AFP functions we're authorized to.
-- For the purpose of this documentation, we will attempt to list the servers share points.
-- We do this by issuing the following:
-- <code>
-- status, shares = helper:ListShares()
-- </code>
--
-- Once we're finished, we need to logout and close the AFP session this is done by calling the
-- following two methods of the Helper class:
-- <code>
-- status, response = helper:Logout()
-- status, response = helper:CloseSession()
-- </code>
--
-- Consult the documentation of each function to learn more about their respective return values.
--
--@author Patrik Karlsson <patrik@cqure.net>
--@copyright Same as Nmap--See https://nmap.org/book/man-legal.html
--
-- @args afp.username The username to use for authentication.
-- @args afp.password The password to use for authentication.

--
-- Version 0.5
--
-- Created 01/03/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
-- Revised 01/20/2010 - v0.2 - updated all bitmaps to hex for better readability
-- Revised 02/15/2010 - v0.3 - added a bunch of new functions and re-designed the code to be OO
--
--   New functionality added as of v0.3
--    o File reading, writing
--    o Authentication
--    o Helper functions for most AFP functions
--    o More robust error handling
--
-- Revised 03/05/2010 - v0.4 - changed output table of Helper:Dir to include type and ID
--                           - added support for --without-openssl
--
-- Revised 03/09/2010 - v0.5 - documentation, documentation and more documentation
-- Revised 04/03/2011 - v0.6 - add support for getting file- sizes, dates and Unix ACLs
--                           - moved afp.username & afp.password arguments to library

local datetime = require "datetime"
local ipOps = require "ipOps"
local nmap = require "nmap"
local os = require "os"
local stdnse = require "stdnse"
local string = require "string"
local stringaux = require "stringaux"
local table = require "table"
_ENV = stdnse.module("afp", stdnse.seeall);

local HAVE_SSL, openssl = pcall(require,'openssl')

-- Table of valid REQUESTs
local REQUEST = {
  CloseSession = 0x01,
  OpenSession = 0x04,
  Command = 0x02,
  GetStatus = 0x03,
  Write = 0x06,
}

-- Table of headers flags to be set accordingly in requests and responses
local FLAGS = {
  Request = 0,
  Response = 1
}

-- Table of possible AFP_COMMANDs
COMMAND = {
  FPCloseVol = 0x02,
  FPCloseFork = 0x04,
  FPCopyFile = 0x05,
  FPCreateDir = 0x06,
  FPCreateFile = 0x07,
  FPGetSrvrInfo = 0x0f,
  FPGetSrvParms = 0x10,
  FPLogin = 0x12,
  FPLoginCont = 0x13,
  FPLogout = 0x14,
  FPMapId = 0x15,
  FPMapName = 0x16,
  FPGetUserInfo = 0x25,
  FPOpenVol = 0x18,
  FPOpenFork = 0x1a,
  FPGetFileDirParams = 0x22,
  FPChangePassword = 0x24,
  FPReadExt = 0x3c,
  FPWriteExt = 0x3d,
  FPGetAuthMethods = 0x3e,
  FPLoginExt = 0x3f,
  FPEnumerateExt2 = 0x44,
}

USER_BITMAP = {
  UserId = 0x01,
  PrimaryGroupId = 0x2,
  UUID = 0x4
}

VOL_BITMAP = {
  Attributes = 0x1,
  Signature = 0x2,
  CreationDate = 0x4,
  ModificationDate = 0x8,
  BackupDate = 0x10,
  ID = 0x20,
  BytesFree = 0x40,
  BytesTotal = 0x80,
  Name = 0x100,
  ExtendedBytesFree = 0x200,
  ExtendedBytesTotal = 0x400,
  BlockSize = 0x800
}

FILE_BITMAP = {
  Attributes = 0x1,
  ParentDirId = 0x2,
  CreationDate = 0x4,
  ModificationDate = 0x8,
  BackupDate = 0x10,
  FinderInfo = 0x20,
  LongName = 0x40,
  ShortName = 0x80,
  NodeId = 0x100,
  DataForkSize = 0x200,
  ResourceForkSize = 0x400,
  ExtendedDataForkSize = 0x800,
  LaunchLimit = 0x1000,
  UTF8Name = 0x2000,
  ExtendedResourceForkSize = 0x4000,
  UnixPrivileges = 0x8000,
  ALL = 0xFFFF
}

DIR_BITMAP = {
  Attributes = 0x1,
  ParentDirId = 0x2,
  CreationDate = 0x4,
  ModificationDate = 0x8,
  BackupDate = 0x10,
  FinderInfo = 0x20,
  LongName = 0x40,
  ShortName = 0x80,
  NodeId = 0x100,
  OffspringCount = 0x200,
  OwnerId = 0x400,
  GroupId = 0x800,
  AccessRights = 0x1000,
  UTF8Name = 0x2000,
  UnixPrivileges = 0x8000,
  ALL = 0xBFFF,
}

PATH_TYPE = {
  ShortName = 1,
  LongName = 2,
  UTF8Name = 3,
}

ACCESS_MODE = {
  Read = 0x1,
  Write = 0x2,
  DenyRead = 0x10,
  DenyWrite = 0x20
}

-- Access controls
ACLS = {
  OwnerSearch = 0x1,
  OwnerRead = 0x2,
  OwnerWrite = 0x4,

  GroupSearch = 0x100,
  GroupRead = 0x200,
  GroupWrite = 0x400,

  EveryoneSearch = 0x10000,
  EveryoneRead = 0x20000,
  EveryoneWrite = 0x40000,

  UserSearch = 0x100000,
  UserRead = 0x200000,
  UserWrite = 0x400000,

  BlankAccess = 0x10000000,
  UserIsOwner = 0x80000000
}

-- User authentication modules
UAM =
{
  NoUserAuth = "No User Authent",
  ClearText = "Cleartxt Passwrd",
  RandNum = "Randnum Exchange",
  TwoWayRandNum = "2-Way Randnum",
  DHCAST128 = "DHCAST128",
  DHX2 = "DHX2",
  Kerberos = "Client Krb v2",
  Reconnect = "Recon1",
}

ERROR =
{
  SocketError = 1000,
  CustomError = 0xdeadbeef,

  FPNoErr = 0,
  FPAccessDenied = -5000,
  FPAuthContinue = -5001,
  FPBadUAM = -5002,
  FPBadVersNum = -5003,
  FPBitmapErr = - 5004,
  FPCantMove = - 5005,
  FPEOFErr = -5009,
  FPItemNotFound = -5012,
  FPLockErr = -5013,
  FPMiscErr = -5014,
  FPObjectExists = -5017,
  FPObjectNotFound = -5018,
  FPParamErr = -5019,
  FPUserNotAuth = -5023,
  FPCallNotSupported = -5024,
}

MAP_ID =
{
  UserIDToName = 1,
  GroupIDToName = 2,
  UserIDToUTF8Name = 3,
  GroupIDToUTF8Name = 4,
  UserUUIDToUTF8Name = 5,
  GroupUUIDToUTF8Name = 6
}

MAP_NAME =
{
  NameToUserID = 1,
  NameToGroupID = 2,
  UTF8NameToUserID = 3,
  UTF8NameToGroupID = 4,
  UTF8NameToUserUUID = 5,
  UTF8NameToGroupUUID = 6
}


SERVERFLAGS =
{
  CopyFile = 0x01,
  ChangeablePasswords = 0x02,
  NoPasswordSaving = 0x04,
  ServerMessages = 0x08,
  ServerSignature = 0x10,
  TCPoverIP = 0x20,
  ServerNotifications = 0x40,
  Reconnect = 0x80,
  OpenDirectory = 0x100,
  UTF8ServerName = 0x200,
  UUIDs = 0x400,
  SuperClient = 0x8000
}

local ERROR_MSG = {
  [ERROR.FPAccessDenied]="Access Denied",
  [ERROR.FPAuthContinue]="Authentication is not yet complete",
  [ERROR.FPBadUAM]="Specified UAM is unknown",
  [ERROR.FPBadVersNum]="Server does not support the specified AFP version",
  [ERROR.FPBitmapErr]="Attempt was made to get or set a parameter that cannot be obtained or set with this command, or a required bitmap is null",
  [ERROR.FPCantMove]="Attempt was made to move a directory into one of its descendant directories.",
  [ERROR.FPEOFErr]="No more matches or end of fork reached.",
  [ERROR.FPLockErr]="Some or all of the requested range is locked by another user; a lock range conflict exists.",
  [ERROR.FPMiscErr]="Non-AFP error occurred.",
  [ERROR.FPObjectNotFound]="Input parameters do not point to an existing directory, file, or volume.",
  [ERROR.FPParamErr]="Parameter error.",
  [ERROR.FPObjectExists] = "File or directory already exists.",
  [ERROR.FPUserNotAuth] = "UAM failed (the specified old password doesn't match); no user is logged in yet for the specified session; authentication failed; password is incorrect.",
  [ERROR.FPItemNotFound] = "Specified APPL mapping, comment, or icon was not found in the Desktop database; specified ID is unknown.",
  [ERROR.FPCallNotSupported] = "Server does not support this command.",
}

-- Dates are shifted forward one day to avoid referencing 12/31/1969 UTC
-- when specifying 1/1/1970 (local) in a timezone that is ahead of UTC
local TIME_OFFSET = os.time({year=2000, month=1, day=2, hour=0}) - os.time({year=1970, month=1, day=2, hour=0})

-- Check if all the bits in flag are set in bitmap.
local function flag_is_set(bitmap, flag)
  return (bitmap & flag) == flag
end

-- Serialize path of a given type
-- NB: For now the actual UTF-8 encoding is ignored
local function encode_path (path)
  if path.type == PATH_TYPE.ShortName or path.type == PATH_TYPE.LongName then
    return string.pack("Bs1", path.type, path.name)
  elseif path.type == PATH_TYPE.UTF8Name then
    return string.pack(">BI4s2", path.type, 0x08000103, path.name)
  end
  assert(false, ("Unrecognized path type '%s'"):format(tostring(path.type)))
end

-- Response class returned by all functions in Proto
Response = {

  new = function(self,o)
    o = o or {}
    setmetatable(o, self)
    self.__index = self
    return o
  end,

  --- Sets the error code
  --
  -- @param code number containing the error code
  setErrorCode = function( self, code )
    self.error_code = code
  end,

  --- Gets the error code
  --
  -- @return code number containing the error code
  getErrorCode = function( self )
    return self.error_code
  end,

  --- Gets the error message
  --
  -- @return msg string containing the error
  getErrorMessage = function(self)
    if self.error_msg then
      return self.error_msg
    else
      return ERROR_MSG[self.error_code] or ("Unknown error (%d) occurred"):format(self.error_code)
    end
  end,

  --- Sets the error message
  --
  -- @param msg string containing the error message
  setErrorMessage = function(self, msg)
    self.error_code = ERROR.CustomError
    self.error_msg = msg
  end,

  --- Sets the result
  --
  -- @param result result to set
  setResult = function(self, result)
    self.result = result
  end,

  --- Get the result
  --
  -- @return result
  getResult = function(self)
    return self.result
  end,

  --- Sets the packet
  setPacket = function( self, packet )
    self.packet = packet
  end,

  getPacket = function( self )
    return self.packet
  end,

  --- Gets the packet data
  getPacketData = function(self)
    return self.packet.data
  end,

  --- Gets the packet header
  getPacketHeader = function(self)
    return self.packet.header
  end,
}

--- Proto class containing all AFP specific code
--
-- For more details consult:
-- http://developer.apple.com/mac/library/documentation/Networking/Reference/AFP_Reference/Reference/reference.html
Proto = {

  RequestId = 1,

  new = function(self,o)
    o = o or {}
    setmetatable(o, self)
    self.__index = self
    return o
  end,

  setSocket = function(self, socket)
    self.socket = socket
  end,

  --- Creates an AFP packet
  --
  -- @param command number should be one of the commands in the COMMAND table
  -- @param data_offset number holding the offset to the data
  -- @param data the actual data of the request
  create_fp_packet = function( self, command, data_offset, data )
    local reserved = 0
    local data = data or ""
    local data_len = data:len()
    local header = string.pack(">BBI2I4I4I4", FLAGS.Request, command, self.RequestId, data_offset, data_len, reserved)

    self.RequestId = self.RequestId + 1
    return header .. data
  end,

  --- Parses the FP header (first 16-bytes of packet)
  --
  -- @param packet string containing the raw packet
  -- @return table with header data containing <code>flags</code>, <code>command</code>,
  -- <code>request_id</code>, <code>error_code</code>, <code>length</code> and <code>reserved</code> fields
  parse_fp_header = function( self, packet )
    local header = {}
    local pos

    header.flags, header.command, header.request_id, pos = string.unpack( ">BBI2", packet )
    header.error_code, header.length, header.reserved, pos = string.unpack( ">i4I4I4", packet, pos )

    if header.error_code ~= 0 then
      header.error_msg = ERROR_MSG[header.error_code] or ("Unknown error: %d"):format(header.error_code)
      header.error_msg = "ERROR: " .. header.error_msg
    end
    header.raw = packet:sub(1,16)
    return header
  end,

  --- Reads a AFP packet of the socket
  --
  -- @return Response object
  read_fp_packet = function( self )

    local packet = {}
    local buf = ""
    local status, response

    status, buf = self.socket:receive_bytes(16)
    if ( not status ) then
      response = Response:new()
      response:setErrorCode(ERROR.SocketError)
      response:setErrorMessage(buf)
      return response
    end

    packet.header = self:parse_fp_header( buf )
    while buf:len() < packet.header.length + packet.header.raw:len() do
      local tmp
      status, tmp = self.socket:receive_bytes( packet.header.length + 16 - buf:len() )
      if not status then
        response = Response:new()
        response:setErrorCode(ERROR.SocketError)
        response:setErrorMessage(buf)
        return response
      end
      buf = buf .. tmp
    end

    packet.data = buf:len() > 16 and buf:sub( 17 ) or ""
    response = Response:new()
    response:setErrorCode(packet.header.error_code)
    response:setPacket(packet)

    return response
  end,

  --- Sends the raw packet over the socket
  --
  -- @param packet containing the raw data
  -- @return Response object
  send_fp_packet = function( self, packet )
    return self.socket:send(packet)
  end,

  --- Sends an DSIOpenSession request to the server and handles the response
  --
  -- @return Response object
  dsi_open_session = function( self, host, port )
    local data_offset = 0
    local option = 0x01 -- Attention Quantum
    local option_len = 4
    local quantum = 1024
    local data, packet, status

    data = string.pack( ">BBI4", option, option_len, quantum  )
    packet = self:create_fp_packet( REQUEST.OpenSession, data_offset, data )

    self:send_fp_packet( packet )
    return self:read_fp_packet()
  end,

  --- Sends an DSICloseSession request to the server and handles the response
  dsi_close_session = function( self )
    local data_offset = 0
    local option = 0x01 -- Attention Quantum
    local option_len = 4
    local quantum = 1024
    local data, packet, status

    data = ""
    packet = self:create_fp_packet( REQUEST.CloseSession, data_offset, data )

    self:send_fp_packet( packet )
  end,

  -- Sends an FPCopyFile request to the server
  --
  -- @param src_vol number containing the ID of the src file volume
  -- @param srd_did number containing the directory id of the src file
  -- @param src_path string containing the file path/name of the src file
  -- @param dst_vol number containing the ID of the dst file volume
  -- @param dst_did number containing the id of the dest. directory
  -- @param dst_path string containing the dest path (can be nil or "")
  -- @param new_name string containing the new name of the destination
  -- @return Response object
  fp_copy_file = function(self, src_vol, src_did, src_path, dst_vol, dst_did, dst_path, new_name )
    local data_offset = 0
    local unicode_names, unicode_hint = 0x03, 0x08000103
    local data, packet, response

    -- make sure we have empty names rather than nil values
    local dst_path = dst_path or ""
    local src_path = src_path or ""
    local new_name = new_name or ""

    data = string.pack(">BxI2I4I2I4", COMMAND.FPCopyFile, src_vol, src_did, dst_vol, dst_did )
           .. encode_path({type=PATH_TYPE.UTF8Name, name=src_path})
           .. encode_path({type=PATH_TYPE.UTF8Name, name=dst_path})
           .. encode_path({type=PATH_TYPE.UTF8Name, name=new_name})

    packet = self:create_fp_packet( REQUEST.Command, data_offset, data )
    self:send_fp_packet( packet )
    return self:read_fp_packet()
  end,

  --- Sends an GetStatus DSI request (which is basically a FPGetSrvrInfo
  -- AFP request) to the server and handles the response
  --
  -- @return status (true or false)
  -- @return table with server information (if status is true) or error string
  -- (if status is false)
  fp_get_server_info = function(self)
    local packet
    local data_offset = 0
    local response, result = {}, {}
    local offsets = {}
    local pos
    local status

    local data = string.pack("Bx", COMMAND.FPGetSrvrInfo)
    packet = self:create_fp_packet(REQUEST.GetStatus, data_offset, data)
    self:send_fp_packet(packet)
    response = self:read_fp_packet()

    if response:getErrorCode() ~= ERROR.FPNoErr then
      return response
    end

    packet = response.packet

    -- parse and store the offsets in the 'header'
    offsets.machine_type, offsets.afp_version_count,
      offsets.uam_count, offsets.volume_icon_and_mask, pos
      = string.unpack(">I2I2I2I2", packet.data)

    -- the flags are directly in the 'header'
    result.flags = {}
    result.flags.raw, pos = string.unpack(">I2", packet.data, pos)

    -- the short server name is stored directly in the 'header' as
    -- well
    result.server_name, pos = string.unpack("s1", packet.data, pos)

    -- Server offset should begin at an even boundary see link below
    -- http://developer.apple.com/mac/library/documentation/Networking/Reference/AFP_Reference/Reference/reference.html#//apple_ref/doc/uid/TP40003548-CH3-CHDIEGED
    if (pos + 1) % 2 ~= 0 then
      pos = pos + 1
    end

    -- and some more offsets
    offsets.server_signature, offsets.network_addresses_count,
    offsets.directory_names_count, offsets.utf8_server_name, pos
      = string.unpack(">I2I2I2I2", packet.data, pos)

    -- this sets up all the server flags in the response table as booleans
    result.flags.SuperClient = flag_is_set(result.flags.raw, SERVERFLAGS.SuperClient)
    result.flags.UUIDs = flag_is_set(result.flags.raw, SERVERFLAGS.UUIDs)
    result.flags.UTF8ServerName = flag_is_set(result.flags.raw, SERVERFLAGS.UTF8ServerName)
    result.flags.OpenDirectory = flag_is_set(result.flags.raw, SERVERFLAGS.OpenDirectory)
    result.flags.Reconnect = flag_is_set(result.flags.raw, SERVERFLAGS.Reconnect)
    result.flags.ServerNotifications = flag_is_set(result.flags.raw, SERVERFLAGS.ServerNotifications)
    result.flags.TCPoverIP = flag_is_set(result.flags.raw, SERVERFLAGS.TCPoverIP)
    result.flags.ServerSignature = flag_is_set(result.flags.raw, SERVERFLAGS.ServerSignature)
    result.flags.ServerMessages = flag_is_set(result.flags.raw, SERVERFLAGS.ServerMessages)
    result.flags.NoPasswordSaving = flag_is_set(result.flags.raw, SERVERFLAGS.NoPasswordSaving)
    result.flags.ChangeablePasswords = flag_is_set(result.flags.raw, SERVERFLAGS.ChangeablePasswords)
    result.flags.CopyFile = flag_is_set(result.flags.raw, SERVERFLAGS.CopyFile)

    -- store the machine type
    result.machine_type = string.unpack("s1", packet.data, offsets.machine_type + 1)

    -- this tells us the number of afp versions supported
    result.afp_version_count, pos = string.unpack("B", packet.data, offsets.afp_version_count + 1)

    -- now we loop through them all, storing for the response
    result.afp_versions = {}
    for i = 1,result.afp_version_count do
      local v
      v, pos = string.unpack("s1", packet.data, pos)
      table.insert(result.afp_versions, v)
    end

    -- same idea as the afp versions here
    result.uam_count, pos = string.unpack("B", packet.data, offsets.uam_count + 1)

    result.uams = {}
    for i = 1,result.uam_count do
      local uam
      uam, pos = string.unpack("s1", packet.data, pos)
      table.insert(result.uams, uam)
    end

    -- volume_icon_and_mask would normally be parsed out here,
    -- however the apple docs say it is deprecated in Mac OS X, so
    -- we don't bother with it

    -- server signature is 16 bytes
    result.server_signature = string.sub(packet.data, offsets.server_signature + 1, offsets.server_signature + 16)

    -- this is the same idea as afp_version and uam above
    result.network_addresses_count, pos = string.unpack("B", packet.data, offsets.network_addresses_count + 1)

    result.network_addresses = {}

    -- gets a little complicated in here, basically each entry has
    -- a length byte, a tag byte, and then the data. We parse
    -- differently based on the tag
    for i = 1, result.network_addresses_count do
      local length
      local tag

      length, tag, pos = string.unpack("BB", packet.data, pos)

      if tag == 0x00 then
        -- reserved, shouldn't ever come up, maybe this should
        -- return an error? maybe not, lets just ignore this
      elseif tag == 0x01 then
        -- four byte ip
        local ip
        ip, pos = string.unpack("c4", packet.data, pos)
        table.insert(result.network_addresses, ipOps.str_to_ip(ip))
      elseif tag == 0x02 then
        -- four byte ip and two byte port
        local ip, port
        ip, port, pos = string.unpack("c4 >I2", packet.data, pos)
        table.insert(result.network_addresses, string.format("%s:%d", ipOps.str_to_ip(ip), port))
      elseif tag == 0x03 then
        -- ddp address (two byte network, one byte
        -- node, one byte socket) not tested, anyone
        -- use ddp anymore?
        local network, node, socket
        network, node, socket, pos = string.unpack(">I2BB", packet.data, pos)
        table.insert(result.network_addresses, string.format("ddp %d.%d:%d", network, node, socket))
      elseif tag == 0x04 then
        -- dns name (string)
        local temp
        temp, pos = string.unpack("z", packet.data:sub(1,pos+length-3), pos)
        table.insert(result.network_addresses, temp)
      elseif tag == 0x05 then
        -- four byte ip and two byte port, client
        -- should use ssh. not tested, should work as it
        -- is the same as tag 0x02
        local ip, port
        ip, port, pos = string.unpack("c4 >I2", packet.data, pos)
        table.insert(result.network_addresses, string.format("ssh://%s:%d", ipOps.str_to_ip(ip), port))
      elseif tag == 0x06 then
        -- 16 byte ipv6
        -- not tested, but should work (next tag is
        -- tested)
        local ip
        ip, pos = string.unpack("c16", packet.data, pos)

        table.insert(result.network_addresses, ipOps.str_to_ip(ip))
      elseif tag == 0x07 then
        -- 16 byte ipv6 and two byte port
        local ip, port
        ip, port, pos = string.unpack(">c16 I2", packet.data, pos)

        table.insert(result.network_addresses,
          string.format("[%s]:%d", ipOps.str_to_ip(ip), port))
      end
    end

    -- same idea as the others here
    result.directory_names_count, pos = string.unpack("B", packet.data, offsets.directory_names_count + 1)

    result.directory_names = {}
    for i = 1, result.directory_names_count do
      local dirname
      dirname, pos = string.unpack("s1", packet.data, pos)
      table.insert(result.directory_names, dirname)
    end

    -- only one utf8 server name. note this string has a two-byte length.
    result.utf8_server_name = string.unpack(">s2", packet.data, offsets.utf8_server_name + 1)
    response.result = result

    return response
  end,


  --- Sends an FPGetUserInfo AFP request to the server and handles the response
  --
  -- @return response object with the following result <code>user_bitmap</code> and
  --     <code>uid</code> fields
  fp_get_user_info = function( self )

    local packet, pos, status, response
    local data_offset = 0
    local flags = 1 -- Default User
    local uid = 0
    local bitmap = USER_BITMAP.UserId
    local result = {}

    local data = string.pack( ">BBI4I2", COMMAND.FPGetUserInfo, flags, uid, bitmap )
    packet = self:create_fp_packet( REQUEST.Command, data_offset, data )

    self:send_fp_packet( packet )
    response = self:read_fp_packet()
    if response:getErrorCode() ~= ERROR.FPNoErr then
      return response
    end

    response.result.user_bitmap, response.result.uid, pos = string.unpack(">I2I4", packet.data)

    return response
  end,

  --- Sends an FPGetSrvrParms AFP request to the server and handles the response
  --
  -- @return response object with the following result <code>server_time</code>,
  -- <code>vol_count</code>, <code>volumes</code> fields
  fp_get_srvr_parms = function(self)
    local packet, status, data
    local data_offset = 0
    local response = {}
    local pos = 0
    local parms = {}

    data = string.pack("Bx", COMMAND.FPGetSrvParms)
    packet = self:create_fp_packet( REQUEST.Command, data_offset, data )
    self:send_fp_packet( packet )
    response = self:read_fp_packet()

    if response:getErrorCode() ~= ERROR.FPNoErr then
      return response
    end

    data = response:getPacketData()
    parms.server_time, parms.vol_count, pos = string.unpack(">I4B", data)

    parms.volumes = {}

    for i=1, parms.vol_count do
      local volume_name
      -- pos+1 to skip over the volume bitmap
      volume_name, pos = string.unpack("s1", data, pos + 1)
      table.insert(parms.volumes, string.format("%s", volume_name) )
    end

    response:setResult(parms)

    return response
  end,


  --- Sends an FPLogin request to the server and handles the response
  --
  -- This function currently only supports the 3.1 through 3.3 protocol versions
  -- It currently supports the following authentication methods:
  --   o No User Authent
  --   o DHCAST128
  --
  -- The DHCAST128 UAM should work against most servers even though it's
  -- superceded by the DHX2 UAM.
  --
  -- @param afp_version string (AFP3.3|AFP3.2|AFP3.1)
  -- @param uam string containing authentication information
  -- @return Response object
  fp_login = function( self, afp_version, uam, username, password, options )
    local packet, status, data
    local data_offset = 0
    local status, response

    if not HAVE_SSL then
      response = Response:new()
      response:setErrorMessage("OpenSSL not available, aborting ...")
      return response
    end

    -- currently we only support AFP3.3
    if afp_version == nil or ( afp_version ~= "AFP3.3" and afp_version ~= "AFP3.2" and afp_version ~= "AFP3.1" ) then
      response = Response:new()
      response:setErrorMessage("Incorrect AFP version")
      return response
    end

    if ( uam == "No User Authent" ) then
      data = string.pack( "Bs1s1", COMMAND.FPLogin, afp_version, uam )
      packet = self:create_fp_packet( REQUEST.Command, data_offset, data )
      self:send_fp_packet( packet )
      return self:read_fp_packet( )
    elseif( uam == "DHCAST128" ) then
      local dhx_s2civ, dhx_c2civ = 'CJalbert', 'LWallace'
      local p, g, Ra, Ma, Mb, K, nonce
      local EncData, PlainText, K_bin, auth_response
      local Id
      local username = username or ""
      local password = password or ""

      username = username .. string.rep('\0', (#username + 1) % 2)

      p = openssl.bignum_hex2bn("BA2873DFB06057D43F2024744CEEE75B")
      g = openssl.bignum_dec2bn("7")
      Ra = openssl.bignum_hex2bn("86F6D3C0B0D63E4B11F113A2F9F19E3BBBF803F28D30087A1450536BE979FD42")
      Ma = openssl.bignum_mod_exp(g, Ra, p)

      data = string.pack( "Bs1s1s1", COMMAND.FPLogin, afp_version, uam, username) .. openssl.bignum_bn2bin(Ma)
      packet = self:create_fp_packet( REQUEST.Command, data_offset, data )
      self:send_fp_packet( packet )
      response = self:read_fp_packet( )
      if ( response:getErrorCode() ~= ERROR.FPAuthContinue ) then
        return response
      end

      if ( response.packet.header.length ~= 50 ) then
        response:setErrorMessage("LoginContinue packet contained invalid data")
        return response
      end

      Id, Mb, EncData = string.unpack(">I2c16c32", response.packet.data )

      Mb = openssl.bignum_bin2bn( Mb )
      K = openssl.bignum_mod_exp (Mb, Ra, p)
      K_bin = openssl.bignum_bn2bin(K)
      nonce = openssl.decrypt("cast5-cbc", K_bin, dhx_s2civ, EncData, false ):sub(1,16)
      nonce = openssl.bignum_add( openssl.bignum_bin2bn(nonce), openssl.bignum_dec2bn("1") )
      PlainText = openssl.bignum_bn2bin(nonce) .. Util.ZeroPad(password, 64)
      auth_response = openssl.encrypt( "cast5-cbc", K_bin, dhx_c2civ, PlainText, true)

      data = string.pack( ">BBI2", COMMAND.FPLoginCont, 0, Id) .. auth_response
      packet = self:create_fp_packet( REQUEST.Command, data_offset, data )
      self:send_fp_packet( packet )
      response = self:read_fp_packet( )
      if ( response:getErrorCode() ~= ERROR.FPNoErr ) then
        return response
      end
      return response
    end
    response:setErrorMessage("Unsupported uam: " .. uam or "nil")
    return response
  end,

  -- Terminates sessions and frees server resources established by FPLoginand FPLoginExt.
  --
  -- @return response object
  fp_logout = function( self )
    local packet, data, response
    local data_offset = 0

    data = string.pack("Bx", COMMAND.FPLogout)
    packet = self:create_fp_packet( REQUEST.Command, data_offset, data )
    self:send_fp_packet( packet )
    return self:read_fp_packet( )
  end,

  --- Sends an FPOpenVol request to the server and handles the response
  --
  -- @param bitmap number bitmask of volume information to request
  -- @param volume_name string containing the volume name to query
  -- @return response object with the following result <code>bitmap</code> and
  --     <code>volume_id</code> fields
  fp_open_vol = function( self, bitmap, volume_name )
    local packet, status, pos, data
    local data_offset = 0
    local response, volume = {}, {}

    data = string.pack(">BxI2s1", COMMAND.FPOpenVol, bitmap, volume_name)
    packet = self:create_fp_packet( REQUEST.Command, data_offset, data )
    self:send_fp_packet( packet )
    response = self:read_fp_packet()
    if response:getErrorCode() ~= ERROR.FPNoErr then
      return response
    end

    volume.bitmap, volume.volume_id, pos = string.unpack(">I2I2", response.packet.data)
    response:setResult(volume)
    return response
  end,


  --- Sends an FPGetFileDirParms request to the server and handles the response
  --
  -- @param volume_id number containing the id of the volume to query
  -- @param did number containing the id of the directory to query
  -- @param file_bitmap number bitmask of file information to query
  -- @param dir_bitmap number bitmask of directory information to query
  -- @param path table containing the name and the name encoding type of the directory to query
  -- @return response object with the following result <code>file_bitmap</code>, <code>dir_bitmap</code>,
  --     <code>file_type</code> and (<code>dir<code> or <code>file</code> tables) depending on whether
  --     <code>did</code> is a file or directory
  fp_get_file_dir_parms = function( self, volume_id, did, file_bitmap, dir_bitmap, path )

    local packet, status, data
    local data_offset = 0
    local response, parms = {}, {}
    local pos

    if ( did == nil ) then
      response = Response:new()
      response:setErrorMessage("No Directory Id supplied")
      return response
    end

    if ( volume_id == nil ) then
      response = Response:new()
      response:setErrorMessage("No Volume Id supplied")
      return response
    end

    data = string.pack(">BxI2I4I2I2", COMMAND.FPGetFileDirParams, volume_id, did, file_bitmap, dir_bitmap)
           .. encode_path(path)
    packet = self:create_fp_packet( REQUEST.Command, data_offset, data )
    self:send_fp_packet( packet )
    response = self:read_fp_packet()

    if response:getErrorCode() ~= ERROR.FPNoErr then
      return response
    end

    parms.file_bitmap, parms.dir_bitmap, parms.file_type, pos = string.unpack( ">I2I2Bx", response.packet.data )

    -- file or dir?
    if ( parms.file_type == 0x80 ) then
      pos, parms.dir = Util.decode_dir_bitmap( parms.dir_bitmap, response.packet.data, pos )
    else
      -- file
      pos, parms.file = Util.decode_file_bitmap( parms.file_bitmap, response.packet.data, pos )
    end

    response:setResult(parms)
    return response
  end,

  --- Sends an FPEnumerateExt2 request to the server and handles the response
  --
  -- @param volume_id number containing the id of the volume to query
  -- @param did number containing the id of the directory to query
  -- @param file_bitmap number bitmask of file information to query
  -- @param dir_bitmap number bitmask of directory information to query
  -- @param req_count number
  -- @param start_index number
  -- @param reply_size number
  -- @param path table containing the name and the name encoding type of the directory to query
  -- @return response object with the following result set to a table of tables containing
  --   <code>file_bitmap</code>, <code>dir_bitmap</code>, <code>req_count</code> fields
  fp_enumerate_ext2 = function( self, volume_id, did, file_bitmap, dir_bitmap, req_count, start_index, reply_size, path )

    local packet, pos, status
    local data_offset = 0
    local response,records = {}, {}

    local data = string.pack( ">BxI2I4I2I2", COMMAND.FPEnumerateExt2, volume_id, did, file_bitmap, dir_bitmap )
                .. string.pack( ">I2I4I4", req_count, start_index, reply_size)
                .. encode_path(path)
    packet = self:create_fp_packet( REQUEST.Command, data_offset, data )

    self:send_fp_packet( packet )
    response = self:read_fp_packet( )

    if response:getErrorCode() ~= ERROR.FPNoErr then
      return response
    end

    file_bitmap, dir_bitmap, req_count, pos = string.unpack(">I2I2I2", response.packet.data)

    records = {}

    for i=1, req_count do
      local record = {}
      local len, _, ftype

      len, ftype, pos = string.unpack(">I2Bx", response.packet.data, pos)

      if ( ftype == 0x80 ) then
        _, record = Util.decode_dir_bitmap( dir_bitmap, response.packet.data, pos )
      else
        -- file
        _, record = Util.decode_file_bitmap( file_bitmap, response.packet.data, pos )
      end

      if ( len % 2 ) ~= 0 then
        len = len + 1
      end

      pos = pos + ( len - 4 )

      record.type = ftype
      table.insert(records, record)
    end

    response:setResult(records)
    return response
  end,

  --- Sends an FPOpenFork request to the server and handles the response
  --
  -- @param flag number
  -- @param volume_id number containing the id of the volume to query
  -- @param did number containing the id of the directory to query
  -- @param file_bitmap number bitmask of file information to query
  -- @param access_mode number containing bitmask of options from <code>ACCESS_MODE</code>
  -- @param path string containing the name of the directory to query
  -- @return response object with the following result contents <code>file_bitmap</code> and <code>fork_id</code>
  fp_open_fork = function( self, flag, volume_id, did, file_bitmap, access_mode, path )

    local packet
    local data_offset = 0
    local response, fork = {}, {}

    local data = string.pack( ">BBI2I4I2I2", COMMAND.FPOpenFork, flag, volume_id, did, file_bitmap, access_mode )
                 .. encode_path(path)

    packet = self:create_fp_packet( REQUEST.Command, data_offset, data )
    self:send_fp_packet( packet )
    response = self:read_fp_packet()

    if response:getErrorCode() ~= ERROR.FPNoErr then
      return response
    end

    fork.file_bitmap, fork.fork_id = string.unpack(">I2I2", response.packet.data)
    response:setResult(fork)
    return response
  end,

  --- FPCloseFork
  --
  -- @param fork number containing the fork to close
  -- @return response object
  fp_close_fork = function( self, fork )
    local packet
    local data_offset = 0
    local response = {}

    local data = string.pack( ">BxI2", COMMAND.FPCloseFork, fork )

    packet = self:create_fp_packet( REQUEST.Command, data_offset, data )
    self:send_fp_packet( packet )
    return self:read_fp_packet( )
  end,

  --- FPCreateDir
  --
  -- @param vol_id number containing the volume id
  -- @param dir_id number containing the directory id
  -- @param path table containing the name and name encoding type of the directory to query
  -- @return response object
  fp_create_dir = function( self, vol_id, dir_id, path )
    local packet
    local data_offset = 0
    local response = {}

    local data = string.pack( ">BxI2I4", COMMAND.FPCreateDir, vol_id, dir_id)
                 .. encode_path(path)

    packet = self:create_fp_packet( REQUEST.Command, data_offset, data )
    self:send_fp_packet( packet )
    return self:read_fp_packet( )
  end,

  --- Sends an FPCloseVol request to the server and handles the response
  --
  -- @param volume_id number containing the id of the volume to close
  -- @return response object
  fp_close_vol = function( self, volume_id )
    local packet
    local data_offset = 0
    local response = {}

    local data = string.pack( ">BxI2", COMMAND.FPCloseVol, volume_id )

    packet = self:create_fp_packet( REQUEST.Command, data_offset, data )
    self:send_fp_packet( packet )
    return self:read_fp_packet( )
  end,

  --- FPReadExt
  --
  -- @param fork number containing the open fork
  -- @param offset number containing the offset from where writing should start. Negative value indicates offset from the end of the fork
  -- @param count number containing the number of bytes to be written
  -- @return response object
  fp_read_ext = function( self, fork, offset, count )
    local packet, response
    local data_offset = 0
    local block_size = 1024
    local data = string.pack( ">BxI2I8I8", COMMAND.FPReadExt, fork, offset, count  )

    packet = self:create_fp_packet( REQUEST.Command, data_offset, data )
    self:send_fp_packet( packet )
    response = self:read_fp_packet( )

    if ( response:getErrorCode() == ERROR.FPEOFErr and response.packet.header.length > 0 ) then
      response:setErrorCode( ERROR.FPNoErr )
    end

    response:setResult( response.packet.data )
    return response
  end,

  --- FPWriteExt
  --
  -- @param flag number indicates whether Offset is relative to the beginning or end of the fork.
  -- @param fork number containing the open fork
  -- @param offset number containing the offset from where writing should start. Negative value indicates offset from the end of the fork
  -- @param count number containing the number of bytes to be written
  -- @param fdata string containing the data to be written
  -- @return response object
  fp_write_ext = function( self, flag, fork, offset, count, fdata )
    local packet
    local data_offset = 20
    local data

    if count > fdata:len() then
      local err = Response:new()
      err:setErrorMessage("fp_write_ext: Count is greater than the amount of data")
      return err
    end
    if count < 0 then
      local err = Response:new()
      err:setErrorMessage("fp_write_ext: Count must exceed zero")
      return err
    end

    data = string.pack( ">BBI2I8I8", COMMAND.FPWriteExt, flag, fork, offset, count) .. fdata
    packet = self:create_fp_packet( REQUEST.Write, data_offset, data )
    self:send_fp_packet( packet )
    return self:read_fp_packet( )
  end,

  --- FPCreateFile
  --
  -- @param flag number where 0 indicates a soft create and 1 indicates a hard create.
  -- @param vol_id number containing the volume id
  -- @param did number containing the ancestor directory id
  -- @param path string containing the path, including the volume, path and file name
  -- @return response object
  fp_create_file = function(self, flag, vol_id, did, path )
    local packet
    local data_offset = 0
    local data = string.pack(">BBI2I4", COMMAND.FPCreateFile, flag, vol_id, did)
                 .. encode_path(path)

    packet = self:create_fp_packet( REQUEST.Command, data_offset, data )
    self:send_fp_packet( packet )
    return self:read_fp_packet()
  end,

  --- FPMapId
  --
  -- @param subfunc number containing the subfunction to call
  -- @param id number containing th id to translate
  -- @return response object with the id in the <code>result</code> field
  fp_map_id = function( self, subfunc, id )
    local packet, response
    local data_offset = 0
    local data = string.pack( "BB", COMMAND.FPMapId, subfunc )

    if ( subfunc == MAP_ID.UserUUIDToUTF8Name or subfunc == MAP_ID.GroupUUIDToUTF8Name ) then
      data = data .. string.pack(">I8", id)
    else
      data = data .. string.pack(">I4", id)
    end

    packet = self:create_fp_packet( REQUEST.Command, data_offset, data )
    self:send_fp_packet( packet )
    response = self:read_fp_packet( )

    if response:getErrorCode() ~= ERROR.FPNoErr then
      return response
    end

    -- Netatalk returns the name with 1-byte length prefix,
    -- Mac OS has a 2-byte (UTF-8) length prefix
    local len = string.unpack("B", response.packet.data)

    -- if length is zero assume 2-byte length (UTF-8 name)
    if len == 0 then
      response:setResult(string.unpack(">s2", response.packet.data))
    else
      response:setResult(string.unpack("s1", response.packet.data ))
    end
    return response
  end,

  --- FPMapName
  --
  -- @param subfunc number containing the subfunction to call
  -- @param name string containing name to map
  -- @return response object with the mapped name in the <code>result</code> field
  fp_map_name = function( self, subfunc, name )
    local packet
    local data_offset = 0
    local data = string.pack(">BBs2", COMMAND.FPMapName, subfunc, name )
    local response

    packet = self:create_fp_packet( REQUEST.Command, data_offset, data )
    self:send_fp_packet( packet )
    response = self:read_fp_packet( )

    if response:getErrorCode() ~= ERROR.FPNoErr then
      return response
    end

    response:setResult(string.unpack(">I4", response.packet.data))
    return response
  end,
}

--- The helper class wraps the protocol class and their functions. It contains
-- high-level functions with descriptive names, facilitating the use and
-- minimizing the need to fully understand the AFP low-level protocol details.
Helper = {

  --- Creates a new helper object
  new = function(self,o)
    local o = {}
    setmetatable(o, self)
    self.__index = self
    o.username = stdnse.get_script_args("afp.username")
    o.password = stdnse.get_script_args("afp.password")
    return o
  end,

  --- Connects to the remote server and establishes a new AFP session
  --
  -- @param host table as received by the action function of the script
  -- @param port table as received by the action function of the script
  -- @return status boolean
  -- @return string containing error message (if status is false)
  OpenSession = function( self, host, port )
    local status, response

    self.socket = nmap.new_socket()
    self.socket:set_timeout( 5000 )
    status = self.socket:connect(host, port)
    if not status then
      return false, "Socket connection failed"
    end

    self.proto = Proto:new( { socket=self.socket} )
    response = self.proto:dsi_open_session(self.socket)

    if response:getErrorCode() ~= ERROR.FPNoErr then
      self.socket:close()
      return false, response:getErrorMessage()
    end

    return true
  end,

  --- Closes the AFP session and then the socket
  --
  -- @return status boolean
  -- @return string containing error message (if status is false)
  CloseSession = function( self )
    local status, packet = self.proto:dsi_close_session( )
    self.socket:close()

    return status, packet
  end,

  --- Terminates the connection, without closing the AFP session
  --
  -- @return status (always true)
  -- @return string (always "")
  Terminate = function( self )
    self.socket:close()
    return true,""
  end,

  --- Logs in to an AFP service
  --
  -- @param username (optional) string containing the username
  -- @param password (optional) string containing the user password
  -- @param options table containing additional options <code>uam</code>
  Login = function( self, username, password, options )
    local uam = ( options and options.UAM ) and options.UAM or "DHCAST128"
    local response

    -- username and password arguments override the ones supplied using the
    -- script arguments afp.username and afp.password
    local username = username or self.username
    local password = password or self.password

    if ( username and uam == "DHCAST128" ) then
      response = self.proto:fp_login( "AFP3.1", "DHCAST128", username, password )
    elseif( username ) then
      return false, ("Unsupported UAM: %s"):format(uam)
    else
      response = self.proto:fp_login( "AFP3.1", "No User Authent" )
    end

    if response:getErrorCode() ~= ERROR.FPNoErr then
      return false, response:getErrorMessage()
    end

    return true, "Success"
  end,

  --- Logs out from the AFP service
  Logout = function(self)
    return self.proto:fp_logout()
  end,

  --- Walks the directory tree specified by <code>str_path</code> and returns the node information
  --
  -- @param str_path string containing the directory
  -- @return status boolean true on success, otherwise false
  -- @return item table containing node information <code>DirectoryId</code> and <code>DirectoryName</code>
  WalkDirTree = function( self, str_path )
    local status, response
    local elements = stringaux.strsplit( "/", str_path )
    local f_bm = FILE_BITMAP.NodeId + FILE_BITMAP.ParentDirId + FILE_BITMAP.LongName
    local d_bm = DIR_BITMAP.NodeId + DIR_BITMAP.ParentDirId + DIR_BITMAP.LongName
    local item = { DirectoryId = 2 }

    response = self.proto:fp_open_vol( VOL_BITMAP.ID, elements[1] )
    if response:getErrorCode() ~= ERROR.FPNoErr then
      return false, response:getErrorMessage()
    end

    item.VolumeId = response.result.volume_id
    item.DirectoryName = str_path

    for i=2, #elements do
      local path = { type=PATH_TYPE.LongName, name=elements[i] }
      response = self.proto:fp_get_file_dir_parms( item.VolumeId, item.DirectoryId, f_bm, d_bm, path )
      if response:getErrorCode() ~= ERROR.FPNoErr then
        return false, response:getErrorMessage()
      end
      item.DirectoryId = response.result.dir.NodeId
      item.DirectoryName = response.result.dir.LongName
    end

    return true, item
  end,

  --- Reads a file on the AFP server
  --
  -- @param str_path string containing the AFP sharepoint, path and filename eg. HR/Documents/File.doc
  -- @return status boolean true on success, false on failure
  -- @return content string containing the file contents
  ReadFile = function( self, str_path )
    local status, response, fork, content, vol_name
    local offset, count, did = 0, 1024, 2
    local status, path, vol_id
    local p = Util.SplitPath( str_path )

    status, response = self:WalkDirTree( p.dir )
    if ( not status ) then
      return false, response
    end

    vol_id = response.VolumeId
    did = response.DirectoryId

    path = { type=PATH_TYPE.LongName, name=p.file }

    response = self.proto:fp_open_fork(0, vol_id, did, 0, ACCESS_MODE.Read, path )
    if response:getErrorCode() ~= ERROR.FPNoErr then
      return false, response:getErrorMessage()
    end

    fork = response.result.fork_id
    content = ""

    while true do
      response = self.proto:fp_read_ext( fork, offset, count )
      if response:getErrorCode() ~= ERROR.FPNoErr then
        break
      end
      content = content .. response.result
      offset = offset + count
    end

    response = self.proto:fp_close_fork( fork )
    if response:getErrorCode() ~= ERROR.FPNoErr then
      return false, response:getErrorMessage()
    end

    return true, content
  end,

  --- Writes a file to the AFP server
  --
  -- @param str_path string containing the AFP sharepoint, path and filename eg. HR/Documents/File.doc
  -- @param fdata string containing the data to write to the file
  -- @return status boolean true on success, false on failure
  -- @return error string containing error message if status is false
  WriteFile = function( self, str_path, fdata )
    local status, response, fork, content
    local offset, count = 1, 1024
    local status, vol_id, did, path
    local p = Util.SplitPath( str_path )

    status, response = self:WalkDirTree( p.dir )
    vol_id = response.VolumeId
    did = response.DirectoryId

    if ( not status ) then
      return false, response
    end

    path = { type=PATH_TYPE.LongName, name=p.file }

    status, response = self.proto:fp_create_file( 0, vol_id, did, path )
    if not status then
      if ( response.header.error_code ~= ERROR.FPObjectExists ) then
        return false, response.header.error_msg
      end
    end

    response = self.proto:fp_open_fork( 0, vol_id, did, 0, ACCESS_MODE.Write, path )
    if response:getErrorCode() ~= ERROR.FPNoErr then
      return false, response:getErrorMessage()
    end

    fork = response.result.fork_id

    response = self.proto:fp_write_ext( 0, fork, 0, fdata:len(), fdata )

    return true, nil
  end,

  --- Maps a user id (uid) to a user name
  --
  -- @param uid number containing the uid to resolve
  -- @return status boolean true on success, false on failure
  -- @return username string on success
  --         error string on failure
  UIDToName = function( self, uid )
    local response = self.proto:fp_map_id( MAP_ID.UserIDToName, uid )
    if response:getErrorCode() ~= ERROR.FPNoErr then
      return false, response:getErrorMessage()
    end
    return true, response.result
  end,

  --- Maps a group id (gid) to group name
  --
  -- @param gid number containing the gid to lookup
  -- @return status boolean true on success, false on failure
  -- @return groupname string on success
  --         error string on failure
  GIDToName = function( self, gid )
    local response = self.proto:fp_map_id( MAP_ID.GroupIDToName, gid )
    if response:getErrorCode() ~= ERROR.FPNoErr then
      return false, response:getErrorMessage()
    end
    return true, response.result
  end,

  --- Maps a username to a UID
  --
  -- @param name string containing the username to map to an UID
  -- @return status boolean true on success, false on failure
  -- @return UID number on success
  --         error string on failure
  NameToUID = function( self, name )
    local response = self.proto:fp_map_name( MAP_NAME.NameToUserID, name )
    if response:getErrorCode() ~= ERROR.FPNoErr then
      return false, response:getErrorMessage()
    end
    return true, response.result
  end,

  --- List the contents of a directory
  --
  -- @param str_path string containing the sharepoint and directory names
  -- @param options table options containing zero or more of the options
  -- <code>max_depth</code> and <code>dironly</code>
  -- @param depth number containing the current depth (used when called recursively)
  -- @param parent table containing information about the parent object (used when called recursively)
  -- @return status boolean true on success, false on failure
  -- @return dir table containing a table for each directory item with the following:
  --         <code>type</code>, <code>name</code>, <code>id</code>,
  --         <code>fsize</code>, <code>uid</code>, <code>gid</code>,
  --         <code>privs</code>, <code>create</code>, <code>modify</code>
  Dir = function( self, str_path, options, depth, parent )
    local status, result
    local depth = depth or 1
    local options = options or { max_depth = 1 }
    local response, records
    local f_bm = FILE_BITMAP.NodeId | FILE_BITMAP.ParentDirId
                 | FILE_BITMAP.LongName | FILE_BITMAP.UnixPrivileges
                 | FILE_BITMAP.CreationDate | FILE_BITMAP.ModificationDate
                 | FILE_BITMAP.ExtendedDataForkSize
    local d_bm = DIR_BITMAP.NodeId | DIR_BITMAP.ParentDirId
                 | DIR_BITMAP.LongName | DIR_BITMAP.UnixPrivileges
                 | DIR_BITMAP.CreationDate | DIR_BITMAP.ModificationDate

    local TYPE_DIR = 0x80

    if ( parent == nil ) then
      status, response = self:WalkDirTree( str_path )
      if ( not status ) then
        return false, response
      end

      parent = {}
      parent.vol_id = response.VolumeId
      parent.did = response.DirectoryId
      parent.dir_name = response.DirectoryName or ""
      parent.out_tbl = {}
    end

    if ( options and options.max_depth and options.max_depth > 0 and options.max_depth < depth ) then
      return false, "Max Depth Reached"
    end

    local path = { type=PATH_TYPE.LongName, name="" }
    response = self.proto:fp_enumerate_ext2( parent.vol_id, parent.did, f_bm, d_bm, 1000, 1, 1000 * 300, path)

    if response:getErrorCode() ~= ERROR.FPNoErr then
      return false, response:getErrorMessage()
    end

    records = response.result or {}
    local dir_items = {}

    for _, record in ipairs( records ) do
      local isdir = record.type == TYPE_DIR
      -- Skip non-directories if option "dironly" is set
      if isdir or not options.dironly then
        local item = {type = record.type,
                      name = record.LongName,
                      id = record.NodeId,
                      fsize = record.ExtendedDataForkSize or 0}
        local privs = (record.UnixPrivileges or {}).ua_permissions
        if privs then
          item.uid = record.UnixPrivileges.uid
          item.gid = record.UnixPrivileges.gid
          item.privs = (isdir and "d" or "-") .. Util.decode_unix_privs(privs)
        end
        item.create = Util.time_to_string(record.CreationDate)
        item.modify = Util.time_to_string(record.ModificationDate)
        table.insert( dir_items, item )
      end
      if isdir then
        self:Dir("", options, depth + 1, { vol_id = parent.vol_id, did=record.NodeId, dir_name=record.LongName, out_tbl=dir_items} )
      end
    end

    table.insert( parent.out_tbl, dir_items )

    return true, parent.out_tbl
  end,

  --- Displays a directory tree
  --
  -- @param str_path string containing the sharepoint and the directory
  -- @param options table options containing zero or more of the options
  -- <code>max_depth</code> and <code>dironly</code>
  -- @return dirtree table containing the directories
  DirTree = function( self, str_path, options )
    local options = options or {}
    options.dironly = true
    return self:Dir( str_path, options )
  end,

  --- List the AFP sharepoints
  --
  -- @return volumes table containing the sharepoints
  ListShares = function( self )
    local response
    response = self.proto:fp_get_srvr_parms( )

    if response:getErrorCode() ~= ERROR.FPNoErr then
      return false, response:getErrorMessage()
    end

    return true, response.result.volumes
  end,

  --- Determine the sharepoint permissions
  --
  -- @param vol_name string containing the name of the volume
  -- @return status boolean true on success, false on failure
  -- @return acls table containing the volume acls as returned by <code>acls_to_long_string</code>
  GetSharePermissions = function( self, vol_name )
    local status, response, acls

    response = self.proto:fp_open_vol( VOL_BITMAP.ID, vol_name )

    if response:getErrorCode() == ERROR.FPNoErr then
      local vol_id = response.result.volume_id
      local path = { type = PATH_TYPE.LongName, name = "" }

      response = self.proto:fp_get_file_dir_parms( vol_id, 2, FILE_BITMAP.ALL, DIR_BITMAP.ALL, path )
      if response:getErrorCode() == ERROR.FPNoErr then
        if ( response.result.dir and response.result.dir.AccessRights ) then
          acls = Util.acls_to_long_string(response.result.dir.AccessRights)
          acls.name = nil
        end
      end
      self.proto:fp_close_vol( vol_id )
    end

    return true, acls
  end,

  --- Gets the Unix permissions of a file
  -- @param vol_name string containing the name of the volume
  -- @param str_path string containing the name of the file
  -- @return status true on success, false on failure
  -- @return acls table (on success) containing the following fields
  --  <code>uid</code> - a numeric user identifier
  --  <code>gid</code> - a numeric group identifier
  --  <code>privs</code> - a string value representing the permissions
  --                       eg: drwx------
  -- @return err string (on failure) containing the error message
  GetFileUnixPermissions = function(self, vol_name, str_path)
    local response = self.proto:fp_open_vol( VOL_BITMAP.ID, vol_name )

    if ( response:getErrorCode() ~= ERROR.FPNoErr ) then
      return false, response:getErrorMessage()
    end

    local vol_id = response.result.volume_id
    local path = { type = PATH_TYPE.LongName, name = str_path }
    response = self.proto:fp_get_file_dir_parms( vol_id, 2, FILE_BITMAP.UnixPrivileges, DIR_BITMAP.UnixPrivileges, path )
    if ( response:getErrorCode() ~= ERROR.FPNoErr ) then
      return false, response:getErrorMessage()
    end

    local item = response.result.file or response.result.dir
    local item_type = ( response.result.file ) and "-" or "d"
    local privs = item.UnixPrivileges and item.UnixPrivileges.ua_permissions
    if ( privs ) then
      local uid = item.UnixPrivileges.uid
      local gid = item.UnixPrivileges.gid
      local str_privs = item_type .. Util.decode_unix_privs(privs)
      return true, { uid = uid, gid = gid, privs = str_privs }
    end
  end,

  --- Gets the Unix permissions of a file
  -- @param vol_name string containing the name of the volume
  -- @param str_path string containing the name of the file
  -- @return status true on success, false on failure
  -- @return size containing the size of the file in bytes
  -- @return err string (on failure) containing the error message
  GetFileSize = function( self, vol_name, str_path )
    local response = self.proto:fp_open_vol( VOL_BITMAP.ID, vol_name )

    if ( response:getErrorCode() ~= ERROR.FPNoErr ) then
      return false, response:getErrorMessage()
    end

    local vol_id = response.result.volume_id
    local path = { type = PATH_TYPE.LongName, name = str_path }
    response = self.proto:fp_get_file_dir_parms( vol_id, 2, FILE_BITMAP.ExtendedDataForkSize, 0, path )
    if ( response:getErrorCode() ~= ERROR.FPNoErr ) then
      return false, response:getErrorMessage()
    end

    return true, response.result.file and response.result.file.ExtendedDataForkSize or 0
  end,


  --- Returns the creation, modification and backup dates of a file
  -- @param vol_name string containing the name of the volume
  -- @param str_path string containing the name of the file
  -- @return status true on success, false on failure
  -- @return dates table containing the following fields:
  --  <code>create</code> - Creation date of the file
  --  <code>modify</code> - Modification date of the file
  --  <code>backup</code> - Date of last backup
  -- @return err string (on failure) containing the error message
  GetFileDates = function( self, vol_name, str_path )
    local response = self.proto:fp_open_vol( VOL_BITMAP.ID, vol_name )

    if ( response:getErrorCode() ~= ERROR.FPNoErr ) then
      return false, response:getErrorMessage()
    end

    local vol_id = response.result.volume_id
    local path = { type = PATH_TYPE.LongName, name = str_path }
    local f_bm = FILE_BITMAP.CreationDate + FILE_BITMAP.ModificationDate + FILE_BITMAP.BackupDate
    local d_bm = DIR_BITMAP.CreationDate + DIR_BITMAP.ModificationDate + DIR_BITMAP.BackupDate
    response = self.proto:fp_get_file_dir_parms( vol_id, 2, f_bm, d_bm, path )
    if ( response:getErrorCode() ~= ERROR.FPNoErr ) then
      return false, response:getErrorMessage()
    end

    local item = response.result.file or response.result.dir

    local create = Util.time_to_string(item.CreationDate)
    local backup = Util.time_to_string(item.BackupDate)
    local modify = Util.time_to_string(item.ModificationDate)

    return true, { create = create, backup = backup, modify = modify }
  end,

  --- Creates a new directory on the AFP sharepoint
  --
  -- @param str_path containing the sharepoint and the directory
  -- @return status boolean true on success, false on failure
  -- @return dirId number containing the new directory id
  CreateDir = function( self, str_path )
    local status, response, vol_id, did
    local p = Util.SplitPath( str_path )
    local path = { type=PATH_TYPE.LongName, name=p.file }


    status, response = self:WalkDirTree( p.dir )
    if not status then
      return false, response
    end

    response = self.proto:fp_create_dir( response.VolumeId, response.DirectoryId, path )
    if response:getErrorCode() ~= ERROR.FPNoErr then
      return false, response:getErrorMessage()
    end

    return true, response
  end,

}

--- Util class, containing some static functions used by Helper and Proto
Util =
{
  --- Pads a string with zeroes
  --
  -- @param str string containing the string to be padded
  -- @param len number containing the length of the new string
  -- @return str string containing the new string
  ZeroPad = function( str, len )
    return str .. string.rep('\0', len - str:len())
  end,

  --- Splits a path into two pieces, directory and file
  --
  -- @param str_path string containing the path to split
  -- @return dir table containing <code>dir</code> and <code>file</code>
  SplitPath = function( str_path )
    local elements = stringaux.strsplit("/", str_path)
    local dir, file = "", ""

    if #elements < 2 then
      return nil
    end

    file = elements[#elements]

    table.remove( elements, #elements )
    dir = table.concat( elements, "/" )

    return { ['dir']=dir, ['file']=file }

  end,

  --- Converts a group bitmask of Search, Read and Write to table
  --
  -- @param acls number containing bitmasked acls
  -- @return table of ACLs
  acl_group_to_long_string = function(acls)

    local acl_table = {}

    if ( acls & ACLS.OwnerSearch ) == ACLS.OwnerSearch then
      table.insert( acl_table, "Search")
    end

    if ( acls & ACLS.OwnerRead ) == ACLS.OwnerRead then
      table.insert( acl_table, "Read")
    end

    if ( acls & ACLS.OwnerWrite ) == ACLS.OwnerWrite then
      table.insert( acl_table, "Write")
    end

    return acl_table
  end,


  --- Converts a numeric acl to string
  --
  -- @param acls number containing acls as received from <code>fp_get_file_dir_parms</code>
  -- @return table of long ACLs
  acls_to_long_string = function( acls )

    local owner = Util.acl_group_to_long_string( ( acls & 255 ) )
    local group = Util.acl_group_to_long_string( ( (acls >> 8) & 255 ) )
    local everyone = Util.acl_group_to_long_string( ( (acls >> 16) & 255 ) )
    local user = Util.acl_group_to_long_string( ( (acls >> 24) & 255 ) )

    local blank = ( acls & ACLS.BlankAccess ) == ACLS.BlankAccess and "Blank" or nil
    local isowner = ( acls & ACLS.UserIsOwner ) == ACLS.UserIsOwner and "IsOwner" or nil

    local options = {}

    if blank then
      table.insert(options, "Blank")
    end

    if isowner then
      table.insert(options, "IsOwner")
    end

    local acls_tbl = {}

    table.insert( acls_tbl, string.format( "Owner: %s", table.concat(owner, ",") ) )
    table.insert( acls_tbl, string.format( "Group: %s", table.concat(group, ",") ) )
    table.insert( acls_tbl, string.format( "Everyone: %s", table.concat(everyone, ",") ) )
    table.insert( acls_tbl, string.format( "User: %s", table.concat(user, ",") ) )

    if #options > 0 then
      table.insert( acls_tbl, string.format( "Options: %s", table.concat(options, ",") ) )
    end

    return acls_tbl

  end,


  --- Converts AFP file timestamp to a standard text format
  --
  -- @param timestamp value returned by FPEnumerateExt2 or FPGetFileDirParms
  -- @return string representing the timestamp
  time_to_string = function (timestamp)
    return timestamp and datetime.format_timestamp(timestamp + TIME_OFFSET) or nil
  end,


  --- Decodes the UnixPrivileges.ua_permissions value
  --
  -- @param privs number containing the UnixPrivileges.ua_permissions value
  -- @return string containing the ACL characters
  decode_unix_privs = function( privs )
    local owner = ( ( privs & ACLS.OwnerRead ) == ACLS.OwnerRead ) and "r" or "-"
    owner = owner .. (( ( privs & ACLS.OwnerWrite ) == ACLS.OwnerWrite ) and "w" or "-")
    owner = owner .. (( ( privs & ACLS.OwnerSearch ) == ACLS.OwnerSearch ) and "x" or "-")

    local group = ( ( privs & ACLS.GroupRead ) == ACLS.GroupRead ) and "r" or "-"
    group = group .. (( ( privs & ACLS.GroupWrite ) == ACLS.GroupWrite ) and "w" or "-")
    group = group .. (( ( privs & ACLS.GroupSearch ) == ACLS.GroupSearch ) and "x" or "-")

    local other = ( ( privs & ACLS.EveryoneRead ) == ACLS.EveryoneRead ) and "r" or "-"
    other = other .. (( ( privs & ACLS.EveryoneWrite ) == ACLS.EveryoneWrite ) and "w" or "-")
    other = other .. (( ( privs & ACLS.EveryoneSearch ) == ACLS.EveryoneSearch ) and "x" or "-")

    return owner .. group .. other
  end,


  --- Decodes a file bitmap
  --
  -- @param bitmap number containing the bitmap
  -- @param data string containing the data to be decoded
  -- @param pos number containing the offset into data
  -- @return pos number containing the new offset after decoding
  -- @return file table containing the decoded values
  decode_file_bitmap = function( bitmap, data, pos )
    local origpos = pos
    local file = {}

    if ( ( bitmap & FILE_BITMAP.Attributes ) == FILE_BITMAP.Attributes ) then
      file.Attributes, pos = string.unpack(">I2", data, pos )
    end
    if ( ( bitmap & FILE_BITMAP.ParentDirId ) == FILE_BITMAP.ParentDirId ) then
      file.ParentDirId, pos = string.unpack(">I4", data, pos )
    end
    if ( ( bitmap & FILE_BITMAP.CreationDate ) == FILE_BITMAP.CreationDate ) then
      file.CreationDate, pos = string.unpack(">I4", data, pos )
    end
    if ( ( bitmap & FILE_BITMAP.ModificationDate ) == FILE_BITMAP.ModificationDate ) then
      file.ModificationDate, pos = string.unpack(">I4", data, pos )
    end
    if ( ( bitmap & FILE_BITMAP.BackupDate ) == FILE_BITMAP.BackupDate ) then
      file.BackupDate, pos = string.unpack(">I4", data, pos )
    end
    if ( ( bitmap & FILE_BITMAP.FinderInfo ) == FILE_BITMAP.FinderInfo ) then
      file.FinderInfo, pos = string.unpack("c32", data, pos )
    end
    if ( ( bitmap & FILE_BITMAP.LongName ) == FILE_BITMAP.LongName ) then
      local offset
      offset, pos = string.unpack(">I2", data, pos)
      if offset > 0 then
        file.LongName = string.unpack("s1", data, origpos + offset)
      end
    end
    if ( ( bitmap & FILE_BITMAP.ShortName ) == FILE_BITMAP.ShortName ) then
      local offset
      offset, pos = string.unpack(">I2", data, pos)
      if offset > 0 then
        file.ShortName = string.unpack("s1", data, origpos + offset)
      end
    end
    if ( ( bitmap & FILE_BITMAP.NodeId ) == FILE_BITMAP.NodeId ) then
      file.NodeId, pos = string.unpack(">I4", data, pos )
    end
    if ( ( bitmap & FILE_BITMAP.DataForkSize ) == FILE_BITMAP.DataForkSize ) then
      file.DataForkSize, pos = string.unpack(">I4", data, pos )
    end
    if ( ( bitmap & FILE_BITMAP.ResourceForkSize ) == FILE_BITMAP.ResourceForkSize ) then
      file.ResourceForkSize, pos = string.unpack(">I4", data, pos )
    end
    if ( ( bitmap & FILE_BITMAP.ExtendedDataForkSize ) == FILE_BITMAP.ExtendedDataForkSize ) then
      file.ExtendedDataForkSize, pos = string.unpack(">I8", data, pos )
    end
    if ( ( bitmap & FILE_BITMAP.LaunchLimit ) == FILE_BITMAP.LaunchLimit ) then
      -- should not be set as it's deprecated according to:
      -- http://developer.apple.com/mac/library/documentation/Networking/Reference/AFP_Reference/Reference/reference.html#//apple_ref/doc/c_ref/kFPLaunchLimitBit
    end
    if ( ( bitmap & FILE_BITMAP.UTF8Name ) == FILE_BITMAP.UTF8Name ) then
      local offset
      offset, pos = string.unpack(">I2", data, pos)
      if offset > 0 then
        -- +4 to skip over the encoding hint
        file.UTF8Name = string.unpack(">s2", data, origpos + offset + 4)
      end
      -- Skip over the trailing pad
      pos = pos + 4
    end
    if ( ( bitmap & FILE_BITMAP.ExtendedResourceForkSize ) == FILE_BITMAP.ExtendedResourceForkSize ) then
      file.ExtendedResourceForkSize, pos = string.unpack(">I8", data, pos )
    end
    if ( ( bitmap & FILE_BITMAP.UnixPrivileges ) == FILE_BITMAP.UnixPrivileges ) then
      local unixprivs = {}
      unixprivs.uid, unixprivs.gid, unixprivs.permissions, unixprivs.ua_permissions, pos = string.unpack(">I4I4I4I4", data, pos)
      file.UnixPrivileges = unixprivs
    end
    return pos, file
  end,

  --- Decodes a directory bitmap
  --
  -- @param bitmap number containing the bitmap
  -- @param data string containing the data to be decoded
  -- @param pos number containing the offset into data
  -- @return pos number containing the new offset after decoding
  -- @return dir table containing the decoded values
  decode_dir_bitmap = function( bitmap, data, pos )
    local origpos = pos
    local dir = {}

    if ( ( bitmap & DIR_BITMAP.Attributes ) == DIR_BITMAP.Attributes ) then
      dir.Attributes, pos = string.unpack(">I2", data, pos)
    end
    if ( ( bitmap & DIR_BITMAP.ParentDirId ) == DIR_BITMAP.ParentDirId ) then
      dir.ParentDirId, pos = string.unpack(">I4", data, pos)
    end
    if ( ( bitmap & DIR_BITMAP.CreationDate ) == DIR_BITMAP.CreationDate ) then
      dir.CreationDate, pos = string.unpack(">I4", data, pos)
    end
    if ( ( bitmap & DIR_BITMAP.ModificationDate ) == DIR_BITMAP.ModificationDate ) then
      dir.ModificationDate, pos = string.unpack(">I4", data, pos)
    end
    if ( ( bitmap & DIR_BITMAP.BackupDate ) == DIR_BITMAP.BackupDate ) then
      dir.BackupDate, pos = string.unpack(">I4", data, pos)
    end
    if ( ( bitmap & DIR_BITMAP.FinderInfo ) == DIR_BITMAP.FinderInfo ) then
      dir.FinderInfo, pos = string.unpack("c32", data, pos)
    end
    if ( ( bitmap & DIR_BITMAP.LongName ) == DIR_BITMAP.LongName ) then
      local offset
      offset, pos = string.unpack(">I2", data, pos)

      -- TODO: This really needs to be addressed someway
      -- Barely, never, ever happens, which makes it difficult to pin down
      -- http://developer.apple.com/mac/library/documentation/Networking/Reference/AFP_Reference/Reference/reference.html#//apple_ref/doc/uid/TP40003548-CH3-CHDBEHBG

      -- [nnposter, 8/1/2020] URL above not available. Offset below (pos+4)
      -- seems illogical, as it partially covers two separate fields: bottom
      -- half of the file ID and the entire offspring count.
      -- Disabled the hack, as it interfered with valid cases

      --[[
      local justkidding = string.unpack(">I4", data, pos + 4)
      if ( justkidding ~= 0 ) then
        offset = 5
      end
      ]]

      if offset > 0 then
        dir.LongName = string.unpack("s1", data, origpos + offset)
      end
    end
    if ( ( bitmap & DIR_BITMAP.ShortName ) == DIR_BITMAP.ShortName ) then
      local offset
      offset, pos = string.unpack(">I2", data, pos)
      if offset > 0 then
        dir.ShortName = string.unpack("s1", data, origpos + offset)
      end
    end
    if ( ( bitmap & DIR_BITMAP.NodeId ) == DIR_BITMAP.NodeId ) then
      dir.NodeId, pos = string.unpack(">I4", data, pos )
    end
    if ( ( bitmap & DIR_BITMAP.OffspringCount ) == DIR_BITMAP.OffspringCount ) then
      dir.OffspringCount, pos = string.unpack(">I2", data, pos )
    end
    if ( ( bitmap & DIR_BITMAP.OwnerId ) == DIR_BITMAP.OwnerId ) then
      dir.OwnerId, pos = string.unpack(">I4", data, pos )
    end
    if ( ( bitmap & DIR_BITMAP.GroupId ) == DIR_BITMAP.GroupId ) then
      dir.GroupId, pos = string.unpack(">I4", data, pos )
    end
    if ( ( bitmap & DIR_BITMAP.AccessRights ) == DIR_BITMAP.AccessRights ) then
      dir.AccessRights, pos = string.unpack(">I4", data, pos )
    end
    if ( ( bitmap & DIR_BITMAP.UTF8Name ) == DIR_BITMAP.UTF8Name ) then
      local offset
      offset, pos = string.unpack(">I2", data, pos)
      if offset > 0 then
        -- +4 to skip over the encoding hint
        dir.UTF8Name = string.unpack(">s2", data, origpos + offset + 4)
      end
      -- Skip over the trailing pad
      pos = pos + 4
    end
    if ( ( bitmap & DIR_BITMAP.UnixPrivileges ) == DIR_BITMAP.UnixPrivileges ) then
      local unixprivs = {}

      unixprivs.uid, unixprivs.gid, unixprivs.permissions, unixprivs.ua_permissions, pos = string.unpack(">I4I4I4I4", data, pos)
      dir.UnixPrivileges = unixprivs
    end
    return pos, dir
  end,

}




return _ENV;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   e           H  )                      O                    ]                      
           0                         r                  =      C                                      O                                 O  )         |  ;         V                                                    e           H  +                    e                      h             <                           h                      '  ~         d  	           (         <  Q         M  s                   z                                                            h           E                                   &           q                                        !                         l                          0                        M         h  a         E                                   4             a         E           0           0           0           0  /         0  A         0  U         0  _         0  f         0  q         E                                         0                         U      ,         O  9           @                   J           U                  ]           e         -  m                               	   <                 #                  +           S         u  `                  t            &               c  G           q         <                      `                                 S  2           V           D       t                               	         &           S         #                                                                  J      9           A            j      Y         3                             {                              S  *                  3            3      I         y  a           g                   o           A                   b         {                                        S  	                  &           8         L  I                  U                              @               v                                               R                        !           -           9           E         :  \           d           q           {                      [           h           r                                                                                   2      v           ,                           B              x                 7         h  [         S  k         1                                                          Y      	           4         S  c         Q  w                  |            :                  `               c             	            H                 3            ?      C                                                                E  +         S  G           |         {                                 {           0           S  #            x      *                  6         K  A         E  T                               {                      {           E             7           A         {  l           v         {                      {                      E             4           A         {  k         S           !           E           S           S           0           E  &           U           _         {  p                               {           0           E           {  "         0  6                   =            
      I         K  Q         E           0           !           E  E         0           0  !         E           0  G                  N            @      e         K  j         h  q         E           !                      !                        %         0  9           `           t         !                                           `               K                                    @                  
               K  '                   .            (      :         K  Q         E           0              p                                 K           S           h           E  |                    S           S             N         S  u         0           S             6            (      =                  I         K  [                  b            P      n         K                                               K                    !                  -         K  4         h  A         E  i         S  ~         S           S           S                                 S           S  1         0  =         h  Q         E                      0           E           S           S           S           0           E             )           6           C           a                                                                                                   
           0           D           a           u                                                                                        
                      $           1           >           K           X           e                                                                                                                                                          (           5           B           O           \           i           y                                                                  
           -           C           s         0                                 S           S           S          S  #        S  D          a        h  q        E          S          S  h        u          0             U                 p              K             P                                K  '           U      .           8      :        K  X        S          S  
           U                 
              K  ?                 Q           U      V        K             e                 
              K             9                               K             e                  
              K             x                 X
              K          h          E  %          5          G        0  Q        E  y        0          0          0          E             `              0          E          z  ,           `      >        S  F        0  a        E                    v                    S          0          h  !        E  ;        S  \                              
        !  8        !  J          [                  {                    0          z          {          z  	                 	                 %	        K  4	                 \	                 c	                 o	        K  	                 	           `      	        K  	        E  	          
        0  A
        E  R
          
          
        0  
        E  
          
                  0          E  "        0  N                  0             H                               K          0          E  	        S          0          E          S          0  
        E  8
        S  
        0  
        E  
        S  
        0          E           S  p        {                              0          0                              x              K          E  !        E  B        S          {                    {                    0  
        0                   %                 1        K  C                 J                 V        K  q        E          $          0          E          S  "        S  L        S          0          S                              
              K                              	      (        K  A                 H           H
      T        K  e                 l           
      x        K          h          E          S          S          S  =        0  L           	      ^                 c        K  x           h	                               K          h          E          S          S          S           0  /           0	      A                 F        K  Q        h  a        E          S                              0  .          g                 n                 z        K          E                              S          0  "          <          K          j                 q                 }        K          E          S          S          S          S          0                                    "        K  2                 9                 E        K  X                 _                 k        K                                            K                              P              K          E  #        0  ?           8      F                 R        K  b                 i                 u        K                              p              K                                             K             @                       +        K  <        0  A        E  i        0  q        E          0          E  
        S            .                    X        0  }                                           K             p                 X              K          0          E          0  B           -      I                 U        K  \        0  u                 |                         K                                            K                              x              K                              @              K          0  !        E  j        S          0                                 )           9           [           k                                          S           S  !          1!          C!        h  Q!        E  l!          !          !          !        E  !          	"          0"          D"        !  t"        0  "          "          "        0  "          "        !  #        E  #          #        0  !#        E  6#        0  A#        E  P#          s#        {  #          #        0  #        0  #                 #           (      #        K  #        0  #        E  #          #          $        0  $        E  "$        S  )$        0  5$        S  <$        0  A$        E  u$          $          $        S  $          %        0  ,%        S  R%        S  c%          w%        !  %        h  %        E  %          %        S  &        S  W&          x&        0  &        h  &        E  &        S  &                 &        S  $'        S  ,'                 ~'        0  '        h  '        E  '        z  '        0  (        E  C(          Q(          (        S  (        0  /)                 6)                 B)        K  Q)        E  w)          )        0  )          )                 )                 )        K  )        E  9*        0  l*        0  q*        E  *        0  *        E  +          %+          \+        0  +          +          ,          ,           C      ,                 -        K  !-        E  -        0  -        E  -        S  -          .          .          .        0  .          .          ,/        0  9/          V/           H      d/                 i/        K  /        0  0        0  !0        E  |0        S  0                 0        S  0                 0        S  0                  1        S  %1        0  *1        h  /1                 A1        E  p1          1        0  1        0  1        E  1           2        0  %2        0  12        E  E2        S  L2        0  Q2        E  4        0  q4        E  4        0  4        0  4        E  4          4          e5        !  w5          5          5        0  5                 5                 5        K  5                 5                 5        K  6           V      
6                 6        K  !6        E  Y6        S  p6        0  6          6          6          6        S  7        0  $7                 +7           0      77        K  R7           h      Y7                 e7        K  q7        E  8          /8        0  ;8        h  A8        E  O8        0  b8          8          8        !  8        z  8                 8        !  9        z  9          ?9          I9        {  q9        0  9        E  9          ':                 3:        S  f:        S  :        0  :        h  :        E  ;          t;        S  |;           ;      ;                 ;        S  <        0  "<        h  1<        E  |<        S  <        S  <        S  !=        0  &=        h  1=        E  =        S  =        S  =        S  =        0  >        S  ;>        S  >        h  >        E  >        S  >        S  <?        0  A?        h  Q?        E  `?          ?        0  ?          ?        E  ?          @        0  %@          C@        0  Q@        E  k@          v@        0  @        E  @        S  A          'A        S  CA        S  SA          nA        S  A        S  A          A        S  A        S  A          B        S  0B        j  sB        0  B           s      B                 B        K  B        S  C           s      	C                 C        K  ,C        h  1C        E  C        0  C        S  C        S  C        S  1D        S  \D                 cD                 oD        K  D           @      D           P      D        K  D           h      D                 D        K  D                 D                  D        K  D                 D                  	E        K  E        h  !E        E  IE        0  QE        E  ]E        0  qE        E  }E        0  E        E  E        0  E        E  E        0  E        E  E        0  F        E  OF        0  aF        E  kF        0  qF        E  F        0  F        E  G        S  eG        S  G        0  G        h  G        E  H        0  +H          H        !  #I        S  8I           a      PI        S  gI        0  I        E  I        S  I        S  J           |      ;J        0  fJ        h  qJ        E  K        0  K        3  !K        0  .K                 8K        0  AK        E  PK        0  \K        3  cK        0  qK        E  K        _  K           `      K                 &L                 .L                 RL                 ZL                 L        0  L        h  L        E  M        3  M        0  M          M                 M                 M                 N        h  N        E  zN          N        a  5O        0  QO        E  ]O        >  qO        E  O        >  P        0  !P        E  DP        >  P        0  P          Q          Q        0  1Q        E  Q        0  Q        0  Q        0  aS        0  S          T        0  !T        E  T          T        S  U          OU        S  eU        S  mU          U        0  U        S  U          U        E  V          *V        0  ;V        {  CV        0  kV        S  zV        0  V        E  V                 V          V          !W                  ;W        0  HW                 yW        0  W        E  W          W        0  W        E  4X          DX          TX          dX          tX          X          X          X          X          X          X          X          X          
Y          Y          -Y          =Y          RY          bY          rY          Y          Y          Y          Y          Y          Y          Y          Y          Z          Z          "Z          5Z          mZ          }Z          Z          Z          Z          Z          
[          7[          d[          [          [          [          \          0\          R\          h\          \          \          \          \          \          ]          0]          C]          V]          i]          |]          ]          ]          ]          ]          ^          <^          i^          ^          ^          ^          _          J_          g_          }_          _          _          _          _          _          
`           `          3`          F`          Y`          l`          `          `          `          `          `           a          %a          Ja          oa          a          a          /e          Be          Ue          he          {e          e          e          e          e          e          e           f          f          &f          9f          Lf          _f          rf          f          f          f          f          f          f          f          
g          g          0g          Cg          Vg          ig          |g          g          g          g          g          g          h          (h          >h          `h          vh          h          h          h          h          h          i          @i          Si          fi          yi          i          i          i          i          i          i          i          j          'j          Fj          Yj          lj          j          j          j          j          j          j          j          k        0  k          )k          ?k          dk          k          k          k        E  $l          /l          6l                  @l            '      m        0  m        h  m        E  Gn        0  Qn                  cn          jn            '      n                 n          n                 n          n                 n          n                 n          n                 n          o                 o          o                 o          .o           #      3o          Do           `      Vo          ]o                  ko                 uo                 o                 o                 o                 o           $      o           ,      o           4      o           <      o           D      o           L      o           T      o        h  p        E  p          q        0  q        3  q          q          q        h  q        E  r          Gr        0  Ur        0  cr        0  qr        E  s        !  _s          s        T  t          ^t          mt        ^  ut        p  t          u          =v          v          v        0  qw          ix          x          x        E  z        d  /z        0  z          z                 {           7      A{        E  T{                  b{        u  l{           a      {           U      {                  {        c  {        u  {           a      {        c  {        u  |           a      |           U      "|        c  7|        0  A|        E  |          |        0  |          |        E  |        1  |        0  U}          s}        0  }}        0  }        E  }          ~        S  F~          N~          d~        0  ~        E  ~        0  ~        0  ~        0          E           0  2        0  A        E  k        1  }        0          0          E  ?                         S          0  +                 A                         S  Ɓ          Ձ                                             ,          P          k           Q                           8              S  ΂          ݂                 4                 I                  S            ܃                     o      L                     ܄                  S          4            <        S  V          z          څ                  !           S  D        S  ^        S          S          S          S  چ        S          S  7        w            c        C          i             W               Έ                                   !  V          j        !  ɉ          ݉        !  L          `        !                    !  Ê          "        ^  /                 v                                                m      ȍ           W     ͍                     W               	                 =          k        i                    |            [          z                  f          7                                ^          i        ]  {          ڐ                              ڑ                           Y        f  a        7  i          q          {                  h          E             y        0          E  Y        S  ,        0          h          E          0                      A        E          0          <  њ          ܚ                               
                                         <  0          G          \          w                               ]      Ȟ          _        $  t           -      |                    G           E      Y           u      ^        h  q        E  |                          0          E          0           0          0  \        0  o        0          0          0          0          0          E  ġ        S  ء        S          S          0          S          E  T        S          S          S  ע        0  ܢ        h          E          0            A        E  n        S                    0          E            ͤ        0                       9                         
        K  !        E  A          K        {  R        0  a        E                    {                    0          E                      3        !  A        0  M                 T                 a        K  q        E                                        !            +          6        0  A                 H           h      T        K  a        E                              !  ѧ                  %          0            +          :        0  N                 U           0      a        K  q        E                      Ϩ        !                              0             <          K        0  _                 f                 r        K          E            ϩ                  S          S            Ǫ          ժ                  0             U                               K  ̫        h  ѫ        E            u        {          S          0  '        {  H        S             P                               K  ­        h  ѭ        E  ߭                                      E  "        m  Y        S  e        0          0          S  Ѯ        E  ۮ                  S                    0  /        0  A        E  `        [  e          q        E          <                    E                    m  ۯ                  {          0          E  6        S  >          e        0  y                  h          E          c          0            6        S  >                 S                 r        S  z                                          S                   Ǳ                 ̱        h  ѱ        E          S          S  ,        S  ;          G        0  a                                         U                 X      Ĳ        K  Ѳ        E  ڲ        m          0                      '          4        0  A        E  T          d        0  q                            0          E          S          j                                                  0  
        S                           0  (        U  3                :           Э     A           в     f           Ы     l        0  q        E          S          S             б                       մ                                            0                      %           `     0           p     A        E          0          }  Ե        S                    S  ?        S  [        !  x        S          S  ƶ        !  ֶ                         D                                            K          h          E  T          c        0  q        E          S  շ        0  ݷ                  S          S  @        S  M          x        S                      Ÿ        !  ϸ                  {            -                 4           x      @        K  O          _                    Ź        S  ޹        S          S  /        S  J        S  k        S          S          U  !        h  1        E  G          W          f        {  ~        0                      Ļ          ѻ                              .          ;          V        !  o          |                                                0                              @      ̼        K             `                               K          E          0  !        E  3        0  A        E          0  ʾ        0  վ                 ܾ           `              K          0             p                       
        K  !        E          0          E  x        0                              (              K                                            K  -                 4                 @        K  e           	      l                 x        K                              H              K                                            K          E  ]                            {          0          0                                            K          E  ?          e          s        {          0                    0                                            K          E  q        0          0          0          0          0          0          E  
        0  &        0  0                  E                      M        0  W        0  a        E          0          E          S          S                              0  l           H      s                         K                               P              K          h          E                    0  )           p      0                  ?        K  Q        E                    0             p                 8              K          E  "          E        S  n        S          0          S          !          S  L        S  d           (	      k                 w        K          h          E          E          E          S          S  /           p     :                  f        0  q        b                  S          }                                              h          E                      )          9          B        0  V                 ]                 i        K  q        E          S          0          E            w        0          h          E          S          S  )        S  D          b        S          S                    0            
                            S                              h                                            K  !        E  A          K        {  R        0  a        E                    {                    0          E          S  
        S  .        S  N        S  d        S          S          S          S          S          S  
          ,          @        !  J          [        {  u                                      S          S  %        0  4        S  T        S  d                 k           p      w        K          h          E                    S  -        S  W        0  i        h  q        E  }        c          0                    0          z                    E            X          g        0  q        E          0  !        E  [        S          0          h          E  \          f        {  x                  0          E                    {           0          E  l          v        {                              0                    {            '        {  E           8      L                 X        K  a        E                    {          E            
        {  &        {  R        {  W        h  a        E                              {            
        {  :        {  Y        0  d           I	      k                 w        K  ~        h          E                    {          {  '        0  >           d	      E                 Q        K  X        h  a        E                              0          E            W        {  s        {                    0          h          E            C        {  Z        {          h          E          S          S          0  #           	      *           0      6        K          E                    0  A        S  q        S  .        S  a        h  q        E                                                  !                    0          E  &        S  ~        S          0          E          E  !        E  ?        S                    S          0          E          E  A        E  M          j          w          |                  0          E                              {  -          X        {          0          h          E  ,        S  5        {  ?          O           h      ]           	      b        K  x        0             `                               K          {  !        E          S          0                              p              K          S                              8              K          {  =        h  Q        E          S  F        S          S          0  W                 ^                  j        K          E          0          0  1        E  H          T        0  a        0  h        0  s           	      z                         K          E          S           m  (        S  9        S  T          {        S          {          0                                                      K          h          E  M        {  Z                  {                              0                               X              K             @                                 K  !        E  J        S  ^          r        S          0   