Skip to content

Commit 4bcd46d

Browse files
new ES6 build pipeline and reworked off-click-filter
1 parent 1095546 commit 4bcd46d

12 files changed

+318
-70
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules

.nmpignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node_modules
2+
src
3+
bower.json
4+
gulpfile.js

README.md

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,21 @@ It's like click, but when you don't click on your element.
77
```
88
bower install angular-off-click --save
99
```
10+
1011
```javascript
1112
angular('yourAngularApp',['offClick']);
1213
```
1314

1415
<h4>Usage/Example</h4>
1516
Here we have a slide out navigation div that will appear when the user clicks a button. We want the div to go away when they click off of it (`off-click`). We also want to make sure the button that triggers the div to open, also does initial close it (`off-click-filter`).
1617
```html
17-
<button id="nav-toggle">Show Navigation</button>
18-
<div id="slide-out-nav" ng-show="showNav" off-click="showNav = false" off-click-filter="'#nav-toggle'" off-click-if="showNav">
18+
<button id="nav-toggle" off-click-filter="#slide-out-nav" ng-click="showNav = !showNav">Show Navigation</button>
19+
<div id="slide-out-nav" ng-show="showNav" off-click="showNav = false" off-click-if="showNav">
1920
...
2021
</div>
2122
```
2223

2324
The `off-click` attribute is the expression or function that will execute each time the user doesn't click on your element (or filter)<br />
24-
The optional `off-click-filter` attribute is a comma separated list of selectors that will not trigger `off-click` when they are clicked.<br />
25-
The optional `off-click-if` attribute is an expression that will determine if the `off-click` should trigger or not.
26-
27-
<h4>Other Features</h4>
28-
Ability to choose elements outside of your element that will not trigger the `off-click` event.<br />
29-
Ability to pass an expression to determine of the `off-click` event should trigger.
25+
The optional `off-click-if` attribute is an expression that will determine if the `off-click` should trigger or not.<br/>
26+
The optional `off-click-filter` directive allows you to pass a comma separated list of targets whose `off-click` will not be triggered when the element `off-click-filter` was applied to is clicked.
3027

