<commit_before>import pathToRegexp from "path-to-regexp-es";

import style from "@agir/front/genericComponents/_variables.scss";
import logger from "@agir/lib/utils/logger";
import { lazy } from "./utils";

import { AUTHENTICATION } from "@agir/front/authentication/common";

const AgendaPage = lazy(() => import("@agir/events/agendaPage/AgendaPage"));
const HomePage = lazy(() => import("@agir/front/app/Homepage/Home"));
const EventMap = lazy(() => import("@agir/carte/page__eventMap/EventMap"));
const EventPage = lazy(() => import("@agir/events/eventPage/EventPage"));
const CreateEvent = lazy(() =>
  import("@agir/events/createEventPage/CreateEvent")
);
const MissingDocumentsPage = lazy(() =>
  import(
    "@agir/events/eventRequiredDocuments/MissingDocuments/MissingDocumentsPage"
  )
);
const EventRequiredDocuments = lazy(() =>
  import("@agir/events/eventRequiredDocuments/EventRequiredDocumentsPage")
);
const GroupsPage = lazy(() => import("@agir/groups/groupsPage/GroupsPage"));
const FullGroupPage = lazy(() =>
  import("@agir/groups/fullGroupPage/FullGroupPage")
);
const GroupPage = lazy(() => import("@agir/groups/groupPage/GroupPage"));

const GroupMessagePage = lazy(() =>
  import("@agir/groups/groupPage/GroupMessagePage")
);
const GroupMap = lazy(() => import("@agir/carte/page__groupMap/GroupMap"));

const ActivityPage = lazy(() =>
  import("@agir/activity/ActivityPage/ActivityPage")
);

const ToolsPage = lazy(() => import("@agir/front/toolsPage/ToolsPage"));

const NavigationPage = lazy(() =>
  import("@agir/front/navigationPage/NavigationPage")
);

const SignupPage = lazy(() =>
  import("@agir/front/authentication/Connexion/SignupPage")
);
const LoginPage = lazy(() =>
  import("@agir/front/authentication/Connexion/LoginPage")
);
const CodeLoginPage = lazy(() =>
  import("@agir/front/authentication/Connexion/Code/CodeLogin")
);
const CodeSignupPage = lazy(() =>
  import("@agir/front/authentication/Connexion/Code/CodeSignup")
);
const TellMorePage = lazy(() =>
  import("@agir/front/authentication/Connexion/TellMore/TellMorePage")
);
const LogoutPage = lazy(() =>
  import("@agir/front/authentication/Connexion/Logout")
);
const MessagePage = lazy(() => import("@agir/msgs/MessagePage/MessagePage"));
const CreateContactPage = lazy(() =>
  import("@agir/people/contacts/CreateContactPage")
);
const DonationPage = lazy(() =>
  import("@agir/donations/donationPage/DonationPage")
);

export const BASE_PATH = "/";

const log = logger(__filename);

export class RouteConfig {
  constructor(props) {
    Object.keys(props).forEach((key) => (this[key] = props[key]));

    this.__keys__ = [];
    const path = Array.isArray(this.path) ? this.path[0] : this.path;
    this.__re__ = pathToRegexp(this.path, this.__keys__, {
      end: this.exact === false ? false : true,
    });
    this.__toPath__ = pathToRegexp.compile(path);

    this.match = this.match.bind(this);
    this.getLink = this.getLink.bind(this);
  }

  /**
   * Method to match a pathname string against the RouteConfig path
   * @param  {string} pathname The pathname to match against the RouteConfig path
   * @return {boolean} True if the argument path matches, false otherwise
   */
  match(pathname) {
    return !!pathname && !!this.__re__.exec(pathname);
  }

  /**
   * Method to build a link to the RouteConfig pathname with optional URL parameters
   * @param  {object} params An object mapping the path parameters value
   * @return {string} The link path string
   */
  getLink(params) {
    try {
      params = {
        ...(this.params || {}),
        ...(params || {}),
      };
      return this.__toPath__(params);
    } catch (e) {
      log.error("Failed to generate path", e);
      return Array.isArray(this.path) ? this.path[0] : this.path;
    }
  }
}

const notificationSettingRoute = new RouteConfig({
  id: "notificationSettings",
  path: "/:root*/parametres/",
  exact: true,
  neededAuthentication: AUTHENTICATION.HARD,
  label: "Paramètres de notification",
  params: { root: "activite" },
  isPartial: true,
});

export const routeConfig = {
  events: new RouteConfig({
    id: "events",
    path: ["/", "/documents-justificatifs/"],
    exact: true,
    neededAuthentication: AUTHENTICATION.SOFT,
    label: "Événements",
    Component: AgendaPage,
    AnonymousComponent: HomePage,
    hasLayout: false,
    hideFeedbackButton: true,
    keepScroll: true,
    hideFooterBanner: true,
  }),
  missingEventDocumentsModal: new RouteConfig({
    id: "missingEventDocuments",
    path: "/documents-justificatifs/",
    exact: true,
    neededAuthentication: AUTHENTICATION.SOFT,
    label: "Documents justificatifs",
    isPartial: true,
  }),
  missingEventDocuments: new RouteConfig({
    id: "missingEventDocuments",
    path: "/evenements/documents-justificatifs/",
    exact: true,
    neededAuthentication: AUTHENTICATION.HARD,
    label: "Documents justificatifs",
    Component: MissingDocumentsPage,
    hasLayout: false,
    backLink: {
      route: "events",
      label: "Liste des événements",
      isProtected: true,
    },
  }),
  eventMap: new RouteConfig({
    id: "eventMap",
    path: "/evenements/carte/",
    exact: true,
    neededAuthentication: AUTHENTICATION.NONE,
    label: "Carte des événements",
    Component: EventMap,
    hideFooter: true,
    hideFeedbackButton: true,
  }),
  createEvent: new RouteConfig({
    id: "createEvent",
    path: "/evenements/creer/",
    exact: false,
    neededAuthentication: AUTHENTICATION.SOFT,
    label: "Nouvel événement",
    Component: CreateEvent,
    backLink: {
      route: "events",
      label: "Liste des événements",
      isProtected: true,
    },
    hideFooter: true,
  }),
  eventDetails: new RouteConfig({
    id: "eventDetails",
    path: "/evenements/:eventPk/",
    exact: true,
    neededAuthentication: AUTHENTICATION.NONE,
    label: "Détails de l'événement",
    Component: EventPage,
    backLink: {
      route: "events",
      label: "Liste des événements",
      isProtected: true,
    },
  }),
  eventSettings: new RouteConfig({
    id: "eventSettings",
    path: "/evenements/:eventPk/gestion/:activePanel?/",
    params: { activePanel: null },
    exact: true,
    neededAuthentication: AUTHENTICATION.HARD,
    label: "Paramètres de l'événement",
    Component: EventPage,
    hideFeedbackButton: true,
  }),
  eventRequiredDocuments: new RouteConfig({
    id: "eventRequiredDocuments",
    path: "/evenements/:eventPk/documents/",
    exact: true,
    neededAuthentication: AUTHENTICATION.HARD,
    label: "Documents de l'événement",
    Component: EventRequiredDocuments,
    backLink: {
      route: "events",
      label: "Liste des événements",
      isProtected: true,
    },
  }),
  groups: new RouteConfig({
    id: "groups",
    path: "/mes-groupes/",
    exact: true,
    neededAuthentication: AUTHENTICATION.SOFT,
    label: "Groupes",
    Component: GroupsPage,
    hasLayout: true,
    layoutProps: {
      smallBackgroundColor: style.black25,
    },
    keepScroll: true,
  }),
  groupMap: new RouteConfig({
    id: "groupMap",
    path: "/groupes/carte/",
    exact: true,
    neededAuthentication: AUTHENTICATION.NONE,
    label: "Carte des groupes",
    Component: GroupMap,
    hideFooter: true,
    hideFeedbackButton: true,
  }),
  fullGroup: new RouteConfig({
    id: "fullGroup",
    path: "/groupes/:groupPk/complet/",
    exact: true,
    neededAuthentication: AUTHENTICATION.NONE,
    label: "Groupe complet",
    Component: FullGroupPage,
    hasLayout: false,
  }),
  groupSettings: new RouteConfig({
    id: "groupSettings",
    path: "/groupes/:groupPk/:activeTab?/gestion/:activePanel?/",
    params: { activeTab: null, activePanel: null },
    exact: true,
    neededAuthentication: AUTHENTICATION.HARD,
    label: "Gestion du groupe",
    Component: GroupPage,
    isPartial: true,
  }),
  groupMessage: new RouteConfig({
    id: "groupMessage",
    path: "/groupes/:groupPk/message/:messagePk/",
    exact: true,
    neededAuthentication: AUTHENTICATION.SOFT,
    label: "Message du groupe",
    Component: GroupMessagePage,
    hideFeedbackButton: true,
  }),
  groupDetails: new RouteConfig({
    id: "groupDetails",
    path: "/groupes/:groupPk/:activeTab?/",
    exact: false,
    neededAuthentication: AUTHENTICATION.NONE,
    label: "Détails du groupe",
    Component: GroupPage,
    backLink: {
      route: "groups",
      label: "Retour à l'accueil",
      isProtected: true,
    },
  }),
  activities: new RouteConfig({
    id: "activities",
    path: ["/activite/", "/activite/parametres/"],
    exact: true,
    neededAuthentication: AUTHENTICATION.SOFT,
    label: "Notifications",
    Component: ActivityPage,
    hasLayout: true,
    layoutProps: {
      smallBackgroundColor: style.black25,
    },
    topBarRightLink: {
      label: notificationSettingRoute.label,
      to: notificationSettingRoute.getLink({ root: "activite" }),
      protected: true,
    },
    keepScroll: true,
  }),
  tools: new RouteConfig({
    id: "tools",
    path: "/outils/",
    exact: true,
    neededAuthentication: AUTHENTICATION.NONE,
    label: "Outils",
    Component: ToolsPage,
    hideFeedbackButton: true,
    displayFooterOnMobileApp: true,
    backLink: {
      route: "events",
      label: "Liste des événements",
      isProtected: true,
    },
  }),
  notificationSettings: notificationSettingRoute,
  menu: new RouteConfig({
    id: "menu",
    path: "/navigation/",
    exact: true,
    neededAuthentication: AUTHENTICATION.SOFT,
    label: "Menu",
    Component: NavigationPage,
    hasLayout: true,
    displayFooterOnMobileApp: true,
    layoutProps: {
      style: { paddingBottom: 0 },
    },
  }),
  login: new RouteConfig({
    id: "login",
    path: "/connexion/",
    exact: true,
    neededAuthentication: AUTHENTICATION.NONE,
    label: "Connexion",
    Component: LoginPage,
    hideTopBar: true,
    hideFeedbackButton: true,
    hideFooter: true,
  }),
  signup: new RouteConfig({
    id: "signup",
    path: "/inscription/",
    exact: true,
    neededAuthentication: AUTHENTICATION.NONE,
    label: "Inscription",
    Component: SignupPage,
    hideTopBar: true,
    hideFeedbackButton: true,
    hideFooter: true,
  }),
  codeLogin: new RouteConfig({
    id: "codeLogin",
    path: "/connexion/code/",
    exact: true,
    neededAuthentication: AUTHENTICATION.NONE,
    label: "Code de connexion",
    Component: CodeLoginPage,
    hideTopBar: true,
    hideFeedbackButton: true,
    hideFooter: true,
  }),
  codeSignup: new RouteConfig({
    id: "codeSignup",
    path: "/inscription/code/",
    exact: true,
    neededAuthentication: AUTHENTICATION.NONE,
    label: "Code d'inscription'",
    Component: CodeSignupPage,
    hideTopBar: true,
    hideFeedbackButton: true,
    hideFooter: true,
  }),
  tellMore: new RouteConfig({
    id: "tellMore",
    path: "/bienvenue/",
    exact: true,
    neededAuthentication: AUTHENTICATION.NONE,
    label: "J'en dis plus",
    Component: TellMorePage,
    hideTopBar: true,
    hideFeedbackButton: true,
    hidePushModal: true,
    hideFooter: true,
  }),
  logout: new RouteConfig({
    id: "logout",
    path: "/deconnexion/",
    exact: true,
    neededAuthentication: AUTHENTICATION.NONE,
    label: "Déconnexion",
    Component: LogoutPage,
  }),
  messages: new RouteConfig({
    id: "messages",
    path: ["/messages/:messagePk?/", "/messages/:messagePk/parametres/"],
    params: { messagePk: null },
    exact: true,
    neededAuthentication: AUTHENTICATION.HARD,
    label: "Messages",
    Component: MessagePage,
    hasLayout: false,
    hideFeedbackButton: true,
    hideFooter: true,
  }),
  createContact: new RouteConfig({
    id: "createContact",
    path: "/contacts/creer/:step?/",
    params: { step: null },
    exact: true,
    neededAuthentication: AUTHENTICATION.SOFT,
    label: "Nouveau contact",
    Component: CreateContactPage,
    hasLayout: false,
    hideFooter: true,
  }),
  donations: new RouteConfig({
    id: "donations",
    path: "/:type?/dons/",
    params: { type: null },
    exact: true,
    neededAuthentication: AUTHENTICATION.NONE,
    label: "Dons",
    Component: DonationPage,
    hasLayout: false,
    hideFeedbackButton: true,
    hideFooter: true,
    appOnlyTopBar: true,
  }),
};

