<commit_before>/*global observable*/
/*jslint forin: true*/
var MAPJS = MAPJS || {};
MAPJS.MapModel = function (layoutCalculator, titlesToRandomlyChooseFrom) {
	'use strict';
	titlesToRandomlyChooseFrom = titlesToRandomlyChooseFrom || ['double click to edit'];
	var self = this,
		analytic,
		currentLayout = {
			nodes: {},
			connectors: {}
		},
		idea,
		isInputEnabled,
		currentlySelectedIdeaId,
		getRandomTitle = function () {
			return titlesToRandomlyChooseFrom[Math.floor(titlesToRandomlyChooseFrom.length * Math.random())];
		},
		parentNode = function (root, id) {
			var rank, childResult;
			for (rank in root.ideas) {
				if (root.ideas[rank].id === id) {
					return root;
				}
				childResult = parentNode(root.ideas[rank], id);
				if (childResult) {
					return childResult;
				}
			}
		},
		updateCurrentLayout = function (newLayout) {
			var nodeId, newNode, oldNode, newConnector, oldConnector;
			for (nodeId in currentLayout.connectors) {
				newConnector = newLayout.connectors[nodeId];
				oldConnector = currentLayout.connectors[nodeId];
				if (!newConnector || newConnector.from !== oldConnector.from || newConnector.to !== oldConnector.to) {
					self.dispatchEvent('connectorRemoved', oldConnector);
				}
			}
			for (nodeId in currentLayout.nodes) {
				nodeId = parseFloat(nodeId);
				oldNode = currentLayout.nodes[nodeId];
				newNode = newLayout.nodes[nodeId];
				if (!newNode) {
					if (nodeId === currentlySelectedIdeaId) {
						self.selectNode(idea.id);
					}
					self.dispatchEvent('nodeRemoved', oldNode);
				}
			}
			for (nodeId in newLayout.nodes) {
				oldNode = currentLayout.nodes[nodeId];
				newNode = newLayout.nodes[nodeId];
				if (!oldNode) {
					self.dispatchEvent('nodeCreated', newNode);
				} else {
					if (newNode.x !== oldNode.x || newNode.y !== oldNode.y) {
						self.dispatchEvent('nodeMoved', newNode);
					}
					if (newNode.title !== oldNode.title) {
						self.dispatchEvent('nodeTitleChanged', newNode);
					}
				}
			}
			for (nodeId in newLayout.connectors) {
				newConnector = newLayout.connectors[nodeId];
				oldConnector = currentLayout.connectors[nodeId];
				if (!oldConnector || newConnector.from !== oldConnector.from || newConnector.to !== oldConnector.to) {
					self.dispatchEvent('connectorCreated', newConnector);
				}
			}
			currentLayout = newLayout;
		},
		onIdeaChanged = function (command, args) {
			var newIdeaId;
			updateCurrentLayout(layoutCalculator(idea));
			if (command === 'addSubIdea') {
				newIdeaId = args[2];
				self.selectNode(newIdeaId);
				self.editNode(false, true);
			}
		};
	observable(this);
	analytic = self.dispatchEvent.bind(self, 'analytic', 'mapModel');
	this.setIdea = function (anIdea) {
		if (idea) {
			idea.removeEventListener('changed', onIdeaChanged);
		}
		idea = anIdea;
		idea.addEventListener('changed', onIdeaChanged);
		onIdeaChanged();
		self.selectNode(idea.id);
	};
	this.setInputEnabled = function (value) {
		if (isInputEnabled !== value) {
			isInputEnabled = value;
			self.dispatchEvent('inputEnabledChanged', value);
		}
	};
	this.selectNode = function (id) {
		if (id !== currentlySelectedIdeaId) {
			if (currentlySelectedIdeaId) {
				self.dispatchEvent('nodeSelectionChanged', currentlySelectedIdeaId, false);
			}
			currentlySelectedIdeaId = id;
			self.dispatchEvent('nodeSelectionChanged', id, true);
		}
	};
	this.addSubIdea = function (source) {
		analytic('addSubIdea', source);
		idea.addSubIdea(currentlySelectedIdeaId, getRandomTitle());
	};
	this.addSiblingIdea = function (source) {
		analytic('addSiblingIdea', source);
		var parent = parentNode(idea, currentlySelectedIdeaId) || idea;
		idea.addSubIdea(parent.id, getRandomTitle());
	};
	this.removeSubIdea = function (source) {
		analytic('removeSubIdea', source);
		var parent = parentNode(idea, currentlySelectedIdeaId);
		if (idea.removeSubIdea(currentlySelectedIdeaId)) {
			self.selectNode(parent.id);
		}
	};
	this.updateTitle = function (ideaId, title) {
		idea.updateTitle(ideaId, title);
	};
	this.editNode = function (source, shouldSelectAll) {
		if (source) analytic('editNode', source);
		self.dispatchEvent('nodeEditRequested:' + currentlySelectedIdeaId, shouldSelectAll );
	};
	this.scaleUp = function (source) {
		self.dispatchEvent('mapScaleChanged', true);
		analytic('scaleUp', source);
	};
	this.scaleDown = function (source) {
		self.dispatchEvent('mapScaleChanged', false);
		analytic('scaleDown', source);
	};
	(function () {
		var isRootOrRightHalf = function (id) {
				return currentLayout.nodes[id].x >= currentLayout.nodes[idea.id].x;
			},
			isRootOrLeftHalf = function (id) {
				return currentLayout.nodes[id].x <= currentLayout.nodes[idea.id].x;
			},
			currentlySelectedIdeaRank = function (parent) {
				var rank;
				for (rank in parent.ideas) {
					rank = parseFloat(rank);
					if (parent.ideas[rank].id === currentlySelectedIdeaId) {
						return rank;
					}
				}
			};
		self.selectNodeLeft = function (source) {
			var node,
				rank,
				isRoot = currentlySelectedIdeaId === idea.id,
				targetRank = isRoot ? -Infinity : Infinity,
				targetNode;
			analytic('selectNodeLeft', source);
			if (isRootOrLeftHalf(currentlySelectedIdeaId)) {
				node = idea.id === currentlySelectedIdeaId ? idea : idea.findSubIdeaById(currentlySelectedIdeaId);
				for (rank in node.ideas) {
					rank = parseFloat(rank);
					if (isRoot && rank < 0 && rank > targetRank || !isRoot && rank > 0 && rank < targetRank) {
						targetRank = rank;
					}
				}
				if (targetRank !== Infinity && targetRank !== -Infinity) {
					self.selectNode(node.ideas[targetRank].id);
				}
			} else {
				self.selectNode(parentNode(idea, currentlySelectedIdeaId).id);
			}
		};
		self.selectNodeRight = function (source) {
			var node, rank, minimumPositiveRank = Infinity;
			analytic('selectNodeRight', source);
			if (isRootOrRightHalf(currentlySelectedIdeaId)) {
				node = idea.id === currentlySelectedIdeaId ? idea : idea.findSubIdeaById(currentlySelectedIdeaId);
				for (rank in node.ideas) {
					rank = parseFloat(rank);
					if (rank > 0 && rank < minimumPositiveRank) {
						minimumPositiveRank = rank;
					}
				}
				if (minimumPositiveRank !== Infinity) {
					self.selectNode(node.ideas[minimumPositiveRank].id);
				}
			} else {
				self.selectNode(parentNode(idea, currentlySelectedIdeaId).id);
			}
		};
		self.selectNodeUp = function (source) {
			var parent = parentNode(idea, currentlySelectedIdeaId), myRank, previousSiblingRank, rank, isPreviousSiblingWithNegativeRank, isPreviousSiblingWithPositiveRank;
			analytic('selectNodeUp', source);
			if (parent) {
				myRank = currentlySelectedIdeaRank(parent);
				previousSiblingRank = myRank > 0 ? -Infinity : Infinity;
				for (rank in parent.ideas) {
					rank = parseFloat(rank);
					isPreviousSiblingWithNegativeRank = myRank < 0 && rank < 0 && rank > myRank && rank < previousSiblingRank;
					isPreviousSiblingWithPositiveRank = myRank > 0 && rank > 0 && rank < myRank && rank > previousSiblingRank;
					if (isPreviousSiblingWithNegativeRank || isPreviousSiblingWithPositiveRank) {
						previousSiblingRank = rank;
					}
				}
				if (previousSiblingRank !== Infinity && previousSiblingRank !== -Infinity) {
					self.selectNode(parent.ideas[previousSiblingRank].id);
				}
			}
		};
		self.selectNodeDown = function (source) {
			var parent = parentNode(idea, currentlySelectedIdeaId), myRank, nextSiblingRank, rank, isNextSiblingWithNegativeRank, isNextSiblingWithPositiveRank;
			analytic('selectNodeDown', source);
			if (parent) {
				myRank = currentlySelectedIdeaRank(parent);
				nextSiblingRank = myRank > 0 ? Infinity : -Infinity;
				for (rank in parent.ideas) {
					rank = parseFloat(rank);
					isNextSiblingWithNegativeRank = myRank < 0 && rank < 0 && rank < myRank && rank > nextSiblingRank;
					isNextSiblingWithPositiveRank = myRank > 0 && rank > 0 && rank > myRank && rank < nextSiblingRank;
					if (isNextSiblingWithNegativeRank || isNextSiblingWithPositiveRank) {
						nextSiblingRank = rank;
					}
				}
				if (nextSiblingRank !== Infinity && nextSiblingRank !== -Infinity) {
					self.selectNode(parent.ideas[nextSiblingRank].id);
				}
			}
		};
	}());
	//Todo - clean up this shit below
	(function () {
		var currentDroppable,
			updateCurrentDroppable = function (value) {
				if (currentDroppable !== value) {
					if (currentDroppable) {
						self.dispatchEvent('nodeDroppableChanged', currentDroppable, false);
					}
					currentDroppable = value;
					if (currentDroppable) {
						self.dispatchEvent('nodeDroppableChanged', currentDroppable, true);
					}
				}
			},
			canDropOnNode = function (id, x, y, node) {
				return id !== node.id
					&& x >= node.x
					&& y >= node.y
					&& x <= node.x + node.width - 2 * 10
					&& y <= node.y + node.height - 2 * 10;
			},
			tryFlip = function (rootNode, nodeBeingDragged, nodeDragEndX) {
				var flipRightToLeft = rootNode.x < nodeBeingDragged.x && nodeDragEndX < rootNode.x,
					flipLeftToRight = rootNode.x > nodeBeingDragged.x && rootNode.x < nodeDragEndX;
				if (flipRightToLeft || flipLeftToRight) {
					return idea.flip(nodeBeingDragged.id);
				} else {
					return false;
				}
			};
		self.nodeDragMove = function (id, x, y) {
			var nodeId, node;
			for (nodeId in currentLayout.nodes) {
				nodeId = parseFloat(nodeId);
				node = currentLayout.nodes[nodeId];
				if (canDropOnNode(id, x, y, node)) {
					updateCurrentDroppable(nodeId);
					return;
				}
			}
			updateCurrentDroppable(undefined);
		};
		self.nodeDragEnd = function (id, x, y) {
			var nodeBeingDragged = currentLayout.nodes[id],
				nodeId,
				node,
				rootNode = currentLayout.nodes[idea.id],
				verticallyClosestNode = { id: null, y: Infinity };
			updateCurrentDroppable(undefined);
			self.dispatchEvent('nodeMoved', nodeBeingDragged);
			for (nodeId in currentLayout.nodes) {
				node = currentLayout.nodes[nodeId];
				if (canDropOnNode(id, x, y, node)) {
					if (!idea.changeParent(id, nodeId)) {
						self.dispatchEvent('nodeMoved', nodeBeingDragged, 'failed');
					}
					return;
				} else if ((nodeBeingDragged.x === node.x || nodeBeingDragged.x + nodeBeingDragged.width === node.x + node.width) && y < node.y) {
					if (!verticallyClosestNode || node.y < verticallyClosestNode.y) {
						verticallyClosestNode = node;
					}
				}
			}
			if (tryFlip(rootNode, nodeBeingDragged, x)) {
				return;
			}
			if (idea.positionBefore(id, verticallyClosestNode.id)) {
				return;
			}
			self.dispatchEvent('nodeMoved', nodeBeingDragged, 'failed');
		};
	}());
};
<commit_msg>using findParent from content instead of reimplementing it here<commit_after>/*global observable*/
/*jslint forin: true*/
var MAPJS = MAPJS || {};
MAPJS.MapModel = function (layoutCalculator, titlesToRandomlyChooseFrom) {
	'use strict';
	titlesToRandomlyChooseFrom = titlesToRandomlyChooseFrom || ['double click to edit'];
	var self = this,
		analytic,
		currentLayout = {
			nodes: {},
			connectors: {}
		},
		idea,
		isInputEnabled,
		currentlySelectedIdeaId,
		getRandomTitle = function () {
			return titlesToRandomlyChooseFrom[Math.floor(titlesToRandomlyChooseFrom.length * Math.random())];
		},
		parentNode = function (root, id) {
      return root.findParent(id);
		},
		updateCurrentLayout = function (newLayout) {
			var nodeId, newNode, oldNode, newConnector, oldConnector;
			for (nodeId in currentLayout.connectors) {
				newConnector = newLayout.connectors[nodeId];
				oldConnector = currentLayout.connectors[nodeId];
				if (!newConnector || newConnector.from !== oldConnector.from || newConnector.to !== oldConnector.to) {
					self.dispatchEvent('connectorRemoved', oldConnector);
				}
			}
			for (nodeId in currentLayout.nodes) {
				nodeId = parseFloat(nodeId);
				oldNode = currentLayout.nodes[nodeId];
				newNode = newLayout.nodes[nodeId];
				if (!newNode) {
					if (nodeId === currentlySelectedIdeaId) {
						self.selectNode(idea.id);
					}
					self.dispatchEvent('nodeRemoved', oldNode);
				}
			}
			for (nodeId in newLayout.nodes) {
				oldNode = currentLayout.nodes[nodeId];
				newNode = newLayout.nodes[nodeId];
				if (!oldNode) {
					self.dispatchEvent('nodeCreated', newNode);
				} else {
					if (newNode.x !== oldNode.x || newNode.y !== oldNode.y) {
						self.dispatchEvent('nodeMoved', newNode);
					}
					if (newNode.title !== oldNode.title) {
						self.dispatchEvent('nodeTitleChanged', newNode);
					}
				}
			}
			for (nodeId in newLayout.connectors) {
				newConnector = newLayout.connectors[nodeId];
				oldConnector = currentLayout.connectors[nodeId];
				if (!oldConnector || newConnector.from !== oldConnector.from || newConnector.to !== oldConnector.to) {
					self.dispatchEvent('connectorCreated', newConnector);
				}
			}
			currentLayout = newLayout;
		},
		onIdeaChanged = function (command, args) {
			var newIdeaId;
			updateCurrentLayout(layoutCalculator(idea));
			if (command === 'addSubIdea') {
				newIdeaId = args[2];
				self.selectNode(newIdeaId);
				self.editNode(false, true);
			}
		};
	observable(this);
	analytic = self.dispatchEvent.bind(self, 'analytic', 'mapModel');
	this.setIdea = function (anIdea) {
		if (idea) {
			idea.removeEventListener('changed', onIdeaChanged);
		}
		idea = anIdea;
		idea.addEventListener('changed', onIdeaChanged);
		onIdeaChanged();
		self.selectNode(idea.id);
	};
	this.setInputEnabled = function (value) {
		if (isInputEnabled !== value) {
			isInputEnabled = value;
			self.dispatchEvent('inputEnabledChanged', value);
		}
	};
	this.selectNode = function (id) {
		if (id !== currentlySelectedIdeaId) {
			if (currentlySelectedIdeaId) {
				self.dispatchEvent('nodeSelectionChanged', currentlySelectedIdeaId, false);
			}
			currentlySelectedIdeaId = id;
			self.dispatchEvent('nodeSelectionChanged', id, true);
		}
	};
	this.addSubIdea = function (source) {
		analytic('addSubIdea', source);
		idea.addSubIdea(currentlySelectedIdeaId, getRandomTitle());
	};
	this.addSiblingIdea = function (source) {
		analytic('addSiblingIdea', source);
		var parent = parentNode(idea, currentlySelectedIdeaId) || idea;
		idea.addSubIdea(parent.id, getRandomTitle());
	};
	this.removeSubIdea = function (source) {
		analytic('removeSubIdea', source);
		var parent = parentNode(idea, currentlySelectedIdeaId);
		if (idea.removeSubIdea(currentlySelectedIdeaId)) {
			self.selectNode(parent.id);
		}
	};
	this.updateTitle = function (ideaId, title) {
		idea.updateTitle(ideaId, title);
	};
	this.editNode = function (source, shouldSelectAll) {
		if (source) analytic('editNode', source);
		self.dispatchEvent('nodeEditRequested:' + currentlySelectedIdeaId, shouldSelectAll );
	};
	this.scaleUp = function (source) {
		self.dispatchEvent('mapScaleChanged', true);
		analytic('scaleUp', source);
	};
	this.scaleDown = function (source) {
		self.dispatchEvent('mapScaleChanged', false);
		analytic('scaleDown', source);
	};
	(function () {
		var isRootOrRightHalf = function (id) {
				return currentLayout.nodes[id].x >= currentLayout.nodes[idea.id].x;
			},
			isRootOrLeftHalf = function (id) {
				return currentLayout.nodes[id].x <= currentLayout.nodes[idea.id].x;
			},
			currentlySelectedIdeaRank = function (parent) {
				var rank;
				for (rank in parent.ideas) {
					rank = parseFloat(rank);
					if (parent.ideas[rank].id === currentlySelectedIdeaId) {
						return rank;
					}
				}
			};
		self.selectNodeLeft = function (source) {
			var node,
				rank,
				isRoot = currentlySelectedIdeaId === idea.id,
				targetRank = isRoot ? -Infinity : Infinity,
				targetNode;
			analytic('selectNodeLeft', source);
			if (isRootOrLeftHalf(currentlySelectedIdeaId)) {
				node = idea.id === currentlySelectedIdeaId ? idea : idea.findSubIdeaById(currentlySelectedIdeaId);
				for (rank in node.ideas) {
					rank = parseFloat(rank);
					if (isRoot && rank < 0 && rank > targetRank || !isRoot && rank > 0 && rank < targetRank) {
						targetRank = rank;
					}
				}
				if (targetRank !== Infinity && targetRank !== -Infinity) {
					self.selectNode(node.ideas[targetRank].id);
				}
			} else {
				self.selectNode(parentNode(idea, currentlySelectedIdeaId).id);
			}
		};
		self.selectNodeRight = function (source) {
			var node, rank, minimumPositiveRank = Infinity;
			analytic('selectNodeRight', source);
			if (isRootOrRightHalf(currentlySelectedIdeaId)) {
				node = idea.id === currentlySelectedIdeaId ? idea : idea.findSubIdeaById(currentlySelectedIdeaId);
				for (rank in node.ideas) {
					rank = parseFloat(rank);
					if (rank > 0 && rank < minimumPositiveRank) {
						minimumPositiveRank = rank;
					}
				}
				if (minimumPositiveRank !== Infinity) {
					self.selectNode(node.ideas[minimumPositiveRank].id);
				}
			} else {
				self.selectNode(parentNode(idea, currentlySelectedIdeaId).id);
			}
		};
		self.selectNodeUp = function (source) {
			var parent = parentNode(idea, currentlySelectedIdeaId), myRank, previousSiblingRank, rank, isPreviousSiblingWithNegativeRank, isPreviousSiblingWithPositiveRank;
			analytic('selectNodeUp', source);
			if (parent) {
				myRank = currentlySelectedIdeaRank(parent);
				previousSiblingRank = myRank > 0 ? -Infinity : Infinity;
				for (rank in parent.ideas) {
					rank = parseFloat(rank);
					isPreviousSiblingWithNegativeRank = myRank < 0 && rank < 0 && rank > myRank && rank < previousSiblingRank;
					isPreviousSiblingWithPositiveRank = myRank > 0 && rank > 0 && rank < myRank && rank > previousSiblingRank;
					if (isPreviousSiblingWithNegativeRank || isPreviousSiblingWithPositiveRank) {
						previousSiblingRank = rank;
					}
				}
				if (previousSiblingRank !== Infinity && previousSiblingRank !== -Infinity) {
					self.selectNode(parent.ideas[previousSiblingRank].id);
				}
			}
		};
		self.selectNodeDown = function (source) {
			var parent = parentNode(idea, currentlySelectedIdeaId), myRank, nextSiblingRank, rank, isNextSiblingWithNegativeRank, isNextSiblingWithPositiveRank;
			analytic('selectNodeDown', source);
			if (parent) {
				myRank = currentlySelectedIdeaRank(parent);
				nextSiblingRank = myRank > 0 ? Infinity : -Infinity;
				for (rank in parent.ideas) {
					rank = parseFloat(rank);
					isNextSiblingWithNegativeRank = myRank < 0 && rank < 0 && rank < myRank && rank > nextSiblingRank;
					isNextSiblingWithPositiveRank = myRank > 0 && rank > 0 && rank > myRank && rank < nextSiblingRank;
					if (isNextSiblingWithNegativeRank || isNextSiblingWithPositiveRank) {
						nextSiblingRank = rank;
					}
				}
				if (nextSiblingRank !== Infinity && nextSiblingRank !== -Infinity) {
					self.selectNode(parent.ideas[nextSiblingRank].id);
				}
			}
		};
	}());
	//Todo - clean up this shit below
	(function () {
		var currentDroppable,
			updateCurrentDroppable = function (value) {
				if (currentDroppable !== value) {
					if (currentDroppable) {
						self.dispatchEvent('nodeDroppableChanged', currentDroppable, false);
					}
					currentDroppable = value;
					if (currentDroppable) {
						self.dispatchEvent('nodeDroppableChanged', currentDroppable, true);
					}
				}
			},
			canDropOnNode = function (id, x, y, node) {
				return id !== node.id
					&& x >= node.x
					&& y >= node.y
					&& x <= node.x + node.width - 2 * 10
					&& y <= node.y + node.height - 2 * 10;
			},
			tryFlip = function (rootNode, nodeBeingDragged, nodeDragEndX) {
				var flipRightToLeft = rootNode.x < nodeBeingDragged.x && nodeDragEndX < rootNode.x,
					flipLeftToRight = rootNode.x > nodeBeingDragged.x && rootNode.x < nodeDragEndX;
				if (flipRightToLeft || flipLeftToRight) {
					return idea.flip(nodeBeingDragged.id);
				} else {
					return false;
				}
			};
		self.nodeDragMove = function (id, x, y) {
			var nodeId, node;
			for (nodeId in currentLayout.nodes) {
				nodeId = parseFloat(nodeId);
				node = currentLayout.nodes[nodeId];
				if (canDropOnNode(id, x, y, node)) {
					updateCurrentDroppable(nodeId);
					return;
				}
			}
			updateCurrentDroppable(undefined);
		};
		self.nodeDragEnd = function (id, x, y) {
			var nodeBeingDragged = currentLayout.nodes[id],
				nodeId,
				node,
				rootNode = currentLayout.nodes[idea.id],
				verticallyClosestNode = { id: null, y: Infinity };
			updateCurrentDroppable(undefined);
			self.dispatchEvent('nodeMoved', nodeBeingDragged);
			for (nodeId in currentLayout.nodes) {
				node = currentLayout.nodes[nodeId];
				if (canDropOnNode(id, x, y, node)) {
					if (!idea.changeParent(id, nodeId)) {
						self.dispatchEvent('nodeMoved', nodeBeingDragged, 'failed');
					}
					return;
				} else if ((nodeBeingDragged.x === node.x || nodeBeingDragged.x + nodeBeingDragged.width === node.x + node.width) && y < node.y) {
					if (!verticallyClosestNode || node.y < verticallyClosestNode.y) {
						verticallyClosestNode = node;
					}
				}
			}
			if (tryFlip(rootNode, nodeBeingDragged, x)) {
				return;
			}
			if (idea.positionBefore(id, verticallyClosestNode.id)) {
				return;
			}
			self.dispatchEvent('nodeMoved', nodeBeingDragged, 'failed');
		};
	}());
};
<|endoftext|>


