summaryrefslogtreecommitdiffstats
path: root/js/vendor/angular-ui/modules/directives/select2
diff options
context:
space:
mode:
Diffstat (limited to 'js/vendor/angular-ui/modules/directives/select2')
-rw-r--r--js/vendor/angular-ui/modules/directives/select2/dependencies.json8
-rw-r--r--js/vendor/angular-ui/modules/directives/select2/select2.js124
-rw-r--r--js/vendor/angular-ui/modules/directives/select2/test/select2Spec.js144
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