<commit_before>/*global _, window, document, jQuery, Kinetic*/
var MAPJS = MAPJS || {};
if (Kinetic.Stage.prototype.isRectVisible) {
	throw ("isRectVisible already exists, should not mix in our methods");
}
MAPJS.Rectangle = function (x, y, width, height) {
	'use strict';
	this.scale = function (scale) {
		return new MAPJS.Rectangle(x * scale, y * scale, width * scale, height * scale);
	};
	this.translate = function (dx, dy) {
		return new MAPJS.Rectangle(x + dx, y + dy, width, height);
	};
	this.inset = function (margin) {
		return new MAPJS.Rectangle(x + margin, y + margin, width - (margin * 2), height - (margin * 2));
	};
	this.x = x;
	this.y = y;
	this.height = height;
	this.width = width;
};
Kinetic.Stage.prototype.isRectVisible = function (rect, offset) {
	'use strict';
	offset = offset || {x: 0, y: 0, margin: 0};
	var scale = this.getScale().x || 1;
	rect = rect.scale(scale).translate(offset.x, offset.y).inset(offset.margin);
	return !(
		rect.x + this.attrs.x > this.getWidth() ||
		rect.x + rect.width + this.attrs.x < 0  ||
		rect.y + this.attrs.y > this.getHeight() ||
		rect.y + rect.height + this.attrs.y < 0
	);
};

MAPJS.KineticMediator = function (mapModel, stage, imageRendering) {
	'use strict';
	var layer = new Kinetic.Layer(),
		nodeByIdeaId = {},
		connectorByFromIdeaId_ToIdeaId = {},
		connectorKey = function (fromIdeaId, toIdeaId) {
			return fromIdeaId + '_' + toIdeaId;
		},
		atLeastOneVisible = function (list, deltaX, deltaY) {
			var margin = Math.min(stage.getHeight(), stage.getWidth()) * 0.1;
			return _.find(list, function (node) {
				return node.isVisible({x: deltaX, y: deltaY, margin: margin});
			});
		},
		moveStage = function (deltaX, deltaY) {
			var visibleAfterMove, visibleBeforeMove;
			if (!stage) {
				return;
			}
			visibleBeforeMove = atLeastOneVisible(nodeByIdeaId, 0, 0) || atLeastOneVisible(connectorByFromIdeaId_ToIdeaId, 0, 0);
			visibleAfterMove = atLeastOneVisible(nodeByIdeaId, deltaX, deltaY) || atLeastOneVisible(connectorByFromIdeaId_ToIdeaId, deltaX, deltaY);
			if (visibleAfterMove || (!visibleBeforeMove)) {
				if (deltaY !== 0) { stage.attrs.y += deltaY; }
				if (deltaX !== 0) { stage.attrs.x += deltaX; }
				stage.draw();
			}
		},
		getTargetShape = function (evt) {
			var is;
			if (evt.offsetX && evt.offsetY) {
				is = stage.getIntersection({x: evt.offsetX, y: evt.offsetY});
				return is && is.shape;
			}
			return false;
		},
		resetStage = function (evt) {
			stage.transitionTo({
				x: 0.5 * stage.getWidth(),
				y: 0.5 * stage.getHeight(),
				scale: {
					x: 1,
					y: 1
				},
				duration: 0.5,
				easing: 'ease-in-out'
			});
		},
		resetstageOnClick = function (evt) {
			if (!getTargetShape(evt)) {
				resetStage();
			}
		},
		stageCenteringEvents = function (isInputEnabled) {
			jQuery(document)[isInputEnabled ? 'on' : 'off']('dblclick', stage.simulate.bind(stage, 'dblclick'));
			stage[isInputEnabled ? 'on' : 'off']('dbltap dblclick', resetstageOnClick);
		};

	stage.add(layer);
	mapModel.addEventListener('inputEnabledChanged', stageCenteringEvents);
	stageCenteringEvents(true);
	mapModel.addEventListener('nodeCreated', function (n) {
		var node = new Kinetic.Idea({
			level: n.level,
			x: n.x,
			y: n.y,
			text: n.title,
			mmStyle: n.style,
			opacity: 1
		});

		if (imageRendering) {
			node = Kinetic.IdeaProxy(node, stage, layer);
		}
		/* in kinetic 4.3 cannot use click because click if fired on dragend */
		node.on('click tap', mapModel.selectNode.bind(mapModel, n.id));
		node.on('dragstart', function () {
			node.moveToTop();
			node.getNodeAttrs().shadow.offset = {
				x: 8,
				y: 8
			};
		});
		node.on('dragmove', function () {
			mapModel.nodeDragMove(
				n.id,
				node.attrs.x,
				node.attrs.y
			);
		});
		node.on('dragend', function () {
			node.getNodeAttrs().shadow.offset = {
				x: 4,
				y: 4
			};
			mapModel.nodeDragEnd(
				n.id,
				node.attrs.x,
				node.attrs.y
			);
			if (n.level > 1) {
				stage.setDraggable(true);
			}
		});
		node.on(':textChanged', function (event) {
			mapModel.updateTitle(n.id, event.text);
			mapModel.dispatchEvent('inputEnabledChanged', true);
		});
		node.on(':editing', function (event) {
			mapModel.dispatchEvent('inputEnabledChanged', false);
		});
		node.on(':nodeEditRequested', mapModel.editNode.bind(mapModel, 'mouse', false));

		if (n.level > 1) {
			node.on('mouseover touchstart', stage.setDraggable.bind(stage, false));
			node.on('mouseout touchend', stage.setDraggable.bind(stage, true));

		}
		layer.add(node);
		node.transitionToAndDontStopCurrentTransitions({
			opacity: 1,
			duration: 0.4
		});

		mapModel.addEventListener('nodeEditRequested:' + n.id, node.editNode);
		nodeByIdeaId[n.id] = node;

	});
	mapModel.addEventListener('nodeSelectionChanged', function (ideaId, isSelected) {
		var node = nodeByIdeaId[ideaId],
			scale = stage.getScale().x || 1,
			offset = 100,
			move = { x: 0, y: 0 };
		node.setIsSelected(isSelected);
		if (!isSelected) {
			return;
		}
		if (node.getAbsolutePosition().x + node.getWidth() * scale + offset > stage.getWidth()) {
			move.x = stage.getWidth() - (node.getAbsolutePosition().x + node.getWidth() * scale + offset);
		} else if (node.getAbsolutePosition().x < offset) {
			move.x  = offset - node.getAbsolutePosition().x;
		}
		if (node.getAbsolutePosition().y + node.getHeight() * scale + offset > stage.getHeight()) {
			move.y = stage.getHeight() - (node.getAbsolutePosition().y + node.getHeight() * scale + offset);
		} else if (node.getAbsolutePosition().y < offset) {
			move.y = offset - node.getAbsolutePosition().y;
		}
		stage.transitionTo({
			x: stage.attrs.x + move.x,
			y: stage.attrs.y + move.y,
			duration: 0.4,
			easing: 'ease-in-out'
		});
	});
	mapModel.addEventListener('nodeStyleChanged', function (n) {
		var node = nodeByIdeaId[n.id];
		node.setMMStyle(n.style);
	});
	mapModel.addEventListener('nodeDroppableChanged', function (ideaId, isDroppable) {
		var node = nodeByIdeaId[ideaId];
		node.setIsDroppable(isDroppable);
	});
	mapModel.addEventListener('nodeRemoved', function (n) {
		var node = nodeByIdeaId[n.id];
		delete nodeByIdeaId[n.id];
		node.transitionTo({
			opacity: 0.25,
			duration: 0.4,
			callback: node.remove.bind(node)
		});
		node.off('click dblclick tap dragstart dragmove dragend mouseover mouseout :textChanged :nodeEditRequested');
		mapModel.removeEventListener('nodeEditRequested:' + n.id, node.editNode);
	});
	mapModel.addEventListener('nodeMoved', function (n, reason) {
		var node = nodeByIdeaId[n.id];
		node.transitionTo({
			x: n.x,
			y: n.y,
			duration: 0.4,
			easing: reason === 'failed' ? 'bounce-ease-out' : 'ease-in-out'
		});
	});
	mapModel.addEventListener('nodeTitleChanged', function (n) {
		var node = nodeByIdeaId[n.id];
		node.setText(n.title);
		layer.draw();
	});
	mapModel.addEventListener('connectorCreated', function (n) {
		var connector = new Kinetic.Connector({
			shapeFrom: nodeByIdeaId[n.from],
			shapeTo: nodeByIdeaId[n.to],
			stroke: '#888',
			strokeWidth: 1,
			opacity: 0
		});
		connector.opacity = 0;
		connectorByFromIdeaId_ToIdeaId[connectorKey(n.from, n.to)] = connector;
		layer.add(connector);
		connector.moveToBottom();
		connector.transitionTo({
			opacity: 1,
			duration: 0.4
		});
	});
	mapModel.addEventListener('connectorRemoved', function (n) {
		var key = connectorKey(n.from, n.to),
			connector = connectorByFromIdeaId_ToIdeaId[key];
		delete connectorByFromIdeaId_ToIdeaId[key];
		connector.transitionTo({
			opacity: 0,
			duration: 0.1,
			callback: connector.remove.bind(connector)
		});
	});
	mapModel.addEventListener('mapScaleChanged', function (scaleMultiplier, zoomPoint) {
		var currentScale = stage.getScale().x || 1,
			targetScale = Math.max(Math.min(currentScale * scaleMultiplier, 5), 0.2);
		if (currentScale === targetScale) {
			return;
		}
		zoomPoint = zoomPoint || {x:  0.5 * stage.getWidth(), y: 0.5 * stage.getHeight()};
		stage.transitionTo({
			scale: {
				x: targetScale,
				y: targetScale
			},
			x: zoomPoint.x + (stage.attrs.x - zoomPoint.x) * targetScale / currentScale,
			y: zoomPoint.y + (stage.attrs.y - zoomPoint.y) * targetScale / currentScale,
			duration: 0.1,
			easing: 'ease-in-out',
			callback: function () {
				if (imageRendering) {
					stage.fire(':scaleChangeComplete');
				}
			}
		});
	});
	mapModel.addEventListener('mapMoveRequested', function (deltaX, deltaY) {
		moveStage(deltaX, deltaY);
	});
	(function () {
		var x, y;
		stage.on('dragmove', function () {
			if (!(atLeastOneVisible(nodeByIdeaId, 0, 0) || atLeastOneVisible(connectorByFromIdeaId_ToIdeaId, 0, 0))) {
				moveStage(x - stage.attrs.x, y - stage.attrs.y);
			}
			x = stage.attrs.x;
			y = stage.attrs.y;
		});
	}());
	(function () {
		var keyboardEventHandlers = {
			13: mapModel.addSiblingIdea.bind(mapModel, 'keyboard'),
			8: mapModel.removeSubIdea.bind(mapModel, 'keyboard'),
			9: mapModel.addSubIdea.bind(mapModel, 'keyboard'),
			37: mapModel.selectNodeLeft.bind(mapModel, 'keyboard'),
			38: mapModel.selectNodeUp.bind(mapModel, 'keyboard'),
			39: mapModel.selectNodeRight.bind(mapModel, 'keyboard'),
			40: mapModel.selectNodeDown.bind(mapModel, 'keyboard'),
			46: mapModel.removeSubIdea.bind(mapModel, 'keyboard'),
			32: mapModel.editNode.bind(mapModel, 'keyboard'),
			191: mapModel.toggleCollapse.bind(mapModel, 'keyboard'),
			67: mapModel.mark.bind(mapModel, 'keyboard'),
			80: mapModel.moveMarked.bind(mapModel, 'keyboard')
		}, shiftKeyboardEventHandlers = {
			9: mapModel.insertIntermediate.bind(mapModel, 'keyboard'),
			38: mapModel.toggleCollapse.bind(mapModel, 'keyboard')
		}, metaKeyboardEventHandlers = {
			48: resetStage,
			90: mapModel.undo.bind(mapModel, 'keyboard'),
			89: mapModel.redo.bind(mapModel, 'keyboard'),
			187: mapModel.scaleUp.bind(mapModel, 'keyboard'),
			189: mapModel.scaleDown.bind(mapModel, 'keyboard'),
			38: mapModel.moveRelative.bind(mapModel, 'keyboard', -1),
			40: mapModel.moveRelative.bind(mapModel, 'keyboard', 1),
			88: mapModel.mark.bind(mapModel, 'keyboard'),
			86: mapModel.moveMarked.bind(mapModel, 'keyboard')
		},
			onKeydown = function (evt) {
				var eventHandler = ((evt.metaKey || evt.ctrlKey) ? metaKeyboardEventHandlers :
						(evt.shiftKey ? shiftKeyboardEventHandlers : keyboardEventHandlers))[evt.which];
				if (eventHandler) {
					eventHandler();
					evt.preventDefault();
				}
			},
			onScroll = function (event, delta, deltaX, deltaY) {
				moveStage(-1 * deltaX, deltaY);
				if (event.preventDefault) { // stop the back button
					event.preventDefault();
				}
			};
		jQuery(window).mousewheel(onScroll);
		mapModel.addEventListener('inputEnabledChanged', function (isInputEnabled) {
			jQuery(document)[isInputEnabled ? 'bind' : 'unbind']('keydown', onKeydown);
			jQuery(window)[isInputEnabled ? 'mousewheel' : 'unmousewheel'](onScroll);
		});
		jQuery(document).keydown(onKeydown);
	}());
};
MAPJS.KineticMediator.dimensionProvider = _.memoize(function (title) {
	'use strict';
	var text = new Kinetic.Idea({
		text: title
	});
	return {
		width: text.getWidth(),
		height: text.getHeight()
	};
});
MAPJS.KineticMediator.layoutCalculator = function (idea) {
	'use strict';
	return MAPJS.calculateLayout(idea, MAPJS.KineticMediator.dimensionProvider);
};
<commit_msg>can no longer drag map out of the screen<commit_after>/*global _, window, document, jQuery, Kinetic*/
var MAPJS = MAPJS || {};
if (Kinetic.Stage.prototype.isRectVisible) {
	throw ('isRectVisible already exists, should not mix in our methods');
}
MAPJS.Rectangle = function (x, y, width, height) {
	'use strict';
	this.scale = function (scale) {
		return new MAPJS.Rectangle(x * scale, y * scale, width * scale, height * scale);
	};
	this.translate = function (dx, dy) {
		return new MAPJS.Rectangle(x + dx, y + dy, width, height);
	};
	this.inset = function (margin) {
		return new MAPJS.Rectangle(x + margin, y + margin, width - (margin * 2), height - (margin * 2));
	};
	this.x = x;
	this.y = y;
	this.height = height;
	this.width = width;
};
Kinetic.Stage.prototype.isRectVisible = function (rect, offset) {
	'use strict';
	offset = offset || {x: 0, y: 0, margin: 0};
	var scale = this.getScale().x || 1;
	rect = rect.scale(scale).translate(offset.x, offset.y).inset(offset.margin);
	return !(
		rect.x + this.attrs.x > this.getWidth() ||
		rect.x + rect.width + this.attrs.x < 0  ||
		rect.y + this.attrs.y > this.getHeight() ||
		rect.y + rect.height + this.attrs.y < 0
	);
};

