<commit_before>import {
  get,
  find,
  map,
  keyBy,
  mapValues,
  every,
  pick,
  sum,
  difference,
} from "lodash/fp";

import { markSampleUploaded, uploadFileToUrlWithRetries } from "~/api";

import { putWithCSRF, postWithCSRF } from "./core";

export const MAX_MARK_SAMPLE_RETRIES = 10;

export const exponentialDelayWithJitter = tryCount => {
  // ~13 sec, ~46 sec, ~158 sec, ... -> ~115 minutes.
  // Derived via eyeballing. Adjust based on user feedback.
  const delay = ((tryCount + Math.random()) * 10) ** 3.5 + 10000;
  return new Promise(resolve => setTimeout(resolve, delay));
};

export const bulkUploadBasespace = ({ samples, metadata }) =>
  bulkUploadWithMetadata(samples, metadata);

export const bulkUploadRemote = ({ samples, metadata }) =>
  bulkUploadWithMetadata(samples, metadata);

export const initiateBulkUploadLocalWithMetadata = async ({
  samples,
  metadata,
  callbacks = {
    onCreateSamplesError: null,
  },
}) => {
  // Only upload these fields from the sample.
  const processedSamples = map(
    pick([
      "alignment_config_name",
      "client",
      "dag_vars",
      "do_not_process",
      "host_genome_id",
      "input_files_attributes",
      "max_input_fragments",
      "name",
      "pipeline_branch",
      "pipeline_execution_strategy",
      "project_id",
      "s3_preload_result_path",
      "subsample",
      "wetlab_protocol",
      "workflows",
    ]),
    samples
  );

  let response;

  try {
    // Creates the Sample objects and assigns a presigned S3 URL so we can upload the sample files to S3 via the URL
    response = await bulkUploadWithMetadata(processedSamples, metadata);
  } catch (e) {
    callbacks.onCreateSamplesError &&
      callbacks.onCreateSamplesError([e], map("name", samples));
    return;
  }

  // It's possible that a subset of samples errored out, but other ones can still be uploaded.
  if (response.errors.length > 0) {
    callbacks.onCreateSamplesError &&
      callbacks.onCreateSamplesError(
        response.errors,
        response.errored_sample_names
      );
  }

  // The samples created from the network response (response.samples) does not contain the files that need to be upload to S3.
  // They contain information pertaining to the sample itself (metadata) as well as presigned S3 URL links
  // The sample files that need to be uploaded to S3 are in the samples argument passed into initiateBulkUploadLocalWithMetadata
  // So we need to fetch the files from samples argument and copy them over to response.samples where they're later uploaded to S3 via uploadSampleFilesToPresignedURL
  response.samples.forEach(
    createdSample =>
      (createdSample["filesToUpload"] = get(
        "files",
        find({ name: createdSample.name }, samples)
      ))
  );
  return response.samples;
};

export const uploadSampleFilesToPresignedURL = ({
  samples,
  callbacks = {
    onSampleUploadProgress: null,
    onSampleUploadError: null,
    onSampleUploadSuccess: null,
    onMarkSampleUploadedError: null,
  },
}) => {
  // Store the upload progress of file names, so we can track when
  // everything is done.
  const fileNamesToProgress = {};
  const markedUploaded = {};
  const sampleNamesToFiles = mapValues("filesToUpload", keyBy("name", samples));

  // This function needs access to fileNamesToProgress.
  const onFileUploadSuccess = async (sample, sampleId) => {
    const sampleFiles = sampleNamesToFiles[sample.name];
    // If every file for this sample is uploaded, mark it as uploaded.
    if (
      !markedUploaded[sample.name] &&
      every(file => fileNamesToProgress[file.name] === 1, sampleFiles)
    ) {
      markedUploaded[sample.name] = true;

      let tryCount = 0;
      while (true) {
        try {
          await markSampleUploaded(sampleId);

          callbacks.onSampleUploadSuccess &&
            callbacks.onSampleUploadSuccess(sample);
          break;
        } catch (_) {
          tryCount++;
          if (tryCount === MAX_MARK_SAMPLE_RETRIES) {
            callbacks.onMarkSampleUploadedError &&
              callbacks.onMarkSampleUploadedError(sample);
            break;
          }
          await exponentialDelayWithJitter(tryCount);
        }
      }
    }
  };

  // Calculate the current sample upload percentage.
  const getSampleUploadPercentage = sample => {
    const sampleFiles = sample.input_files.map(inputFileAttributes => {
      return sampleNamesToFiles[sample.name][inputFileAttributes.name];
    });

    const sampleFileUploadProgress = map(
      file => ({
        percentage: fileNamesToProgress[file.name] || null,
        size: file.size,
      }),
      sampleFiles
    );

    const uploadedSize = sum(
      map(
        progress => (progress.percentage || 0) * progress.size,
        sampleFileUploadProgress
      )
    );

    const totalSize = sum(
      map(progress => progress.size, sampleFileUploadProgress)
    );

    return uploadedSize / totalSize;
  };

  samples.forEach(sample => {
    const files = sampleNamesToFiles[sample.name];

    // Start pinging server to monitor uploads server-side
    const interval = startUploadHeartbeat(sample.id);

    sample.input_files.map(inputFileAttributes => {
      const file = files[inputFileAttributes.name];
      const url = inputFileAttributes.presigned_url;

      uploadFileToUrlWithRetries(file, url, {
        onUploadProgress: e => {
          const percent = e.loaded / e.total;
          fileNamesToProgress[file.name] = percent;

          if (callbacks.onSampleUploadProgress) {
            const uploadedPercentage = getSampleUploadPercentage(sample);
            callbacks.onSampleUploadProgress(sample, uploadedPercentage);
          }
        },
        onSuccess: () => {
          fileNamesToProgress[file.name] = 1;
          onFileUploadSuccess(sample, sample.id);
          clearInterval(interval);
        },
        onError: error => {
          fileNamesToProgress[file.name] = 0;
          callbacks.onSampleUploadError &&
            callbacks.onSampleUploadError(sample, error);
          clearInterval(interval);
        },
      });
    });
  });
};

// Bulk-upload samples (both local and remote), with metadata.
const bulkUploadWithMetadata = async (samples, metadata) => {
  const response = await postWithCSRF(
    `/samples/bulk_upload_with_metadata.json`,
    {
      samples,
      metadata,
      client: "web",
    }
  );

  // Add the errored sample names to the response.
  if (response.errors.length > 0) {
    const erroredSampleNames = difference(
      map("name", samples),
      map("name", response.samples)
    );

    return {
      ...response,
      errored_sample_names: erroredSampleNames,
    };
  }

  return response;
};

// Local uploads go directly from the browser to S3, so we don't know if an upload was interrupted.
// Ping the heartbeat endpoint periodically to say the browser is actively uploading this sample.
export const startUploadHeartbeat = async sampleId => {
  const sendHeartbeat = () => {
    putWithCSRF(`/samples/${sampleId}/upload_heartbeat.json`).catch(() =>
      // eslint-disable-next-line no-console
      console.error("Can't connect to IDseq server.")
    );
  };
  sendHeartbeat(); // Send first heartbeat immediately so we know it is working
  const interval = 60000; // 60 sec
  return setInterval(sendHeartbeat, interval);
};
<commit_msg>Add jitter to upload_heartbeat requests (#671)<commit_after>import {
  get,
  find,
  map,
  keyBy,
  mapValues,
  every,
  pick,
  sum,
  difference,
} from "lodash/fp";

import { markSampleUploaded, uploadFileToUrlWithRetries } from "~/api";

import { putWithCSRF, postWithCSRF } from "./core";

export const MAX_MARK_SAMPLE_RETRIES = 10;

export const exponentialDelayWithJitter = tryCount => {
  // ~13 sec, ~46 sec, ~158 sec, ... -> ~115 minutes.
  // Derived via eyeballing. Adjust based on user feedback.
  const delay = ((tryCount + Math.random()) * 10) ** 3.5 + 10000;
  return new Promise(resolve => setTimeout(resolve, delay));
};

export const bulkUploadBasespace = ({ samples, metadata }) =>
  bulkUploadWithMetadata(samples, metadata);

export const bulkUploadRemote = ({ samples, metadata }) =>
  bulkUploadWithMetadata(samples, metadata);

export const initiateBulkUploadLocalWithMetadata = async ({
  samples,
  metadata,
  callbacks = {
    onCreateSamplesError: null,
  },
}) => {
  // Only upload these fields from the sample.
  const processedSamples = map(
    pick([
      "alignment_config_name",
      "client",
      "dag_vars",
      "do_not_process",
      "host_genome_id",
      "input_files_attributes",
      "max_input_fragments",
      "name",
      "pipeline_branch",
      "pipeline_execution_strategy",
      "project_id",
      "s3_preload_result_path",
      "subsample",
      "wetlab_protocol",
      "workflows",
    ]),
    samples
  );

  let response;

  try {
    // Creates the Sample objects and assigns a presigned S3 URL so we can upload the sample files to S3 via the URL
    response = await bulkUploadWithMetadata(processedSamples, metadata);
  } catch (e) {
    callbacks.onCreateSamplesError &&
      callbacks.onCreateSamplesError([e], map("name", samples));
    return;
  }

  // It's possible that a subset of samples errored out, but other ones can still be uploaded.
  if (response.errors.length > 0) {
    callbacks.onCreateSamplesError &&
      callbacks.onCreateSamplesError(
        response.errors,
        response.errored_sample_names
      );
  }

  // The samples created from the network response (response.samples) does not contain the files that need to be upload to S3.
  // They contain information pertaining to the sample itself (metadata) as well as presigned S3 URL links
  // The sample files that need to be uploaded to S3 are in the samples argument passed into initiateBulkUploadLocalWithMetadata
  // So we need to fetch the files from samples argument and copy them over to response.samples where they're later uploaded to S3 via uploadSampleFilesToPresignedURL
  response.samples.forEach(
    createdSample =>
      (createdSample["filesToUpload"] = get(
        "files",
        find({ name: createdSample.name }, samples)
      ))
  );
  return response.samples;
};

