<commit_before>(function() {
	"use strict";

	var root = this,
		Chart = root.Chart,
		helpers = Chart.helpers;

	// The layout service is ver self explanatory.  It's responsible for the layout within a chart.
	// Scales, Legends and Plugins all rely on the layout service and can easily register to be placed anywhere they need
	// It is this service's responsibility of carrying out that layout.
	Chart.layoutService = {
		defaults: {},

		// Register a box to a chartInstance. A box is simply a reference to an object that requires layout. eg. Scales, Legend, Plugins.
		addBox: function(chartInstance, box) {
			if (!chartInstance.boxes) {
				chartInstance.boxes = [];
			}
			chartInstance.boxes.push(box);
		},

		removeBox: function(chartInstance, box) {
			if (!chartInstance.boxes) {
				return;
			}
			chartInstance.boxes.splice(chartInstance.boxes.indexOf(box), 1);
		},

		// The most important function
		update: function(chartInstance, width, height) {

			if (!chartInstance) {
				return;
			}

			var xPadding = width > 30 ? 5 : 2;
			var yPadding = height > 30 ? 5 : 2;

			var leftBoxes = helpers.where(chartInstance.boxes, function(box) {
				return box.options.position == "left";
			});
			var rightBoxes = helpers.where(chartInstance.boxes, function(box) {
				return box.options.position == "right";
			});
			var topBoxes = helpers.where(chartInstance.boxes, function(box) {
				return box.options.position == "top";
			});
			var bottomBoxes = helpers.where(chartInstance.boxes, function(box) {
				return box.options.position == "bottom";
			});

			// Boxes that overlay the chartarea such as the radialLinear scale
			var chartAreaBoxes = helpers.where(chartInstance.boxes, function(box) {
				return box.options.position == "chartArea";
			});

			function fullWidthSorter(a, b) {

			}

			// Ensure that full width boxes are at the very top / bottom
			topBoxes.sort(function(a, b) {
				return (b.options.fullWidth ? 1 : 0) - (a.options.fullWidth ? 1 : 0);
			});
			bottomBoxes.sort(function(a, b) {
				return (a.options.fullWidth ? 1 : 0) - (b.options.fullWidth ? 1 : 0);
			});

			// Essentially we now have any number of boxes on each of the 4 sides.
			// Our canvas looks like the following.
			// The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and
			// B1 is the bottom axis
			// There are also 4 quadrant-like locations (left to right instead of clockwise) reserved for chart overlays
			// These locations are single-box locations only, when trying to register a chartArea location that is already taken,
			// an error will be thrown.
			//
			// |----------------------------------------------------|
			// |                  T1 (Full Width)                   |
			// |----------------------------------------------------|
			// |    |    |                 T2                  |    |
			// |    |----|-------------------------------------|----|
			// |    |    | C1 |                           | C2 |    |
			// |    |    |----|                           |----|    |
			// |    |    |                                     |    |
			// | L1 | L2 |           ChartArea (C0)            | R1 |
			// |    |    |                                     |    |
			// |    |    |----|                           |----|    |
			// |    |    | C3 |                           | C4 |    |
			// |    |----|-------------------------------------|----|
			// |    |    |                 B1                  |    |
			// |----------------------------------------------------|
			// |                  B2 (Full Width)                   |
			// |----------------------------------------------------|
			//
			// What we do to find the best sizing, we do the following
			// 1. Determine the minimum size of the chart area.
			// 2. Split the remaining width equally between each vertical axis
			// 3. Split the remaining height equally between each horizontal axis
			// 4. Give each layout the maximum size it can be. The layout will return it's minimum size
			// 5. Adjust the sizes of each axis based on it's minimum reported size.
			// 6. Refit each axis
			// 7. Position each axis in the final location
			// 8. Tell the chart the final location of the chart area
			// 9. Tell any axes that overlay the chart area the positions of the chart area

			// Step 1
			var chartWidth = width - (2 * xPadding);
			var chartHeight = height - (2 * yPadding);
			var chartAreaWidth = chartWidth / 2; // min 50%
			var chartAreaHeight = chartHeight / 2; // min 50%

			// Step 2
			var verticalBoxWidth = (width - chartAreaWidth) / (leftBoxes.length + rightBoxes.length);

			// Step 3
			var horizontalBoxHeight = (height - chartAreaHeight) / (topBoxes.length + bottomBoxes.length);

			// Step 4
			var maxChartAreaWidth = chartWidth;
			var maxChartAreaHeight = chartHeight;
			var minBoxSizes = [];

			helpers.each(leftBoxes.concat(rightBoxes, topBoxes, bottomBoxes), getMinimumBoxSize);

			function getMinimumBoxSize(box) {
				var minSize;
				var isHorizontal = box.isHorizontal();

				if (isHorizontal) {
					minSize = box.update(box.options.fullWidth ? chartWidth : maxChartAreaWidth, horizontalBoxHeight);
					maxChartAreaHeight -= minSize.height;
				} else {
					minSize = box.update(verticalBoxWidth, chartAreaHeight);
					maxChartAreaWidth -= minSize.width;
				}

				minBoxSizes.push({
					horizontal: isHorizontal,
					minSize: minSize,
					box: box,
				});
			}

			// At this point, maxChartAreaHeight and maxChartAreaWidth are the size the chart area could
			// be if the axes are drawn at their minimum sizes.

			// Steps 5 & 6
			var totalLeftBoxesWidth = xPadding;
			var totalRightBoxesWidth = xPadding;
			var totalTopBoxesHeight = yPadding;
			var totalBottomBoxesHeight = yPadding;

			// Update, and calculate the left and right margins for the horizontal boxes
			helpers.each(leftBoxes.concat(rightBoxes), fitBox);

			helpers.each(leftBoxes, function(box) {
				totalLeftBoxesWidth += box.width;
			});

			helpers.each(rightBoxes, function(box) {
				totalRightBoxesWidth += box.width;
			});

			// Set the Left and Right margins for the horizontal boxes
			helpers.each(topBoxes.concat(bottomBoxes), fitBox);

			// Function to fit a box
			function fitBox(box) {
				var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minBoxSize) {
					return minBoxSize.box === box;
				});

				if (minBoxSize) {
					if (box.isHorizontal()) {
						var scaleMargin = {
							left: totalLeftBoxesWidth,
							right: totalRightBoxesWidth,
							top: 0,
							bottom: 0,
						};

						box.update(box.options.fullWidth ? chartWidth : maxChartAreaWidth, minBoxSize.minSize.height, scaleMargin);
					} else {
						box.update(minBoxSize.minSize.width, maxChartAreaHeight);
					}
				}
			}

			// Figure out how much margin is on the top and bottom of the vertical boxes
			helpers.each(topBoxes, function(box) {
				totalTopBoxesHeight += box.height;
			});

			helpers.each(bottomBoxes, function(box) {
				totalBottomBoxesHeight += box.height;
			});

			// Let the left layout know the final margin
			helpers.each(leftBoxes.concat(rightBoxes), finalFitVerticalBox);

			function finalFitVerticalBox(box) {
				var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minBoxSize) {
					return minBoxSize.box === box;
				});

				var scaleMargin = {
					left: 0,
					right: 0,
					top: totalTopBoxesHeight,
					bottom: totalBottomBoxesHeight
				};

				if (minBoxSize) {
					box.update(minBoxSize.minSize.width, maxChartAreaHeight, scaleMargin);
				}
			}

			// Recalculate because the size of each layout might have changed slightly due to the margins (label rotation for instance)
			totalLeftBoxesWidth = xPadding;
			totalRightBoxesWidth = xPadding;
			totalTopBoxesHeight = yPadding;
			totalBottomBoxesHeight = yPadding;

			helpers.each(leftBoxes, function(box) {
				totalLeftBoxesWidth += box.width;
			});

			helpers.each(rightBoxes, function(box) {
				totalRightBoxesWidth += box.width;
			});

			helpers.each(topBoxes, function(box) {
				totalTopBoxesHeight += box.height;
			});
			helpers.each(bottomBoxes, function(box) {
				totalBottomBoxesHeight += box.height;
			});

			// Figure out if our chart area changed. This would occur if the dataset layout label rotation
			// changed due to the application of the margins in step 6. Since we can only get bigger, this is safe to do
			// without calling `fit` again
			var newMaxChartAreaHeight = height - totalTopBoxesHeight - totalBottomBoxesHeight;
			var newMaxChartAreaWidth = width - totalLeftBoxesWidth - totalRightBoxesWidth;

			if (newMaxChartAreaWidth !== maxChartAreaWidth || newMaxChartAreaHeight !== maxChartAreaHeight) {
				helpers.each(leftBoxes, function(box) {
					box.height = newMaxChartAreaHeight;
				});

				helpers.each(rightBoxes, function(box) {
					box.height = newMaxChartAreaHeight;
				});

				helpers.each(topBoxes, function(box) {
					box.width = newMaxChartAreaWidth;
				});

				helpers.each(bottomBoxes, function(box) {
					box.width = newMaxChartAreaWidth;
				});

				maxChartAreaHeight = newMaxChartAreaHeight;
				maxChartAreaWidth = newMaxChartAreaWidth;
			}

			// Step 7 - Position the boxes
			var left = xPadding;
			var top = yPadding;
			var right = 0;
			var bottom = 0;

			helpers.each(leftBoxes.concat(topBoxes), placeBox);

			// Account for chart width and height
			left += maxChartAreaWidth;
			top += maxChartAreaHeight;

			helpers.each(rightBoxes, placeBox);
			helpers.each(bottomBoxes, placeBox);

			function placeBox(box) {
				if (box.isHorizontal()) {
					box.left = box.options.fullWidth ? xPadding : totalLeftBoxesWidth;
					box.right = box.options.fullWidth ? width - xPadding : totalLeftBoxesWidth + maxChartAreaWidth;
					box.top = top;
					box.bottom = top + box.height;

					// Move to next point
					top = box.bottom;

				} else {

					box.left = left;
					box.right = left + box.width;
					box.top = totalTopBoxesHeight;
					box.bottom = totalTopBoxesHeight + maxChartAreaHeight;

					// Move to next point
					left = box.right;
				}
			}

			// Step 8
			chartInstance.chartArea = {
				left: totalLeftBoxesWidth,
				top: totalTopBoxesHeight,
				right: totalLeftBoxesWidth + maxChartAreaWidth,
				bottom: totalTopBoxesHeight + maxChartAreaHeight,
			};

			// Step 9
			helpers.each(chartAreaBoxes, function(box) {
				box.left = chartInstance.chartArea.left;
				box.top = chartInstance.chartArea.top;
				box.right = chartInstance.chartArea.right;
				box.bottom = chartInstance.chartArea.bottom;

				box.update(maxChartAreaWidth, maxChartAreaHeight);
			});
		}
	};

}).call(this);
<commit_msg>Fix layout service<commit_after>(function() {
	"use strict";

	var root = this,
		Chart = root.Chart,
		helpers = Chart.helpers;

	// The layout service is ver self explanatory.  It's responsible for the layout within a chart.
	// Scales, Legends and Plugins all rely on the layout service and can easily register to be placed anywhere they need
	// It is this service's responsibility of carrying out that layout.
	Chart.layoutService = {
		defaults: {},

		// Register a box to a chartInstance. A box is simply a reference to an object that requires layout. eg. Scales, Legend, Plugins.
		addBox: function(chartInstance, box) {
			if (!chartInstance.boxes) {
				chartInstance.boxes = [];
			}
			chartInstance.boxes.push(box);
		},

		removeBox: function(chartInstance, box) {
			if (!chartInstance.boxes) {
				return;
			}
			chartInstance.boxes.splice(chartInstance.boxes.indexOf(box), 1);
		},

		// The most important function
		update: function(chartInstance, width, height) {

			if (!chartInstance) {
				return;
			}

			var xPadding = width > 30 ? 5 : 2;
			var yPadding = height > 30 ? 5 : 2;

			var leftBoxes = helpers.where(chartInstance.boxes, function(box) {
				return box.options.position == "left";
			});
			var rightBoxes = helpers.where(chartInstance.boxes, function(box) {
				return box.options.position == "right";
			});
			var topBoxes = helpers.where(chartInstance.boxes, function(box) {
				return box.options.position == "top";
			});
			var bottomBoxes = helpers.where(chartInstance.boxes, function(box) {
				return box.options.position == "bottom";
			});

			// Boxes that overlay the chartarea such as the radialLinear scale
			var chartAreaBoxes = helpers.where(chartInstance.boxes, function(box) {
				return box.options.position == "chartArea";
			});

			function fullWidthSorter(a, b) {

			}

			// Ensure that full width boxes are at the very top / bottom
			topBoxes.sort(function(a, b) {
				return (b.options.fullWidth ? 1 : 0) - (a.options.fullWidth ? 1 : 0);
			});
			bottomBoxes.sort(function(a, b) {
				return (a.options.fullWidth ? 1 : 0) - (b.options.fullWidth ? 1 : 0);
			});

			// Essentially we now have any number of boxes on each of the 4 sides.
			// Our canvas looks like the following.
			// The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and
			// B1 is the bottom axis
			// There are also 4 quadrant-like locations (left to right instead of clockwise) reserved for chart overlays
			// These locations are single-box locations only, when trying to register a chartArea location that is already taken,
			// an error will be thrown.
			//
			// |----------------------------------------------------|
			// |                  T1 (Full Width)                   |
			// |----------------------------------------------------|
			// |    |    |                 T2                  |    |
			// |    |----|-------------------------------------|----|
			// |    |    | C1 |                           | C2 |    |
			// |    |    |----|                           |----|    |
			// |    |    |                                     |    |
			// | L1 | L2 |           ChartArea (C0)            | R1 |
			// |    |    |                                     |    |
			// |    |    |----|                           |----|    |
			// |    |    | C3 |                           | C4 |    |
			// |    |----|-------------------------------------|----|
			// |    |    |                 B1                  |    |
			// |----------------------------------------------------|
			// |                  B2 (Full Width)                   |
			// |----------------------------------------------------|
			//
			// What we do to find the best sizing, we do the following
			// 1. Determine the minimum size of the chart area.
			// 2. Split the remaining width equally between each vertical axis
			// 3. Split the remaining height equally between each horizontal axis
			// 4. Give each layout the maximum size it can be. The layout will return it's minimum size
			// 5. Adjust the sizes of each axis based on it's minimum reported size.
			// 6. Refit each axis
			// 7. Position each axis in the final location
			// 8. Tell the chart the final location of the chart area
			// 9. Tell any axes that overlay the chart area the positions of the chart area

			// Step 1
			var chartWidth = width - (2 * xPadding);
			var chartHeight = height - (2 * yPadding);
			var chartAreaWidth = chartWidth / 2; // min 50%
			var chartAreaHeight = chartHeight / 2; // min 50%

			// Step 2
			var verticalBoxWidth = (width - chartAreaWidth) / (leftBoxes.length + rightBoxes.length);

			// Step 3
			var horizontalBoxHeight = (height - chartAreaHeight) / (topBoxes.length + bottomBoxes.length);

			// Step 4
			var maxChartAreaWidth = chartWidth;
			var maxChartAreaHeight = chartHeight;
			var minBoxSizes = [];

			helpers.each(leftBoxes.concat(rightBoxes, topBoxes, bottomBoxes), getMinimumBoxSize);

			function getMinimumBoxSize(box) {
				var minSize;
				var isHorizontal = box.isHorizontal();

				if (isHorizontal) {
					minSize = box.update(box.options.fullWidth ? chartWidth : maxChartAreaWidth, horizontalBoxHeight);
					maxChartAreaHeight -= minSize.height;
				} else {
					minSize = box.update(verticalBoxWidth, chartAreaHeight);
					maxChartAreaWidth -= minSize.width;
				}

				minBoxSizes.push({
					horizontal: isHorizontal,
					minSize: minSize,
					box: box,
				});
			}

			// At this point, maxChartAreaHeight and maxChartAreaWidth are the size the chart area could
			// be if the axes are drawn at their minimum sizes.

			// Steps 5 & 6
			var totalLeftBoxesWidth = xPadding;
			var totalRightBoxesWidth = xPadding;
			var totalTopBoxesHeight = yPadding;
			var totalBottomBoxesHeight = yPadding;

			// Update, and calculate the left and right margins for the horizontal boxes
			helpers.each(leftBoxes.concat(rightBoxes), fitBox);

			helpers.each(leftBoxes, function(box) {
				totalLeftBoxesWidth += box.width;
			});

			helpers.each(rightBoxes, function(box) {
				totalRightBoxesWidth += box.width;
			});

			// Set the Left and Right margins for the horizontal boxes
			helpers.each(topBoxes.concat(bottomBoxes), fitBox);

			// Function to fit a box
			function fitBox(box) {
				var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minBoxSize) {
					return minBoxSize.box === box;
				});

				if (minBoxSize) {
					if (box.isHorizontal()) {
						var scaleMargin = {
							left: totalLeftBoxesWidth,
							right: totalRightBoxesWidth,
							top: 0,
							bottom: 0,
						};

						// Don't use min size here because of label rotation. When the labels are rotated, their rotation highly depends
						// on the margin. Sometimes they need to increase in size slightly
						box.update(box.options.fullWidth ? chartWidth : maxChartAreaWidth, chartHeight / 2, scaleMargin);
					} else {
						box.update(minBoxSize.minSize.width, maxChartAreaHeight);
					}
				}
			}

			// Figure out how much margin is on the top and bottom of the vertical boxes
			helpers.each(topBoxes, function(box) {
				totalTopBoxesHeight += box.height;
			});

			helpers.each(bottomBoxes, function(box) {
				totalBottomBoxesHeight += box.height;
			});

			// Let the left layout know the final margin
			helpers.each(leftBoxes.concat(rightBoxes), finalFitVerticalBox);

			function finalFitVerticalBox(box) {
				var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minBoxSize) {
					return minBoxSize.box === box;
				});

				var scaleMargin = {
					left: 0,
					right: 0,
					top: totalTopBoxesHeight,
					bottom: totalBottomBoxesHeight
				};

				if (minBoxSize) {
					box.update(minBoxSize.minSize.width, maxChartAreaHeight, scaleMargin);
				}
			}

			// Recalculate because the size of each layout might have changed slightly due to the margins (label rotation for instance)
			totalLeftBoxesWidth = xPadding;
			totalRightBoxesWidth = xPadding;
			totalTopBoxesHeight = yPadding;
			totalBottomBoxesHeight = yPadding;

			helpers.each(leftBoxes, function(box) {
				totalLeftBoxesWidth += box.width;
			});

			helpers.each(rightBoxes, function(box) {
				totalRightBoxesWidth += box.width;
			});

			helpers.each(topBoxes, function(box) {
				totalTopBoxesHeight += box.height;
			});
			helpers.each(bottomBoxes, function(box) {
				totalBottomBoxesHeight += box.height;
			});

			// Figure out if our chart area changed. This would occur if the dataset layout label rotation
			// changed due to the application of the margins in step 6. Since we can only get bigger, this is safe to do
			// without calling `fit` again
			var newMaxChartAreaHeight = height - totalTopBoxesHeight - totalBottomBoxesHeight;
			var newMaxChartAreaWidth = width - totalLeftBoxesWidth - totalRightBoxesWidth;

			if (newMaxChartAreaWidth !== maxChartAreaWidth || newMaxChartAreaHeight !== maxChartAreaHeight) {
				helpers.each(leftBoxes, function(box) {
					box.height = newMaxChartAreaHeight;
				});

				helpers.each(rightBoxes, function(box) {
					box.height = newMaxChartAreaHeight;
				});

				helpers.each(topBoxes, function(box) {
					box.width = newMaxChartAreaWidth;
				});

				helpers.each(bottomBoxes, function(box) {
					box.width = newMaxChartAreaWidth;
				});

				maxChartAreaHeight = newMaxChartAreaHeight;
				maxChartAreaWidth = newMaxChartAreaWidth;
			}

			// Step 7 - Position the boxes
			var left = xPadding;
			var top = yPadding;
			var right = 0;
			var bottom = 0;

			helpers.each(leftBoxes.concat(topBoxes), placeBox);

			// Account for chart width and height
			left += maxChartAreaWidth;
			top += maxChartAreaHeight;

			helpers.each(rightBoxes, placeBox);
			helpers.each(bottomBoxes, placeBox);

			function placeBox(box) {
				if (box.isHorizontal()) {
					box.left = box.options.fullWidth ? xPadding : totalLeftBoxesWidth;
					box.right = box.options.fullWidth ? width - xPadding : totalLeftBoxesWidth + maxChartAreaWidth;
					box.top = top;
					box.bottom = top + box.height;

					// Move to next point
					top = box.bottom;

				} else {

					box.left = left;
					box.right = left + box.width;
					box.top = totalTopBoxesHeight;
					box.bottom = totalTopBoxesHeight + maxChartAreaHeight;

					// Move to next point
					left = box.right;
				}
			}

			// Step 8
			chartInstance.chartArea = {
				left: totalLeftBoxesWidth,
				top: totalTopBoxesHeight,
				right: totalLeftBoxesWidth + maxChartAreaWidth,
				bottom: totalTopBoxesHeight + maxChartAreaHeight,
			};

			// Step 9
			helpers.each(chartAreaBoxes, function(box) {
				box.left = chartInstance.chartArea.left;
				box.top = chartInstance.chartArea.top;
				box.right = chartInstance.chartArea.right;
				box.bottom = chartInstance.chartArea.bottom;

				box.update(maxChartAreaWidth, maxChartAreaHeight);
			});
		}
	};

}).call(this);
<|endoftext|>