MAPJS.KineticMediator = function (mapModel, stage, imageRendering) {
	'use strict';
	var layer = new Kinetic.Layer(),
		nodeByIdeaId = {},
		connectorByFromIdeaId_ToIdeaId = {},
		connectorKey = function (fromIdeaId, toIdeaId) {
			return fromIdeaId + '_' + toIdeaId;
		},
		atLeastOneVisible = function (list, deltaX, deltaY) {
			var margin = Math.min(stage.getHeight(), stage.getWidth()) * 0.1;
			return _.find(list, function (node) {
				return node.isVisible({x: deltaX, y: deltaY, margin: margin});
			});
		},
		moveStage = function (deltaX, deltaY) {
			var visibleAfterMove, visibleBeforeMove;
			if (!stage) {
				return;
			}
			visibleBeforeMove = atLeastOneVisible(nodeByIdeaId, 0, 0) || atLeastOneVisible(connectorByFromIdeaId_ToIdeaId, 0, 0);
			visibleAfterMove = atLeastOneVisible(nodeByIdeaId, deltaX, deltaY) || atLeastOneVisible(connectorByFromIdeaId_ToIdeaId, deltaX, deltaY);
			if (visibleAfterMove || (!visibleBeforeMove)) {
				if (deltaY !== 0) { stage.attrs.y += deltaY; }
				if (deltaX !== 0) { stage.attrs.x += deltaX; }
				stage.draw();
			}
		},
		getTargetShape = function (evt) {
			var is;
			if (evt.offsetX && evt.offsetY) {
				is = stage.getIntersection({x: evt.offsetX, y: evt.offsetY});
				return is && is.shape;
			}
			return false;
		},
		resetStage = function () {
			stage.transitionTo({
				x: 0.5 * stage.getWidth(),
				y: 0.5 * stage.getHeight(),
				scale: {
					x: 1,
					y: 1
				},
				duration: 0.5,
				easing: 'ease-in-out'
			});
		},
		resetstageOnClick = function (evt) {
			if (!getTargetShape(evt)) {
				resetStage();
			}
		},
		stageCenteringEvents = function (isInputEnabled) {
			jQuery(document)[isInputEnabled ? 'on' : 'off']('dblclick', stage.simulate.bind(stage, 'dblclick'));
			stage[isInputEnabled ? 'on' : 'off']('dbltap dblclick', resetstageOnClick);
		};

	stage.add(layer);
	mapModel.addEventListener('inputEnabledChanged', stageCenteringEvents);
	stageCenteringEvents(true);
	mapModel.addEventListener('nodeCreated', function (n) {
		var node = new Kinetic.Idea({
			level: n.level,
			x: n.x,
			y: n.y,
			text: n.title,
			mmStyle: n.style,
			opacity: 1
		});

		if (imageRendering) {
			node = Kinetic.IdeaProxy(node, stage, layer);
		}
		/* in kinetic 4.3 cannot use click because click if fired on dragend */
		node.on('click tap', mapModel.selectNode.bind(mapModel, n.id));
		node.on('dragstart', function () {
			node.moveToTop();
			node.getNodeAttrs().shadow.offset = {
				x: 8,
				y: 8
			};
		});
		node.on('dragmove', function () {
			mapModel.nodeDragMove(
				n.id,
				node.attrs.x,
				node.attrs.y
			);
		});
		node.on('dragend', function () {
			node.getNodeAttrs().shadow.offset = {
				x: 4,
				y: 4
			};
			mapModel.nodeDragEnd(
				n.id,
				node.attrs.x,
				node.attrs.y
			);
			if (n.level > 1) {
				stage.setDraggable(true);
			}
		});
		node.on(':textChanged', function (event) {
			mapModel.updateTitle(n.id, event.text);
			mapModel.dispatchEvent('inputEnabledChanged', true);
		});
		node.on(':editing', function () {
			mapModel.dispatchEvent('inputEnabledChanged', false);
		});
		node.on(':nodeEditRequested', mapModel.editNode.bind(mapModel, 'mouse', false));

		if (n.level > 1) {
			node.on('mouseover touchstart', stage.setDraggable.bind(stage, false));
			node.on('mouseout touchend', stage.setDraggable.bind(stage, true));

		}
		layer.add(node);
		node.transitionToAndDontStopCurrentTransitions({
			opacity: 1,
			duration: 0.4
		});

		mapModel.addEventListener('nodeEditRequested:' + n.id, node.editNode);
		nodeByIdeaId[n.id] = node;

	});
	mapModel.addEventListener('nodeSelectionChanged', function (ideaId, isSelected) {
		var node = nodeByIdeaId[ideaId],
			scale = stage.getScale().x || 1,
			offset = 100,
			move = { x: 0, y: 0 };
		node.setIsSelected(isSelected);
		if (!isSelected) {
			return;
		}
		if (node.getAbsolutePosition().x + node.getWidth() * scale + offset > stage.getWidth()) {
			move.x = stage.getWidth() - (node.getAbsolutePosition().x + node.getWidth() * scale + offset);
		} else if (node.getAbsolutePosition().x < offset) {
			move.x  = offset - node.getAbsolutePosition().x;
		}
		if (node.getAbsolutePosition().y + node.getHeight() * scale + offset > stage.getHeight()) {
			move.y = stage.getHeight() - (node.getAbsolutePosition().y + node.getHeight() * scale + offset);
		} else if (node.getAbsolutePosition().y < offset) {
			move.y = offset - node.getAbsolutePosition().y;
		}
		stage.transitionTo({
			x: stage.attrs.x + move.x,
			y: stage.attrs.y + move.y,
			duration: 0.4,
			easing: 'ease-in-out'
		});
	});
	mapModel.addEventListener('nodeStyleChanged', function (n) {
		var node = nodeByIdeaId[n.id];
		node.setMMStyle(n.style);
	});
	mapModel.addEventListener('nodeDroppableChanged', function (ideaId, isDroppable) {
		var node = nodeByIdeaId[ideaId];
		node.setIsDroppable(isDroppable);
	});
	mapModel.addEventListener('nodeRemoved', function (n) {
		var node = nodeByIdeaId[n.id];
		delete nodeByIdeaId[n.id];
		node.transitionTo({
			opacity: 0.25,
			duration: 0.4,
			callback: node.remove.bind(node)
		});
		node.off('click dblclick tap dragstart dragmove dragend mouseover mouseout :textChanged :nodeEditRequested');
		mapModel.removeEventListener('nodeEditRequested:' + n.id, node.editNode);
	});
	mapModel.addEventListener('nodeMoved', function (n, reason) {
		var node = nodeByIdeaId[n.id];
		node.transitionTo({
			x: n.x,
			y: n.y,
			duration: 0.4,
			easing: reason === 'failed' ? 'bounce-ease-out' : 'ease-in-out'
		});
	});
	mapModel.addEventListener('nodeTitleChanged', function (n) {
		var node = nodeByIdeaId[n.id];
		node.setText(n.title);
		layer.draw();
	});
	mapModel.addEventListener('connectorCreated', function (n) {
		var connector = new Kinetic.Connector({
			shapeFrom: nodeByIdeaId[n.from],
			shapeTo: nodeByIdeaId[n.to],
			stroke: '#888',
			strokeWidth: 1,
			opacity: 0
		});
		connector.opacity = 0;
		connectorByFromIdeaId_ToIdeaId[connectorKey(n.from, n.to)] = connector;
		layer.add(connector);
		connector.moveToBottom();
		connector.transitionTo({
			opacity: 1,
			duration: 0.4
		});
	});
	mapModel.addEventListener('connectorRemoved', function (n) {
		var key = connectorKey(n.from, n.to),
			connector = connectorByFromIdeaId_ToIdeaId[key];
		delete connectorByFromIdeaId_ToIdeaId[key];
		connector.transitionTo({
			opacity: 0,
			duration: 0.1,
			callback: connector.remove.bind(connector)
		});
	});
	mapModel.addEventListener('mapScaleChanged', function (scaleMultiplier, zoomPoint) {
		var currentScale = stage.getScale().x || 1,
			targetScale = Math.max(Math.min(currentScale * scaleMultiplier, 5), 0.2);
		if (currentScale === targetScale) {
			return;
		}
		zoomPoint = zoomPoint || {x:  0.5 * stage.getWidth(), y: 0.5 * stage.getHeight()};
		stage.transitionTo({
			scale: {
				x: targetScale,
				y: targetScale
			},
			x: zoomPoint.x + (stage.attrs.x - zoomPoint.x) * targetScale / currentScale,
			y: zoomPoint.y + (stage.attrs.y - zoomPoint.y) * targetScale / currentScale,
			duration: 0.1,
			easing: 'ease-in-out',
			callback: function () {
				if (imageRendering) {
					stage.fire(':scaleChangeComplete');
				}
			}
		});
	});
	mapModel.addEventListener('mapMoveRequested', function (deltaX, deltaY) {
		moveStage(deltaX, deltaY);
	});
	(function () {
		var x, y;
		stage.on('dragmove', function () {
			var deltaX = x - stage.attrs.x,
				deltaY = y - stage.attrs.y,
				visibleAfterMove = atLeastOneVisible(nodeByIdeaId, 0, 0) || atLeastOneVisible(connectorByFromIdeaId_ToIdeaId, 0, 0),
				shouldMoveBack = !visibleAfterMove && !(atLeastOneVisible(nodeByIdeaId, deltaX, deltaY) || atLeastOneVisible(connectorByFromIdeaId_ToIdeaId, deltaX, deltaY));
			if (shouldMoveBack) {
				moveStage(deltaX, deltaY);
			} else {
				x = stage.attrs.x;
				y = stage.attrs.y;
			}
		});
	}());
	(function () {
		var keyboardEventHandlers = {
			13: mapModel.addSiblingIdea.bind(mapModel, 'keyboard'),
			8: mapModel.removeSubIdea.bind(mapModel, 'keyboard'),
			9: mapModel.addSubIdea.bind(mapModel, 'keyboard'),
			37: mapModel.selectNodeLeft.bind(mapModel, 'keyboard'),
			38: mapModel.selectNodeUp.bind(mapModel, 'keyboard'),
			39: mapModel.selectNodeRight.bind(mapModel, 'keyboard'),
			40: mapModel.selectNodeDown.bind(mapModel, 'keyboard'),
			46: mapModel.removeSubIdea.bind(mapModel, 'keyboard'),
			32: mapModel.editNode.bind(mapModel, 'keyboard'),
			191: mapModel.toggleCollapse.bind(mapModel, 'keyboard'),
			67: mapModel.mark.bind(mapModel, 'keyboard'),
			80: mapModel.moveMarked.bind(mapModel, 'keyboard')
		}, shiftKeyboardEventHandlers = {
			9: mapModel.insertIntermediate.bind(mapModel, 'keyboard'),
			38: mapModel.toggleCollapse.bind(mapModel, 'keyboard')
		}, metaKeyboardEventHandlers = {
			48: resetStage,
			90: mapModel.undo.bind(mapModel, 'keyboard'),
			89: mapModel.redo.bind(mapModel, 'keyboard'),
			187: mapModel.scaleUp.bind(mapModel, 'keyboard'),
			189: mapModel.scaleDown.bind(mapModel, 'keyboard'),
			38: mapModel.moveRelative.bind(mapModel, 'keyboard', -1),
			40: mapModel.moveRelative.bind(mapModel, 'keyboard', 1),
			88: mapModel.mark.bind(mapModel, 'keyboard'),
			86: mapModel.moveMarked.bind(mapModel, 'keyboard')
		},
			onKeydown = function (evt) {
				var eventHandler = ((evt.metaKey || evt.ctrlKey) ? metaKeyboardEventHandlers :
						(evt.shiftKey ? shiftKeyboardEventHandlers : keyboardEventHandlers))[evt.which];
				if (eventHandler) {
					eventHandler();
					evt.preventDefault();
				}
			},
			onScroll = function (event, delta, deltaX, deltaY) {
				moveStage(-1 * deltaX, deltaY);
				if (event.preventDefault) { // stop the back button
					event.preventDefault();
				}
			};
		jQuery(window).mousewheel(onScroll);
		mapModel.addEventListener('inputEnabledChanged', function (isInputEnabled) {
			jQuery(document)[isInputEnabled ? 'bind' : 'unbind']('keydown', onKeydown);
			jQuery(window)[isInputEnabled ? 'mousewheel' : 'unmousewheel'](onScroll);
		});
		jQuery(document).keydown(onKeydown);
	}());
};
MAPJS.KineticMediator.dimensionProvider = _.memoize(function (title) {
	'use strict';
	var text = new Kinetic.Idea({
		text: title
	});
	return {
		width: text.getWidth(),
		height: text.getHeight()
	};
});
MAPJS.KineticMediator.layoutCalculator = function (idea) {
	'use strict';
	return MAPJS.calculateLayout(idea, MAPJS.KineticMediator.dimensionProvider);
};
<|endoftext|>