export const uploadSampleFilesToPresignedURL = ({
  samples,
  callbacks = {
    onSampleUploadProgress: null,
    onSampleUploadError: null,
    onSampleUploadSuccess: null,
    onMarkSampleUploadedError: null,
  },
}) => {
  // Store the upload progress of file names, so we can track when
  // everything is done.
  const fileNamesToProgress = {};
  const markedUploaded = {};
  const sampleNamesToFiles = mapValues("filesToUpload", keyBy("name", samples));

  // This function needs access to fileNamesToProgress.
  const onFileUploadSuccess = async (sample, sampleId) => {
    const sampleFiles = sampleNamesToFiles[sample.name];
    // If every file for this sample is uploaded, mark it as uploaded.
    if (
      !markedUploaded[sample.name] &&
      every(file => fileNamesToProgress[file.name] === 1, sampleFiles)
    ) {
      markedUploaded[sample.name] = true;

      let tryCount = 0;
      while (true) {
        try {
          await markSampleUploaded(sampleId);

          callbacks.onSampleUploadSuccess &&
            callbacks.onSampleUploadSuccess(sample);
          break;
        } catch (_) {
          tryCount++;
          if (tryCount === MAX_MARK_SAMPLE_RETRIES) {
            callbacks.onMarkSampleUploadedError &&
              callbacks.onMarkSampleUploadedError(sample);
            break;
          }
          await exponentialDelayWithJitter(tryCount);
        }
      }
    }
  };

  // Calculate the current sample upload percentage.
  const getSampleUploadPercentage = sample => {
    const sampleFiles = sample.input_files.map(inputFileAttributes => {
      return sampleNamesToFiles[sample.name][inputFileAttributes.name];
    });

    const sampleFileUploadProgress = map(
      file => ({
        percentage: fileNamesToProgress[file.name] || null,
        size: file.size,
      }),
      sampleFiles
    );

    const uploadedSize = sum(
      map(
        progress => (progress.percentage || 0) * progress.size,
        sampleFileUploadProgress
      )
    );

    const totalSize = sum(
      map(progress => progress.size, sampleFileUploadProgress)
    );

    return uploadedSize / totalSize;
  };

  samples.forEach(sample => {
    const files = sampleNamesToFiles[sample.name];

    // Start pinging server to monitor uploads server-side
    const interval = startUploadHeartbeat(sample.id);

    sample.input_files.map(inputFileAttributes => {
      const file = files[inputFileAttributes.name];
      const url = inputFileAttributes.presigned_url;

      uploadFileToUrlWithRetries(file, url, {
        onUploadProgress: e => {
          const percent = e.loaded / e.total;
          fileNamesToProgress[file.name] = percent;

          if (callbacks.onSampleUploadProgress) {
            const uploadedPercentage = getSampleUploadPercentage(sample);
            callbacks.onSampleUploadProgress(sample, uploadedPercentage);
          }
        },
        onSuccess: () => {
          fileNamesToProgress[file.name] = 1;
          onFileUploadSuccess(sample, sample.id);
          clearInterval(interval);
        },
        onError: error => {
          fileNamesToProgress[file.name] = 0;
          callbacks.onSampleUploadError &&
            callbacks.onSampleUploadError(sample, error);
          clearInterval(interval);
        },
      });
    });
  });
};

// Bulk-upload samples (both local and remote), with metadata.
const bulkUploadWithMetadata = async (samples, metadata) => {
  const response = await postWithCSRF(
    `/samples/bulk_upload_with_metadata.json`,
    {
      samples,
      metadata,
      client: "web",
    }
  );

  // Add the errored sample names to the response.
  if (response.errors.length > 0) {
    const erroredSampleNames = difference(
      map("name", samples),
      map("name", response.samples)
    );

    return {
      ...response,
      errored_sample_names: erroredSampleNames,
    };
  }

  return response;
};

// Local uploads go directly from the browser to S3, so we don't know if an upload was interrupted.
// Ping the heartbeat endpoint periodically to say the browser is actively uploading this sample.
export const startUploadHeartbeat = async sampleId => {
  const sendHeartbeat = () => {
    putWithCSRF(`/samples/${sampleId}/upload_heartbeat.json`).catch(() =>
      // eslint-disable-next-line no-console
      console.error("Can't connect to IDseq server.")
    );
  };
  sendHeartbeat(); // Send first heartbeat immediately so we know it is working

  // Range from 170-230 seconds each. Picked via eyeballing.
  const interval = (17 + 6 * Math.random()) * 10000;
  return setInterval(sendHeartbeat, interval);
};
<|endoftext|>


<commit_before>import KML from '../src/ol/format/KML.js';
import Map from '../src/ol/Map.js';
import Stamen from '../src/ol/source/Stamen.js';
import VectorSource from '../src/ol/source/Vector.js';
import View from '../src/ol/View.js';
import {Fill, Stroke, Style} from '../src/ol/style.js';
import {Tile as TileLayer, Vector as VectorLayer} from '../src/ol/layer.js';

/*
 * Compute the style of the feature.  Here we want the opacity of polygons to
 * be based on the offset from local noon.  For example, a timezone where it is
 * currently noon would have an opacity of 0.75.  And a timezone where it is
 * currently midnight would have an opacity of 0.  This doesn't account for
 * daylight savings, so don't use it to plan your vacation.
 */
const styleFunction = function (feature) {
  let offset = 0;
  const name = feature.get('name'); // e.g. GMT -08:30
  const match = name.match(/([\-+]\d{2}):(\d{2})$/);
  if (match) {
    const hours = parseInt(match[1], 10);
    const minutes = parseInt(match[2], 10);
    offset = 60 * hours + minutes;
  }
  const date = new Date();
  const local = new Date(
    date.getTime() + (date.getTimezoneOffset() + offset) * 60000
  );
  // offset from local noon (in hours)
  let delta = Math.abs(12 - local.getHours() + local.getMinutes() / 60);
  if (delta > 12) {
    delta = 24 - delta;
  }
  const opacity = 0.75 * (1 - delta / 12);
  return new Style({
    fill: new Fill({
      color: [0xff, 0xff, 0x33, opacity],
    }),
    stroke: new Stroke({
      color: '#ffffff',
    }),
  });
};

const vector = new VectorLayer({
  source: new VectorSource({
    url: 'data/kml/timezones.kml',
    format: new KML({
      extractStyles: false,
    }),
  }),
  style: styleFunction,
});

const raster = new TileLayer({
  source: new Stamen({
    layer: 'toner',
  }),
});

const map = new Map({
  layers: [raster, vector],
  target: 'map',
  view: new View({
    center: [0, 0],
    zoom: 2,
  }),
});

const info = $('#info');
info.tooltip({
  animation: false,
  trigger: 'manual',
});

const displayFeatureInfo = function (pixel) {
  info.css({
    left: pixel[0] + 'px',
    top: pixel[1] - 15 + 'px',
  });
  const feature = map.forEachFeatureAtPixel(pixel, function (feature) {
    return feature;
  });
  if (feature) {
    info
      .tooltip('hide')
      .attr('data-original-title', feature.get('name'))
      .tooltip('fixTitle')
      .tooltip('show');
  } else {
    info.tooltip('hide');
  }
};

map.on('pointermove', function (evt) {
  if (evt.dragging) {
    info.tooltip('hide');
    return;
  }
  displayFeatureInfo(map.getEventPixel(evt.originalEvent));
});

map.on('click', function (evt) {
  displayFeatureInfo(evt.pixel);
});
<commit_msg>Fix kml-timezone example tooltip<commit_after>import KML from '../src/ol/format/KML.js';
import Map from '../src/ol/Map.js';
import Stamen from '../src/ol/source/Stamen.js';
import VectorSource from '../src/ol/source/Vector.js';
import View from '../src/ol/View.js';
import {Fill, Stroke, Style} from '../src/ol/style.js';
import {Tile as TileLayer, Vector as VectorLayer} from '../src/ol/layer.js';

/*
 * Compute the style of the feature.  Here we want the opacity of polygons to
 * be based on the offset from local noon.  For example, a timezone where it is
 * currently noon would have an opacity of 0.75.  And a timezone where it is
 * currently midnight would have an opacity of 0.  This doesn't account for
 * daylight savings, so don't use it to plan your vacation.
 */
const styleFunction = function (feature) {
  let offset = 0;
  const name = feature.get('name'); // e.g. GMT -08:30
  const match = name.match(/([\-+]\d{2}):(\d{2})$/);
  if (match) {
    const hours = parseInt(match[1], 10);
    const minutes = parseInt(match[2], 10);
    offset = 60 * hours + minutes;
  }
  const date = new Date();
  const local = new Date(
    date.getTime() + (date.getTimezoneOffset() + offset) * 60000
  );
  // offset from local noon (in hours)
  let delta = Math.abs(12 - local.getHours() + local.getMinutes() / 60);
  if (delta > 12) {
    delta = 24 - delta;
  }
  const opacity = 0.75 * (1 - delta / 12);
  return new Style({
    fill: new Fill({
      color: [0xff, 0xff, 0x33, opacity],
    }),
    stroke: new Stroke({
      color: '#ffffff',
    }),
  });
};

const vector = new VectorLayer({
  source: new VectorSource({
    url: 'data/kml/timezones.kml',
    format: new KML({
      extractStyles: false,
    }),
  }),
  style: styleFunction,
});

const raster = new TileLayer({
  source: new Stamen({
    layer: 'toner',
  }),
});

const map = new Map({
  layers: [raster, vector],
  target: 'map',
  view: new View({
    center: [0, 0],
    zoom: 2,
  }),
});

const info = $('#info');
info.tooltip({
  animation: false,
  trigger: 'manual',
});

const displayFeatureInfo = function (pixel) {
  info.css({
    left: pixel[0] + 'px',
    top: pixel[1] - 15 + 'px',
  });
  const feature = map.forEachFeatureAtPixel(pixel, function (feature) {
    return feature;
  });
  if (feature) {
    info.attr('data-original-title', feature.get('name')).tooltip('show');
  } else {
    info.tooltip('hide');
  }
};

map.on('pointermove', function (evt) {
  if (evt.dragging) {
    info.tooltip('hide');
    return;
  }
  displayFeatureInfo(map.getEventPixel(evt.originalEvent));
});

map.on('click', function (evt) {
  displayFeatureInfo(evt.pixel);
});
<|endoftext|>


<commit_before>import React from 'react';
import { connect } from 'react-redux';
import { Field, reduxForm } from 'redux-form';
import { rules } from '../../ReduxForm';
import EmailList from './EmailList';

const SuccessMessage = props => {
  return (

The Enrolment Request was send Successfully!

  );
};

const ErrorMessage = props => {
  const { error } = props;
  return (

  );
};

const TabReqForm = props => {
  const { handleSubmit, pristine, invalid, userEnrolmentForm: { submitSuccess, error } } = props;

  return (
    <form onSubmit={handleSubmit}>
      {submitSuccess ? (
        <SuccessMessage />
      ) : (
        <Field
          component={EmailList}
          label="Users' Daco Email"
          name="email"
          validate={rules.required}
        />
      )}
      {error ? <ErrorMessage error={error} /> : null}
      {!submitSuccess ? (
        <button type="submit" className="action-button" disabled={pristine || invalid}>
          Submit
        </button>
      ) : null}
    </form>
  );
};

const mapStateToProps = state => {
  return {
    userEnrolmentForm: state.userEnrolmentForm,
  };
};

TabReqForm.displayName = 'TabReqForm';

export default connect(mapStateToProps, null)(
  reduxForm({
    form: 'userRequestForm',
    destroyOnUnmount: true,
    forceUnregisterOnUnmount: true,
  })(TabReqForm),
);
<commit_msg>Spelling<commit_after>import React from 'react';
import { connect } from 'react-redux';
import { Field, reduxForm } from 'redux-form';
import { rules } from '../../ReduxForm';
import EmailList from './EmailList';

const SuccessMessage = props => {
  return (

The Enrolment Request was sent Successfully!

  );
};

const ErrorMessage = props => {
  const { error } = props;
  return (

  );
};

const TabReqForm = props => {
  const { handleSubmit, pristine, invalid, userEnrolmentForm: { submitSuccess, error } } = props;

  return (
    <form onSubmit={handleSubmit}>
      {submitSuccess ? (
        <SuccessMessage />
      ) : (
        <Field
          component={EmailList}
          label="Users' Daco Email"
          name="email"
          validate={rules.required}
        />
      )}
      {error ? <ErrorMessage error={error} /> : null}
      {!submitSuccess ? (
        <button type="submit" className="action-button" disabled={pristine || invalid}>
          Submit
        </button>
      ) : null}
    </form>
  );
};