<commit_before>'use strict';

module.exports = function(app) {
  app.controller('CreateGameController', ['$rootScope', '$route', 'UserService', 'FriendService', 'GameService', 'ResultService', function($rs, $route, userService, friendService, gameService, resultService) {

    const ctrl = this;
    const async = require('async');
    ctrl.user = userService.data.user;
    ctrl.allFriends = friendService.data.allFriends;
    ctrl.players = [];
    ctrl.editing = false;

    ctrl.createGame = function(gameData) {
      gameData.players = ctrl.players;
      gameService.create(gameData)
        .then((newGame) => {
          resultService.create(newGame._id, gameData.players)
            .then((newResults) => {
              $route.reload();
            });
        })
        .catch((err) => {
          console.log(err);
        });
    }

    // let userUpdateData = {
    //   usersArray: resultsArray,
    //   updateQuery: { $addToSet: { gameIds: newGame._id }},
    // };
    // userService.updateMany(userUpdateData)

    ctrl.addUser = function(user) {
      let friendsArray = ctrl.allFriends.friends;
      ctrl.players.push(user);
      friendsArray.splice(friendsArray.indexOf(user), 1);
    }

    ctrl.removeUser = function(user) {
      ctrl.players.splice(ctrl.players.indexOf(user), 1);
      ctrl.allFriends.friends.push(user);
    }

    ctrl.init = function() {
      let userData = {
        _id: ctrl.user._id,
        fullName: ctrl.user.fullName,
        email: ctrl.user.email,
      };
      ctrl.players.push(userData);
      friendService.getAllFriends(ctrl.user.email);
    }

    ctrl.init();
  }]);
}
<commit_msg>Call single gameService method to create a game.<commit_after>'use strict';