<commit_before>var _ = require('./helpers');

var Regularity = function() {

    var _regexpSource       = '',
        _beginning          = '',
        _end                = '',
        _flags              = '',
        _escapedChars       = [ '*', '.', '?', '^', '+', '$', '|', '(', ')', '[', ']', '{', '}' ],
        _specialIdentifiers = {
            'digit'       : '[0-9]',
            'lowercase'   : '[a-z]',
            'uppercase'   : '[A-Z]',
            'letter'      : '[A-Za-z]',
            'alphanumeric': '[A-Za-z0-9]',
            'whitespace'  : '\\s',
            'space'       : ' ',
            'tab'         : '\\t'
        };

    var isArray = function (value) {
        return value &&
            typeof value === 'object' &&
            typeof value.length === 'number' &&
            typeof value.splice === 'function' &&
            !(value.propertyIsEnumerable('length'));
    };

    var _appendPatternChunk = function(str, args) {
        _regexpSource += _createPatternChunk(str, args);
    };

    var _createPatternChunk = function(str, args) {
        return (args === undefined ? str : str.replace("%s", args));
    };

    var _setBeginning = function(str, args) {
        _beginning = _createPatternChunk(str, args);
    };

    var _setEnd = function(str, args) {
        _end = _createPatternChunk(str, args);
    };

    var _interpret = function(args) {
        switch (args.length) {
            case 2:
                return _numberedConstraint(args[0], args[1]);
            case 1:
                return _patternedConstraint(args[0]);
            default:
                throw new Error('_interpret: wrong number of arguments: got ' + args.length + ', expected 1 or 2');
        }
    };

    var _numberedConstraint = function(count, type) {
        return _makeRepeatedPattern(_patternedConstraint(type), count);
    };

    var _patternedConstraint = function(pattern) {
        if (pattern === null) {
            throw new Error('Unrecognized pattern');
        }
        return _translate(_escapeStr(pattern));
    };

    var _escapeStr = function(pattern) {
        return Array.prototype.map.call(pattern, function(character) {
            return ((_escapedChars.indexOf(character) !== -1) ? ('\\' + character) : character);
        }).join('');
    };

    var _translate = function(pattern) {
        return _specialIdentifiers[_singularizeWord(pattern)] || pattern;
    };

    var _singularizeWord = function(word) {
        if ((typeof(word) === 'string') && (word[word.length - 1] === 's')) {
            return word.substring(0, word.length - 1);
        }

        return word;
    };

    var _enableFlag = function(flagIdentifier) {
        _flags += flagIdentifier;
    };

    var _isValidRange = function(range) {
        return (isArray(range) &&
            (range.length === 2) &&
            (typeof(range[0]) === 'number' || typeof(range[1]) === 'number'));
    };

    var _makeRangedPattern = function(pattern, beginning, end) {
        return ('(?:' + pattern +')' + '{' + beginning + ',' + end + '}');
    };

    var _makeRepeatedPattern = function(pattern, count) {
        return ((count === 1) ? pattern : '(?:' + pattern + ')' + '{' + count + '}');
    };

    this.startWith = _.onceOrThrow(function startWith() {
        _setBeginning('^%s', _interpret(arguments));

        return this;
    });

    this.append = function() {
        _appendPatternChunk(_interpret(arguments));

        return this;
    };
    this.then = this.append;

    this.endWith = _.onceOrThrow(function endWith() {
        _setEnd('%s$', _interpret(arguments));

        return this;
    });

    this.maybe = function() {
        _appendPatternChunk('(?:%s)?', _interpret(arguments));

        return this;
    };

    this.oneOf = function() {
        var choices = Array.prototype.slice.call(arguments)
            .map(_escapeStr)
            .map(_translate)
            .join('|');

        _appendPatternChunk('(?:%s)', choices);

        return this;
    };

    this.between = function(range, pattern) {
        if (!_isValidRange(range)) throw new Error("between: you must provide an array of 2 elements, at least one of them must be an integer");

        var beginning = range[0] || '0';
        var end       = range[1] || '';

        _appendPatternChunk(_makeRangedPattern(_interpret([pattern]), beginning, end));

        return this;
    };

    this.zeroOrMore = function() {
        _appendPatternChunk('(?:%s)*', _interpret(arguments));

        return this;
    };

    this.oneOrMore = function() {
        _appendPatternChunk('(?:%s)+', _interpret(arguments));

        return this;
    };

    this.atLeast = function(times, pattern) {
        return this.between([times, null], pattern);
    };

    this.atMost = function(times, pattern) {
        return this.between([null, times], pattern);
    };

    this.insensitive = _.onceOrThrow(function insensitive() {
        _enableFlag('i');

        return this;
    });

    this.global = _.onceOrThrow(function global() {
        _enableFlag('g');

        return this;
    });

    this.multiLine = _.onceOrThrow(function multiLine() {
        _enableFlag('m');

        return this;
    });

    this.done = function() {
        return (new RegExp(_beginning + _regexpSource + _end, _flags));
    };
    this.regexp = this.done;
};

module.exports = exports = Regularity;
<commit_msg>Remove unnecessary check in '_singularizeWord' and refactor a little.<commit_after>var _ = require('./helpers');