<commit_before>/**
 * Created by bogdanbegovic on 8/20/16.
 */

import React, {Component} from 'react';
import {
    Text,
    View,
    Image,
    Animated,
    Easing,
    TouchableOpacity
} from 'react-native';
import CalendarDay from './CalendarDay';
import moment from 'moment';
import styles from './Calendar.style.js';

//Just a shallow array of 7 elements
const arr = [];
for (let i = 0; i < 7; i++) {
    arr.push(i);
}
/*
 * Class CalendarStrip that is representing the whole calendar strip and contains CalendarDay elements
 *
 */
export default class CalendarStrip extends Component {

    static propTypes = {
        style: React.PropTypes.any,
        calendarColor: React.PropTypes.string,
        highlightColor: React.PropTypes.string,
        borderHighlightColor: React.PropTypes.string,

        startingDate: React.PropTypes.any,
        selectedDate: React.PropTypes.any,
        onDateSelected: React.PropTypes.func,
        useIsoWeekday: React.PropTypes.bool,

        iconLeft: React.PropTypes.any,
        iconRight: React.PropTypes.any,
        iconStyle: React.PropTypes.any,
        iconLeftStyle: React.PropTypes.any,
        iconRightStyle: React.PropTypes.any,
        iconContainer: React.PropTypes.any,

        calendarHeaderStyle: React.PropTypes.any,
        calendarHeaderFormat: React.PropTypes.string,

        calendarAnimation: React.PropTypes.object,
        selection: React.PropTypes.string,
        selectionAnimation: React.PropTypes.object,

        dateNameStyle: React.PropTypes.any,
        dateNumberStyle: React.PropTypes.any,
        weekendDateNameStyle: React.PropTypes.any,
        weekendDateNumberStyle: React.PropTypes.any,

        locale: React.PropTypes.object
    };

