'use strict'; app.controller('slidersGroupController', ['$scope', '$http', '$location', '$timeout', function ($scope, $http, $location, $timeout) { $scope.id = $location.absUrl().substr($location.absUrl().lastIndexOf('ls/') + 3, 36); $scope.C_ADD_ITEMS_SELECT2_ID_TEMPLATE = "addItems_{0}"; $scope.totalAvailableSumm = 100; $scope.slidersData = {}; $scope.arrSliders = []; $scope.movableSliders = []; $scope.groupId = null; $scope.availableToAddItems = []; $scope.isReadOnly = false; $scope.showConfirmDialog = true; $scope.activeSliderKey = ""; $scope.init = function (initData) { $scope.groupId = initData.groupId; $scope.availableToAddItems = initData.availableToAddItems; for (var index = 0; index < initData.slidersData.length; index++) { var key = initData.slidersData[index].DisplayId; $scope.slidersData[key] = initData.slidersData[index]; $scope.arrSliders.push(initData.slidersData[index]); $scope.slidersData[key].Css = ["ui-slider"]; $scope.slidersData[key].SliderContainerId = ""; } $scope.initWatchers(); $scope.resetMovableSliders(); }; $scope.initWatchers = function () { for (var key in $scope.slidersData) { $scope.addSliderWatch(key); } } $scope.addSliderWatch = function (key) { var releaseWatchFunction = $scope.$watch("slidersData['" + key + "']", function (changedSlider) { if (key && (key.length > 0) && (key != $scope.activeSliderKey)) { var selector = changedSlider.SliderContainerId; $(selector).slider("value", changedSlider.AllocatePercentage); } }, true); $scope.slidersData[key].unwatch = releaseWatchFunction; } $scope.addItemToIndexedArray = function (key) { $scope.arrSliders.push($scope.slidersData[key]); } $scope.removeItemFromIndexedArray = function (dataElement) { var index = $scope.arrSliders.indexOf(dataElement); if (index >= 0) { $scope.arrSliders.splice(index, 1); } } $scope.recreateSliders = function () { for (var key in $scope.slidersData) { if ($scope.slidersData[key].IsExcludable) { var containerSelector = $scope.slidersData[key].SliderContainerId; var containerElement = $(containerSelector); $scope.createSliderControl(containerElement, $scope, $scope.slidersData[key]); } } } $scope.IsEmpty = function (value) { return !value || (value == null) || (value == ""); } $scope.getSliderKey = function (container) { return $(container).attr("data-slider-key"); } $scope.setSliderValue = function (key, value) { $scope.slidersData[key].AllocatePercentage = value; } $scope.resetMovableSliders = function () { $scope.movableSliders = []; for (var key in $scope.slidersData) { $scope.movableSliders.push(key); var index = $scope.slidersData[key].Css.indexOf("ui-slider-success"); if (index >= 0) { $scope.slidersData[key].Css.splice(index, 1); } } } $scope.isSliderMovable = function (key) { return $.inArray(key, $scope.movableSliders); } $scope.freezeSlider = function (key) { var foundIndex = $scope.isSliderMovable(key); if (foundIndex >= 0) { $scope.slidersData[key].Css.push("ui-slider-success"); $scope.movableSliders.splice(foundIndex, 1); } } $scope.disableSliders = function (excludeKey) { for (var key in $scope.slidersData) { if (!excludeKey || (excludeKey.length < 1) || (excludeKey != key)) { var selector = $scope.slidersData[key].SliderContainerId; $(selector).slider("disable"); } } } $scope.enableSliders = function () { for (var key in $scope.slidersData) { var selector = $scope.slidersData[key].SliderContainerId; $(selector).slider("enable"); } } $scope.getAllSlidersTotal = function (excludeActiveSlider) { var total = 0; for (var key in $scope.slidersData) { if (!excludeActiveSlider || !$scope.activeSliderKey || ($scope.activeSliderKey.length < 1) || ($scope.activeSliderKey != key)) { total += $scope.slidersData[key].AllocatePercentage; } } return total; } $scope.getMovableSlidersTotal = function (excludeActiveSlider) { var total = 0; $scope.movableSliders.forEach(function (key, i) { if (!excludeActiveSlider || !$scope.activeSliderKey || ($scope.activeSliderKey.length < 1) || ($scope.activeSliderKey != key)) { total += $scope.slidersData[key].AllocatePercentage; } }); return total; } $scope.getUnmovableSlidersTotal = function (excludeActiveSlider) { var total = 0; for (var key in $scope.slidersData) { if (!excludeActiveSlider || !$scope.activeSliderKey || ($scope.activeSliderKey.length < 1) || ($scope.activeSliderKey != key)) { if ($scope.movableSliders.indexOf(key) < 0) { total += $scope.slidersData[key].AllocatePercentage; } } } return total; } $scope.getSlidersCount = function () { var count = Object.keys($scope.slidersData).length; return count; } $scope.onSliderSlide = function (event, ui) { var key = $scope.getSliderKey(event.target); if (!$scope.activeSliderKey || ($scope.activeSliderKey.length < 1) || (key != $scope.activeSliderKey)) return; var value = Math.round(parseInt(ui.value)); $scope.setSliderValue(key, value); $scope.recalculateDependentSliders(); }; $scope.onSliderStartSliding = function (event, ui) { $scope.activeSliderKey = $scope.getSliderKey(event.target); $scope.showConfirmDialog = false; $scope.freezeSlider($scope.activeSliderKey); if (typeof onScenarioDataChanged === 'function') onScenarioDataChanged(); }; $scope.onSliderStopSliding = function (event, ui) { if ($scope.showConfirmDialog) { // Some values of the sliders group are wrong (not 100% summary). var result = confirm("By moving this slider, you will adjust previously set values for other teams on this scenario."); $scope.$apply(function () { $scope.processConfirmed(result); }); } else { // Fix incorrect sliders summ $scope.correctSlidersByTotal(); } $scope.activeSliderKey = ""; }; $scope.processConfirmed = function (result) { var total = $scope.getAllSlidersTotal(true); var valueToSet = $scope.totalAvailableSumm - total; var activeKey = $scope.activeSliderKey; $scope.activeSliderKey = ""; $scope.setSliderValue(activeKey, valueToSet); if (result) { $scope.resetMovableSliders(); $scope.enableSliders(); } else { $scope.disableSliders(activeKey); } } $scope.getDistribution = function (valueToDistribute) { // Check, can we distribute it? var slidersCapacity = 0; var distribution = {}; var structForSort = []; $scope.movableSliders.forEach(function (key, i) { var currentValue = $scope.slidersData[key].AllocatePercentage; var currentCapacity; if (valueToDistribute > 0) currentCapacity = $scope.totalAvailableSumm - currentValue; else currentCapacity = currentValue; slidersCapacity += currentCapacity; if (currentCapacity != 0) structForSort.push({ key: key, value: currentCapacity }); }); if (Math.abs(valueToDistribute) > slidersCapacity) // We cant distribute the value, so the slider group remains correct return null; var sortedKeys = []; while (structForSort.length > 0) { var foundIndex = -1; var minValue = $scope.totalAvailableSumm; for (var index = 0; index < structForSort.length; index++) { if ((foundIndex < 0) || (structForSort[index].value <= minValue)) { foundIndex = index; minValue = structForSort[index].value; } } if (foundIndex >= 0) { sortedKeys.push(structForSort[foundIndex].key); structForSort.splice(foundIndex, 1); } else break; } // Calculate the distribution map var slidersCount = sortedKeys.length; var remainingSumm = valueToDistribute; for (var index = slidersCount - 1; index >= 0; index--) { var idealPortion = remainingSumm / (index + 1); var key = sortedKeys[index]; var currentValue = $scope.slidersData[key].AllocatePercentage; var newCurrentSliderValue = currentValue + idealPortion; if ((newCurrentSliderValue < 0) || (newCurrentSliderValue > $scope.totalAvailableSumm)) { if (valueToDistribute > 0) { distribution[key] = $scope.totalAvailableSumm - currentValue; } else { distribution[key] = -currentValue; } remainingSumm -= distribution[key]; } else { if (index > 0) { distribution[key] = idealPortion; remainingSumm -= distribution[key]; } else { // The last slider distribution[key] = remainingSumm; } } } return distribution; } $scope.recalculateDependentSliders = function () { var total = 0; var changeToDistribute = 0; var activeSliderValue = $scope.slidersData[$scope.activeSliderKey].AllocatePercentage; var slidersCount = $scope.getSlidersCount(); if ((slidersCount > 1) && ($scope.movableSliders.length == 0)) { $scope.showConfirmDialog = true; } if (!$scope.showConfirmDialog) { total = $scope.getAllSlidersTotal(false); changeToDistribute = $scope.totalAvailableSumm - total; if (changeToDistribute != 0) { var distributionMap = $scope.getDistribution(changeToDistribute); if (distributionMap != null) { for (var key in distributionMap) { var newSliderValueRaw = $scope.slidersData[key].AllocatePercentage + distributionMap[key]; var newSliderValueRounded = Math.round(newSliderValueRaw); $scope.setSliderValue(key, newSliderValueRounded); } } else $scope.showConfirmDialog = true; } } } $scope.correctSlidersByTotal = function () { var totalSumm = $scope.getAllSlidersTotal(false); if (totalSumm != $scope.totalAvailableSumm) { // 100% var unmovableSumm = $scope.getUnmovableSlidersTotal(false); var factMovableSumm = $scope.getMovableSlidersTotal(false); var plannedMovableSumm = $scope.totalAvailableSumm - unmovableSumm; var popravka = plannedMovableSumm - factMovableSumm; for (var index = $scope.movableSliders.length - 1; index >= 0; index--) { var key = $scope.movableSliders[index]; var potentialValue = $scope.slidersData[key].AllocatePercentage + popravka; if ((potentialValue >= 0) && (potentialValue <= 100)) { $timeout(function () { $scope.slidersData[key].AllocatePercentage = potentialValue; $scope.setSliderValue(key, potentialValue); }, 100); break; } } } } /* --- items in the group management --- */ $scope.showAddItemsDiv = false; $scope.getAddItemsControlSelector = function () { return ("#" + $scope.C_ADD_ITEMS_SELECT2_ID_TEMPLATE.replace("{0}", $scope.groupId)); } $scope.showControlsToAddItems = function () { $scope.showAddItemsDiv = true; } $scope.hideControlsToAddItems = function () { $scope.showAddItemsDiv = false; // Clear select2 selection var selector = $scope.getAddItemsControlSelector(); $(selector).select2('val', ''); $scope.selectedTeams = null; } $scope.removeItem = function (key) { var sliderValue = $scope.slidersData[key].AllocatePercentage; if (sliderValue > 0) { alert("It is not allowed to delete a team with non-zero allocation"); return; } if ($scope.slidersData[key].unwatch && ($scope.slidersData[key].unwatch != null)) // remove watch $scope.slidersData[key].unwatch(); $scope.removeItemFromIndexedArray($scope.slidersData[key]); $scope.slidersData[key] = undefined; delete $scope.slidersData[key]; $timeout(function() { $scope.recreateSliders(); $scope.resetMovableSliders(); $scope.enableSliders(); }, 200); if (typeof onScenarioDataChanged === 'function') onScenarioDataChanged(); }; // SA. ENV-1149. Shows given allocation on sliders $scope.setAllocation = function (items) { var externalSumm = 0; var multiplier = 1; // Calculate the total summ for items allocation for (var index = 0; index < items.length; index++) { var currentTeam = items[index]; if (currentTeam.Allocation && $.isNumeric(currentTeam.Allocation)) { var foundSliderForTeam = $scope.getSliderByEntityId(currentTeam.TeamId); if (foundSliderForTeam && (foundSliderForTeam != null)) { externalSumm += currentTeam.Allocation; } } } // Set new allocation, so the total summ should be 100% if (externalSumm > 0) { multiplier = $scope.totalAvailableSumm / externalSumm; for (var index = 0; index < items.length; index++) { var currentTeam = items[index]; if (currentTeam.Allocation && $.isNumeric(currentTeam.Allocation)) { var foundSliderForTeam = $scope.getSliderByEntityId(currentTeam.TeamId); if (foundSliderForTeam && (foundSliderForTeam != null)) { var newValue = Math.round(currentTeam.Allocation * multiplier); newValue = newValue > $scope.totalAvailableSumm ? $scope.totalAvailableSumm : newValue; $scope.setSliderValue(foundSliderForTeam.DisplayId, newValue); } } } // Correct slider values for case, the total summ is not exact 100% $scope.correctSlidersByTotal(); } } // SA. ENV-1149. Shows given allocation on sliders $scope.setReadonly = function () { $scope.disableSliders(); $scope.isReadOnly = true; } $scope.addItems = function (items) { if (!items || (items == null) || (items.length < 1)) { // No teams selected return; } var itemsToAdd = []; for (var index = 0; index < items.length; index++) { var currentTeam = items[index]; var foundSliderForTeam = $scope.getSliderByEntityId(currentTeam.TeamId); if (!foundSliderForTeam || (foundSliderForTeam == null)) { var itemName = $scope.getItemNameById(currentTeam.TeamId); var newItemStruct = { Text: itemName, Value: currentTeam.TeamId }; itemsToAdd.push(newItemStruct); } } if (itemsToAdd.length > 0) { $scope.addItemsInternal(itemsToAdd); } } $scope.addItemsByUI = function() { if (!$scope.selectedTeams || ($scope.selectedTeams == null) || ($scope.selectedTeams.length < 1)) { // No teams selected return; } $scope.addItemsInternal($scope.selectedTeams, $scope.hideControlsToAddItems); $scope.hideControlsToAddItems(); if (typeof onScenarioDataChanged === 'function') onScenarioDataChanged(); }; $scope.addItemsInternal = function (itemsToAdd, callBackFunction) { $scope.registerAddedSliders(itemsToAdd); if (callBackFunction && (typeof callBackFunction === 'function')) { callBackFunction(); } }; $scope.registerAddedSliders = function (selectedItems) { var addedKeys = []; for (var index = 0; index < selectedItems.length; index++) { var itemId = selectedItems[index].Value; var key = $scope.getKeyByEntityId(itemId); $scope.slidersData[key] = { Id: itemId, Name: selectedItems[index].Text, EntityId: itemId, AllocatePercentage: 0, IsExcludable: true, ParentId: $scope.groupId, DisplayId: key, Css: ["ui-slider"] }; addedKeys.push(key); $scope.addItemToIndexedArray(key); } for (var index = 0; index < addedKeys.length; index++) { var key = addedKeys[index]; $scope.addSliderWatch(key); } $scope.resetMovableSliders(); $scope.enableSliders(); } $scope.itemIsNotSelected = function (itemId) { return false; } $scope.getTeamList = function () { var result = []; for (var key in $scope.slidersData) { if ($scope.slidersData[key].EntityId) result.push($scope.slidersData[key].EntityId); } return result; } $scope.getSliderByEntityId = function (entityId) { var result = null; for (var key in $scope.slidersData) { var currentId = $scope.slidersData[key].EntityId; if (currentId && (currentId == entityId)) { result = $scope.slidersData[key]; break; } } return result; } $scope.getItemNameById = function (entityId) { var name = ''; for (var index = 0; index < $scope.availableToAddItems.length; index++) { if ($scope.availableToAddItems[index].Value == entityId) { name = $scope.availableToAddItems[index].Text; break; } } return name; } $scope.getKeyByEntityId = function (entityId) { var matchTemplate = /-/gi; var key = entityId.replace(matchTemplate, ""); return key; }; $scope.createSliderControl = function (jqElement, scope, dataElement) { jqElement.slider({ range: 'min', min: 0, max: 100, step: 1, value: dataElement.AllocatePercentage, slide: function (event, ui) { scope.$apply(function () { scope.onSliderSlide(event, ui); }); }, start: scope.onSliderStartSliding, stop: scope.onSliderStopSliding }); }; }]) .directive('sliderKey', function ($compile) { return { restrict: 'A', link: function (scope, element, attrs) { scope.createSliderControl($(element), scope, scope.slidersData[attrs.sliderKey]); scope.slidersData[attrs.sliderKey].SliderContainerId = ("#" + attrs.id); } }; }) .filter('excludeFrom', function () { return function (inputArray, filterCriteria) { if (null === inputArray || undefined === inputArray) return null; return inputArray.filter(function (item) { // if the value of filterCriteria is "falsy", retain the inputArray as it is // then check if the currently checked item in the inputArray is different from the filterCriteria, // if so, keep it in the filtered results var exists = false; if (filterCriteria) { for (var key in filterCriteria) { if (filterCriteria[key].EntityId === item.Value) { exists = true; break; } } } return !filterCriteria || !exists; }); }; });