var Regularity = function() {

    var _regexpSource       = '',
        _beginning          = '',
        _end                = '',
        _flags              = '',
        _escapedChars       = [ '*', '.', '?', '^', '+', '$', '|', '(', ')', '[', ']', '{', '}' ],
        _specialIdentifiers = {
            'digit'       : '[0-9]',
            'lowercase'   : '[a-z]',
            'uppercase'   : '[A-Z]',
            'letter'      : '[A-Za-z]',
            'alphanumeric': '[A-Za-z0-9]',
            'whitespace'  : '\\s',
            'space'       : ' ',
            'tab'         : '\\t'
        };

    var isArray = function (value) {
        return value &&
            typeof value === 'object' &&
            typeof value.length === 'number' &&
            typeof value.splice === 'function' &&
            !(value.propertyIsEnumerable('length'));
    };

    var _appendPatternChunk = function(str, args) {
        _regexpSource += _createPatternChunk(str, args);
    };

    var _createPatternChunk = function(str, args) {
        return (args === undefined ? str : str.replace("%s", args));
    };

    var _setBeginning = function(str, args) {
        _beginning = _createPatternChunk(str, args);
    };

    var _setEnd = function(str, args) {
        _end = _createPatternChunk(str, args);
    };

    var _interpret = function(args) {
        switch (args.length) {
            case 2:
                return _numberedConstraint(args[0], args[1]);
            case 1:
                return _patternedConstraint(args[0]);
            default:
                throw new Error('_interpret: wrong number of arguments: got ' + args.length + ', expected 1 or 2');
        }
    };

    var _numberedConstraint = function(count, type) {
        return _makeRepeatedPattern(_patternedConstraint(type), count);
    };

    var _patternedConstraint = function(pattern) {
        if (pattern === null) {
            throw new Error('Unrecognized pattern');
        }
        return _translate(_escapeStr(pattern));
    };

    var _escapeStr = function(pattern) {
        return Array.prototype.map.call(pattern, function(character) {
            return ((_escapedChars.indexOf(character) !== -1) ? ('\\' + character) : character);
        }).join('');
    };

    var _translate = function(pattern) {
        return _specialIdentifiers[_singularize(pattern)] || pattern;
    };

    var _singularize = function(word) {
        return ((word[word.length - 1] === 's')
            ? word.substring(0, word.length - 1)
            : word);
    };

    var _enableFlag = function(flagIdentifier) {
        _flags += flagIdentifier;
    };

    var _isValidRange = function(range) {
        return (isArray(range) &&
            (range.length === 2) &&
            (typeof(range[0]) === 'number' || typeof(range[1]) === 'number'));
    };

    var _makeRangedPattern = function(pattern, beginning, end) {
        return ('(?:' + pattern +')' + '{' + beginning + ',' + end + '}');
    };

    var _makeRepeatedPattern = function(pattern, count) {
        return ((count === 1) ? pattern : '(?:' + pattern + ')' + '{' + count + '}');
    };

    this.startWith = _.onceOrThrow(function startWith() {
        _setBeginning('^%s', _interpret(arguments));

        return this;
    });

    this.append = function() {
        _appendPatternChunk(_interpret(arguments));

        return this;
    };
    this.then = this.append;

    this.endWith = _.onceOrThrow(function endWith() {
        _setEnd('%s$', _interpret(arguments));

        return this;
    });

    this.maybe = function() {
        _appendPatternChunk('(?:%s)?', _interpret(arguments));

        return this;
    };

    this.oneOf = function() {
        var choices = Array.prototype.slice.call(arguments)
            .map(_escapeStr)
            .map(_translate)
            .join('|');

        _appendPatternChunk('(?:%s)', choices);

        return this;
    };

    this.between = function(range, pattern) {
        if (!_isValidRange(range)) throw new Error("between: you must provide an array of 2 elements, at least one of them must be an integer");

        var beginning = range[0] || '0';
        var end       = range[1] || '';

        _appendPatternChunk(_makeRangedPattern(_interpret([pattern]), beginning, end));

        return this;
    };

    this.zeroOrMore = function() {
        _appendPatternChunk('(?:%s)*', _interpret(arguments));

        return this;
    };

    this.oneOrMore = function() {
        _appendPatternChunk('(?:%s)+', _interpret(arguments));

        return this;
    };

    this.atLeast = function(times, pattern) {
        return this.between([times, null], pattern);
    };

    this.atMost = function(times, pattern) {
        return this.between([null, times], pattern);
    };

    this.insensitive = _.onceOrThrow(function insensitive() {
        _enableFlag('i');

        return this;
    });

    this.global = _.onceOrThrow(function global() {
        _enableFlag('g');

        return this;
    });

    this.multiLine = _.onceOrThrow(function multiLine() {
        _enableFlag('m');

        return this;
    });

    this.done = function() {
        return (new RegExp(_beginning + _regexpSource + _end, _flags));
    };
    this.regexp = this.done;
};

module.exports = exports = Regularity;
<|endoftext|>


<commit_before>/*global console, document, jQuery, Kinetic*/
var MAPJS = MAPJS || {};
MAPJS.KineticMediator = function (mapModel, stage) {
	'use strict';
	var layer = new Kinetic.Layer(),
		nodeByIdeaId = {},
		connectorByFromIdeaId_ToIdeaId = {},
		connectorKey = function (fromIdeaId, toIdeaId) {
			return fromIdeaId + '_' + toIdeaId;
		};
	stage.add(layer);
	mapModel.addEventListener('nodeCreated', function (n) {
		var node = new Kinetic.Idea({
			level: n.level,
			x: n.x,
			y: n.y,
			text: n.title,
			opacity: 0
		});
		node.on('click tap', mapModel.selectNode.bind(mapModel, n.id));
		node.on('dragstart', function () {
			node.moveToTop();
			node.attrs.shadow.offset = {
				x: 8,
				y: 8
			};
		});
		node.on('dragmove', function () {
			mapModel.nodeDragMove(
				n.id,
				node.attrs.x,
				node.attrs.y
			);
		});
		node.on('dragend', function () {
			node.attrs.shadow.offset = {
				x: 4,
				y: 4
			};
			mapModel.nodeDragEnd(
				n.id,
				node.attrs.x,
				node.attrs.y
			);
		});
		node.on(':textChanged', function (event) {
			mapModel.updateTitle(event.text);
		});
		node.on(':nodeEditRequested', mapModel.editNode);
		mapModel.addEventListener('nodeEditRequested:' + n.id, node.editNode);
		nodeByIdeaId[n.id] = node;
		layer.add(node);
		node.transitionToAndDontStopCurrentTransitions({
			opacity: 1,
			duration: 0.4
		});
	});
	mapModel.addEventListener('nodeSelectionChanged', function (ideaId, isSelected) {
		var node = nodeByIdeaId[ideaId];
		node.setIsSelected(isSelected);
	});
	mapModel.addEventListener('nodeDroppableChanged', function (ideaId, isDroppable) {
		var node = nodeByIdeaId[ideaId];
		node.setIsDroppable(isDroppable);
	});
	mapModel.addEventListener('nodeRemoved', function (n) {
		var node = nodeByIdeaId[n.id];
		delete nodeByIdeaId[n.id];
		node.transitionTo({
			opacity: 0.25,
			duration: 0.4,
			callback: node.remove.bind(node)
		});
		node.off('click dblclick tap dragstart dragmove dragend mouseover mouseout :textChanged :nodeEditRequested');
		mapModel.removeEventListener('nodeEditRequested:' + n.id, node.editNode);
	});
	mapModel.addEventListener('nodeMoved', function (n, reason) {
		var node = nodeByIdeaId[n.id];
		node.transitionTo({
			x: n.x,
			y: n.y,
			duration: 0.4,
			easing: (reason === 'failed' ? 'bounce-ease-out' : 'ease-in-out')
		});
	});
	mapModel.addEventListener('nodeTitleChanged', function (n) {
		var node = nodeByIdeaId[n.id];
		node.setText(n.title);
		layer.draw();
	});
	mapModel.addEventListener('connectorCreated', function (n) {
		var connector = new Kinetic.Connector({
			shapeFrom: nodeByIdeaId[n.from],
			shapeTo: nodeByIdeaId[n.to],
			stroke: '#888',
			strokeWidth: 1,
			opacity: 0
		});
		connector.opacity = 0;
		connectorByFromIdeaId_ToIdeaId[connectorKey(n.from, n.to)] = connector;
		layer.add(connector);
		connector.moveToBottom();
		connector.transitionTo({
			opacity: 1,
			duration: 0.4
		});
	});
	mapModel.addEventListener('connectorRemoved', function (n) {
		var key = connectorKey(n.from, n.to),
			connector = connectorByFromIdeaId_ToIdeaId[key];
		delete connectorByFromIdeaId_ToIdeaId[key];
		connector.transitionTo({
			opacity: 0,
			duration: 0.1,
			callback: connector.remove.bind(connector)
		});
	});
	(function () {
		var keyboardEventHandlers = {
			13: mapModel.addSubIdea.bind(mapModel),
			8: mapModel.removeSubIdea.bind(mapModel),
			37: mapModel.selectNodeLeft.bind(mapModel),
			38: mapModel.selectNodeUp.bind(mapModel),
			39: mapModel.selectNodeRight.bind(mapModel),
			40: mapModel.selectNodeDown.bind(mapModel)
		};
		jQuery(document).keydown(function (evt) {
			var eventHandler = keyboardEventHandlers[evt.which];
			if (eventHandler) {
				eventHandler();
				evt.preventDefault();
			}
		});
	}());
};
MAPJS.KineticMediator.dimensionProvider = function (title) {
	'use strict';
	var text = new Kinetic.Idea({
		text: title
	});
	return {
		width: text.getWidth(),
		height: text.getHeight()
	};
};
<commit_msg>space bar edits node<commit_after>/*global console, document, jQuery, Kinetic*/
var MAPJS = MAPJS || {};
MAPJS.KineticMediator = function (mapModel, stage) {
	'use strict';
	var layer = new Kinetic.Layer(),
		nodeByIdeaId = {},
		connectorByFromIdeaId_ToIdeaId = {},
		connectorKey = function (fromIdeaId, toIdeaId) {
			return fromIdeaId + '_' + toIdeaId;
		};
	stage.add(layer);
	mapModel.addEventListener('nodeCreated', function (n) {
		var node = new Kinetic.Idea({
			level: n.level,
			x: n.x,
			y: n.y,
			text: n.title,
			opacity: 0
		});
		node.on('click tap', mapModel.selectNode.bind(mapModel, n.id));
		node.on('dragstart', function () {
			node.moveToTop();
			node.attrs.shadow.offset = {
				x: 8,
				y: 8
			};
		});
		node.on('dragmove', function () {
			mapModel.nodeDragMove(
				n.id,
				node.attrs.x,
				node.attrs.y
			);
		});
		node.on('dragend', function () {
			node.attrs.shadow.offset = {
				x: 4,
				y: 4
			};
			mapModel.nodeDragEnd(
				n.id,
				node.attrs.x,
				node.attrs.y
			);
		});
		node.on(':textChanged', function (event) {
			mapModel.updateTitle(event.text);
		});
		node.on(':nodeEditRequested', mapModel.editNode);
		mapModel.addEventListener('nodeEditRequested:' + n.id, node.editNode);
		nodeByIdeaId[n.id] = node;
		layer.add(node);
		node.transitionToAndDontStopCurrentTransitions({
			opacity: 1,
			duration: 0.4
		});
	});
	mapModel.addEventListener('nodeSelectionChanged', function (ideaId, isSelected) {
		var node = nodeByIdeaId[ideaId];
		node.setIsSelected(isSelected);
	});
	mapModel.addEventListener('nodeDroppableChanged', function (ideaId, isDroppable) {
		var node = nodeByIdeaId[ideaId];
		node.setIsDroppable(isDroppable);
	});
	mapModel.addEventListener('nodeRemoved', function (n) {
		var node = nodeByIdeaId[n.id];
		delete nodeByIdeaId[n.id];
		node.transitionTo({
			opacity: 0.25,
			duration: 0.4,
			callback: node.remove.bind(node)
		});
		node.off('click dblclick tap dragstart dragmove dragend mouseover mouseout :textChanged :nodeEditRequested');
		mapModel.removeEventListener('nodeEditRequested:' + n.id, node.editNode);
	});
	mapModel.addEventListener('nodeMoved', function (n, reason) {
		var node = nodeByIdeaId[n.id];
		node.transitionTo({
			x: n.x,
			y: n.y,
			duration: 0.4,
			easing: (reason === 'failed' ? 'bounce-ease-out' : 'ease-in-out')
		});
	});
	mapModel.addEventListener('nodeTitleChanged', function (n) {
		var node = nodeByIdeaId[n.id];
		node.setText(n.title);
		layer.draw();
	});
	mapModel.addEventListener('connectorCreated', function (n) {
		var connector = new Kinetic.Connector({
			shapeFrom: nodeByIdeaId[n.from],
			shapeTo: nodeByIdeaId[n.to],
			stroke: '#888',
			strokeWidth: 1,
			opacity: 0
		});
		connector.opacity = 0;
		connectorByFromIdeaId_ToIdeaId[connectorKey(n.from, n.to)] = connector;
		layer.add(connector);
		connector.moveToBottom();
		connector.transitionTo({
			opacity: 1,
			duration: 0.4
		});
	});
	mapModel.addEventListener('connectorRemoved', function (n) {
		var key = connectorKey(n.from, n.to),
			connector = connectorByFromIdeaId_ToIdeaId[key];
		delete connectorByFromIdeaId_ToIdeaId[key];
		connector.transitionTo({
			opacity: 0,
			duration: 0.1,
			callback: connector.remove.bind(connector)
		});
	});
	(function () {
		var keyboardEventHandlers = {
			13: mapModel.addSubIdea.bind(mapModel),
			8: mapModel.removeSubIdea.bind(mapModel),
			37: mapModel.selectNodeLeft.bind(mapModel),
			38: mapModel.selectNodeUp.bind(mapModel),
			39: mapModel.selectNodeRight.bind(mapModel),
			40: mapModel.selectNodeDown.bind(mapModel),
      46: mapModel.removeSubIdea.bind(mapModel), /* DELETE */
      32: mapModel.editNode.bind(mapModel) /* SPACE BAR */
		};
		jQuery(document).keydown(function (evt) {
			var eventHandler = keyboardEventHandlers[evt.which];
			if (eventHandler) {
				eventHandler();
				evt.preventDefault();
			}
		});
	}());
};
MAPJS.KineticMediator.dimensionProvider = function (title) {
	'use strict';
	var text = new Kinetic.Idea({
		text: title
	});
	return {
		width: text.getWidth(),
		height: text.getHeight()
	};
};
<|endoftext|>


