<commit_before>define([
	"dojo/_base/declare", "dojo/_base/lang",
	"dojo/topic",
	"dijit/popup",
	"./GridContainer", "./BlastResultGrid", "./PerspectiveToolTip"
], function(declare, lang,
			Topic,
			popup,
			GridContainer, BlastResultGrid, PerspectiveToolTipDialog){

	return declare([GridContainer], {
		gridCtor: BlastResultGrid,
		containerType: "",
		enableFilterPanel: false,
		visible: true,
		store: null,

		buildQuery: function(){
		},
		_setQueryAttr: function(q){
		},

		_setStoreAttr: function(store){
			if(this.grid){
				this.grid.store = store;
			}
			this._set('store', store);
		},

		onSetState: function(attr, oldState, state){
			if(!state){
				return;
			}

			if(this.grid){
				this.grid.set('state', state);
			}
		},
		/*
		add GenomeBrowser as a special case and override ViewGenomeItem, ViewGenomeItems, ViewCDSFeaturesSeq,
		ViewFeatureItem, ViewFeatureItem to open in a new tab
		*/
		selectionActions: GridContainer.prototype.selectionActions.concat([
			[
				"GenomeBrowser",
				"fa icon-genome-browser fa-2x",
				{
					label: "Browser",
					multiple: false,
					validTypes: ["*"],
					validContainerTypes: ["sequence_data"],
					tooltip: "View in Genome Browser"
				},
				function(selection){

					var target = selection[0];
					var hit_from = target.detail.hsps[0].hit_from;
					var hit_to = target.detail.hsps[0].hit_to;

					var hash = lang.replace("#view_tab=browser&loc={0}:{1}..{2}&highlight={0}:{3}..{4}&tracks=refseqs,PATRICGenes,RefSeqGenes", [target.accession, Math.min(hit_from, hit_from - 200), Math.max(hit_to, hit_to + 200), hit_from, hit_to]);

					Topic.publish("/navigate", {
						href: "/view/Genome/" + target.genome_id + hash,
						target: "blank"
					});
				},
				false
			],
			[
				"ViewGenomeItem",
				"MultiButton fa icon-selection-Genome fa-2x",
				{
					label: "GENOME",
					validTypes: ["*"],
					multiple: false,
					tooltip: "Switch to Genome View. Press and Hold for more options.",
					ignoreDataType: true,
					validContainerTypes: ["sequence_data", "feature_data", "spgene_data", "sequence_data"],
					pressAndHold: function(selection, button, opts, evt){
						popup.open({
							popup: new PerspectiveToolTipDialog({perspectiveUrl: "/view/Genome/" + selection[0].genome_id}),
							around: button,
							orient: ["below"]
						});

					}
				},
				function(selection){
					var sel = selection[0];

					Topic.publish("/navigate", {href: "/view/Genome/" + sel.genome_id, target: "blank"});
				},
				false
			],
			[
				"ViewGenomeItems",
				"MultiButton fa icon-selection-GenomeList fa-2x",
				{
					label: "GENOMES",
					validTypes: ["*"],
					multiple: true,
					min: 2,
					max: 1000,
					tooltip: "Switch to Genome List View. Press and Hold for more options.",
					ignoreDataType: true,
					validContainerTypes: ["genome_data", "sequence_data", "feature_data", "spgene_data", "sequence_data"],
					pressAndHold: function(selection, button, opts, evt){
						var map = {};
						selection.forEach(function(sel){
							if(!map[sel.genome_id]){
								map[sel.genome_id] = true
							}
						})
						var genome_ids = Object.keys(map);
						popup.open({
							popup: new PerspectiveToolTipDialog({
								perspective: "GenomeList",
								perspectiveUrl: "/view/GenomeList/?in(genome_id,(" + genome_ids.join(",") + "))"
							}),
							around: button,
							orient: ["below"]
						});

					}
				},
				function(selection){
					var map = {};
					selection.forEach(function(sel){
						if(!map[sel.genome_id]){
							map[sel.genome_id] = true
						}
					});
					var genome_ids = Object.keys(map);
					Topic.publish("/navigate", {
						href: "/view/GenomeList/?in(genome_id,(" + genome_ids.join(",") + "))",
						target: "blank"
					});
				},
				false
			],
			[
				"ViewCDSFeaturesSeq",
				"MultiButton fa icon-selection-FeatureList fa-2x",
				{
					label: "FEATURES",
					validTypes: ["*"],
					multiple: false,
					tooltip: "Switch to Feature List View. Press and Hold for more options.",
					validContainerTypes: ["sequence_data"],
					pressAndHold: function(selection, button, opts, evt){
						// console.log("PressAndHold");
						// console.log("Selection: ", selection, selection[0])
						popup.open({
							popup: new PerspectiveToolTipDialog({
								perspective: "FeatureList",
								perspectiveUrl: "/view/FeatureList/?and(eq(annotation,PATRIC),eq(feature_type,CDS),eq(sequence_id," + selection[0].sequence_id + "))"
							}),
							around: button,
							orient: ["below"]
						});
					}
				},
				function(selection){

					var sel = selection[0];
					Topic.publish("/navigate", {
						href: "/view/FeatureList/?and(eq(annotation,PATRIC),eq(sequence_id," + sel.sequence_id + "),eq(feature_type,CDS))",
						target: "blank"
					});
				},
				false
			],
			[
				"ViewFeatureItem",
				"MultiButton fa icon-selection-Feature fa-2x",
				{
					label: "FEATURE",
					validTypes: ["*"],
					multiple: false,
					tooltip: "Switch to Feature View. Press and Hold for more options.",
					validContainerTypes: ["feature_data"],
					pressAndHold: function(selection, button, opts, evt){

						popup.open({
							popup: new PerspectiveToolTipDialog({
								perspective: "Feature",
								perspectiveUrl: "/view/Feature/" + selection[0].feature_id
							}),
							around: button,
							orient: ["below"]
						});
					}
				},
				function(selection){
					var sel = selection[0];
					Topic.publish("/navigate", {
						href: "/view/Feature/" + sel.feature_id + "#view_tab=overview",
						target: "blank"
					});
				},
				false
			],
			[
				"ViewFeatureItems",
				"MultiButton fa icon-selection-FeatureList fa-2x",
				{
					label: "FEATURES",
					validTypes: ["*"],
					multiple: true,
					min: 2,
					max: 5000,
					tooltip: "Switch to Feature List View. Press and Hold for more options.",
					validContainerTypes: ["feature_data"],
					pressAndHold: function(selection, button, opts, evt){

						popup.open({
							popup: new PerspectiveToolTipDialog({
								perspective: "FeatureList",
								perspectiveUrl: "/view/FeatureList/?in(feature_id,(" + selection.map(function(x){
									return x.feature_id;
								}).join(",") + "))"
							}),
							around: button,
							orient: ["below"]
						});

					}
				},
				function(selection){

					Topic.publish("/navigate", {
						href: "/view/FeatureList/?in(feature_id,(" + selection.map(function(x){
							return x.feature_id;
						}).join(",") + "))",
						target: "blank"
					});
				},
				false
			]
		])
	});
});<commit_msg>add download BLAST result. resolves PATRIC3/patric3_website#1309<commit_after>define([
	"dojo/_base/declare", "dojo/_base/lang",
	"dojo/on", "dojo/topic",
	"dijit/popup", "dijit/TooltipDialog",
	"./ContainerActionBar", "FileSaver",
	"./GridContainer", "./BlastResultGrid", "./PerspectiveToolTip"
], function(declare, lang,
			on, Topic,
			popup, TooltipDialog,
			ContainerActionBar, saveAs,
			GridContainer, BlastResultGrid, PerspectiveToolTipDialog){

	var dfc = '
Download Table As...

Text

CSV
';

	var downloadTT = new TooltipDialog({
		content: dfc, onMouseLeave: function(){
			popup.close(downloadTT);
		}
	});

	on(downloadTT.domNode, "div:click", lang.hitch(function(evt){
		var rel = evt.target.attributes.rel.value;
		var data = downloadTT.get("data");
		var headers = downloadTT.get("headers");
		var filename = "PATRIC_blast";
		// console.log(data, headers);

		var DELIMITER, ext;
		if(rel === 'text/csv'){
			DELIMITER = ',';
			ext = 'csv';
		}else{
			DELIMITER = '\t';
			ext = 'txt';
		}

		var content = data.map(function(d){
			return d.join(DELIMITER);
		});

		saveAs(new Blob([headers.join(DELIMITER) + '\n' + content.join('\n')], {type: rel}), filename + '.' + ext);

		popup.close(downloadTT);
	}));

	return declare([GridContainer], {
		gridCtor: BlastResultGrid,
		containerType: "",
		visible: true,
		store: null,

		buildQuery: function(){
		},
		_setQueryAttr: function(q){
		},

		_setStoreAttr: function(store){
			if(this.grid){
				this.grid.store = store;
			}
			this._set('store', store);
		},

		onSetState: function(attr, oldState, state){
			if(!state){
				return;
			}

			if(this.grid){
				this.grid.set('state', state);
			}
		},
		createFilterPanel: function(opts){
			this.containerActionBar = this.filterPanel = new ContainerActionBar({
				region: "top",
				layoutPriority: 7,
				splitter: true,
				"className": "BrowserHeader",
				dataModel: this.dataModel,
				facetFields: this.facetFields,
				state: lang.mixin({}, this.state),
				enableAnchorButton: false,
				currentContainerWidget: this
			});
		},
		containerActions: GridContainer.prototype.containerActions.concat([
			[
				"DownloadTable",
				"fa icon-download fa-2x",
				{
					label: "DOWNLOAD",
					multiple: false,
					validTypes: ["*"],
					tooltip: "Download Table",
					tooltipDialog: downloadTT
				},
				function(){

					downloadTT.set("content", dfc);

					var data = this.grid.store.query("", {});
					var headers, content;

					switch(this.type){
						case "genome_feature":
							headers = ["Genome", "Genome ID", "PATRIC ID", "RefSeq Locus Tag", "Gene", "Product", "Length (NT)", "Length (AA)", "ALN Length", "Identity", "Query cover", "Subject cover", "Score", "E value"];
							content = data.map(function(row){
								return [row.genome_name, row.genome_id, row.patric_id, row.refseq_locus_tag, row.gene, JSON.stringify(row.function), row.na_length, row.aa_length, row.length, row.pident, row.query_coverage, row.subject_coverage, row.bitscore, row.evalue];
							});
							break;
						case "genome_sequence":
							headers = ["Genome", "Genome ID", "Accession", "Description", "Product", "Identity", "Query cover", "Subject cover", "Score", "E value"];
							content = data.map(function(row){
								return [row.genome_name, row.genome_id, row.accession, JSON.stringify(row.description), JSON.stringify(row.function), row.pident, row.query_coverage, row.subject_coverage, row.length, row.bitscore, row.evalue];
							});
							break;
						case "specialty_genes":
							headers = ["Database", "Source ID", "Description", "Organism", "Identity", "Query cover", "Subject cover", "Length", "Score", "E value"];
							content = data.map(function(row){
								return [row.database, row.source_id, JSON.stringify(row.function), row.organism, row.pident, row.query_coverage, row.subject_coverage, row.length, row.bitscore, row.evalue];
							});
							break;
						default:
							headers = [];
							content = [];
							break;
					}

					downloadTT.set("data", content);
					downloadTT.set("headers", headers);

					popup.open({
						popup: this.containerActionBar._actions.DownloadTable.options.tooltipDialog,
						around: this.containerActionBar._actions.DownloadTable.button,
						orient: ["below"]
					});
				},
				true
			]
		]),
		/*
		add GenomeBrowser as a special case and override ViewGenomeItem, ViewGenomeItems, ViewCDSFeaturesSeq,
		ViewFeatureItem, ViewFeatureItem to open in a new tab
		*/
		selectionActions: GridContainer.prototype.selectionActions.concat([
			[
				"GenomeBrowser",
				"fa icon-genome-browser fa-2x",
				{
					label: "Browser",
					multiple: false,
					validTypes: ["*"],
					validContainerTypes: ["sequence_data"],
					tooltip: "View in Genome Browser"
				},
				function(selection){

					var target = selection[0];
					var hit_from = target.detail.hsps[0].hit_from;
					var hit_to = target.detail.hsps[0].hit_to;

					var hash = lang.replace("#view_tab=browser&loc={0}:{1}..{2}&highlight={0}:{3}..{4}&tracks=refseqs,PATRICGenes,RefSeqGenes", [target.accession, Math.min(hit_from, hit_from - 200), Math.max(hit_to, hit_to + 200), hit_from, hit_to]);

					Topic.publish("/navigate", {
						href: "/view/Genome/" + target.genome_id + hash,
						target: "blank"
					});
				},
				false
			],
			[
				"ViewGenomeItem",
				"MultiButton fa icon-selection-Genome fa-2x",
				{
					label: "GENOME",
					validTypes: ["*"],
					multiple: false,
					tooltip: "Switch to Genome View. Press and Hold for more options.",
					ignoreDataType: true,
					validContainerTypes: ["sequence_data", "feature_data", "spgene_data", "sequence_data"],
					pressAndHold: function(selection, button, opts, evt){
						popup.open({
							popup: new PerspectiveToolTipDialog({perspectiveUrl: "/view/Genome/" + selection[0].genome_id}),
							around: button,
							orient: ["below"]
						});

					}
				},
				function(selection){
					var sel = selection[0];

					Topic.publish("/navigate", {href: "/view/Genome/" + sel.genome_id, target: "blank"});
				},
				false
			],
			[
				"ViewGenomeItems",
				"MultiButton fa icon-selection-GenomeList fa-2x",
				{
					label: "GENOMES",
					validTypes: ["*"],
					multiple: true,
					min: 2,
					max: 1000,
					tooltip: "Switch to Genome List View. Press and Hold for more options.",
					ignoreDataType: true,
					validContainerTypes: ["genome_data", "sequence_data", "feature_data", "spgene_data", "sequence_data"],
					pressAndHold: function(selection, button, opts, evt){
						var map = {};
						selection.forEach(function(sel){
							if(!map[sel.genome_id]){
								map[sel.genome_id] = true
							}
						})
						var genome_ids = Object.keys(map);
						popup.open({
							popup: new PerspectiveToolTipDialog({
								perspective: "GenomeList",
								perspectiveUrl: "/view/GenomeList/?in(genome_id,(" + genome_ids.join(",") + "))"
							}),
							around: button,
							orient: ["below"]
						});

					}
				},
				function(selection){
					var map = {};
					selection.forEach(function(sel){
						if(!map[sel.genome_id]){
							map[sel.genome_id] = true
						}
					});
					var genome_ids = Object.keys(map);
					Topic.publish("/navigate", {
						href: "/view/GenomeList/?in(genome_id,(" + genome_ids.join(",") + "))",
						target: "blank"
					});
				},
				false
			],
			[
				"ViewCDSFeaturesSeq",
				"MultiButton fa icon-selection-FeatureList fa-2x",
				{
					label: "FEATURES",
					validTypes: ["*"],
					multiple: false,
					tooltip: "Switch to Feature List View. Press and Hold for more options.",
					validContainerTypes: ["sequence_data"],
					pressAndHold: function(selection, button, opts, evt){
						// console.log("PressAndHold");
						// console.log("Selection: ", selection, selection[0])
						popup.open({
							popup: new PerspectiveToolTipDialog({
								perspective: "FeatureList",
								perspectiveUrl: "/view/FeatureList/?and(eq(annotation,PATRIC),eq(feature_type,CDS),eq(sequence_id," + selection[0].sequence_id + "))"
							}),
							around: button,
							orient: ["below"]
						});
					}
				},
				function(selection){

					var sel = selection[0];
					Topic.publish("/navigate", {
						href: "/view/FeatureList/?and(eq(annotation,PATRIC),eq(sequence_id," + sel.sequence_id + "),eq(feature_type,CDS))",
						target: "blank"
					});
				},
				false
			],
			[
				"ViewFeatureItem",
				"MultiButton fa icon-selection-Feature fa-2x",
				{
					label: "FEATURE",
					validTypes: ["*"],
					multiple: false,
					tooltip: "Switch to Feature View. Press and Hold for more options.",
					validContainerTypes: ["feature_data"],
					pressAndHold: function(selection, button, opts, evt){

						popup.open({
							popup: new PerspectiveToolTipDialog({
								perspective: "Feature",
								perspectiveUrl: "/view/Feature/" + selection[0].feature_id
							}),
							around: button,
							orient: ["below"]
						});
					}
				},
				function(selection){
					var sel = selection[0];
					Topic.publish("/navigate", {
						href: "/view/Feature/" + sel.feature_id + "#view_tab=overview",
						target: "blank"
					});
				},
				false
			],
			[
				"ViewFeatureItems",
				"MultiButton fa icon-selection-FeatureList fa-2x",
				{
					label: "FEATURES",
					validTypes: ["*"],
					multiple: true,
					min: 2,
					max: 5000,
					tooltip: "Switch to Feature List View. Press and Hold for more options.",
					validContainerTypes: ["feature_data"],
					pressAndHold: function(selection, button, opts, evt){

						popup.open({
							popup: new PerspectiveToolTipDialog({
								perspective: "FeatureList",
								perspectiveUrl: "/view/FeatureList/?in(feature_id,(" + selection.map(function(x){
									return x.feature_id;
								}).join(",") + "))"
							}),
							around: button,
							orient: ["below"]
						});

					}
				},
				function(selection){

					Topic.publish("/navigate", {
						href: "/view/FeatureList/?in(feature_id,(" + selection.map(function(x){
							return x.feature_id;
						}).join(",") + "))",
						target: "blank"
					});
				},
				false
			]
		])
	});
});<|endoftext|>


