diff --git a/3rdparty/bootstrap-tagsinput/bootstrap-tagsinput-angular.js b/3rdparty/bootstrap-tagsinput/bootstrap-tagsinput-angular.js new file mode 100644 index 0000000..adcaccb --- /dev/null +++ b/3rdparty/bootstrap-tagsinput/bootstrap-tagsinput-angular.js @@ -0,0 +1,87 @@ +angular.module('bootstrap-tagsinput', []) +.directive('bootstrapTagsinput', [function() { + + function getItemProperty(scope, property) { + if (!property) + return undefined; + + if (angular.isFunction(scope.$parent[property])) + return scope.$parent[property]; + + return function(item) { + return item[property]; + }; + } + + return { + restrict: 'EA', + scope: { + model: '=ngModel' + }, + template: '', + replace: false, + link: function(scope, element, attrs) { + $(function() { + if (!angular.isArray(scope.model)) + scope.model = []; + + var select = $('select', element); + var typeaheadSourceArray = attrs.typeaheadSource ? attrs.typeaheadSource.split('.') : null; + var typeaheadSource = typeaheadSourceArray ? + (typeaheadSourceArray.length > 1 ? + scope.$parent[typeaheadSourceArray[0]][typeaheadSourceArray[1]] + : scope.$parent[typeaheadSourceArray[0]]) + : null; + + select.tagsinput(scope.$parent[attrs.options || ''] || { + typeahead : { + source : angular.isFunction(typeaheadSource) ? typeaheadSource : null + }, + itemValue: getItemProperty(scope, attrs.itemvalue), + itemText : getItemProperty(scope, attrs.itemtext), + confirmKeys : getItemProperty(scope, attrs.confirmkeys) ? JSON.parse(attrs.confirmkeys) : [13], + tagClass : angular.isFunction(scope.$parent[attrs.tagclass]) ? scope.$parent[attrs.tagclass] : function(item) { return attrs.tagclass; } + }); + + for (var i = 0; i < scope.model.length; i++) { + select.tagsinput('add', scope.model[i]); + } + + select.on('itemAdded', function(event) { + if (scope.model.indexOf(event.item) === -1) + scope.model.push(event.item); + }); + + select.on('itemRemoved', function(event) { + var idx = scope.model.indexOf(event.item); + if (idx !== -1) + scope.model.splice(idx, 1); + }); + + // create a shallow copy of model's current state, needed to determine + // diff when model changes + var prev = scope.model.slice(); + scope.$watch("model", function() { + var added = scope.model.filter(function(i) {return prev.indexOf(i) === -1;}), + removed = prev.filter(function(i) {return scope.model.indexOf(i) === -1;}), + i; + + prev = scope.model.slice(); + + // Remove tags no longer in binded model + for (i = 0; i < removed.length; i++) { + select.tagsinput('remove', removed[i]); + } + + // Refresh remaining tags + select.tagsinput('refresh'); + + // Add new items in model as tags + for (i = 0; i < added.length; i++) { + select.tagsinput('add', added[i]); + } + }, true); + }); + } + }; +}]); diff --git a/3rdparty/bootstrap-tagsinput/bootstrap-tagsinput-angular.min.js b/3rdparty/bootstrap-tagsinput/bootstrap-tagsinput-angular.min.js new file mode 100644 index 0000000..b1372b1 --- /dev/null +++ b/3rdparty/bootstrap-tagsinput/bootstrap-tagsinput-angular.min.js @@ -0,0 +1,7 @@ +/* + * bootstrap-tagsinput v0.6.1 by Tim Schlechter + * + */ + +angular.module("bootstrap-tagsinput",[]).directive("bootstrapTagsinput",[function(){function a(a,b){return b?angular.isFunction(a.$parent[b])?a.$parent[b]:function(a){return a[b]}:void 0}return{restrict:"EA",scope:{model:"=ngModel"},template:"",replace:!1,link:function(b,c,d){$(function(){angular.isArray(b.model)||(b.model=[]);var e=$("select",c),f=d.typeaheadSource?d.typeaheadSource.split("."):null,g=f?f.length>1?b.$parent[f[0]][f[1]]:b.$parent[f[0]]:null;e.tagsinput(b.$parent[d.options||""]||{typeahead:{source:angular.isFunction(g)?g:null},itemValue:a(b,d.itemvalue),itemText:a(b,d.itemtext),confirmKeys:a(b,d.confirmkeys)?JSON.parse(d.confirmkeys):[13],tagClass:angular.isFunction(b.$parent[d.tagclass])?b.$parent[d.tagclass]:function(a){return d.tagclass}});for(var h=0;h'); + this.$input = $('').appendTo(this.$container); + + this.$element.before(this.$container); + + this.build(options); + } + + TagsInput.prototype = { + constructor: TagsInput, + + /** + * Adds the given item as a new tag. Pass true to dontPushVal to prevent + * updating the elements val() + */ + add: function(item, dontPushVal, options) { + var self = this; + + if (self.options.maxTags && self.itemsArray.length >= self.options.maxTags) + return; + + // Ignore falsey values, except false + if (item !== false && !item) + return; + + // Trim value + if (typeof item === "string" && self.options.trimValue) { + item = $.trim(item); + } + + // Throw an error when trying to add an object while the itemValue option was not set + if (typeof item === "object" && !self.objectItems) + throw("Can't add objects when itemValue option is not set"); + + // Ignore strings only containg whitespace + if (item.toString().match(/^\s*$/)) + return; + + // If SELECT but not multiple, remove current tag + if (self.isSelect && !self.multiple && self.itemsArray.length > 0) + self.remove(self.itemsArray[0]); + + if (typeof item === "string" && this.$element[0].tagName === 'INPUT') { + var delimiter = (self.options.delimiterRegex) ? self.options.delimiterRegex : self.options.delimiter; + var items = item.split(delimiter); + if (items.length > 1) { + for (var i = 0; i < items.length; i++) { + this.add(items[i], true); + } + + if (!dontPushVal) + self.pushVal(); + return; + } + } + + var itemValue = self.options.itemValue(item), + itemText = self.options.itemText(item), + tagClass = self.options.tagClass(item), + itemTitle = self.options.itemTitle(item); + + // Ignore items allready added + var existing = $.grep(self.itemsArray, function(item) { return self.options.itemValue(item) === itemValue; } )[0]; + if (existing && !self.options.allowDuplicates) { + // Invoke onTagExists + if (self.options.onTagExists) { + var $existingTag = $(".tag", self.$container).filter(function() { return $(this).data("item") === existing; }); + self.options.onTagExists(item, $existingTag); + } + return; + } + + // if length greater than limit + if (self.items().toString().length + item.length + 1 > self.options.maxInputLength) + return; + + // raise beforeItemAdd arg + var beforeItemAddEvent = $.Event('beforeItemAdd', { item: item, cancel: false, options: options}); + self.$element.trigger(beforeItemAddEvent); + if (beforeItemAddEvent.cancel) + return; + + // register item in internal array and map + self.itemsArray.push(item); + + // add a tag element + + var $tag = $('' + htmlEncode(itemText) + ''); + $tag.data('item', item); + self.findInputWrapper().before($tag); + $tag.after(' '); + + // add