module.exports = function(app) {
  app.controller('CreateGameController', ['$rootScope', '$route', 'UserService', 'FriendService', 'GameService', 'ResultService', function($rs, $route, userService, friendService, gameService, resultService) {

    const ctrl = this;
    ctrl.user = userService.data.user;
    ctrl.allFriends = friendService.data.allFriends;
    ctrl.players = [];
    ctrl.editing = false;

    ctrl.createGame = function(gameData) {
      gameData.players = ctrl.players;
      gameService.newGame(gameData)
        .catch((err) => {
          console.log('Error creating game.');
          console.log(err);
        });
    };

    ctrl.addUser = function(user) {
      let friendsArray = ctrl.allFriends.friends;
      ctrl.players.push(user);
      friendsArray.splice(friendsArray.indexOf(user), 1);
    };

    ctrl.removeUser = function(user) {
      ctrl.players.splice(ctrl.players.indexOf(user), 1);
      ctrl.allFriends.friends.push(user);
    };

    ctrl.init = function() {
      let userData = {
        _id: ctrl.user._id,
        fullName: ctrl.user.fullName,
        email: ctrl.user.email,
      };
      ctrl.players.push(userData);
      friendService.getAllFriends(ctrl.user.email);
    }

    ctrl.init();
  }]);
}
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { mapToCssModules } from './utils';

const CarouselIndicators = (props) => {
  const { items, activeIndex, cssModule, onClickHandler, className } = props;

  const listClasses = mapToCssModules(classNames(className, 'carousel-indicators', cssModule));
  const indicators = items.map((item, idx) => {
    const indicatorClasses = mapToCssModules(classNames(
      { active: activeIndex === idx }
    ), cssModule);
    return (
      <li
        key={`${item.key || item.src}${item.caption}${item.altText}`}
        onClick={(e) => {
          e.preventDefault();
          onClickHandler(idx);
        }}
        className={indicatorClasses}
      />);
  });

  return (

      {indicators}

  );
};

CarouselIndicators.propTypes = {
  items: PropTypes.array.isRequired,
  activeIndex: PropTypes.number.isRequired,
  cssModule: PropTypes.object,
  onClickHandler: PropTypes.func.isRequired,
  className: PropTypes.string,
};