<commit_before>/*eslint react/display-name:0 react/prop-types:0 */
import React from "react";
import { Input, FormControls, Nav, NavItem, DropdownButton, MenuItem, Panel, PanelGroup, Accordion, Tabs, Tab } from "react-bootstrap";

import { asArray } from "../..";
import FloatInput from "./FloatInput";
import IntInput from "./IntInput";
import UintInput from "./UintInput";
import TextInput from "./TextInput";
import Slider from "./Slider";

let translate = text => text;

export function setTranslationFunc(func) {
	translate = func;
}

function noop() {
}

export default function mapDefault(element, getValue, setValue, extraProps = {}, parents = [], refCallback = noop, childKey = "parent") {
	if (!element || !element.type || !element.type.dataModelType) {
		return element;
	}
	const factoryFunc = mapping[element.type.dataModelType];
	if (!factoryFunc) {
		return element;
	}
	return factoryFunc(element, getValue, setValue, extraProps, parents, refCallback, childKey);
}

function getLabel(props) {
	return props.label ? translate(props.label) + ":" : null;
}

const columnsParentStyle = {
	display: "flex",
	flexWrap: "wrap",
	justifyContent: "space-between",
};

const mapping = {
	DataModel: (element, getValue, setValue, extraProps, parents, refCallback) => {
		const props = element.props;
		const childParents = [element, ...parents];
		const mappedChildren = asArray(props.children).map((child, childKey) => {
			return mapDefault(child, getValue, setValue, extraProps, childParents, refCallback, childKey);
		});

		let style = null;
		if (Number.isInteger(props.style.columns) && props.style.columns > 1) {
			style = columnsParentStyle;
			const wrapStyle = {width: `${100 / props.style.columns - 2 * (props.style.columns-1)}%`};
			for (let i = 0; i < mappedChildren.length; i++) {
				mappedChildren[i] =
{mappedChildren[i]}
;
			}
		}

		if (props.style.accordion) {
			return <Accordion className="dmodel dmodel-bootstrap" style={style}>{mappedChildren}</Accordion>;
		} else if (props.style.panelGroup) {
			return <PanelGroup className="dmodel dmodel-bootstrap" style={style}>{mappedChildren}</PanelGroup>;
		} else if (props.style.tabs) {
			return <Tabs className="dmodel dmodel-bootstrap" style={style}>{mappedChildren}</Tabs>;
		}
		return
{mappedChildren}
;
	},

	Group: (element, getValue, setValue, extraProps, parents, refCallback, key) => {
		const props = element.props;
		const childParents = [element, ...parents];
		const mappedChildren = asArray(props.children).map((child, childKey) => {
			return mapDefault(child, getValue, setValue, extraProps, childParents, refCallback, childKey);
		});

		let style = null;
		if (Number.isInteger(props.style.columns) && props.style.columns > 1) {
			style = columnsParentStyle;
			const wrapStyle = {width: `${100 / props.style.columns - 2 * (props.style.columns-1)}%`};
			for (let i = 0; i < mappedChildren.length; i++) {
				mappedChildren[i] =
{mappedChildren[i]}
;
			}
		}

		const parentStyle = parents[0].props.style;
		if (parentStyle.accordion || parentStyle.panelGroup) {
			return (
				<Panel
					key={key}
					eventKey={key}
					header={getLabel(props)}
					className="dmodel-group"
					style={style}
				>
					{mappedChildren}
				</Panel>
			);
		} else if (parentStyle.tabs) {
			return (
				<Tab
					key={key}
					eventKey={key}
					title={translate(props.label)}
					className="dmodel-group"
				>

{mappedChildren}

				</Tab>
			);
		}
		return
{mappedChildren}
;
	},

	Bool: (element, getValue, setValue, extraProps, parents, refCallback, key) => {
		const props = element.props;
		if (props.children) {
			return React.cloneElement(props.children, {...props, ...extraProps, ...{getValue, setValue, parents, refCallback, key}});
		}
		return (
			<Input
				{...{...props, ...extraProps}}
				ref={refCallback}
				key={key}
				label={getLabel(props)}
				disabled={setValue === null}
				checked={getValue(props.name)}
				onChange={() => setValue(props.name, !getValue(props.name))}
			/>
		);
	},

	Float: (element, getValue, setValue, extraProps, parents, refCallback, key) => {
		const props = element.props;
		if (props.children) {
			return React.cloneElement(props.children, {...props, ...extraProps, ...{getValue, setValue, parents, refCallback, key}});
		}
		if (props.style.slider) {
			return (
				<Slider
					{...{...props.style.slider, ...props, ...extraProps}}
					ref={refCallback}
					key={key}
					label={getLabel(props)}
					fixed={setValue === null}
					value={getValue(props.name)}
					onChange={value => setValue(props.name, value)}
				/>
			);
		}
		return (
			<FloatInput
				{...{...props, ...extraProps}}
				ref={refCallback}
				key={key}
				label={getLabel(props)}
				disabled={setValue === null}
				placeholder={props.style.placeholder}
				addonAfter={props.unit}
				value={getValue(props.name)}
				onChange={value => setValue(props.name, value)}
			/>
		);
	},

	Int: (element, getValue, setValue, extraProps, parents, refCallback, key) => {
		const props = element.props;
		if (props.children) {
			return React.cloneElement(props.children, {...props, ...extraProps, ...{getValue, setValue, parents, refCallback, key}});
		}
		if (props.style.slider) {
			return (
				<Slider
					{...{...props.style.slider, ...props, ...extraProps}}
					ref={refCallback}
					key={key}
					label={getLabel(props)}
					fixed={setValue === null}
					integer
					value={getValue(props.name)}
					onChange={value => setValue(props.name, value)}
				/>
			);
		}
		return (
			<IntInput
				{...{...props, ...extraProps}}
				ref={refCallback}
				key={key}
				label={getLabel(props)}
				disabled={setValue === null}
				placeholder={props.style.placeholder}
				addonAfter={props.unit}
				value={getValue(props.name)}
				onChange={value => setValue(props.name, value)}
			/>
		);
	},

	Uint: (element, getValue, setValue, extraProps, parents, refCallback, key) => {
		const props = element.props;
		if (props.children) {
			return React.cloneElement(props.children, {...props, ...extraProps, ...{getValue, setValue, parents, refCallback, key}});
		}
		if (props.style.slider) {
			return (
				<Slider
					{...{...props.style.slider, ...props, ...extraProps}}
					ref={refCallback}
					key={key}
					label={getLabel(props)}
					fixed={setValue === null}
					integer
					value={getValue(props.name)}
					onChange={value => setValue(props.name, value)}
				/>
			);
		}
		return (
			<UintInput
				{...{...props, ...extraProps}}
				ref={refCallback}
				key={key}
				label={getLabel(props)}
				disabled={setValue === null}
				placeholder={props.style.placeholder}
				addonAfter={props.unit}
				value={getValue(props.name)}
				onChange={value => setValue(props.name, value)}
			/>
		);
	},

	Text: (element, getValue, setValue, extraProps, parents, refCallback, key) => {
		const props = element.props;
		if (props.children) {
			return React.cloneElement(props.children, {...props, ...extraProps, ...{getValue, setValue, parents, refCallback, key}});
		}
		return (
			<TextInput
				{...{...props, ...extraProps}}
				ref={refCallback}
				key={key}
				label={getLabel(props)}
				disabled={setValue === null}
				placeholder={props.style.placeholder}
				value={getValue(props.name)}
				onChange={value => setValue(props.name, value)}
			/>
		);
	},

	StaticText: (element, getValue, setValue, extraProps, parents, refCallback, key) => {
		const props = element.props;
		if (props.children) {
			return React.cloneElement(props.children, {...props, ...extraProps, ...{getValue, setValue, parents, refCallback, key}});
		}
		return <FormControls.Static key={key} label={getLabel(props)} value={getValue(props.name)} />;
	},

	Enum: (element, getValue, setValue, extraProps, parents, refCallback, key) => {
		const props = element.props;
		if (props.children) {
			return React.cloneElement(props.children, {...props, ...extraProps, ...{getValue, setValue, parents, refCallback, key}});
		}
		const label = getLabel(props);
		const value = getValue(props.name);
		return (

				{label ? {label} : null}
				{props.style.dropdown ?

						<DropdownButton {...extraProps} id={label} disabled={setValue === null} title={props.optionsPrefix + translate(value) + props.optionsPostfix}>
							{
								props.options.map(option => {
									return (
										<MenuItem key={option} eventKey={option} active={option === value} onSelect={() => setValue(props.name, option)}>
											{props.optionsPrefix}{translate(option)}{props.optionsPostfix}
										</MenuItem>
									);
								})
							}
						</DropdownButton>

				:
					 setValue(props.name, key)}>
						{
							props.options.map(option => {
								return <NavItem key={option} eventKey={option}>{props.optionsPrefix}{translate(option)}{props.optionsPostfix}</NavItem>;
							})
						}

				}

		);
	},
};
<commit_msg>mapDefault Bool<commit_after>/*eslint react/display-name:0 react/prop-types:0 */
import React from "react";
import { Input, FormControls, Nav, NavItem, DropdownButton, MenuItem, Panel, PanelGroup, Accordion, Tabs, Tab } from "react-bootstrap";