export const getRouteByPathname = (pathname) => {
  return Object.values(routeConfig).find(
    (route) => route.path === pathname || route.match(pathname)
  );
};

const routes = Object.values(routeConfig).filter((route) => !route.isPartial);

export default routes;
<commit_msg>fix: donation front route label<commit_after>import pathToRegexp from "path-to-regexp-es";

import style from "@agir/front/genericComponents/_variables.scss";
import logger from "@agir/lib/utils/logger";
import { lazy } from "./utils";

import { AUTHENTICATION } from "@agir/front/authentication/common";

const AgendaPage = lazy(() => import("@agir/events/agendaPage/AgendaPage"));
const HomePage = lazy(() => import("@agir/front/app/Homepage/Home"));
const EventMap = lazy(() => import("@agir/carte/page__eventMap/EventMap"));
const EventPage = lazy(() => import("@agir/events/eventPage/EventPage"));
const CreateEvent = lazy(() =>
  import("@agir/events/createEventPage/CreateEvent")
);
const MissingDocumentsPage = lazy(() =>
  import(
    "@agir/events/eventRequiredDocuments/MissingDocuments/MissingDocumentsPage"
  )
);
const EventRequiredDocuments = lazy(() =>
  import("@agir/events/eventRequiredDocuments/EventRequiredDocumentsPage")
);
const GroupsPage = lazy(() => import("@agir/groups/groupsPage/GroupsPage"));
const FullGroupPage = lazy(() =>
  import("@agir/groups/fullGroupPage/FullGroupPage")
);
const GroupPage = lazy(() => import("@agir/groups/groupPage/GroupPage"));

const GroupMessagePage = lazy(() =>
  import("@agir/groups/groupPage/GroupMessagePage")
);
const GroupMap = lazy(() => import("@agir/carte/page__groupMap/GroupMap"));

const ActivityPage = lazy(() =>
  import("@agir/activity/ActivityPage/ActivityPage")
);

const ToolsPage = lazy(() => import("@agir/front/toolsPage/ToolsPage"));

const NavigationPage = lazy(() =>
  import("@agir/front/navigationPage/NavigationPage")
);

const SignupPage = lazy(() =>
  import("@agir/front/authentication/Connexion/SignupPage")
);
const LoginPage = lazy(() =>
  import("@agir/front/authentication/Connexion/LoginPage")
);
const CodeLoginPage = lazy(() =>
  import("@agir/front/authentication/Connexion/Code/CodeLogin")
);
const CodeSignupPage = lazy(() =>
  import("@agir/front/authentication/Connexion/Code/CodeSignup")
);
const TellMorePage = lazy(() =>
  import("@agir/front/authentication/Connexion/TellMore/TellMorePage")
);
const LogoutPage = lazy(() =>
  import("@agir/front/authentication/Connexion/Logout")
);
const MessagePage = lazy(() => import("@agir/msgs/MessagePage/MessagePage"));
const CreateContactPage = lazy(() =>
  import("@agir/people/contacts/CreateContactPage")
);
const DonationPage = lazy(() =>
  import("@agir/donations/donationPage/DonationPage")
);

export const BASE_PATH = "/";

const log = logger(__filename);

export class RouteConfig {
  constructor(props) {
    Object.keys(props).forEach((key) => (this[key] = props[key]));

    this.__keys__ = [];
    const path = Array.isArray(this.path) ? this.path[0] : this.path;
    this.__re__ = pathToRegexp(this.path, this.__keys__, {
      end: this.exact === false ? false : true,
    });
    this.__toPath__ = pathToRegexp.compile(path);

    this.match = this.match.bind(this);
    this.getLink = this.getLink.bind(this);
  }

  /**
   * Method to match a pathname string against the RouteConfig path
   * @param  {string} pathname The pathname to match against the RouteConfig path
   * @return {boolean} True if the argument path matches, false otherwise
   */
  match(pathname) {
    return !!pathname && !!this.__re__.exec(pathname);
  }

  /**
   * Method to build a link to the RouteConfig pathname with optional URL parameters
   * @param  {object} params An object mapping the path parameters value
   * @return {string} The link path string
   */
  getLink(params) {
    try {
      params = {
        ...(this.params || {}),
        ...(params || {}),
      };
      return this.__toPath__(params);
    } catch (e) {
      log.error("Failed to generate path", e);
      return Array.isArray(this.path) ? this.path[0] : this.path;
    }
  }
}

const notificationSettingRoute = new RouteConfig({
  id: "notificationSettings",
  path: "/:root*/parametres/",
  exact: true,
  neededAuthentication: AUTHENTICATION.HARD,
  label: "Paramètres de notification",
  params: { root: "activite" },
  isPartial: true,
});