export default CarouselIndicators;
<commit_msg>fix(CarouselIndicators`): fix class name when using `cssModule` (#726)<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { mapToCssModules } from './utils';

const CarouselIndicators = (props) => {
  const { items, activeIndex, cssModule, onClickHandler, className } = props;

  const listClasses = mapToCssModules(classNames(className, 'carousel-indicators'), cssModule);
  const indicators = items.map((item, idx) => {
    const indicatorClasses = mapToCssModules(classNames(
      { active: activeIndex === idx }
    ), cssModule);
    return (
      <li
        key={`${item.key || item.src}${item.caption}${item.altText}`}
        onClick={(e) => {
          e.preventDefault();
          onClickHandler(idx);
        }}
        className={indicatorClasses}
      />);
  });

  return (

      {indicators}

  );
};

CarouselIndicators.propTypes = {
  items: PropTypes.array.isRequired,
  activeIndex: PropTypes.number.isRequired,
  cssModule: PropTypes.object,
  onClickHandler: PropTypes.func.isRequired,
  className: PropTypes.string,
};

export default CarouselIndicators;
<|endoftext|>


<commit_before>/**
 * @fileoverview Pingpong
 * @author burning <iburning@live.cn>
 * @version 2017.08.29
 */

import { Container, Loader, Renderer, Resources, Text } from './PIXI'
import Ball from './Ball'
import Bat from './Bat'
import Tinker from './Tinker'
import ScoreView from './ScoreView'

const UNIT = 8
const GRAVITY = 0.15  // 重力加速度

// Create a new instance of the Bump collision library
const bump = new Bump(PIXI)

// Create a Pixi renderer
const renderer = Renderer(UNIT * 20 * 3, UNIT * 20 * 4)
// Set the canvas's border style and background color
// renderer.view.style.border = "1px solid #333333"
renderer.backgroundColor = "0xEFEFEF"

// Add the canvas to the HTML document
// document.getElementById("pixi").appendChild(renderer.view)
document.body.appendChild(renderer.view)

// Scale the canvas to the maximum window
let scale = scaleToWindow(renderer.view) || 1
console.log('scale', scale)

// Create a new instance of Tink, the interactive module.
const tink = new Tink(PIXI, renderer.view, scale)

// Create a container object called the 'stage'
const stage = new Container()

let gameScene = null
let gameOverScene = null

// Set the initial game state
let state = play
let ball = new Ball(UNIT * 2)
let bat = new Bat(UNIT * 15, UNIT * 2)  // 球拍，防止穿越，球拍厚度要大于小球的直径
let batTinker = new Tinker(UNIT * 15, UNIT * 15)
let scoreDisplay = null
let message = null
let score = 0
let miss = 0
let health = 3

linkFont("assets/Pixilator.ttf")

// Load resources (images and fonts) and run the 'setup' function when it's done
Loader.add([
  "assets/Pixilator.ttf"
]).load(setup)

function setup() {
  // console.log('Resources', Resources)

  gameScene = new Container()
  stage.addChild(gameScene)

  // 因为依赖字体下载，所以将实例化放在setup中
  scoreDisplay = new ScoreView(0)
  scoreDisplay.view.x = (renderer.view.width - scoreDisplay.view.width) / 2
  scoreDisplay.view.y = UNIT * 3
  gameScene.addChild(scoreDisplay.view)

  ball.speed = 10
  ball.vx = 5
  ball.vy = ball.speed
  ball.maxSpeed = 18
  ball.acceleration =  2
  gameScene.addChild(ball)

  bat.x = (renderer.view.width - bat.width) / 2
  bat.y = renderer.view.height - UNIT * 30
  // bindKeyBorad(bat)
  gameScene.addChild(bat)
  // tink.makeDraggable(bat)

  batTinker.x = (renderer.view.width - batTinker.width) / 2
  batTinker.y = renderer.view.height - UNIT * 30
  // bindKeyBorad(bat)
  gameScene.addChild(batTinker)
  tink.makeDraggable(batTinker)

  // // Make the pointer
  // const pointer = tink.makePointer()
  // pointer.tap = () => {
  //   // console.log('tap', pointer.x, pointer.y)
  // }
  //
  // pointer.press = () => {
  //   // console.log('press', pointer.x, pointer.y)
  //   batTinker.x = pointer.x
  //   batTinker.y = pointer.y
  // }

  gameOverScene = new Container()
  stage.addChild(gameOverScene)
  let message = new Text("Game Over", { font: "48px Pixilator" })
  message.x = (renderer.view.width - message.width) / 2
  message.y = (renderer.view.height - message.height) / 2
  gameOverScene.addChild(message)
  gameOverScene.visible = false

  // Set the game's current state to 'play'
  state = play

  // Start the game loop
  gameLoop()
}

function gameLoop() {
  // Loop this function 60 times per second
  requestAnimationFrame(gameLoop)

  // Run the current state
  state()

  tink.update()   // !important

  // Render the stage
  renderer.render(stage)
}

function play() {
  ball.vy += GRAVITY
  ball.x += ball.vx
  ball.y += ball.vy

  // bat.x += bat.vx
  // bat.y += bat.vy
  bat.x = batTinker.x
  // bat.y = batTinker.y - UNIT * 5

  bump.contain(bat, {
    x: UNIT,
    y: renderer.view.height / 2,
    width: renderer.view.width - UNIT,
    height: renderer.view.height - UNIT
  })

  let collision = bump.contain(ball, {
    x: 0,
    y: 0,
    width: renderer.view.width,
    height: renderer.view.height
  })

  if (collision) {
    console.log("collision", collision, score, miss, ball.vx, ball.vy)
    // Reverse the sprite's 'vx' value if it hits the left or right
    if (collision.has("left") || collision.has("right")) {
      ball.vx = -ball.vx
    }

    if (collision.has("top")) {
      ball.vy = -ball.vy
    }

    if (collision.has("bottom")) {
      health--
      miss++
      scoreDisplay.setMiss(miss)
      if (health <= 0) {
        state = end
      }
      // ball.vy = -ball.vy
      ball.vx = (ball.vx > 0) ? ball.speed : -ball.speed
      ball.vy = -ball.speed
    }
  }

  let hitTest = bump.rectangleCollision(ball, bat)
  if (hitTest) {
    console.log('hitTest', hitTest)
    if (hitTest == "bottom") {
      score++
      scoreDisplay.setScore(score)
    }

    if (hitTest == "top" || hitTest == "bottom") {
      if (Math.abs(ball.vx) < ball.maxSpeed) {
        ball.vx += ball.acceleration
      }

      if (Math.abs(ball.vy) < ball.maxSpeed) {
        ball.vy += ball.acceleration
      }

      ball.vy = -ball.vy
    }
  }
}

function end() {
  // gameScene.visible = false
  gameOverScene.visible = true
}

function linkFont(source) {
  // console.log("linkFont", source)
  // Use the font's filename as the 'fontFamily' name.
  // This code captures the font file's name without the extension or file path
  let fontFamily = source.split("/").pop().split(".")[0]

  // Append an '@font-face' style rule to the head of the HTML document
  let newStyle = document.createElement("style")
  let fontFace = `@font-face {
    font-family: "${fontFamily}";
    src: url("${source}");
  }`
  newStyle.appendChild(document.createTextNode(fontFace))
  document.head.appendChild(newStyle)
}

function bindKeyBorad(object) {
  // Capture the keyboard arrow keys
  let left = keyboard(37)
  let right = keyboard(39)
  let up = keyboard(38)
  let down = keyboard(40)

  // Left arrow key 'press' method
  left.press = function () {
    // Change the object's velocity when the key is pressed
    object.vx = -object.speed
    object.vy = 0
  }

  // Left arrow key 'elease' method
  left.release = function () {
    // If the left arrow has been released, and the right arrow isn't down,
    // and the object isn't moving vertically, stop the sprite from moving
    // by setting its velocity to zero
    if (!right.isDown && object.vy === 0) {
      object.vx = 0
    }
  }

  //Right
  right.press = function () {
    object.vx = object.speed
    object.vy = 0
  }

  right.release = function () {
    if (!left.isDown && object.vy === 0) {
      object.vx = 0
    }
  }

  // Up
  up.press = function () {
    object.vy = -object.speed
    object.vx = 0
  }

  up.release = function () {
    if (!down.isDown && object.vx === 0) {
      object.vy = 0
    }
  }

  // Down
  down.press = function () {
    object.vy = object.speed
    object.vx = 0
  }

  down.release = function () {
    if (!up.isDown && object.vx === 0) {
      object.vy = 0
    }
  }
}
<commit_msg>state控制<commit_after>/**
 * @fileoverview Pingpong
 * @author burning <iburning@live.cn>
 * @version 2017.08.29
 */

import { Container, Loader, Renderer, Resources, Text } from './PIXI'
import Ball from './Ball'
import Bat from './Bat'
import Tinker from './Tinker'
import ScoreView from './ScoreView'

const UNIT = 8
const GRAVITY = 0.15  // 重力加速度

// Create a new instance of the Bump collision library
const bump = new Bump(PIXI)

// Create a Pixi renderer
const renderer = Renderer(UNIT * 20 * 3, UNIT * 20 * 4)
// Set the canvas's border style and background color
// renderer.view.style.border = "1px solid #333333"
renderer.backgroundColor = "0xEFEFEF"

// Add the canvas to the HTML document
// document.getElementById("pixi").appendChild(renderer.view)
document.body.appendChild(renderer.view)

// Scale the canvas to the maximum window
let scale = scaleToWindow(renderer.view) || 1
console.log('scale', scale)

// Create a new instance of Tink, the interactive module.
const tink = new Tink(PIXI, renderer.view, scale)

// Create a container object called the 'stage'
const stage = new Container()

let gameScene = null
let gameOverScene = null

// Set the initial game state
let state = stop
let ball = new Ball(UNIT * 2)
let bat = new Bat(UNIT * 15, UNIT * 2)  // 球拍，防止穿越，球拍厚度要大于小球的直径
let batTinker = new Tinker(UNIT * 15, UNIT * 15)
let scoreDisplay = null
let message = null
let score = 0
let miss = 0
let health = 3

linkFont("assets/Pixilator.ttf")

// Load resources (images and fonts) and run the 'setup' function when it's done
Loader.add([
  "assets/Pixilator.ttf"
]).load(setup)

function setup() {
  // console.log('Resources', Resources)

  gameScene = new Container()
  stage.addChild(gameScene)

  // 因为依赖字体下载，所以将实例化放在setup中
  scoreDisplay = new ScoreView(0)
  scoreDisplay.view.x = (renderer.view.width - scoreDisplay.view.width) / 2
  scoreDisplay.view.y = UNIT * 3
  gameScene.addChild(scoreDisplay.view)

  ball.speed = 10
  ball.vx = 5
  ball.vy = ball.speed
  ball.maxSpeed = 18
  ball.acceleration =  2
  gameScene.addChild(ball)

  bat.x = (renderer.view.width - bat.width) / 2
  bat.y = renderer.view.height - UNIT * 30
  // bindKeyBorad(bat)
  gameScene.addChild(bat)
  // tink.makeDraggable(bat)

  batTinker.x = (renderer.view.width - batTinker.width) / 2
  batTinker.y = renderer.view.height - UNIT * 30
  // bindKeyBorad(bat)
  gameScene.addChild(batTinker)
  tink.makeDraggable(batTinker)

  // Make the pointer
  const pointer = tink.makePointer()
  pointer.press = () => {
    console.log('press', pointer.x, pointer.y)
    // Set the game's current state to 'play'
    state = play
  }

  pointer.release = () => {
    console.log('release', pointer.x, pointer.y)
    // Set the game's current state to 'play'
    state = stop
  }

  // pointer.press = () => {
  //   // console.log('press', pointer.x, pointer.y)
  //   batTinker.x = pointer.x
  //   batTinker.y = pointer.y
  // }

  gameOverScene = new Container()
  stage.addChild(gameOverScene)
  let message = new Text("Game Over", { font: "48px Pixilator" })
  message.x = (renderer.view.width - message.width) / 2
  message.y = (renderer.view.height - message.height) / 2
  gameOverScene.addChild(message)
  gameOverScene.visible = false

  // Start the game loop
  gameLoop()
}

function gameLoop() {
  // Loop this function 60 times per second
  requestAnimationFrame(gameLoop)

  // Run the current state
  state()

  tink.update()   // !important

  // Render the stage
  renderer.render(stage)
}

function play() {
  ball.vy += GRAVITY
  ball.x += ball.vx
  ball.y += ball.vy

  // bat.x += bat.vx
  // bat.y += bat.vy
  bat.x = batTinker.x
  // bat.y = batTinker.y - UNIT * 5

  bump.contain(bat, {
    x: UNIT,
    y: renderer.view.height / 2,
    width: renderer.view.width - UNIT,
    height: renderer.view.height - UNIT
  })

  let collision = bump.contain(ball, {
    x: 0,
    y: 0,
    width: renderer.view.width,
    height: renderer.view.height
  })

  if (collision) {
    console.log("collision", collision, score, miss, ball.vx, ball.vy)
    // Reverse the sprite's 'vx' value if it hits the left or right
    if (collision.has("left") || collision.has("right")) {
      ball.vx = -ball.vx
    }

    if (collision.has("top")) {
      ball.vy = -ball.vy
    }

    if (collision.has("bottom")) {
      health--
      miss++
      scoreDisplay.setMiss(miss)
      if (health <= 0) {
        state = end
      }
      // ball.vy = -ball.vy
      ball.vx = (ball.vx > 0) ? ball.speed : -ball.speed
      ball.vy = -ball.speed
    }
  }

  let hitTest = bump.rectangleCollision(ball, bat)
  if (hitTest) {
    console.log('hitTest', hitTest)
    if (hitTest == "bottom") {
      score++
      scoreDisplay.setScore(score)
    }

    if (hitTest == "top" || hitTest == "bottom") {
      if (Math.abs(ball.vx) < ball.maxSpeed) {
        ball.vx += ball.acceleration
      }

      if (Math.abs(ball.vy) < ball.maxSpeed) {
        ball.vy += ball.acceleration
      }

      ball.vy = -ball.vy
    }
  }
}

function stop() {
  // gameScene.visible = true
}

function end() {
  // gameScene.visible = false
  gameOverScene.visible = true
}

function linkFont(source) {
  // console.log("linkFont", source)
  // Use the font's filename as the 'fontFamily' name.
  // This code captures the font file's name without the extension or file path
  let fontFamily = source.split("/").pop().split(".")[0]

  // Append an '@font-face' style rule to the head of the HTML document
  let newStyle = document.createElement("style")
  let fontFace = `@font-face {
    font-family: "${fontFamily}";
    src: url("${source}");
  }`
  newStyle.appendChild(document.createTextNode(fontFace))
  document.head.appendChild(newStyle)
}

function bindKeyBorad(object) {
  // Capture the keyboard arrow keys
  let left = keyboard(37)
  let right = keyboard(39)
  let up = keyboard(38)
  let down = keyboard(40)

  // Left arrow key 'press' method
  left.press = function () {
    // Change the object's velocity when the key is pressed
    object.vx = -object.speed
    object.vy = 0
  }

  // Left arrow key 'elease' method
  left.release = function () {
    // If the left arrow has been released, and the right arrow isn't down,
    // and the object isn't moving vertically, stop the sprite from moving
    // by setting its velocity to zero
    if (!right.isDown && object.vy === 0) {
      object.vx = 0
    }
  }

  //Right
  right.press = function () {
    object.vx = object.speed
    object.vy = 0
  }

  right.release = function () {
    if (!left.isDown && object.vy === 0) {
      object.vx = 0
    }
  }

  // Up
  up.press = function () {
    object.vy = -object.speed
    object.vx = 0
  }

  up.release = function () {
    if (!down.isDown && object.vx === 0) {
      object.vy = 0
    }
  }

  // Down
  down.press = function () {
    object.vy = object.speed
    object.vx = 0
  }

  down.release = function () {
    if (!up.isDown && object.vx === 0) {
      object.vy = 0
    }
  }
}
<|endoftext|>


<commit_before>'use strict';

// MODULES //

var debug = require( 'debug' )( 'module-pkg-deps:async' );
var isString = require( '@stdlib/assert/is-string' ).isPrimitive;
var isStringArray = require( '@stdlib/assert/is-string-array' ).primitives;
var isFunction = require( '@stdlib/assert/is-function' );
var copy = require( '@stdlib/utils/copy' );
var defaults = require( './defaults.json' );
var validate = require( './validate.js' );
var walkFile = require( './walk_file.js' );
var toArray = require( './to_array.js' );

// MAIN //

/**
* Lists package dependencies.
*
* @param {(string|StringArray)} files - file or file list
* @param {Options} [options] - function options
* @param {boolean} [options.builtins=true] - boolean indicating whether to include built-in packages
* @param {boolean} [options.walk=true] - boolean indicating whether to walk relative module dependencies
* @param {Callback} clbk - callback
* @throws {TypeError} first argument must be a string array
* @throws {TypeError} callback argument must be a function
* @throws {TypeError} options argument must be an object
* @throws {TypeError} must provide valid options
*
* @example
* var files = [ '/foo/bar/index.js' ];
*
* pkgDeps( files, clbk );
*
* function clbk( error, deps ) {
*     if ( error ) {
*         throw error;
*     }
*     console.dir( deps );
* }
*/
function pkgDeps( files, options, clbk ) {
	var isStr;
	var count;
	var cache;
	var list;
	var opts;
	var err;
	var len;
	var cb;
	var i;

	isStr = isString( files );
	if (
		!isStr &&
		!isStringArray( files )
	) {
		throw new TypeError( 'invalid input argument. First argument must be either a string or string array. Value: `' + files + '`.' );
	}
	if ( isStr ) {
		list = [ files ];
	} else {
		list = files;
	}
	opts = copy( defaults );
	if ( arguments.length < 3 ) {
		cb = options;
	} else {
		cb = clbk;
		err = validate( opts, options );
		if ( err ) {
			throw err;
		}
	}
	if ( !isFunction( cb ) ) {
		throw new TypeError( 'invalid input argument. Callback argument must be a function. Value: `' + cb + '`.' );
	}
	debug( 'Options: %s', JSON.stringify( opts ) );

	len = list.length;
	cache = {};
	count = 0;

	debug( 'Walking %s files...', len );
	for ( i = 0; i < len; i++ ) {
		debug( 'Walking file: %s (%d of %d)...', list[ i ], i+1, len );
		walkFile( cache, list[ i ], opts, onWalk( i ) );
	}

	/**
	* Returns a callback to be invoked upon walking a file.
	*
	* @private
	* @param {NonNegativeInteger} idx - index
	* @returns {Callback} callback
	*/
	function onWalk( idx ) {
		var file = list[ idx ];
		var k = idx + 1;
		return onWalk;
		/**
		* Callback invoked upon walking a file.
		*
		* @private
		* @param {(Error|null)} error - error object
		* @returns {void}
		*/
		function onWalk( error ) {
			if ( error ) {
				debug( 'Encountered an error when walking file: %s (%d of %d). Error: %s', file, k, len, error.message );
				return cb( error );
			}
			debug( 'Successfully walked file: %s (%d of %d).', file, k, len );

			count += 1;
			debug( 'Walked %d of %d files.', count, len );
			if ( count === len ) {
				return cb( null, toArray( cache ) );
			}
		} // end FUNCTION onWalk()
	} // end FUNCTION onWalk()
} // end FUNCTION pkgDeps()

// EXPORTS //

module.exports = pkgDeps;
<commit_msg>Fix repeated invocation of callback if multiple errors<commit_after>'use strict';

// MODULES //

var debug = require( 'debug' )( 'module-pkg-deps:async' );
var isString = require( '@stdlib/assert/is-string' ).isPrimitive;
var isStringArray = require( '@stdlib/assert/is-string-array' ).primitives;
var isFunction = require( '@stdlib/assert/is-function' );
var copy = require( '@stdlib/utils/copy' );
var defaults = require( './defaults.json' );
var validate = require( './validate.js' );
var walkFile = require( './walk_file.js' );
var toArray = require( './to_array.js' );

// MAIN //

/**
* Lists package dependencies.
*
* @param {(string|StringArray)} files - file or file list
* @param {Options} [options] - function options
* @param {boolean} [options.builtins=true] - boolean indicating whether to include built-in packages
* @param {boolean} [options.walk=true] - boolean indicating whether to walk relative module dependencies
* @param {Callback} clbk - callback
* @throws {TypeError} first argument must be a string array
* @throws {TypeError} callback argument must be a function
* @throws {TypeError} options argument must be an object
* @throws {TypeError} must provide valid options
*
* @example
* var files = [ '/foo/bar/index.js' ];
*
* pkgDeps( files, clbk );
*
* function clbk( error, deps ) {
*     if ( error ) {
*         throw error;
*     }
*     console.dir( deps );
* }
*/
function pkgDeps( files, options, clbk ) {
	var errFLG;
	var isStr;
	var count;
	var cache;
	var list;
	var opts;
	var err;
	var len;
	var cb;
	var i;

	isStr = isString( files );
	if (
		!isStr &&
		!isStringArray( files )
	) {
		throw new TypeError( 'invalid input argument. First argument must be either a string or string array. Value: `' + files + '`.' );
	}
	if ( isStr ) {
		list = [ files ];
	} else {
		list = files;
	}
	opts = copy( defaults );
	if ( arguments.length < 3 ) {
		cb = options;
	} else {
		cb = clbk;
		err = validate( opts, options );
		if ( err ) {
			throw err;
		}
	}
	if ( !isFunction( cb ) ) {
		throw new TypeError( 'invalid input argument. Callback argument must be a function. Value: `' + cb + '`.' );
	}
	debug( 'Options: %s', JSON.stringify( opts ) );

	len = list.length;
	cache = {};
	count = 0;

	debug( 'Walking %s files...', len );
	for ( i = 0; i < len; i++ ) {
		debug( 'Walking file: %s (%d of %d)...', list[ i ], i+1, len );
		walkFile( cache, list[ i ], opts, onWalk( i ) );
	}

	/**
	* Returns a callback to be invoked upon walking a file.
	*
	* @private
	* @param {NonNegativeInteger} idx - index
	* @returns {Callback} callback
	*/
	function onWalk( idx ) {
		var file = list[ idx ];
		var k = idx + 1;
		return onWalk;
		/**
		* Callback invoked upon walking a file.
		*
		* @private
		* @param {(Error|null)} error - error object
		* @returns {void}
		*/
		function onWalk( error ) {
			if ( errFLG ) {
				return;
			}
			if ( error ) {
				debug( 'Encountered an error when walking file: %s (%d of %d). Error: %s', file, k, len, error.message );
				errFLG = true;
				return cb( error );
			}
			debug( 'Successfully walked file: %s (%d of %d).', file, k, len );

			count += 1;
			debug( 'Walked %d of %d files.', count, len );
			if ( count === len ) {
				return cb( null, toArray( cache ) );
			}
		} // end FUNCTION onWalk()
	} // end FUNCTION onWalk()
} // end FUNCTION pkgDeps()

// EXPORTS //

module.exports = pkgDeps;
<|endoftext|>


<commit_before>/*jslint sloppy: true*/
/*global define*/

define([
    //>>includeStart('checks', pragmas.checks);
    'Utils/lang/isObject',
    'Utils/lang/isFunction',
    'Utils/object/forOwn',
    'Utils/Array/insert',
    'Utils/lang/createObject'
    //>>includeEnd('checks');
], function (
    //>>includeStart('checks', pragmas.checks);
    isObject,
    isFunction,
    forOwn,
    insert,
    createObject
    //>>includeEnd('checks');
) {

    /**
     *
     */
    function Interface(params) {

        //>>includeStart('checks', pragmas.checks);
        if (!isObject(params)) {
            throw new TypeError('Argument "params" must be an object.');
        }
        //>>includeEnd('checks');

        var interf = function () {
            //>>includeStart('checks', pragmas.checks);
            throw new Error('Interfaces cannot be instantiated.');
            //>>includeEnd('checks');
        };

        //>>includeStart('checks', pragmas.checks);
        params.Name = params.Name || 'Unnamed';

        /**
         * Grabs the static methods from the constructor parent and itself and merges them.
         *
         * @param {Function} constructor The constructor
         */
        function grabStatics(constructor) {

            var parent = constructor.Super ? constructor.Super.$constructor : null;

            constructor.$statics = [];

            if (constructor.prototype.Statics) {

                if (!isObject(constructor.prototype.Statics)) {
                    throw new TypeError('Statics definition for "' + params.Name + '" must be an object.');
                }

                forOwn(constructor.prototype.Statics, function (value, key) {
                    if (isFunction(value)) {
                        constructor.$statics.push(key);
                    }
                });
            }

            if (parent && parent.$statics) {

                parent.$statics.forEach(function (value) {
                    insert(constructor.$statics, value);
                });
            }

            if (!constructor.$statics.length) {
                delete constructor.$statics;
            }
        }

        if (params.Extends) {

            if (!isFunction(params.Extends) || !params.Extends.$interface) {
                throw new TypeError('The parent interface of "' + params.Name + '" is not a valid interface (defined in Extends).');
            }

            interf.Super = params.Extends.prototype;
            delete params.Extends;

            interf.prototype = createObject(interf.Super, params);
            delete interf.prototype.Extends;
        } else {
            interf.prototype = params;
        }

        interf.prototype.$constructor = interf;

        // Grab all statics from the parent and itself and references them for later use
        grabStatics(interf);
        delete interf.prototype.Statics;

        // TODO: Make a way to validate an interface
        interf.$interface = true;   // Mark it as an interface

        //>>includeEnd('checks');

        return interf;
    }

    return Interface;
});<commit_msg>Little refactor.<commit_after>/*jslint sloppy: true*/
/*global define*/

define([
    //>>includeStart('checks', pragmas.checks);
    'Utils/lang/isObject',
    'Utils/lang/isFunction',
    'Utils/object/forOwn',
    'Utils/Array/combine',
    'Utils/lang/createObject'
    //>>includeEnd('checks');
], function (
    //>>includeStart('checks', pragmas.checks);
    isObject,
    isFunction,
    forOwn,
    combine,
    createObject
    //>>includeEnd('checks');
) {

    /**
     *
     */
    function Interface(params) {

        //>>includeStart('checks', pragmas.checks);
        if (!isObject(params)) {
            throw new TypeError('Argument "params" must be an object.');
        }
        //>>includeEnd('checks');

        var interf = function () {
            //>>includeStart('checks', pragmas.checks);
            throw new Error('Interfaces cannot be instantiated.');
            //>>includeEnd('checks');
        };

        //>>includeStart('checks', pragmas.checks);
        params.Name = params.Name || 'Unnamed';

        /**
         * Grabs the static methods from the constructor parent and itself and merges them.
         *
         * @param {Function} constructor The constructor
         */
        function grabStatics(constructor) {

            var parent = constructor.Super ? constructor.Super.$constructor : null;

            constructor.$statics = [];

            if (constructor.prototype.Statics) {

                if (!isObject(constructor.prototype.Statics)) {
                    throw new TypeError('Statics definition for "' + params.Name + '" must be an object.');
                }

                forOwn(constructor.prototype.Statics, function (value, key) {
                    if (isFunction(value)) {
                        constructor.$statics.push(key);
                    }
                });
            }

            if (parent && parent.$statics) {
                combine(constructor.$statics, parent.$statics);
            } else if (!constructor.$statics.length) {
                delete constructor.$statics;
            }
        }

        if (params.Extends) {

            if (!isFunction(params.Extends) || !params.Extends.$interface) {
                throw new TypeError('The parent interface of "' + params.Name + '" is not a valid interface (defined in Extends).');
            }

            interf.Super = params.Extends.prototype;
            delete params.Extends;

            interf.prototype = createObject(interf.Super, params);
            delete interf.prototype.Extends;
        } else {
            interf.prototype = params;
        }

        interf.prototype.$constructor = interf;

        // Grab all statics from the parent and itself and references them for later use
        grabStatics(interf);
        delete interf.prototype.Statics;

        // TODO: Make a way to validate an interface
        interf.$interface = true;   // Mark it as an interface
        //>>includeEnd('checks');

        return interf;
    }

    return Interface;
});<|endoftext|>


<commit_before>cbecc.factory('Shared', ['$q', '$cacheFactory', '$templateCache', '$sce', 'usSpinnerService', 'uiGridConstants', function ($q, $cacheFactory, $templateCache, $sce, usSpinnerService, uiGridConstants) {
  var service = {};
  var projectId = null;
  var buildingId = null;
  var cache = $cacheFactory('constructionsCache');
  var cacheKeys = [];

  service.defaultParams = function () {
    return {
      project_id: this.getProjectId(),
      building_id: this.getBuildingId()
    };
  };

  service.setIds = function ($stateParams) {
    if ($stateParams.project_id) {
      this.setProjectId($stateParams.project_id);
    }
    if ($stateParams.building_id) {
      this.setBuildingId($stateParams.building_id);
    }
  };
  service.setProjectId = function (value) {
    if (value != projectId) {
      projectId = value;
    }
  };

  service.getProjectId = function () {
    return projectId;
  };

  service.setBuildingId = function (value) {
    if (value != buildingId) {
      buildingId = value;
    }
  };

  service.getBuildingId = function () {
    return buildingId;
  };

  service.lookupBuilding = function ($q, data, requireBuilding) {
    var deferred = $q.defer();
    var scope = this;
    if (!this.getProjectId()) {
      deferred.reject('No project ID');
    } else {
      if (!this.getBuildingId()) {
        data.list('buildings', this.defaultParams()).then(
          function (response) {
            if (response.length > 0 && response[0].hasOwnProperty('id')) {
              scope.setBuildingId(response[0].id);
              deferred.resolve("success");
            } else {
              if (requireBuilding) {
                deferred.reject("No building ID");
              } else {
                deferred.resolve("Unable to look up building but building not required.");
              }
            }
          },
          function (response) {
            if (requireBuilding) {
              deferred.reject("No building ID");
            } else {
              deferred.resolve("Error looking up building but building not required.");
            }
          });
      } else {
        deferred.resolve('Building ID already set');
      }
    }
    return deferred.promise;
  };

  service.projectPath = function () {
    var path = "/projects";
    if (projectId) {
      path += "/" + projectId;
    }
    return path;
  };

  service.buildingPath = function () {
    var path = "";
    if (projectId) {
      path = "/projects/" + projectId + "/buildings";
      if (buildingId) {
        path += "/" + buildingId;
      }
    } else {
      path = "/buildings"; //can't actually navigate here without project id...
    }
    return path;
  };

  service.constructionsPath = function () {
    if (projectId && buildingId) {
      return "/projects/" + projectId + "/buildings/" + buildingId + "/constructions";
    } else {
      //shouldn't be able to get to this
      return "/constructions";
    }
  };

  service.spacesPath = function () {
    if (projectId && buildingId) {
      return "/projects/" + projectId + "/buildings/" + buildingId + "/spaces";
    } else {
      //shouldn't be able to get to this
      return "/spaces";
    }
  };

  service.systemsPath = function () {
    if (projectId && buildingId) {
      return "/projects/" + projectId + "/buildings/" + buildingId + "/systems";
    } else {
      //shouldn't be able to get to this
      return "/systems";
    }
  };

  service.zonesPath = function () {
    if (projectId && buildingId) {
      return "/projects/" + projectId + "/buildings/" + buildingId + "/zones";
    } else {
      //shouldn't be able to get to this
      return "/zones";
    }
  };

  service.reviewPath = function () {
    if (projectId && buildingId) {
      return "/projects/" + projectId + "/buildings/" + buildingId + "/review";
    } else {
      //shouldn't be able to get to this
      return "/review";
    }
  };

  service.compliancePath = function () {
    if (projectId && buildingId) {
      return "/projects/" + projectId + "/buildings/" + buildingId + "/compliance";
    } else {
      //shouldn't be able to get to this
      return "/compliance";
    }
  };

  service.startSpinner = function () {
    usSpinnerService.spin('spinner');
  };

  service.stopSpinner = function () {
    usSpinnerService.stop('spinner');
  };

  service.loadFromCache = function (key) {
    if (cacheKeys.indexOf(key) == -1) {
      return null;
    }
    return cache.get(key);
  };

  service.saveToCache = function (key, value) {
    if (cacheKeys.indexOf(key) == -1) {
      cacheKeys.push(key);
    }
    cache.put(key, value === undefined ? null : value);
  };

  // Contains All condition, split by spaces
  service.textFilter = function () {
    return {
      condition: function (searchTerm, cellValue) {
        var terms = _.uniq(searchTerm.toLowerCase().split(/ +/));
        var value = cellValue.toLowerCase();
        return _.every(terms, function (term) {
          var regex = new RegExp(term);
          return regex.test(value);
        });
      }
    };
  };

  service.enumFilter = function (input) {
    return {
      condition: function (searchTerm, cellValue) {
        if (cellValue === null) return false;
        var terms = _.uniq(searchTerm.toLowerCase().split(/ +/));
        var value = input[cellValue];
        if (input instanceof Array) {
          value = value.value;
        }
        value = value.toLowerCase();
        return _.every(terms, function (term) {
          var regex = new RegExp(term);
          return regex.test(value);
        });
      }
    };
  };

  service.numberFilter = function () {
    return [{
      condition: function (searchTerm, cellValue) {
        var term = searchTerm.replace(/[^\d.-]/g, '');
        if (term.length) {
          term = Number(term);
          if (isNaN(term)) term = 0;
          if (cellValue === null) return false;
          return cellValue >= term;
        }
        return true;
      },
      placeholder: 'At least'
    }, {
      condition: function (searchTerm, cellValue) {
        var term = searchTerm.replace(/[^\d.-]/g, '');
        if (term.length) {
          term = Number(term);
          if (isNaN(term)) term = 0;
          if (cellValue === null) return false;
          return cellValue <= term;
        }
        return true;
      },
      placeholder: 'No more than'
    }];
  };

  service.sort = function (input) {
    return function (a, b) {
      if (a === b) {
        return 0;
      }
      if (a === null) {
        return 1;
      } else if (b === null) {
        return -1;
      }
      var strA = input[a];
      var strB = input[b];
      if (input instanceof Array) {
        strA = strA.value;
        strB = strB.value;
      }
      return strA < strB ? -1 : 1;
    };
  };

  service.html = function (input) {
    return $sce.trustAsHtml(input);
  };

  $templateCache.put('ui-grid/cbeccHeaderCell', "

{{ col.displayName CUSTOM_FILTERS }}

<input type=\"text\" class=\"ui-grid-filter-input\" ng-model=\"colFilter.term\" ng-attr-placeholder=\"{{colFilter.placeholder || ''}}\">

");
  $templateCache.put('ui-grid/cbeccHeaderCellWithUnits', "

{{ col.displayName CUSTOM_FILTERS }}

<input type=\"text\" class=\"ui-grid-filter-input\" ng-model=\"colFilter.term\" ng-attr-placeholder=\"{{colFilter.placeholder || ''}}\">

");

  return service;
}]);
<commit_msg>Moving total exhaust calculation to Shared<commit_after>cbecc.factory('Shared', ['$q', '$cacheFactory', '$templateCache', '$sce', 'usSpinnerService', 'uiGridConstants', function ($q, $cacheFactory, $templateCache, $sce, usSpinnerService, uiGridConstants) {
  var service = {};
  var projectId = null;
  var buildingId = null;
  var cache = $cacheFactory('constructionsCache');
  var cacheKeys = [];

  service.defaultParams = function () {
    return {
      project_id: this.getProjectId(),
      building_id: this.getBuildingId()
    };
  };

  service.setIds = function ($stateParams) {
    if ($stateParams.project_id) {
      this.setProjectId($stateParams.project_id);
    }
    if ($stateParams.building_id) {
      this.setBuildingId($stateParams.building_id);
    }
  };
  service.setProjectId = function (value) {
    if (value != projectId) {
      projectId = value;
    }
  };

  service.getProjectId = function () {
    return projectId;
  };

  service.setBuildingId = function (value) {
    if (value != buildingId) {
      buildingId = value;
    }
  };

  service.getBuildingId = function () {
    return buildingId;
  };

  service.lookupBuilding = function ($q, data, requireBuilding) {
    var deferred = $q.defer();
    var scope = this;
    if (!this.getProjectId()) {
      deferred.reject('No project ID');
    } else {
      if (!this.getBuildingId()) {
        data.list('buildings', this.defaultParams()).then(
          function (response) {
            if (response.length > 0 && response[0].hasOwnProperty('id')) {
              scope.setBuildingId(response[0].id);
              deferred.resolve("success");
            } else {
              if (requireBuilding) {
                deferred.reject("No building ID");
              } else {
                deferred.resolve("Unable to look up building but building not required.");
              }
            }
          },
          function (response) {
            if (requireBuilding) {
              deferred.reject("No building ID");
            } else {
              deferred.resolve("Error looking up building but building not required.");
            }
          });
      } else {
        deferred.resolve('Building ID already set');
      }
    }
    return deferred.promise;
  };

  service.projectPath = function () {
    var path = "/projects";
    if (projectId) {
      path += "/" + projectId;
    }
    return path;
  };

  service.buildingPath = function () {
    var path = "";
    if (projectId) {
      path = "/projects/" + projectId + "/buildings";
      if (buildingId) {
        path += "/" + buildingId;
      }
    } else {
      path = "/buildings"; //can't actually navigate here without project id...
    }
    return path;
  };

  service.constructionsPath = function () {
    if (projectId && buildingId) {
      return "/projects/" + projectId + "/buildings/" + buildingId + "/constructions";
    } else {
      //shouldn't be able to get to this
      return "/constructions";
    }
  };

  service.spacesPath = function () {
    if (projectId && buildingId) {
      return "/projects/" + projectId + "/buildings/" + buildingId + "/spaces";
    } else {
      //shouldn't be able to get to this
      return "/spaces";
    }
  };

  service.systemsPath = function () {
    if (projectId && buildingId) {
      return "/projects/" + projectId + "/buildings/" + buildingId + "/systems";
    } else {
      //shouldn't be able to get to this
      return "/systems";
    }
  };

  service.zonesPath = function () {
    if (projectId && buildingId) {
      return "/projects/" + projectId + "/buildings/" + buildingId + "/zones";
    } else {
      //shouldn't be able to get to this
      return "/zones";
    }
  };

  service.reviewPath = function () {
    if (projectId && buildingId) {
      return "/projects/" + projectId + "/buildings/" + buildingId + "/review";
    } else {
      //shouldn't be able to get to this
      return "/review";
    }
  };

  service.compliancePath = function () {
    if (projectId && buildingId) {
      return "/projects/" + projectId + "/buildings/" + buildingId + "/compliance";
    } else {
      //shouldn't be able to get to this
      return "/compliance";
    }
  };

  service.startSpinner = function () {
    usSpinnerService.spin('spinner');
  };

  service.stopSpinner = function () {
    usSpinnerService.stop('spinner');
  };

  service.loadFromCache = function (key) {
    if (cacheKeys.indexOf(key) == -1) {
      return null;
    }
    return cache.get(key);
  };

  service.saveToCache = function (key, value) {
    if (cacheKeys.indexOf(key) == -1) {
      cacheKeys.push(key);
    }
    cache.put(key, value === undefined ? null : value);
  };

  // Contains All condition, split by spaces
  service.textFilter = function () {
    return {
      condition: function (searchTerm, cellValue) {
        var terms = _.uniq(searchTerm.toLowerCase().split(/ +/));
        var value = cellValue.toLowerCase();
        return _.every(terms, function (term) {
          var regex = new RegExp(term);
          return regex.test(value);
        });
      }
    };
  };

  service.enumFilter = function (input) {
    return {
      condition: function (searchTerm, cellValue) {
        if (cellValue === null) return false;
        var terms = _.uniq(searchTerm.toLowerCase().split(/ +/));
        var value = input[cellValue];
        if (input instanceof Array) {
          value = value.value;
        }
        value = value.toLowerCase();
        return _.every(terms, function (term) {
          var regex = new RegExp(term);
          return regex.test(value);
        });
      }
    };
  };

  service.numberFilter = function () {
    return [{
      condition: function (searchTerm, cellValue) {
        var term = searchTerm.replace(/[^\d.-]/g, '');
        if (term.length) {
          term = Number(term);
          if (isNaN(term)) term = 0;
          if (cellValue === null) return false;
          return cellValue >= term;
        }
        return true;
      },
      placeholder: 'At least'
    }, {
      condition: function (searchTerm, cellValue) {
        var term = searchTerm.replace(/[^\d.-]/g, '');
        if (term.length) {
          term = Number(term);
          if (isNaN(term)) term = 0;
          if (cellValue === null) return false;
          return cellValue <= term;
        }
        return true;
      },
      placeholder: 'No more than'
    }];
  };

  service.sort = function (input) {
    return function (a, b) {
      if (a === b) {
        return 0;
      }
      if (a === null) {
        return 1;
      } else if (b === null) {
        return -1;
      }
      var strA = input[a];
      var strB = input[b];
      if (input instanceof Array) {
        strA = strA.value;
        strB = strB.value;
      }
      return strA < strB ? -1 : 1;
    };
  };

  service.html = function (input) {
    return $sce.trustAsHtml(input);
  };

  service.calculateTotalExhaust = function (space) {
    return Math.round((space.exhaust_per_area * space.area) + (space.exhaust_air_changes_per_hour * space.floor_to_ceiling_height * space.area / 60) + space.exhaust_per_space);
  };

  $templateCache.put('ui-grid/cbeccHeaderCell', "

{{ col.displayName CUSTOM_FILTERS }}

<input type=\"text\" class=\"ui-grid-filter-input\" ng-model=\"colFilter.term\" ng-attr-placeholder=\"{{colFilter.placeholder || ''}}\">

");
  $templateCache.put('ui-grid/cbeccHeaderCellWithUnits', "

{{ col.displayName CUSTOM_FILTERS }}

<input type=\"text\" class=\"ui-grid-filter-input\" ng-model=\"colFilter.term\" ng-attr-placeholder=\"{{colFilter.placeholder || ''}}\">

");

  return service;
}]);
<|endoftext|>


<commit_before>import Utils from "../Utils.js";

/**
 * Unicode operations.
 *
 * @author n1474335 [n1474335@gmail.com]
 * @copyright Crown Copyright 2016
 * @license Apache-2.0
 *
 * @namespace
 */
const Unicode = {

    /**
     * @constant
     * @default
     */
    PREFIXES: ["\\u", "%u", "U+"],

    /**
     * Unescape Unicode Characters operation.
     *
     * @param {string} input
     * @param {Object[]} args
     * @returns {string}
     */
    runUnescape: function(input, args) {
        let prefix = Unicode._prefixToRegex[args[0]],
            regex = new RegExp(prefix+"([a-f\\d]{4,6})", "ig"),
            output = "",
            m,
            i = 0;

        while ((m = regex.exec(input))) {
            // Add up to match
            output += input.slice(i, m.index);
            i = m.index;

            // Add match
            output += Utils.chr(parseInt(m[1], 16));

            i = regex.lastIndex;
        }

        // Add all after final match
        output += input.slice(i, input.length);

        return output;
    },

    /**
     * Lookup table to add prefixes to unicode delimiters so that they can be used in a regex.
     *
     * @private
     * @constant
     */
    _prefixToRegex: {
        "\\u": "\\\\u",
        "%u": "%u",
        "U+": "U\\+"
    },

};

export default Unicode;
<commit_msg>Reduced byte length in 'Unescape Unicode Characters'<commit_after>import Utils from "../Utils.js";

/**
 * Unicode operations.
 *
 * @author n1474335 [n1474335@gmail.com]
 * @copyright Crown Copyright 2016
 * @license Apache-2.0
 *
 * @namespace
 */
const Unicode = {

    /**
     * @constant
     * @default
     */
    PREFIXES: ["\\u", "%u", "U+"],

    /**
     * Unescape Unicode Characters operation.
     *
     * @param {string} input
     * @param {Object[]} args
     * @returns {string}
     */
    runUnescape: function(input, args) {
        let prefix = Unicode._prefixToRegex[args[0]],
            regex = new RegExp(prefix+"([a-f\\d]{4})", "ig"),
            output = "",
            m,
            i = 0;

        while ((m = regex.exec(input))) {
            // Add up to match
            output += input.slice(i, m.index);
            i = m.index;

            // Add match
            output += Utils.chr(parseInt(m[1], 16));

            i = regex.lastIndex;
        }

        // Add all after final match
        output += input.slice(i, input.length);

        return output;
    },

    /**
     * Lookup table to add prefixes to unicode delimiters so that they can be used in a regex.
     *
     * @private
     * @constant
     */
    _prefixToRegex: {
        "\\u": "\\\\u",
        "%u": "%u",
        "U+": "U\\+"
    },

};

export default Unicode;
<|endoftext|>


<commit_before>/**
 * @author Mat Groves http://matgroves.com/
 */

/**
 * the Rectangle object is an area defined by its position, as indicated by its top-left corner point (x, y) and by its width and its height.
 * @class Rectangle
 * @constructor
 * @param x {Number} position of the rectangle
 * @param y {Number} position of the rectangle
 * @param width {Number} of the rectangle
 * @param height {Number} of the rectangle
 */
PIXI.Rectangle = function(x, y, width, height)
{
	/**
	 * @property x
	 * @type Number
	 * @default 0
	 */
	this.x = x || 0;

	/**
	 * @property y
	 * @type Number
	 * @default 0
	 */
	this.y = y || 0;

	/**
	 * @property width
	 * @type Number
	 * @default 0
	 */
	this.width = width || 0;

	/**
	 * @property height
	 * @type Number
	 * @default 0
	 */
	this.height = height || 0;
}

/**
 * @method clone
 * @return a copy of the rectangle
 */
PIXI.Rectangle.prototype.clone = function()
{
	return new PIXI.Rectangle(this.x, this.y, this.width, this.height);
}

/**
 * @method contains
 * @param x {Number} The X coord of the point to test
 * @param y {Number} The Y coord of the point to test
 * @return if the x/y coords are within this polygon
 */
PIXI.Rectangle.contains = function(x, y)
{
    if(this.width <= 0 || this.height <= 0)
        return false;

	var x1 = this.x;
	if(x > x1 && x < x1 + this.width)
	{
		var y1 = this.y;

		if(y > y1 && y < y1 + this.height)
		{
			return true;
		}
	}

	return false;
}

// constructor
PIXI.Rectangle.constructor = PIXI.Rectangle;

<commit_msg>add prototype keyword to Rectangle<commit_after>/**
 * @author Mat Groves http://matgroves.com/
 */

/**
 * the Rectangle object is an area defined by its position, as indicated by its top-left corner point (x, y) and by its width and its height.
 * @class Rectangle
 * @constructor
 * @param x {Number} position of the rectangle
 * @param y {Number} position of the rectangle
 * @param width {Number} of the rectangle
 * @param height {Number} of the rectangle
 */
PIXI.Rectangle = function(x, y, width, height)
{
	/**
	 * @property x
	 * @type Number
	 * @default 0
	 */
	this.x = x || 0;

	/**
	 * @property y
	 * @type Number
	 * @default 0
	 */
	this.y = y || 0;

	/**
	 * @property width
	 * @type Number
	 * @default 0
	 */
	this.width = width || 0;

	/**
	 * @property height
	 * @type Number
	 * @default 0
	 */
	this.height = height || 0;
}

/**
 * @method clone
 * @return a copy of the rectangle
 */
PIXI.Rectangle.prototype.clone = function()
{
	return new PIXI.Rectangle(this.x, this.y, this.width, this.height);
}

/**
 * @method contains
 * @param x {Number} The X coord of the point to test
 * @param y {Number} The Y coord of the point to test
 * @return if the x/y coords are within this polygon
 */
PIXI.Rectangle.prototype.contains = function(x, y)
{
    if(this.width <= 0 || this.height <= 0)
        return false;

	var x1 = this.x;
	if(x > x1 && x < x1 + this.width)
	{
		var y1 = this.y;

		if(y > y1 && y < y1 + this.height)
		{
			return true;
		}
	}

	return false;
}

// constructor
PIXI.Rectangle.constructor = PIXI.Rectangle;

<|endoftext|>


<commit_before>/*!
 * Copyright 2015 Digital Services, University of Cambridge Licensed
 * under the Educational Community 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://opensource.org/licenses/ECL-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.
 */

define(['gh.core', 'gh.constants', 'gh.api.orgunit'], function(gh, constants, orgUnitAPI) {

    // Cache the tripos data
    var triposData = null;
    // Keep track of when the user started
    var timeFromStart = null;

    /**
     * Open the closest picker component when it receives keyboard focus
     *
     * @private
     */
    var openPicker = function() {
        var $picker = $($(this).closest('.chosen-container').prev());
        $picker.trigger('chosen:open');
    };

    /**
     * Set up the modules of events in the list.
     *
     * @param  {Event}     ev      Standard jQuery event
     * @param  {Object}    data    Data object describing the selected part to fetch modules for
     * @private
     */
    var setUpModules = function(ev, data) {
        var partId = parseInt(data.selected, 10);

        // Track the user choosing a part
        gh.utils.trackEvent(['Manage', 'Borrow series', 'Chosen part']);

        // Retrieve the organisational unit information for the modules
        orgUnitAPI.getOrgUnits(gh.data.me.appId, true, null, partId, ['module'], function(err, modules) {
            if (err) {
                gh.utils.notification('Could not fetch modules', constants.messaging.default.error, 'error');
            }

            gh.utils.renderTemplate('admin-borrow-series-module-item', {
                'data': {
                    'modules': modules.results,
                    'state': History.getState().data
                },
                'utils': gh.utils
            }, $('#gh-borrow-series-modal #gh-modules-list'));
        });
    };

    /**
     * Set up the part picker in the modal
     *
     * @param  {Event}     ev      Standard jQuery event
     * @param  {Object}    data    Data object describing the selected tripos to fetch parts for
     * @private
     */
    var setUpPartPicker = function(ev, data) {
        var triposId = parseInt(data.selected, 10);

        // Track the user choosing a tripos
        gh.utils.trackEvent(['Manage', 'Borrow series', 'Chosen tripos']);

        // Get the parts associated to the selected tripos
        var parts = _.filter(triposData.parts, function(part) {
            return parseInt(data.selected, 10) === part.ParentId;
        });

        // Render the results in the part picker
        gh.utils.renderTemplate('subheader-part', {
            'data': {
                'parts': parts,
                'excludePart': History.getState().data.part
            }
        }, $('#gh-borrow-series-part'), function() {
            // Show the subheader part picker
            $('#gh-borrow-series-part').show();

            // Destroy the field if it's been initialised previously
            $('#gh-borrow-series-part').chosen('destroy').off('change', setUpModules);

            // Initialise the Chosen plugin on the part picker
            $('#gh-borrow-series-part').chosen({
                'no_results_text': 'No matches for',
                'disable_search_threshold': 10
            }).on('change', setUpModules);
        });
    };

    /**
     * Render and show the 'borrow series' modal dialog
     *
     * @private
     */
    var showBorrowSeriesModal = function() {
        // Get the ID of the module to borrow series to
        var moduleId = $(this).closest('.list-group-item').attr('data-moduleid');
        // Get the part ID of the module
        var partId = $(this).closest('#gh-modules-list-container').attr('data-partid');

        // Fetch the triposes
        gh.utils.getTriposStructure(null, true, function(err, _triposData) {
            // Cache the triposdata for use in the other picker
            triposData = _triposData;

            // Slim down the dataset before passing it into the template
            var triposPickerData = {
                'courses': triposData.courses
            };

            // Add the optional subject to the course
            _.each(triposPickerData.courses, function(course) {
                course.subjects = _.filter(triposData.subjects, function(subject) {
                    return course.id === subject.ParentId;
                });
            });

            // Render the modal and pickers
            gh.utils.renderTemplate('admin-borrow-series-modal', {
                'data': {
                    'triposPickerData': triposPickerData,
                    'moduleId': moduleId,
                    'partId': partId,
                    'gh': gh
                }
            }, $('#gh-modal'), function() {
                // Show the modal
                $('#gh-borrow-series-modal').modal();
            });
        });
    };

    /**
     * Add a series to the list of series that will be borrowed by applying the `gh-borrow-series-borrowed`
     * class to its container
     *
     * @private
     */
    var addSeriesToBorrow = function() {
        $(this).closest('.list-group-item ').addClass('gh-borrow-series-borrowed');
        // Track the user adding a series to borrow
        gh.utils.trackEvent(['Manage', 'Borrow series', 'Series added']);
    };

    /**
     * Remove a series from the list of series that will be borrowed by removing the
     * `gh-borrow-series-borrowed` class from its container
     *
     * @private
     */
    var removeSeriesToBorrow = function() {
        $(this).closest('.list-group-item ').removeClass('gh-borrow-series-borrowed');
        // Track the user removing a series to borrow
        gh.utils.trackEvent(['Manage', 'Borrow series', 'Series removed']);
    };

    /**
     * Collect all series marked as 'to borrow' and borrow them into a module
     *
     * @private
     */
    var borrowSeries = function()  {
        // Calculate how long it takes the user to borrow the series
        timeFromStart = (new Date() - timeFromStart) / 1000;

        // Get the ID of the module to borrow series to
        var moduleId = $(this).data('moduleid');

        // Get the parent ID of the module
        var partId = $(this).data('partid');

        // Get all series marked as 'to borrow'
        var seriesToBorrow = $('.gh-borrow-series-borrowed', $('#gh-borrow-series-modal'));

        // Get all series' IDs
        var seriesIDs = _.map(seriesToBorrow, function(series) { return $(series).data('id'); });

        // Borrow the event series in the module
        orgUnitAPI.addOrgUnitSeries(moduleId, seriesIDs, function(err, data) {
            // Show a success or failure notification
            if (err) {
                return gh.utils.notification('Could not borrow series', constants.messaging.default.error, 'error');
            }
            gh.utils.notification('Successfully borrowed series', null, 'success');

            // Track the user completing borrowing of a series
            gh.utils.trackEvent(['Manage', 'Borrow series', 'Completed'], {
                'time_from_start': timeFromStart,
                'number_of_borrowed_series': seriesIDs.length
            });

            // Hide the module modal
            $('#gh-borrow-series-modal').modal('hide');

            // Retrieve the organisational unit information for the modules
            orgUnitAPI.getOrgUnits(gh.data.me.AppId, true, null, partId, ['module'], function(err, modules) {
                if (err) {
                    gh.utils.notification('Could not fetch modules', constants.messaging.default.error, 'error');
                }

                // Refresh the modules list
                $(document).trigger('gh.listview.refresh', {
                    'partId': partId,
                    'modules': modules
                });
            });
        });
    };

    /////////////
    // BINDING //
    /////////////

    /**
     * Add handlers to various elements in the borrow series modal
     *
     * @private
     */
    var addBinding = function() {
        $('body').on('click', '.gh-borrow-series', showBorrowSeriesModal);
        $('body').on('shown.bs.modal', '#gh-borrow-series-modal', function () {
            // Show the subheader tripos picker
            $('#gh-borrow-series-tripos').show();

            // Destroy the field if it's been initialised previously
            $('#gh-borrow-series-tripos').chosen('destroy').off('change', setUpModules);

            // Initialise the Chosen plugin on the tripos picker
            $('#gh-borrow-series-tripos').chosen({
                'no_results_text': 'No matches for'
            }).change(setUpPartPicker);

            // Track the user starting borrowing of a series
            gh.utils.trackEvent(['Manage', 'Borrow series', 'Started']);
            // Track how long the user takes to borrow the series
            timeFromStart = new Date();
        });
        $('body').on('click', '#gh-borrow-series-modal [data-dismiss="modal"]', function() {
            // Track the user cancelling borrowing of a module
            gh.utils.trackEvent(['Manage', 'Borrow series', 'Cancelled']);
        });
        // Mark a series as 'to borrow'
        $('body').on('click', '.gh-borrow-series-select', addSeriesToBorrow);
        // Unmark a series as 'to borrow'
        $('body').on('click', '.gh-borrow-series-deselect', removeSeriesToBorrow);
        // Mark or unmark a series as 'to borrow' depending on the status
        $('body').on('click', '.gh-list-description', function() {
            $(this).next().find('button:visible').click();
        });
        // Borrow all series marked as 'to borrow' into a module
        $('body').on('click', '#gh-borrow-series-submit', borrowSeries);

        var throttlePickerFocus = _.throttle(openPicker, 200, {'trailing': false});
        $('body').on('focus', '.chosen-search input', throttlePickerFocus);
    };

    addBinding();
});
<commit_msg>Fix borrowing in IE9<commit_after>/*!
 * Copyright 2015 Digital Services, University of Cambridge Licensed
 * under the Educational Community 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://opensource.org/licenses/ECL-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.
 */

define(['gh.core', 'gh.constants', 'gh.api.orgunit'], function(gh, constants, orgUnitAPI) {

    // Cache the tripos data
    var triposData = null;
    // Keep track of when the user started
    var timeFromStart = null;

    /**
     * Open the closest picker component when it receives keyboard focus
     *
     * @private
     */
    var openPicker = function() {
        var $picker = $($(this).closest('.chosen-container').prev());
        $picker.trigger('chosen:open');
    };

    /**
     * Set up the modules of events in the list.
     *
     * @param  {Event}     ev      Standard jQuery event
     * @param  {Object}    data    Data object describing the selected part to fetch modules for
     * @private
     */
    var setUpModules = function(ev, data) {
        var partId = parseInt(data.selected, 10);

        // Track the user choosing a part
        gh.utils.trackEvent(['Manage', 'Borrow series', 'Chosen part']);

        // Retrieve the organisational unit information for the modules
        orgUnitAPI.getOrgUnits(gh.data.me.appId, true, null, partId, ['module'], function(err, modules) {
            if (err) {
                gh.utils.notification('Could not fetch modules', constants.messaging.default.error, 'error');
            }

            gh.utils.renderTemplate('admin-borrow-series-module-item', {
                'data': {
                    'modules': modules.results,
                    'state': History.getState().data
                },
                'utils': gh.utils
            }, $('#gh-borrow-series-modal #gh-modules-list'));
        });
    };

    /**
     * Set up the part picker in the modal
     *
     * @param  {Event}     ev      Standard jQuery event
     * @param  {Object}    data    Data object describing the selected tripos to fetch parts for
     * @private
     */
    var setUpPartPicker = function(ev, data) {
        var triposId = parseInt(data.selected, 10);

        // Track the user choosing a tripos
        gh.utils.trackEvent(['Manage', 'Borrow series', 'Chosen tripos']);

        // Get the parts associated to the selected tripos
        var parts = _.filter(triposData.parts, function(part) {
            return parseInt(data.selected, 10) === part.ParentId;
        });

        // Render the results in the part picker
        gh.utils.renderTemplate('subheader-part', {
            'data': {
                'parts': parts,
                'excludePart': History.getState().data.part
            }
        }, $('#gh-borrow-series-part'), function() {
            // Show the subheader part picker
            $('#gh-borrow-series-part').show();

            // Destroy the field if it's been initialised previously
            $('#gh-borrow-series-part').chosen('destroy').off('change', setUpModules);

            // Initialise the Chosen plugin on the part picker
            $('#gh-borrow-series-part').chosen({
                'no_results_text': 'No matches for',
                'disable_search_threshold': 10
            }).on('change', setUpModules);
        });
    };

    /**
     * Render and show the 'borrow series' modal dialog
     *
     * @private
     */
    var showBorrowSeriesModal = function() {
        // Get the ID of the module to borrow series to
        var moduleId = $(this).closest('.list-group-item').attr('data-moduleid');
        // Get the part ID of the module
        var partId = $(this).closest('#gh-modules-list-container').attr('data-partid');

        // Fetch the triposes
        gh.utils.getTriposStructure(null, true, function(err, _triposData) {
            // Cache the triposdata for use in the other picker
            triposData = _triposData;

            // Slim down the dataset before passing it into the template
            var triposPickerData = {
                'courses': triposData.courses
            };

            // Add the optional subject to the course
            _.each(triposPickerData.courses, function(course) {
                course.subjects = _.filter(triposData.subjects, function(subject) {
                    return course.id === subject.ParentId;
                });
            });

            // Render the modal and pickers
            gh.utils.renderTemplate('admin-borrow-series-modal', {
                'data': {
                    'triposPickerData': triposPickerData,
                    'moduleId': moduleId,
                    'partId': partId,
                    'gh': gh
                }
            }, $('#gh-modal'), function() {
                // Show the modal
                $('#gh-borrow-series-modal').modal();
            });
        });
    };

    /**
     * Add a series to the list of series that will be borrowed by applying the `gh-borrow-series-borrowed`
     * class to its container
     *
     * @private
     */
    var addSeriesToBorrow = function() {
        $(this).closest('.list-group-item ').addClass('gh-borrow-series-borrowed');
        // Track the user adding a series to borrow
        gh.utils.trackEvent(['Manage', 'Borrow series', 'Series added']);
    };

    /**
     * Remove a series from the list of series that will be borrowed by removing the
     * `gh-borrow-series-borrowed` class from its container
     *
     * @private
     */
    var removeSeriesToBorrow = function() {
        $(this).closest('.list-group-item ').removeClass('gh-borrow-series-borrowed');
        // Track the user removing a series to borrow
        gh.utils.trackEvent(['Manage', 'Borrow series', 'Series removed']);
    };

    /**
     * Collect all series marked as 'to borrow' and borrow them into a module
     *
     * @private
     */
    var borrowSeries = function()  {
        // Calculate how long it takes the user to borrow the series
        timeFromStart = (new Date() - timeFromStart) / 1000;

        // Get the ID of the module to borrow series to
        var moduleId = $(this).data('moduleid');

        // Get the parent ID of the module
        var partId = $(this).data('partid');

        // Get all series marked as 'to borrow'
        var seriesToBorrow = $('.gh-borrow-series-borrowed', $('#gh-borrow-series-modal'));

        // Get all series' IDs
        var seriesIDs = _.map(seriesToBorrow, function(series) { return $(series).data('id'); });

        // Borrow the event series in the module
        orgUnitAPI.addOrgUnitSeries(moduleId, seriesIDs, function(err, data) {
            // Show a success or failure notification
            if (err) {
                return gh.utils.notification('Could not borrow series', constants.messaging.default.error, 'error');
            }
            gh.utils.notification('Successfully borrowed series', null, 'success');

            // Track the user completing borrowing of a series
            gh.utils.trackEvent(['Manage', 'Borrow series', 'Completed'], {
                'time_from_start': timeFromStart,
                'number_of_borrowed_series': seriesIDs.length
            });

            // Hide the module modal
            $('#gh-borrow-series-modal').modal('hide');

            // Retrieve the organisational unit information for the modules
            orgUnitAPI.getOrgUnits(gh.data.me.AppId, true, null, partId, ['module'], function(err, modules) {
                if (err) {
                    gh.utils.notification('Could not fetch modules', constants.messaging.default.error, 'error');
                }

                // Refresh the modules list
                $(document).trigger('gh.listview.refresh', {
                    'partId': partId,
                    'modules': modules
                });
            });
        });
    };

    /////////////
    // BINDING //
    /////////////

    /**
     * Add handlers to various elements in the borrow series modal
     *
     * @private
     */
    var addBinding = function() {
        $('body').on('click', '.gh-borrow-series', showBorrowSeriesModal);
        $('body').on('shown.bs.modal', '#gh-borrow-series-modal', function () {
            $('#gh-borrow-series-tripos option + option').onAvailable(function() {
                // Show the subheader tripos picker
                $('#gh-borrow-series-tripos').show();

                // Destroy the field if it's been initialised previously
                $('#gh-borrow-series-tripos').chosen('destroy').off('change', setUpModules);

                // Initialise the Chosen plugin on the tripos picker
                $('#gh-borrow-series-tripos').chosen({
                    'no_results_text': 'No matches for'
                }).change(setUpPartPicker);

                // Track the user starting borrowing of a series
                gh.utils.trackEvent(['Manage', 'Borrow series', 'Started']);
                // Track how long the user takes to borrow the series
                timeFromStart = new Date();
            });
        });
        $('body').on('click', '#gh-borrow-series-modal [data-dismiss="modal"]', function() {
            // Track the user cancelling borrowing of a module
            gh.utils.trackEvent(['Manage', 'Borrow series', 'Cancelled']);
        });
        // Mark a series as 'to borrow'
        $('body').on('click', '.gh-borrow-series-select', addSeriesToBorrow);
        // Unmark a series as 'to borrow'
        $('body').on('click', '.gh-borrow-series-deselect', removeSeriesToBorrow);
        // Mark or unmark a series as 'to borrow' depending on the status
        $('body').on('click', '.gh-list-description', function() {
            $(this).next().find('button:visible').click();
        });
        // Borrow all series marked as 'to borrow' into a module
        $('body').on('click', '#gh-borrow-series-submit', borrowSeries);

        var throttlePickerFocus = _.throttle(openPicker, 200, {'trailing': false});
        $('body').on('focus', '.chosen-search input', throttlePickerFocus);
    };

    addBinding();
});
<|endoftext|>