import { asArray } from "../..";
import FloatInput from "./FloatInput";
import IntInput from "./IntInput";
import UintInput from "./UintInput";
import TextInput from "./TextInput";
import Slider from "./Slider";

let translate = text => text;

export function setTranslationFunc(func) {
	translate = func;
}

function noop() {
}

export default function mapDefault(element, getValue, setValue, extraProps = {}, parents = [], refCallback = noop, childKey = "parent") {
	if (!element || !element.type || !element.type.dataModelType) {
		return element;
	}
	const factoryFunc = mapping[element.type.dataModelType];
	if (!factoryFunc) {
		return element;
	}
	return factoryFunc(element, getValue, setValue, extraProps, parents, refCallback, childKey);
}

function getLabel(props) {
	return props.label ? translate(props.label) + ":" : null;
}

const columnsParentStyle = {
	display: "flex",
	flexWrap: "wrap",
	justifyContent: "space-between",
};

const mapping = {
	DataModel: (element, getValue, setValue, extraProps, parents, refCallback) => {
		const props = element.props;
		const childParents = [element, ...parents];
		const mappedChildren = asArray(props.children).map((child, childKey) => {
			return mapDefault(child, getValue, setValue, extraProps, childParents, refCallback, childKey);
		});

		let style = null;
		if (Number.isInteger(props.style.columns) && props.style.columns > 1) {
			style = columnsParentStyle;
			const wrapStyle = {width: `${100 / props.style.columns - 2 * (props.style.columns-1)}%`};
			for (let i = 0; i < mappedChildren.length; i++) {
				mappedChildren[i] =
{mappedChildren[i]}
;
			}
		}

		if (props.style.accordion) {
			return <Accordion className="dmodel dmodel-bootstrap" style={style}>{mappedChildren}</Accordion>;
		} else if (props.style.panelGroup) {
			return <PanelGroup className="dmodel dmodel-bootstrap" style={style}>{mappedChildren}</PanelGroup>;
		} else if (props.style.tabs) {
			return <Tabs className="dmodel dmodel-bootstrap" style={style}>{mappedChildren}</Tabs>;
		}
		return
{mappedChildren}
;
	},

	Group: (element, getValue, setValue, extraProps, parents, refCallback, key) => {
		const props = element.props;
		const childParents = [element, ...parents];
		const mappedChildren = asArray(props.children).map((child, childKey) => {
			return mapDefault(child, getValue, setValue, extraProps, childParents, refCallback, childKey);
		});

		let style = null;
		if (Number.isInteger(props.style.columns) && props.style.columns > 1) {
			style = columnsParentStyle;
			const wrapStyle = {width: `${100 / props.style.columns - 2 * (props.style.columns-1)}%`};
			for (let i = 0; i < mappedChildren.length; i++) {
				mappedChildren[i] =
{mappedChildren[i]}
;
			}
		}

		const parentStyle = parents[0].props.style;
		if (parentStyle.accordion || parentStyle.panelGroup) {
			return (
				<Panel
					key={key}
					eventKey={key}
					header={getLabel(props)}
					className="dmodel-group"
					style={style}
				>
					{mappedChildren}
				</Panel>
			);
		} else if (parentStyle.tabs) {
			return (
				<Tab
					key={key}
					eventKey={key}
					title={translate(props.label)}
					className="dmodel-group"
				>

{mappedChildren}

				</Tab>
			);
		}
		return
{mappedChildren}
;
	},

	Bool: (element, getValue, setValue, extraProps, parents, refCallback, key) => {
		const props = element.props;
		if (props.children) {
			return React.cloneElement(props.children, {...props, ...extraProps, ...{getValue, setValue, parents, refCallback, key}});
		}
		return (
			<Input
				{...{...props, ...extraProps}}
 				type="checkbox"
 				ref={refCallback}
				key={key}
				label={getLabel(props)}
				disabled={setValue === null}
				checked={getValue(props.name)}
				onChange={() => setValue(props.name, !getValue(props.name))}
			/>
		);
	},

	Float: (element, getValue, setValue, extraProps, parents, refCallback, key) => {
		const props = element.props;
		if (props.children) {
			return React.cloneElement(props.children, {...props, ...extraProps, ...{getValue, setValue, parents, refCallback, key}});
		}
		if (props.style.slider) {
			return (
				<Slider
					{...{...props.style.slider, ...props, ...extraProps}}
					ref={refCallback}
					key={key}
					label={getLabel(props)}
					fixed={setValue === null}
					value={getValue(props.name)}
					onChange={value => setValue(props.name, value)}
				/>
			);
		}
		return (
			<FloatInput
				{...{...props, ...extraProps}}
				ref={refCallback}
				key={key}
				label={getLabel(props)}
				disabled={setValue === null}
				placeholder={props.style.placeholder}
				addonAfter={props.unit}
				value={getValue(props.name)}
				onChange={value => setValue(props.name, value)}
			/>
		);
	},

	Int: (element, getValue, setValue, extraProps, parents, refCallback, key) => {
		const props = element.props;
		if (props.children) {
			return React.cloneElement(props.children, {...props, ...extraProps, ...{getValue, setValue, parents, refCallback, key}});
		}
		if (props.style.slider) {
			return (
				<Slider
					{...{...props.style.slider, ...props, ...extraProps}}
					ref={refCallback}
					key={key}
					label={getLabel(props)}
					fixed={setValue === null}
					integer
					value={getValue(props.name)}
					onChange={value => setValue(props.name, value)}
				/>
			);
		}
		return (
			<IntInput
				{...{...props, ...extraProps}}
				ref={refCallback}
				key={key}
				label={getLabel(props)}
				disabled={setValue === null}
				placeholder={props.style.placeholder}
				addonAfter={props.unit}
				value={getValue(props.name)}
				onChange={value => setValue(props.name, value)}
			/>
		);
	},

	Uint: (element, getValue, setValue, extraProps, parents, refCallback, key) => {
		const props = element.props;
		if (props.children) {
			return React.cloneElement(props.children, {...props, ...extraProps, ...{getValue, setValue, parents, refCallback, key}});
		}
		if (props.style.slider) {
			return (
				<Slider
					{...{...props.style.slider, ...props, ...extraProps}}
					ref={refCallback}
					key={key}
					label={getLabel(props)}
					fixed={setValue === null}
					integer
					value={getValue(props.name)}
					onChange={value => setValue(props.name, value)}
				/>
			);
		}
		return (
			<UintInput
				{...{...props, ...extraProps}}
				ref={refCallback}
				key={key}
				label={getLabel(props)}
				disabled={setValue === null}
				placeholder={props.style.placeholder}
				addonAfter={props.unit}
				value={getValue(props.name)}
				onChange={value => setValue(props.name, value)}
			/>
		);
	},

	Text: (element, getValue, setValue, extraProps, parents, refCallback, key) => {
		const props = element.props;
		if (props.children) {
			return React.cloneElement(props.children, {...props, ...extraProps, ...{getValue, setValue, parents, refCallback, key}});
		}
		return (
			<TextInput
				{...{...props, ...extraProps}}
				ref={refCallback}
				key={key}
				label={getLabel(props)}
				disabled={setValue === null}
				placeholder={props.style.placeholder}
				value={getValue(props.name)}
				onChange={value => setValue(props.name, value)}
			/>
		);
	},

	StaticText: (element, getValue, setValue, extraProps, parents, refCallback, key) => {
		const props = element.props;
		if (props.children) {
			return React.cloneElement(props.children, {...props, ...extraProps, ...{getValue, setValue, parents, refCallback, key}});
		}
		return <FormControls.Static key={key} label={getLabel(props)} value={getValue(props.name)} />;
	},

	Enum: (element, getValue, setValue, extraProps, parents, refCallback, key) => {
		const props = element.props;
		if (props.children) {
			return React.cloneElement(props.children, {...props, ...extraProps, ...{getValue, setValue, parents, refCallback, key}});
		}
		const label = getLabel(props);
		const value = getValue(props.name);
		return (

				{label ? {label} : null}
				{props.style.dropdown ?

						<DropdownButton {...extraProps} id={label} disabled={setValue === null} title={props.optionsPrefix + translate(value) + props.optionsPostfix}>
							{
								props.options.map(option => {
									return (
										<MenuItem key={option} eventKey={option} active={option === value} onSelect={() => setValue(props.name, option)}>
											{props.optionsPrefix}{translate(option)}{props.optionsPostfix}
										</MenuItem>
									);
								})
							}
						</DropdownButton>

				:
					 setValue(props.name, key)}>
						{
							props.options.map(option => {
								return <NavItem key={option} eventKey={option}>{props.optionsPrefix}{translate(option)}{props.optionsPostfix}</NavItem>;
							})
						}

				}

		);
	},
};
<|endoftext|>


