diff options
Diffstat (limited to 'js/vendor/angular-ui/modules/directives/select2')
3 files changed, 276 insertions, 0 deletions
diff --git a/js/vendor/angular-ui/modules/directives/select2/dependencies.json b/js/vendor/angular-ui/modules/directives/select2/dependencies.json new file mode 100644 index 000000000..6ba865c45 --- /dev/null +++ b/js/vendor/angular-ui/modules/directives/select2/dependencies.json @@ -0,0 +1,8 @@ +{ + "core": [ "jquery" ], + "internal": [], + "external": [ + "http://ivaynberg.github.com/select2/select2-3.2/select2.js", + "http://ivaynberg.github.com/select2/select2-3.2/select2.css", + ] +}
\ No newline at end of file diff --git a/js/vendor/angular-ui/modules/directives/select2/select2.js b/js/vendor/angular-ui/modules/directives/select2/select2.js new file mode 100644 index 000000000..c0ed5c8ef --- /dev/null +++ b/js/vendor/angular-ui/modules/directives/select2/select2.js @@ -0,0 +1,124 @@ +/** + * Enhanced Select2 Dropmenus + * + * @AJAX Mode - When in this mode, your value will be an object (or array of objects) of the data used by Select2 + * This change is so that you do not have to do an additional query yourself on top of Select2's own query + * @params [options] {object} The configuration options passed to $.fn.select2(). Refer to the documentation + */ +angular.module('ui.directives').directive('uiSelect2', ['ui.config', '$timeout', function (uiConfig, $timeout) { + var options = {}; + if (uiConfig.select2) { + angular.extend(options, uiConfig.select2); + } + return { + require: '?ngModel', + compile: function (tElm, tAttrs) { + var watch, + repeatOption, + repeatAttr, + isSelect = tElm.is('select'), + isMultiple = (tAttrs.multiple !== undefined); + + // Enable watching of the options dataset if in use + if (tElm.is('select')) { + repeatOption = tElm.find('option[ng-repeat], option[data-ng-repeat]'); + + if (repeatOption.length) { + repeatAttr = repeatOption.attr('ng-repeat') || repeatOption.attr('data-ng-repeat'); + watch = jQuery.trim(repeatAttr.split('|')[0]).split(' ').pop(); + } + } + + return function (scope, elm, attrs, controller) { + // instance-specific options + var opts = angular.extend({}, options, scope.$eval(attrs.uiSelect2)); + + if (isSelect) { + // Use <select multiple> instead + delete opts.multiple; + delete opts.initSelection; + } else if (isMultiple) { + opts.multiple = true; + } + + if (controller) { + // Watch the model for programmatic changes + controller.$render = function () { + if (isSelect) { + elm.select2('val', controller.$modelValue); + } else { + if (isMultiple) { + if (!controller.$modelValue) { + elm.select2('data', []); + } else if (angular.isArray(controller.$modelValue)) { + elm.select2('data', controller.$modelValue); + } else { + elm.select2('val', controller.$modelValue); + } + } else { + if (angular.isObject(controller.$modelValue)) { + elm.select2('data', controller.$modelValue); + } else { + elm.select2('val', controller.$modelValue); + } + } + } + }; + + // Watch the options dataset for changes + if (watch) { + scope.$watch(watch, function (newVal, oldVal, scope) { + if (!newVal) return; + // Delayed so that the options have time to be rendered + $timeout(function () { + elm.select2('val', controller.$viewValue); + // Refresh angular to remove the superfluous option + elm.trigger('change'); + }); + }); + } + + if (!isSelect) { + // Set the view and model value and update the angular template manually for the ajax/multiple select2. + elm.bind("change", function () { + scope.$apply(function () { + controller.$setViewValue(elm.select2('data')); + }); + }); + + if (opts.initSelection) { + var initSelection = opts.initSelection; + opts.initSelection = function (element, callback) { + initSelection(element, function (value) { + controller.$setViewValue(value); + callback(value); + }); + }; + } + } + } + + attrs.$observe('disabled', function (value) { + elm.select2(value && 'disable' || 'enable'); + }); + + if (attrs.ngMultiple) { + scope.$watch(attrs.ngMultiple, function(newVal) { + elm.select2(opts); + }); + } + + // Set initial value since Angular doesn't + elm.val(scope.$eval(attrs.ngModel)); + + // Initialize the plugin late so that the injected DOM does not disrupt the template compiler + $timeout(function () { + elm.select2(opts); + // Not sure if I should just check for !isSelect OR if I should check for 'tags' key + if (!opts.initSelection && !isSelect) + controller.$setViewValue(elm.select2('data')); + }); + }; + } + }; +}]); diff --git a/js/vendor/angular-ui/modules/directives/select2/test/select2Spec.js b/js/vendor/angular-ui/modules/directives/select2/test/select2Spec.js new file mode 100644 index 000000000..3e393aecb --- /dev/null +++ b/js/vendor/angular-ui/modules/directives/select2/test/select2Spec.js @@ -0,0 +1,144 @@ +/*global describe, beforeEach, module, inject, it, spyOn, expect, $ */ +describe('uiSelect2', function () { + 'use strict'; + + var scope, $compile, options, $timeout; + beforeEach(module('ui.directives')); + beforeEach(inject(function (_$rootScope_, _$compile_, _$window_, _$timeout_) { + scope = _$rootScope_.$new(); + $compile = _$compile_; + $timeout = _$timeout_; + scope.options = { + query: function (query) { + var data = { + results: [{ id: 1, text: 'first' }] + }; + query.callback(data); + } + }; + })); + + /** + * Compile a template synchronously + * @param {String} template The string to compile + * @return {Object} A reference to the compiled template + */ + function compile(template) { + var element = $compile(template)(scope); + scope.$apply(); + $timeout.flush(); + return element; + } + + describe('with a <select> element', function () { + describe('compiling this directive', function () { + it('should throw an error if we have no model defined', function () { + expect(function(){ + compile('<select type="text" ui-reset></select>'); + }).toThrow(); + }); + it('should create proper DOM structure', function () { + var element = compile('<select ui-select2 ng-model="foo"></select>'); + expect(element.siblings().is('div.select2-container')).toBe(true); + }); + }); + describe('when model is changed programmatically', function(){ + it('should set select2 to the value', function(){ + scope.foo = 'First'; + var element = compile('<select ui-select2 ng-model="foo"><option>First</option><option>Second</option></select>'); + expect(element.select2('val')).toBe('First'); + scope.$apply('foo = "Second"'); + expect(element.select2('val')).toBe('Second'); + }); + it('should set select2 to the value for multiples', function(){ + scope.foo = 'First'; + var element = compile('<select ui-select2 multiple ng-model="foo"><option>First</option><option>Second</option><option>Third</option></select>'); + expect(element.select2('val')).toEqual(['First']); + scope.$apply('foo = ["Second"]'); + expect(element.select2('val')).toEqual(['Second']); + scope.$apply('foo = ["Second","Third"]'); + expect(element.select2('val')).toEqual(['Second','Third']); + }); + }); + it('should observe the disabled attribute', function () { + var element = compile('<select ui-select2 ng-model="foo" ng-disabled="disabled"></select>'); + expect(element.siblings().hasClass('select2-container-disabled')).toBe(false); + scope.$apply('disabled = true'); + expect(element.siblings().hasClass('select2-container-disabled')).toBe(true); + scope.$apply('disabled = false'); + expect(element.siblings().hasClass('select2-container-disabled')).toBe(false); + }); + it('should observe the multiple attribute', function () { + var element = $compile('<select ui-select2 ng-model="foo" ng-multiple="multiple"></select>')(scope); + + expect(element.siblings().hasClass('select2-container-multi')).toBe(false); + scope.$apply('multiple = true'); + expect(element.siblings().hasClass('select2-container-multi')).toBe(true); + scope.$apply('multiple = false'); + expect(element.siblings().hasClass('select2-container-multi')).toBe(false); + }); + it('should observe an option with ng-repeat for changes', function(){ + scope.items = ['first', 'second', 'third']; + scope.foo = 'fourth'; + var element = compile('<select ui-select2 ng-model="foo"><option ng-repeat="item in items">{{item}}</option></select>'); + expect(element.select2('val')).toNotBe('fourth'); + scope.$apply('items=["fourth"]'); + $timeout.flush(); + expect(element.select2('val')).toBe('fourth'); + }); + }); + describe('with an <input> element', function () { + describe('compiling this directive', function () { + it('should throw an error if we have no model defined', function () { + expect(function() { + compile('<input ui-select2/>'); + }).toThrow(); + }); + it('should creae proper DOM structure', function () { + var element = compile('<input ui-select2="options" ng-model="foo"/>'); + expect(element.siblings().is('div.select2-container')).toBe(true); + }); + }); + describe('when model is changed programmatically', function(){ + describe('for single-select', function(){ + it('should call select2(data, ...) for objects', function(){ + var element = compile('<input ng-model="foo" ui-select2="options">'); + spyOn($.fn, 'select2'); + scope.$apply('foo={ id: 1, text: "first" }'); + expect(element.select2).toHaveBeenCalledWith('data', { id: 1, text: "first" }); + }); + it('should call select2(val, ...) for strings', function(){ + var element = compile('<input ng-model="foo" ui-select2="options">'); + spyOn($.fn, 'select2'); + scope.$apply('foo="first"'); + expect(element.select2).toHaveBeenCalledWith('val', 'first'); + }); + }); + describe('for multi-select', function(){ + it('should call select2(data, ...) for arrays', function(){ + var element = compile('<input ng-model="foo" multiple ui-select2="options">'); + spyOn($.fn, 'select2'); + scope.$apply('foo=[{ id: 1, text: "first" },{ id: 2, text: "second" }]'); + expect(element.select2).toHaveBeenCalledWith('data', [{ id: 1, text: "first" },{ id: 2, text: "second" }]); + }); + it('should call select2(data, []) for falsey values', function(){ + var element = compile('<input ng-model="foo" multiple ui-select2="options">'); + spyOn($.fn, 'select2'); + scope.$apply('foo=[]'); + expect(element.select2).toHaveBeenCalledWith('data', []); + }); + it('should call select2(val, ...) for strings', function(){ + var element = compile('<input ng-model="foo" multiple ui-select2="options">'); + spyOn($.fn, 'select2'); + scope.$apply('foo="first,second"'); + expect(element.select2).toHaveBeenCalledWith('val', 'first,second'); + }); + }); + }); + it('should set the model when the user selects an item', function(){ + var element = compile('<input ng-model="foo" multiple ui-select2="options">'); + // TODO: programmactically select an option + // expect(scope.foo).toBe(/* selected val */); + }); + }); +});
\ No newline at end of file |