<commit_before>/*
  Regularity.js: Regular expressions for humans
  Author: Fernando Martinez de la Cueva
  License: MIT
  Url: https://github.com/oinak/regularity
*/

// souvenirs from Crockford-land:
Function.prototype.method = function(name, func){
  this.prototype[name] = func;
  return this;
};

Function.method('bind', function(that){
  var method = this,
      slice = Array.prototype.slice,
      args = slice.apply(arguments, [1]);
  return function(){
    return method.apply(that,
      args.concat(slice.apply(arguments, [0])));
  };
});

var Regularity = function() {

  /* PRIVATE state and auxiliary data*/
  var _result     = '',
    _flags      = '',
    _escapedChars = [ '*', '.', '?', '^', '+', '$', '|', '(', ')', '[', ']', '{', '}' ],
    _patterns     = {
      'digit'       : '[0-9]',
      'lowercase'   : '[a-z]',
      'uppercase'   : '[A-Z]',
      'letter'      : '[A-Za-z]',
      'alphanumeric': '[A-Za-z0-9]',
      'whitespace'  : '\\s',
      'space'       : ' ',
      'tab'         : '\t'
    };

  /* PRIVATE Helpers */

  //Javascript, the good parts: Douglas Crockford
  var isArray = function (value) {
    return value &&
      typeof value === 'object' &&
      typeof value.length === 'number' &&
      typeof value.splice === 'function' &&
      !(value.propertyIsEnumerable('length'));
  };

  var write = function(str, args){
    //console.log('write(str:'+str+' args:'+JSON.stringify(args)+')');
    var toAdd = ( args === null ? str : str.replace("%s", args ) );
    _result = _result + toAdd;
    return this;
  }.bind(this);

  // Translate/escape characters etc and return regex-ready string
  function interpret(){
    //console.log('  interpret(arguments: '+JSON.stringify(arguments)+')');
    //console.log('  interpret(flat:'+JSON.stringify(flat)+') <flat.length:'+flat.length+'>');
    switch (arguments.length) {
      case 2:
        return numberedConstraint(arguments[0],arguments[1]);
      case 1:
        return patternedConstraint(arguments[0]);
      default:
        var argumentError = new Error('interpret: wrong number of arguments, ' + arguments.length + ' for 1,2');
        throw(argumentError);
    }
  }

  // Ex: (2, 'x') or (3, 'digits')
  function numberedConstraint(count, type){
    var replaced = '',
        pattern = patternedConstraint(type);
    //console.log('    numberedConstraint('+count+', '+type+')');
    replaced = '%s{%c}'.replace('%s', pattern).replace('%c', count);
    return replaced;
  }

  // Ex: ('aa') or ('$')
  function patternedConstraint(pattern){
    //console.log('  patternedConstraint('+JSON.stringify(pattern)+')');
    if (pattern === null){
      var patternError = new Error('Unrecognized pattern');
      throw( patternError );
    }
    return translate( escapeStr(pattern));
  }

  // Escape special regex characters in a string
  // Ex:
  //   escapeStr("one.two")
  //   # => "one\.two"
  function escapeStr(pattern){
    return Array.prototype.map.call(pattern, function(character) {
          return ((_escapedChars.indexOf(character) !== -1) ? ('\\' + character) : character);
    }).join('');
  }

  // Translate an identifier such as 'digits' to [0-9], etc
  // Returns the original identifier if no character class found
  function translate(pattern){
    return _patterns[singularizeWord(pattern)] || pattern;
  }

  // Remove a trailing 's', if there is one
  function singularizeWord(word){
    if ((typeof(word) === 'string') && (word[word.length - 1] === 's')) {
      return word.substring(0, word.length - 1);
    }

    return word;
  }

  function empty(){
    return (_result === '');
  }

  var _enableFlag = function(flagName) {
    if (_flags.indexOf(flagName) !== -1) {
      throw new Error('can\'t enable the "' + flagName + '"" flag more than once!');
    }

    _flags += flagName;
  };

/* PUBLIC OBJECT API: */

  // The line must start with the specified pattern
  this.startWith = function() {
    if (empty()) {
      return write('^%s', interpret(arguments));
    }

    throw new Error('startWith called multiple times');
  };

  // Append a pattern to the end (Also aliased to then)
  this.append = function(){
    return write(interpret(arguments));
  };

  // The line must end with the specified pattern
  this.endWith = function(){
    //console.log('  endWith => arguments:'+JSON.stringify(arguments)+' flat:'+JSON.stringify(flat)+'');
    return write('%s$', interpret(arguments));
  };

  // Zero or one of the specified pattern
  this.maybe = function(){
    return write('%s?', interpret(arguments));
  };

  // Specify an alternation, e.g. one_of(['a', 'b', 'c'])
  this.oneOf = function(){
    var choices = Array.prototype.slice.call(arguments)
      .map(escapeStr) // missing interpret?
      .join('|');

    return write('[%s]', choices);
  };

  //: Specify a bounded repetition, e.g. between([2,4], 'digits')
  this.between = function(range, pattern){
    var values = [];
    if (isArray(range) &&
        range.length === 2 &&
        ( typeof(range[0]) === 'number' || typeof(range[1]) === 'number' ) ){
      values = [interpret(pattern), range[0] || '0', range[1] || '0'];
      var replaced = '%s{%f,%t}'.replace('%s', values[0]).replace('%f', values[1]).replace('%t', values[2]);
      return write(replaced);
    }

    throw new Error("must provide an array of 2 elements, one of them must be an integer");
  };

  // Specify that the pattern or identifer should appear zero or many times
  this.zeroOrMore = function(){
    return write('%s*', interpret(arguments));
  };

  // Specify that the pattern or identifer should appear one or many times
  this.oneOrMore = function(){
    return write('%s+', interpret(arguments));
  };

  // Specify that the pattern or identifer should appear n or more times
  this.atLeast = function(times, pattern){
    return this.between([times, null], pattern); // interpret in between()
  };

  // Specify that the pattern or identifer should appear n or less times
  this.atMost = function(times, pattern){
    return this.between([null, times], pattern); // interpret in between()
  };

  this.insensitive = function(){
    _enableFlag('i');

    return this;
  };

  this.global = function(){
    _enableFlag('g');

    return this;
  };

  this.multiLine = function(){
    _enableFlag('m');

    return this;
  };

  // Return the regexp, aliased as regexp
  this.done = function(){
    return new RegExp( _result, _flags);
  };
  this.regexp = this.done;

};

module.exports = exports = Regularity;

/*
   r = new Regularity();
   r.startWith(3, 'digits').
     append('-').
     append(2, 'letters').
     maybe('#').
     oneOf(['a','b']).
     between([2,4], 'a').
     endWith('$').
     done();
*/
<commit_msg>Remove unnecessary initialization.<commit_after>/*
  Regularity.js: Regular expressions for humans
  Author: Fernando Martinez de la Cueva
  License: MIT
  Url: https://github.com/oinak/regularity
*/

// souvenirs from Crockford-land:
Function.prototype.method = function(name, func){
  this.prototype[name] = func;
  return this;
};

Function.method('bind', function(that){
  var method = this,
      slice = Array.prototype.slice,
      args = slice.apply(arguments, [1]);
  return function(){
    return method.apply(that,
      args.concat(slice.apply(arguments, [0])));
  };
});