<commit_before>var Deferred  = require('./deferred');

var IterativeDeferred = module.exports = Deferred.extend({
  initialize: function(to_map) {
    this.supr();
    this.to_map = to_map;
    this._started = false;

    this._reduceBuffer = [];
    this._finallyShouldBeLaunched = false;
  },

  /**
   * Functional programming: easy setter for the map function.
   * @see #mapFn
   *
   * @param  {Function} mapFn [description]
   */
  map: function(mapFn) {
    this.mapFn = mapFn;

    //directly start if anything to map
    if(this.to_map)
      this.start();
    return this;
  },

  /**
   * The map function, to be defined: should map a key to a deferred object.
   *
   * Should return a deferred object that will be registered: the iterative
   * process won't stop until all registered deferred are completed or a
   * manual intervention.
   *
   * If the key has already been mapped, the mapping will be ignored. To test
   * equality between key, @see #equalTestFn.
   *
   * @param  {object} key - key to map to a Deferred
   * @return {undefined | Deferred} mapped Deferred
   */
  //mapFn: function(key) {
  //},

  /**
   * Set itinial reduce value.
   *
   * @param  {*} init_value
   */
  init: function(init_value) {
    this._currentReduceResult = init_value;
    return this;
  },

  /**
   * Functional programming: easy setter for the reduce function.
   * @see #reduceFn
   *
   * @param  {function} reduceFn [description]
   * @param {*}       init_value - initial reduce value
   */
  reduce: function(reduceFn, init_value) {
    this.reduceFn = reduceFn;

    if(init_value)
      this._currentReduceResult = init_value;

    //if waiting reduces in buffer, empty it :
      while(this._reduceBuffer.length >0) {
        var args = this._reduceBuffer.shift();
        this._launchReduce.apply(this, args);
      }

    return this;
  },

  /**
   * The reduce function, to be defined: should combine resolved result from
   * mapped deferred and the previous reduce result. It can feed the mapping
   * process with keys to map, by calling the map argument function.
   *
   * If the deferred resolved multiple arguments, the additional arguments are
   * present.
   *
   * At any moment the iterative process can be stopped manually just by
   * completing the working process as deferred: simply call `this.resolve` or
   * `this.reject`.
   *
   * The end arguments key, resolved and rejected are if needed to decide the
   * reduce process.
   *
   * @param  {*}     previous - previously returned by the reduce function
   * @param  {*}       result - the result resolved by the mapped Deferred
   * @param  {*} [additional] - if the resolve callback was called with multiple
   *                            arguments, additional arguments are present
   * @param  {function}   map - use this function to feed the mapping process with
   *                            new keys
   * @param  {object}     key - original mapping key whose deferred produced the
   *                            given resolved result
   * @param  {array} resolved - array of keys which mapped Deferred have been resolved
   * @param  {array} rejected - array of keys which mapped Deferred have been rejected
   *
   * @return {*} reduce result
   */
  //reduceFn: function(result /* ... */, previous, map, key, resolved, rejected) {
  //},

  /**
   * Functionnal programming: easy setter for the finally function.
   * @see #finallyFn
   *
   * @param  {function} finallyFn [description]
   */
  finally: function(finallyFn) {
    this.finallyFn = finallyFn;

    //it's over : launch immediatly finally
    if(this._finallyShouldBeLaunched)
      this._launchFinally();

    return this;
  },

  /**
   * The final function, will be called when the iterative process ends, ie. there
   * is no more uncompleted mapped Deferred and all reduce processes are finished.
   *
   * The final function should complete the process by calling `this.resolve` or
   * `this.reject`. If this is not done, the process will be automatically resolved.
   *
   * @param  {*} reduce_result - what finally came out the reduce process
   * @param  {array} resolved  - array of keys which mapped Deferred have been resolved
   * @param  {array} rejected  - array of keys which mapped Deferred have been rejected
   */
  //finallyFn: function(reduce_result, resolved, rejected) {
  //},

  /**
   * Start the iterative map/reduce given the this array of
   * map consumable.
   *
   * @param  {Array<key>} array [description]
   */
  start: function(array) {
    if(this._started)
      return this;
    this._started = true;

    if(array)
      this.to_map = array;

    var length = this.to_map.length || this.to_map.size();
    if(length !== 0) {
      //initialization
      this._onFly = 0;
      this._alreadyMaped = [];
      this._resolved = [];
      this._rejected = [];

      //go !
      for (var i = 0, l = to_map.length; i < l; i++) {
        this._launchMap(to_map[i]);
      }

    } else {
      this.finallyFn(this._currentReduceResult);
    }
    return this;
  },

  /**
   * Test the equality of 2 keys.
   *
   * Used to determine if a key has already been mapped. Use an #equals method if
   * present. Else use the result of `===`.
   *
   * @param  {*} key1
   * @param  {*} key2
   * @return {boolean} result
   */
  equalTestFn: function(key1, key2) {
    return (typeof key1.equals !== 'undefined') ?
            key1.equals(key2)
          : key1 === key2;
  },

  _launchMap: function(key) {

    //if the key has alreday been mapped
    var already = this._alreadyMaped.some(function(key2) {
      return this.equalTestFn(key, key2);
    }, this);
    if(already)
      return;
    this._alreadyMaped.push(key);

    //call the map function and get the deferred
    var def = this.mapFn(key);
    if(!def || def === null)
      return;

    //it's not a deferred, it's  just an object to reduce directly :
    if(typeof def.then !== 'function') {
      var res = def;
      def = new Deferred()
                .resolve(res);
    }

    //we've got a new deferred on the fly
    this._onFly ++;

    //on deferred resolve or reject, decrement
    def.always(function() { this._onFly --;}, this)

       .addCallback(function() {
          if(!this.isCompleted()) {
            //add to resolved
            this._resolved.push(key);
            //reduce result
            this._launchReduce(key, Array.prototype.slice.call(arguments));
          }
       }, this)

       .addErrback(function() {
        if(!this.isCompleted()) {
          //add to rejected
          this._rejected.push(key);
          //end ?
          this._checkFinish();
        }
       }, this);
  },

  _launchReduce: function(key, result) {
    //if the reduce function is not yet defined, put in a buffer for later
    if(!this.reduceFn) {
      this._reduceBuffer.push(arguments);
      return;
    }

    var reduce_args = []
        i, l;

    //add previous reduce result
    reduce_args.push(this._currentReduceResult);
    //add resolve result of the mapped deferred
    for (i = 0, l = result.length; i < l; i++) { reduce_args.push(result[i]); }
    //add back-to-mapping function : aggregate keys
    // to map and do mapping at the end.
    var to_map = [];
    reduce_args.push(function map(key) {
      to_map.push(key);
    });
    //add the key that produced result
    reduce_args.push(key);
    //add current resolved key
    reduce_args.push(this._resolved);
    //add current rejected key
    reduce_args.push(this._rejected);

    //call reduce
    this._currentReduceResult = this.reduceFn.apply(this, reduce_args);

    //launch mappings
    for (i = 0, l = to_map.length; i < l; i++) {
      this._launchMap(to_map[i]);
    }

    //end ?
    this._checkFinish();
  },

  _launchFinally: function() {
    this._finallyShouldBeLaunched = true;

    if(this.finallyFn) {
      this.finallyFn(this._currentReduceResult, this._resolved, this._rejected);

      //force the completion of the process if finallyFn didn't do it
      if(!this.isCompleted())
        this.resolve(this._currentReduceResult);
    }
  },

  _checkFinish: function() {
    if(this._onFly === 0 && this._reduceBuffer.length === 0 && !this.isCompleted()) {
      this._launchFinally();
    }
  }
});<commit_msg>iterative-deferred: stop convert arguments to array<commit_after>var Deferred  = require('./deferred');