bower.json

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,29 @@
11
{
22
"name": "angular-off-click",
3-
"version": "0.0.10",
4-
"main": "offClick.js",
3+
"version": "1.0.0",
4+
"main": [
5+
"./dist/angular-off-click.js"
6+
],
57
"ignore": [
68
".jshintrc",
79
"**/*.txt"
810
],
11+
"license": "MIT",
12+
"keywords": [
13+
"angular",
14+
"off",
15+
"click"
16+
],
17+
"author": {
18+
"name": "Evan Sharp",
19+
"url": "https://github.com/TheSharpieOne"
20+
},
21+
"contributors": [
22+
{
23+
"name": "Ricardo Marques",
24+
"url": "https://github.com/RicardoAlmeidaMarques"
25+
}
26+
],
927
"dependencies": {
1028
"angular": "*"
1129
}

dist/angular-off-click.js

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
'use strict';
2+
3+
angular.module('offClick', []);
4+
angular.module('offClick').directive('offClick', ["$rootScope", "$parse", "OffClickFilterCache", function ($rootScope, $parse, OffClickFilterCache) {
5+
var id = 0;
6+
var listeners = {};
7+
// add variable to detect touch users moving..
8+
var touchMove = false;
9+
10+
var targetInFilter = function targetInFilter(target, elms) {
11+
if (!target || !elms) return false;
12+
var elmsLen = elms.length;
13+
for (var i = 0; i < elmsLen; ++i) {
14+
var currentElem = elms[i];
15+
var containsTarget = false;
16+
try {
17+
containsTarget = currentElem.contains(target);
18+
} catch (e) {
19+
// If the node is not an Element (e.g., an SVGElement) node.contains() throws Exception in IE,
20+
// see https://connect.microsoft.com/IE/feedback/details/780874/node-contains-is-incorrect
21+
// In this case we use compareDocumentPosition() instead.
22+
if (typeof currentElem.compareDocumentPosition !== 'undefined') {
23+
containsTarget = currentElem === target || Boolean(currentElem.compareDocumentPosition(target) & 16);
24+
}
25+
}
26+
27+
if (containsTarget) {
28+
return true;
29+
}
30+
}
31+
return false;
32+
};
33+
34+
var offClickEventHandler = function offClickEventHandler(event) {
35+
// If event is a touchmove adjust touchMove state
36+
if (event.type === 'touchmove') {
37+
touchMove = true;
38+
// And end function
39+
return false;
40+
}
41+
// This will always fire on the touchend after the touchmove runs...
42+
if (touchMove) {
43+
// Reset touchmove to false
44+
touchMove = false;
45+
// And end function
46+
return false;
47+
}
48+
var target = event.target || event.srcElement;
49+
angular.forEach(listeners, function (listener, i) {
50+
var filters = [];
51+
if (listener.elm.id && listener.elm.id !== '') {
52+
if (OffClickFilterCache['#' + listener.elm.id]) filters = filters.concat(OffClickFilterCache['#' + listener.elm.id]);
53+
}
54+
// classList is an object in IE10 and 11 iirc, using angular.forEach to iterate both over an array or object values
55+
angular.forEach(listener.elm.classList, function (className) {
56+
if (OffClickFilterCache['.' + className]) filters = filters.concat(OffClickFilterCache['.' + className]);
57+
});
58+
if (!(listener.elm.contains(target) || targetInFilter(target, filters))) {
59+
$rootScope.$evalAsync(function () {
60+
listener.cb(listener.scope, {
61+
$event: event
62+
});
63+
});
64+
}
65+
});
66+
};
67+
68+
// Add event listeners to handle various events. Destop will ignore touch events
69+
document.addEventListener("touchmove", offClickEventHandler, true);
70+
document.addEventListener("touchend", offClickEventHandler, true);
71+
document.addEventListener('click', offClickEventHandler, true);
72+
73+
return {
74+
restrict: 'A',
75+
compile: function compile(elem, attrs) {
76+
var fn = $parse(attrs.offClick);
77+
78+
var elmId = id++;
79+
var removeWatcher = void 0;
80+
81+
var on = function on() {
82+
listeners[elmId] = {
83+
elm: element[0],
84+
cb: fn,
85+
scope: scope
86+
};
87+
};
88+
89+
var off = function off() {
90+
listeners[elmId] = null;
91+
delete listeners[elmId];
92+
};
93+
94+
if (attrs.offClickIf) {
95+
removeWatcher = $rootScope.$watch(function () {
96+
return $parse(attrs.offClickIf)(scope);
97+
}, function (newVal) {
98+
newVal && on() || !newVal && off();
99+
});
100+
} else on();
101+
102+
return function (scope, element) {
103+
scope.$on('$destroy', function () {
104+
off();
105+
if (removeWatcher) {
106+
removeWatcher();
107+
}
108+
element = null;
109+
});
110+
};
111+
}
112+
};
113+
}]);
114+
angular.module('offClick').directive('offClickFilter', ["OffClickFilterCache", "$parse", function (OffClickFilterCache, $parse) {
115+
var filters = void 0;
116+
var addFiltersToCache = function addFiltersToCache(filters) {
117+
filters.forEach(function (filter) {
118+
OffClickFilterCache[filter] ? OffClickFilterCache[filter].push(elem[0]) : OffClickFilterCache[filter] = [elem[0]];
119+
});
120+
};
121+
return {
122+
restrict: 'A',
123+
compile: function compile(elem, attrs) {
124+
addFiltersToCache($parse(attrs.OffClickFilterCache).split(',').map(function (x) {
125+
return x.trim();
126+
}));
127+
return function (scope, element) {
128+
scope.$on('$destroy', function () {
129+
element = null;
130+
filters.forEach(function (filter) {
131+
if (OffClickFilterCache[filter].length > 1) {
132+
OffClickFilterCache[filter].splice(OffClickFilterCache[filter].indexOf(elem[0]), 1);
133+
} else {
134+
OffClickFilterCache[filter] = null;
135+
delete OffClickFilterCache[filter];
136+
}
137+
});
138+
});
139+
};
140+
}
141+
};
142+
}]);
143+
angular.module('offClick').factory('OffClickFilterCache', function () {
144+
var filterCache = {};
145+
return filterCache;
146+
});

dist/angular-off-click.min.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gulpfile.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
var gulp = require('gulp');
2+
var tasks = ['concat','uglify','ng-annotate','babel','rename'];
3+
tasks.forEach((task) => {
4+
gulp[task] = require(`gulp-${ task }`);
5+
});
6+
7+
gulp.task('build',() => {
8+
gulp.src(['src/*.module.js','src/**/*.js'])
9+
.pipe(gulp.concat('angular-off-click.js'))
10+
.pipe(gulp.babel({
11+
presets: ['es2015']
12+
}))
13+
.pipe(gulp['ng-annotate']())
14+
.pipe(gulp.dest('dist'))
15+
.pipe(gulp.rename('angular-off-click.min.js'))
16+
.pipe(gulp.uglify())
17+
.pipe(gulp.dest('dist'));
18+
});

package.json

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,41 @@
11
{
22
"name": "angular-off-click",
3-
"version": "0.0.10",
3+
"version": "1.0.0",
44
"repository": {
55
"type": "git"
66
},
7-
"main": "./offClick",
7+
"main": [
8+
"./dist/angular-off-click.js"
9+
],
10+
"scripts": {
11+
"build": "gulp build"
12+
},
13+
"keywords": [
14+
"angular",
15+
"off",
16+
"click"
17+
],
18+
"author": {
19+
"name": "Evan Sharp",
20+
"url": "https://github.com/TheSharpieOne"
21+
},
22+
"contributors": [
23+
{
24+
"name": "Ricardo Marques",
25+
"url": "https://github.com/RicardoAlmeidaMarques"
26+
}
27+
],
28+
"license": "MIT",
829
"dependencies": {
930
"angular": "*"
31+
},
32+
"devDependencies": {
33+
"babel-preset-es2015": "^6.9.0",
34+
"gulp": "^3.9.1",
35+
"gulp-babel": "^6.1.2",
36+
"gulp-concat": "^2.6.0",
37+
"gulp-ng-annotate": "^2.0.0",
38+
"gulp-rename": "^1.2.2",
39+
"gulp-uglify": "^1.5.3"
1040
}
1141
}

src/angular-off-click.module.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
angular.module('offClick',[]);

0 commit comments

Comments
 (0)