    static defaultProps = {
        startingDate: moment(),
        useIsoWeekday: true,
        iconLeft: require('./img/left-arrow-black.png'),
        iconRight: require('./img/right-arrow-black.png'),
        calendarHeaderFormat: 'MMMM YYYY'
    };

    constructor(props) {
        super(props);

        if(props.locale) {
            if(props.locale.name && props.locale.config) {
                moment.locale(props.locale.name, props.locale.config);
            } else {
                throw new Error('Locale prop is not in the correct format. \b Locale has to be in form of object, with params NAME and CONFIG!');
            }
        }

        const startingDate = this.setLocale(moment(this.props.startingDate));
        const selectedDate = this.setLocale(moment(this.props.selectedDate));

        this.state = {
            startingDate,
            selectedDate
        };

        this.resetAnimation();

        this.componentDidMount = this.componentDidMount.bind(this);
        this.componentWillUpdate = this.componentWillUpdate.bind(this);
        this.getDatesForWeek = this.getDatesForWeek.bind(this);
        this.getPreviousWeek = this.getPreviousWeek.bind(this);
        this.getNextWeek = this.getNextWeek.bind(this);
        this.onDateSelected = this.onDateSelected.bind(this);
        this.isDateSelected = this.isDateSelected.bind(this);
        this.formatCalendarHeader = this.formatCalendarHeader.bind(this);
        this.animate = this.animate.bind(this);
        this.resetAnimation = this.resetAnimation.bind(this);
    }

    //Animate showing of CalendarDay elements
    componentDidMount() {
        this.animate();
    }

    //Receiving props and set selected date
    componentWillReceiveProps(nextProps) {
        if (nextProps.selectedDate !== this.props.selectedDate) {
            const selectedDate = this.setLocale(moment(nextProps.selectedDate));
            this.setState({
                selectedDate
            });
        }
    }

    //Only animate CalendarDays if the selectedDate is the same
    //Prevents animation on pressing on a date
    componentWillUpdate(nextProps, nextState) {
        if (nextState.selectedDate === this.state.selectedDate) {
            this.resetAnimation();
            this.animate();
        }
    }