var Regularity = function() {

  /* PRIVATE state and auxiliary data*/
  var _result     = '',
    _flags      = '',
    _escapedChars = [ '*', '.', '?', '^', '+', '$', '|', '(', ')', '[', ']', '{', '}' ],
    _patterns     = {
      'digit'       : '[0-9]',
      'lowercase'   : '[a-z]',
      'uppercase'   : '[A-Z]',
      'letter'      : '[A-Za-z]',
      'alphanumeric': '[A-Za-z0-9]',
      'whitespace'  : '\\s',
      'space'       : ' ',
      'tab'         : '\t'
    };

  /* PRIVATE Helpers */

  //Javascript, the good parts: Douglas Crockford
  var isArray = function (value) {
    return value &&
      typeof value === 'object' &&
      typeof value.length === 'number' &&
      typeof value.splice === 'function' &&
      !(value.propertyIsEnumerable('length'));
  };

  var write = function(str, args){
    //console.log('write(str:'+str+' args:'+JSON.stringify(args)+')');
    var toAdd = ( args === null ? str : str.replace("%s", args ) );
    _result = _result + toAdd;
    return this;
  }.bind(this);

  // Translate/escape characters etc and return regex-ready string
  function interpret(){
    //console.log('  interpret(arguments: '+JSON.stringify(arguments)+')');
    //console.log('  interpret(flat:'+JSON.stringify(flat)+') <flat.length:'+flat.length+'>');
    switch (arguments.length) {
      case 2:
        return numberedConstraint(arguments[0],arguments[1]);
      case 1:
        return patternedConstraint(arguments[0]);
      default:
        var argumentError = new Error('interpret: wrong number of arguments, ' + arguments.length + ' for 1,2');
        throw(argumentError);
    }
  }

  // Ex: (2, 'x') or (3, 'digits')
  function numberedConstraint(count, type){
    var replaced = '',
        pattern = patternedConstraint(type);
    //console.log('    numberedConstraint('+count+', '+type+')');
    replaced = '%s{%c}'.replace('%s', pattern).replace('%c', count);
    return replaced;
  }

  // Ex: ('aa') or ('$')
  function patternedConstraint(pattern){
    //console.log('  patternedConstraint('+JSON.stringify(pattern)+')');
    if (pattern === null){
      var patternError = new Error('Unrecognized pattern');
      throw( patternError );
    }
    return translate( escapeStr(pattern));
  }

  // Escape special regex characters in a string
  // Ex:
  //   escapeStr("one.two")
  //   # => "one\.two"
  function escapeStr(pattern){
    return Array.prototype.map.call(pattern, function(character) {
          return ((_escapedChars.indexOf(character) !== -1) ? ('\\' + character) : character);
    }).join('');
  }

  // Translate an identifier such as 'digits' to [0-9], etc
  // Returns the original identifier if no character class found
  function translate(pattern){
    return _patterns[singularizeWord(pattern)] || pattern;
  }

  // Remove a trailing 's', if there is one
  function singularizeWord(word){
    if ((typeof(word) === 'string') && (word[word.length - 1] === 's')) {
      return word.substring(0, word.length - 1);
    }

    return word;
  }

  function empty(){
    return (_result === '');
  }

  var _enableFlag = function(flagName) {
    if (_flags.indexOf(flagName) !== -1) {
      throw new Error('can\'t enable the "' + flagName + '"" flag more than once!');
    }

    _flags += flagName;
  };

/* PUBLIC OBJECT API: */

  // The line must start with the specified pattern
  this.startWith = function() {
    if (empty()) {
      return write('^%s', interpret(arguments));
    }

    throw new Error('startWith called multiple times');
  };

  // Append a pattern to the end (Also aliased to then)
  this.append = function(){
    return write(interpret(arguments));
  };

  // The line must end with the specified pattern
  this.endWith = function(){
    //console.log('  endWith => arguments:'+JSON.stringify(arguments)+' flat:'+JSON.stringify(flat)+'');
    return write('%s$', interpret(arguments));
  };

  // Zero or one of the specified pattern
  this.maybe = function(){
    return write('%s?', interpret(arguments));
  };

  // Specify an alternation, e.g. one_of(['a', 'b', 'c'])
  this.oneOf = function(){
    var choices = Array.prototype.slice.call(arguments)
      .map(escapeStr) // missing interpret?
      .join('|');

    return write('[%s]', choices);
  };

  //: Specify a bounded repetition, e.g. between([2,4], 'digits')
  this.between = function(range, pattern){
    if (isArray(range) &&
        range.length === 2 &&
        ( typeof(range[0]) === 'number' || typeof(range[1]) === 'number' ) ){
      var values = [interpret(pattern), range[0] || '0', range[1] || '0'];
      var replaced = '%s{%f,%t}'.replace('%s', values[0]).replace('%f', values[1]).replace('%t', values[2]);
      return write(replaced);
    }

    throw new Error("must provide an array of 2 elements, one of them must be an integer");
  };

  // Specify that the pattern or identifer should appear zero or many times
  this.zeroOrMore = function(){
    return write('%s*', interpret(arguments));
  };

  // Specify that the pattern or identifer should appear one or many times
  this.oneOrMore = function(){
    return write('%s+', interpret(arguments));
  };

  // Specify that the pattern or identifer should appear n or more times
  this.atLeast = function(times, pattern){
    return this.between([times, null], pattern); // interpret in between()
  };

  // Specify that the pattern or identifer should appear n or less times
  this.atMost = function(times, pattern){
    return this.between([null, times], pattern); // interpret in between()
  };

  this.insensitive = function(){
    _enableFlag('i');

    return this;
  };

  this.global = function(){
    _enableFlag('g');

    return this;
  };

  this.multiLine = function(){
    _enableFlag('m');

    return this;
  };

  // Return the regexp, aliased as regexp
  this.done = function(){
    return new RegExp( _result, _flags);
  };
  this.regexp = this.done;

};

module.exports = exports = Regularity;

/*
   r = new Regularity();
   r.startWith(3, 'digits').
     append('-').
     append(2, 'letters').
     maybe('#').
     oneOf(['a','b']).
     between([2,4], 'a').
     endWith('$').
     done();
*/
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2016 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.
 */
'use strict';

const execSync = require('child_process').execSync;
const gs = require('glob-stream');
const path = require('path');
const process = require('process');

const env = process.env;

const Base = require('../Base.es6.js');
const Memo = require('../memo/Memo.es6.js');
const MemoBuilder = require('../memo/MemoBuilder.es6.js');
const memos = require('./memos.es6.js');

// TODO: Copied from webidl2-js. Consolidate into exposed function from
// webidl2-js?
const fSort = (fs, a, b) => {
  let aValue;
  let bValue;
  for (let i = 0; i < fs.length; i++) {
    aValue = fs[i](a);
    bValue = fs[i](b);
    if (aValue !== undefined && bValue !== undefined) break;
  }
  if (aValue < bValue) return -1;
  else if (aValue > bValue) return 1;
  return 0;
};
const sortParses = fSort.bind(this, [
  v => v.name, v => v.implementer, v => v.constructor.name
]);
const sortParseCollections = fSort.bind(this, [v => v.url]);

class BlinkRunner extends Base {
  init(opts) {
    super.init(opts);
    this.running = false;
    this.eachFilePromises = [];

    //
    // Setup custom memos tailored to this runner.
    //

    // Input: [{output: url, delegates: [array-of-parses]}].
    // Output: data/idl/blink JSON blob:
    //         [{parses, url}].
    this.store = new Memo({
      getKey: () => `blink_${this.blinkHash}`,
      cache: memos.ppcache('webidl-parses-by-url'),
      f: fileResults => {
        return fileResults.map(result => {
          // TODO: Should be a more elegant way to manage this.
          const path = result.output.match(/(third_party\/WebKit.*)$/)[1];

          return {
            url: `https://chromium.googlesource.com/chromium/src.git/+/${this.blinkHash}/${path}`,
            parses: result.delegates[0].sort(sortParses),
          };
        }).sort(sortParseCollections);
      }
    });
  }

  collapse(a) {
    if (!(Array.isArray(a) && a.every(i => Array.isArray(i))))
      return a;

    return a.reduce((acc, i) => acc.concat(this.collapse(i)), []);
  }

  configure(argv) {
    memos.configure(argv);
    this.blinkPath = path.resolve(argv['blink-dir']);
  }

  run() {
    // TODO: Do something more robust?
    if (this.running) throw new Error('BlinkRunner already running');
    this.running = true;

    // Clear runner-output-data.
    this.eachFilePromises = [];
    this.fileResults = [];

    this.blinkHash = this.getBlinkHash();

    // Compose fresh set of memos for main pipeline computation(s).
    this.pipeline = this.createPipeline();

    // Find files for pipeline inputs
    this.globStream = gs.create(`${this.blinkPath}/**/*.idl`, {
      ignore: '**/bindings/tests/**',
    });
    this.globStream.on(
      'data',
      file => this.processFile(file.path.substr(`${this.blinkPath}/`.length))
    );

    // Return Promise handled by streaming input event listeners.
    return new Promise((resolve, reject) => {
      this.globStream.on('error', error => {
        return Promise.resolve(this.onError(resolve, reject, error));
      });
      this.globStream.on('end', data => {
        return Promise.all(this.eachFilePromises).then(
          this.onDataReady.bind(this, resolve, reject, data),
          this.onError.bind(this, resolve, reject)
        );
      });
    });
  }

  getBlinkHash() {
    return execSync(
      'git rev-parse HEAD',
      {shell: env.SHELL, cwd: this.blinkPath}
    ).toString().split('\n')[0];
  }

  createPipeline() {
    const parseWebIDL = memos.parseWebIDL();
    const readFile = memos.readFile();

    const builder = new MemoBuilder();

    return builder.start()
      .keep()
      .then(readFile)
      .then(parseWebIDL)
      .keep()
      .build();
  }

  // Invoke pipeline over each individual file, then add results to
  // runner-output-data variables.
  processFile(file) {
    const promise = this.pipeline.runAll(`${this.blinkPath}/${file}`).then(
      output => this.fileResults.push(output)
    );
    this.eachFilePromises.push(promise);
    return promise;
  }

  // Store data in the appropriate format using file-backed cache in this.store.
  onDataReady(resolve, reject, data) {
    this.logger.log('Storing data from ${this.fileResults.length} files');
    return this.store.run(this.fileResults).then(resolve, reject);
  }

  // Deal with glob-stream errors.
  onError(resolve, reject, error) {
    return reject(error);
  }
}

module.exports = BlinkRunner;
<commit_msg>Add to ignore patterns for Blink IDL file listing<commit_after>/**
 * @license
 * Copyright 2016 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.
 */
'use strict';

const execSync = require('child_process').execSync;
const gs = require('glob-stream');
const path = require('path');
const process = require('process');

const env = process.env;

const Base = require('../Base.es6.js');
const Memo = require('../memo/Memo.es6.js');
const MemoBuilder = require('../memo/MemoBuilder.es6.js');
const memos = require('./memos.es6.js');

// TODO: Copied from webidl2-js. Consolidate into exposed function from
// webidl2-js?
const fSort = (fs, a, b) => {
  let aValue;
  let bValue;
  for (let i = 0; i < fs.length; i++) {
    aValue = fs[i](a);
    bValue = fs[i](b);
    if (aValue !== undefined && bValue !== undefined) break;
  }
  if (aValue < bValue) return -1;
  else if (aValue > bValue) return 1;
  return 0;
};
const sortParses = fSort.bind(this, [
  v => v.name, v => v.implementer, v => v.constructor.name
]);
const sortParseCollections = fSort.bind(this, [v => v.url]);

class BlinkRunner extends Base {
  init(opts) {
    super.init(opts);
    this.running = false;
    this.eachFilePromises = [];

    //
    // Setup custom memos tailored to this runner.
    //

    // Input: [{output: url, delegates: [array-of-parses]}].
    // Output: data/idl/blink JSON blob:
    //         [{parses, url}].
    this.store = new Memo({
      getKey: () => `blink_${this.blinkHash}`,
      cache: memos.ppcache('webidl-parses-by-url'),
      f: fileResults => {
        return fileResults.map(result => {
          // TODO: Should be a more elegant way to manage this.
          const path = result.output.match(/(third_party\/WebKit.*)$/)[1];

          return {
            url: `https://chromium.googlesource.com/chromium/src.git/+/${this.blinkHash}/${path}`,
            parses: result.delegates[0].sort(sortParses),
          };
        }).sort(sortParseCollections);
      }
    });
  }