const mapStateToProps = state => {
  return {
    userEnrolmentForm: state.userEnrolmentForm,
  };
};

TabReqForm.displayName = 'TabReqForm';

export default connect(mapStateToProps, null)(
  reduxForm({
    form: 'userRequestForm',
    destroyOnUnmount: true,
    forceUnregisterOnUnmount: true,
  })(TabReqForm),
);
<|endoftext|>


<commit_before>let app     = require('../../../index.js');
let discord = require('discord.js-commando');
let moment  = require('moment');
let logger  = app.logger;

module.exports = class ShowCommand extends discord.Command {
	constructor(client) {
		super(client, {
			name: 'show',
			aliases: ['show'],
			group: 'everyone',
			memberName: 'show',
			description: 'Zeigt die nächste Sendung an',
			throttling: {
				usages: 3,
				duration: 60
			}
		});
	}

	async run(msg, args) {
		app.services.google.calendar.getNextShow()
			.then(function(response) {
				var nextShow = response;

				if (nextShow === undefined) {
					let message = 'Derzeit ist keine Sendung geplant. :lucy_oO:'
					return msg.reply(message);
				}

				let richEmbed = { embed: {
					color: 6360082,
					title: '🎵  **' + nextShow.summary.toUpperCase() + '**\n\n',
					description: moment().to(nextShow.start.dateTime),
					fields: [
						{
							name: 'Anfang:',
							value: moment(nextShow.start.dateTime).format('dd D. MMMM HH:mm')
						},
						{
							name: 'Ende:',
							value: moment(nextShow.end.dateTime).format('dd D. MMMM HH:mm')
						}
					]
				}};

				if (nextShow.description) {
					let description = nextShow.description;

					console.log(description.length);

					if (description.length >= 1024) {
						description = description.slice(0, 1014) + '...';
					}

					console.log(description.length);

					richEmbed.embed.fields.push({
						name: 'Beschreibung:',
						value: '```' + description + '```'
					});

					richEmbed.embed.fields.push({
						name: 'Link:',
						value: nextShow.htmlLink
					});
				}

				if (msg.channel !== undefined) {
					return msg.channel.send("", richEmbed);
				}
				else {
					app.client.channels.find((channel) => {return channel.id === app.config.discord.channelId;})
						.send("", richEmbed)
						.then((message) => {})
						.catch((error) => {
							logger.error(error);
						});
				}
			})
			.catch(function(error) {
				logger.error(error);
				return msg.reply('Sorry, es gab einen Fehler beim ausführen des Befehls.\nhttps://derpicdn.net/img/2012/6/20/10898/full.gif');
			});
	}
};
<commit_msg>Remove debug statements<commit_after>let app     = require('../../../index.js');
let discord = require('discord.js-commando');
let moment  = require('moment');
let logger  = app.logger;

module.exports = class ShowCommand extends discord.Command {
	constructor(client) {
		super(client, {
			name: 'show',
			aliases: ['show'],
			group: 'everyone',
			memberName: 'show',
			description: 'Zeigt die nächste Sendung an',
			throttling: {
				usages: 3,
				duration: 60
			}
		});
	}

	async run(msg, args) {
		app.services.google.calendar.getNextShow()
			.then(function(response) {
				var nextShow = response;

				if (nextShow === undefined) {
					let message = 'Derzeit ist keine Sendung geplant. :lucy_oO:'
					return msg.reply(message);
				}

				let richEmbed = { embed: {
					color: 6360082,
					title: '🎵  **' + nextShow.summary.toUpperCase() + '**\n\n',
					description: moment().to(nextShow.start.dateTime),
					fields: [
						{
							name: 'Anfang:',
							value: moment(nextShow.start.dateTime).format('dd D. MMMM HH:mm')
						},
						{
							name: 'Ende:',
							value: moment(nextShow.end.dateTime).format('dd D. MMMM HH:mm')
						}
					]
				}};

				if (nextShow.description) {
					let description = nextShow.description;

					if (description.length >= 1024) {
						description = description.slice(0, 1014) + '...';
					}

					richEmbed.embed.fields.push({
						name: 'Beschreibung:',
						value: '```' + description + '```'
					});

					richEmbed.embed.fields.push({
						name: 'Link:',
						value: nextShow.htmlLink
					});
				}

				if (msg.channel !== undefined) {
					return msg.channel.send("", richEmbed);
				}
				else {
					app.client.channels.find((channel) => {return channel.id === app.config.discord.channelId;})
						.send("", richEmbed)
						.then((message) => {})
						.catch((error) => {
							logger.error(error);
						});
				}
			})
			.catch(function(error) {
				logger.error(error);
				return msg.reply('Sorry, es gab einen Fehler beim ausführen des Befehls.\nhttps://derpicdn.net/img/2012/6/20/10898/full.gif');
			});
	}
};
<|endoftext|>


<commit_before>import {Map} from "immutable";
import get from "lodash.get";
import {Content} from "native-base";
import {isEmpty, isNil} from "ramda";
import React, {Component, PropTypes} from "react";
import IPropTypes from "react-immutable-proptypes";
import {Dimensions, StyleSheet, TouchableOpacity, View} from "react-native";
import Accordion from "react-native-collapsible/Accordion";
import {connect} from "react-redux";
import {bindActionCreators} from "redux";

import {saveQuestionnaireAnswers} from "../actions/questionnaire";
import Icon from "../components/iwapp-icons";
import QuestionnaireProgress from "../components/questionnaire-progress";
import Text from "../components/text-lato";
import * as colors from "../lib/colors";
import FaIcons from "react-native-vector-icons/FontAwesome";
import {heightWithoutHeader} from "../lib/const";

const styles = StyleSheet.create({
    answer: {
        padding: 5,
        flexDirection: "row"
    },
    lastAnswer: {
        borderBottomWidth: .5,
        borderBottomColor: colors.borderAccordion,
        paddingBottom: 20
    },
    header: {
        borderTopWidth: .5,
        borderTopColor: colors.borderAccordion,
        flexDirection: "row",
        alignItems: "center",
        justifyContent: "space-between",
        paddingHorizontal: 18,
        paddingVertical: 5
    },
    lastHeader: {
        borderBottomWidth: .5,
        borderBottomColor: colors.borderAccordion
    },
    questionnaireProgressContainer: {
        marginBottom: 10,
        alignItems: "center"
    },
    questionStatus: {
        borderRadius: 100,
        height: 8,
        width: 8,
        marginRight: 10,
        alignSelf: "center"
    },
    questionTextWrp: {
        justifyContent: "center",
        minHeight: 40
    },
    questionText: {
        fontSize: 13,
        color: colors.textGrey
    },
    answerStatus: {
        borderRadius: 100,
        height: 22,
        width: 22,
        borderWidth: .5,
        borderColor: colors.borderAccordion,
        marginHorizontal: 5,
        alignItems: "center"
    },
    answerStatusText: {
        color: colors.questionnaireAnswerStatusText,
        top: 2,
        fontWeight: "bold"
    },
    answerWrp: {
        marginBottom: 5
    },
    textAnswer: {
        alignSelf: "center",
        fontSize: 13,
        color: colors.questionnaireAnswerStatusText,
    }
});

class Questionnaire extends Component {

    static propTypes = {
        asteroid: PropTypes.object.isRequired,
        collections: IPropTypes.map,
        saveQuestionnaireAnswers: PropTypes.func.isRequired,
        selectedQuestionnaire: PropTypes.shape({
            key: PropTypes.string.isRequired
        }).isRequired,
        sessionId: PropTypes.string.isRequired,
        site: PropTypes.object.isRequired,
        userId: PropTypes.string.isRequired
    }

    static defaultProps: {
        site: {}
    }

    constructor (props) {
        super(props);
        this.state = {
            answers: []
        };
    }

    componentWillMount () {
        this.setAnswersState(this.props);
    }

    componentDidMount () {
        this.subscribeToAnswers();
        this.subscribeToQuestions();
    }

    componentWillReceiveProps (nextProps) {
        this.setAnswersState(nextProps);
    }

    setAnswersState (props) {
        const questionnaire = this.getQuestionnaire(props.collections);
        const answersCollection = this.getAnswersFromCollection(props.collections).toJS();
        const answers = this.setAnswers(answersCollection.answers, questionnaire);
        this.setState({answers});
    }

    subscribeToQuestions () {
        const category = this.getQuestionnaireCategory();
        this.props.asteroid.subscribe("questions", {type: "questionnaire", category});
    }

    subscribeToAnswers () {
        const category = this.getQuestionnaireCategory();
        this.props.asteroid.subscribe("answers", {
            siteId: this.props.site._id,
            category,
            type: "questionnaire"
        });
    }

    getQuestionnaire (collections) {
        if (collections.get("questions")) {
            const questionnaire = collections.get("questions").find(questions =>
                questions.get("type") === "questionnaire" &&
                questions.get("category") === this.getQuestionnaireCategory()
            ) || Map();
            return questionnaire.toJS();
        }
        return {};
    }

    getQuestionnaireCategory () {
        return get(this.props, "selectedQuestionnaire.key");
    }

    getAnswersId (collections) {
        const {type, category} = this.getQuestionnaire(collections);
        return `${type}-${category}-${this.props.site._id}`;
    }

    getAnswersFromCollection (collections) {
        return collections.getIn(["answers", this.getAnswersId(collections)]) || Map();
    }

    getAnswerFromState (questionId) {
        return this.state.answers.find(answer => questionId === answer.id);
    }

    setAnswers (answers = [], questionnaire, selectedAnswer, questionIndex) {
        if (!questionnaire.questions) {
            return [];
        }
        return questionnaire.questions.map((question, index) => {
            const selectedAnswerInCollection = answers.find(answer => question.id === answer.id) || {};
            if (isEmpty(selectedAnswerInCollection) && isNil(selectedAnswer)) {
                return {};
            }
            if (index !== questionIndex) {
                return (
                    this.getAnswerFromState(question.id) ||
                    selectedAnswerInCollection
                );
            }
            return {
                id: question.id,
                timestamp: new Date().toISOString(),
                answer: selectedAnswer,
                question: {
                    text: question.text
                }
            };
        });
    }

    isLastSection (questionIndex) {
        return questionIndex === get(this.getQuestionnaire(this.props.collections), "questions.length") - 1;
    }

    isAlreadyAnswered (questionId) {
        const answer = this.getAnswerFromState(questionId);
        return answer && answer.id === questionId;
    }

    isActiveAnswer (option, optionIndex, questionId) {
        const answer = this.getAnswerFromState(questionId);
        return (
            answer &&
            answer.id === questionId &&
            answer.answer === option
        );
    }

    onSaveQuestionnaireAnswers ({option, questionIndex}) {
        const questionnaire = this.getQuestionnaire(this.props.collections);
        const collectionsAnswer = this.getAnswersFromCollection(this.props.collections).get("answers");
        const answers = this.setAnswers(
            collectionsAnswer ? collectionsAnswer.toJS() : [],
            questionnaire,
            option,
            questionIndex
        );
        this.setState({answers});
        this.props.saveQuestionnaireAnswers(
            [answers[questionIndex]],
            this.props.userId,
            this.props.site._id,
            questionnaire,
            this.props.sessionId
        );
    }