var IterativeDeferred = module.exports = Deferred.extend({
  initialize: function(to_map) {
    this.supr();
    this.to_map = to_map;
    this._started = false;

    this._reduceBuffer = [];
    this._finallyShouldBeLaunched = false;
  },

  /**
   * Functional programming: easy setter for the map function.
   * @see #mapFn
   *
   * @param  {Function} mapFn [description]
   */
  map: function(mapFn) {
    this.mapFn = mapFn;

    //directly start if anything to map
    if(this.to_map)
      this.start();
    return this;
  },

  /**
   * The map function, to be defined: should map a key to a deferred object.
   *
   * Should return a deferred object that will be registered: the iterative
   * process won't stop until all registered deferred are completed or a
   * manual intervention.
   *
   * If the key has already been mapped, the mapping will be ignored. To test
   * equality between key, @see #equalTestFn.
   *
   * @param  {object} key - key to map to a Deferred
   * @return {undefined | Deferred} mapped Deferred
   */
  //mapFn: function(key) {
  //},

  /**
   * Set itinial reduce value.
   *
   * @param  {*} init_value
   */
  init: function(init_value) {
    this._currentReduceResult = init_value;
    return this;
  },

  /**
   * Functional programming: easy setter for the reduce function.
   * @see #reduceFn
   *
   * @param  {function} reduceFn [description]
   * @param {*}       init_value - initial reduce value
   */
  reduce: function(reduceFn, init_value) {
    this.reduceFn = reduceFn;

    if(init_value)
      this._currentReduceResult = init_value;

    //if waiting reduces in buffer, empty it :
      while(this._reduceBuffer.length >0) {
        var args = this._reduceBuffer.shift();
        this._launchReduce.apply(this, args);
      }

    return this;
  },

  /**
   * The reduce function, to be defined: should combine resolved result from
   * mapped deferred and the previous reduce result. It can feed the mapping
   * process with keys to map, by calling the map argument function.
   *
   * If the deferred resolved multiple arguments, the additional arguments are
   * present.
   *
   * At any moment the iterative process can be stopped manually just by
   * completing the working process as deferred: simply call `this.resolve` or
   * `this.reject`.
   *
   * The end arguments key, resolved and rejected are if needed to decide the
   * reduce process.
   *
   * @param  {*}     previous - previously returned by the reduce function
   * @param  {*}       result - the result resolved by the mapped Deferred
   * @param  {*} [additional] - if the resolve callback was called with multiple
   *                            arguments, additional arguments are present
   * @param  {function}   map - use this function to feed the mapping process with
   *                            new keys
   * @param  {object}     key - original mapping key whose deferred produced the
   *                            given resolved result
   * @param  {array} resolved - array of keys which mapped Deferred have been resolved
   * @param  {array} rejected - array of keys which mapped Deferred have been rejected
   *
   * @return {*} reduce result
   */
  //reduceFn: function(result /* ... */, previous, map, key, resolved, rejected) {
  //},

  /**
   * Functionnal programming: easy setter for the finally function.
   * @see #finallyFn
   *
   * @param  {function} finallyFn [description]
   */
  finally: function(finallyFn) {
    this.finallyFn = finallyFn;

    //it's over : launch immediatly finally
    if(this._finallyShouldBeLaunched)
      this._launchFinally();

    return this;
  },

  /**
   * The final function, will be called when the iterative process ends, ie. there
   * is no more uncompleted mapped Deferred and all reduce processes are finished.
   *
   * The final function should complete the process by calling `this.resolve` or
   * `this.reject`. If this is not done, the process will be automatically resolved.
   *
   * @param  {*} reduce_result - what finally came out the reduce process
   * @param  {array} resolved  - array of keys which mapped Deferred have been resolved
   * @param  {array} rejected  - array of keys which mapped Deferred have been rejected
   */
  //finallyFn: function(reduce_result, resolved, rejected) {
  //},

  /**
   * Start the iterative map/reduce given the this array of
   * map consumable.
   *
   * @param  {Array<key>} array [description]
   */
  start: function(array) {
    if(this._started)
      return this;
    this._started = true;

    if(array)
      this.to_map = array;

    var length = this.to_map.length || this.to_map.size();
    if(length !== 0) {
      //initialization
      this._onFly = 0;
      this._alreadyMaped = [];
      this._resolved = [];
      this._rejected = [];

      //go !
      for (var i = 0, l = to_map.length; i < l; i++) {
        this._launchMap(to_map[i]);
      }

    } else {
      this.finallyFn(this._currentReduceResult);
    }
    return this;
  },

  /**
   * Test the equality of 2 keys.
   *
   * Used to determine if a key has already been mapped. Use an #equals method if
   * present. Else use the result of `===`.
   *
   * @param  {*} key1
   * @param  {*} key2
   * @return {boolean} result
   */
  equalTestFn: function(key1, key2) {
    return (typeof key1.equals !== 'undefined') ?
            key1.equals(key2)
          : key1 === key2;
  },

  _launchMap: function(key) {

    //if the key has alreday been mapped
    var already = this._alreadyMaped.some(function(key2) {
      return this.equalTestFn(key, key2);
    }, this);
    if(already)
      return;
    this._alreadyMaped.push(key);

    //call the map function and get the deferred
    var def = this.mapFn(key);
    if(!def || def === null)
      return;

    //it's not a deferred, it's  just an object to reduce directly :
    if(typeof def.then !== 'function') {
      var res = def;
      def = new Deferred()
                .resolve(res);
    }

    //we've got a new deferred on the fly
    this._onFly ++;

    //on deferred resolve or reject, decrement
    def.always(function() { this._onFly --;}, this)

       .addCallback(function() {
          if(!this.isCompleted()) {
            //add to resolved
            this._resolved.push(key);
            //reduce result
            this._launchReduce(key, arguments);
          }
       }, this)

       .addErrback(function() {
        if(!this.isCompleted()) {
          //add to rejected
          this._rejected.push(key);
          //end ?
          this._checkFinish();
        }
       }, this);
  },

  _launchReduce: function(key, result) {
    //if the reduce function is not yet defined, put in a buffer for later
    if(!this.reduceFn) {
      this._reduceBuffer.push(arguments);
      return;
    }

    var reduce_args = []
        i, l;

    //add previous reduce result
    reduce_args.push(this._currentReduceResult);
    //add resolve result of the mapped deferred
    for (i = 0, l = result.length; i < l; i++) { reduce_args.push(result[i]); }
    //add back-to-mapping function : aggregate keys
    // to map and do mapping at the end.
    var to_map = [];
    reduce_args.push(function map(key) {
      to_map.push(key);
    });
    //add the key that produced result
    reduce_args.push(key);
    //add current resolved key
    reduce_args.push(this._resolved);
    //add current rejected key
    reduce_args.push(this._rejected);

    //call reduce
    this._currentReduceResult = this.reduceFn.apply(this, reduce_args);

    //launch mappings
    for (i = 0, l = to_map.length; i < l; i++) {
      this._launchMap(to_map[i]);
    }

    //end ?
    this._checkFinish();
  },

  _launchFinally: function() {
    this._finallyShouldBeLaunched = true;

    if(this.finallyFn) {
      this.finallyFn(this._currentReduceResult, this._resolved, this._rejected);

      //force the completion of the process if finallyFn didn't do it
      if(!this.isCompleted())
        this.resolve(this._currentReduceResult);
    }
  },

  _checkFinish: function() {
    if(this._onFly === 0 && this._reduceBuffer.length === 0 && !this.isCompleted()) {
      this._launchFinally();
    }
  }
});<|endoftext|>


<commit_before>'use strict';

var Assert = require('assert');
var BabelFish = require('../..');

module.exports = {
  title: "#3: Compilation fails.",
  pending: true,
  fixed: false,
  test: function () {
    var i18n = new BabelFish();

    Assert.doesNotThrow(function () {
      i18n.addPhrase('en', 'test', 'foo #{bar} baz\n');
    }, SyntaxError);
  }
};
<commit_msg>New parser fixed #3<commit_after>'use strict';

var Assert = require('assert');
var BabelFish = require('../..');

module.exports = {
  title: "#3: Compilation fails.",
  fixed: true,
  test: function () {
    var i18n = new BabelFish();

    Assert.doesNotThrow(function () {
      i18n.addPhrase('en', 'test', 'foo #(bar) baz\n');
    }, SyntaxError);
  }
};
<|endoftext|>


<commit_before>/**
 * This utility implements dragging for SVG elements in React. That is, it
 * wraps mouse events and simulates drag events, but does not support complex
 * drag-and-drop functionality.
 *
 * Users of this utility should define the three callbacks and then set the
 * onMouseDown of the draggable element to mouseDownDrag.bind(this,
 * someUniqueName, myPseudoDragStartCallback, myPseudoDragEndCallback,
 * myPseudoDragCallback). someUniqueName can be anything, as long it's unique
 * within the component.
 *
 * The using component can check its state for the key
 * (someUniqueName + '_dragging') to see if a particular draggee is dragging.
 */

/**
 * Callback which is called upon at some point during a pseudo drag generated by
 *   this utility.
 *
 * @callback dragCallback
 * @param {event} event - the original mouse event
 */

/**
 * This method replaces onmousemove on the window. It is used internally by the
 *   utility and should not be used outside of the utility.
 *
 * @function onMouseMove
 * @param {string} name - used to uniquely identify a particular draggable
 *   element within a React component
 * @param {dragCallback}pseudoDragCallback - callback that is fired upon mouseMoves
 *   in order to simulate drag events for the pseudo drag. It is passed an event
 *   containing the drag position delta.
 * @param {dragCallback}event - the original mouseDown event that starts the pseudo
      drag
 * @returns {void}
 */
var onMouseMove = function(name, pseudoDragCallback, event) {
  if (this.state[name + '_dragging'] && pseudoDragCallback) {
    pseudoDragCallback(event);
  }
};

/**
 * This method replaces onmouseup and onmouseleave on the window. It is used
 *   internally by the utility and should not be used outside of the utility.
 *
 * @function onMouseUp
 * @param {string} name - used to uniquely identify a particular draggable
 *   element within a React component
 * @param {dragCallback}pseudoDragEndCallback - callback that is fired upon
 *   mouseUp or mouseLeave in order to stop pseudo dragging.
 * @param {dragCallback}event - the original mouseDown event that starts the pseudo
      drag
 * @returns {void}
 */
var onMouseUp = function(name, pseudoDragEndCallback, event) {
  // Turn off the dragging flag.
  var newState = {};
  newState[name + '_dragging'] = false;
  this.setState(newState);
  // Remove mouse listeners from the window.
  window.onmousemove = null;
  window.onmouseleave = null;
  // Call the pseudo drag end callback.
  if (pseudoDragEndCallback) {
    pseudoDragEndCallback(event);
  }
};

/**
 * This method replaces onmousedown on the draggable element. It is not used
 *   internally by the utility, but should be bound to the dragable element by
 *   its containing React component outside of the utility. For a more detailed
 *   description of the way you should use this function, see the comment at the
 *   top of the file.
 *
 * @function mouseDownDrag
 * @param {string} name - used to uniquely identify a particular draggable
 *   element within a React component
 * @param {dragCallback}pseudoDragStartCallback - callback that is fired upon
 *   mouseDown in order to start pseudo dragging.
 * @param {dragCallback}pseudoDragEndCallback - callback that is fired upon
 *   mouseUp or mouseLeave in order to stop pseudo dragging.
 * @param {dragCallback}pseudoDragCallback - callback that is fired upon mouseMoves
 *   in order to simulate drag events for the pseudo drag. It is passed an event
 *   containing the drag position delta.
 * @param {event} event - the original mouseDown event that starts the pseudo
      drag
 * @returns {void}
 */
