diff --git a/.bowerrc b/.bowerrc
new file mode 100644
index 0000000..ebcc9f7
--- /dev/null
+++ b/.bowerrc
@@ -0,0 +1,3 @@
+{
+ "directory": "src/lib/"
+}
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..beffa30
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,11 @@
+root = true
+
+[*]
+indent_style = space
+indent_size = 2
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.md]
+trim_trailing_whitespace = false
diff --git a/.eslintrc b/.eslintrc
new file mode 100644
index 0000000..36b905b
--- /dev/null
+++ b/.eslintrc
@@ -0,0 +1,11 @@
+{
+ "env": {
+ "es6": true,
+ "browser": true
+ },
+ "extends": ["eslint:recommended", "angular"],
+ "rules": {
+ "no-console": ["error", { allow: ["warn", "error"] }],
+ "angular/di": [2, "array"]
+ }
+}
diff --git a/.gitignore b/.gitignore
index e550e4f..3996ab9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,29 +1,3 @@
-# Packages #
-############
-# Ignore if there is any compressed files
-*.7z
-*.dmg
-*.gz
-*.iso
-*.jar
-*.rar
-*.tar
-*.zip
-
-# Unwanted OS/Editors generated file #
-######################################
-.DS_Store
-.DS_Store?
-._*
-.Spotlight-V100
-.Trashes
-ehthumbs.db
-Thumbs.db
-*~
-*.swp
-*.bak
-
-# Node Modules Directory #
-##########################
-/node_modules/*
-
+# Dependency directories
+node_modules
+src/lib
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 5c86cdc..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,13 +0,0 @@
-sudo: required
-before_script:
- - npm install -g gulp
- - npm install gulp
- - npm install gulp-clean
- - npm install gulp-watch
- - npm install run-sequence
- - npm install gulp-ngmin
- - npm install gulp-order
- - npm install gulp-jshint
- - npm install gulp-lintspaces
- - npm install jshint-summary
-script: gulp
diff --git a/bower.json b/bower.json
index a92e7cd..0fcf609 100644
--- a/bower.json
+++ b/bower.json
@@ -1,7 +1,7 @@
{
- "name": "amitava82-angular-multiselect",
- "version": "1.2.0",
- "main": "dist/multiselect-tpls.js",
+ "name": "nithin-angular-multiselect",
+ "version": "1.3.13",
+ "main": ["dist/js/multiselect.min.js","dist/js/templates.min.js","dist/css/multiselect.min.css"],
"description": "Native AngularJS multiselect directive",
"license": "MIT",
"ignore": [
@@ -9,7 +9,16 @@
"**/*.txt"
],
"dependencies": {
- "angular": ">=1.0.4"
+ "angular": "^1.6.4",
+ "bootstrap": "^3.3.7"
+ },
+ "overrides": {
+ "bootstrap": {
+ "main": [
+ "dist/css/bootstrap.min.css",
+ "dist/fonts/*"
+ ]
+ }
},
"keywords": [
"angularjs",
diff --git a/dist/app.js b/dist/app.js
deleted file mode 100644
index 7f14db4..0000000
--- a/dist/app.js
+++ /dev/null
@@ -1,17 +0,0 @@
-angular.module('app', ['am.multiselect'])
-
-.controller('appCtrl', ['$scope', function($scope){
- $scope.cars = [
- {id:1, name: 'Audi'},
- {id:2, name: 'BMW'},
- {id:3, name: 'Honda'}
- ];
- $scope.selectedCar = [];
-
- $scope.fruits = [
- {id: 1, name: 'Apple'},
- {id: 2, name: 'Orange'},
- {id: 3, name: 'Banana'}
- ];
- $scope.selectedFruit = null;
-}]);
diff --git a/dist/css/multiselect.css b/dist/css/multiselect.css
new file mode 100644
index 0000000..4890e90
--- /dev/null
+++ b/dist/css/multiselect.css
@@ -0,0 +1,30 @@
+/*csslint box-sizing: false*/
+
+am-multiselect .dropdown-menu{
+ box-sizing: content-box;
+ height: 200px;
+ overflow-x: scroll;
+ overflow-y: scroll;
+ padding-left: 5px;
+ padding-right: 5px;
+}
+
+am-multiselect .dropdown-menu > li > a {
+ cursor:pointer;
+ padding: 3px 10px;
+}
+
+am-multiselect .dropdown-menu > .selected {
+ background-color: #ADD8E6;
+}
+
+am-multiselect .btn-group > button {
+ border-radius:5px !important; /* csslint allow: known-properties, important */
+ overflow:hidden;
+ white-space: normal;
+ width:100%;
+}
+
+am-multiselect .btn-group {
+ width:100%;
+}
diff --git a/dist/css/multiselect.min.css b/dist/css/multiselect.min.css
new file mode 100644
index 0000000..3bca72e
--- /dev/null
+++ b/dist/css/multiselect.min.css
@@ -0,0 +1 @@
+am-multiselect .dropdown-menu{box-sizing:content-box;height:200px;overflow-x:scroll;overflow-y:scroll;padding-left:5px;padding-right:5px}am-multiselect .dropdown-menu>li>a{cursor:pointer;padding:3px 10px}am-multiselect .dropdown-menu>.selected{background-color:#add8e6}am-multiselect .btn-group>button{border-radius:5px!important;overflow:hidden;white-space:normal;width:100%}am-multiselect .btn-group{width:100%}
\ No newline at end of file
diff --git a/dist/html/multiselect.tmpl.html b/dist/html/multiselect.tmpl.html
new file mode 100644
index 0000000..49cbf17
--- /dev/null
+++ b/dist/html/multiselect.tmpl.html
@@ -0,0 +1 @@
+
{{header}}
\ No newline at end of file
diff --git a/dist/index.html b/dist/index.html
deleted file mode 100644
index b32f42e..0000000
--- a/dist/index.html
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
- Angular multiselect
-
-
-
-
-
-
-
- Example
-
-
- {{selectedCar}}
-
- Single select:
-
-
- {{selectedFruit}}
-
-
-
diff --git a/dist/multiselect-tpls.js b/dist/js/multiselect.js
similarity index 82%
rename from dist/multiselect-tpls.js
rename to dist/js/multiselect.js
index 8b8179a..2622f7c 100644
--- a/dist/multiselect-tpls.js
+++ b/dist/js/multiselect.js
@@ -1,4 +1,5 @@
// Source: https://github.com/amitava82/angular-multiselect
+
angular.module('am.multiselect', [])
// from bootstrap-ui typeahead parser
@@ -39,6 +40,7 @@ angular.module('am.multiselect', [])
var exp = attrs.options,
parsedResult = optionParser.parse(exp),
isMultiple = attrs.multiple ? true : false,
+ isHover = attrs.hover ? true : false,
required = false,
scope = originalScope.$new(),
changeHandler = attrs.change || angular.noop;
@@ -47,7 +49,11 @@ angular.module('am.multiselect', [])
scope.header = 'Select';
scope.multiple = isMultiple;
scope.disabled = false;
+ scope.searchDisable = false;
scope.onBlur = attrs.ngBlur || angular.noop;
+ scope.hoverText = isHover ? scope.header : '';
+ scope.onFocus = attrs.ngFocus;
+ scope.clazz = attrs.clazz;
originalScope.$on('$destroy', function () {
scope.$destroy();
@@ -67,11 +73,18 @@ angular.module('am.multiselect', [])
// watch disabled state
scope.$watch(function () {
- return $parse(attrs.disabled)(originalScope);
+ return $parse(attrs.ngDisabled)(originalScope);
}, function (newVal) {
scope.disabled = newVal;
});
+ // watch disabled state for search text box
+ scope.$watch(function () {
+ return $parse(attrs.searchDisable)(originalScope);
+ }, function (newVal) {
+ scope.searchDisable = newVal;
+ });
+
// watch single/multiple state for dynamically change single to multiple
scope.$watch(function () {
return $parse(attrs.multiple)(originalScope);
@@ -90,22 +103,29 @@ angular.module('am.multiselect', [])
// watch model change
scope.$watch(function () {
return modelCtrl.$modelValue;
- }, function (newVal, oldVal) {
+ }, function (newVal) {
+ // When the model is assigned a "" or undefined value from controller, need to uncheck all items and clear searchText.label
+ if(angular.isUndefined(newVal) || newVal==="" || newVal===null) {
+ scope.uncheckAll();
+ if(angular.isDefined(scope.searchText))
+ scope.searchText.label="";
+ }
// when directive initialize, newVal usually undefined. Also, if model value already set in the controller
// for preselected list then we need to mark checked in our scope item. But we don't want to do this every time
// model changes. We need to do this only if it is done outside directive scope, from controller, for example.
- if (angular.isDefined(newVal)) {
+ else if (angular.isDefined(newVal)) {
markChecked(newVal);
scope.$eval(changeHandler);
}
getHeaderText();
+ scope.hoverText = isHover ? scope.header : '';
modelCtrl.$setValidity('required', scope.valid());
}, true);
function parseModel() {
scope.items.length = 0;
var model = parsedResult.source(originalScope);
- if(!angular.isDefined(model)) return;
+ if(angular.isUndefined(model)) return;
for (var i = 0; i < model.length; i++) {
var local = {};
local[parsedResult.itemName] = model[i];
@@ -121,8 +141,16 @@ angular.module('am.multiselect', [])
element.append($compile(popUpEl)(scope));
+ function getItemLabel(items,model) {
+ for(var i = 0; i < items.length; i++) {
+ if(items[i].model==model) {
+ return items[i].label;
+ }
+ }
+ }
+
function getHeaderText() {
- if (is_empty(modelCtrl.$modelValue)) return scope.header = (attrs.msHeader!==undefined ? attrs.msHeader : 'Select');
+ if (is_empty(modelCtrl.$modelValue)) return scope.header = (angular.isDefined(attrs.msHeader) ? attrs.msHeader : 'Select');
if (isMultiple) {
if (attrs.msSelected) {
@@ -140,11 +168,11 @@ angular.module('am.multiselect', [])
}
} else {
if(angular.isString(modelCtrl.$modelValue)){
- scope.header = modelCtrl.$modelValue;
+ scope.header = getItemLabel(scope.items,modelCtrl.$modelValue);
} else {
var local = {};
local[parsedResult.itemName] = modelCtrl.$modelValue;
- scope.header = parsedResult.viewMapper(local) || scope.items[modelCtrl.$modelValue].label;
+ scope.header = parsedResult.viewMapper(local) || getItemLabel(scope.items,modelCtrl.$modelValue);
}
}
}
@@ -154,7 +182,7 @@ angular.module('am.multiselect', [])
if (obj && obj.length && obj.length > 0) return false;
for (var prop in obj) if (obj[prop]) return false;
return true;
- };
+ }
scope.valid = function validModel() {
if(!required) return true;
@@ -178,13 +206,14 @@ angular.module('am.multiselect', [])
}
function setModelValue(isMultiple) {
- var value = null;
+ var value = undefined;
if (isMultiple) {
value = [];
angular.forEach(scope.items, function (item) {
if (item.checked) value.push(item.model);
})
+ if(value.length==0) value=undefined;
} else {
angular.forEach(scope.items, function (item) {
if (item.checked) {
@@ -228,11 +257,13 @@ angular.module('am.multiselect', [])
};
scope.uncheckAll = function () {
- var items = (scope.searchText && scope.searchText.label.length > 0) ? $filter('filter')(scope.items, scope.searchText) : scope.items;
- angular.forEach(items, function (item) {
+ // need to uncheck from the entire list of items. If user filers with ine text and selects item A. Next time user fileters and selects item B (item A now not in the filtered set). The item A will not get unchecked
+ // var items = (scope.searchText && scope.searchText.label.length > 0) ? $filter('filter')(scope.items, scope.searchText) : scope.items;
+ angular.forEach(scope.items, function (item) {
item.checked = false;
});
- setModelValue(true);
+ // sending scope.multiple instead of true to setModelValue. Since different values geeting set when single and multiple.
+ setModelValue(scope.multiple);
};
scope.select = function (item) {
@@ -252,15 +283,18 @@ angular.module('am.multiselect', [])
restrict: 'E',
scope: false,
replace: true,
- templateUrl: function (element, attr) {
- return attr.templateUrl || 'multiselect.tmpl.html';
- },
- link: function (scope, element, attrs) {
+ templateUrl: 'html/multiselect.tmpl.html',
+ link: function (scope, element) {
scope.selectedIndex = null;
scope.isVisible = false;
scope.filteredItems = null;
+ scope.ngClazz = {
+ 'error': !scope.valid()
+ };
+ scope.ngClazz[scope.$eval(scope.clazz)] = true;
+
scope.toggleSelect = function () {
if (element.hasClass('open')) {
element.removeClass('open');
@@ -273,6 +307,10 @@ angular.module('am.multiselect', [])
}
};
+ scope.evalFocus = function() {
+ scope.$parent.$eval(scope.onFocus);
+ }
+
function clickHandler(event) {
if (elementMatchesAnyInArray(event.target, element.find(event.target.tagName))) {
scope.$parent.$eval(scope.onBlur);
@@ -322,5 +360,3 @@ angular.module('am.multiselect', [])
}
}
}]);
-
-angular.module("am.multiselect").run(["$templateCache", function($templateCache) {$templateCache.put("multiselect.tmpl.html","\n \n {{header}}\n \n \n \n
\n");}]);
\ No newline at end of file
diff --git a/dist/js/multiselect.min.js b/dist/js/multiselect.min.js
new file mode 100644
index 0000000..fc578be
--- /dev/null
+++ b/dist/js/multiselect.min.js
@@ -0,0 +1 @@
+angular.module("am.multiselect",[]).factory("optionParser",["$parse",function(e){return{parse:function(n){var l=n.match(/^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+([\s\S]+?)$/);if(!l)throw new Error('Expected typeahead specification in form of "_modelValue_ (as _label_)? for _item_ in _collection_" but got "'+n+'".');return{itemName:l[3],source:e(l[4]),viewMapper:e(l[2]||l[1]),modelMapper:e(l[1])}}}}]).directive("amMultiselect",["$parse","$document","$compile","$interpolate","$filter","optionParser",function(e,n,l,t,r,a){return{restrict:"E",require:"ngModel",link:function(n,i,c,u){function o(){V.items.length=0;var e=k.source(n);if(!angular.isUndefined(e))for(var l=0;l0)return!1;for(var n in e)if(e[n])return!1;return!0}function m(e){e.checked?V.uncheckAll():(V.uncheckAll(),e.checked=!e.checked),p(!1)}function h(e){e.checked=!e.checked,p(!0)}function p(e){var n=void 0;e?(n=[],angular.forEach(V.items,function(e){e.checked&&n.push(e.model)}),0==n.length&&(n=void 0)):angular.forEach(V.items,function(e){if(e.checked)return n=e.model,!1}),u.$setViewValue(n)}function g(e){angular.isArray(e)?angular.forEach(V.items,function(n){n.checked=!1,angular.forEach(e,function(e){angular.equals(n.model,e)&&(n.checked=!0)})}):angular.forEach(V.items,function(n){if(angular.equals(n.model,e))return V.uncheckAll(),n.checked=!0,p(!1),!1})}var $=u.$isEmpty;u.$isEmpty=function(e){return $(e)||angular.isArray(e)&&0==e.length};var v=c.options,k=a.parse(v),x=!!c.multiple,b=!!c.hover,w=!1,V=n.$new(),y=c.change||angular.noop;V.items=[],V.header="Select",V.multiple=x,V.disabled=!1,V.searchDisable=!1,V.onBlur=c.ngBlur||angular.noop,V.hoverText=b?V.header:"",V.onFocus=c.ngFocus,V.clazz=c.clazz,n.$on("$destroy",function(){V.$destroy()});var E=angular.element(" ");(c.required||c.ngRequired)&&(w=!0),c.$observe("required",function(e){w=e}),V.$watch(function(){return e(c.ngDisabled)(n)},function(e){V.disabled=e}),V.$watch(function(){return e(c.searchDisable)(n)},function(e){V.searchDisable=e}),V.$watch(function(){return e(c.multiple)(n)},function(e){x=e||!1}),V.$watch(function(){return k.source(n)},function(e){angular.isDefined(e)&&o()},!0),V.$watch(function(){return u.$modelValue},function(e){angular.isUndefined(e)||""===e||null===e?(V.uncheckAll(),angular.isDefined(V.searchText)&&(V.searchText.label="")):angular.isDefined(e)&&(g(e),V.$eval(y)),d(),V.hoverText=b?V.header:"",u.$setValidity("required",V.valid())},!0),o(),i.append(l(E)(V)),V.valid=function(){if(!w)return!0;var e=u.$modelValue;return angular.isArray(e)&&e.length>0||!angular.isArray(e)&&null!=e},V.checkAll=function(){if(x){var e=V.searchText&&V.searchText.label.length>0?r("filter")(V.items,V.searchText):V.items;angular.forEach(e,function(e){e.checked=!0}),p(!0)}},V.uncheckAll=function(){angular.forEach(V.items,function(e){e.checked=!1}),p(V.multiple)},V.select=function(e){!1===x?(m(e),V.toggleSelect()):h(e)}}}}]).directive("amMultiselectPopup",["$document","$filter",function(e,n){return{restrict:"E",scope:!1,replace:!0,templateUrl:"html/multiselect.tmpl.html",link:function(l,t){function r(n){a(n.target,t.find(n.target.tagName))?l.$parent.$eval(l.onBlur):(t.removeClass("open"),e.unbind("click",r),l.$apply())}l.selectedIndex=null,l.isVisible=!1,l.filteredItems=null,l.ngClazz={error:!l.valid()},l.ngClazz[l.$eval(l.clazz)]=!0,l.toggleSelect=function(){t.hasClass("open")?(t.removeClass("open"),e.unbind("click",r),l.$parent.$eval(l.onBlur)):(t.addClass("open"),e.bind("click",r),l.focus())},l.evalFocus=function(){l.$parent.$eval(l.onFocus)},l.focus=function(){var e=t.find("input")[0];e&&e.focus()},l.keydown=function(e){var t=n("filter")(l.items,l.searchText),r=e.keyCode||e.which;13===r?t[l.selectedIndex]&&l.select(t[l.selectedIndex]):l.selectedIndex=38===r?null===l.selectedIndex?t.length-1:l.selectedIndex-1:40===r?null===l.selectedIndex?0:l.selectedIndex+1:null,l.selectedIndex<0?l.selectedIndex=t.length-1:l.selectedIndex>t.length-1&&(l.selectedIndex=0)};var a=function(e,n){for(var l=0;l{{header}} ');}]);
\ No newline at end of file
diff --git a/dist/js/templates.min.js b/dist/js/templates.min.js
new file mode 100644
index 0000000..9b0d69c
--- /dev/null
+++ b/dist/js/templates.min.js
@@ -0,0 +1 @@
+angular.module("am.multiselect").run(["$templateCache",function(l){l.put("html/multiselect.tmpl.html",'{{header}}
')}]);
\ No newline at end of file
diff --git a/dist/multiselect.css b/dist/multiselect.css
deleted file mode 100644
index 3b817ad..0000000
--- a/dist/multiselect.css
+++ /dev/null
@@ -1,13 +0,0 @@
-am-multiselect .dropdown-menu{
- padding-left: 5px;
- padding-right: 5px;
-}
-
-am-multiselect .dropdown-menu > li > a {
- padding: 3px 10px;
- cursor:pointer;
-}
-
-am-multiselect .dropdown-menu > li.selected {
- background-color: #ADD8E6;
-}
\ No newline at end of file
diff --git a/gulp.config.js b/gulp.config.js
new file mode 100644
index 0000000..12e27ce
--- /dev/null
+++ b/gulp.config.js
@@ -0,0 +1,96 @@
+/* global module */
+/* global require */
+
+module.exports = function() {
+ var appSrc='./src/';
+ var distDir='./dist/';
+
+ var config={
+ temp: './.tmp/',
+
+// distFiles: [appSrc+'js/inputDateField.js',appSrc+'html/inputDateField.tmpl.html'],
+ distJsFiles: appSrc+'js/multiselect.js',
+ distHtmlFiles: appSrc+'html/multiselect.tmpl.html',
+ distCssFiles: appSrc+'css/multiselect.css',
+
+ // all js to vet
+ alljs: ['./*.js','./src/js/*.js'],
+ allcss: [appSrc+'*.css',appSrc+'css/*.css'],
+ allhtml: [appSrc+'*.html',appSrc+'html/*.html'],
+ index: './src/index.html',
+ js: [appSrc+'js/*.js'],
+ css: [appSrc+'*.css',appSrc+'css/*.css'],
+// less: ['./src/*.less','./src/modules/less/*.less'],
+
+
+ bowerJSON: './bower.json',
+ bower: {
+ json: require('./bower.json'),
+ directory: './src/lib/',
+ ignorePath: '..'
+ },
+
+ optimized: {
+ lib: 'lib.js',
+ app: 'app.js'
+ },
+
+ htmlAngularValidate: {
+ customattrs: [''],
+ customtags: ['']
+ },
+
+ source: appSrc,
+ build: './gh-pages/',
+ dist: distDir,
+ resources: './src/resources/*',
+ images: './src/resources/images/*',
+ json: './src/resources/json/*.json',
+
+ htmltemplates: [appSrc+'**/**/*.html',appSrc+'**/*.html','!'+appSrc+'index.html'],
+ templateCache: {
+ file: 'templates.js',
+ options: {
+ module: 'am.multiselect',
+ standAlone: true//,
+ //root: '/'
+ }
+ },
+
+ htmltemplatesDist: [distDir+'**/**/*.html',distDir+'**/*.html'],
+ templateCacheDist: {
+ file: 'templates.js',
+ options: {
+ module: 'am.multiselect',
+ standAlone: true//,
+ //root: '/'
+ }
+ }
+
+
+ };
+
+ config.getWiredepDefaultOptions = function() {
+ var options = {
+ bowerJson: config.bower.json,
+ directory: config.bower.directory,
+ ignorePath: config.bower.ignorePath
+ };
+ return options;
+ };
+
+ config.getWebserverOptions = function() {
+ var options = {
+ livereload: true,
+ directoryListing: false,
+ open: true,
+ host: 'localhost',
+ port: 3000,
+ path: '/',
+ https: false
+ };
+ return options;
+ }
+
+ return config;
+}
diff --git a/gulpfile.js b/gulpfile.js
index c554877..8397004 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -1,94 +1,338 @@
-/*
- Licenced Under MIT
- @file gulpfile.js
- @author Nagaraja.T (github/naga2raja)
- @desc Buildfile for angular-multiselect
-*/
-
-var gulp = require('gulp'),
- del = require('del'),
- watch = require('gulp-watch'),
- sequence = require('run-sequence'),
- mergeStream = require('merge-stream'),
- concat = require('gulp-concat'),
- lintspaces = require("gulp-lintspaces"),
- templateCache = require("gulp-angular-templatecache"),
- angularFilesort = require("gulp-angular-filesort"),
- lintspacesConfig = {
- indentation: 'spaces',
- spaces: 4,
- trailingspaces: true,
- ignores: [
- 'js-comments'
- ]
- };
-
-/*
- Gulp monitors the src tree and rebuilds it detects a modification. Used of dev purpose
-*/
-gulp.task('watch', function() {
- watch("src/**/*.*", function() {
- gulp.start("build-clean");
- });
-});
-
-gulp.task('default', ['build-clean'], function() {
-});
-
-/*
- Check for lintspaces errors in source code
-*/
-gulp.task('whitespace', function() {
- return gulp.src([
- 'src/*.js',
- 'src/*.html',
- 'src/*.css'])
- .pipe(lintspaces(lintspacesConfig))
- .pipe(lintspaces.reporter());
-});
-
-gulp.task('js', ['build-multiselect-tpls.js'], function () {
- return gulp.src('src/*.js')
- .pipe(gulp.dest('dist'));
-});
-
-gulp.task('build-multiselect-tpls.js', function () {
- var templateJsStream = gulp
- .src('src/multiselect.tmpl.html')
- .pipe(templateCache({ module: 'am.multiselect' }));
-
- var combinedStream = mergeStream(templateJsStream, gulp.src('src/multiselect.js'));
-
- combinedStream
- .pipe(angularFilesort())
- .pipe(concat('multiselect-tpls.js'))
- .pipe(gulp.dest('dist'));
-
-});
-
-gulp.task('css', function () {
- return gulp.src('src/*.css')
- .pipe(gulp.dest('dist'));
-});
-
-gulp.task('html', function () {
- return gulp.src('src/*.html')
- .pipe(gulp.dest('dist'));
-});
-
-gulp.task('clean', function(cb) {
- return del(['dist']);
-});
-
-/*
- Need to finish clean before building all the other stuff, and all other stuff
- needs to complete before injecting build artifacts into index.html.
-*/
-gulp.task('build-clean', function () {
- console.log("----------------------");
- sequence('clean',
- 'whitespace',
- ['js','css'],
- 'html'
- );
+/* global require */
+/* eslint angular/typecheck-object: "off" */
+/* eslint angular/definedundefined: "off" */
+
+const gulp = require('gulp');
+const args = require('yargs').argv;
+var config = require('./gulp.config')();
+const del = require('del');
+var $ = require('gulp-load-plugins')({lazy: true});
+var serveStatic=require('serve-static');
+var parseurl = require('parseurl')
+
+gulp.task('help', $.taskListing);
+gulp.task('default',['help']);
+
+gulp.task('lintjs', function () {
+ log('Analyzing JS files');
+ return gulp.src(config.alljs)
+ .pipe($.if(args.verbose,$.print()))
+ .pipe($.eslint())
+ .pipe($.eslint.format())
+ .pipe($.eslint.failAfterError());
+});
+
+gulp.task('lintcss', function () {
+ log('Analyzing CSS files');
+ return gulp.src(config.allcss)
+ .pipe($.if(args.verbose,$.print()))
+ .pipe($.csslint())
+ .pipe($.csslint.formatter())
+ .pipe($.csslint.failFormatter());
+});
+
+gulp.task('linthtml', function () {
+ log('Analyzing HTML files');
+ var options = {
+ customattrs: config.htmlAngularValidate.customattrs,
+ customtags: config.htmlAngularValidate.customtags,
+ emitError: true,
+ reportpath: null,
+ reportCheckstylePath: null,
+ reportFn:function(fileFailures){
+ for (var i = 0; i < fileFailures.length; i++) {
+ var fileResult = fileFailures[i];
+ $.util.log(fileResult.filepath);
+ for (var j = 0; j < fileResult.errors.length; j++) {
+ var err = fileResult.errors[j];
+ if (err.line !== undefined) {
+ $.util.log('[line' +err.line +', col: ' + err.col +'] ' +err.msg);
+ } else {
+ $.util.log(err.msg);
+ }
+ }
+ }
+ }
+ };
+ return gulp.src(config.allhtml)
+ .pipe($.if(args.verbose,$.print()))
+ .pipe($.htmlAngularValidate(options));
+});
+
+gulp.task('lintjson', function () {
+ log('Analyzing JSON files');
+ return gulp.src(config.json)
+ .pipe($.if(args.verbose,$.print()))
+ .pipe($.jsonlint())
+ .pipe($.jsonlint.reporter(myCustomReporter))
+ .pipe($.jsonlint.failAfterError());
+});
+
+gulp.task('wireindex',['lintjs','linthtml','lintcss','lintjson'] , function() {
+ var options = config.getWiredepDefaultOptions();
+ var wiredep = require('wiredep').stream;
+ log('Linking all js/css files into index.html');
+ return gulp
+ .src(config.index)
+ .pipe($.if(args.verbose,$.print()))
+ .pipe(wiredep(options))
+ .pipe($.inject(gulp.src(config.js,{read:false}),{relative:true}))
+ .pipe($.inject(gulp.src(config.css,{read:false}),{relative:true}))
+ .pipe(gulp.dest(config.source));
+});
+
+gulp.task('serve', function() {
+ log('Starting webserver for - '+($.util.env.production?'PROD':'DEV'));
+ var options = config.getWebserverOptions();
+ gulp
+ .src($.util.env.production?config.build:config.source)
+ .pipe($.if(args.verbose,$.print()))
+ .pipe($.webserver(options));
+});
+
+gulp.task('resources', function() {
+ log('copying resources');
+ return gulp
+ .src(config.resources)
+ .pipe(gulp.dest(config.build+'resources'));
+});
+
+gulp.task('jsoncopy', function() {
+ log('copying and minifying json resources');
+ return gulp
+ .src(config.json)
+ .pipe($.jsonminify())
+ .pipe(gulp.dest(config.build+'resources/json'));
+});
+
+gulp.task('htmlcopy', ['fontscopy'], function() {
+ log('copying html files for making it ready for templatecache');
+ return gulp
+ .src(config.htmltemplates)
+ .pipe($.flatten())
+ .pipe(gulp.dest(config.temp+'html/'));
+});
+
+gulp.task('fontscopy', ['clean'], function() {
+ log('copying bootstrap and other fonts');
+ var filterFonts = $.filter('**/*.{eot,svg,ttf,woff,woff2}', { restore: true });
+
+ return gulp
+ .src(config.bowerJSON)
+ .pipe($.mainBowerFiles())
+ .pipe(filterFonts)
+ .pipe($.flatten())
+ .pipe(gulp.dest(config.build+'fonts/'));
+});
+
+gulp.task('images', function() {
+ log('copying and compressing images');
+ return gulp
+ .src(config.images)
+ .pipe($.imagemin({optimizationLevel:4}))
+ .pipe(gulp.dest(config.build+'resources/images'));
+});
+
+gulp.task('clean', function(done){
+ var delconfig=[].concat(config.build,config.temp,config.dist);
+ log('cleaning '+$.util.colors.blue(delconfig));
+ return del(delconfig,done);
+});
+
+gulp.task('templatecache',['fontscopy'],function(){
+ log('creating AngularJS $templateCache');
+ return gulp
+ .src(config.htmltemplates)
+ .pipe($.if(args.verbose,$.print()))
+ .pipe($.minifyHtml({empty:true}))
+ .pipe($.angularTemplatecache(
+ config.templateCache.file,
+ config.templateCache.options
+ ))
+ .pipe(gulp.dest(config.temp));
});
+
+gulp.task('optimize',['wireindex','templatecache'],function() {
+ log('optimizing the js/html/css files');
+ var assets = $.useref({searchPath: './src/'});
+ var templateCache = config.temp+config.templateCache.file;
+ var cssFilter = $.filter('**/*.css',{restore:true});
+ var jsLibFilter = $.filter('**/'+config.optimized.lib,{restore:true});
+ var jsAppFilter = $.filter('**/'+config.optimized.app,{restore:true});
+ return gulp
+ .src(config.index)
+ //.pipe($.plumber())
+ .pipe($.inject(gulp.src(templateCache,{read:false}), {
+ starttag: '',
+ relative: true
+ }))
+ .pipe(assets)
+ .pipe(cssFilter)
+ .pipe($.csso())
+ .pipe($.rev())
+ .pipe(cssFilter.restore)
+ .pipe(jsLibFilter)
+ .pipe($.uglify())
+ .pipe($.rev())
+ .pipe(jsLibFilter.restore)
+ .pipe(jsAppFilter)
+ .pipe($.ngAnnotate({add:true}))
+ .pipe($.uglify())
+ .pipe($.rev())
+ .pipe(jsAppFilter.restore)
+ .pipe($.revReplace())
+ .pipe(gulp.dest(config.build))
+ .pipe($.rev.manifest())
+ .pipe(gulp.dest(config.build));
+});
+
+gulp.task('testTask', function() {
+ return gulp
+ .src(config.bowerJSON)
+ .pipe($.if(args.verbose,$.print()))
+ .pipe($.mainBowerFiles())
+ .pipe(gulp.dest(config.temp));
+});
+
+gulp.task('copyDistJS', ['clean'], function() {
+ var copyconfig=[].concat(config.distJsFiles);
+ log('copying distribution JS files');
+ return gulp
+ .src(copyconfig)
+ .pipe(gulp.dest(config.dist+'js/'));
+});
+
+gulp.task('copyDistHTML', ['copyDistJS'], function() {
+ var copyconfig=[].concat(config.distHtmlFiles);
+ log('copying distribution HTML files');
+ return gulp
+ .src(copyconfig)
+ .pipe(gulp.dest(config.dist+'html/'));
+});
+
+gulp.task('copyDistCSS', ['copyDistHTML'], function() {
+ var copyconfig=[].concat(config.distCssFiles);
+ log('copying distribution CSS files');
+ return gulp
+ .src(copyconfig)
+ .pipe(gulp.dest(config.dist+'css/'));
+});
+
+gulp.task('templatecachedist',['copyDistCSS'],function(){
+ log('creating AngularJS $templateCache for dist');
+ return gulp
+ .src(config.htmltemplatesDist)
+ .pipe($.if(args.verbose,$.print()))
+ .pipe($.minifyHtml({empty:true}))
+ .pipe($.angularTemplatecache(
+ config.templateCacheDist.file,
+ config.templateCacheDist.options
+ ))
+ .pipe(gulp.dest(config.dist+'js/'));
+});
+
+gulp.task('distribute', ['templatecachedist'], function() {
+ log('optimizing the dist files');
+ var cssFilter = $.filter('**/*.css',{restore:true});
+ var jsFilter = $.filter('**/*.js',{restore:true});
+ var htmlFilter = $.filter('**/*.html',{restore:true});
+
+ return gulp
+ .src(config.dist+'**/*')
+ .pipe(cssFilter)
+ .pipe($.if(args.verbose,$.print()))
+ .pipe($.csso())
+ .pipe($.rename({suffix: '.min'}))
+ .pipe(cssFilter.restore)
+ .pipe(jsFilter)
+ .pipe($.if(args.verbose,$.print()))
+ .pipe($.uglify())
+ .pipe($.rename({suffix: '.min'}))
+ .pipe(jsFilter.restore)
+ .pipe(htmlFilter)
+ .pipe($.if(args.verbose,$.print()))
+ .pipe($.minifyHtml({empty:true}))
+ .pipe(htmlFilter.restore)
+ .pipe(gulp.dest(config.dist));
+});
+
+gulp.task('serve-dev',['wireindex'],function() {
+ log('Starting webserver for - DEV');
+ var options = config.getWebserverOptions();
+ options.middleware=[getStatic({route: /^\/json/, handle: serveStatic('resources')})];
+ gulp
+ .src(config.source)
+ .pipe($.if(args.verbose,$.print()))
+ .pipe($.webserver(options));
+});
+
+gulp.task('serve-prod',['optimize'],function() {
+ log('Starting webserver for - PROD');
+ var options = config.getWebserverOptions();
+ options.middleware=[getStatic({route: /^\/json/, handle: serveStatic('resources')})];
+ gulp
+ .src(config.build)
+ .pipe($.if(args.verbose,$.print()))
+ .pipe($.webserver(options));
+});
+
+//gulp.task('bump', function() {
+// // not using this -- mainly for bumping the npm version in package.json
+//});
+
+//gulp.task('lintHTML', function () {
+// log('Analyzing HTML files');
+// return gulp.src(config.allhtml)
+// .pipe($.if(args.verbose,$.print()))
+// .pipe($.htmllint({failOnError: true}, htmllintReporter));
+//});
+
+//gulp.task('styles', function() {
+// log('compiling Less --> CSS');
+//
+// return gulp
+// .src(config.less)
+// .pipe($.less())
+// .pipe($.autoprefixer({browsers: ['last 2 version','> 5%']}))
+// .pipe(gulp.dest(config.temp));
+//
+//});
+
+////////////////////
+
+function log(msg) {
+ if(typeof(msg)==='object') {
+ for(var item in msg) {
+ if(msg.hasOwnProperty(item)) {
+ $.util.log($.util.colors.blue(msg[item]));
+ }
+ }
+ } else {
+ $.util.log($.util.colors.blue(msg));
+ }
+}
+
+var myCustomReporter = function (file) {
+ $.util.log('File ' + file.path + ' is not valid JSON.');
+};
+
+function getStatic(opts) {
+ return function(req, res, next) {
+ if (parseurl(req).pathname.match(opts.route)) {
+ return opts.handle(req, res, next);
+ } else {
+ return next();
+ }
+ }
+}
+
+//function htmllintReporter(filepath, issues) {
+// if (issues.length > 0) {
+// issues.forEach(function (issue) {
+// $.util.log($.util.colors.cyan('[gulp-htmllint] ') + $.util.colors.white(filepath + ' [' + issue.line + ',' + issue.column + ']: ') + $.util.colors.red('(' + issue.code + ') ' + issue.msg));
+// });
+// //process.exitCode = 1;
+// this.emit('error', new $.util.PluginError('gulp-htmllint', 'Linter errors occurred!'));
+// this.emit('end');
+// }
+//}
diff --git a/package.json b/package.json
index 9d486b1..c54ff5b 100644
--- a/package.json
+++ b/package.json
@@ -1,27 +1,51 @@
{
"name": "angular-multiselect",
- "version": "1.2.0",
+ "version": "1.3.13",
"description": "A Native AngularJS multiselect directive",
"main": "gulpfile.js",
"dependencies": {},
"devDependencies": {
- "del": "^2.0.2",
- "gulp": "^3.9.0",
- "gulp-angular-filesort": "^1.1.1",
- "gulp-angular-templatecache": "^1.7.0",
- "gulp-concat": "^2.6.0",
- "gulp-lintspaces": "^0.3.1",
- "gulp-uglify": "^1.4.1",
- "gulp-watch": "^4.3.5",
- "merge-stream": "^1.0.0",
- "run-sequence": "^1.1.4"
+ "del": "^2.2.2",
+ "eslint-config-angular": "^0.5.0",
+ "eslint-plugin-angular": "^2.4.0",
+ "gulp": "^3.9.1",
+ "gulp-angular-templatecache": "^2.0.0",
+ "gulp-csslint": "^1.0.0",
+ "gulp-csso": "^3.0.0",
+ "gulp-eslint": "^3.0.1",
+ "gulp-filter": "^5.0.0",
+ "gulp-flatten": "^0.3.1",
+ "gulp-html-angular-validate": "^0.2.0",
+ "gulp-if": "^2.0.2",
+ "gulp-imagemin": "^3.2.0",
+ "gulp-inject": "^4.2.0",
+ "gulp-jsonlint": "^1.2.0",
+ "gulp-jsonminify": "^1.0.0",
+ "gulp-load-plugins": "^1.5.0",
+ "gulp-main-bower-files": "^1.6.2",
+ "gulp-minify-html": "^1.0.6",
+ "gulp-ng-annotate": "^2.0.0",
+ "gulp-plumber": "^1.1.0",
+ "gulp-print": "^2.0.1",
+ "gulp-rename": "^1.2.2",
+ "gulp-rev": "^7.1.2",
+ "gulp-rev-replace": "^0.4.3",
+ "gulp-task-listing": "^1.0.1",
+ "gulp-uglify": "^2.1.2",
+ "gulp-useref": "^3.1.2",
+ "gulp-util": "^3.0.8",
+ "gulp-webserver": "^0.9.1",
+ "parseurl": "^1.3.1",
+ "serve-static": "^1.12.2",
+ "wiredep": "^4.0.0",
+ "yargs": "^8.0.1"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
- "url": "git+https://github.com/amitava82/angular-multiselect.git"
+ "url": "git+https://github.com/NithinBiliya/angular-multiselect.git"
},
"keywords": [
"angular-multiselect",
@@ -29,20 +53,10 @@
"angular",
"angulajs-multiselect"
],
- "author": "amitava82 (github.com/amitava82)",
- "contributors": [
- {
- "name": "Nagaraja T",
- "email": "naga2raja@gmail.com"
- },
- {
- "name": "Zach Lysobey",
- "email": "zlysobey@gmail.com"
- }
- ],
+ "author": "Nithin Biliya",
"license": "MIT",
"bugs": {
- "url": "https://github.com/amitava82/angular-multiselect/issues"
+ "url": "https://github.com/NithinBiliya/angular-multiselect/issues"
},
- "homepage": "https://github.com/amitava82/angular-multiselect#readme"
+ "homepage": "https://github.com/NithinBiliya/angular-multiselect#readme"
}
diff --git a/src/app.js b/src/app.js
deleted file mode 100644
index 7f14db4..0000000
--- a/src/app.js
+++ /dev/null
@@ -1,17 +0,0 @@
-angular.module('app', ['am.multiselect'])
-
-.controller('appCtrl', ['$scope', function($scope){
- $scope.cars = [
- {id:1, name: 'Audi'},
- {id:2, name: 'BMW'},
- {id:3, name: 'Honda'}
- ];
- $scope.selectedCar = [];
-
- $scope.fruits = [
- {id: 1, name: 'Apple'},
- {id: 2, name: 'Orange'},
- {id: 3, name: 'Banana'}
- ];
- $scope.selectedFruit = null;
-}]);
diff --git a/src/css/multiselect.css b/src/css/multiselect.css
new file mode 100644
index 0000000..4890e90
--- /dev/null
+++ b/src/css/multiselect.css
@@ -0,0 +1,30 @@
+/*csslint box-sizing: false*/
+
+am-multiselect .dropdown-menu{
+ box-sizing: content-box;
+ height: 200px;
+ overflow-x: scroll;
+ overflow-y: scroll;
+ padding-left: 5px;
+ padding-right: 5px;
+}
+
+am-multiselect .dropdown-menu > li > a {
+ cursor:pointer;
+ padding: 3px 10px;
+}
+
+am-multiselect .dropdown-menu > .selected {
+ background-color: #ADD8E6;
+}
+
+am-multiselect .btn-group > button {
+ border-radius:5px !important; /* csslint allow: known-properties, important */
+ overflow:hidden;
+ white-space: normal;
+ width:100%;
+}
+
+am-multiselect .btn-group {
+ width:100%;
+}
diff --git a/dist/multiselect.tmpl.html b/src/html/multiselect.tmpl.html
similarity index 74%
rename from dist/multiselect.tmpl.html
rename to src/html/multiselect.tmpl.html
index 4a57429..4bb8d58 100644
--- a/dist/multiselect.tmpl.html
+++ b/src/html/multiselect.tmpl.html
@@ -1,11 +1,11 @@
-
+
{{header}}
diff --git a/dist/singleselect.tmpl.html b/src/html/singleselect.tmpl.html
similarity index 98%
rename from dist/singleselect.tmpl.html
rename to src/html/singleselect.tmpl.html
index 5d629d1..cc894a8 100644
--- a/dist/singleselect.tmpl.html
+++ b/src/html/singleselect.tmpl.html
@@ -9,4 +9,4 @@
{{i.label}}
-
\ No newline at end of file
+
diff --git a/src/index.html b/src/index.html
index b32f42e..05662f1 100644
--- a/src/index.html
+++ b/src/index.html
@@ -1,28 +1,55 @@
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
Angular multiselect
-
-
-
-
-
-
-
- Example
-
-
- {{selectedCar}}
-
- Single select:
-
-
- {{selectedFruit}}
-
-
+
+
+
+ Example
+
+
+ {{vm.selectedCar}}
+
+ Single select:
+
+
+ {{vm.selectedFruit}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/js/app.js b/src/js/app.js
new file mode 100644
index 0000000..ed95899
--- /dev/null
+++ b/src/js/app.js
@@ -0,0 +1,106 @@
+/* eslint angular/controller-name: "off" */
+
+angular.module('app', ['am.multiselect'])
+ .controller('appCtrl', ['$log',function ($log) {
+ var vm=this;
+
+ vm.cars = [
+ {
+ id: 1,
+ name: 'Audi'
+ },
+ {
+ id: 2,
+ name: 'BMW'
+ },
+ {
+ id: 3,
+ name: 'Honda'
+ },
+ {
+ id: 4,
+ name: 'Audi'
+ },
+ {
+ id: 5,
+ name: 'BMW'
+ },
+ {
+ id: 6,
+ name: 'Honda'
+ },
+ {
+ id: 7,
+ name: 'Audi'
+ },
+ {
+ id: 8,
+ name: 'BMW'
+ },
+ {
+ id: 9,
+ name: 'Honda'
+ },
+ {
+ id: 10,
+ name: 'Audi'
+ },
+ {
+ id: 11,
+ name: 'BMW'
+ },
+ {
+ id: 12,
+ name: 'Honda'
+ },
+ {
+ id: 13,
+ name: 'Audi'
+ },
+ {
+ id: 14,
+ name: 'BMW'
+ },
+ {
+ id: 15,
+ name: 'Honda'
+ },
+ {
+ id: 16,
+ name: 'Audi'
+ },
+ {
+ id: 17,
+ name: 'BMW'
+ },
+ {
+ id: 18,
+ name: 'Honda'
+ }
+ ];
+ vm.selectedCar = [];
+
+ vm.fruits = [
+ {
+ id: 1,
+ name: 'Apple'
+ },
+ {
+ id: 2,
+ name: 'Orange'
+ },
+ {
+ id: 3,
+ name: 'Banana'
+ },
+ {
+ id: 4,
+ name: 'BananaBananaBananaBananaBananaBananaBananaBananaBananaBananaBananaBananaBananaBananaBananaBananaBanana'
+ }
+ ];
+ vm.selectedFruit = null;
+
+ vm.focusHanadler=function(prop1) {
+ $log.info("inside focusHandler - "+prop1);
+ }
+}]);
diff --git a/dist/multiselect.js b/src/js/multiselect.js
similarity index 81%
rename from dist/multiselect.js
rename to src/js/multiselect.js
index 94987b9..2622f7c 100644
--- a/dist/multiselect.js
+++ b/src/js/multiselect.js
@@ -1,4 +1,5 @@
// Source: https://github.com/amitava82/angular-multiselect
+
angular.module('am.multiselect', [])
// from bootstrap-ui typeahead parser
@@ -39,6 +40,7 @@ angular.module('am.multiselect', [])
var exp = attrs.options,
parsedResult = optionParser.parse(exp),
isMultiple = attrs.multiple ? true : false,
+ isHover = attrs.hover ? true : false,
required = false,
scope = originalScope.$new(),
changeHandler = attrs.change || angular.noop;
@@ -47,7 +49,11 @@ angular.module('am.multiselect', [])
scope.header = 'Select';
scope.multiple = isMultiple;
scope.disabled = false;
+ scope.searchDisable = false;
scope.onBlur = attrs.ngBlur || angular.noop;
+ scope.hoverText = isHover ? scope.header : '';
+ scope.onFocus = attrs.ngFocus;
+ scope.clazz = attrs.clazz;
originalScope.$on('$destroy', function () {
scope.$destroy();
@@ -67,11 +73,18 @@ angular.module('am.multiselect', [])
// watch disabled state
scope.$watch(function () {
- return $parse(attrs.disabled)(originalScope);
+ return $parse(attrs.ngDisabled)(originalScope);
}, function (newVal) {
scope.disabled = newVal;
});
+ // watch disabled state for search text box
+ scope.$watch(function () {
+ return $parse(attrs.searchDisable)(originalScope);
+ }, function (newVal) {
+ scope.searchDisable = newVal;
+ });
+
// watch single/multiple state for dynamically change single to multiple
scope.$watch(function () {
return $parse(attrs.multiple)(originalScope);
@@ -90,22 +103,29 @@ angular.module('am.multiselect', [])
// watch model change
scope.$watch(function () {
return modelCtrl.$modelValue;
- }, function (newVal, oldVal) {
+ }, function (newVal) {
+ // When the model is assigned a "" or undefined value from controller, need to uncheck all items and clear searchText.label
+ if(angular.isUndefined(newVal) || newVal==="" || newVal===null) {
+ scope.uncheckAll();
+ if(angular.isDefined(scope.searchText))
+ scope.searchText.label="";
+ }
// when directive initialize, newVal usually undefined. Also, if model value already set in the controller
// for preselected list then we need to mark checked in our scope item. But we don't want to do this every time
// model changes. We need to do this only if it is done outside directive scope, from controller, for example.
- if (angular.isDefined(newVal)) {
+ else if (angular.isDefined(newVal)) {
markChecked(newVal);
scope.$eval(changeHandler);
}
getHeaderText();
+ scope.hoverText = isHover ? scope.header : '';
modelCtrl.$setValidity('required', scope.valid());
}, true);
function parseModel() {
scope.items.length = 0;
var model = parsedResult.source(originalScope);
- if(!angular.isDefined(model)) return;
+ if(angular.isUndefined(model)) return;
for (var i = 0; i < model.length; i++) {
var local = {};
local[parsedResult.itemName] = model[i];
@@ -121,8 +141,16 @@ angular.module('am.multiselect', [])
element.append($compile(popUpEl)(scope));
+ function getItemLabel(items,model) {
+ for(var i = 0; i < items.length; i++) {
+ if(items[i].model==model) {
+ return items[i].label;
+ }
+ }
+ }
+
function getHeaderText() {
- if (is_empty(modelCtrl.$modelValue)) return scope.header = (attrs.msHeader!==undefined ? attrs.msHeader : 'Select');
+ if (is_empty(modelCtrl.$modelValue)) return scope.header = (angular.isDefined(attrs.msHeader) ? attrs.msHeader : 'Select');
if (isMultiple) {
if (attrs.msSelected) {
@@ -140,11 +168,11 @@ angular.module('am.multiselect', [])
}
} else {
if(angular.isString(modelCtrl.$modelValue)){
- scope.header = modelCtrl.$modelValue;
+ scope.header = getItemLabel(scope.items,modelCtrl.$modelValue);
} else {
var local = {};
local[parsedResult.itemName] = modelCtrl.$modelValue;
- scope.header = parsedResult.viewMapper(local) || scope.items[modelCtrl.$modelValue].label;
+ scope.header = parsedResult.viewMapper(local) || getItemLabel(scope.items,modelCtrl.$modelValue);
}
}
}
@@ -154,7 +182,7 @@ angular.module('am.multiselect', [])
if (obj && obj.length && obj.length > 0) return false;
for (var prop in obj) if (obj[prop]) return false;
return true;
- };
+ }
scope.valid = function validModel() {
if(!required) return true;
@@ -178,13 +206,14 @@ angular.module('am.multiselect', [])
}
function setModelValue(isMultiple) {
- var value = null;
+ var value = undefined;
if (isMultiple) {
value = [];
angular.forEach(scope.items, function (item) {
if (item.checked) value.push(item.model);
})
+ if(value.length==0) value=undefined;
} else {
angular.forEach(scope.items, function (item) {
if (item.checked) {
@@ -228,11 +257,13 @@ angular.module('am.multiselect', [])
};
scope.uncheckAll = function () {
- var items = (scope.searchText && scope.searchText.label.length > 0) ? $filter('filter')(scope.items, scope.searchText) : scope.items;
- angular.forEach(items, function (item) {
+ // need to uncheck from the entire list of items. If user filers with ine text and selects item A. Next time user fileters and selects item B (item A now not in the filtered set). The item A will not get unchecked
+ // var items = (scope.searchText && scope.searchText.label.length > 0) ? $filter('filter')(scope.items, scope.searchText) : scope.items;
+ angular.forEach(scope.items, function (item) {
item.checked = false;
});
- setModelValue(true);
+ // sending scope.multiple instead of true to setModelValue. Since different values geeting set when single and multiple.
+ setModelValue(scope.multiple);
};
scope.select = function (item) {
@@ -252,15 +283,18 @@ angular.module('am.multiselect', [])
restrict: 'E',
scope: false,
replace: true,
- templateUrl: function (element, attr) {
- return attr.templateUrl || 'multiselect.tmpl.html';
- },
- link: function (scope, element, attrs) {
+ templateUrl: 'html/multiselect.tmpl.html',
+ link: function (scope, element) {
scope.selectedIndex = null;
scope.isVisible = false;
scope.filteredItems = null;
+ scope.ngClazz = {
+ 'error': !scope.valid()
+ };
+ scope.ngClazz[scope.$eval(scope.clazz)] = true;
+
scope.toggleSelect = function () {
if (element.hasClass('open')) {
element.removeClass('open');
@@ -273,6 +307,10 @@ angular.module('am.multiselect', [])
}
};
+ scope.evalFocus = function() {
+ scope.$parent.$eval(scope.onFocus);
+ }
+
function clickHandler(event) {
if (elementMatchesAnyInArray(event.target, element.find(event.target.tagName))) {
scope.$parent.$eval(scope.onBlur);
diff --git a/src/multiselect.css b/src/multiselect.css
deleted file mode 100644
index 3b817ad..0000000
--- a/src/multiselect.css
+++ /dev/null
@@ -1,13 +0,0 @@
-am-multiselect .dropdown-menu{
- padding-left: 5px;
- padding-right: 5px;
-}
-
-am-multiselect .dropdown-menu > li > a {
- padding: 3px 10px;
- cursor:pointer;
-}
-
-am-multiselect .dropdown-menu > li.selected {
- background-color: #ADD8E6;
-}
\ No newline at end of file
diff --git a/src/multiselect.js b/src/multiselect.js
deleted file mode 100644
index 94987b9..0000000
--- a/src/multiselect.js
+++ /dev/null
@@ -1,324 +0,0 @@
-// Source: https://github.com/amitava82/angular-multiselect
-angular.module('am.multiselect', [])
-
-// from bootstrap-ui typeahead parser
-.factory('optionParser', ['$parse', function ($parse) {
- // 00000111000000000000022200000000000000003333333333333330000000000044000
- var TYPEAHEAD_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+([\s\S]+?)$/;
- return {
- parse:function (input) {
- var match = input.match(TYPEAHEAD_REGEXP);
- if (!match) {
- throw new Error(
- 'Expected typeahead specification in form of "_modelValue_ (as _label_)? for _item_ in _collection_"' +
- ' but got "' + input + '".');
- }
- return {
- itemName:match[3],
- source:$parse(match[4]),
- viewMapper:$parse(match[2] || match[1]),
- modelMapper:$parse(match[1])
- };
- }
- };
-}])
-
-.directive('amMultiselect', ['$parse', '$document', '$compile', '$interpolate', '$filter', 'optionParser',
-
- function ($parse, $document, $compile, $interpolate, $filter, optionParser) {
- return {
- restrict: 'E',
- require: 'ngModel',
- link: function (originalScope, element, attrs, modelCtrl) {
- // Redefine isEmpty - this allows this to work on at least Angular 1.2.x
- var isEmpty = modelCtrl.$isEmpty;
- modelCtrl.$isEmpty = function(value) {
- return isEmpty(value) || (angular.isArray(value) && value.length == 0);
- };
-
- var exp = attrs.options,
- parsedResult = optionParser.parse(exp),
- isMultiple = attrs.multiple ? true : false,
- required = false,
- scope = originalScope.$new(),
- changeHandler = attrs.change || angular.noop;
-
- scope.items = [];
- scope.header = 'Select';
- scope.multiple = isMultiple;
- scope.disabled = false;
- scope.onBlur = attrs.ngBlur || angular.noop;
-
- originalScope.$on('$destroy', function () {
- scope.$destroy();
- });
-
- var popUpEl = angular.element(' ');
-
- // required validator
- if (attrs.required || attrs.ngRequired) {
- required = true;
- }
- attrs.$observe('required', function(newVal) {
- required = newVal;
- });
-
- // watch disabled state
- scope.$watch(function () {
- return $parse(attrs.disabled)(originalScope);
- }, function (newVal) {
- scope.disabled = newVal;
- });
-
- // watch single/multiple state for dynamically change single to multiple
- scope.$watch(function () {
- return $parse(attrs.multiple)(originalScope);
- }, function (newVal) {
- isMultiple = newVal || false;
- });
-
- // watch option changes for options that are populated dynamically
- scope.$watch(function () {
- return parsedResult.source(originalScope);
- }, function (newVal) {
- if (angular.isDefined(newVal))
- parseModel();
- }, true);
-
- // watch model change
- scope.$watch(function () {
- return modelCtrl.$modelValue;
- }, function (newVal, oldVal) {
- // when directive initialize, newVal usually undefined. Also, if model value already set in the controller
- // for preselected list then we need to mark checked in our scope item. But we don't want to do this every time
- // model changes. We need to do this only if it is done outside directive scope, from controller, for example.
- if (angular.isDefined(newVal)) {
- markChecked(newVal);
- scope.$eval(changeHandler);
- }
- getHeaderText();
- modelCtrl.$setValidity('required', scope.valid());
- }, true);
-
- function parseModel() {
- scope.items.length = 0;
- var model = parsedResult.source(originalScope);
- if(!angular.isDefined(model)) return;
- for (var i = 0; i < model.length; i++) {
- var local = {};
- local[parsedResult.itemName] = model[i];
- scope.items.push({
- label: parsedResult.viewMapper(local),
- model: parsedResult.modelMapper(local),
- checked: false
- });
- }
- }
-
- parseModel();
-
- element.append($compile(popUpEl)(scope));
-
- function getHeaderText() {
- if (is_empty(modelCtrl.$modelValue)) return scope.header = (attrs.msHeader!==undefined ? attrs.msHeader : 'Select');
-
- if (isMultiple) {
- if (attrs.msSelected) {
- scope.header = $interpolate(attrs.msSelected)(scope);
- } else {
- if (modelCtrl.$modelValue.length == 1) {
- for(var i = 0; i < scope.items.length; i++) {
- if(scope.items[i].model === modelCtrl.$modelValue[0]) {
- scope.header = scope.items[i].label;
- }
- }
- } else {
- scope.header = modelCtrl.$modelValue.length + ' ' + 'selected';
- }
- }
- } else {
- if(angular.isString(modelCtrl.$modelValue)){
- scope.header = modelCtrl.$modelValue;
- } else {
- var local = {};
- local[parsedResult.itemName] = modelCtrl.$modelValue;
- scope.header = parsedResult.viewMapper(local) || scope.items[modelCtrl.$modelValue].label;
- }
- }
- }
-
- function is_empty(obj) {
- if (angular.isNumber(obj)) return false;
- if (obj && obj.length && obj.length > 0) return false;
- for (var prop in obj) if (obj[prop]) return false;
- return true;
- };
-
- scope.valid = function validModel() {
- if(!required) return true;
- var value = modelCtrl.$modelValue;
- return (angular.isArray(value) && value.length > 0) || (!angular.isArray(value) && value != null);
- };
-
- function selectSingle(item) {
- if (item.checked) {
- scope.uncheckAll();
- } else {
- scope.uncheckAll();
- item.checked = !item.checked;
- }
- setModelValue(false);
- }
-
- function selectMultiple(item) {
- item.checked = !item.checked;
- setModelValue(true);
- }
-
- function setModelValue(isMultiple) {
- var value = null;
-
- if (isMultiple) {
- value = [];
- angular.forEach(scope.items, function (item) {
- if (item.checked) value.push(item.model);
- })
- } else {
- angular.forEach(scope.items, function (item) {
- if (item.checked) {
- value = item.model;
- return false;
- }
- })
- }
- modelCtrl.$setViewValue(value);
- }
-
- function markChecked(newVal) {
- if (!angular.isArray(newVal)) {
- angular.forEach(scope.items, function (item) {
- if (angular.equals(item.model, newVal)) {
- scope.uncheckAll();
- item.checked = true;
- setModelValue(false);
- return false;
- }
- });
- } else {
- angular.forEach(scope.items, function (item) {
- item.checked = false;
- angular.forEach(newVal, function (i) {
- if (angular.equals(item.model, i)) {
- item.checked = true;
- }
- });
- });
- }
- }
-
- scope.checkAll = function () {
- if (!isMultiple) return;
- var items = (scope.searchText && scope.searchText.label.length > 0) ? $filter('filter')(scope.items, scope.searchText) : scope.items;
- angular.forEach(items, function (item) {
- item.checked = true;
- });
- setModelValue(true);
- };
-
- scope.uncheckAll = function () {
- var items = (scope.searchText && scope.searchText.label.length > 0) ? $filter('filter')(scope.items, scope.searchText) : scope.items;
- angular.forEach(items, function (item) {
- item.checked = false;
- });
- setModelValue(true);
- };
-
- scope.select = function (item) {
- if (isMultiple === false) {
- selectSingle(item);
- scope.toggleSelect();
- } else {
- selectMultiple(item);
- }
- }
- }
- };
-}])
-
-.directive('amMultiselectPopup', ['$document', '$filter', function ($document, $filter) {
- return {
- restrict: 'E',
- scope: false,
- replace: true,
- templateUrl: function (element, attr) {
- return attr.templateUrl || 'multiselect.tmpl.html';
- },
- link: function (scope, element, attrs) {
-
- scope.selectedIndex = null;
- scope.isVisible = false;
- scope.filteredItems = null;
-
- scope.toggleSelect = function () {
- if (element.hasClass('open')) {
- element.removeClass('open');
- $document.unbind('click', clickHandler);
- scope.$parent.$eval(scope.onBlur);
- } else {
- element.addClass('open');
- $document.bind('click', clickHandler);
- scope.focus();
- }
- };
-
- function clickHandler(event) {
- if (elementMatchesAnyInArray(event.target, element.find(event.target.tagName))) {
- scope.$parent.$eval(scope.onBlur);
- } else {
- element.removeClass('open');
- $document.unbind('click', clickHandler);
- scope.$apply();
- }
- }
-
- scope.focus = function focus(){
- var searchBox = element.find('input')[0];
- if (searchBox) {
- searchBox.focus();
- }
- }
-
- scope.keydown = function (event) {
- var list = $filter('filter')(scope.items, scope.searchText);
- var keyCode = (event.keyCode || event.which);
-
- if(keyCode === 13){ // On enter
- if(list[scope.selectedIndex]){
- scope.select(list[scope.selectedIndex]); // (un)select item
- }
- }else if(keyCode === 38){ // On arrow up
- scope.selectedIndex = scope.selectedIndex===null ? list.length-1 : scope.selectedIndex-1;
- }else if(keyCode === 40){ // On arrow down
- scope.selectedIndex = scope.selectedIndex===null ? 0 : scope.selectedIndex+1;
- }else{ // On any other key
- scope.selectedIndex = null;
- }
-
- if(scope.selectedIndex < 0){ // Select last in list
- scope.selectedIndex = list.length-1;
- }else if(scope.selectedIndex > list.length-1){ // Set selection to first item in list
- scope.selectedIndex = 0;
- }
- };
-
- var elementMatchesAnyInArray = function (element, elementArray) {
- for (var i = 0; i < elementArray.length; i++)
- if (element == elementArray[i])
- return true;
- return false;
- }
- }
- }
-}]);
diff --git a/src/multiselect.tmpl.html b/src/multiselect.tmpl.html
deleted file mode 100644
index 4a57429..0000000
--- a/src/multiselect.tmpl.html
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
- {{header}}
-
-
-
-
diff --git a/src/singleselect.tmpl.html b/src/singleselect.tmpl.html
deleted file mode 100644
index 5d629d1..0000000
--- a/src/singleselect.tmpl.html
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
- {{header}}
-
-
-
-
\ No newline at end of file