    renderQuestionStatus (question) {
        return (
            <View
                style={[styles.questionStatus, {
                    backgroundColor: (
                        this.isAlreadyAnswered(question.id) ?
                        colors.greenStatus :
                        colors.redStatus
                    )
                }]}
            />
        );
    }

    renderHeader (question, questionIndex, isActive) {
        const {width} = Dimensions.get("window");
        return (
            <View
                key={questionIndex}
                style={[styles.header, this.isLastSection(questionIndex) ? styles.lastHeader : {}]}
            >
                <View style={{flexDirection: "row"}}>
                    {this.renderQuestionStatus(question, questionIndex)}
                    <View style={[styles.questionTextWrp, {width: width * 0.75}]}>
                        <Text style={styles.questionText}>{question.text} {"neowniwofnewoifniwoenfwiofnwioncjwiebeiw uwjqibdqiud dbwqui jdiw jiwe wi"}</Text>
                    </View>
                </View>
                <FaIcons
                    color={colors.iconArrow}
                    name={isActive ? "angle-up" : "angle-down"}
                    size={26}
                />
            </View>
        );
    }

    renderAnswerStatus (option, optionIndex, questionId) {
        return this.isActiveAnswer(option, optionIndex, questionId) ? (
            <View style={[styles.answerStatus, {borderColor: colors.greenStatus}]}>
                <Icon
                    color={colors.greenStatus}
                    name="iw-check"
                    size={16}
                    style={{backgroundColor: colors.transparent, top: 2}}
                />
            </View>
        ) : (
            <View style={styles.answerStatus}>
                <Text style={styles.answerStatusText}>{optionIndex + 1}</Text>
            </View>
        );
    }

    renderAnswer ({option, optionIndex, questionIndex, question}) {
        const {width} = Dimensions.get("window");
        const optionsLength = question.options.length;
        return (
            <View key={optionIndex} style={[
                (optionIndex === optionsLength - 1 && this.isLastSection(questionIndex)) ? styles.lastAnswer : {}
            ]}>
                <TouchableOpacity onPress={() => this.onSaveQuestionnaireAnswers(...arguments)} style={styles.answerWrp}>
                    <View style={styles.answer}>
                        {this.renderAnswerStatus(option, optionIndex, question.id)}
                        <Text style={[styles.textAnswer, {width: width * 0.85}]}>{option}</Text>
                    </View>
                </TouchableOpacity>
            </View>
        );
    }

    renderContent (question, questionIndex) {
        return question.options.map((option, optionIndex) =>
            ::this.renderAnswer({option, optionIndex, question, questionIndex})
        );
    }

    renderAccordion (questionnaire) {
        return !isEmpty(questionnaire) ? (
            <Accordion
                renderContent={::this.renderContent}
                renderHeader={::this.renderHeader}
                sections={questionnaire.questions}
                underlayColor={colors.transparent}
            />
        ) : null;
    }

    render () {
        const {height} = Dimensions.get("window");
        const {selectedQuestionnaire} = this.props;
        const questionnaire = this.getQuestionnaire(this.props.collections);
        return (
            <View style={{height: heightWithoutHeader(height)}}>
                <Content>
                    <View style={styles.questionnaireProgressContainer}>
                        <QuestionnaireProgress questionnaire={selectedQuestionnaire} />
                    </View>
                    <View>
                        {this.renderAccordion(questionnaire)}
                    </View>
                </Content>
            </View>
        );
    }

}

function mapStateToProps (state) {
    return {
        collections: state.collections,
        sessionId: state.sessionId,
        site: state.site,
        userId: state.userId
    };
}
function mapDispatchToProps (dispatch) {
    return {
        saveQuestionnaireAnswers: bindActionCreators(saveQuestionnaireAnswers, dispatch)
    };
}
export default connect(mapStateToProps, mapDispatchToProps)(Questionnaire);
<commit_msg>Fixed question text.<commit_after>import {Map} from "immutable";
import get from "lodash.get";
import {Content} from "native-base";
import {isEmpty, isNil} from "ramda";
import React, {Component, PropTypes} from "react";
import IPropTypes from "react-immutable-proptypes";
import {Dimensions, StyleSheet, TouchableOpacity, View} from "react-native";
import Accordion from "react-native-collapsible/Accordion";
import {connect} from "react-redux";
import {bindActionCreators} from "redux";

import {saveQuestionnaireAnswers} from "../actions/questionnaire";
import Icon from "../components/iwapp-icons";
import QuestionnaireProgress from "../components/questionnaire-progress";
import Text from "../components/text-lato";
import * as colors from "../lib/colors";
import FaIcons from "react-native-vector-icons/FontAwesome";
import {heightWithoutHeader} from "../lib/const";

const styles = StyleSheet.create({
    answer: {
        padding: 5,
        flexDirection: "row"
    },
    lastAnswer: {
        borderBottomWidth: .5,
        borderBottomColor: colors.borderAccordion,
        paddingBottom: 20
    },
    header: {
        borderTopWidth: .5,
        borderTopColor: colors.borderAccordion,
        flexDirection: "row",
        alignItems: "center",
        justifyContent: "space-between",
        paddingHorizontal: 18,
        paddingVertical: 5
    },
    lastHeader: {
        borderBottomWidth: .5,
        borderBottomColor: colors.borderAccordion
    },
    questionnaireProgressContainer: {
        marginBottom: 10,
        alignItems: "center"
    },
    questionStatus: {
        borderRadius: 100,
        height: 8,
        width: 8,
        marginRight: 10,
        alignSelf: "center"
    },
    questionTextWrp: {
        justifyContent: "center",
        minHeight: 40
    },
    questionText: {
        fontSize: 13,
        color: colors.textGrey
    },
    answerStatus: {
        borderRadius: 100,
        height: 22,
        width: 22,
        borderWidth: .5,
        borderColor: colors.borderAccordion,
        marginHorizontal: 5,
        alignItems: "center"
    },
    answerStatusText: {
        color: colors.questionnaireAnswerStatusText,
        top: 2,
        fontWeight: "bold"
    },
    answerWrp: {
        marginBottom: 5
    },
    textAnswer: {
        alignSelf: "center",
        fontSize: 13,
        color: colors.questionnaireAnswerStatusText,
    }
});

class Questionnaire extends Component {

    static propTypes = {
        asteroid: PropTypes.object.isRequired,
        collections: IPropTypes.map,
        saveQuestionnaireAnswers: PropTypes.func.isRequired,
        selectedQuestionnaire: PropTypes.shape({
            key: PropTypes.string.isRequired
        }).isRequired,
        sessionId: PropTypes.string.isRequired,
        site: PropTypes.object.isRequired,
        userId: PropTypes.string.isRequired
    }

    static defaultProps: {
        site: {}
    }

    constructor (props) {
        super(props);
        this.state = {
            answers: []
        };
    }

    componentWillMount () {
        this.setAnswersState(this.props);
    }

    componentDidMount () {
        this.subscribeToAnswers();
        this.subscribeToQuestions();
    }

    componentWillReceiveProps (nextProps) {
        this.setAnswersState(nextProps);
    }

    setAnswersState (props) {
        const questionnaire = this.getQuestionnaire(props.collections);
        const answersCollection = this.getAnswersFromCollection(props.collections).toJS();
        const answers = this.setAnswers(answersCollection.answers, questionnaire);
        this.setState({answers});
    }

    subscribeToQuestions () {
        const category = this.getQuestionnaireCategory();
        this.props.asteroid.subscribe("questions", {type: "questionnaire", category});
    }

    subscribeToAnswers () {
        const category = this.getQuestionnaireCategory();
        this.props.asteroid.subscribe("answers", {
            siteId: this.props.site._id,
            category,
            type: "questionnaire"
        });
    }

    getQuestionnaire (collections) {
        if (collections.get("questions")) {
            const questionnaire = collections.get("questions").find(questions =>
                questions.get("type") === "questionnaire" &&
                questions.get("category") === this.getQuestionnaireCategory()
            ) || Map();
            return questionnaire.toJS();
        }
        return {};
    }

    getQuestionnaireCategory () {
        return get(this.props, "selectedQuestionnaire.key");
    }

    getAnswersId (collections) {
        const {type, category} = this.getQuestionnaire(collections);
        return `${type}-${category}-${this.props.site._id}`;
    }

    getAnswersFromCollection (collections) {
        return collections.getIn(["answers", this.getAnswersId(collections)]) || Map();
    }

    getAnswerFromState (questionId) {
        return this.state.answers.find(answer => questionId === answer.id);
    }

    setAnswers (answers = [], questionnaire, selectedAnswer, questionIndex) {
        if (!questionnaire.questions) {
            return [];
        }
        return questionnaire.questions.map((question, index) => {
            const selectedAnswerInCollection = answers.find(answer => question.id === answer.id) || {};
            if (isEmpty(selectedAnswerInCollection) && isNil(selectedAnswer)) {
                return {};
            }
            if (index !== questionIndex) {
                return (
                    this.getAnswerFromState(question.id) ||
                    selectedAnswerInCollection
                );
            }
            return {
                id: question.id,
                timestamp: new Date().toISOString(),
                answer: selectedAnswer,
                question: {
                    text: question.text
                }
            };
        });
    }

    isLastSection (questionIndex) {
        return questionIndex === get(this.getQuestionnaire(this.props.collections), "questions.length") - 1;
    }

    isAlreadyAnswered (questionId) {
        const answer = this.getAnswerFromState(questionId);
        return answer && answer.id === questionId;
    }

    isActiveAnswer (option, optionIndex, questionId) {
        const answer = this.getAnswerFromState(questionId);
        return (
            answer &&
            answer.id === questionId &&
            answer.answer === option
        );
    }

    onSaveQuestionnaireAnswers ({option, questionIndex}) {
        const questionnaire = this.getQuestionnaire(this.props.collections);
        const collectionsAnswer = this.getAnswersFromCollection(this.props.collections).get("answers");
        const answers = this.setAnswers(
            collectionsAnswer ? collectionsAnswer.toJS() : [],
            questionnaire,
            option,
            questionIndex
        );
        this.setState({answers});
        this.props.saveQuestionnaireAnswers(
            [answers[questionIndex]],
            this.props.userId,
            this.props.site._id,
            questionnaire,
            this.props.sessionId
        );
    }

    renderQuestionStatus (question) {
        return (
            <View
                style={[styles.questionStatus, {
                    backgroundColor: (
                        this.isAlreadyAnswered(question.id) ?
                        colors.greenStatus :
                        colors.redStatus
                    )
                }]}
            />
        );
    }

    renderHeader (question, questionIndex, isActive) {
        const {width} = Dimensions.get("window");
        return (
            <View
                key={questionIndex}
                style={[styles.header, this.isLastSection(questionIndex) ? styles.lastHeader : {}]}
            >
                <View style={{flexDirection: "row"}}>
                    {this.renderQuestionStatus(question, questionIndex)}
                    <View style={[styles.questionTextWrp, {width: width * 0.75}]}>
                        <Text style={styles.questionText}>{question.text}</Text>
                    </View>
                </View>
                <FaIcons
                    color={colors.iconArrow}
                    name={isActive ? "angle-up" : "angle-down"}
                    size={26}
                />
            </View>
        );
    }