var mouseDownDrag = function(name, pseudoDragStartCallback, pseudoDragEndCallback, pseudoDragCallback, event) {

  // Turn on the dragging flag and store the initial drag position in state.
  var newState = {};
  newState[name + '_dragging'] = true;
  this.setState(newState);

  // Register event handlers to terminate dragging when the mouse leaves
  // the window or when the button is released.
  window.onmouseup = window.onmouseleave = onMouseUp.bind(this, name, pseudoDragEndCallback);

  // Register the pseudo drag callback.
  window.onmousemove = onMouseMove.bind(this, name, pseudoDragCallback);

  // Call the pseudo drag start callback.
  if (pseudoDragStartCallback) {
    pseudoDragStartCallback(event);
  }
};

module.exports = mouseDownDrag;
<commit_msg>spacing<commit_after>/**
 * This utility implements dragging for SVG elements in React. That is, it
 * wraps mouse events and simulates drag events, but does not support complex
 * drag-and-drop functionality.
 *
 * Users of this utility should define the three callbacks and then set the
 * onMouseDown of the draggable element to mouseDownDrag.bind(this,
 * someUniqueName, myPseudoDragStartCallback, myPseudoDragEndCallback,
 * myPseudoDragCallback). someUniqueName can be anything, as long it's unique
 * within the component.
 *
 * The using component can check its state for the key
 * (someUniqueName + '_dragging') to see if a particular draggee is dragging.
 */

/**
 * Callback which is called upon at some point during a pseudo drag generated by
 *   this utility.
 *
 * @callback dragCallback
 * @param {event} event - the original mouse event
 */

/**
 * This method replaces onmousemove on the window. It is used internally by the
 *   utility and should not be used outside of the utility.
 *
 * @function onMouseMove
 * @param {string} name - used to uniquely identify a particular draggable
 *   element within a React component
 * @param {dragCallback} pseudoDragCallback - callback that is fired upon mouseMoves
 *   in order to simulate drag events for the pseudo drag. It is passed an event
 *   containing the drag position delta.
 * @param {dragCallback} event - the original mouseDown event that starts the pseudo
      drag
 * @returns {void}
 */
var onMouseMove = function(name, pseudoDragCallback, event) {
  if (this.state[name + '_dragging'] && pseudoDragCallback) {
    pseudoDragCallback(event);
  }
};

/**
 * This method replaces onmouseup and onmouseleave on the window. It is used
 *   internally by the utility and should not be used outside of the utility.
 *
 * @function onMouseUp
 * @param {string} name - used to uniquely identify a particular draggable
 *   element within a React component
 * @param {dragCallback} pseudoDragEndCallback - callback that is fired upon
 *   mouseUp or mouseLeave in order to stop pseudo dragging.
 * @param {dragCallback} event - the original mouseDown event that starts the pseudo
      drag
 * @returns {void}
 */
var onMouseUp = function(name, pseudoDragEndCallback, event) {
  // Turn off the dragging flag.
  var newState = {};
  newState[name + '_dragging'] = false;
  this.setState(newState);
  // Remove mouse listeners from the window.
  window.onmousemove = null;
  window.onmouseleave = null;
  // Call the pseudo drag end callback.
  if (pseudoDragEndCallback) {
    pseudoDragEndCallback(event);
  }
};

/**
 * This method replaces onmousedown on the draggable element. It is not used
 *   internally by the utility, but should be bound to the dragable element by
 *   its containing React component outside of the utility. For a more detailed
 *   description of the way you should use this function, see the comment at the
 *   top of the file.
 *
 * @function mouseDownDrag
 * @param {string} name - used to uniquely identify a particular draggable
 *   element within a React component
 * @param {dragCallback} pseudoDragStartCallback - callback that is fired upon
 *   mouseDown in order to start pseudo dragging.
 * @param {dragCallback} pseudoDragEndCallback - callback that is fired upon
 *   mouseUp or mouseLeave in order to stop pseudo dragging.
 * @param {dragCallback} pseudoDragCallback - callback that is fired upon mouseMoves
 *   in order to simulate drag events for the pseudo drag. It is passed an event
 *   containing the drag position delta.
 * @param {event} event - the original mouseDown event that starts the pseudo
      drag
 * @returns {void}
 */
var mouseDownDrag = function(name, pseudoDragStartCallback, pseudoDragEndCallback, pseudoDragCallback, event) {

  // Turn on the dragging flag and store the initial drag position in state.
  var newState = {};
  newState[name + '_dragging'] = true;
  this.setState(newState);

  // Register event handlers to terminate dragging when the mouse leaves
  // the window or when the button is released.
  window.onmouseup = window.onmouseleave = onMouseUp.bind(this, name, pseudoDragEndCallback);

  // Register the pseudo drag callback.
  window.onmousemove = onMouseMove.bind(this, name, pseudoDragCallback);

  // Call the pseudo drag start callback.
  if (pseudoDragStartCallback) {
    pseudoDragStartCallback(event);
  }
};

module.exports = mouseDownDrag;
<|endoftext|>


<commit_before>/* Copyright 2019 Google Inc. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

import {Polygon} from '../../lib/math/polygon2d.js';
import fs from 'fs';
import {easyLog} from '../../lib/log.js';

const log = easyLog('wall:wall_geometry');

// Returns a polygon that entirely contains the wall geometry. NOTE: any point
// to the left of the polygon is outside of it, because we assume that points
// are addressed from the top-left pixel.
function parseGeometry(polygonPoints) {
  const points = polygonPoints.reduce(function(agg, point) {
    const last = agg[agg.length - 1];
    let next;
    if (point.right) {
      next = {x: last.x + point.right, y: last.y};
    } else if (point.down) {
      next = {x: last.x, y: last.y + point.down};
    } else if (point.left) {
      next = {x: last.x - point.left, y: last.y};
    } else if (point.up) {
      next = {x: last.x, y: last.y - point.up};
    }
    agg.push(next);
    return agg;
  }, [{x: 0, y: 0}]);

  return new Polygon(points);
}

export function loadGeometry(path) {
  // Convert from config description to actual polygon.
  const config = JSON.parse(fs.readFileSync(path));
  return config.polygon;
}

let geo;

export function getGeo() {
  return geo;
}

// TODO(applmak): Geometry specified as a single polygon doesn't really accurately reflect what's going on
// on an actual wall, which has a bunch of rectangles slightly offset from one another. Instead of a poly,
// switch the model generating a concave hull of the screens as they load in, which becomes the poly.
export function init(flags) {
  let xscale = 1920;
  let yscale = 1080;
  if (flags.screen_width) {
    xscale = flags.screen_width;
    yscale = xscale * 1080 / 1920;
  }

  if (!flags.use_geometry && !flags.geometry_file) {
    log.warn('No wall geometry specified... assuming 1x1.');
    geo = parseGeometry([{"right":1},{"down":1},{"left":1},{"up":1}]).scale(xscale, yscale);
  } else if (flags.use_geometry) {
    geo = parseGeometry(flags.use_geometry).scale(xscale, yscale);
  } else if (flags.geometry_file) {
    // Note that the geometry loaded from a file isn't scaled.
    geo = parseGeometry(loadGeometry(flags.geometry_file));
  }
}
<commit_msg>Fix scale when reading from a geometry file.<commit_after>/* Copyright 2019 Google Inc. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

import {Polygon} from '../../lib/math/polygon2d.js';
import fs from 'fs';
import {easyLog} from '../../lib/log.js';

const log = easyLog('wall:wall_geometry');

// Returns a polygon that entirely contains the wall geometry. NOTE: any point
// to the left of the polygon is outside of it, because we assume that points
// are addressed from the top-left pixel.
function parseGeometry(polygonPoints) {
  const points = polygonPoints.reduce(function(agg, point) {
    const last = agg[agg.length - 1];
    let next;
    if (point.right) {
      next = {x: last.x + point.right, y: last.y};
    } else if (point.down) {
      next = {x: last.x, y: last.y + point.down};
    } else if (point.left) {
      next = {x: last.x - point.left, y: last.y};
    } else if (point.up) {
      next = {x: last.x, y: last.y - point.up};
    }
    agg.push(next);
    return agg;
  }, [{x: 0, y: 0}]);

  return new Polygon(points);
}

export function loadGeometry(path) {
  // Convert from config description to actual polygon.
  const config = JSON.parse(fs.readFileSync(path));
  return config.polygon;
}

let geo;

export function getGeo() {
  return geo;
}

// TODO(applmak): Geometry specified as a single polygon doesn't really accurately reflect what's going on
// on an actual wall, which has a bunch of rectangles slightly offset from one another. Instead of a poly,
// switch the model generating a concave hull of the screens as they load in, which becomes the poly.
export function init(flags) {
  let xscale = 1920;
  let yscale = 1080;
  if (flags.screen_width) {
    xscale = flags.screen_width;
    yscale = xscale * 1080 / 1920;
  }

  if (!flags.use_geometry && !flags.geometry_file) {
    log.warn('No wall geometry specified... assuming 1x1.');
    geo = parseGeometry([{"right":1},{"down":1},{"left":1},{"up":1}]).scale(xscale, yscale);
  } else if (flags.use_geometry) {
    geo = parseGeometry(flags.use_geometry).scale(xscale, yscale);
  } else if (flags.geometry_file) {
    // Note that the geometry loaded from a file isn't scaled.
    geo = parseGeometry(loadGeometry(flags.geometry_file)).scale(xscale, yscale);
  }
}
<|endoftext|>


<commit_before>var
    Book = Backbone.RelationalModel.extend({
       defaults:{
        name: 'untitled'
       },
       idAttribute: "_id"
    }),
    Author = Backbone.RelationalModel.extend({
       defaults:{
         name: 'untitled'
        },
        idAttribute: "_id",
        relations:[
            {
            type: 'HasMany',
             key: 'books',
             relatedModel: Book,
             includeInJSON: "_id",
             reverseRelation: {
             key: 'author',
             includeInJSON: "_id"
        }}
        ]
    }),
    BookStore = Backbone.RelationalModel.extend({
       relations:[
      {
          type: 'HasMany',
          key: 'books',
          relatedModel: Book
      },
      {
          type: 'HasMany',
          key: 'authors',
          relatedModel: Author
      }
      ]
    });

Author.setup();
BookStore.setup();

var
    bs = new BookStore({
        books:[
            {
            _id:"b1",
            name: "Book One",
            author: "a1"
            },
            {
            _id:"b2",
            name: "Book Two",
            author: "a1"               // does not work
//            author: "a2"               // works
            },
        ],
        authors:[
            {
                name: 'fred',
                _id: "a1",
                books: ["b1","b2"]    // does not work
//                books: ["b1"]         // works
            },
            {
                name: 'ted',
                _id: "a2",
                books: []             // does not work
//                books: ["b2"]         // works

            }
        ]
    });

function bookVmcreate(model){
    var vm = {
        name: kb.observable(model,'name'),
        author: kb.observable(model,'author'),
        editMode: ko.observable(),
        edit:function(){
              model._save = model.toJSON();
              this.editMode(true);},
        confirm:function(){
            model._save = null;
            this.editMode(false);},
        cancel:function(){
            model.set(model._save);
            this.editMode(false);
        },
        model: model
    };
    vm.authorName = ko.computed(function(){
            var a = vm.author();
            if(a) return a.get('name');
            return null;
        });
    return vm;
};

function authorVMcreate(model){
    return {
        name: kb.observable(model,'name'),
        books: kb.collectionObservable(model.get('books'), {models_only: true}),
        // books: kb.observable(model,{
        //     key: 'books',
        //     read: function(){
        //         return model.get('books').models;
        //     },
        //     write: function(val){
        //         model.get('books').reset(val);
        //     }
        // }),
        model: model

    }
}

view_model = {   books: kb.collectionObservable(bs.get('books'), {
                        create: bookVmcreate}) ,
                 authors: kb.collectionObservable(bs.get('authors'), {
                        create: authorVMcreate}),
        showBS:  function(){
          alert(JSON.stringify(bs.toJSON()));
        },
        changeInCode:function(){
              bs.get('books').at(0).set('author',bs.get('authors').at(1));
        }
};
ko.applyBindings(view_model);<commit_msg>Tweak<commit_after>var
    Book = Backbone.RelationalModel.extend({
       defaults:{
        name: 'untitled'
       },
       idAttribute: "_id"
    }),
    Author = Backbone.RelationalModel.extend({
       defaults:{
         name: 'untitled'
        },
        idAttribute: "_id",
        relations:[
            {
            type: 'HasMany',
             key: 'books',
             relatedModel: Book,
             includeInJSON: "_id",
             reverseRelation: {
             key: 'author',
             includeInJSON: "_id"
        }}
        ]
    }),
    BookStore = Backbone.RelationalModel.extend({
       relations:[
      {
          type: 'HasMany',
          key: 'books',
          relatedModel: Book
      },
      {
          type: 'HasMany',
          key: 'authors',
          relatedModel: Author
      }
      ]
    });

Author.setup();
BookStore.setup();

var
    bs = new BookStore({
        books:[
            {
            _id:"b1",
            name: "Book One",
            author: "a1"
            },
            {
            _id:"b2",
            name: "Book Two",
            author: "a1"               // does not work
//            author: "a2"               // works
            },
        ],
        authors:[
            {
                name: 'fred',
                _id: "a1",
                books: ["b1","b2"]    // does not work
//                books: ["b1"]         // works
            },
            {
                name: 'ted',
                _id: "a2",
                books: []             // does not work
//                books: ["b2"]         // works

            }
        ]
    });

function bookVmcreate(model){
    var vm = {
        name: kb.observable(model,'name'),
        author: kb.observable(model,'author'),
        editMode: ko.observable(),
        edit:function(){
              model._save = model.toJSON();
              this.editMode(true);},
        confirm:function(){
            model._save = null;
            this.editMode(false);},
        cancel:function(){
            model.set(model._save);
            this.editMode(false);
        },
        model: model
    };
    vm.authorName = vm.author().name
    return vm;
};

function authorVMcreate(model){
    return {
        name: kb.observable(model,'name'),
        books: kb.collectionObservable(model.get('books'), {models_only: true}),
        // books: kb.observable(model,{
        //     key: 'books',
        //     read: function(){
        //         return model.get('books').models;
        //     },
        //     write: function(val){
        //         model.get('books').reset(val);
        //     }
        // }),
        model: model

    }
}

view_model = {   books: kb.collectionObservable(bs.get('books'), {
                        create: bookVmcreate}) ,
                 authors: kb.collectionObservable(bs.get('authors'), {
                        create: authorVMcreate}),
        showBS:  function(){
          alert(JSON.stringify(bs.toJSON()));
        },
        changeInCode:function(){
              bs.get('books').at(0).set('author',bs.get('authors').at(1));
        }
};
ko.applyBindings(view_model);<|endoftext|>


<commit_before>'use strict';

const	Products	= require(__dirname + '/products.js'),
	Product	= require(__dirname + '/product.js'),
	fastCsv	= require('fast-csv'),
	async	= require('async'),
	log	= require('winston'),
	fs	= require('fs');

/**
 * Import from file
 *
 * @param str filePath
 * @param obj options	{
 *		'formatCols':	{'colName': function},	// Will be applied to all values of selected column
 *		'ignoreCols':	['colName1', 'colName2'],	// Will not write these cols to database
 *		'ignoreTopRows':	0,	// Number of top rows to ignore before treating it as the top row
 *		'renameCols':	{'oldName': 'newName'},	// Rename columns, using first row as names
 *		'replaceByCols':	['col1', 'col2'],	// With erase all previous product data where BOTH these attributes/columns matches
 *		'staticColHeads':	{'4': 'foo', '7': 'bar'},	// Manually set the column names for 4 to "foo" and 7 to "bar". Counting starts at 0
 *		'staticCols':	{'colName': colValues, 'colName2': colValues ...},	// Will extend the columns with this
 *		'updateByCols':	['col1', 'col2'],	// With update product data where BOTH these attributes/columns matches
 *		'noNew':	boolean	// Option to create products that did not exist before
 *	}
 * @param func cb(err, [productUuid1, productUuid2]) the second array is a list of all added/altered products
 */