    //Function that checks if the locale is passed to the component and sets it to the passed moment instance
    setLocale(momentInstance) {
        if (this.props.locale) {
            momentInstance.locale(this.props.locale.name);
        }
        return momentInstance;
    }

    //Set startingDate to the previous week
    getPreviousWeek() {
        this.setState({startingDate: this.state.startingDate.subtract(1, 'w')});
    }

    //Set startingDate to the next week
    getNextWeek() {
        this.setState({startingDate: this.state.startingDate.add(1, 'w')});
    }

    //Get dates for the week based on the startingDate
    //Using isoWeekday so that it will start from Monday
    getDatesForWeek() {
        const me = this;
        let dates = [];
        let startDate = moment(this.state.startingDate);

        arr.forEach((item, index) => {
            let date;
            if (me.props.useIsoWeekday) {
              date = me.setLocale(moment(startDate.isoWeekday(index + 1)));
            }
            else {
              date = me.setLocale(moment(startDate.add(1, 'days')));
            }
            dates.push(date);
        });
        return dates;
    }

    //Handling press on date/selecting date
    onDateSelected(date) {
        this.setState({selectedDate: date});
        if (this.props.onDateSelected) {
            this.props.onDateSelected(date);
        }
    }

    //Function to check if provided date is the same as selected one, hence date is selected
    //using isSame moment query with 'day' param so that it check years, months and day
    isDateSelected(date) {
        return date.isSame(this.state.selectedDate, 'day');
    }

    //Function for reseting animations
    resetAnimation() {
        this.animatedValue = [];
        arr.forEach((value) => {
            this.animatedValue[value] = new Animated.Value(0);
        });
    }

    //Function to animate showing the CalendarDay elements.
    //Possible cases for animations are sequence and parallel
    animate() {
        if (this.props.calendarAnimation) {
            let animations = arr.map((item) => {
                return Animated.timing(
                    this.animatedValue[item],
                    {
                        toValue: 1,
                        duration: this.props.calendarAnimation.duration,
                        easing: Easing.linear
                    }
                );
            });

            if (this.props.calendarAnimation.type.toLowerCase() === 'sequence') {
                Animated.sequence(animations).start();
            } else {
                if (this.props.calendarAnimation.type.toLowerCase() === 'parallel') {
                    Animated.parallel(animations).start();
                } else {
                    throw new Error('CalendarStrip Error! Type of animation is incorrect!');
                }
            }
        }
    }

    //Function that formats the calendar header
    //It also formats the month section if the week is in between months
    formatCalendarHeader() {
        let firstDay = this.getDatesForWeek()[0];
        let lastDay = this.getDatesForWeek()[this.getDatesForWeek().length - 1];
        let monthFormatting = '';
        //Parsing the month part of the user defined formating
        if ((this.props.calendarHeaderFormat.match(/Mo/g) || []).length > 0) {
            monthFormatting = 'Mo';
        } else {
            if ((this.props.calendarHeaderFormat.match(/M/g) || []).length > 0) {
                for (let i = (this.props.calendarHeaderFormat.match(/M/g) || []).length; i > 0; i--) {
                    monthFormatting += 'M';
                }
            }
        }

        if (firstDay.month() === lastDay.month()) {
            return firstDay.format(this.props.calendarHeaderFormat);
        }
        if (firstDay.year() !== lastDay.year()) {
            return `${firstDay.format(this.props.calendarHeaderFormat)} / ${lastDay.format(this.props.calendarHeaderFormat)}`;
        }
        return `${monthFormatting.length > 1 ? firstDay.format(monthFormatting) : ''} ${monthFormatting.length > 1 ? '/' : ''} ${lastDay.format(this.props.calendarHeaderFormat)}`;
    }

    render() {
        let opacityAnim = 1;
        let datesRender = this.getDatesForWeek().map((date, index) => {
            if (this.props.calendarAnimation) {
                opacityAnim = this.animatedValue[index];
            }
            return (
                <Animated.View key={date} style={{opacity: opacityAnim, flex: 1}}>
                    <CalendarDay
                        date={date}
                        key={date}
                        selected={this.isDateSelected(date)}
                        onDateSelected={this.onDateSelected}
                        calendarColor={this.props.calendarColor}
                        highlightColor={this.props.highlightColor}
                        dateNameStyle={this.props.dateNameStyle}
                        dateNumberStyle={this.props.dateNumberStyle}
                        weekendDateNameStyle={this.props.weekendDateNameStyle}
                        weekendDateNumberStyle={this.props.weekendDateNumberStyle}
                        selection={this.props.selection}
                        selectionAnimation={this.props.selectionAnimation}
                        borderHighlightColor={this.props.borderHighlightColor}
                    />
                </Animated.View>
            );
        });
        return (
            <View style={[styles.calendarContainer, {backgroundColor: this.props.calendarColor}, this.props.style]}>
                <Text style={[styles.calendarHeader, this.props.calendarHeaderStyle]}>{this.formatCalendarHeader()}</Text>
                <View style={styles.datesStrip}>
                    <TouchableOpacity style={[styles.iconContainer, this.props.iconContainer]} onPress={this.getPreviousWeek}>
                        <Image style={[styles.icon, this.props.iconStyle, this.props.iconLeftStyle]} source={this.props.iconLeft}/>
                    </TouchableOpacity>
                    <View style={styles.calendarDates}>
                        {datesRender}
                    </View>
                    <TouchableOpacity style={[styles.iconContainer, this.props.iconContainer]} onPress={this.getNextWeek}>
                        <Image style={[styles.icon, this.props.iconStyle, this.props.iconRightStyle]} source={this.props.iconRight}/>
                    </TouchableOpacity>
                </View>
            </View>
        );
    }
}
<commit_msg>Bugfix when starting week on startingDate<commit_after>/**
 * Created by bogdanbegovic on 8/20/16.
 */

import React, {Component} from 'react';
import {
    Text,
    View,
    Image,
    Animated,
    Easing,
    TouchableOpacity
} from 'react-native';
import CalendarDay from './CalendarDay';
import moment from 'moment';
import styles from './Calendar.style.js';

//Just a shallow array of 7 elements
const arr = [];
for (let i = 0; i < 7; i++) {
    arr.push(i);
}
/*
 * Class CalendarStrip that is representing the whole calendar strip and contains CalendarDay elements
 *
 */
export default class CalendarStrip extends Component {

    static propTypes = {
        style: React.PropTypes.any,
        calendarColor: React.PropTypes.string,
        highlightColor: React.PropTypes.string,
        borderHighlightColor: React.PropTypes.string,

        startingDate: React.PropTypes.any,
        selectedDate: React.PropTypes.any,
        onDateSelected: React.PropTypes.func,
        useIsoWeekday: React.PropTypes.bool,

        iconLeft: React.PropTypes.any,
        iconRight: React.PropTypes.any,
        iconStyle: React.PropTypes.any,
        iconLeftStyle: React.PropTypes.any,
        iconRightStyle: React.PropTypes.any,
        iconContainer: React.PropTypes.any,

        calendarHeaderStyle: React.PropTypes.any,
        calendarHeaderFormat: React.PropTypes.string,

        calendarAnimation: React.PropTypes.object,
        selection: React.PropTypes.string,
        selectionAnimation: React.PropTypes.object,

        dateNameStyle: React.PropTypes.any,
        dateNumberStyle: React.PropTypes.any,
        weekendDateNameStyle: React.PropTypes.any,
        weekendDateNumberStyle: React.PropTypes.any,

        locale: React.PropTypes.object
    };

    static defaultProps = {
        startingDate: moment(),
        useIsoWeekday: true,
        iconLeft: require('./img/left-arrow-black.png'),
        iconRight: require('./img/right-arrow-black.png'),
        calendarHeaderFormat: 'MMMM YYYY'
    };

    constructor(props) {
        super(props);

        if(props.locale) {
            if(props.locale.name && props.locale.config) {
                moment.locale(props.locale.name, props.locale.config);
            } else {
                throw new Error('Locale prop is not in the correct format. \b Locale has to be in form of object, with params NAME and CONFIG!');
            }
        }

        const startingDate = this.setLocale(moment(this.props.startingDate));
        const selectedDate = this.setLocale(moment(this.props.selectedDate));

        this.state = {
            startingDate,
            selectedDate
        };

        this.resetAnimation();

        this.componentDidMount = this.componentDidMount.bind(this);
        this.componentWillUpdate = this.componentWillUpdate.bind(this);
        this.getDatesForWeek = this.getDatesForWeek.bind(this);
        this.getPreviousWeek = this.getPreviousWeek.bind(this);
        this.getNextWeek = this.getNextWeek.bind(this);
        this.onDateSelected = this.onDateSelected.bind(this);
        this.isDateSelected = this.isDateSelected.bind(this);
        this.formatCalendarHeader = this.formatCalendarHeader.bind(this);
        this.animate = this.animate.bind(this);
        this.resetAnimation = this.resetAnimation.bind(this);
    }

    //Animate showing of CalendarDay elements
    componentDidMount() {
        this.animate();
    }

    //Receiving props and set selected date
    componentWillReceiveProps(nextProps) {
        if (nextProps.selectedDate !== this.props.selectedDate) {
            const selectedDate = this.setLocale(moment(nextProps.selectedDate));
            this.setState({
                selectedDate
            });
        }
    }

