'use strict'; var directiveModule = angular.module('angularjs-dropdown-multiselect', []); directiveModule.directive('ngDropdownMultiselect', ['$filter', '$document', '$compile', '$parse', '$log', '$timeout', function ($filter, $document, $compile, $parse, $log, $timeout) { return { restrict : 'AE', scope : { selectedModel : '=', options : '=', extraSettings : '=', events : '=', searchFilter : '=?', translationTexts : '=', customText : "=", changeEvent : "=changeEvent", }, templateUrl : "custom_components/angular-multi-select/angular-multi-select.html", link : function ($scope, $element, $attrs) { var $dropdownTrigger = $element.children()[0]; $scope.externalEvents = { onItemSelect : angular.noop, onItemDeselect : angular.noop, onSelectAll : angular.noop, onDeselectAll : angular.noop, onInitDone : angular.noop, onMaxSelectionReached : angular.noop }; $scope.settings = { dynamicTitle : true, scrollable : false, scrollableHeight : '300px', displayProp : 'fieldValue', idProp : 'fieldKey', externalIdProp : 'fieldKey', enableSearch : false, selectionLimit : 0, showCheckAll : true, showUncheckAll : true, closeOnSelect : false, buttonClasses : 'btn btn-default multi-select-form-control', closeOnDeselect : false, smartButtonMaxItems : 0, smartButtonTextConverter : angular.noop, width : '', widthable : false, stringTypeOption : false, maxlength : 50 }; $scope.texts = { checkAll : "common.checkAll", unCheckAll : "common.unCheckAll", selectionCount : "common.selected", selectionOf : '/', searchPlaceholder : 'Search...', buttonDefaultText : "common.select", dynamicButtonTextSuffix : "common.select", totalSelected : null, selectYn : false }; $scope.searchFilter = $scope.searchFilter || ''; angular.extend($scope.settings, $scope.extraSettings || []); angular.extend($scope.externalEvents, $scope.events || []); angular.extend($scope.texts, $scope.translationTexts); // 함수 모음 $scope.fn = { closeLayer : closeLayer, initOptionSelected : initOptionSelected, clickActionAddFocus : clickActionAddFocus, toggleDropdown : toggleDropdown, findBeforeFocus : findBeforeFocus, findNextFocus : findNextFocus, getFindObj : getFindObj, getButtonText : getButtonText, getPropertyForObject : getPropertyForObject, selectAll : selectAll, deselectAll : deselectAll, setSelectedItem : setSelectedItem, isChecked : isChecked, }; // 팝업 창이 올라왔을 때 body 이벤트 사라지는 현상을 수정하기 재실행. $scope.$on("closeLayer", function () { $scope.fn.closeLayer(); }); // options 내용 변경시 텍스트 초기화 $scope.$watch("options", function (newValue) { $scope.texts.totalSelected = null; if (angular.isDefined(newValue)) { angular.forEach(newValue, function (option) { option[$scope.settings.displayProp] = option[$scope.settings.displayProp].replace(//g,">"); }); } }); $scope.$watch("selectedModel", function (newValue) { if (angular.isDefined(newValue)) { if (newValue.length < 1) { $scope.texts.totalSelected = null; } } if (angular.isDefined($scope.changeEvent)) { $scope.changeEvent(); } }); // 선택한 옵션에 있는 클래스를 초기화한다. function initOptionSelected() { $($element).find(".option-target").each(function () { $(this).removeClass("option-selected"); }); } // 클릭시 포커스를 준다. function clickActionAddFocus(id) { $scope.fn.initOptionSelected(); $($element).find(".option-target").each(function () { if ($(this).find("a").attr("data-id") == id) { $(this).addClass("option-selected"); return false; } }); $scope.fn.setSelectedItem(id); } function toggleDropdown($event) { $($element).unbind("keydown"); if (!$scope.open && (($event.type == "keydown" && $event.keyCode == 40) || ($event.type == "click"))) { $scope.open = !$scope.open; if ($scope.open) { // 옵션 선택 초기화 $scope.fn.initOptionSelected(); $timeout(function () { $($element).find(".select-search").focus(); }); if ($event.type != "click") { event.preventDefault(); event.stopPropagation(); } $($element).keydown(function (event) { switch (event.keyCode) { case 13 : event.preventDefault(); event.stopPropagation(); $scope.open = false; $scope.fn.initOptionSelected(); $timeout(function () { $($element).find(".select-display-btn").focus(); }); break; case 9 : // 탭키 닫기 $scope.open = false; $scope.fn.initOptionSelected(); if ($scope.$root.$$phase != '$apply' && $scope.$root.$$phase != '$digest') { $scope.$apply(); } break; case 32 : // 대상 선택 var target = $($element).find(".option-selected").find("a"); var id = target.attr("data-id"); if (angular.isDefined(id)) { if ($scope.settings.stringTypeOption) { $scope.fn.setSelectedItem(String(id)); } else { $scope.fn.setSelectedItem(Number(id)); } event.preventDefault(); event.stopPropagation(); } break; case 38 : // 위 event.preventDefault(); event.stopPropagation(); var target = $scope.fn.findBeforeFocus(); if (target != null) { target.addClass("option-selected"); target.find("a").focus(); } break; case 40 : // 아래 event.preventDefault(); event.stopPropagation(); var target = $scope.fn.findNextFocus(); target.addClass("option-selected"); target.find("a").focus(); break; } }); } } else { if ($event.type == "click" && $scope.open) { $scope.open = false; } } } function findBeforeFocus() { var searchTarget = $($element).find(".option-selected"); var target = null; if (angular.isDefined(searchTarget[0])) { var currentTargetId = $(searchTarget).find("a").attr("data-id"); $($element).find(".option-target").each(function (index) { $(this).removeClass("option-selected"); if ($(this).find("a").attr("data-id") == currentTargetId) { if (index > 0) { target = $(this).prev(); } else { $timeout(function () { $($element).find(".select-search").focus(); }); } } }); } return target; } function findNextFocus() { var searchTarget = $($element).find(".option-selected"); var target = null; if (angular.isDefined(searchTarget[0])) { var currentTargetId = $(searchTarget).find("a").attr("data-id"); var nextCheck = false; $($element).find(".option-target").each(function () { if (nextCheck) { target = $(this); nextCheck = false; } $(this).removeClass("option-selected"); if ($(this).find("a").attr("data-id") == currentTargetId) { nextCheck = true; } }); if (target == null) { $($element).find(".option-target").each(function () { if ($(this).find("a").attr("data-id") == currentTargetId) { target = $(this); } }); } } else { target = $($element).find(".option-target").eq(0); $(".multi-select-option-ul").scrollTop(); } return target; } function getFindObj(id) { var findObj = {}; if ($scope.settings.externalIdProp === '') { findObj[$scope.settings.idProp] = id; } else { findObj[$scope.settings.externalIdProp] = id; } return findObj; } // 레이어 팝업 닫히는 이벤트 제어 function closeLayer() { $(document).ready(function () { $("body").click(function (e) { var target = e.target.parentElement; var parentFound = false; while (angular.isDefined(target) && target !== null && !parentFound) { if (_.contains(target.className.split(' '), 'multiselect-parent') && !parentFound) { if (target === $dropdownTrigger) { parentFound = true; } } target = target.parentElement; } if (!parentFound) { $scope.$apply(function () { $scope.open = false; $scope.searchFilter = ""; }); } }); }); } function getButtonText() { if (!angular.isDefined($scope.selectedModel)) { $scope.selectedModel = []; } if ($scope.settings.dynamicTitle && ($scope.selectedModel.length > 0 || (angular.isObject($scope.selectedModel) && _.keys($scope.selectedModel).length > 0))) { if ($scope.settings.smartButtonMaxItems > 0) { var itemsText = []; angular.forEach($scope.options, function (optionItem) { if ($scope.fn.isChecked($scope.fn.getPropertyForObject(optionItem, $scope.settings.idProp))) { var displayText = $scope.fn.getPropertyForObject(optionItem, $scope.settings.displayProp); var converterResponse = $scope.settings.smartButtonTextConverter(displayText, optionItem); itemsText.push(converterResponse ? converterResponse : displayText); } }); if ($scope.selectedModel.length > $scope.settings.smartButtonMaxItems) { itemsText = itemsText.slice(0, $scope.settings.smartButtonMaxItems); itemsText.push('...'); } return itemsText.join(', '); } else { var totalSelected = angular.isDefined($scope.selectedModel) ? $scope.selectedModel.length : 0; if (totalSelected === 0) { return $scope.customText == undefined ? $scope.texts.buttonDefaultText : $scope.customText; } else { $scope.texts.totalSelected = totalSelected; return $scope.texts.dynamicButtonTextSuffix; } } } else { return $scope.customText == undefined ? $scope.texts.buttonDefaultText : $scope.customText; } } function getPropertyForObject(object, property) { if (angular.isDefined(object) && object.hasOwnProperty(property)) { return object[property]; } return ''; } function selectAll() { $scope.fn.deselectAll(false); $scope.externalEvents.onSelectAll(); angular.forEach($filter("filter")($scope.options, $scope.searchFilter), function (value) { $scope.fn.setSelectedItem(value[$scope.settings.idProp], true); }); // 옵션 포커스 초기화 $scope.fn.initOptionSelected(); } function deselectAll(sendEvent) { // 옵션 포커스 초기화 $scope.fn.initOptionSelected(); $scope.texts.totalSelected = null; sendEvent = sendEvent || true; if (sendEvent) { $scope.externalEvents.onDeselectAll(); } var filterConvertOptions = $filter("filter")($scope.options, $scope.searchFilter); var adds = []; angular.forEach($scope.selectedModel, function (target) { var removeCheck = false; for (var count in filterConvertOptions) { if (target.fieldKey == filterConvertOptions[count].fieldKey) { removeCheck = true; break; } } if (!removeCheck) { adds.push(target); } }); $scope.selectedModel = adds; } function setSelectedItem(id, dontRemove) { var findObj = $scope.fn.getFindObj(id); var finalObj = null; if ($scope.settings.externalIdProp === '') { finalObj = _.find($scope.options, findObj); } else { finalObj = findObj; } dontRemove = dontRemove || false; var exists = _.findIndex($scope.selectedModel, findObj) !== -1; if (!dontRemove && exists) { $scope.selectedModel.splice(_.findIndex($scope.selectedModel, findObj), 1); $scope.externalEvents.onItemDeselect(findObj); $scope.texts.totalSelected = null; } else if (!exists && ($scope.settings.selectionLimit === 0 || $scope.selectedModel.length < $scope.settings.selectionLimit)) { $scope.selectedModel.push(finalObj); $scope.externalEvents.onItemSelect(finalObj); } if ($scope.settings.closeOnSelect) $scope.open = false; if ($scope.$root.$$phase != '$apply' && $scope.$root.$$phase != '$digest') { $scope.$apply(); } } function isChecked(id) { return _.findIndex($scope.selectedModel, $scope.fn.getFindObj(id)) !== -1; } $scope.externalEvents.onInitDone(); $scope.fn.closeLayer(); } }; }]); directiveModule.filter('multiSelectFilter', ["$log", "$filter", function ($log, $filter) { return function (items, searchObj) { var results = []; angular.forEach(items, function (item) { var translateWord = $filter("translate")(item[searchObj.searchKey]); if (translateWord.indexOf(searchObj.searchWord) != -1) { results.push(item); } }); return results; } }]);