/**
* angular-bootstrap-duallistbox
* @version v0.1.0 - 2015-06-13
* @author Francesco Pontillo (francescopontillo@gmail.com)
* @link https://github.com/frapontillo/angular-bootstrap-duallistbox
* @license Apache License 2.0
**/
'use strict';
// Source: common/module.js
angular.module('frapontillo.bootstrap-duallistbox', []);
// Source: dist/.temp/directives/bsDuallistbox.js
angular.module('frapontillo.bootstrap-duallistbox').directive('bsDuallistbox', [
'$compile',
'$timeout',
function ($compile, $timeout) {
return {
restrict: 'A',
require: 'ngModel',
link: function link(scope, element, attrs) {
//000011111111110000000000022222222220000000000000000000003333333333000000000000004444444444444440000000005555555555555550000000666666666666666000000000000000777777777700000000000000000008888888888
var NG_OPTIONS_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/;
// The select collection
var collection = attrs.ngOptions.match(NG_OPTIONS_REGEXP)[7];
var getBooleanValue = function (attributeValue) {
return attributeValue === true || attributeValue === 'true';
};
// The attribute names to $observe with related functions to call
var attributes = {
'bootstrap2': {
changeFn: 'setBootstrap2Compatible',
transformFn: getBooleanValue
},
'postfix': 'setHelperSelectNamePostfix',
'selectMinHeight': {
changeFn: 'setSelectOrMinimalHeight',
defaultValue: 100
},
'filter': {
changeFn: 'setShowFilterInputs',
defaultValue: true,
transformFn: getBooleanValue
},
'filterClear': {
changeFn: 'setFilterTextClear',
defaultValue: 'show all'
},
'filterPlaceholder': 'setFilterPlaceHolder',
'filterValues': {
changeFn: 'setFilterOnValues',
transformFn: getBooleanValue
},
'moveOnSelect': {
changeFn: 'setMoveOnSelect',
defaultValue: true,
transformFn: getBooleanValue
},
'preserveSelection': 'setPreserveSelectionOnMove',
'moveSelectedLabel': 'setMoveSelectedLabel',
'moveAllLabel': 'setMoveAllLabel',
'removeSelectedLabel': 'setRemoveSelectedLabel',
'removeAllLabel': 'setRemoveAllLabel',
'selectedListLabel': 'setSelectedListLabel',
'nonSelectedListLabel': 'setNonSelectedListLabel',
'infoAll': {
changeFn: 'setInfoText',
defaultValue: 'Showing all {0}'
},
'infoFiltered': {
changeFn: 'setInfoTextFiltered',
defaultValue: '<span class="label label-warning">Filtered</span> {0} from {1}'
},
'infoEmpty': {
changeFn: 'setInfoTextEmpty',
defaultValue: 'Empty list'
}
};
// The duallistbox element
var dualListBox;
/**
* Calculates the proper attribute value, given its name.
* The attribute value depends on:
* - the current value
* - the default value, if it exists
* - the transformFn, if it exists
*
* If the current value is undefined, it gets the default value.
* The calculated value is then applied to the transformFn, if it is defined; the result is then returned.
*
* @param attributeName The {String} name of the attribute.
* @returns {Object} representing the value of the attribute.
*/
var getAttributeValueOrDefault = function (attributeName) {
// get the attribute function/object for default and transformation
var attributeFunction = attributes[attributeName];
// get the current attribute value
var attributeValue = attrs[attributeName];
// By default, the default value and the transform function are not defined
var defaultValue;
var transformFn;
// If the attributeFunction is an object
if (angular.isObject(attributeFunction)) {
// extract the default value and the transform function
defaultValue = attributeFunction.defaultValue;
transformFn = attributeFunction.transformFn;
}
// If the upcoming value is falsy, get the default
if (!attributeValue) {
attributeValue = defaultValue;
}
// If a transform function is defined, use it to change the value
if (angular.isFunction(transformFn)) {
attributeValue = transformFn(attributeValue);
}
return attributeValue;
};
/**
* Gets the name of the function of `bootstrap-duallistbox` to be called to effectively
* change the attribute in input.
*
* @param attributeName The name of the attribute to change.
* @returns {String}, name of the `bootstrap-dual-listbox` to be called.
*/
var getAttributeChangeFunction = function (attributeName) {
// get the attribute function/object for default and transformation
var attributeFunction = attributes[attributeName];
// By default, attributeFunction is a function
var actualFunction = attributeFunction;
// If the attributeFunction is an object
if (angular.isObject(attributeFunction)) {
// extract the actual function name
actualFunction = attributeFunction.changeFn;
}
return actualFunction;
};
/**
* Listen to model changes.
*/
var listenToModel = function () {
// When ngModel changes, refresh the list
// controller.$formatters.push(refresh);
scope.$watch(attrs.ngModel, function () {
initMaybe();
refresh();
});
// When ngOptions changes, refresh the list
scope.$watch(collection, refresh, true);
// Watch for changes to the filter scope variables
scope.$watch(attrs.filterNonSelected, function () {
refresh();
});
scope.$watch(attrs.filterSelected, function () {
refresh();
});
// $observe every attribute change
angular.forEach(attributes, function (attributeFunction, attributeName) {
attrs.$observe(attributeName, function () {
var actualFunction = getAttributeChangeFunction(attributeName);
var actualValue = getAttributeValueOrDefault(attributeName);
// Depending on the attribute, call the right function (and always refresh)
element.bootstrapDualListbox(actualFunction, actualValue, true);
});
});
};
/**
* Refresh the Dual List Box using its own API.
*/
var refresh = function () {
// TODO: consider removing $timeout calls
$timeout(function () {
element.bootstrapDualListbox('refresh');
});
};
/**
* If the directive has not been initialized yet, do so.
*/
var initMaybe = function () {
// if it's the first initialization
if (!dualListBox) {
init();
}
};
// Delay listbox init
var init = function () {
var defaults = {};
// for every attribute the directive handles
angular.forEach(attributes, function (attributeFunction, attributeName) {
var actualValue = getAttributeValueOrDefault(attributeName);
defaults[attributeName] = actualValue;
});
// Init the plugin
dualListBox = element.bootstrapDualListbox({
bootstrap2Compatible: defaults.bootstrap2,
filterTextClear: defaults.filterClear,
filterPlaceHolder: defaults.filterPlaceholder,
moveSelectedLabel: defaults.moveSelectedLabel,
moveAllLabel: defaults.moveAllLabel,
removeSelectedLabel: defaults.removeSelectedLabel,
removeAllLabel: defaults.removeAllLabel,
moveOnSelect: defaults.moveOnSelect,
preserveSelectionOnMove: defaults.preserveSelection,
selectedListLabel: defaults.selectedListLabel,
nonSelectedListLabel: defaults.nonSelectedListLabel,
helperSelectNamePostfix: defaults.postfix,
selectOrMinimalHeight: defaults.selectMinHeight,
showFilterInputs: defaults.filter,
nonSelectedFilter: '',
selectedFilter: '',
infoText: defaults.infoAll,
infoTextFiltered: defaults.infoFiltered,
infoTextEmpty: defaults.infoEmpty,
filterOnValues: defaults.filterValues
});
// Inject the ng-model into the filters and re-compile them
var container = element.bootstrapDualListbox('getContainer');
var filterNonSelectedInput = container.find('.box1 .filter');
filterNonSelectedInput.attr('ng-model', attrs.filterNonSelected);
$compile(filterNonSelectedInput)(scope);
var filterSelectedInput = container.find('.box2 .filter');
filterSelectedInput.attr('ng-model', attrs.filterSelected);
$compile(filterSelectedInput)(scope);
};
// Listen and respond to model changes
listenToModel();
// On destroy, collect ya garbage
scope.$on('$destroy', function () {
element.bootstrapDualListbox('destroy');
});
}
};
}
]);