    renderAnswerStatus (option, optionIndex, questionId) {
        return this.isActiveAnswer(option, optionIndex, questionId) ? (
            <View style={[styles.answerStatus, {borderColor: colors.greenStatus}]}>
                <Icon
                    color={colors.greenStatus}
                    name="iw-check"
                    size={16}
                    style={{backgroundColor: colors.transparent, top: 2}}
                />
            </View>
        ) : (
            <View style={styles.answerStatus}>
                <Text style={styles.answerStatusText}>{optionIndex + 1}</Text>
            </View>
        );
    }

    renderAnswer ({option, optionIndex, questionIndex, question}) {
        const {width} = Dimensions.get("window");
        const optionsLength = question.options.length;
        return (
            <View key={optionIndex} style={[
                (optionIndex === optionsLength - 1 && this.isLastSection(questionIndex)) ? styles.lastAnswer : {}
            ]}>
                <TouchableOpacity onPress={() => this.onSaveQuestionnaireAnswers(...arguments)} style={styles.answerWrp}>
                    <View style={styles.answer}>
                        {this.renderAnswerStatus(option, optionIndex, question.id)}
                        <Text style={[styles.textAnswer, {width: width * 0.85}]}>{option}</Text>
                    </View>
                </TouchableOpacity>
            </View>
        );
    }

    renderContent (question, questionIndex) {
        return question.options.map((option, optionIndex) =>
            ::this.renderAnswer({option, optionIndex, question, questionIndex})
        );
    }

    renderAccordion (questionnaire) {
        return !isEmpty(questionnaire) ? (
            <Accordion
                renderContent={::this.renderContent}
                renderHeader={::this.renderHeader}
                sections={questionnaire.questions}
                underlayColor={colors.transparent}
            />
        ) : null;
    }

    render () {
        const {height} = Dimensions.get("window");
        const {selectedQuestionnaire} = this.props;
        const questionnaire = this.getQuestionnaire(this.props.collections);
        return (
            <View style={{height: heightWithoutHeader(height)}}>
                <Content>
                    <View style={styles.questionnaireProgressContainer}>
                        <QuestionnaireProgress questionnaire={selectedQuestionnaire} />
                    </View>
                    <View>
                        {this.renderAccordion(questionnaire)}
                    </View>
                </Content>
            </View>
        );
    }

}

function mapStateToProps (state) {
    return {
        collections: state.collections,
        sessionId: state.sessionId,
        site: state.site,
        userId: state.userId
    };
}
function mapDispatchToProps (dispatch) {
    return {
        saveQuestionnaireAnswers: bindActionCreators(saveQuestionnaireAnswers, dispatch)
    };
}
export default connect(mapStateToProps, mapDispatchToProps)(Questionnaire);
<|endoftext|>


<commit_before>// get the namespace, or declare it here
window.eureka = (typeof window.eureka == "undefined" || !window.eureka ) ? {} : window.eureka;

// add in the namespace
window.eureka.registration = new function () {
	var self = this;

	self.setup = function (registrationForm, agreementAnchor, agreementModal) {
		$(registrationForm).submit(this.submit);
		$(agreementAnchor).click(function () {
			$(agreementModal).modal('show');
		});
		$.validator.addMethod("passwordCheck",
			function (value, element) {
				return value.length >= 8 && /[0-9]+/.test(value) && /[a-zA-Z]+/.test(value);
			},
			"Please enter at least 8 characters with at least 1 digit."
		);
	};

	self.validator = $("#signupForm").validate({
		errorElement: 'span',
		errorClass: null,
		rules: {
			firstName: "required",
			lastName: "required",
			password: {
				required: true,
				minlength: 8,
				passwordCheck: true
			},
			organization: {
				required: true
			},
			verifyPassword: {
				required: true,
				equalTo: "#password"
			},
			email: {
				required: true,
				email: true
			},
			verifyEmail: {
				required: true,
				email: true,
				equalTo: "#email"
			},
			agreement: {
				required: true
			},
			title: {
				required: true
			},
			department: {
				required: true
			}
		},
		messages: {
			firstName: "Enter your firstname",
			lastName: "Enter your lastname",
			organization: "Enter your organization",
			password: {
				required: "Provide a password",
				rangelength: $.validator.format("Please enter at least {0} characters")
			},
			verifyPassword: {
				required: "Repeat your password",
				minlength: $.validator.format("Please enter at least {0} characters"),
				equalTo: "Enter the same password as above"
			},
			email: {
				required: "Please enter a valid email address",
				minlength: "Please enter a valid email address"
			},
			verifyEmail: {
				required: "Please enter a valid email address",
				minlength: "Please enter a valid email address",
				equalTo: "Enter the same email as above"
			},
			agreement: {
				required: "Please check the agreement checkbox"
			},
			title: "Enter your title",
			department: "Enter your department"
		},
		errorPlacement: function (error, element) {
			$(element).closest('.form-group').find('.help-block').empty();
			error.appendTo($(element).closest('.form-group').find('.help-block'));
		},
		highlight: function (element) {
			$(element).closest('.form-group').addClass('has-error');
			$(element).closest('.form-group').find('.help-block').removeClass('default-hidden');
		},
		unhighlight: function (element) {
			$(element).closest('.form-group').removeClass('has-error');
			$(element).closest('.form-group').find('.help-block').addClass('default-hidden');
		},
		// set this class to error-labels to indicate valid fields
		success: function (label) {
			// set   as text for IE
			label.html(" ").addClass("checked");
		}
	});

	self.submit = function () {
		if (self.validator.valid()) {
			var username = $('#username').val();
			var firstName = $('#firstName').val();
			var lastName = $('#lastName').val();
			var organization = $('#organization').val();
			var password = $('#password').val();
			var verifyPassword = $('#verifyPassword').val();
			var email = $('#email').val();
			var verifyEmail = $('#verifyEmail').val();
			var title = $('#title').val();
			var department = $('#department').val();
			var providerUsername = $('#providerUsername').val();
			var authenticationMethod = $('#authenticationMethod').val();
			var oauthProvider = $('#oauthProvider').val();

			var dataString = 'firstName=' + firstName + '&lastName=' + lastName + '&organization=' + organization +
				'&password=' + password + '&verifyPassword=' + verifyPassword +
				'&email=' + email + '&verifyEmail=' + verifyEmail + '&title=' + title + '&department=' + department +
				(username ? '&username=' + username : '') +
				(providerUsername ? '&providerUsername=' + providerUsername : '') +
				'&authenticationMethod=' + authenticationMethod +
				(oauthProvider ? '&oauthProvider=' + oauthProvider : '');
			$.ajax({
				type: 'POST',
				url: 'register',
				data: dataString,
				success: function () {
					$('#passwordChangeFailure').hide();
					$('#signupForm').hide();
					$('#registerHeading').hide();
					$('#registrationComplete').show();
				},
				error: function (xhr, err) {
					$('#passwordChangeFailure').show();
					$('#passwordErrorMessage').text(xhr.responseText);
					$("#passwordErrorMessage:contains('login')").html(function(_, html) {
                                            return html.replace(/(login)/g, '$1');
					});
				}
			});
		}
		return false;
	}
};
<commit_msg>Generate error messages from js code<commit_after>// get the namespace, or declare it here
window.eureka = (typeof window.eureka == "undefined" || !window.eureka ) ? {} : window.eureka;

// add in the namespace
window.eureka.registration = new function () {
	var self = this;

	self.setup = function (registrationForm, agreementAnchor, agreementModal) {
		$(registrationForm).submit(this.submit);
		$(agreementAnchor).click(function () {
			$(agreementModal).modal('show');
		});
		$.validator.addMethod("passwordCheck",
			function (value, element) {
				return value.length >= 8 && /[0-9]+/.test(value) && /[a-zA-Z]+/.test(value);
			},
			"Please enter at least 8 characters with at least 1 digit."
		);
	};

	self.validator = $("#signupForm").validate({
		errorElement: 'span',
		errorClass: null,
		rules: {
			firstName: "required",
			lastName: "required",
			password: {
				required: true,
				minlength: 8,
				passwordCheck: true
			},
			organization: {
				required: true
			},
			verifyPassword: {
				required: true,
				equalTo: "#password"
			},
			email: {
				required: true,
				email: true
			},
			verifyEmail: {
				required: true,
				email: true,
				equalTo: "#email"
			},
			agreement: {
				required: true
			},
			title: {
				required: true
			},
			department: {
				required: true
			}
		},
		messages: {
			firstName: "Enter your firstname",
			lastName: "Enter your lastname",
			organization: "Enter your organization",
			password: {
				required: "Provide a password",
				rangelength: $.validator.format("Please enter at least {0} characters")
			},
			verifyPassword: {
				required: "Repeat your password",
				minlength: $.validator.format("Please enter at least {0} characters"),
				equalTo: "Enter the same password as above"
			},
			email: {
				required: "Please enter a valid email address",
				minlength: "Please enter a valid email address"
			},
			verifyEmail: {
				required: "Please enter a valid email address",
				minlength: "Please enter a valid email address",
				equalTo: "Enter the same email as above"
			},
			agreement: {
				required: "Please check the agreement checkbox"
			},
			title: "Enter your title",
			department: "Enter your department"
		},
		errorPlacement: function (error, element) {
			$(element).closest('.form-group').find('.help-block').empty();
			error.appendTo($(element).closest('.form-group').find('.help-block'));
		},
		highlight: function (element) {
			$(element).closest('.form-group').addClass('has-error');
			$(element).closest('.form-group').find('.help-block').removeClass('default-hidden');
		},
		unhighlight: function (element) {
			$(element).closest('.form-group').removeClass('has-error');
			$(element).closest('.form-group').find('.help-block').addClass('default-hidden');
		},
		// set this class to error-labels to indicate valid fields
		success: function (label) {
			// set   as text for IE
			label.html(" ").addClass("checked");
		}
	});

	self.submit = function () {
		if (self.validator.valid()) {
			var username = $('#username').val();
			var firstName = $('#firstName').val();
			var lastName = $('#lastName').val();
			var organization = $('#organization').val();
			var password = $('#password').val();
			var verifyPassword = $('#verifyPassword').val();
			var email = $('#email').val();
			var verifyEmail = $('#verifyEmail').val();
			var title = $('#title').val();
			var department = $('#department').val();
			var providerUsername = $('#providerUsername').val();
			var authenticationMethod = $('#authenticationMethod').val();
			var oauthProvider = $('#oauthProvider').val();

			var dataString = 'firstName=' + firstName + '&lastName=' + lastName + '&organization=' + organization +
				'&password=' + password + '&verifyPassword=' + verifyPassword +
				'&email=' + email + '&verifyEmail=' + verifyEmail + '&title=' + title + '&department=' + department +
				(username ? '&username=' + username : '') +
				(providerUsername ? '&providerUsername=' + providerUsername : '') +
				'&authenticationMethod=' + authenticationMethod +
				(oauthProvider ? '&oauthProvider=' + oauthProvider : '');
			$.ajax({
				type: 'POST',
				url: 'register',
				data: dataString,
				success: function () {
					$('#passwordChangeFailure').hide();
					$('#signupForm').hide();
					$('#registerHeading').hide();
					$('#registrationComplete').show();
				},
				error: function (xhr, err) {
					$('#passwordChangeFailure').show();
                                        if (xhr.status == "409") {
                                                $('#passwordErrorMessage').html("Account already exists! You may go ahead and login.");
                                        } else if (xhr.status == "400"){
                                                $('#passwordErrorMessage').html("Please review your profile and complete all required fields.");
                                        } else {
                                                $('#passwordErrorMessage').html("An internal error has occurred. Please contact the technical team for further assistance.");
                                        }

				}
			});
		}
		return false;
	}
};
<|endoftext|>