export const routeConfig = {
  events: new RouteConfig({
    id: "events",
    path: ["/", "/documents-justificatifs/"],
    exact: true,
    neededAuthentication: AUTHENTICATION.SOFT,
    label: "Événements",
    Component: AgendaPage,
    AnonymousComponent: HomePage,
    hasLayout: false,
    hideFeedbackButton: true,
    keepScroll: true,
    hideFooterBanner: true,
  }),
  missingEventDocumentsModal: new RouteConfig({
    id: "missingEventDocuments",
    path: "/documents-justificatifs/",
    exact: true,
    neededAuthentication: AUTHENTICATION.SOFT,
    label: "Documents justificatifs",
    isPartial: true,
  }),
  missingEventDocuments: new RouteConfig({
    id: "missingEventDocuments",
    path: "/evenements/documents-justificatifs/",
    exact: true,
    neededAuthentication: AUTHENTICATION.HARD,
    label: "Documents justificatifs",
    Component: MissingDocumentsPage,
    hasLayout: false,
    backLink: {
      route: "events",
      label: "Liste des événements",
      isProtected: true,
    },
  }),
  eventMap: new RouteConfig({
    id: "eventMap",
    path: "/evenements/carte/",
    exact: true,
    neededAuthentication: AUTHENTICATION.NONE,
    label: "Carte des événements",
    Component: EventMap,
    hideFooter: true,
    hideFeedbackButton: true,
  }),
  createEvent: new RouteConfig({
    id: "createEvent",
    path: "/evenements/creer/",
    exact: false,
    neededAuthentication: AUTHENTICATION.SOFT,
    label: "Nouvel événement",
    Component: CreateEvent,
    backLink: {
      route: "events",
      label: "Liste des événements",
      isProtected: true,
    },
    hideFooter: true,
  }),
  eventDetails: new RouteConfig({
    id: "eventDetails",
    path: "/evenements/:eventPk/",
    exact: true,
    neededAuthentication: AUTHENTICATION.NONE,
    label: "Détails de l'événement",
    Component: EventPage,
    backLink: {
      route: "events",
      label: "Liste des événements",
      isProtected: true,
    },
  }),
  eventSettings: new RouteConfig({
    id: "eventSettings",
    path: "/evenements/:eventPk/gestion/:activePanel?/",
    params: { activePanel: null },
    exact: true,
    neededAuthentication: AUTHENTICATION.HARD,
    label: "Paramètres de l'événement",
    Component: EventPage,
    hideFeedbackButton: true,
  }),
  eventRequiredDocuments: new RouteConfig({
    id: "eventRequiredDocuments",
    path: "/evenements/:eventPk/documents/",
    exact: true,
    neededAuthentication: AUTHENTICATION.HARD,
    label: "Documents de l'événement",
    Component: EventRequiredDocuments,
    backLink: {
      route: "events",
      label: "Liste des événements",
      isProtected: true,
    },
  }),
  groups: new RouteConfig({
    id: "groups",
    path: "/mes-groupes/",
    exact: true,
    neededAuthentication: AUTHENTICATION.SOFT,
    label: "Groupes",
    Component: GroupsPage,
    hasLayout: true,
    layoutProps: {
      smallBackgroundColor: style.black25,
    },
    keepScroll: true,
  }),
  groupMap: new RouteConfig({
    id: "groupMap",
    path: "/groupes/carte/",
    exact: true,
    neededAuthentication: AUTHENTICATION.NONE,
    label: "Carte des groupes",
    Component: GroupMap,
    hideFooter: true,
    hideFeedbackButton: true,
  }),
  fullGroup: new RouteConfig({
    id: "fullGroup",
    path: "/groupes/:groupPk/complet/",
    exact: true,
    neededAuthentication: AUTHENTICATION.NONE,
    label: "Groupe complet",
    Component: FullGroupPage,
    hasLayout: false,
  }),
  groupSettings: new RouteConfig({
    id: "groupSettings",
    path: "/groupes/:groupPk/:activeTab?/gestion/:activePanel?/",
    params: { activeTab: null, activePanel: null },
    exact: true,
    neededAuthentication: AUTHENTICATION.HARD,
    label: "Gestion du groupe",
    Component: GroupPage,
    isPartial: true,
  }),
  groupMessage: new RouteConfig({
    id: "groupMessage",
    path: "/groupes/:groupPk/message/:messagePk/",
    exact: true,
    neededAuthentication: AUTHENTICATION.SOFT,
    label: "Message du groupe",
    Component: GroupMessagePage,
    hideFeedbackButton: true,
  }),
  groupDetails: new RouteConfig({
    id: "groupDetails",
    path: "/groupes/:groupPk/:activeTab?/",
    exact: false,
    neededAuthentication: AUTHENTICATION.NONE,
    label: "Détails du groupe",
    Component: GroupPage,
    backLink: {
      route: "groups",
      label: "Retour à l'accueil",
      isProtected: true,
    },
  }),
  activities: new RouteConfig({
    id: "activities",
    path: ["/activite/", "/activite/parametres/"],
    exact: true,
    neededAuthentication: AUTHENTICATION.SOFT,
    label: "Notifications",
    Component: ActivityPage,
    hasLayout: true,
    layoutProps: {
      smallBackgroundColor: style.black25,
    },
    topBarRightLink: {
      label: notificationSettingRoute.label,
      to: notificationSettingRoute.getLink({ root: "activite" }),
      protected: true,
    },
    keepScroll: true,
  }),
  tools: new RouteConfig({
    id: "tools",
    path: "/outils/",
    exact: true,
    neededAuthentication: AUTHENTICATION.NONE,
    label: "Outils",
    Component: ToolsPage,
    hideFeedbackButton: true,
    displayFooterOnMobileApp: true,
    backLink: {
      route: "events",
      label: "Liste des événements",
      isProtected: true,
    },
  }),
  notificationSettings: notificationSettingRoute,
  menu: new RouteConfig({
    id: "menu",
    path: "/navigation/",
    exact: true,
    neededAuthentication: AUTHENTICATION.SOFT,
    label: "Menu",
    Component: NavigationPage,
    hasLayout: true,
    displayFooterOnMobileApp: true,
    layoutProps: {
      style: { paddingBottom: 0 },
    },
  }),
  login: new RouteConfig({
    id: "login",
    path: "/connexion/",
    exact: true,
    neededAuthentication: AUTHENTICATION.NONE,
    label: "Connexion",
    Component: LoginPage,
    hideTopBar: true,
    hideFeedbackButton: true,
    hideFooter: true,
  }),
  signup: new RouteConfig({
    id: "signup",
    path: "/inscription/",
    exact: true,
    neededAuthentication: AUTHENTICATION.NONE,
    label: "Inscription",
    Component: SignupPage,
    hideTopBar: true,
    hideFeedbackButton: true,
    hideFooter: true,
  }),
  codeLogin: new RouteConfig({
    id: "codeLogin",
    path: "/connexion/code/",
    exact: true,
    neededAuthentication: AUTHENTICATION.NONE,
    label: "Code de connexion",
    Component: CodeLoginPage,
    hideTopBar: true,
    hideFeedbackButton: true,
    hideFooter: true,
  }),
  codeSignup: new RouteConfig({
    id: "codeSignup",
    path: "/inscription/code/",
    exact: true,
    neededAuthentication: AUTHENTICATION.NONE,
    label: "Code d'inscription'",
    Component: CodeSignupPage,
    hideTopBar: true,
    hideFeedbackButton: true,
    hideFooter: true,
  }),
  tellMore: new RouteConfig({
    id: "tellMore",
    path: "/bienvenue/",
    exact: true,
    neededAuthentication: AUTHENTICATION.NONE,
    label: "J'en dis plus",
    Component: TellMorePage,
    hideTopBar: true,
    hideFeedbackButton: true,
    hidePushModal: true,
    hideFooter: true,
  }),
  logout: new RouteConfig({
    id: "logout",
    path: "/deconnexion/",
    exact: true,
    neededAuthentication: AUTHENTICATION.NONE,
    label: "Déconnexion",
    Component: LogoutPage,
  }),
  messages: new RouteConfig({
    id: "messages",
    path: ["/messages/:messagePk?/", "/messages/:messagePk/parametres/"],
    params: { messagePk: null },
    exact: true,
    neededAuthentication: AUTHENTICATION.HARD,
    label: "Messages",
    Component: MessagePage,
    hasLayout: false,
    hideFeedbackButton: true,
    hideFooter: true,
  }),
  createContact: new RouteConfig({
    id: "createContact",
    path: "/contacts/creer/:step?/",
    params: { step: null },
    exact: true,
    neededAuthentication: AUTHENTICATION.SOFT,
    label: "Nouveau contact",
    Component: CreateContactPage,
    hasLayout: false,
    hideFooter: true,
  }),
  donations: new RouteConfig({
    id: "donations",
    path: "/:type?/dons/",
    params: { type: null },
    exact: true,
    neededAuthentication: AUTHENTICATION.NONE,
    label: "Faire un don",
    Component: DonationPage,
    hasLayout: false,
    hideFeedbackButton: true,
    hideFooter: true,
    appOnlyTopBar: true,
  }),
};

export const getRouteByPathname = (pathname) => {
  return Object.values(routeConfig).find(
    (route) => route.path === pathname || route.match(pathname)
  );
};

const routes = Object.values(routeConfig).filter((route) => !route.isPartial);

export default routes;
<|endoftext|>


<commit_before>import React from 'react'
import enhanceWithClickOutside from 'react-click-outside'
import ArrowDown from './svg/arrowdown'

class Dropdown extends React.Component {
  constructor(props) {
    super()
    this.state = {
      listVisible: false,
      selected: props.selected || props.list[0]
    }
    this.select = this.select.bind(this)
    this.toggle = this.toggle.bind(this)
  }

  select(item) {
    this.setState({ selected: item })
  }

  toggle() {
    this.setState({ listVisible: !this.state.listVisible })
  }

  handleClickOutside() {
    this.setState({ listVisible: false });
  }

  renderListItems() {
    return this.props.list.map((item, i) => (
      <div className={`dropdown-list-item ${this.state.selected === item ? "selected" : ""}`} key={i} onClick={this.select.bind(null, item)}>
        { item.name }

    ))
  }

  render() {
    // find longest list value in number of characters
    const MIN_WIDTH = this.props.list.reduce((max, { name }) =>
      (name.length > max ? name.length : max), 0)

    return (

        <div className={`dropdown-display ${this.state.listVisible ? 'list-visible' : ''}`}>
          { this.state.selected.name }

<ArrowDown />

          { this.renderListItems() }

    )
  }
}

export default enhanceWithClickOutside(Dropdown)
<commit_msg>listvisible -> isVisible<commit_after>import React from 'react'
import enhanceWithClickOutside from 'react-click-outside'
import ArrowDown from './svg/arrowdown'

class Dropdown extends React.Component {
  constructor(props) {
    super()
    this.state = {
      isVisible: false,
      selected: props.selected || props.list[0]
    }
    this.select = this.select.bind(this)
    this.toggle = this.toggle.bind(this)
  }

  select(item) {
    this.setState({ selected: item })
  }

  toggle() {
    this.setState({ isVisible: !this.state.isVisible })
  }

  handleClickOutside() {
    this.setState({ isVisible: false });
  }

  renderListItems() {
    return this.props.list.map((item, i) => (
      <div className={`dropdown-list-item ${this.state.selected === item ? "selected" : ""}`} key={i} onClick={this.select.bind(null, item)}>
        { item.name }

    ))
  }

  render() {
    // find longest list value in number of characters
    const MIN_WIDTH = this.props.list.reduce((max, { name }) =>
      (name.length > max ? name.length : max), 0)

    return (

        <div className={`dropdown-display ${this.state.isVisible ? 'is-visible' : ''}`}>
          { this.state.selected.name }

<ArrowDown />

          { this.renderListItems() }

    )
  }
}

export default enhanceWithClickOutside(Dropdown)
<|endoftext|>


<commit_before>const electron = require('electron');

const { app, BrowserWindow } = electron;
let win;

function createWindow() {
  // Create the browser window.
    win = new BrowserWindow({
        width: 1000,
        height: 800,
        autoHideMenuBar: true,
        webPreferences: {
            nodeIntegration: false,
        },
        icon: './icon.png',
    });

  // and load the index.html of the app.

    win.loadURL('https://wx.qq.com');

  // Open the DevTools.
  // win.webContents.openDevTools()

  // Emitted when the window is closed.
    win.on('close', () => {
        try {
            app.exit(0);
        } catch (e) {
            win = null;
        }
    });
    win.on('closed', () => {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
        win = null;
    });
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow);

// Quit when all windows are closed.
app.on('window-all-closed', () => {
  // On macOS it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
    app.quit();
});

app.on('activate', () => {
  // On macOS it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
    if (win === null) {
        createWindow();
    }
});
<commit_msg>add editor config & format text by editorconfig\eslint<commit_after>const electron = require('electron');

const {
    app,
    BrowserWindow,
} = electron;
let win;

function createWindow() {
    // Create the browser window.
    win = new BrowserWindow({
        width: 1000,
        height: 800,
        autoHideMenuBar: true,
        webPreferences: {
            nodeIntegration: false,
        },
        icon: './icon.png',
    });

    // and load the index.html of the app.

    win.loadURL('https://wx.qq.com');

    // Open the DevTools.
    // win.webContents.openDevTools()

    // Emitted when the window is closed.
    win.on('close', () => {
        try {
            app.exit(0);
        } catch (e) {
            win = null;
        }
    });
    win.on('closed', () => {
       // Dereference the window object, usually you would store windows
       // in an array if your app supports multi windows, this is the time
       // when you should delete the corresponding element.
        win = null;
    });
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow);

// Quit when all windows are closed.
app.on('window-all-closed', () => {
    // On macOS it is common for applications and their menu bar
    // to stay active until the user quits explicitly with Cmd + Q
    app.quit();
});

app.on('activate', () => {
    // On macOS it's common to re-create a window in the app when the
    // dock icon is clicked and there are no other windows open.
    if (win === null) {
        createWindow();
    }
});
<|endoftext|>


<commit_before>import { iconNode } from 'discourse/helpers/fa-icon';
import { createWidget } from 'discourse/widgets/widget';
import { h } from 'virtual-dom';
import RawHtml from 'discourse/widgets/raw-html';

const MAX_GUTTER_LINKS = 5;