  collapse(a) {
    if (!(Array.isArray(a) && a.every(i => Array.isArray(i))))
      return a;

    return a.reduce((acc, i) => acc.concat(this.collapse(i)), []);
  }

  configure(argv) {
    memos.configure(argv);
    this.blinkPath = path.resolve(argv['blink-dir']);
  }

  run() {
    // TODO: Do something more robust?
    if (this.running) throw new Error('BlinkRunner already running');
    this.running = true;

    // Clear runner-output-data.
    this.eachFilePromises = [];
    this.fileResults = [];

    this.blinkHash = this.getBlinkHash();

    // Compose fresh set of memos for main pipeline computation(s).
    this.pipeline = this.createPipeline();

    // Find files for pipeline inputs
    this.globStream = gs.create(`${this.blinkPath}/Source/**/*.idl`, {
      ignore: [
        '**/Source/core/testing/**',
        '**/Source/modules/**/testing/**',
        '**/bindings/tests/**',
      ],
    });
    this.globStream.on(
      'data',
      file => this.processFile(file.path.substr(`${this.blinkPath}/`.length))
    );

    // Return Promise handled by streaming input event listeners.
    return new Promise((resolve, reject) => {
      this.globStream.on('error', error => {
        return Promise.resolve(this.onError(resolve, reject, error));
      });
      this.globStream.on('end', data => {
        return Promise.all(this.eachFilePromises).then(
          this.onDataReady.bind(this, resolve, reject, data),
          this.onError.bind(this, resolve, reject)
        );
      });
    });
  }

  getBlinkHash() {
    return execSync(
      'git rev-parse HEAD',
      {shell: env.SHELL, cwd: this.blinkPath}
    ).toString().split('\n')[0];
  }

  createPipeline() {
    const parseWebIDL = memos.parseWebIDL();
    const readFile = memos.readFile();

    const builder = new MemoBuilder();

    return builder.start()
      .keep()
      .then(readFile)
      .then(parseWebIDL)
      .keep()
      .build();
  }

  // Invoke pipeline over each individual file, then add results to
  // runner-output-data variables.
  processFile(file) {
    const promise = this.pipeline.runAll(`${this.blinkPath}/${file}`).then(
      output => this.fileResults.push(output)
    );
    this.eachFilePromises.push(promise);
    return promise;
  }

  // Store data in the appropriate format using file-backed cache in this.store.
  onDataReady(resolve, reject, data) {
    this.logger.log('Storing data from ${this.fileResults.length} files');
    return this.store.run(this.fileResults).then(resolve, reject);
  }

  // Deal with glob-stream errors.
  onError(resolve, reject, error) {
    return reject(error);
  }
}

module.exports = BlinkRunner;
<|endoftext|>


<commit_before>import { Component } from 'substance'

const DISABLED = { disabled: true }

/**
 * A component that renders a group of tools.
 *
 * @param {string} props.name
 * @param {string} props.style
 * @param {string} props.theme
 * @param {boolean} props.hideDisabled
 * @param {array} props.items array of item specifications
 * @param {object} props.commandStates command states by name
 */
export default class ToolGroup extends Component {
  constructor (...args) {
    super(...args)

    this._deriveState(this.props)
  }

  willReceiveProps (newProps) {
    this._deriveState(newProps)
  }

  getTheme () {
    // HACK: falling back to 'light' in a hard-coded way
    return this.props.theme || 'light'
  }

  _deriveState (props) {
    if (this._isTopLevel) {
      this._derivedState = this._deriveGroupState(this.props, this.props.commandStates)
    } else {
      this._derivedState = props.itemState
    }
  }

  render ($$) {
    const { name, hideDisabled } = this.props
    let el = $$('div')
      .addClass(this._getClassNames())
      .addClass('sm-' + name)

    let hasEnabledItem = this._derivedState.hasEnabledItem
    if (hasEnabledItem || !hideDisabled) {
      el.append(this._renderLabel($$))
      el.append(this._renderItems($$))
    }

    return el
  }

  _renderLabel ($$) {
    // EXPERIMENTAL: showing a ToolGroup label an
    const { style, label } = this.props
    if (style === 'descriptive' && label) {
      const SeparatorClass = this.getComponent('tool-separator')
      return $$(SeparatorClass, { label })
    }
  }

  _renderItems ($$) {
    const { style, hideDisabled, commandStates } = this.props
    const theme = this.getTheme()
    const { itemStates } = this._derivedState
    let els = []
    for (let itemState of itemStates) {
      let item = itemState.item
      let type = item.type
      switch (type) {
        case 'command': {
          const commandName = item.name
          let commandState = itemState.commandState
          if (itemState.enabled || !hideDisabled) {
            let ToolClass = this._getToolClass(item)
            els.push(
              $$(ToolClass, {
                item,
                commandState,
                style,
                theme
              }).ref(commandName)
            )
          }
          break
        }
        case 'separator': {
          let ToolSeparator = this.getComponent('tool-separator')
          els.push(
            $$(ToolSeparator, item)
          )
          break
        }
        default: {
          let ToolClass = this._getToolClass(item)
          els.push(
            $$(ToolClass, Object.assign({}, item, {
              commandStates,
              itemState,
              theme
            })).ref(item.name)
          )
        }
      }
    }
    return els
  }

  get _isTopLevel () { return false }

  // ATTENTION: this is only called for top-level tool groups (Menu, Prompt, ) which are ToolDrop
  _deriveGroupState (group, commandStates) {
    let itemStates = group.items.map(item => this._deriveItemState(item, commandStates))
    let hasEnabledItem = itemStates.some(item => item.enabled)
    return {
      item: group,
      itemStates,
      hasEnabledItem
    }
  }

  _deriveItemState (item, commandStates) {
    switch (item.type) {
      case 'command': {
        let commandState = commandStates[item.name] || DISABLED
        return {
          item,
          commandState,
          enabled: !commandState.disabled
        }
      }
      case 'group':
      case 'dropdown':
      case 'prompt':
      case 'switcher': {
        return this._deriveGroupState(item, commandStates)
      }
      case 'separator': {
        return { item }
      }
      default:
        throw new Error('Unsupported item type')
    }
  }

  _getClassNames () {
    return 'sc-tool-group'
  }

  _getToolClass (item) {
    // use an ToolClass from toolSpec if configured inline in ToolGroup spec
    let ToolClass
    if (item.ToolClass) {
      ToolClass = item.ToolClass
    } else {
      switch (item.type) {
        case 'command': {
          // that should should be just a tool, and in a minimal icon based toolbar
          // it looks like a toggle-tool, in a menu or dropdown it would look more descriptive
          ToolClass = this.getComponent('tool')
          break
        }
        case 'dropdown': {
          ToolClass = this.getComponent('tool-dropdown')
          break
        }
        case 'group': {
          ToolClass = this.getComponent('tool-group')
          break
        }
        case 'prompt': {
          ToolClass = this.getComponent('tool-prompt')
          break
        }
        case 'separator': {
          ToolClass = this.getComponent('tool-separator')
          break
        }
        // TODO: IMO this is a custom Tool, and should instead be used via ToolClass
        case 'switcher': {
          ToolClass = this.getComponent('tool-switcher')
          break
        }
        default: {
          console.error('Unsupported item type inside ToolGroup:', item.type)
        }
      }
    }

    return ToolClass
  }
}
<commit_msg>Fix regression.<commit_after>import { Component } from 'substance'

const DISABLED = { disabled: true }

/**
 * A component that renders a group of tools.
 *
 * @param {string} props.name
 * @param {string} props.style
 * @param {string} props.theme
 * @param {boolean} props.hideDisabled
 * @param {array} props.items array of item specifications
 * @param {object} props.commandStates command states by name
 */
export default class ToolGroup extends Component {
  constructor (...args) {
    super(...args)

    this._deriveState(this.props)
  }

  willReceiveProps (newProps) {
    this._deriveState(newProps)
  }

  getTheme () {
    // HACK: falling back to 'light' in a hard-coded way
    return this.props.theme || 'light'
  }

  _deriveState (props) {
    if (this._isTopLevel) {
      this._derivedState = this._deriveGroupState(props, props.commandStates)
    } else {
      this._derivedState = props.itemState
    }
  }

  render ($$) {
    const { name, hideDisabled } = this.props
    let el = $$('div')
      .addClass(this._getClassNames())
      .addClass('sm-' + name)

    let hasEnabledItem = this._derivedState.hasEnabledItem
    if (hasEnabledItem || !hideDisabled) {
      el.append(this._renderLabel($$))
      el.append(this._renderItems($$))
    }

    return el
  }

  _renderLabel ($$) {
    // EXPERIMENTAL: showing a ToolGroup label an
    const { style, label } = this.props
    if (style === 'descriptive' && label) {
      const SeparatorClass = this.getComponent('tool-separator')
      return $$(SeparatorClass, { label })
    }
  }

  _renderItems ($$) {
    const { style, hideDisabled, commandStates } = this.props
    const theme = this.getTheme()
    const { itemStates } = this._derivedState
    let els = []
    for (let itemState of itemStates) {
      let item = itemState.item
      let type = item.type
      switch (type) {
        case 'command': {
          const commandName = item.name
          let commandState = itemState.commandState
          if (itemState.enabled || !hideDisabled) {
            let ToolClass = this._getToolClass(item)
            els.push(
              $$(ToolClass, {
                item,
                commandState,
                style,
                theme
              }).ref(commandName)
            )
          }
          break
        }
        case 'separator': {
          let ToolSeparator = this.getComponent('tool-separator')
          els.push(
            $$(ToolSeparator, item)
          )
          break
        }
        default: {
          let ToolClass = this._getToolClass(item)
          els.push(
            $$(ToolClass, Object.assign({}, item, {
              commandStates,
              itemState,
              theme
            })).ref(item.name)
          )
        }
      }
    }
    return els
  }

  get _isTopLevel () { return false }

  // ATTENTION: this is only called for top-level tool groups (Menu, Prompt, ) which are ToolDrop
  _deriveGroupState (group, commandStates) {
    let itemStates = group.items.map(item => this._deriveItemState(item, commandStates))
    let hasEnabledItem = itemStates.some(item => item.enabled)
    return {
      item: group,
      itemStates,
      hasEnabledItem
    }
  }

  _deriveItemState (item, commandStates) {
    switch (item.type) {
      case 'command': {
        let commandState = commandStates[item.name] || DISABLED
        return {
          item,
          commandState,
          enabled: !commandState.disabled
        }
      }
      case 'group':
      case 'dropdown':
      case 'prompt':
      case 'switcher': {
        return this._deriveGroupState(item, commandStates)
      }
      case 'separator': {
        return { item }
      }
      default:
        throw new Error('Unsupported item type')
    }
  }

  _getClassNames () {
    return 'sc-tool-group'
  }