<commit_before>var imagesHTML = '
'+
		'
Document
'+
		'
'+
			'<button id="save_document" data-ng-disabled=\'permitSaveblocks()\' data-toggle="modal" data-target="#actions-workspace" type="button" class="doc_save btn_simple light_blue pull-left">enregistrer</button>'+
            '<button type="button" data-ng-click=\'resiseWorkspace($event)\' class="doc_resizing pull-left"> </button>'+
        '
'+
    '
'+
'
'+
'
'+
	'
'+
		'
'+
			'
'+
				'<button id="edit_text" data-ng-click="modifierTexte()"  type="button" class="set_txtbtn btn_simple light_blue pull-left with_icnleft" title="{{\'Editer le texte\' | translate}}" >editer le texte</button>'+
				'<button data-ng-show="(currentImage.source===undefined)" data-ng-click="duplicateBlock2()" type="button" class="duplicate_txtbtn btn_simple light_blue pull-left with_icnleft" title="{{\'Dupliquer le bloc\' | translate}}">Dupliquer le bloc</button>'+
				'<button data-ng-click="remove2()"  type="button" class="delete_txtbtn btn_simple light_blue pull-left with_icnleft" title="{{\'Supprimer le bloc\' | translate}}">Supprimer le bloc</button>'+
			'
'+
			'
'+
				'
'+
				'Style de texte'+
				'<select sselect class="" id="select-tag" data-ng-init="tagSelected = listTags[0]" data-ng-model="tagSelected" data-ng-change="updateBlockType()">'+
					'<option data-ng-repeat="tag in listTags" value="{{tag._id}}">{{tag.libelle}}</option>'+
				'</select>'+
				'
'+
				'
'+
					'
'+
						'Synthèse vocale'+
						''+
					'
'+
					'
'+
						'<button data-ng-click="textToSpeech()" type="button" class="btn_simple light_blue generate_vocale">génerer</button>'+
					'
'+
					'
'+
						'<button type="button" class="btn_simple light_blue play_vocale" data-ng-click="playSong()"> </button>'+
						'<audio id="player" src="" preload="auto"></audio>'+
					'
'+
				'
'+
				'<button type="button" class="btn_simple light_blue pull-right" data-ng-click="textToSpeech()" data-ng-show="!showSynthese" title="{{\'synthese vocale\' | translate}}" >synthèse vocale</button>'+
			'
'+
			'
'+
				''+
					'

'+
				''+
				'
'+
					'<textarea ck-editor data-ng-model="textes.text" id="editorOcr" data-barre="OcrVersion" data-ng-change ="initCkEditorChange()"></textarea>'+
				'
'+
				''+
					'
'+
						'
'+
							'<button data-ng-click="removeZone(zone._id)"  class="label label-default">'+
							''+
							'</button>'+
						'
'+
					'
'+
					''+
				''+
			'
'+
		'
'+
		'
'+
				'
 div\''+
				'}" ui-nested-sortable-stop="updateDragDrop($event, $ui)">'+
				'
'+
					'
'+
						''+
							'<button class="toggle" data-ng-click="toggleMinimized(child)" data-ng-switch on="child.minimized">'+
								'0" data-ng-switch-when="true" class="tree_closed">'+
								''+
								'0" data-ng-switch-default class="tree_opened">'+
								''+
							'</button>'+
							'<button class="delete_layer" data-ng-click="remove(child)" title="{{\'Supprimer calque\' | translate}}" > </button>'+
							''+
							''+
							'<button data-ng-show="(child.source===undefined)" class="duplicate_layer" data-ng-click="duplicateBlock(child)" title="{{\'Dupliquer calque\' | translate}}" > </button>'+
						''+
						''+
							' '+
							' '+
							''+
								''+
							''+
						''+
					'
'+
					'

'+
				'
'+
			'
'+
		'
'+
	'
'+
	'
'+
	'
'+
	'
'+
    '
'+
      '
'+
        '
 '+
        '
'+
      '
'+
      '
{{loaderMessage}}
'+
    '
'+
'
'+
'
'+
''+
'
'+
	'
'+
		'
'+
			'
'+
				'<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>'+
				'
Enregistrer document
'+
				'
Modifier document
'+
			'
'+
			'
{{msgErrorModal}}
'+
			'
'+
				'<form id="show_document" name="show_document" class="" role="form" style="overflow:hidden;">'+
					'<fieldset class="padding_large">'+
						'
'+
						'Titre du document *'+
						'<input type="text" max-length="32" data-ng-disabled="editBlocks" class="" id="docTitre" placeholder="Entrez le titre du document" data-ng-model="docTitre" required>'+
						'
'+
					'</fieldset>'+
					'
'+
						'<button id="reset_save_inDropbox" type="button" class="reset_btn" data-dismiss="modal" title="{{\'Annuler\' | translate}}">Annuler</button>'+
						'<button id="save_inDropbox" data-ng-show="!editBlocks" type="button" class="btn_simple light_blue" data-ng-click="showlocks()"  title="{{\'Enregistrer sur ma Dropbox\' | translate}}">Enregistrer sur ma Dropbox</button>'+
						'<button id="saveEdited_inDropbox"  data-ng-show="editBlocks" type="button" class="btn_simple light_blue" data-ng-disabled="!show_document.$valid" data-ng-click="saveRestBlocks()" data-dismiss="modal" title="{{\'Modifier sur ma Dropbox\' | translate}}">Modifier sur ma Dropbox</button>'+
					'
'+
				'</form>'+
			'
'+
		'
'+
	'
'+
'
'+
''+
'
'+
	'
'+
		'
'+
			'
'+
				'
lien non valide
'+
			'
'+
			'
'+
				'
'+
				'Veuillez utiliser la Bookmarklet CnedAdapt avec l\'URL d\'un fichier PDF.'+
				'
'+
			'
'+
			'
'+
				'<button type="button" class="btn_simple light_blue much_padding" data-dismiss="modal">OK</button>'+
			'
'+
		'
'+
	'
'+
'
'+
'
'+
	'
'+
		'
'+
			'
'+
				'
information
'+
			'
'+
			'
'+
				'
'+
				'L\’application ne pourra pas traiter votre document de façon optimale en raison du poids du fichier et/ou de son contenu. Aussi, nous vous invitons à réessayer avec une autre version de votre document.'+
				'
'+
			'
'+
			'
'+
				'<button type="button" class="btn_simple light_blue much_padding" data-ng-click="modalError(\'myModalWorkSpaceBig\')">OK</button>'+
			'
'+
		'
'+
	'
'+
'
'+
'
'+
	'
'+
		'
'+
			'
'+
				'
information
'+
			'
'+
			'
'+
				'
'+
				'La taille de l\'ePub est supérieur à la taille limite supportée par l\'application.'+
				'
'+
			'
'+
			'
'+
				'<button type="button" class="btn_simple light_blue much_padding" data-ng-click="modalError(\'myModalWorkSpaceTooMany\')">OK</button>'+
			'
'+
		'
'+
	'
'+
'
'+
'
'+
	'
'+
		'
'+
			'
'+
				'
INFORMATION
'+
			'
'+
			'
'+
				'
'+
				'Pour structurer le document vous devez être authentifié. Veuillez-vous authentifier avant de poursuivre la structuration.'+
				'
'+
			'
'+
			'
'+
				'<button type="button" class="btn_simple light_blue much_padding" data-dismiss="modal">OK</button>'+
			'
'+
		'
'+
	'
'+
'
'+
'
'+
	'
'+
		'
'+
			'
'+
				'
INFORMATION
'+
			'
'+
			'
'+
				'
'+
				'Le document choisi a deja été structuré,voulez-vous ?'+
				'
'+
			'
'+
			'
'+
				'<button type="button" data-ng-click=\'resumeWorking()\' class="btn_simple light_blue much_padding" data-dismiss="modal">Continuer la structuration</button>'+
				'<button type="button" data-ng-click=\'createNew()\' class="btn_simple light_blue much_padding" data-dismiss="modal">Structurer à nouveau</button>'+
				'<button type="button" data-ng-click=\'openApercu()\' class="btn_simple light_blue much_padding" data-dismiss="modal">Afficher le document</button>'+
			'
'+
		'
'+
	'
'+
'
'+
''+
'
'+
    '
'+
		'
{{loaderMessage}}
'+
    '
'+
'
'+
'
';
<commit_msg>Correction de la selection des images de l'espace de travail<commit_after>var imagesHTML = '
'+
		'
Document
'+
		'
'+
			'<button id="save_document" data-ng-disabled=\'permitSaveblocks()\' data-toggle="modal" data-target="#actions-workspace" type="button" class="doc_save btn_simple light_blue pull-left">enregistrer</button>'+
            '<button type="button" data-ng-click=\'resiseWorkspace($event)\' class="doc_resizing pull-left"> </button>'+
        '
'+
    '
'+
'
'+
'
'+
	'
'+
		'
'+
			'
'+
				'<button id="edit_text" data-ng-click="modifierTexte()"  type="button" class="set_txtbtn btn_simple light_blue pull-left with_icnleft" title="{{\'Editer le texte\' | translate}}" >editer le texte</button>'+
				'<button data-ng-show="(currentImage.source===undefined)" data-ng-click="duplicateBlock2()" type="button" class="duplicate_txtbtn btn_simple light_blue pull-left with_icnleft" title="{{\'Dupliquer le bloc\' | translate}}">Dupliquer le bloc</button>'+
				'<button data-ng-click="remove2()"  type="button" class="delete_txtbtn btn_simple light_blue pull-left with_icnleft" title="{{\'Supprimer le bloc\' | translate}}">Supprimer le bloc</button>'+
			'
'+
			'
'+
				'
'+
				'Style de texte'+
				'<select sselect class="" id="select-tag" data-ng-init="tagSelected = listTags[0]" data-ng-model="tagSelected" data-ng-change="updateBlockType()">'+
					'<option data-ng-repeat="tag in listTags" value="{{tag._id}}">{{tag.libelle}}</option>'+
				'</select>'+
				'
'+
				'
'+
					'
'+
						'Synthèse vocale'+
						''+
					'
'+
					'
'+
						'<button data-ng-click="textToSpeech()" type="button" class="btn_simple light_blue generate_vocale">génerer</button>'+
					'
'+
					'
'+
						'<button type="button" class="btn_simple light_blue play_vocale" data-ng-click="playSong()"> </button>'+
						'<audio id="player" src="" preload="auto"></audio>'+
					'
'+
				'
'+
				'<button type="button" class="btn_simple light_blue pull-right" data-ng-click="textToSpeech()" data-ng-show="!showSynthese" title="{{\'synthese vocale\' | translate}}" >synthèse vocale</button>'+
			'