export default createWidget('post-gutter', {
  tagName: 'div.gutter',

  defaultState() {
    return { collapsed: true };
  },

  html(attrs, state) {
    const links = this.attrs.links || [];

    const result = [];
    let toShow = links.length;
    if (state.collapsed && toShow > MAX_GUTTER_LINKS) { toShow = MAX_GUTTER_LINKS; }

    const seenTitles = {};

    let i = 0;
    while (i < links.length && result.length < toShow) {
      const l = links[i++];

      let title = l.title;
      if (title && !seenTitles[title]) {
        seenTitles[title] = true;
        const linkBody = [new RawHtml({ html: `<span>${Discourse.Emoji.unescape(title)}</span>` })];
        if (l.clicks) {
          linkBody.push(h('span.badge.badge-notification.clicks', l.clicks.toString()));
        }

        const link = h('a.track-link', { attributes: { href: l.url } }, linkBody);
        result.push(h('li', link));
      }
    }

    if (state.collapsed) {
      const remaining = links.length - MAX_GUTTER_LINKS;
      if (remaining > 0) {
        result.push(h('li', h('a.toggle-more', I18n.t('post.more_links', {count: remaining}))));
      }
    }

    if (attrs.canReplyAsNewTopic) {
      result.push(h('a.reply-new', [iconNode('plus'), I18n.t('post.reply_as_new_topic')]));
    }

    return h('ul.post-links', result);
  },

  click(e) {
    const $target = $(e.target);
    if ($target.hasClass('toggle-more')) {
      this.sendWidgetAction('showAll');
    } else if ($target.closest('.reply-new').length) {
      this.sendWidgetAction('newTopicAction');
    }
    return true;
  },

  showAll() {
    this.state.collapsed = false;
  }
});
<commit_msg>FIX: Missing `inbound` / `outbound` classes<commit_after>import { iconNode } from 'discourse/helpers/fa-icon';
import { createWidget } from 'discourse/widgets/widget';
import { h } from 'virtual-dom';
import RawHtml from 'discourse/widgets/raw-html';

const MAX_GUTTER_LINKS = 5;

export default createWidget('post-gutter', {
  tagName: 'div.gutter',

  defaultState() {
    return { collapsed: true };
  },

  html(attrs, state) {
    const links = this.attrs.links || [];

    const result = [];
    let toShow = links.length;
    if (state.collapsed && toShow > MAX_GUTTER_LINKS) { toShow = MAX_GUTTER_LINKS; }

    const seenTitles = {};

    let i = 0;
    while (i < links.length && result.length < toShow) {
      const l = links[i++];

      let title = l.title;
      if (title && !seenTitles[title]) {
        seenTitles[title] = true;
        const linkBody = [new RawHtml({ html: `<span>${Discourse.Emoji.unescape(title)}</span>` })];
        if (l.clicks) {
          linkBody.push(h('span.badge.badge-notification.clicks', l.clicks.toString()));
        }

        const className = l.reflection ? 'inbound' : 'outbound';
        const link = h('a.track-link', { className, attributes: { href: l.url } }, linkBody);
        result.push(h('li', link));
      }
    }

    if (state.collapsed) {
      const remaining = links.length - MAX_GUTTER_LINKS;
      if (remaining > 0) {
        result.push(h('li', h('a.toggle-more', I18n.t('post.more_links', {count: remaining}))));
      }
    }

    if (attrs.canReplyAsNewTopic) {
      result.push(h('a.reply-new', [iconNode('plus'), I18n.t('post.reply_as_new_topic')]));
    }

    return h('ul.post-links', result);
  },

  click(e) {
    const $target = $(e.target);
    if ($target.hasClass('toggle-more')) {
      this.sendWidgetAction('showAll');
    } else if ($target.closest('.reply-new').length) {
      this.sendWidgetAction('newTopicAction');
    }
    return true;
  },

  showAll() {
    this.state.collapsed = false;
  }
});
<|endoftext|>