exports.fromFile = function fromFile(filePath, options, cb) {
	const	alteredProductUuids	= [],
		fileStream	= fs.createReadStream(filePath),
		csvStream	= fastCsv(),
		products	= new Products(),
		colHeads	= [],
		tasks	= [];

	let	currentRowNr;

	if (options === undefined) {
		options	= {};
		cb	= function(){};
	}

	if (typeof options === 'function') {
		cb	= options;
		options	= {};
	}

	if (typeof cb !== 'function') {
		cb = function(){};
	}

	if (options.ignoreCols	=== undefined) { options.ignoreCols	= [];	}
	if (options.ignoreTopRows	=== undefined) { options.ignoreTopRows	= 0;	}
	if (options.dbMethod	=== undefined) { options.dbMethod	= 'update';	}
	if (options.renameCols	=== undefined) { options.renameCols	= {};	}
	if (options.staticColHeads	=== undefined) { options.staticColHeads	= {};	}

	if ( ! (options.ignoreCols instanceof Array)) {
		options.ignoreCols = [options.ignoreCols];
	}

	if (options.replaceByCols) {
		if ( ! (options.replaceByCols instanceof Array)) {
			options.replaceByCols = [options.replaceByCols];
		}
		options.findByCols	= options.replaceByCols;
	}

	if (options.updateByCols) {
		if ( ! (options.updateByCols instanceof Array)) {
			options.updateByCols = [options.updateByCols];
		}
		options.findByCols	= options.updateByCols;
	}

	fileStream.pipe(csvStream);
	csvStream.on('data', function(csvRow) {

		tasks.push(function(cb) {
			const	attributes	= {},
				tasks	= [];

			let	product;

			if (currentRowNr === undefined) {
				currentRowNr = 0;
			} else {
				currentRowNr ++;
			}

			// Set colHeads and rename cols if applicable
			if (currentRowNr === options.ignoreTopRows) {
				for (let i = 0; csvRow[i] !== undefined; i ++) {
					let	colName	= csvRow[i];

					if (options.staticColHeads[i] !== undefined) {
						colName = options.staticColHeads[i];
					} else if (options.renameCols[colName] !== undefined) {
						colName = options.renameCols[colName];
					} else if (options.colHeadToLowerCase === true) {
						colName = colName.toLowerCase();
					}

					colHeads.push(colName);
				}

				// Manually add the static column heads
				if (options.staticCols) {
					for (const colName of Object.keys(options.staticCols)) {
						colHeads.push(colName);
					}
				}

				return;
			} else if (currentRowNr < options.ignoreTopRows) {
				return;
			}

			// Manually add the static column values
			if (options.staticCols) {
				for (const colName of Object.keys(options.staticCols)) {
					csvRow.push(options.staticCols[colName]);
				}
			}

			// Format cols
			for (let i = 0; csvRow[i] !== undefined; i ++) {
				let	colVal	= csvRow[i];

				if (colHeads[i] === '' && colVal === '') {
					continue;
				} else if (colHeads[i] === '') {
					log.warn('larvitproduct: ./importer.js - fromFile() - Ignoring column ' + i + ' on rowNr: ' + currentRowNr + ' since no column header was found');
					continue;
				}

				if (options.formatCols !== undefined) {
					if (typeof options.formatCols[colHeads[i]] === 'function' && colVal !== undefined) {
						colVal = options.formatCols[colHeads[i]](colVal, csvRow);
					}
				}

				if (options.ignoreCols.indexOf(colHeads[i]) === - 1) {
					attributes[colHeads[i]] = colVal;
				}
			}

			// Check if we should ignore this row
			tasks.push(function(cb) {
				for (let i = 0; options.findByCols[i] !== undefined; i ++) {
					if ( ! attributes[options.findByCols[i]]) {
						const err = new Error('Missing attribute value for "' + options.findByCols[i] + '" rowNr: ' + currentRowNr);

						log.verbose('larvitproduct: ./importer.js - fromFile() - ' + err.message);
						cb(err);
						return;
					}
				}

				cb();
			});

			// Check if we already have a product in the database
			tasks.push(function(cb) {
				if ( ! options.findByCols && options.noNew === true) {
					const	err	= new Error('findByCols is not set and we should not create any new products. This means no product will ever be created.');
					log.verbose('larvitproduct: ./importer.js - fromFile() - ' + err.message);
					cb(err);
					return;
				}

				if (options.findByCols) {
					for (let i = 0; options.findByCols[i] !== undefined; i ++) {
						const	col	= options.findByCols[i];

						if ( ! attributes[col]) {
							const	err	= new Error('replaceByCol: "' + col + '" is entered, but product does not have this col');
							log.warn('larvitproduct: ./importer.js - fromFile() - Ignoring product since replaceByCol "' + col + '" is missing on rowNr: ' + currentRowNr);
							cb(err);
							return;
						}
					}

					products.matchAllAttributes	= {};
					for (let i = 0; options.findByCols[i] !== undefined; i ++) {
						const	col	= options.findByCols[i];

						products.matchAllAttributes[col]	= attributes[col];
					}

					products.limit	= 1;
					products.get(function(err, productList, matchedProducts) {
						if (matchedProducts === 0 && options.noNew === true) {
							const	err	= new Error('No matching product found and options.noNew === true');
							log.verbose('larvitproduct: ./importer.js - fromFile() - ' + err.message);
							cb(err);
							return;
						} else if (matchedProducts === 0) {
							product = new Product();
							cb();
							return;
						}

						if (matchedProducts > 1) {
							log.warn('larvitproduct: ./importer.js - fromFile() - Multiple products matched "' + JSON.stringify(options.findByCols) + '"');
						}

						if ( ! productList) {
							const	err	= new Error('Invalid productList object returned from products.get()');
							log.error('larvitproduct: ./importer.js - fromFile() - ' + err.message);
							cb(err);
							return;
						}

						product = new Product(Object.keys(productList)[0]);
						product.loadFromDb(cb);
					});
				} else if (options.noNew !== true) {
					product = new Product();
					cb();
				} else {
					const	err	= new Error('No product found to be updated or replaced and no new products should be created due to noNew !== true');
					log.verbose('larvitproduct: ./importer.js - fromFile() - ' + err.message);
					cb(err);
				}
			});

			// Assign product attributes and save
			tasks.push(function(cb) {
				if (options.updateByCols) {
					if ( ! product.attributes) {
						product.attributes = {};
					}

					for (const colName of Object.keys(attributes)) {
						product.attributes[colName] = attributes[colName];
					}
				} else {
					product.attributes = attributes;
				}

				product.save(function(err) {
					if (err) {
						log.warn('larvitproduct: ./importer.js - fromFile() - Could not save product: ' + err.message);
					} else {
						alteredProductUuids.push(product.uuid);
					}

					cb(err);
				});
			});

			async.series(tasks, function(err) {
				if ( ! err) {
					log.verbose('larvitproduct: ./importer.js - fromFile() - Imported product uuid: ' + product.uuid);
				}

				cb(); // Never report back an error, since that will break the import of the other products
			});
		});
	});

	csvStream.on('end', function() {
		async.parallelLimit(tasks, 100, function() {
			fs.unlink(filePath, function(err) {
				if (err) {
					log.warn('larvitproduct: ./importer.js - fromFile() - fs.unlink() - err: ' + err.message);
				}

				cb(err, alteredProductUuids);
			});
		});
	});

	csvStream.on('error', function(err) {
		log.warn('larvitproduct: ./importer.js - fromFile() - Could not parse csv: ' + err.message);
		cb(err);
		return;
	});
};
<commit_msg>Checker for non-existing findByCols()<commit_after>'use strict';