'+
			'
'+
				''+
					'

'+
				''+
				'
'+
					'<textarea ck-editor data-ng-model="textes.text" id="editorOcr" data-barre="OcrVersion" data-ng-change ="initCkEditorChange()"></textarea>'+
				'
'+
				''+
					'
'+
						'
'+
							'<button data-ng-click="removeZone(zone._id)"  class="label label-default">'+
							''+
							'</button>'+
						'
'+
					'
'+
					''+
				''+
			'
'+
		'
'+
		'
'+
				'
 div\''+
				'}" ui-nested-sortable-stop="updateDragDrop($event, $ui)">'+
				'
'+
					'
'+
						''+
							'<button class="toggle" data-ng-click="toggleMinimized(child)" data-ng-switch on="child.minimized">'+
								'0" data-ng-switch-when="true" class="tree_closed">'+
								''+
								'0" data-ng-switch-default class="tree_opened">'+
								''+
							'</button>'+
							'<button class="delete_layer" data-ng-click="remove(child)" title="{{\'Supprimer calque\' | translate}}" > </button>'+
							''+
							''+
							'<button data-ng-show="(child.source===undefined)" class="duplicate_layer" data-ng-click="duplicateBlock(child)" title="{{\'Dupliquer calque\' | translate}}" > </button>'+
						''+
						''+
							' '+
							' '+
							''+
								''+
							''+
						''+
					'
'+
					'

'+
				'
'+
			'
'+
		'
'+
	'
'+
	'
'+
	'
'+
	'
'+
    '
'+
      '
'+
        '
 '+
        '
'+
      '
'+
      '
{{loaderMessage}}
'+
    '
'+
'
'+
'
'+
''+
'
'+
	'
'+
		'
'+
			'
'+
				'<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>'+
				'
Enregistrer document
'+
				'
Modifier document
'+
			'
'+
			'
{{msgErrorModal}}
'+
			'
'+
				'<form id="show_document" name="show_document" class="" role="form" style="overflow:hidden;">'+
					'<fieldset class="padding_large">'+
						'
'+
						'Titre du document *'+
						'<input type="text" max-length="32" data-ng-disabled="editBlocks" class="" id="docTitre" placeholder="Entrez le titre du document" data-ng-model="docTitre" required>'+
						'
'+
					'</fieldset>'+
					'
'+
						'<button id="reset_save_inDropbox" type="button" class="reset_btn" data-dismiss="modal" title="{{\'Annuler\' | translate}}">Annuler</button>'+
						'<button id="save_inDropbox" data-ng-show="!editBlocks" type="button" class="btn_simple light_blue" data-ng-click="showlocks()"  title="{{\'Enregistrer sur ma Dropbox\' | translate}}">Enregistrer sur ma Dropbox</button>'+
						'<button id="saveEdited_inDropbox"  data-ng-show="editBlocks" type="button" class="btn_simple light_blue" data-ng-disabled="!show_document.$valid" data-ng-click="saveRestBlocks()" data-dismiss="modal" title="{{\'Modifier sur ma Dropbox\' | translate}}">Modifier sur ma Dropbox</button>'+
					'
'+
				'</form>'+
			'
'+
		'
'+
	'
'+
'
'+
''+
'
'+
	'
'+
		'
'+
			'
'+
				'
lien non valide
'+
			'
'+
			'
'+
				'
'+
				'Veuillez utiliser la Bookmarklet CnedAdapt avec l\'URL d\'un fichier PDF.'+
				'
'+
			'
'+
			'
'+
				'<button type="button" class="btn_simple light_blue much_padding" data-dismiss="modal">OK</button>'+
			'
'+
		'
'+
	'
'+
'
'+
'
'+
	'
'+
		'
'+
			'
'+
				'
information
'+
			'
'+
			'
'+
				'
'+
				'L\’application ne pourra pas traiter votre document de façon optimale en raison du poids du fichier et/ou de son contenu. Aussi, nous vous invitons à réessayer avec une autre version de votre document.'+
				'
'+
			'
'+
			'
'+
				'<button type="button" class="btn_simple light_blue much_padding" data-ng-click="modalError(\'myModalWorkSpaceBig\')">OK</button>'+
			'
'+
		'
'+
	'
'+
'
'+
'
'+
	'
'+
		'
'+
			'
'+
				'
information
'+
			'
'+
			'
'+
				'
'+
				'La taille de l\'ePub est supérieur à la taille limite supportée par l\'application.'+
				'
'+
			'
'+
			'
'+
				'<button type="button" class="btn_simple light_blue much_padding" data-ng-click="modalError(\'myModalWorkSpaceTooMany\')">OK</button>'+
			'
'+
		'
'+
	'
'+
'
'+
'
'+
	'
'+
		'
'+
			'
'+
				'
INFORMATION
'+
			'
'+
			'
'+
				'
'+
				'Pour structurer le document vous devez être authentifié. Veuillez-vous authentifier avant de poursuivre la structuration.'+
				'
'+
			'
'+
			'
'+
				'<button type="button" class="btn_simple light_blue much_padding" data-dismiss="modal">OK</button>'+
			'
'+
		'
'+
	'
'+
'
'+
'
'+
	'
'+
		'
'+
			'
'+
				'
INFORMATION
'+
			'
'+
			'
'+
				'
'+
				'Le document choisi a deja été structuré,voulez-vous ?'+
				'
'+
			'
'+
			'
'+
				'<button type="button" data-ng-click=\'resumeWorking()\' class="btn_simple light_blue much_padding" data-dismiss="modal">Continuer la structuration</button>'+
				'<button type="button" data-ng-click=\'createNew()\' class="btn_simple light_blue much_padding" data-dismiss="modal">Structurer à nouveau</button>'+
				'<button type="button" data-ng-click=\'openApercu()\' class="btn_simple light_blue much_padding" data-dismiss="modal">Afficher le document</button>'+
			'
'+
		'
'+
	'
'+
'
'+
''+
'
'+
    '
'+
		'
{{loaderMessage}}
'+
    '
'+
'
'+
'
';
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import Icon from '../../utils/icons';
import './List.scss';

export default class List extends Component {
  static propTypes = {
    name: PropTypes.string.isRequired,
    items: PropTypes.arrayOf(PropTypes.string),
    label: PropTypes.string,
    instructions: PropTypes.string,
  }

  static defaultProps = {
    items: [''],
    label: null,
    instructions: null,
  }

  constructor(props) {
    super(props);
    this.handleInputChange = this.handleInputChange.bind(this);
    this.addRow = this.addRow.bind(this);
    this.removeRow = this.removeRow.bind(this);
    this.state = { items: props.items };
  }

  handleInputChange(event, i) {
    const { value } = event.target;
    const { items } = this.state;
    this.setState({ items: [
      ...items.slice(0, i),
      value,
      ...items.slice(i + 1),
    ] });
  }

  addRow() {
    const { items } = this.state;
    this.setState({ items: [...items, ''] }, () => {
      this[`input--${items.length}`].focus();
    });
  }

  removeRow(i) {
    const { items } = this.state;
    this.setState({ items: [
      ...items.slice(0, i),
      ...items.slice(i + 1),
    ] });
  }

  render() {
    const { name, label, instructions } = this.props;
    const { items } = this.state;

    return (

        {label && {label}}
        {instructions &&
{instructions}
}

            {items.map((item, i) => ( // eslint-disable-next-line react/no-array-index-key

                  <input ref={(r) => { this[`input--${i}`] = r; }} className="list__table__cell__input" onChange={e => this.handleInputChange(e, i)} value={item} />

                  <button onClick={() => this.removeRow(i)} className="list__table__cell__btn" type="button">
                    <Icon width={9} height={9} icon="cross" />
                  </button>

))}

        <button className="list__btn" type="button" onClick={this.addRow}>Add Row <Icon icon="plus" width={9} height={9} /></button>
        {items.map((item, i) => <input key={item} type="text" hidden readOnly value={item} name={`${name}[${i}]`} />)}

    );
  }
}
<commit_msg>:scroll: Handle enter keypress<commit_after>import React, { Component, PropTypes } from 'react';
import Icon from '../../utils/icons';
import './List.scss';

export default class List extends Component {
  static propTypes = {
    name: PropTypes.string.isRequired,
    items: PropTypes.arrayOf(PropTypes.string),
    label: PropTypes.string,
    instructions: PropTypes.string,
  }

  static defaultProps = {
    items: [''],
    label: null,
    instructions: null,
  }

  constructor(props) {
    super(props);
    this.handleInputChange = this.handleInputChange.bind(this);
    this.handleKeyPress = this.handleKeyPress.bind(this);
    this.addRow = this.addRow.bind(this);
    this.removeRow = this.removeRow.bind(this);
    this.state = { items: props.items };
  }

  handleKeyPress(e) {
    if (e.which === 13) {
      e.preventDefault();
      if (e.target.value !== '') this.addRow();
    }
  }

  handleInputChange(event, i) {
    const { value } = event.target;
    const { items } = this.state;
    this.setState({ items: [
      ...items.slice(0, i),
      value,
      ...items.slice(i + 1),
    ] });
  }

  addRow() {
    const { items } = this.state;
    this.setState({ items: [...items, ''] }, () => {
      this[`input--${items.length}`].focus();
    });
  }

  removeRow(i) {
    const { items } = this.state;
    this.setState({ items: [
      ...items.slice(0, i),
      ...items.slice(i + 1),
    ] });
  }

  render() {
    const { name, label, instructions } = this.props;
    const { items } = this.state;

    return (

        {label && {label}}
        {instructions &&
{instructions}
}

            {items.map((item, i) => ( // eslint-disable-next-line react/no-array-index-key

                  <input
                    ref={(r) => { this[`input--${i}`] = r; }}
                    className="list__table__cell__input"
                    onKeyPress={this.handleKeyPress}
                    onChange={e => this.handleInputChange(e, i)}
                    value={item}
                  />

                  <button onClick={() => this.removeRow(i)} className="list__table__cell__btn" type="button">
                    <Icon width={9} height={9} icon="cross" />
                  </button>

            ))}

        <button className="list__btn" type="button" onClick={this.addRow}>Add Row <Icon icon="plus" width={9} height={9} /></button>
        {items.map((item, i) => <input key={item} type="text" hidden readOnly value={item} name={`${name}[${i}]`} />)}

    );
  }
}
<|endoftext|>


<commit_before>var safetyScores = [];
var finalScores = [];

// Confirms the score selected and displays button to pass the device
var selectScore = function(score) {
  "use strict";
  $('html, body').animate({ scrollTop: "0px" }, 600);
  document.getElementById('message-display').className = 'alert alert-info';
  document.getElementById('message-display').innerHTML = "You have submitted a score of " + score + ".
<button class='btn btn-default' onclick='newSubmit(" + score + ")'>Pass it on</button>
";
};

// Saves submitted score and clears div for the next user
var newSubmit = function(score) {
  "use strict";
  safetyScores.push(score);
  finalScores = safetyScores.sort();
  document.getElementById('message-display').className = 'alert alert-warning';
  document.getElementById('message-display').innerHTML = "Select an option";
};

// Returns true if retrospective should be cancelled.
var cancelRetro = function() {
  "use strict";
  var passFail = (finalScores[0] === 1 || finalScores[0] === 2) ? true : false;
  return passFail;
};