  _getToolClass (item) {
    // use an ToolClass from toolSpec if configured inline in ToolGroup spec
    let ToolClass
    if (item.ToolClass) {
      ToolClass = item.ToolClass
    } else {
      switch (item.type) {
        case 'command': {
          // try to use a tool registered by the same name as the command
          ToolClass = this.getComponent(item.name, 'no-throw')
          if (!ToolClass) {
            // using the default tool otherwise
            ToolClass = this.getComponent('tool')
          }
          break
        }
        case 'dropdown': {
          ToolClass = this.getComponent('tool-dropdown')
          break
        }
        case 'group': {
          ToolClass = this.getComponent('tool-group')
          break
        }
        case 'prompt': {
          ToolClass = this.getComponent('tool-prompt')
          break
        }
        case 'separator': {
          ToolClass = this.getComponent('tool-separator')
          break
        }
        // TODO: IMO this is a custom Tool, and should instead be used via ToolClass
        case 'switcher': {
          ToolClass = this.getComponent('tool-switcher')
          break
        }
        default: {
          console.error('Unsupported item type inside ToolGroup:', item.type)
        }
      }
    }

    return ToolClass
  }
}
<|endoftext|>


<commit_before>/*
Language: Erlang
Description: Erlang is a general-purpose functional language, with strict evaluation, single assignment, and dynamic typing.
Author: Nikolay Zakharov <nikolay.desh@gmail.com>, Dmitry Kovega <arhibot@gmail.com>
*/

hljs.LANGUAGES.erlang = function(){
  var BASIC_ATOM_RE = '[a-z\'][a-zA-Z0-9_\']*';
  var FUNCTION_NAME_RE = '(' + BASIC_ATOM_RE + ':' + BASIC_ATOM_RE + '|' + BASIC_ATOM_RE + ')';
  var ERLANG_RESERVED = {
    keyword:
      'after and andalso|10 band begin bnot bor bsl bzr bxor case catch cond div end fun let ' +
      'not of orelse|10 query receive rem try when xor',
    literal:
      'false true'
  };

  var COMMENT = {
    className: 'comment',
    begin: '%', end: '$',
    relevance: 0
  };
  var NUMBER = {
    className: 'number',
    begin: '\\b(\\d+#[a-fA-F0-9]+|\\d+(\\.\\d+)?([eE][-+]?\\d+)?)',
    relevance: 0
  };
  var NAMED_FUN = {
    begin: 'fun\\s+' + BASIC_ATOM_RE + '/\\d+'
  };
  var FUNCTION_CALL = {
    begin: FUNCTION_NAME_RE + '\\(', end: '\\)',
    returnBegin: true,
    relevance: 0,
    contains: [
      {
        className: 'function_name', begin: FUNCTION_NAME_RE,
        relevance: 0
      },
      {
        begin: '\\(', end: '\\)', endsWithParent: true,
        returnEnd: true,
        relevance: 0
        // "contains" defined later
      }
    ]
  };
  var TUPLE = {
    className: 'tuple',
    begin: '{', end: '}',
    relevance: 0
    // "contains" defined later
  };
  var VAR1 = {
    className: 'variable',
    begin: '\\b_([A-Z][A-Za-z0-9_]*)?',
    relevance: 0
  };
  var VAR2 = {
    className: 'variable',
    begin: '[A-Z][a-zA-Z0-9_]*',
    relevance: 0
  };
  var RECORD_ACCESS = {
    begin: '#', end: '}',
    illegal: '.',
    relevance: 0,
    returnBegin: true,
    contains: [
      {
        className: 'record_name',
        begin: '#' + hljs.UNDERSCORE_IDENT_RE,
        relevance: 0
      },
      {
        begin: '{', endsWithParent: true,
        relevance: 0
        // "contains" defined later
      }
    ]
  };

  var BLOCK_STATEMENTS = {
    keywords: ERLANG_RESERVED,
    begin: '(fun|receive|if|try|case)', end: 'end'
  };
  BLOCK_STATEMENTS.contains = [
    COMMENT,
    NAMED_FUN,
    hljs.inherit(hljs.APOS_STRING_MODE, {className: ''}),
    BLOCK_STATEMENTS,
    FUNCTION_CALL,
    hljs.QUOTE_STRING_MODE,
    NUMBER,
    TUPLE,
    VAR1, VAR2,
    RECORD_ACCESS
  ];

  var BASIC_MODES = [
    COMMENT,
    NAMED_FUN,
    BLOCK_STATEMENTS,
    FUNCTION_CALL,
    hljs.QUOTE_STRING_MODE,
    NUMBER,
    TUPLE,
    VAR1, VAR2,
    RECORD_ACCESS
  ];
  FUNCTION_CALL.contains[1].contains = BASIC_MODES;
  TUPLE.contains = BASIC_MODES;
  RECORD_ACCESS.contains[1].contains = BASIC_MODES;

  var PARAMS = {
    className: 'params',
    begin: '\\(', end: '\\)',
    contains: BASIC_MODES
  };
  return {
    defaultMode: {
      keywords: ERLANG_RESERVED,
      illegal: '(</|\\*=|\\+=|-=|/=|/\\*|\\*/|\\(\\*|\\*\\))',
      contains: [
        {
          className: 'function',
          begin: '^' + BASIC_ATOM_RE + '\\(', end: '->',
          returnBegin: true,
          illegal: '\\(|#|//',
          contains: [
            PARAMS,
            {
              className: 'title', begin: BASIC_ATOM_RE
            }
          ],
          starts: {
            end: ';|\\.',
            keywords: ERLANG_RESERVED,
            contains: BASIC_MODES
          }
        },
        COMMENT,
        {
          className: 'pp',
          begin: '^-', end: '\\.',
          relevance: 0,
          excludeEnd: true,
          returnBegin: true,
          lexems: '-' + hljs.IDENT_RE,
          keywords:
            '-module -record -undef -export -ifdef -ifndef -author -copyright -doc -vsn ' +
            '-import -include -include_lib -compile -define -else -endif -file -behaviour ' +
            '-behavior',
          contains: [PARAMS]
        },
        NUMBER,
        hljs.QUOTE_STRING_MODE,
        RECORD_ACCESS,
        VAR1, VAR2,
        TUPLE
      ]
    }
  };
}();
<commit_msg>Remove opening parenthesis from the Erlang function definition and illegalize more characters.<commit_after>/*
Language: Erlang
Description: Erlang is a general-purpose functional language, with strict evaluation, single assignment, and dynamic typing.
Author: Nikolay Zakharov <nikolay.desh@gmail.com>, Dmitry Kovega <arhibot@gmail.com>
*/

hljs.LANGUAGES.erlang = function(){
  var BASIC_ATOM_RE = '[a-z\'][a-zA-Z0-9_\']*';
  var FUNCTION_NAME_RE = '(' + BASIC_ATOM_RE + ':' + BASIC_ATOM_RE + '|' + BASIC_ATOM_RE + ')';
  var ERLANG_RESERVED = {
    keyword:
      'after and andalso|10 band begin bnot bor bsl bzr bxor case catch cond div end fun let ' +
      'not of orelse|10 query receive rem try when xor',
    literal:
      'false true'
  };

  var COMMENT = {
    className: 'comment',
    begin: '%', end: '$',
    relevance: 0
  };
  var NUMBER = {
    className: 'number',
    begin: '\\b(\\d+#[a-fA-F0-9]+|\\d+(\\.\\d+)?([eE][-+]?\\d+)?)',
    relevance: 0
  };
  var NAMED_FUN = {
    begin: 'fun\\s+' + BASIC_ATOM_RE + '/\\d+'
  };
  var FUNCTION_CALL = {
    begin: FUNCTION_NAME_RE + '\\(', end: '\\)',
    returnBegin: true,
    relevance: 0,
    contains: [
      {
        className: 'function_name', begin: FUNCTION_NAME_RE,
        relevance: 0
      },
      {
        begin: '\\(', end: '\\)', endsWithParent: true,
        returnEnd: true,
        relevance: 0
        // "contains" defined later
      }
    ]
  };
  var TUPLE = {
    className: 'tuple',
    begin: '{', end: '}',
    relevance: 0
    // "contains" defined later
  };
  var VAR1 = {
    className: 'variable',
    begin: '\\b_([A-Z][A-Za-z0-9_]*)?',
    relevance: 0
  };
  var VAR2 = {
    className: 'variable',
    begin: '[A-Z][a-zA-Z0-9_]*',
    relevance: 0
  };
  var RECORD_ACCESS = {
    begin: '#', end: '}',
    illegal: '.',
    relevance: 0,
    returnBegin: true,
    contains: [
      {
        className: 'record_name',
        begin: '#' + hljs.UNDERSCORE_IDENT_RE,
        relevance: 0
      },
      {
        begin: '{', endsWithParent: true,
        relevance: 0
        // "contains" defined later
      }
    ]
  };

  var BLOCK_STATEMENTS = {
    keywords: ERLANG_RESERVED,
    begin: '(fun|receive|if|try|case)', end: 'end'
  };
  BLOCK_STATEMENTS.contains = [
    COMMENT,
    NAMED_FUN,
    hljs.inherit(hljs.APOS_STRING_MODE, {className: ''}),
    BLOCK_STATEMENTS,
    FUNCTION_CALL,
    hljs.QUOTE_STRING_MODE,
    NUMBER,
    TUPLE,
    VAR1, VAR2,
    RECORD_ACCESS
  ];

  var BASIC_MODES = [
    COMMENT,
    NAMED_FUN,
    BLOCK_STATEMENTS,
    FUNCTION_CALL,
    hljs.QUOTE_STRING_MODE,
    NUMBER,
    TUPLE,
    VAR1, VAR2,
    RECORD_ACCESS
  ];
  FUNCTION_CALL.contains[1].contains = BASIC_MODES;
  TUPLE.contains = BASIC_MODES;
  RECORD_ACCESS.contains[1].contains = BASIC_MODES;

  var PARAMS = {
    className: 'params',
    begin: '\\(', end: '\\)',
    contains: BASIC_MODES
  };
  return {
    defaultMode: {
      keywords: ERLANG_RESERVED,
      illegal: '(</|\\*=|\\+=|-=|/=|/\\*|\\*/|\\(\\*|\\*\\))',
      contains: [
        {
          className: 'function',
          begin: '^' + BASIC_ATOM_RE, end: '->',
          returnBegin: true,
          illegal: '\\(|#|//|/\\*|\\\\|:',
          contains: [
            PARAMS,
            {
              className: 'title', begin: BASIC_ATOM_RE
            }
          ],
          starts: {
            end: ';|\\.',
            keywords: ERLANG_RESERVED,
            contains: BASIC_MODES
          }
        },
        COMMENT,
        {
          className: 'pp',
          begin: '^-', end: '\\.',
          relevance: 0,
          excludeEnd: true,
          returnBegin: true,
          lexems: '-' + hljs.IDENT_RE,
          keywords:
            '-module -record -undef -export -ifdef -ifndef -author -copyright -doc -vsn ' +
            '-import -include -include_lib -compile -define -else -endif -file -behaviour ' +
            '-behavior',
          contains: [PARAMS]
        },
        NUMBER,
        hljs.QUOTE_STRING_MODE,
        RECORD_ACCESS,
        VAR1, VAR2,
        TUPLE
      ]
    }
  };
}();
<|endoftext|>