<commit_before>Prism.languages.go = Prism.languages.extend('clike', {
	'keyword': /\b(break|case|chan|const|continue|default|defer|else|fallthrough|for|func|go(to)?|if|import|interface|map|package|range|return|select|struct|switch|type|var)\b/g,
	'builtin': /\b(bool|byte|complex(64|128)|error|float(32|64)|rune|string|u?int(8|16|32|64|)|uintptr|append|cap|close|complex|copy|delete|imag|len|make|new|panic|print(ln)?|real|recover)\b/g,
	'boolean': /\b(_|iota|nil|true|false)\b/g,
	'operator': /([(){}\[\]]|[*\/%^!]=?|\+[=+]?|-[>=-]?|\|[=|]?|>[=>]?|<(<|[=-])?|==?|&(&|=|^=?)?|\.(\.\.)?|[,;]|:=?)/g,
	'number': /\b(-?(0x[a-f\d]+|(\d+\.?\d*|\.\d+)(e[-+]?\d+)?)i?)\b/ig,
	'string': /("|'|`)(\\?.|\r|\n)*?\1/g
});
delete Prism.languages.go['class-name'];
<commit_msg>replace entities with their literals<commit_after>Prism.languages.go = Prism.languages.extend('clike', {
	'keyword': /\b(break|case|chan|const|continue|default|defer|else|fallthrough|for|func|go(to)?|if|import|interface|map|package|range|return|select|struct|switch|type|var)\b/g,
	'builtin': /\b(bool|byte|complex(64|128)|error|float(32|64)|rune|string|u?int(8|16|32|64|)|uintptr|append|cap|close|complex|copy|delete|imag|len|make|new|panic|print(ln)?|real|recover)\b/g,
	'boolean': /\b(_|iota|nil|true|false)\b/g,
	'operator': /([(){}\[\]]|[*\/%^!]=?|\+[=+]?|-[>=-]?|\|[=|]?|>[=>]?|<(<|[=-])?|==?|&(&|=|^=?)?|\.(\.\.)?|[,;]|:=?)/g,
	'number': /\b(-?(0x[a-f\d]+|(\d+\.?\d*|\.\d+)(e[-+]?\d+)?)i?)\b/ig,
	'string': /("|'|`)(\\?.|\r|\n)*?\1/g
});
delete Prism.languages.go['class-name'];
<|endoftext|>


<commit_before>/**
 *
 *      ioBroker mqtt Adapter
 *
 *      (c) 2014 bluefox
 *
 *      MIT License
 *
 */

var mqtt =    require('mqtt');
var adapter = require(__dirname + '/../../lib/adapter.js')('mqtt');

var client = null;
var server = null;
var values = {};
var states = {};

var messageboxLen = 11;// '.messagebox'.length;

function decrypt(key, value) {
    var result = "";
    for (var i = 0; i < value.length; ++i) {
        result += String.fromCharCode(key[i % key.length].charCodeAt(0) ^ value.charCodeAt(i));
    }
    return result;
}

adapter.on('message', function (obj) {
    if (obj) processMessage(obj);
    processMessages();
});

adapter.on('ready', function () {
    var fs = require('fs');
    adapter.config.pass = decrypt("Zgfr56gFe87jJOM", adapter.config.pass);
    if (adapter.config.ssl && adapter.config.type == 'server') {
        // Read the certificates and store it under
        // "adapter/mqtt/cert/privatekey.pem" and
        // "adapter/mqtt/cert/certificate.pem"
        // because mqtt does not support certificates not from file
        adapter.getForeignObject('system.config', function (err, obj) {
            if (err || !obj || !obj.common.certificates || !obj.common.certificates[adapter.config.certPublic] || !obj.common.certificates[adapter.config.certPrivate]) {
                adapter.log.error('Cannot enable secure MQTT server, because no certificates found: ' + adapter.config.certPublic + ', ' + adapter.config.certPrivate);
                setTimeout(function () {
                    process.exit(1);
                }, 500);
            } else {
                // Take care about flash disk and do not write the same
                if (!fs.existsSync(__dirname + '/cert')) {
                    fs.mkdirSync(__dirname + '/cert');
                    fs.writeFileSync(__dirname + '/cert/privatekey.pem', obj.common.certificates[adapter.config.certPrivate]);
                    fs.writeFileSync(__dirname + '/cert/certificate.pem', obj.common.certificates[adapter.config.certPublic]);
                } else {
                    var cert;
                    if (!fs.existsSync(__dirname + '/cert/privatekey.pem')) {
                        fs.writeFileSync(__dirname + '/cert/privatekey.pem', obj.common.certificates[adapter.config.certPrivate]);
                    } else {
                        cert = fs.readFileSync(__dirname + '/cert/privatekey.pem');
                        if (cert != obj.common.certificates[adapter.config.certPrivate]) {
                            fs.writeFileSync(__dirname + '/cert/privatekey.pem', obj.common.certificates[adapter.config.certPrivate]);
                        }
                    }
                    if (!fs.existsSync(__dirname + '/cert/certificate.pem')) {
                        fs.writeFileSync(__dirname + '/cert/certificate.pem', obj.common.certificates[adapter.config.certPublic]);
                    } else {
                        cert = fs.readFileSync(__dirname + '/cert/certificate.pem');
                        if (cert != obj.common.certificates[adapter.config.certPublic]) {
                            fs.writeFileSync(__dirname + '/cert/certificate.pem', obj.common.certificates[adapter.config.certPublic]);
                        }
                    }
                }

                main();
            }
        });
    } else {
        // Delete certificates if they exist
        if (fs.existsSync(__dirname + '/cert/privatekey.pem')) {
            fs.unlinkSync(__dirname + '/cert/privatekey.pem');
        }
        if (fs.existsSync(__dirname + '/cert/certificate.pem')) {
            fs.unlinkSync(__dirname + '/cert/certificate.pem');
        }

        if (fs.existsSync(__dirname + '/cert')) fs.rmdirSync(__dirname + '/cert');

        // Start
        main();
    }
});

adapter.on('unload', function () {
    if (client) {
        client.end();
        client = null;
    }

    if (server) {
        for (var k in server.clients) {
            server.clients[k].stream.end();
        }
        server.clients = {};
        server = null;
    }
});

// is called if a subscribed state changes
adapter.on('stateChange', function (id, state) {
    // you can use the ack flag to detect if state is desired or acknowledged
    if (state.ack && (id.length <= messageboxLen || id.substring(id.length - messageboxLen) != '.messagebox')) {
        var old = states[id] ? states[id].val : null;
        states[id] = state;
        if (!adapter.config.onchange || old !== state.val) {
            id = id.replace(/\./g, '/');
            if (server) {
                for (var k in server.clients) {
                    server.clients[k].publish({topic: adapter.config.prefix + id, payload: state2string(state.val)});
                }
            } else if (client) {
                client.publish(adapter.config.prefix + id, state2string(state.val));
            }
        }
    }
});

function processMessage(obj) {
    if (!obj || !obj.command) return;
    switch (obj.command) {
        case 'test': {
            // Try to connect to mqtt broker
            if (obj.callback && obj.message) {
                var _url = 'mqtt://' + (obj.message.user ? (obj.message.user + ':' + obj.message.pass + '@') : '') + obj.message.url + (obj.message.port ? (':' + obj.message.port) : '') + '?clientId=ioBroker.' + adapter.namespace;
                var _client = mqtt.connect(_url);
                // Set timeout for connection
                var timeout = setTimeout(function () {
                    _client.end();
                    adapter.sendTo(obj.from, obj.command, 'timeout', obj.callback);
                }, 2000);

                // If connected, return success
                _client.on('connect', function () {
                    _client.end();
                    clearTimeout(timeout);
                    adapter.sendTo(obj.from, obj.command, 'connected', obj.callback);
                });
            }
        }
    }
}

function processMessages() {
    adapter.getMessage(function (err, obj) {
        if (obj) {
            processMessage(obj.command, obj.message);
            processMessages();
        }
    });
}

function state2string(val) {
    return (val === null) ? 'null' : (val === undefined ? 'undefined' : val.toString());
}

function createClient(config) {
    var _url  = ((!config.ssl) ? 'mqtt' : 'mqtts') + '://' + (config.user ? (config.user + ':' + config.pass + '@') : '') + config.url + (config.port ? (':' + config.port) : '') + '?clientId=ioBroker.' + adapter.namespace;
    var __url = ((!config.ssl) ? 'mqtt' : 'mqtts') + '://' + (config.user ? (config.user + ':*******************@') : '') + config.url + (config.port ? (':' + config.port) : '') + '?clientId=ioBroker.' + adapter.namespace;
    adapter.log.info('Try to connect to ' + __url);
    client = mqtt.connect(_url);

    // By default subscribr on all topics
    if (!config.patterns) config.patterns = '#';

    if (typeof config.patterns == 'string') {
        config.patterns = config.patterns.split(',');
    }

    for (var i = 0; i < config.patterns.length; i++) {
        config.patterns[i] = config.patterns[i].trim();
        client.subscribe(config.patterns[i]);
    }

    client.on('message', function (topic, message) {
        if (!topic) return;
        if (config.debug) adapter.log.info(topic + ' : ' + message);

        // Ignore message if value does not changed
        if (config.onchange) {
            var oldValue = values[topic];
            if (oldValue !== undefined && oldValue == message) {
                return;
            } else {
                values[topic] = message;
            }
        }
        // Remove own prefix
        if (config.prefix && topic.substring(0, config.prefix.length) == config.prefix) topic = topic.substring(config.prefix.length);

        topic = topic.replace(/\//g, '.');
        if (topic[0] == '.') topic = topic.substring(1);
        adapter.setState(topic.replace(/\//g, '.'), {val: message, ack: true});
    });

    client.on('connect', function () {
        adapter.log.info('Connected to ' + config.url);
        if (config.publishAllOnStart) {
            for (var id in states) {
                if (id.length <= messageboxLen || id.substring(id.length - messageboxLen) != '.messagebox') {
                    client.publish(config.prefix + id.replace(/\./g, '/'), state2string(states[id].val));
                }
            }
        }
    });

    client.on('error', function (err) {
        adapter.log.error(err);
    });
}

function createServer(config) {
    var cltFunction = function (client) {
        var self = this;

        if (!self.clients) self.clients = {};

        client.on('connect', function (packet) {
            client.id = packet.clientId;
            if (config.user) {
                if (config.user != packet.username ||
                    config.pass != packet.password) {
                    adapter.log.warn('Client [' + packet.clientId + '] has invalid password(' + packet.password + ') or username(' + packet.username + ')');
                    client.connack({returnCode: 4});
                    if (self.clients[client.id]) delete self.clients[client.id];
                    client.stream.end();
                    return;
                }
            }

            adapter.log.info('Client [' + packet.clientId + '] connected');
            client.connack({returnCode: 0});
            self.clients[client.id] = client;

            // Send all subscribed variables to client
            if (config.publishAllOnStart) {
                for (var id in states) {
                    if (id.length <= messageboxLen || id.substring(id.length - messageboxLen) != '.messagebox') {
                        client.publish({topic: config.prefix + id.replace(/\./g, '/'), payload: state2string(states[id].val)});
                    }
                }
            }
        });

        client.on('publish', function (packet) {
            if (config.debug) adapter.log.info('Client [' + client.id + '] publishes "' + packet.topic + '": ' +  packet.payload);
            for (var k in self.clients) {
                self.clients[k].publish({topic: packet.topic, payload: packet.payload});
            }
            var topic = packet.topic;

            // Remove own prefix if
            if (config.prefix && topic.substring(0, config.prefix.length) == config.prefix) topic = topic.substring(config.prefix.length);

            topic = topic.replace(/\//g, '.');

            if (states[topic] === undefined) {
                topic = adapter.namespace + ((topic[0] == '.') ? topic : ('.' + topic));
            }

            adapter.setState(topic, {val: packet.payload, ack: true});
        });

        client.on('subscribe', function (packet) {
            var granted = [];
            for (var i = 0; i < packet.subscriptions.length; i++) {
                adapter.log.info('Client [' + client.id + '] subscribes on "' + packet.subscriptions[i].topic + '"');
                granted.push(packet.subscriptions[i].qos);
            }

            client.suback({granted: granted, messageId: packet.messageId});
        });

        client.on('pingreq', function (packet) {
            adapter.log.debug('Client [' + client.id + '] pingreq');
            client.pingresp();
        });

        client.on('disconnect', function (packet) {
            adapter.log.info('Client [' + client.id + '] disconnected');
            client.stream.end();
        });

        client.on('close', function (err) {
            adapter.log.info('Client [' + client.id + '] closed');
            delete self.clients[client.id];
        });

        client.on('error', function (err) {
            adapter.log.error('[' + client.id + '] ' + err);

            if (!self.clients[client.id]) return;

            delete self.clients[client.id];
            client.stream.end();
        });
    };

    if (!config.ssl) {
        adapter.log.info('Started MQTT ' + (config.user ? 'authenticated ' : '') + 'server on port ' + (config.port || 1883));
        server = mqtt.createServer(cltFunction).listen(config.port || 1883);
    } else {
        adapter.log.info('Started MQTTs security ' + (config.user ? 'authenticated ' : '') + 'server on port ' + (config.port || 1883));
        server = mqtt.createSecureServer(
            __dirname + '/cert/privatekey.pem',
            __dirname + '/cert/certificate.pem',
            cltFunction).listen(config.port || 1883);
    }
}

var cnt = 0;
function readStatesForPattern(pattern) {
    adapter.getForeignStates(pattern, function (err, res) {
        if (!err && res) {
            if (!states) states = {};

            for (var id in res) {
                if (id.length <= messageboxLen || id.substring(id.length - messageboxLen) != '.messagebox') {
                    states[id] = res[id];
                }
            }
        }
        cnt--;
        // If all patters answered, start client or server
        if (!cnt) {
            if (adapter.config.type == 'client') {
                createClient(adapter.config);
            } else {
                createServer(adapter.config);
            }
        }
    });
}

function main() {
    // Subscribe on own variables to publish it
    if (adapter.config.publish) {
        var parts = adapter.config.publish.split(',');
        for (var t = 0; t < parts.length; t++) {
            parts[t] = parts[t].trim();
            adapter.subscribeForeignStates(parts[t].trim());
            cnt++;
            readStatesForPattern(parts[t]);
        }
    }
    // If no subscription, start client or server
    if (!cnt) {
        if (adapter.config.type == 'client') {
            createClient(adapter.config);
        } else {
            createServer(adapter.config);
        }
    }
}

<commit_msg>(bluefox) read certificates from new location<commit_after>/**
 *
 *      ioBroker mqtt Adapter
 *
 *      (c) 2014 bluefox
 *
 *      MIT License
 *
 */

var mqtt =    require('mqtt');
var adapter = require(__dirname + '/../../lib/adapter.js')('mqtt');

var client = null;
var server = null;
var values = {};
var states = {};

var messageboxLen = 11;// '.messagebox'.length;

function decrypt(key, value) {
    var result = "";
    for (var i = 0; i < value.length; ++i) {
        result += String.fromCharCode(key[i % key.length].charCodeAt(0) ^ value.charCodeAt(i));
    }
    return result;
}

adapter.on('message', function (obj) {
    if (obj) processMessage(obj);
    processMessages();
});

adapter.on('ready', function () {
    var fs = require('fs');
    adapter.config.pass = decrypt("Zgfr56gFe87jJOM", adapter.config.pass);
    if (adapter.config.ssl && adapter.config.type == 'server') {
        // Read the certificates and store it under
        // "adapter/mqtt/cert/privatekey.pem" and
        // "adapter/mqtt/cert/certificate.pem"
        // because mqtt does not support certificates not from file
        adapter.getForeignObject('system.certificates', function (err, obj) {
            if (err || !obj || !obj.certificates || !obj.certificates[adapter.config.certPublic] || !obj.certificates[adapter.config.certPrivate]) {
                adapter.log.error('Cannot enable secure MQTT server, because no certificates found: ' + adapter.config.certPublic + ', ' + adapter.config.certPrivate);
                setTimeout(function () {
                    process.exit(1);
                }, 500);
            } else {
                // Take care about flash disk and do not write the same
                if (!fs.existsSync(__dirname + '/cert')) {
                    fs.mkdirSync(__dirname + '/cert');
                    fs.writeFileSync(__dirname + '/cert/privatekey.pem', obj.certificates[adapter.config.certPrivate]);
                    fs.writeFileSync(__dirname + '/cert/certificate.pem', obj.certificates[adapter.config.certPublic]);
                } else {
                    var cert;
                    if (!fs.existsSync(__dirname + '/cert/privatekey.pem')) {
                        fs.writeFileSync(__dirname + '/cert/privatekey.pem', obj.certificates[adapter.config.certPrivate]);
                    } else {
                        cert = fs.readFileSync(__dirname + '/cert/privatekey.pem');
                        if (cert != obj.certificates[adapter.config.certPrivate]) {
                            fs.writeFileSync(__dirname + '/cert/privatekey.pem', obj.certificates[adapter.config.certPrivate]);
                        }
                    }
                    if (!fs.existsSync(__dirname + '/cert/certificate.pem')) {
                        fs.writeFileSync(__dirname + '/cert/certificate.pem', obj.certificates[adapter.config.certPublic]);
                    } else {
                        cert = fs.readFileSync(__dirname + '/cert/certificate.pem');
                        if (cert != obj.certificates[adapter.config.certPublic]) {
                            fs.writeFileSync(__dirname + '/cert/certificate.pem', obj.certificates[adapter.config.certPublic]);
                        }
                    }
                }

                main();
            }
        });
    } else {
        // Delete certificates if they exist
        if (fs.existsSync(__dirname + '/cert/privatekey.pem')) {
            fs.unlinkSync(__dirname + '/cert/privatekey.pem');
        }
        if (fs.existsSync(__dirname + '/cert/certificate.pem')) {
            fs.unlinkSync(__dirname + '/cert/certificate.pem');
        }

        if (fs.existsSync(__dirname + '/cert')) fs.rmdirSync(__dirname + '/cert');

        // Start
        main();
    }
});

adapter.on('unload', function () {
    if (client) {
        client.end();
        client = null;
    }

    if (server) {
        for (var k in server.clients) {
            server.clients[k].stream.end();
        }
        server.clients = {};
        server = null;
    }
});

// is called if a subscribed state changes
adapter.on('stateChange', function (id, state) {
    // you can use the ack flag to detect if state is desired or acknowledged
    if (state.ack && (id.length <= messageboxLen || id.substring(id.length - messageboxLen) != '.messagebox')) {
        var old = states[id] ? states[id].val : null;
        states[id] = state;
        if (!adapter.config.onchange || old !== state.val) {
            id = id.replace(/\./g, '/');
            if (server) {
                for (var k in server.clients) {
                    server.clients[k].publish({topic: adapter.config.prefix + id, payload: state2string(state.val)});
                }
            } else if (client) {
                client.publish(adapter.config.prefix + id, state2string(state.val));
            }
        }
    }
});

function processMessage(obj) {
    if (!obj || !obj.command) return;
    switch (obj.command) {
        case 'test': {
            // Try to connect to mqtt broker
            if (obj.callback && obj.message) {
                var _url = 'mqtt://' + (obj.message.user ? (obj.message.user + ':' + obj.message.pass + '@') : '') + obj.message.url + (obj.message.port ? (':' + obj.message.port) : '') + '?clientId=ioBroker.' + adapter.namespace;
                var _client = mqtt.connect(_url);
                // Set timeout for connection
                var timeout = setTimeout(function () {
                    _client.end();
                    adapter.sendTo(obj.from, obj.command, 'timeout', obj.callback);
                }, 2000);

                // If connected, return success
                _client.on('connect', function () {
                    _client.end();
                    clearTimeout(timeout);
                    adapter.sendTo(obj.from, obj.command, 'connected', obj.callback);
                });
            }
        }
    }
}

function processMessages() {
    adapter.getMessage(function (err, obj) {
        if (obj) {
            processMessage(obj.command, obj.message);
            processMessages();
        }
    });
}

function state2string(val) {
    return (val === null) ? 'null' : (val === undefined ? 'undefined' : val.toString());
}

function createClient(config) {
    var _url  = ((!config.ssl) ? 'mqtt' : 'mqtts') + '://' + (config.user ? (config.user + ':' + config.pass + '@') : '') + config.url + (config.port ? (':' + config.port) : '') + '?clientId=ioBroker.' + adapter.namespace;
    var __url = ((!config.ssl) ? 'mqtt' : 'mqtts') + '://' + (config.user ? (config.user + ':*******************@') : '') + config.url + (config.port ? (':' + config.port) : '') + '?clientId=ioBroker.' + adapter.namespace;
    adapter.log.info('Try to connect to ' + __url);
    client = mqtt.connect(_url);

    // By default subscribr on all topics
    if (!config.patterns) config.patterns = '#';

    if (typeof config.patterns == 'string') {
        config.patterns = config.patterns.split(',');
    }

    for (var i = 0; i < config.patterns.length; i++) {
        config.patterns[i] = config.patterns[i].trim();
        client.subscribe(config.patterns[i]);
    }

    client.on('message', function (topic, message) {
        if (!topic) return;
        if (config.debug) adapter.log.info(topic + ' : ' + message);

        // Ignore message if value does not changed
        if (config.onchange) {
            var oldValue = values[topic];
            if (oldValue !== undefined && oldValue == message) {
                return;
            } else {
                values[topic] = message;
            }
        }
        // Remove own prefix
        if (config.prefix && topic.substring(0, config.prefix.length) == config.prefix) topic = topic.substring(config.prefix.length);

        topic = topic.replace(/\//g, '.');
        if (topic[0] == '.') topic = topic.substring(1);
        // add io. at start, because all states have prefix io.
        if (topic[0] != 'i' && topic[1] != 'i' && topic[2] != '.') topic = 'io.' + topic;

        adapter.setState(topic, {val: message, ack: true});
    });

    client.on('connect', function () {
        adapter.log.info('Connected to ' + config.url);
        if (config.publishAllOnStart) {
            for (var id in states) {
                if (id.length <= messageboxLen || id.substring(id.length - messageboxLen) != '.messagebox') {
                    client.publish(config.prefix + id.replace(/\./g, '/'), state2string(states[id].val));
                }
            }
        }
    });

    client.on('error', function (err) {
        adapter.log.error(err);
    });
}

function createServer(config) {
    var cltFunction = function (client) {
        var self = this;

        if (!self.clients) self.clients = {};

        client.on('connect', function (packet) {
            client.id = packet.clientId;
            if (config.user) {
                if (config.user != packet.username ||
                    config.pass != packet.password) {
                    adapter.log.warn('Client [' + packet.clientId + '] has invalid password(' + packet.password + ') or username(' + packet.username + ')');
                    client.connack({returnCode: 4});
                    if (self.clients[client.id]) delete self.clients[client.id];
                    client.stream.end();
                    return;
                }
            }

            adapter.log.info('Client [' + packet.clientId + '] connected');
            client.connack({returnCode: 0});
            self.clients[client.id] = client;

            // Send all subscribed variables to client
            if (config.publishAllOnStart) {
                for (var id in states) {
                    if (id.length <= messageboxLen || id.substring(id.length - messageboxLen) != '.messagebox') {
                        client.publish({topic: config.prefix + id.replace(/\./g, '/'), payload: state2string(states[id].val)});
                    }
                }
            }
        });

        client.on('publish', function (packet) {
            if (config.debug) adapter.log.info('Client [' + client.id + '] publishes "' + packet.topic + '": ' +  packet.payload);
            for (var k in self.clients) {
                self.clients[k].publish({topic: packet.topic, payload: packet.payload});
            }
            var topic = packet.topic;

            // Remove own prefix if
            if (config.prefix && topic.substring(0, config.prefix.length) == config.prefix) topic = topic.substring(config.prefix.length);

            topic = topic.replace(/\//g, '.');

            if (states[topic] === undefined) {
                topic = 'io.' + adapter.namespace + ((topic[0] == '.') ? topic : ('.' + topic));
            }

            adapter.setState(topic, {val: packet.payload, ack: true});
        });

        client.on('subscribe', function (packet) {
            var granted = [];
            for (var i = 0; i < packet.subscriptions.length; i++) {
                adapter.log.info('Client [' + client.id + '] subscribes on "' + packet.subscriptions[i].topic + '"');
                granted.push(packet.subscriptions[i].qos);
            }

            client.suback({granted: granted, messageId: packet.messageId});
        });

        client.on('pingreq', function (packet) {
            adapter.log.debug('Client [' + client.id + '] pingreq');
            client.pingresp();
        });

        client.on('disconnect', function (packet) {
            adapter.log.info('Client [' + client.id + '] disconnected');
            client.stream.end();
        });

        client.on('close', function (err) {
            adapter.log.info('Client [' + client.id + '] closed');
            delete self.clients[client.id];
        });

        client.on('error', function (err) {
            adapter.log.error('[' + client.id + '] ' + err);

            if (!self.clients[client.id]) return;

            delete self.clients[client.id];
            client.stream.end();
        });
    };

    if (!config.ssl) {
        adapter.log.info('Started MQTT ' + (config.user ? 'authenticated ' : '') + 'server on port ' + (config.port || 1883));
        server = mqtt.createServer(cltFunction).listen(config.port || 1883);
    } else {
        adapter.log.info('Started MQTTs security ' + (config.user ? 'authenticated ' : '') + 'server on port ' + (config.port || 1883));
        server = mqtt.createSecureServer(
            __dirname + '/cert/privatekey.pem',
            __dirname + '/cert/certificate.pem',
            cltFunction).listen(config.port || 1883);
    }
}

var cnt = 0;
function readStatesForPattern(pattern) {
    adapter.getForeignStates(pattern, function (err, res) {
        if (!err && res) {
            if (!states) states = {};

            for (var id in res) {
                if (id.length <= messageboxLen || id.substring(id.length - messageboxLen) != '.messagebox') {
                    states[id] = res[id];
                }
            }
        }
        cnt--;
        // If all patters answered, start client or server
        if (!cnt) {
            if (adapter.config.type == 'client') {
                createClient(adapter.config);
            } else {
                createServer(adapter.config);
            }
        }
    });
}

function main() {
    // Subscribe on own variables to publish it
    if (adapter.config.publish) {
        var parts = adapter.config.publish.split(',');
        for (var t = 0; t < parts.length; t++) {
            parts[t] = parts[t].trim();
            adapter.subscribeForeignStates(parts[t].trim());
            cnt++;
            readStatesForPattern(parts[t]);
        }
    }
    // If no subscription, start client or server
    if (!cnt) {
        if (adapter.config.type == 'client') {
            createClient(adapter.config);
        } else {
            createServer(adapter.config);
        }
    }
}

<|endoftext|>


<commit_before>/**
 * Landsat Maptype.
 */
define([], function () {

  'use strict';

  var LandsatMaptype = function(year, tileUrl) {
    // We want to set the zoom level differently depending on the year of the
    // landsat data. As diffrent z-levels exist for diffrent tile-sets.

    var config = {
      name: 'Landsat ' + year,
      alt: 'Global forest height',
      maxZoom: 17,
      isPng: true,
      tileSize: new google.maps.Size(256, 256),
      getTileUrl: function(ll, z) {
        var x = Math.abs(ll.x % (1 << z)); // jshint ignore:line

        switch (year) {
          case 2015:
          case 2016:
            return z > 11
              ? tileUrl.replace('{z}/{x}/{y}', z + '/' + x + '/' +ll.y)
              : 'https://storage.googleapis.com/landsat-cache/{0}/{1}/{2}/{3}.png'.format(year, z, x, ll.y);
            break;
          default:
            return window.gfw.config.GFW_API_HOST + '/gee/landsat_composites/{0}/{1}/{2}.png?year={3}'.format(z, x, ll.y, year);
            break;
        }
      },
    };

    return new google.maps.ImageMapType(config);
  };

  return LandsatMaptype;
});
<commit_msg>added 2013 and 2014 landsat layers<commit_after>/**
 * Landsat Maptype.
 */
define([], function () {

  'use strict';

  var LandsatMaptype = function(year, tileUrl) {
    // We want to set the url differently depending on the z level and year of
    // landsat data. For years before 2013 data is all pre-processed by google.
    // from 2013 onwards, we have pre-processed the z-levels between 0 and 11
    // in GCS. For higher z-levels the tiles come from a live EE-based service.

    var config = {
      name: 'Landsat ' + year,
      alt: 'Global forest height',
      maxZoom: 17,
      isPng: true,
      tileSize: new google.maps.Size(256, 256),
      getTileUrl: function(ll, z) {
        var x = Math.abs(ll.x % (1 << z)); // jshint ignore:line

        switch (year) {
          case 2013:
          case 2014:
          case 2015:
          case 2016:
            return z > 11
              ? tileUrl.replace('{z}/{x}/{y}', z + '/' + x + '/' +ll.y)
              : 'https://storage.googleapis.com/landsat-cache/{0}/{1}/{2}/{3}.png'.format(year, z, x, ll.y);
            break;
          default:
            return window.gfw.config.GFW_API_HOST + '/gee/landsat_composites/{0}/{1}/{2}.png?year={3}'.format(z, x, ll.y, year);
            break;
        }
      },
    };

    return new google.maps.ImageMapType(config);
  };

  return LandsatMaptype;
});
<|endoftext|>


<commit_before>/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with this
 * work for additional information regarding copyright ownership. The ASF
 * licenses this file to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

var App = require('app');

App.ExperimentalController = Em.Controller.extend(App.UserPref, {
  name: 'experimentalController',
  supports: function () {
    return Em.keys(App.get('supports')).map(function (sup) {
      return Ember.Object.create({
        name: sup,
        selected: App.get('supports')[sup]
      });
    });
  }.property('App.supports'),

  loadSupports: function () {
    return this.getUserPref('user-pref-' + App.router.get('loginName') + '-supports');
  },

  getUserPrefSuccessCallback: function (response, request, data) {
    if (response) {
      App.set('supports', response);
    }
  },

  doSave: function () {
    var supports = this.get('supports');
    supports.forEach(function(s){
      var propName = 'App.supports.' + s.get('name');
      var propValue = s.get('selected');
      Ember.set(propName, propValue);
    });
    this.postUserPref('user-pref-' + App.router.get('loginName') + '-supports', App.get('supports')).complete(function(){
      App.router.transitionTo('root.index');
    });
  },

  doCancel: function () {
    App.router.transitionTo('root.index');
  },

  doResetUIStates: function () {
    var self = this;
    return App.ModalPopup.show({
      header: Em.I18n.t('reset.ui.states'),
      bodyClass: Ember.View.extend({
        template: Ember.Handlebars.compile(Em.I18n.t('reset.ui.states.body'))
      }),
      primary: Em.I18n.t('yes'),
      onPrimary: function () {
        var router = App.router;
        App.db.cleanUp();
        router.clearAllSteps();
        App.cache.clear();
        App.clusterStatus.setClusterStatus({});
        this.hide();
        router.transitionTo('root.index');
      }
    });
  }
});
<commit_msg>AMBARI-19276. Reset UI State does not clear ongoing wizard data (alexantonenko)<commit_after>/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with this
 * work for additional information regarding copyright ownership. The ASF
 * licenses this file to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

var App = require('app');

App.ExperimentalController = Em.Controller.extend(App.UserPref, {
  name: 'experimentalController',
  supports: function () {
    return Em.keys(App.get('supports')).map(function (sup) {
      return Ember.Object.create({
        name: sup,
        selected: App.get('supports')[sup]
      });
    });
  }.property('App.supports'),

  loadSupports: function () {
    return this.getUserPref('user-pref-' + App.router.get('loginName') + '-supports');
  },

  getUserPrefSuccessCallback: function (response, request, data) {
    if (response) {
      App.set('supports', response);
    }
  },

  doSave: function () {
    var supports = this.get('supports');
    supports.forEach(function(s){
      var propName = 'App.supports.' + s.get('name');
      var propValue = s.get('selected');
      Ember.set(propName, propValue);
    });
    this.postUserPref('user-pref-' + App.router.get('loginName') + '-supports', App.get('supports')).complete(function(){
      App.router.transitionTo('root.index');
    });
  },

  doCancel: function () {
    App.router.transitionTo('root.index');
  },

  doResetUIStates: function () {
    var self = this;
    return App.ModalPopup.show({
      header: Em.I18n.t('reset.ui.states'),
      bodyClass: Ember.View.extend({
        template: Ember.Handlebars.compile(Em.I18n.t('reset.ui.states.body'))
      }),
      primary: Em.I18n.t('yes'),
      context: self,
      onPrimary: function () {
        var router = App.router;
        App.db.cleanUp();
        router.clearAllSteps();
        App.cache.clear();
        App.clusterStatus.setClusterStatus({});
        this.context.postUserPref('wizard-data', {});
        this.hide();
        router.transitionTo('root.index');
      }
    });
  }
});
<|endoftext|>


<commit_before>import React, { useState, useEffect } from 'react';
import { PropTypes as T } from 'prop-types';
import styled from 'styled-components';
import qs from 'qs';
import config from '../../config';
import LoadingMessage from '../loading-message';
import ErrorMessage from '../error-message';
import Card, {
  CardHeader as BaseHeader,
  CardTitle,
  CardHeadline,
} from '../card';
import TabbedSelector from '../tabbed-selector';
import LineChart from '../line-chart';
import InfoButton from '../info-button';

const ChartContainer = styled.div`
  max-height: 24rem;
  min-height: 20rem;
  padding: 0;
`;

const CardHeader = styled(BaseHeader)`
  display: grid;
  grid-template-rows: min-content 1fr;
  grid-gap: 0.5rem;
`;

const defaultState = {
  fetched: false,
  fetching: false,
  error: null,
  data: null,
};

export default function TimeSeriesCard({
  locationId,
  projectId,
  parameters,
  dateRange,
  titleInfo,
}) {
  // eslint-disable-next-line no-unused-vars
  const [{ fetched, fetching, error, data }, setState] = useState(defaultState);

  const [activeTab, setActiveTab] = useState({
    id: parameters[0].parameter || parameters[0],
    name: parameters[0].parameter || parameters[0],
  });

  const [year, month, day] = (dateRange ? dateRange.split('/') : []).map(
    Number
  );

  // eslint-disable-next-line no-unused-vars
  const [temporal, setTemporal] = useState(day ? 'hour' : 'day');

  useEffect(() => {
    const fetchData = () => {
      setState(state => ({ ...state, fetching: true, error: null }));

      let query = {
        parameter: activeTab.id,
        temporal,
        ...(dateRange
          ? {
              // In user space, month is 1 indexed
              date_from: new Date(year, month - 1, day || 1),
              date_to: day
                ? new Date(year, month - 1, day + 1)
                : new Date(year, month, 0),
            }
          : {}),
      };

      if (locationId) {
        query = { ...query, location: locationId, spatial: 'location' };
      } else if (projectId) {
        query = { ...query, project: projectId, spatial: 'project' };
      }
      //If date range is not lifetime, get hourly data
      if (dateRange) {
        setTemporal('hour');
        query = {
          ...query,
          temporal: 'hour',
        };
      }
      fetch(
        `${config.api}/averages?${qs.stringify(query, { skipNulls: true })}`
      )
        .then(response => {
          if (response.status >= 400) {
            throw new Error('Bad response');
          }
          return response.json();
        })
        .then(
          json => {
            setState(state => ({
              ...state,
              fetched: true,
              fetching: false,
              data: json.results,
            }));
          },
          e => {
            console.log('e', e);
            setState(state => ({
              ...state,
              fetched: true,
              fetching: false,
              error: e,
            }));
          }
        );
    };

    fetchData();

    return () => {
      setState(defaultState);
    };
  }, [activeTab, dateRange]);

  if (!fetched && !fetching) {
    return null;
  }

  return (
    <Card
      id="time-series"
      className="card--time-series"
      renderHeader={() => (
        <CardHeader className="card__header">
          <TabbedSelector
            tabs={parameters.map(x => ({
              id: x.parameter || x.id,
              name: x.displayName || x.name,
            }))}
            activeTab={activeTab}
            onTabSelect={t => {
              setActiveTab(t);
            }}
          />
          <CardHeadline>
            <CardTitle className="card__title">Time Series Data</CardTitle>
            {titleInfo && <InfoButton info={titleInfo} id="time-series-info" />}
          </CardHeadline>
        </CardHeader>
      )}
      renderBody={() => (
        <ChartContainer className="card__body">
          {fetching ? (
            <LoadingMessage />
          ) : data && data.length ? (
            <LineChart
              data={data.map(m => ({ x: new Date(m[temporal]), y: m.average }))}
              yLabel={data && data[0].displayName}
              yUnit={data && data[0].unit}
              xUnit={temporal}
            />
          ) : (
            <ErrorMessage instructions="Please try a different time" />
          )}
        </ChartContainer>
      )}
    />
  );
}

TimeSeriesCard.propTypes = {
  titleInfo: T.string,
  locationId: T.oneOfType([T.string, T.number]),
  projectId: T.string,
  parameters: T.arrayOf(
    T.shape({
      parameter: T.string.isRequired,
    })
  ),
  dateRange: T.string,
};
<commit_msg>Fix time series chart update<commit_after>import React, { useState, useEffect } from 'react';
import { PropTypes as T } from 'prop-types';
import styled from 'styled-components';
import qs from 'qs';
import config from '../../config';
import LoadingMessage from '../loading-message';
import ErrorMessage from '../error-message';
import Card, {
  CardHeader as BaseHeader,
  CardTitle,
  CardHeadline,
} from '../card';
import TabbedSelector from '../tabbed-selector';
import LineChart from '../line-chart';
import InfoButton from '../info-button';

const ChartContainer = styled.div`
  max-height: 24rem;
  min-height: 20rem;
  padding: 0;
`;

const CardHeader = styled(BaseHeader)`
  display: grid;
  grid-template-rows: min-content 1fr;
  grid-gap: 0.5rem;
`;

const defaultState = {
  fetched: false,
  fetching: false,
  error: null,
  data: null,
};

export default function TimeSeriesCard({
  locationId,
  projectId,
  parameters,
  dateRange,
  titleInfo,
}) {
  // eslint-disable-next-line no-unused-vars
  const [{ fetched, fetching, error, data }, setState] = useState(defaultState);

  const [activeTab, setActiveTab] = useState({
    id: parameters[0].parameter || parameters[0],
    name: parameters[0].parameter || parameters[0],
  });

  const [year, month, day] = (dateRange ? dateRange.split('/') : []).map(
    Number
  );

  const temporal = day ? 'hour' : 'day';

  useEffect(() => {
    const fetchData = () => {
      setState(state => ({ ...state, fetching: true, error: null }));

      let query = {
        parameter: activeTab.id,
        temporal,
        ...(dateRange
          ? {
              // In user space, month is 1 indexed
              date_from: new Date(year, month - 1, day || 1),
              date_to: day
                ? new Date(year, month - 1, day + 1)
                : new Date(year, month, 0),
            }
          : {}),
      };

      if (locationId) {
        query = { ...query, location: locationId, spatial: 'location' };
      } else if (projectId) {
        query = { ...query, project: projectId, spatial: 'project' };
      }

      fetch(
        `${config.api}/averages?${qs.stringify(query, { skipNulls: true })}`
      )
        .then(response => {
          if (response.status >= 400) {
            throw new Error('Bad response');
          }
          return response.json();
        })
        .then(
          json => {
            setState(state => ({
              ...state,
              fetched: true,
              fetching: false,
              data: json.results,
            }));
          },
          e => {
            console.log('e', e);
            setState(state => ({
              ...state,
              fetched: true,
              fetching: false,
              error: e,
            }));
          }
        );
    };

    fetchData();

    return () => {
      setState(defaultState);
    };
  }, [activeTab, dateRange]);

  if (!fetched && !fetching) {
    return null;
  }

  return (
    <Card
      id="time-series"
      className="card--time-series"
      renderHeader={() => (
        <CardHeader className="card__header">
          <TabbedSelector
            tabs={parameters.map(x => ({
              id: x.parameter || x.id,
              name: x.displayName || x.name,
            }))}
            activeTab={activeTab}
            onTabSelect={t => {
              setActiveTab(t);
            }}
          />
          <CardHeadline>
            <CardTitle className="card__title">Time Series Data</CardTitle>
            {titleInfo && <InfoButton info={titleInfo} id="time-series-info" />}
          </CardHeadline>
        </CardHeader>
      )}
      renderBody={() => (
        <ChartContainer className="card__body">
          {fetching ? (
            <LoadingMessage />
          ) : data && data.length ? (
            <LineChart
              data={data.map(m => ({ x: new Date(m[temporal]), y: m.average }))}
              yLabel={data && data[0].displayName}
              yUnit={data && data[0].unit}
              xUnit={temporal}
            />
          ) : (
            <ErrorMessage instructions="Please try a different time" />
          )}
        </ChartContainer>
      )}
    />
  );
}

TimeSeriesCard.propTypes = {
  titleInfo: T.string,
  locationId: T.oneOfType([T.string, T.number]),
  projectId: T.string,
  parameters: T.arrayOf(
    T.shape({
      parameter: T.string.isRequired,
    })
  ),
  dateRange: T.string,
};
<|endoftext|>


<commit_before>// DOM APIs, for completeness

if (typeof setTimeout !== 'undefined') exports.setTimeout = function() { return setTimeout.apply(window, arguments); };
if (typeof clearTimeout !== 'undefined') exports.clearTimeout = function() { clearTimeout.apply(window, arguments); };
if (typeof setInterval !== 'undefined') exports.setInterval = function() { return setInterval.apply(window, arguments); };
if (typeof clearInterval !== 'undefined') exports.clearInterval = function() { clearInterval.apply(window, arguments); };

// TODO: Change to more effiecient list approach used in Node.js
// For now, we just implement the APIs using the primitives above.

exports.enroll = function(item, delay) {
  item._timeoutID = setTimeout(item._onTimeout, delay);
};

exports.unenroll = function(item) {
  clearTimeout(item._timeoutID);
};

exports.active = function(item) {
  // our naive impl doesn't care (correctness is still preserved)
};

exports.setImmediate = require('process/browser.js').nextTick;
<commit_msg>Add clearImmediate()<commit_after>var nextTick = require('process/browser.js').nextTick;
var slice = Array.prototype.slice;
var immediateIds = {};
var nextImmediateId = 0;

// DOM APIs, for completeness

if (typeof setTimeout !== 'undefined') exports.setTimeout = function() { return setTimeout.apply(window, arguments); };
if (typeof clearTimeout !== 'undefined') exports.clearTimeout = function() { clearTimeout.apply(window, arguments); };
if (typeof setInterval !== 'undefined') exports.setInterval = function() { return setInterval.apply(window, arguments); };
if (typeof clearInterval !== 'undefined') exports.clearInterval = function() { clearInterval.apply(window, arguments); };

// TODO: Change to more efficient list approach used in Node.js
// For now, we just implement the APIs using the primitives above.

exports.enroll = function(item, delay) {
  item._timeoutID = setTimeout(item._onTimeout, delay);
};

exports.unenroll = function(item) {
  clearTimeout(item._timeoutID);
};

exports.active = function(item) {
  // our naive impl doesn't care (correctness is still preserved)
};

// That's not how node.js implements it but the exposed api is the same.
exports.setImmediate = function(fn) {
    var id = nextImmediateId++;
    var args = arguments.length < 2? false : slice.call(arguments, 1);

    immediateIds[id] = true;

    nextTick(function onNextTick() {
        if (immediateIds[id]) {
            // fn.call() is faster so we optimize for the common use-case
            // @see http://jsperf.com/call-apply-segu
            if (args) {
                fn.apply(null, args);
            } else {
                fn.call(null);
            }
            // Prevent ids from leaking
            exports.clearImmediate(id);
        }
    });

    return id;
};

exports.clearImmediate = function(id) {
    delete immediateIds[id];
};<|endoftext|>


<commit_before>'use strict';
import React, { PropTypes as T } from 'react';
import c from 'classnames';
import _ from 'lodash';
import { hashHistory } from 'react-router';

import { getLanguage } from '../../utils/i18n';

import { Modal, ModalHeader, ModalBody, ModalFooter } from '../modal';

const ProjectFormModal = React.createClass({

  propTypes: {
    revealed: T.bool,
    onCloseClick: T.func,

    projectForm: T.object,
    saveProject: T.func,

    // Only available when editing.
    editing: T.bool,
    projectData: T.object
  },

  getInitialState: function () {
    return {
      errors: {
        name: null
      },
      data: {
        name: _.get(this.props.projectData, 'name', ''),
        description: _.get(this.props.projectData, 'description', '') || ''
      }
    };
  },

  componentWillReceiveProps: function (nextProps) {
    if (this.props.projectForm.processing && !nextProps.projectForm.processing && !nextProps.projectForm.error) {
      if (!this.props.editing) {
        hashHistory.push(`${getLanguage()}/projects/${nextProps.projectForm.data.id}/setup`);
      } else {
        this.props.onCloseClick();
      }
    }
  },

  checkErrors: function () {
    let control = true;
    let errors = this.getInitialState().errors;

    if (this.state.data.name === '') {
      errors.name = true;
      control = false;
    }

    this.setState({errors});
    return control;
  },

  onSubmit: function () {
    if (this.checkErrors()) {
      var payload = {
        name: this.state.data.name,
        description: this.state.data.description || null
      };

      if (this.props.editing) {
        this.props.saveProject(this.props.projectData.id, payload);
      } else {
        // On create we only want to send properties that were filled in.
        payload = _.pickBy(payload, v => v !== null);
        this.props.saveProject(payload);
      }
    }
  },

  onFieldChange: function (field, e) {
    let data = Object.assign({}, this.state.data, {[field]: e.target.value});
    this.setState({data});
  },

  renderError: function () {
    let error = this.props.projectForm.error;

    if (!error) {
      return;
    }

    if (error.statusCode === 409) {
      return
The name is already in use.
;
    } else {
      return
{error.message || error.error}
;
    }
  },

  render: function () {
    let processing = this.props.projectForm.processing;

    return (
      <Modal
        id='modal-showcase'
        className='modal--large'
        onCloseClick={this.props.onCloseClick}
        revealed={this.props.revealed} >

        <ModalHeader>

{this.props.editing ? 'Edit Project' : 'New Project'}

        </ModalHeader>
        <ModalBody>

        {processing ?
Processing...
 : null}

        {this.renderError()}

          <form className={c({'disable': processing})}>

              Project name*
              <input type='text' className='form__control form__control--medium' id='project-name' name='project-name' placeholder='Project name' value={this.state.data.name} onChange={this.onFieldChange.bind(null, 'name')} />

                {this.state.errors.name ?
A name is required.
 : null }

Keep it short and sweet.

              Description
              <textarea ref='description' className='form__control' id='project-desc' rows='4' placeholder='Say something about this project' value={this.state.data.description} onChange={this.onFieldChange.bind(null, 'description')}></textarea>

          </form>
        </ModalBody>
        <ModalFooter>
          <button className='button button--achromic' type='button' onClick={this.props.onCloseClick}>Cancel</button>
          <button className='button button--base' type='submit' onClick={this.onSubmit}>Save</button>
        </ModalFooter>
      </Modal>
    );
  }
});

export default ProjectFormModal;
<commit_msg>Reset form on modal close<commit_after>'use strict';
import React, { PropTypes as T } from 'react';
import c from 'classnames';
import _ from 'lodash';
import { hashHistory } from 'react-router';

import { getLanguage } from '../../utils/i18n';

import { Modal, ModalHeader, ModalBody, ModalFooter } from '../modal';

const ProjectFormModal = React.createClass({

  propTypes: {
    revealed: T.bool,
    onCloseClick: T.func,

    projectForm: T.object,
    saveProject: T.func,

    // Only available when editing.
    editing: T.bool,
    projectData: T.object
  },

  getInitialState: function () {
    return {
      errors: {
        name: null
      },
      data: {
        name: _.get(this.props.projectData, 'name', ''),
        description: _.get(this.props.projectData, 'description', '') || ''
      }
    };
  },

  componentWillReceiveProps: function (nextProps) {
    if (this.props.projectForm.processing && !nextProps.projectForm.processing && !nextProps.projectForm.error) {
      if (!this.props.editing) {
        hashHistory.push(`${getLanguage()}/projects/${nextProps.projectForm.data.id}/setup`);
      } else {
        this.onClose();
      }
    }
  },

  onClose: function () {
    this.setState(this.getInitialState());
    this.props.onCloseClick();
  },

  checkErrors: function () {
    let control = true;
    let errors = this.getInitialState().errors;

    if (this.state.data.name === '') {
      errors.name = true;
      control = false;
    }

    this.setState({errors});
    return control;
  },

  onSubmit: function () {
    if (this.checkErrors()) {
      var payload = {
        name: this.state.data.name,
        description: this.state.data.description || null
      };

      if (this.props.editing) {
        this.props.saveProject(this.props.projectData.id, payload);
      } else {
        // On create we only want to send properties that were filled in.
        payload = _.pickBy(payload, v => v !== null);
        this.props.saveProject(payload);
      }
    }
  },

  onFieldChange: function (field, e) {
    let data = Object.assign({}, this.state.data, {[field]: e.target.value});
    this.setState({data});
  },

  renderError: function () {
    let error = this.props.projectForm.error;

    if (!error) {
      return;
    }

    if (error.statusCode === 409) {
      return
The name is already in use.
;
    } else {
      return
{error.message || error.error}
;
    }
  },

  render: function () {
    let processing = this.props.projectForm.processing;

    return (
      <Modal
        id='modal-showcase'
        className='modal--large'
        onCloseClick={this.onClose}
        revealed={this.props.revealed} >

        <ModalHeader>

{this.props.editing ? 'Edit Project' : 'New Project'}

        </ModalHeader>
        <ModalBody>

        {processing ?
Processing...
 : null}

        {this.renderError()}

          <form className={c({'disable': processing})}>

              Project name*
              <input type='text' className='form__control form__control--medium' id='project-name' name='project-name' placeholder='Project name' value={this.state.data.name} onChange={this.onFieldChange.bind(null, 'name')} />

                {this.state.errors.name ?
A name is required.
 : null }

Keep it short and sweet.

              Description
              <textarea ref='description' className='form__control' id='project-desc' rows='4' placeholder='Say something about this project' value={this.state.data.description} onChange={this.onFieldChange.bind(null, 'description')}></textarea>

          </form>
        </ModalBody>
        <ModalFooter>
          <button className='button button--achromic' type='button' onClick={this.onClose}>Cancel</button>
          <button className='button button--base' type='submit' onClick={this.onSubmit}>Save</button>
        </ModalFooter>
      </Modal>
    );
  }
});

export default ProjectFormModal;
<|endoftext|>