// Displays whether the retrospective should continue or not
var decideProceed = function() {
  "use strict";
  var displayScores = "The lowest score submitted was a " + finalScores[0] + ".";
  var proceedMessage = '';
  if(cancelRetro() && finalScores.length) {
    document.getElementById('message-display').className = 'alert alert-danger';
    proceedMessage = " As there is at least one person who does not feel safe talking, you should cancel the meeting!";
  } else if(finalScores.length) {
    document.getElementById('message-display').className = 'alert alert-success';
    proceedMessage = " Everyone seems comfortable with proceeding!";
  } else {
    displayScores = '';
    proceedMessage = "No votes have been submitted.";
    document.getElementById('message-display').className = 'alert alert-danger';
  }
  document.getElementById('message-display').innerHTML = displayScores + proceedMessage;
};

// Resets scores
var clearScores = function(){
  "use strict";
  safetyScores = [];
  finalScores = [];
};
<commit_msg>Changed what is display when scores are reset<commit_after>var safetyScores = [];
var finalScores = [];

// Confirms the score selected and displays button to pass the device
var selectScore = function(score) {
  "use strict";
  $('html, body').animate({ scrollTop: "0px" }, 600);
  document.getElementById('message-display').className = 'alert alert-info';
  document.getElementById('message-display').innerHTML = "You have submitted a score of " + score + ".
<button class='btn btn-default' onclick='newSubmit(" + score + ")'>Pass it on</button>
";
};

// Saves submitted score and clears div for the next user
var newSubmit = function(score) {
  "use strict";
  safetyScores.push(score);
  finalScores = safetyScores.sort();
  document.getElementById('message-display').className = 'alert alert-warning';
  document.getElementById('message-display').innerHTML = "Select an option";
};

// Returns true if retrospective should be cancelled.
var cancelRetro = function() {
  "use strict";
  var passFail = (finalScores[0] === 1 || finalScores[0] === 2) ? true : false;
  return passFail;
};

// Displays whether the retrospective should continue or not
var decideProceed = function() {
  "use strict";
  var displayScores = "The lowest score submitted was a " + finalScores[0] + ".";
  var proceedMessage = '';
  if(cancelRetro() && finalScores.length) {
    document.getElementById('message-display').className = 'alert alert-danger';
    proceedMessage = " As there is at least one person who does not feel safe talking, you should cancel the meeting!";
  } else if(finalScores.length) {
    document.getElementById('message-display').className = 'alert alert-success';
    proceedMessage = " Everyone seems comfortable with proceeding!";
  } else {
    displayScores = '';
    proceedMessage = "No votes have been submitted.";
    document.getElementById('message-display').className = 'alert alert-danger';
  }
  document.getElementById('message-display').innerHTML = displayScores + proceedMessage;
};

// Resets scores
var clearScores = function(){
  "use strict";
  safetyScores = [];
  finalScores = [];
  document.getElementById('message-display').className = 'alert alert-warning';
  document.getElementById('message-display').innerHTML = "Select an option";
};
<|endoftext|>


<commit_before>import React, {Component} from 'react';

import Widget from 'src/Widget';
import WidgetHeader from 'src/WidgetHeader';
import Paper from 'src/Paper';
import CascaderList from 'src/CascaderList';

import PropTypeDescTable from 'components/PropTypeDescTable';
import doc from 'examples/assets/propTypes/CascaderList.json';

import 'sass/containers/app/modules/layout/CascaderListExamples.scss';

class CascaderListExamples extends Component {

    constructor(props) {

        super(props);

        this.data = [{
            text: 'Asia',
            value: 'Asia',
            children: [{
                text: 'Korea',
                value: 'Korea'
            }, {
                text: 'Japan',
                value: 'Japan',
                children: [{
                    text: 'Tokyo',
                    value: 'Tokyo'
                }, {
                    text: 'Yokohama',
                    value: 'Yokohama'
                }, {
                    text: 'Osaka',
                    value: 'Osaka'
                }]
            }, {
                text: 'China',
                value: 'China',
                children: [{
                    text: 'Shanghai',
                    value: 'Shanghai'
                }, {
                    text: 'Beijing',
                    value: 'Beijing'
                }, {
                    text: 'Shenzhen',
                    value: 'Shenzhen'
                }]
            }]
        }, {
            text: 'Europe',
            value: 'Europe',
            children: [{
                text: 'Germany',
                value: 'Germany'
            }, {
                text: 'France',
                value: 'France'
            }]
        }, {
            text: 'North America',
            value: 'North America',
            children: [{
                text: 'Canada',
                value: 'Canada'
            }, {
                text: 'America',
                value: 'America'
            }]
        }, {
            text: 'South America',
            value: 'South America'
        }, {
            text: 'Africa',
            value: 'Africa'
        }, {
            text: 'Oceania',
            value: 'Oceania'
        }, {
            text: 'Antarctica',
            value: 'Antarctica'
        }];

    }

    nodeSelectHandler = (node, path) => {
        console.log('Node Selected::', node, path);
    };

    changeHandler = value => {
        console.log('Changed::', value);
    };

    render() {
        return (

CascaderList

                    CascaderList can fully display the hierarchy, and has interactive functions such as
                    expansion, withdrawal and selection.

Examples

                <Widget>

                    <WidgetHeader className="example-header"
                                  title="Basic"/>

CascaderListsimple example.

                                <Paper className="cascader-list-wrapper">
                                    <CascaderList data={this.data}
                                                  onNodeSelect={this.nodeSelectHandler}
                                                  onChange={this.changeHandler}/>
                                </Paper>

                </Widget>

                <Widget>

                    <WidgetHeader className="example-header"
                                  title="Self Define Node Renderer"/>

                                <Paper className="cascader-list-wrapper">
                                    <CascaderList data={this.data}
                                                  collapsedIconCls="far fa-plus-square"
                                                  expandedIconCls="far fa-minus-square"
                                                  renderer={node => {
                                                      return (

                                                              {node.text}
                                                              {
                                                                  node.children && node.children.length > 0 ?

                                                                          ({node.children.length})

                                                                      :
                                                                      null
                                                              }

                                                      );
                                                  }}
                                                  onNodeSelect={this.nodeSelectHandler}
                                                  onChange={this.changeHandler}/>
                                </Paper>

                </Widget>

                <Widget>

                    <WidgetHeader className="example-header"
                                  title="Multi Select"/>

                                <Paper className="cascader-list-wrapper">
                                    <CascaderList selectMode={CascaderList.SelectMode.MULTI_SELECT}
                                                  data={this.data}
                                                  isSelectRecursive={true}
                                                  checkboxUncheckedIconCls="far fa-circle"
                                                  checkboxCheckedIconCls="fas fa-check-circle"
                                                  checkboxIndeterminateIconCls="fas fa-minus-circle"
                                                  onNodeSelect={this.nodeSelectHandler}
                                                  onChange={this.changeHandler}/>
                                </Paper>

                </Widget>

Properties

                <PropTypeDescTable data={doc}/>

        );
    }
};

export default CascaderListExamples;<commit_msg>update CascaderListExamples<commit_after>import React, {Component} from 'react';

import Widget from 'src/Widget';
import WidgetHeader from 'src/WidgetHeader';
import Paper from 'src/Paper';
import CascaderList from 'src/CascaderList';

import PropTypeDescTable from 'components/PropTypeDescTable';
import doc from 'examples/assets/propTypes/CascaderList.json';

import 'sass/containers/app/modules/layout/CascaderListExamples.scss';

class CascaderListExamples extends Component {

    constructor(props) {

        super(props);

        this.data = [{
            text: 'Asia',
            value: 'Asia',
            children: [{
                text: 'Korea',
                value: 'Korea'
            }, {
                text: 'Japan',
                value: 'Japan',
                children: [{
                    text: 'Tokyo',
                    value: 'Tokyo'
                }, {
                    text: 'Yokohama',
                    value: 'Yokohama'
                }, {
                    text: 'Osaka',
                    value: 'Osaka'
                }]
            }, {
                text: 'China',
                value: 'China',
                children: [{
                    text: 'Shanghai',
                    value: 'Shanghai'
                }, {
                    text: 'Beijing',
                    value: 'Beijing'
                }, {
                    text: 'Shenzhen',
                    value: 'Shenzhen'
                }]
            }]
        }, {
            text: 'Europe',
            value: 'Europe',
            children: [{
                text: 'Germany',
                value: 'Germany'
            }, {
                text: 'France',
                value: 'France'
            }]
        }, {
            text: 'North America',
            value: 'North America',
            children: [{
                text: 'Canada',
                value: 'Canada'
            }, {
                text: 'America',
                value: 'America'
            }]
        }, {
            text: 'South America',
            value: 'South America'
        }, {
            text: 'Africa',
            value: 'Africa'
        }, {
            text: 'Oceania',
            value: 'Oceania'
        }, {
            text: 'Antarctica',
            value: 'Antarctica'
        }];

    }

    nodeClickHandler = (node) => {
        console.log('Node Clicked::', node);
    };

    nodeSelectHandler = (node, path) => {
        console.log('Node Selected::', node, path);
    };

    changeHandler = value => {
        console.log('Changed::', value);
    };

    render() {
        return (

CascaderList

                    CascaderList can fully display the hierarchy, and has interactive functions such as
                    expansion, withdrawal and selection.

Examples

                <Widget>

                    <WidgetHeader className="example-header"
                                  title="Basic"/>

CascaderListsimple example.

                                <Paper className="cascader-list-wrapper">
                                    <CascaderList data={this.data}
                                                  onNodeClick={this.nodeClickHandler}
                                                  onNodeSelect={this.nodeSelectHandler}
                                                  onChange={this.changeHandler}/>
                                </Paper>

                </Widget>

                <Widget>

                    <WidgetHeader className="example-header"
                                  title="Self Define Node Renderer"/>

                                <Paper className="cascader-list-wrapper">
                                    <CascaderList data={this.data}
                                                  collapsedIconCls="far fa-plus-square"
                                                  expandedIconCls="far fa-minus-square"
                                                  renderer={node => {
                                                      return (

                                                              {node.text}
                                                              {
                                                                  node.children && node.children.length > 0 ?

                                                                          ({node.children.length})

                                                                      :
                                                                      null
                                                              }

                                                      );
                                                  }}
                                                  onNodeSelect={this.nodeSelectHandler}
                                                  onChange={this.changeHandler}/>
                                </Paper>

                </Widget>

                <Widget>

                    <WidgetHeader className="example-header"
                                  title="Multi Select"/>

                                <Paper className="cascader-list-wrapper">
                                    <CascaderList selectMode={CascaderList.SelectMode.MULTI_SELECT}
                                                  data={this.data}
                                                  isSelectRecursive={true}
                                                  checkboxUncheckedIconCls="far fa-circle"
                                                  checkboxCheckedIconCls="fas fa-check-circle"
                                                  checkboxIndeterminateIconCls="fas fa-minus-circle"
                                                  onNodeSelect={this.nodeSelectHandler}
                                                  onChange={this.changeHandler}/>
                                </Paper>

                </Widget>

Properties

                <PropTypeDescTable data={doc}/>

        );
    }
};

export default CascaderListExamples;<|endoftext|>