    //Only animate CalendarDays if the selectedDate is the same
    //Prevents animation on pressing on a date
    componentWillUpdate(nextProps, nextState) {
        if (nextState.selectedDate === this.state.selectedDate) {
            this.resetAnimation();
            this.animate();
        }
    }

    //Function that checks if the locale is passed to the component and sets it to the passed moment instance
    setLocale(momentInstance) {
        if (this.props.locale) {
            momentInstance.locale(this.props.locale.name);
        }
        return momentInstance;
    }

    //Set startingDate to the previous week
    getPreviousWeek() {
        this.setState({startingDate: this.state.startingDate.subtract(1, 'w')});
    }

    //Set startingDate to the next week
    getNextWeek() {
        this.setState({startingDate: this.state.startingDate.add(1, 'w')});
    }

    //Get dates for the week based on the startingDate
    //Using isoWeekday so that it will start from Monday
    getDatesForWeek() {
        const me = this;
        let dates = [];
        let startDate = moment(this.state.startingDate);

        arr.forEach((item, index) => {
            let date;
            if (me.props.useIsoWeekday) {
              date = me.setLocale(moment(startDate.isoWeekday(index + 1)));
            }
            else {
              date = me.setLocale(moment(startDate).add(index, 'days'));
            }
            dates.push(date);
        });
        return dates;
    }

    //Handling press on date/selecting date
    onDateSelected(date) {
        this.setState({selectedDate: date});
        if (this.props.onDateSelected) {
            this.props.onDateSelected(date);
        }
    }

    //Function to check if provided date is the same as selected one, hence date is selected
    //using isSame moment query with 'day' param so that it check years, months and day
    isDateSelected(date) {
        return date.isSame(this.state.selectedDate, 'day');
    }

    //Function for reseting animations
    resetAnimation() {
        this.animatedValue = [];
        arr.forEach((value) => {
            this.animatedValue[value] = new Animated.Value(0);
        });
    }

    //Function to animate showing the CalendarDay elements.
    //Possible cases for animations are sequence and parallel
    animate() {
        if (this.props.calendarAnimation) {
            let animations = arr.map((item) => {
                return Animated.timing(
                    this.animatedValue[item],
                    {
                        toValue: 1,
                        duration: this.props.calendarAnimation.duration,
                        easing: Easing.linear
                    }
                );
            });

            if (this.props.calendarAnimation.type.toLowerCase() === 'sequence') {
                Animated.sequence(animations).start();
            } else {
                if (this.props.calendarAnimation.type.toLowerCase() === 'parallel') {
                    Animated.parallel(animations).start();
                } else {
                    throw new Error('CalendarStrip Error! Type of animation is incorrect!');
                }
            }
        }
    }

    //Function that formats the calendar header
    //It also formats the month section if the week is in between months
    formatCalendarHeader() {
        let firstDay = this.getDatesForWeek()[0];
        let lastDay = this.getDatesForWeek()[this.getDatesForWeek().length - 1];
        let monthFormatting = '';
        //Parsing the month part of the user defined formating
        if ((this.props.calendarHeaderFormat.match(/Mo/g) || []).length > 0) {
            monthFormatting = 'Mo';
        } else {
            if ((this.props.calendarHeaderFormat.match(/M/g) || []).length > 0) {
                for (let i = (this.props.calendarHeaderFormat.match(/M/g) || []).length; i > 0; i--) {
                    monthFormatting += 'M';
                }
            }
        }

        if (firstDay.month() === lastDay.month()) {
            return firstDay.format(this.props.calendarHeaderFormat);
        }
        if (firstDay.year() !== lastDay.year()) {
            return `${firstDay.format(this.props.calendarHeaderFormat)} / ${lastDay.format(this.props.calendarHeaderFormat)}`;
        }
        return `${monthFormatting.length > 1 ? firstDay.format(monthFormatting) : ''} ${monthFormatting.length > 1 ? '/' : ''} ${lastDay.format(this.props.calendarHeaderFormat)}`;
    }

    render() {
        let opacityAnim = 1;
        let datesRender = this.getDatesForWeek().map((date, index) => {
            if (this.props.calendarAnimation) {
                opacityAnim = this.animatedValue[index];
            }
            return (
                <Animated.View key={date} style={{opacity: opacityAnim, flex: 1}}>
                    <CalendarDay
                        date={date}
                        key={date}
                        selected={this.isDateSelected(date)}
                        onDateSelected={this.onDateSelected}
                        calendarColor={this.props.calendarColor}
                        highlightColor={this.props.highlightColor}
                        dateNameStyle={this.props.dateNameStyle}
                        dateNumberStyle={this.props.dateNumberStyle}
                        weekendDateNameStyle={this.props.weekendDateNameStyle}
                        weekendDateNumberStyle={this.props.weekendDateNumberStyle}
                        selection={this.props.selection}
                        selectionAnimation={this.props.selectionAnimation}
                        borderHighlightColor={this.props.borderHighlightColor}
                    />
                </Animated.View>
            );
        });
        return (
            <View style={[styles.calendarContainer, {backgroundColor: this.props.calendarColor}, this.props.style]}>
                <Text style={[styles.calendarHeader, this.props.calendarHeaderStyle]}>{this.formatCalendarHeader()}</Text>
                <View style={styles.datesStrip}>
                    <TouchableOpacity style={[styles.iconContainer, this.props.iconContainer]} onPress={this.getPreviousWeek}>
                        <Image style={[styles.icon, this.props.iconStyle, this.props.iconLeftStyle]} source={this.props.iconLeft}/>
                    </TouchableOpacity>
                    <View style={styles.calendarDates}>
                        {datesRender}
                    </View>
                    <TouchableOpacity style={[styles.iconContainer, this.props.iconContainer]} onPress={this.getNextWeek}>
                        <Image style={[styles.icon, this.props.iconStyle, this.props.iconRightStyle]} source={this.props.iconRight}/>
                    </TouchableOpacity>
                </View>
            </View>
        );
    }
}
<|endoftext|>