const	Products	= require(__dirname + '/products.js'),
	Product	= require(__dirname + '/product.js'),
	fastCsv	= require('fast-csv'),
	async	= require('async'),
	log	= require('winston'),
	fs	= require('fs');

/**
 * Import from file
 *
 * @param str filePath
 * @param obj options	{
 *		'formatCols':	{'colName': function},	// Will be applied to all values of selected column
 *		'ignoreCols':	['colName1', 'colName2'],	// Will not write these cols to database
 *		'ignoreTopRows':	0,	// Number of top rows to ignore before treating it as the top row
 *		'renameCols':	{'oldName': 'newName'},	// Rename columns, using first row as names
 *		'replaceByCols':	['col1', 'col2'],	// With erase all previous product data where BOTH these attributes/columns matches
 *		'staticColHeads':	{'4': 'foo', '7': 'bar'},	// Manually set the column names for 4 to "foo" and 7 to "bar". Counting starts at 0
 *		'staticCols':	{'colName': colValues, 'colName2': colValues ...},	// Will extend the columns with this
 *		'updateByCols':	['col1', 'col2'],	// With update product data where BOTH these attributes/columns matches
 *		'noNew':	boolean	// Option to create products that did not exist before
 *	}
 * @param func cb(err, [productUuid1, productUuid2]) the second array is a list of all added/altered products
 */
exports.fromFile = function fromFile(filePath, options, cb) {
	const	alteredProductUuids	= [],
		fileStream	= fs.createReadStream(filePath),
		csvStream	= fastCsv(),
		products	= new Products(),
		colHeads	= [],
		tasks	= [];

	let	currentRowNr;

	if (options === undefined) {
		options	= {};
		cb	= function(){};
	}

	if (typeof options === 'function') {
		cb	= options;
		options	= {};
	}

	if (typeof cb !== 'function') {
		cb = function(){};
	}

	if (options.ignoreCols	=== undefined) { options.ignoreCols	= [];	}
	if (options.ignoreTopRows	=== undefined) { options.ignoreTopRows	= 0;	}
	if (options.dbMethod	=== undefined) { options.dbMethod	= 'update';	}
	if (options.renameCols	=== undefined) { options.renameCols	= {};	}
	if (options.staticColHeads	=== undefined) { options.staticColHeads	= {};	}

	if ( ! (options.ignoreCols instanceof Array)) {
		options.ignoreCols = [options.ignoreCols];
	}

	if (options.replaceByCols) {
		if ( ! (options.replaceByCols instanceof Array)) {
			options.replaceByCols = [options.replaceByCols];
		}
		options.findByCols	= options.replaceByCols;
	}

	if (options.updateByCols) {
		if ( ! (options.updateByCols instanceof Array)) {
			options.updateByCols = [options.updateByCols];
		}
		options.findByCols	= options.updateByCols;
	}

	fileStream.pipe(csvStream);
	csvStream.on('data', function(csvRow) {
		tasks.push(function(cb) {
			const	attributes	= {},
				tasks	= [];

			let	product;

			if (currentRowNr === undefined) {
				currentRowNr = 0;
			} else {
				currentRowNr ++;
			}

			// Set colHeads and rename cols if applicable
			if (currentRowNr === options.ignoreTopRows) {
				for (let i = 0; csvRow[i] !== undefined; i ++) {
					let	colName	= csvRow[i];

					if (options.staticColHeads[i] !== undefined) {
						colName = options.staticColHeads[i];
					} else if (options.renameCols[colName] !== undefined) {
						colName = options.renameCols[colName];
					} else if (options.colHeadToLowerCase === true) {
						colName = colName.toLowerCase();
					}

					colHeads.push(colName);
				}

				// Manually add the static column heads
				if (options.staticCols) {
					for (const colName of Object.keys(options.staticCols)) {
						colHeads.push(colName);
					}
				}

				return;
			} else if (currentRowNr < options.ignoreTopRows) {
				return;
			}

			// Manually add the static column values
			if (options.staticCols) {
				for (const colName of Object.keys(options.staticCols)) {
					csvRow.push(options.staticCols[colName]);
				}
			}

			// Format cols
			for (let i = 0; csvRow[i] !== undefined; i ++) {
				let	colVal	= csvRow[i];

				if (colHeads[i] === '' && colVal === '') {
					continue;
				} else if (colHeads[i] === '') {
					log.warn('larvitproduct: ./importer.js - fromFile() - Ignoring column ' + i + ' on rowNr: ' + currentRowNr + ' since no column header was found');
					continue;
				}

				if (options.formatCols !== undefined) {
					if (typeof options.formatCols[colHeads[i]] === 'function' && colVal !== undefined) {
						colVal = options.formatCols[colHeads[i]](colVal, csvRow);
					}
				}

				if (options.ignoreCols.indexOf(colHeads[i]) === - 1) {
					attributes[colHeads[i]] = colVal;
				}
			}

			// Check if we should ignore this row
			tasks.push(function(cb) {
				if ( ! options.findByCols) {
					cb();
					return;
				}

				for (let i = 0; options.findByCols[i] !== undefined; i ++) {
					if ( ! attributes[options.findByCols[i]]) {
						const err = new Error('Missing attribute value for "' + options.findByCols[i] + '" rowNr: ' + currentRowNr);

						log.verbose('larvitproduct: ./importer.js - fromFile() - ' + err.message);
						cb(err);
						return;
					}
				}

				cb();
			});

			// Check if we already have a product in the database
			tasks.push(function(cb) {
				if ( ! options.findByCols && options.noNew === true) {
					const	err	= new Error('findByCols is not set and we should not create any new products. This means no product will ever be created.');
					log.verbose('larvitproduct: ./importer.js - fromFile() - ' + err.message);
					cb(err);
					return;
				}

				if (options.findByCols) {
					for (let i = 0; options.findByCols[i] !== undefined; i ++) {
						const	col	= options.findByCols[i];

						if ( ! attributes[col]) {
							const	err	= new Error('replaceByCol: "' + col + '" is entered, but product does not have this col');
							log.warn('larvitproduct: ./importer.js - fromFile() - Ignoring product since replaceByCol "' + col + '" is missing on rowNr: ' + currentRowNr);
							cb(err);
							return;
						}
					}

					products.matchAllAttributes	= {};
					for (let i = 0; options.findByCols[i] !== undefined; i ++) {
						const	col	= options.findByCols[i];

						products.matchAllAttributes[col]	= attributes[col];
					}

					products.limit	= 1;
					products.get(function(err, productList, matchedProducts) {
						if (matchedProducts === 0 && options.noNew === true) {
							const	err	= new Error('No matching product found and options.noNew === true');
							log.verbose('larvitproduct: ./importer.js - fromFile() - ' + err.message);
							cb(err);
							return;
						} else if (matchedProducts === 0) {
							product = new Product();
							cb();
							return;
						}

						if (matchedProducts > 1) {
							log.warn('larvitproduct: ./importer.js - fromFile() - Multiple products matched "' + JSON.stringify(options.findByCols) + '"');
						}

						if ( ! productList) {
							const	err	= new Error('Invalid productList object returned from products.get()');
							log.error('larvitproduct: ./importer.js - fromFile() - ' + err.message);
							cb(err);
							return;
						}

						product = new Product(Object.keys(productList)[0]);
						product.loadFromDb(cb);
					});
				} else if (options.noNew !== true) {
					product = new Product();
					cb();
				} else {
					const	err	= new Error('No product found to be updated or replaced and no new products should be created due to noNew !== true');
					log.verbose('larvitproduct: ./importer.js - fromFile() - ' + err.message);
					cb(err);
				}
			});

			// Assign product attributes and save
			tasks.push(function(cb) {
				if (options.updateByCols) {
					if ( ! product.attributes) {
						product.attributes = {};
					}

					for (const colName of Object.keys(attributes)) {
						product.attributes[colName] = attributes[colName];
					}
				} else {
					product.attributes = attributes;
				}

				product.save(function(err) {
					if (err) {
						log.warn('larvitproduct: ./importer.js - fromFile() - Could not save product: ' + err.message);
					} else {
						alteredProductUuids.push(product.uuid);
					}

					cb(err);
				});
			});

			async.series(tasks, function(err) {
				if ( ! err) {
					log.verbose('larvitproduct: ./importer.js - fromFile() - Imported product uuid: ' + product.uuid);
				}

				cb(); // Never report back an error, since that will break the import of the other products
			});
		});
	});

	csvStream.on('end', function() {
		async.parallelLimit(tasks, 100, function() {
			fs.unlink(filePath, function(err) {
				if (err) {
					log.warn('larvitproduct: ./importer.js - fromFile() - fs.unlink() - err: ' + err.message);
				}

				cb(err, alteredProductUuids);
			});
		});
	});

	csvStream.on('error', function(err) {
		log.warn('larvitproduct: ./importer.js - fromFile() - Could not parse csv: ' + err.message);
		cb(err);
		return;
	});
};
<|endoftext|>