<commit_before>/*
 Copyright 2015 Paul Bevis

 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 expect from 'expect'
import {gamePlay,fillDefaultGrid} from '../../js/reducers/game-play'
import {Cell, Sound}  from '../../js/domain/components'
import {CELL_CLICK, GAME_START, GAME_SELECT} from '../../js/constants/action-types'

function isWordMatched(myState, myWordObj) {
    //console.log('=', (!!myState.words.find(word => word.word === myWordObj.word && word.wordFound)))
    return !!myState.words.find(word => word.word === myWordObj.word && word.wordFound)
}
function findWord(state, wordObj) {
    let charPos = 0;
    while (!isWordMatched(state, wordObj)) {
        let action = {type: CELL_CLICK, rowPos: wordObj.positionInGrid[charPos].rowPosition, columnPos: wordObj.positionInGrid[charPos].colPosition};
        state = gamePlay(state, action)
        // if letterfound is empty, that must mean that a word has been found, so check whether the word found is the one
        // we are looking for, if it is not we reset and start selecting the preset cells again.
        if (state.lettersFound.length !== 0) {
            charPos++;
        } else {
            charPos = 0;
        }
    }
    return state;
}

function allWordsFound(newState) {
    return newState.words.reduce((prev, curr) => prev + curr.wordFound, 0) === newState.words.length
}

function findAllWords(boardNumber, wordNumber = 10) {
    let state = gamePlay(undefined, {type: GAME_SELECT, changeValue: 'wordSet' + boardNumber});
    let wordsFoundArray = state.words.filter(word=>word.wordFound);
    while (!allWordsFound(state)) {
        let wordObjects = Object.assign([], state.words);
        let counter = 0;
        while (counter < wordObjects.length) {
            state = findWord(state, wordObjects[counter++])
        }
    }
    return state
}

function checkBoard(boardNumber, wordNumber = 10) {
    let grids = 0;
    while (grids++ < 100) {
        let state = findAllWords(boardNumber, wordNumber);
        let wordsFoundArray = state.words.filter(word=>word.wordFound);
        if (wordsFoundArray.length !== wordNumber) {
            debugger;
        }
        expect(wordsFoundArray.length).toEqual(wordNumber)
        expect(wordsFoundArray.length).toEqual(state.words.length);
    }
}

describe('each board should be completable', () => {

    it('should be able to find all words in grid 1', () => {
            checkBoard(1)
        }
    )

    it('should be able to find all words in grid 2', () => {
            checkBoard(2)
        }
    )

    it('should be able to find all words in grid 3', () => {
            checkBoard(3)
        }
    )

    it('should be able to find all words in grid 4', () => {
            checkBoard(4)
        }
    )

    it('should be able to find all words in grid 5', () => {
            checkBoard(5)
        }
    )

    it('should be able to find all words in grid 6', () => {
            checkBoard(6)
        }
    )

    it('should be able to find all words in grid 7', () => {
            checkBoard(7)
        }
    )

    it('should be able to find all words in grid 8', () => {
            checkBoard(8)
        }
    )

    it('should be able to find all words in grid 9', () => {
            checkBoard(9)
        }
    )

    it('should be able to find all words in grid 10', () => {
            checkBoard(10)
        }
    )

    it('should be able to find all words in grid 11', () => {
            checkBoard(11)
        }
    )

    it('should be able to find all words in grid 12', () => {
            checkBoard(12)
        }
    )

    it('should be able to find all words in grid 13', () => {
            checkBoard(13)
        }
    )

    it('should be able to find all words in grid 14', () => {
            checkBoard(14)
        }
    )

    it('should be able to find all words in grid 15', () => {
            checkBoard(15)
        }
    )

    it('should be able to find all words in grid 16', () => {
            checkBoard(16)
        }
    )

    it('should be able to find all words in grid 7', () => {
            checkBoard(17)
        }
    )

    it('should be able to find all words in grid 18', () => {
            checkBoard(18)
        }
    )

    it('should be able to find all words in grid 19', () => {
            checkBoard(19)
        }
    )

    it('should be able to find all words in grid 20', () => {
            checkBoard(20)
        }
    )

});<commit_msg>Reduce random board game test to completion to 25 reps<commit_after>/*
 Copyright 2015 Paul Bevis

 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 expect from 'expect'
import {gamePlay,fillDefaultGrid} from '../../js/reducers/game-play'
import {Cell, Sound}  from '../../js/domain/components'
import {CELL_CLICK, GAME_START, GAME_SELECT} from '../../js/constants/action-types'

function isWordMatched(myState, myWordObj) {
    //console.log('=', (!!myState.words.find(word => word.word === myWordObj.word && word.wordFound)))
    return !!myState.words.find(word => word.word === myWordObj.word && word.wordFound)
}
function findWord(state, wordObj) {
    let charPos = 0;
    while (!isWordMatched(state, wordObj)) {
        let action = {type: CELL_CLICK, rowPos: wordObj.positionInGrid[charPos].rowPosition, columnPos: wordObj.positionInGrid[charPos].colPosition};
        state = gamePlay(state, action)
        // if letterfound is empty, that must mean that a word has been found, so check whether the word found is the one
        // we are looking for, if it is not we reset and start selecting the preset cells again.
        if (state.lettersFound.length !== 0) {
            charPos++;
        } else {
            charPos = 0;
        }
    }
    return state;
}

function allWordsFound(newState) {
    return newState.words.reduce((prev, curr) => prev + curr.wordFound, 0) === newState.words.length
}

function findAllWords(boardNumber, wordNumber = 10) {
    let state = gamePlay(undefined, {type: GAME_SELECT, changeValue: 'wordSet' + boardNumber});
    let wordsFoundArray = state.words.filter(word=>word.wordFound);
    while (!allWordsFound(state)) {
        let wordObjects = Object.assign([], state.words);
        let counter = 0;
        while (counter < wordObjects.length) {
            state = findWord(state, wordObjects[counter++])
        }
    }
    return state
}

function checkBoard(boardNumber, wordNumber = 10) {
    let grids = 0;
    while (grids++ < 25) {
        let state = findAllWords(boardNumber, wordNumber);
        let wordsFoundArray = state.words.filter(word=>word.wordFound);
        if (wordsFoundArray.length !== wordNumber) {
            debugger;
        }
        expect(wordsFoundArray.length).toEqual(wordNumber)
        expect(wordsFoundArray.length).toEqual(state.words.length);
    }
}

describe('each board should be completable', () => {

    it('should be able to find all words in grid 1', () => {
            checkBoard(1)
        }
    )

    it('should be able to find all words in grid 2', () => {
            checkBoard(2)
        }
    )

    it('should be able to find all words in grid 3', () => {
            checkBoard(3)
        }
    )

    it('should be able to find all words in grid 4', () => {
            checkBoard(4)
        }
    )

    it('should be able to find all words in grid 5', () => {
            checkBoard(5)
        }
    )

    it('should be able to find all words in grid 6', () => {
            checkBoard(6)
        }
    )

    it('should be able to find all words in grid 7', () => {
            checkBoard(7)
        }
    )

    it('should be able to find all words in grid 8', () => {
            checkBoard(8)
        }
    )

    it('should be able to find all words in grid 9', () => {
            checkBoard(9)
        }
    )

    it('should be able to find all words in grid 10', () => {
            checkBoard(10)
        }
    )

    it('should be able to find all words in grid 11', () => {
            checkBoard(11)
        }
    )

    it('should be able to find all words in grid 12', () => {
            checkBoard(12)
        }
    )

    it('should be able to find all words in grid 13', () => {
            checkBoard(13)
        }
    )

    it('should be able to find all words in grid 14', () => {
            checkBoard(14)
        }
    )

    it('should be able to find all words in grid 15', () => {
            checkBoard(15)
        }
    )

    it('should be able to find all words in grid 16', () => {
            checkBoard(16)
        }
    )

    it('should be able to find all words in grid 7', () => {
            checkBoard(17)
        }
    )

    it('should be able to find all words in grid 18', () => {
            checkBoard(18)
        }
    )

    it('should be able to find all words in grid 19', () => {
            checkBoard(19)
        }
    )

    it('should be able to find all words in grid 20', () => {
            checkBoard(20)
        }
    )

});<|endoftext|>


<commit_before>/*global define*/
(function (root, factory) {
  if (typeof exports === 'object') {
    module.exports = factory(require('chance'))
  } else if (typeof define === 'function' && define.amd) {
    define(['chance'], factory)
  } else {
    root.weknowhow = root.weknowhow || {}
    root.weknowhow.chanceGenerators = factory(root.chance)
  }
})(this, function (Chance) {
  function copy (source) {
    var result = {}
    Object.keys(source).forEach(function (key) {
      result[key] = source[key]
    })
    return result
  }

  function unwrap (v) {
    if (Array.isArray(v)) {
      return v.map(unwrap)
    } else if (v && typeof v === 'object' && v.constructor === Object) {
      return Object.keys(v).reduce(function (result, key) {
        result[key] = unwrap(v[key])
        return result
      }, {})
    } else {
      return v && v.isGenerator ? v() : v
    }
  }

  function ExtendedChance (seed) {
    if (!(this instanceof ExtendedChance)) {
      return new ExtendedChance(seed)
    }

    var that = this
    var chance = typeof seed === 'undefined'
        ? new Chance()
        : new Chance(seed)

    // Fix that pick provided a count of zero or one does not return an array
    var originalPick = Chance.prototype.pick
    chance.pick = function (array, count) {
      if (count === 0) {
        return []
      }

      if (count === 1) {
        return [originalPick.call(chance, array, count)]
      }

      return originalPick.call(chance, array, count)
    }

    chance.shape = function (data) {
      return unwrap(data)
    }

    function generatorFunction (name, args, f) {
      f.isGenerator = true
      f.generatorName = name
      f.args = args
      f.toString = function () {
        return name
      }
      return f
    }

    function installMapFunction (generator) {
      generator.map = function (f) {
        var lastValue, lastMappedValue
        var mapGenerator = generatorFunction(generator.generatorName + '.map', [], function () {
          lastValue = generator()
          lastMappedValue = unwrap(f(lastValue, that))
          return lastMappedValue
        })

        mapGenerator.isMappedGenerator = true
        mapGenerator.parentGenerator = generator
        mapGenerator.mapFunction = f

        if (generator.shrink) {
          mapGenerator.shrink = function (value) {
            if (value === lastMappedValue) {
              return generator.shrink(lastValue).map(f)
            } else {
              return mapGenerator
            }
          }
        }

        installMapFunction(mapGenerator)

        return mapGenerator
      }
    }

    var overrides = {
      n: function (generator, count) {
        return createGenerator('n', [generator, count], [generator])
      },
      pickset: function (data, count) {
        var picksetGenerator = generatorFunction('pickset', [data, count], function () {
          picksetGenerator.lastValue = chance.pickset(data, unwrap(count))
          picksetGenerator.lastUnwrappedValue = unwrap(picksetGenerator.lastValue)
          return picksetGenerator.lastUnwrappedValue
        })

        installMapFunction(picksetGenerator)

        picksetGenerator.shrink = function (data) {
          return shrinkers.pickset(picksetGenerator, data)
        }

        return picksetGenerator
      },
      unique: function (generator, count, options) {
        var uniqueGenerator = generatorFunction('unique', [generator, count], function () {
          const comparator = options && options.comparator
          return comparator
            ? chance.unique(() => generator(), unwrap(count), { comparator: comparator })
            : chance.unique(generator, unwrap(count))
        })

        installMapFunction(uniqueGenerator)

        uniqueGenerator.shrink = function (data) {
          return shrinkers.unique(uniqueGenerator, data)
        }

        return uniqueGenerator
      }
    }

    function minMaxShrinker (generator, data) {
      var currentLimits = generator.args[0] || {}
      var limits = copy(currentLimits)

      var value = typeof data === 'string'
        ? parseFloat(data.replace(/^[^\d]*/, ''))
        : data

      if (value < 0 && value < (currentLimits.max || 0)) {
        var max = currentLimits.max || 0
        limits = {
          min: value,
          max: Math.min(0, max)
        }
      } else if (value > 0 && value > (currentLimits.min || 0)) {
        var min = currentLimits.min || 0
        limits = {
          min: Math.max(0, min),
          max: value
        }
      } else {
        return that.constant(data)
      }

      return that[generator.generatorName](limits)
    }

    var shrinkers = {
      n: function (generator, data) {
        if (data.length === 0) {
          return that.constant(data)
        }

        var length = generator.args[1]

        var minLength = 0

        if (typeof length === 'number') {
          minLength = length
        } else if (length && length.isGenerator && length.args[0] && length.args[0].min) {
          minLength = length.args[0].min
        }

        var dataGenerator = generator.args[0]
        if (dataGenerator.shrink) {
          return that.arraySplicer(data.map(dataGenerator.shrink), { min: minLength })
        } else {
          return that.arraySplicer(data, { min: minLength })
        }
      },
      pick: function (generator, data) {
        if (Array.isArray(data)) {
          return shrinkers.pickset(generator, data)
        } else {
          return shrinkers.pickone(generator, data)
        }
      },
      pickone: function (generator, data) {
        return that.constant(data)
      },
      pickset: function (generator, data) {
        if (data.length === 0) {
          return that.constant(data)
        }

        var shrinkable = false
        var count = generator.args[1]
        if (count && count.shrink) {
          shrinkable = true
          count = count.shrink(data.length)
        }

        var shrinkableData = (generator.lastValue || []).some(function (g) {
          return g && g.shrink
        })

        shrinkable = shrinkable || shrinkableData

        if (shrinkableData && data.length < 10 && generator.lastUnwrappedValue === data) {
          data = generator.lastValue.map(function (g, i) {
            return g && g.shrink
              ? g.shrink(data[i])
              : data[i]
          })
        } else {
          data = generator.lastValue
        }

        if (!shrinkable) {
          return that.constant(data)
        }

        return that.pickset(data, count)
      },
      unique: function (generator, data) {
        if (data.length === 0) {
          return that.constant(data)
        }

        var count = generator.args[1]
        if (count && count.shrink) {
          count = count.shrink(data.length)
        } else {
          count = data.length
        }

        var minCount = 0

        if (typeof count === 'number') {
          minCount = count
        } else if (count && count.isGenerator && count.args[0] && count.args[0].min) {
          minCount = count.args[0].min
        }

        return that.arraySplicer(data, { min: minCount })
      },
      shape: function (generator, data) {
        var shapeGenerators = generator.args[0]
        var shrunk = false
        var newShape = Object.keys(shapeGenerators).reduce(function (result, key) {
          var entry = shapeGenerators[key]
          if (entry && typeof entry.shrink === 'function') {
            shrunk = true
            result[key] = entry.shrink(data[key])
          } else {
            result[key] = entry
          }

          return result
        }, {})

        if (shrunk) {
          return that.shape(newShape)
        } else {
          return that.constant(data)
        }
      },
      string: function (generator, data) {
        var options = generator.args[0] || {}

        if (data.length === 0) {
          return that.constant(data)
        }

        var minLength = 0

        if (typeof options.length === 'number') {
          minLength = options.length
        } else if (options.length && options.length.min) {
          minLength = options.length.min
        }

        return that.stringSplicer(data, { min: minLength })
      },
      integer: minMaxShrinker,
      natural: minMaxShrinker,
      floating: minMaxShrinker,
      year: minMaxShrinker,
      altitude: minMaxShrinker,
      depth: minMaxShrinker,
      latitude: minMaxShrinker,
      longitude: minMaxShrinker,
      dollar: minMaxShrinker
    }

    function createGenerator (name, args, omitUnwap) {
      var omitUnwrapIndex = {}

      omitUnwap && args.forEach(function (arg, i) {
        if (omitUnwap.indexOf(arg) !== -1) {
          omitUnwrapIndex[i] = true
        }
      })

      var g = generatorFunction(name, args, function () {
        if (arguments.length === 0) {
          return chance[name].apply(chance, args.map(function (arg, i) {
            return omitUnwrapIndex[i] ? arg : unwrap(arg)
          }))
        } else {
          return createGenerator(
            name,
            Array.prototype.slice.call(arguments)
          )
        }
      })

      var shrinker = shrinkers[name]
      if (shrinker) {
        g.shrink = function (data) {
          return shrinker(g, data)
        }
      } else {
        g.shrink = function (data) {
          return that.constant(data)
        }
      }

      installMapFunction(g)

      return g
    }

    ['shape'].concat(Object.keys(Chance.prototype)).forEach(function (key) {
      var property = chance[key]
      if (typeof property === 'function') {
        if (overrides[key]) {
          that[key] = generatorFunction(key, [], overrides[key])
        } else {
          that[key] = createGenerator(key, [])
        }
      } else {
        that[key] = property
      }
    })

    that.identity = that.constant = generatorFunction('constant', [], function (data) {
      var constantGenerator = generatorFunction('constant', [data], function () {
        return data
      })

      installMapFunction(constantGenerator)

      return constantGenerator
    })

    that.stringSplicer = generatorFunction('stringSplicer', [], function (text, options) {
      if (typeof text !== 'string') {
        throw new Error('The stringSplicer requires a string as the first argument')
      }

      var min = (options || {}).min || 0

      var g = generatorFunction('stringSplicer', [text, options], function () {
        var from = chance.natural({ max: text.length })
        var length = chance.natural({ max: text.length - min })

        return text.slice(0, from) + text.slice(from + length)
      })

      g.shrink = function (data) {
        if (data.length === min) {
          return that.constant(data)
        }

        return that.stringSplicer(data, options)
      }

      installMapFunction(g)

      return g
    })

    that.array = generatorFunction('array', [], function (generator, count) {
      if (typeof count === 'undefined') {
        return that.n(generator, that.natural({ max: 50 }))
      } else {
        return that.n(generator, count)
      }
    })

    that.arraySplicer = generatorFunction('arraySplicer', [], function (array, options) {
      if (!Array.isArray(array)) {
        throw new Error('The arraySplicer requires an array as the first argument')
      }

      var min = (options || {}).min || 0

      var g = generatorFunction('arraySplicer', [array, options], function () {
        var from = chance.natural({ max: array.length })
        var length = chance.natural({ max: array.length - min })

        g.lastValue = array.slice()
        g.lastValue.splice(from, length)
        g.lastUnwrappedValue = unwrap(g.lastValue)

        return g.lastUnwrappedValue
      })

      g.shrink = function (data) {
        var shrinkableData = (g.lastValue || []).some(function (g) {
          return g && g.shrink
        })

        if (!shrinkableData && data.length === min) {
          return that.constant(data)
        }

        if (shrinkableData && data.length < 10 && g.lastUnwrappedValue === data) {
          data = g.lastValue.map(function (g, i) {
            return g && g.shrink
              ? g.shrink(data[i])
              : data[i]
          })
        } else {
          data = g.lastValue
        }

        return that.arraySplicer(data, options)
      }

      installMapFunction(g)

      return g
    })

    that.sequence = generatorFunction('sequence', [], function (fn, count) {
      count = typeof count === 'undefined'
        ? that.natural({ max: 50 })
        : count

      var g = generatorFunction('sequence', [fn, count], function () {
        var context = {}
        var previous = null
        var valueGenerator = function () {
          var result = previous === null
              ? unwrap(fn(context))
              : unwrap(fn(context, previous))

          previous = result

          return result
        }

        return that.array(valueGenerator, count)()
      })

      g.shrink = function (data) {
        if (data.length === 0) {
          return that.constant([])
        }

        var count = g.args[1]
        if (count && count.shrink) {
          count = count.shrink(data.length)
        } else {
          count = data.length
        }
        var valueGenerator = g.args[0]
        return that.sequence(valueGenerator, count)
      }

      installMapFunction(g)

      return g
    })
  }

  return ExtendedChance
})
<commit_msg>Added ES6 by mistake :-(<commit_after>/*global define*/
(function (root, factory) {
  if (typeof exports === 'object') {
    module.exports = factory(require('chance'))
  } else if (typeof define === 'function' && define.amd) {
    define(['chance'], factory)
  } else {
    root.weknowhow = root.weknowhow || {}
    root.weknowhow.chanceGenerators = factory(root.chance)
  }
})(this, function (Chance) {
  function copy (source) {
    var result = {}
    Object.keys(source).forEach(function (key) {
      result[key] = source[key]
    })
    return result
  }

  function unwrap (v) {
    if (Array.isArray(v)) {
      return v.map(unwrap)
    } else if (v && typeof v === 'object' && v.constructor === Object) {
      return Object.keys(v).reduce(function (result, key) {
        result[key] = unwrap(v[key])
        return result
      }, {})
    } else {
      return v && v.isGenerator ? v() : v
    }
  }

  function ExtendedChance (seed) {
    if (!(this instanceof ExtendedChance)) {
      return new ExtendedChance(seed)
    }

    var that = this
    var chance = typeof seed === 'undefined'
        ? new Chance()
        : new Chance(seed)

    // Fix that pick provided a count of zero or one does not return an array
    var originalPick = Chance.prototype.pick
    chance.pick = function (array, count) {
      if (count === 0) {
        return []
      }

      if (count === 1) {
        return [originalPick.call(chance, array, count)]
      }

      return originalPick.call(chance, array, count)
    }

    chance.shape = function (data) {
      return unwrap(data)
    }

    function generatorFunction (name, args, f) {
      f.isGenerator = true
      f.generatorName = name
      f.args = args
      f.toString = function () {
        return name
      }
      return f
    }

    function installMapFunction (generator) {
      generator.map = function (f) {
        var lastValue, lastMappedValue
        var mapGenerator = generatorFunction(generator.generatorName + '.map', [], function () {
          lastValue = generator()
          lastMappedValue = unwrap(f(lastValue, that))
          return lastMappedValue
        })

        mapGenerator.isMappedGenerator = true
        mapGenerator.parentGenerator = generator
        mapGenerator.mapFunction = f

        if (generator.shrink) {
          mapGenerator.shrink = function (value) {
            if (value === lastMappedValue) {
              return generator.shrink(lastValue).map(f)
            } else {
              return mapGenerator
            }
          }
        }

        installMapFunction(mapGenerator)

        return mapGenerator
      }
    }

    var overrides = {
      n: function (generator, count) {
        return createGenerator('n', [generator, count], [generator])
      },
      pickset: function (data, count) {
        var picksetGenerator = generatorFunction('pickset', [data, count], function () {
          picksetGenerator.lastValue = chance.pickset(data, unwrap(count))
          picksetGenerator.lastUnwrappedValue = unwrap(picksetGenerator.lastValue)
          return picksetGenerator.lastUnwrappedValue
        })

        installMapFunction(picksetGenerator)

        picksetGenerator.shrink = function (data) {
          return shrinkers.pickset(picksetGenerator, data)
        }

        return picksetGenerator
      },
      unique: function (generator, count, options) {
        var uniqueGenerator = generatorFunction('unique', [generator, count], function () {
          var comparator = options && options.comparator
          return comparator
            ? chance.unique(function () { return generator() }, unwrap(count), { comparator: comparator })
            : chance.unique(generator, unwrap(count))
        })

        installMapFunction(uniqueGenerator)

        uniqueGenerator.shrink = function (data) {
          return shrinkers.unique(uniqueGenerator, data)
        }

        return uniqueGenerator
      }
    }

    function minMaxShrinker (generator, data) {
      var currentLimits = generator.args[0] || {}
      var limits = copy(currentLimits)

      var value = typeof data === 'string'
        ? parseFloat(data.replace(/^[^\d]*/, ''))
        : data

      if (value < 0 && value < (currentLimits.max || 0)) {
        var max = currentLimits.max || 0
        limits = {
          min: value,
          max: Math.min(0, max)
        }
      } else if (value > 0 && value > (currentLimits.min || 0)) {
        var min = currentLimits.min || 0
        limits = {
          min: Math.max(0, min),
          max: value
        }
      } else {
        return that.constant(data)
      }

      return that[generator.generatorName](limits)
    }

    var shrinkers = {
      n: function (generator, data) {
        if (data.length === 0) {
          return that.constant(data)
        }

        var length = generator.args[1]

        var minLength = 0

        if (typeof length === 'number') {
          minLength = length
        } else if (length && length.isGenerator && length.args[0] && length.args[0].min) {
          minLength = length.args[0].min
        }

        var dataGenerator = generator.args[0]
        if (dataGenerator.shrink) {
          return that.arraySplicer(data.map(dataGenerator.shrink), { min: minLength })
        } else {
          return that.arraySplicer(data, { min: minLength })
        }
      },
      pick: function (generator, data) {
        if (Array.isArray(data)) {
          return shrinkers.pickset(generator, data)
        } else {
          return shrinkers.pickone(generator, data)
        }
      },
      pickone: function (generator, data) {
        return that.constant(data)
      },
      pickset: function (generator, data) {
        if (data.length === 0) {
          return that.constant(data)
        }

        var shrinkable = false
        var count = generator.args[1]
        if (count && count.shrink) {
          shrinkable = true
          count = count.shrink(data.length)
        }

        var shrinkableData = (generator.lastValue || []).some(function (g) {
          return g && g.shrink
        })

        shrinkable = shrinkable || shrinkableData

        if (shrinkableData && data.length < 10 && generator.lastUnwrappedValue === data) {
          data = generator.lastValue.map(function (g, i) {
            return g && g.shrink
              ? g.shrink(data[i])
              : data[i]
          })
        } else {
          data = generator.lastValue
        }

        if (!shrinkable) {
          return that.constant(data)
        }

        return that.pickset(data, count)
      },
      unique: function (generator, data) {
        if (data.length === 0) {
          return that.constant(data)
        }

        var count = generator.args[1]
        if (count && count.shrink) {
          count = count.shrink(data.length)
        } else {
          count = data.length
        }

        var minCount = 0

        if (typeof count === 'number') {
          minCount = count
        } else if (count && count.isGenerator && count.args[0] && count.args[0].min) {
          minCount = count.args[0].min
        }

        return that.arraySplicer(data, { min: minCount })
      },
      shape: function (generator, data) {
        var shapeGenerators = generator.args[0]
        var shrunk = false
        var newShape = Object.keys(shapeGenerators).reduce(function (result, key) {
          var entry = shapeGenerators[key]
          if (entry && typeof entry.shrink === 'function') {
            shrunk = true
            result[key] = entry.shrink(data[key])
          } else {
            result[key] = entry
          }

          return result
        }, {})

        if (shrunk) {
          return that.shape(newShape)
        } else {
          return that.constant(data)
        }
      },
      string: function (generator, data) {
        var options = generator.args[0] || {}

        if (data.length === 0) {
          return that.constant(data)
        }

        var minLength = 0

        if (typeof options.length === 'number') {
          minLength = options.length
        } else if (options.length && options.length.min) {
          minLength = options.length.min
        }

        return that.stringSplicer(data, { min: minLength })
      },
      integer: minMaxShrinker,
      natural: minMaxShrinker,
      floating: minMaxShrinker,
      year: minMaxShrinker,
      altitude: minMaxShrinker,
      depth: minMaxShrinker,
      latitude: minMaxShrinker,
      longitude: minMaxShrinker,
      dollar: minMaxShrinker
    }

    function createGenerator (name, args, omitUnwap) {
      var omitUnwrapIndex = {}

      omitUnwap && args.forEach(function (arg, i) {
        if (omitUnwap.indexOf(arg) !== -1) {
          omitUnwrapIndex[i] = true
        }
      })

      var g = generatorFunction(name, args, function () {
        if (arguments.length === 0) {
          return chance[name].apply(chance, args.map(function (arg, i) {
            return omitUnwrapIndex[i] ? arg : unwrap(arg)
          }))
        } else {
          return createGenerator(
            name,
            Array.prototype.slice.call(arguments)
          )
        }
      })

      var shrinker = shrinkers[name]
      if (shrinker) {
        g.shrink = function (data) {
          return shrinker(g, data)
        }
      } else {
        g.shrink = function (data) {
          return that.constant(data)
        }
      }

      installMapFunction(g)

      return g
    }

    ['shape'].concat(Object.keys(Chance.prototype)).forEach(function (key) {
      var property = chance[key]
      if (typeof property === 'function') {
        if (overrides[key]) {
          that[key] = generatorFunction(key, [], overrides[key])
        } else {
          that[key] = createGenerator(key, [])
        }
      } else {
        that[key] = property
      }
    })

    that.identity = that.constant = generatorFunction('constant', [], function (data) {
      var constantGenerator = generatorFunction('constant', [data], function () {
        return data
      })

      installMapFunction(constantGenerator)

      return constantGenerator
    })

    that.stringSplicer = generatorFunction('stringSplicer', [], function (text, options) {
      if (typeof text !== 'string') {
        throw new Error('The stringSplicer requires a string as the first argument')
      }

      var min = (options || {}).min || 0

      var g = generatorFunction('stringSplicer', [text, options], function () {
        var from = chance.natural({ max: text.length })
        var length = chance.natural({ max: text.length - min })

        return text.slice(0, from) + text.slice(from + length)
      })

      g.shrink = function (data) {
        if (data.length === min) {
          return that.constant(data)
        }

        return that.stringSplicer(data, options)
      }

      installMapFunction(g)

      return g
    })

    that.array = generatorFunction('array', [], function (generator, count) {
      if (typeof count === 'undefined') {
        return that.n(generator, that.natural({ max: 50 }))
      } else {
        return that.n(generator, count)
      }
    })

    that.arraySplicer = generatorFunction('arraySplicer', [], function (array, options) {
      if (!Array.isArray(array)) {
        throw new Error('The arraySplicer requires an array as the first argument')
      }

      var min = (options || {}).min || 0

      var g = generatorFunction('arraySplicer', [array, options], function () {
        var from = chance.natural({ max: array.length })
        var length = chance.natural({ max: array.length - min })

        g.lastValue = array.slice()
        g.lastValue.splice(from, length)
        g.lastUnwrappedValue = unwrap(g.lastValue)

        return g.lastUnwrappedValue
      })

      g.shrink = function (data) {
        var shrinkableData = (g.lastValue || []).some(function (g) {
          return g && g.shrink
        })

        if (!shrinkableData && data.length === min) {
          return that.constant(data)
        }

        if (shrinkableData && data.length < 10 && g.lastUnwrappedValue === data) {
          data = g.lastValue.map(function (g, i) {
            return g && g.shrink
              ? g.shrink(data[i])
              : data[i]
          })
        } else {
          data = g.lastValue
        }

        return that.arraySplicer(data, options)
      }

      installMapFunction(g)

      return g
    })

    that.sequence = generatorFunction('sequence', [], function (fn, count) {
      count = typeof count === 'undefined'
        ? that.natural({ max: 50 })
        : count

      var g = generatorFunction('sequence', [fn, count], function () {
        var context = {}
        var previous = null
        var valueGenerator = function () {
          var result = previous === null
              ? unwrap(fn(context))
              : unwrap(fn(context, previous))

          previous = result

          return result
        }

        return that.array(valueGenerator, count)()
      })

      g.shrink = function (data) {
        if (data.length === 0) {
          return that.constant([])
        }

        var count = g.args[1]
        if (count && count.shrink) {
          count = count.shrink(data.length)
        } else {
          count = data.length
        }
        var valueGenerator = g.args[0]
        return that.sequence(valueGenerator, count)
      }

      installMapFunction(g)

      return g
    })
  }

  return ExtendedChance
})
<|endoftext|>