7a2f062d6571f02661dd154d7c53179545ccddf9
[nit.git] / share / nitweb / javascripts / ui.js
1 /*
2 * Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 (function() {
18 angular
19 .module('ui', [])
20
21 .controller('SearchCtrl', function(Model, $scope, $location, $document) {
22
23 $scope.query = '';
24
25 $scope.reset = function() {
26 $scope.activeItem = 0;
27 $scope.results = [];
28 }
29
30 $scope.update = function(e) {
31 if(e.keyCode == 38) {
32 $scope.selectUp();
33 } else if(e.keyCode == 40) {
34 $scope.selectDown();
35 } else if(e.keyCode == 27) {
36 $scope.selectEscape();
37 } else if(e.keyCode == 13) {
38 $scope.selectEnter();
39 }
40 }
41
42 $scope.selectUp = function() {
43 if($scope.activeItem > 0) {
44 $scope.activeItem -= 1;
45 }
46 }
47
48 $scope.selectDown = function() {
49 if($scope.activeItem < $scope.results.length - 1) {
50 $scope.activeItem += 1;
51 }
52 }
53
54 $scope.selectEnter = function(e) {
55 $location.url($scope.results[$scope.activeItem].web_url);
56 $scope.reset();
57 }
58
59 $scope.selectEscape = function() {
60 $scope.reset();
61 }
62
63 $scope.setActive = function(index) {
64 $scope.activeItem = index;
65 }
66
67 $scope.search = function() {
68 if(!$scope.query) {
69 $scope.reset();
70 return;
71 }
72 Model.search($scope.query, 10,
73 function(data) {
74 $scope.reset();
75 $scope.results = data;
76 }, function(err) {
77 $scope.reset();
78 $scope.error = err;
79 });
80 }
81
82 $scope.reset();
83 })
84
85 .directive('searchField', function($document) {
86 return {
87 restrict: 'E',
88 replace: true,
89 controller: 'SearchCtrl',
90 controllerAs: 'searchCtrl',
91 templateUrl: '/directives/search/field.html',
92 link: function ($scope, element, attrs) {
93 $document.bind('click', function (event) {
94 var isChild = $(element).has(event.target).length > 0;
95 var isSelf = element[0] == event.target;
96 var isInside = isChild || isSelf;
97 if (!isInside) {
98 $scope.reset();
99 $scope.$apply();
100 }
101 });
102 }
103 };
104 })
105
106 .directive('searchCard', function() {
107 return {
108 restrict: 'E',
109 scope: {
110 mentity: '='
111 },
112 replace: true,
113 templateUrl: '/directives/search/card.html'
114 };
115 })
116
117 .directive('uiFilters', function() {
118 return {
119 restrict: 'E',
120 scope: {
121 property: '=',
122 classesOn: '=',
123 classesOff: '='
124 },
125 replace: true,
126 templateUrl: '/directives/ui-filter-button-vis.html',
127 link: function ($scope, element, attrs) {
128 $scope.toggle = function() {
129 $scope.property = !$scope.property;
130 }
131 }
132 };
133 })
134
135 .filter('visibility', function() {
136 return function(input, visibilityFilter) {
137 var res = [];
138 input.forEach(function(entry) {
139 if(visibilityFilter.public == false && entry.visibility == "public") {
140 return;
141 }
142 if(visibilityFilter.protected == false && entry.visibility == "protected") {
143 return;
144 }
145 if(visibilityFilter.private == false && entry.visibility == "private") {
146 return;
147 }
148 res.push(entry);
149 });
150 return res;
151 };
152 })
153
154 .directive('uiSummary', function($rootScope, $location, $anchorScroll) {
155 return {
156 restrict: 'E',
157 scope: {
158 target: '@'
159 },
160 replace: true,
161 templateUrl: '/directives/ui-summary.html',
162 link: function ($scope, element, attrs) {
163 $scope.goTo = function(entity) {
164 $location.hash(entity.id);
165 }
166
167 $scope.textToId = function(text) {
168 return text.replace(/ /g, '-').replace(/[^A-Za-z_-]/g, '');
169 }
170
171 $rootScope.reloadSummary = function() {
172 var h = angular.element(document.querySelectorAll(
173 $scope.target + ' h1, ' +
174 $scope.target + ' h2, ' +
175 $scope.target + ' h3, ' +
176 $scope.target + ' h4, ' +
177 $scope.target + ' h5, ' +
178 $scope.target + ' h6 '));
179
180 $scope.headings = [];
181 angular.forEach(h, function(heading) {
182 var head = angular.element(heading);
183 if(!head.is(':visible')) { return ; }
184 var text = head.text().trim();
185 var id = $scope.textToId(text);
186 if(!head.attr('id')) {
187 head.attr('id', id);
188 } else {
189 id = head.attr('id');
190 }
191 $scope.headings.push({
192 id: id,
193 text: text,
194 level: parseInt(head[0].nodeName[1])
195 });
196 });
197 $anchorScroll();
198 }
199
200 $scope.$watch('target', function() {
201 setTimeout(function() {
202 $rootScope.reloadSummary();
203 }, 100);
204 });
205 }
206 };
207 })
208
209 .directive('uiFilterForm', function() {
210 return {
211 restrict: 'E',
212 scope: {
213 searchFilter: '=',
214 visibilityFilter: '='
215 },
216 replace: true,
217 templateUrl: '/directives/ui-filter-form.html'
218 };
219 })
220
221 .directive('uiFilterField', function() {
222 return {
223 restrict: 'E',
224 scope: {
225 property: '='
226 },
227 replace: true,
228 templateUrl: '/directives/ui-filter-field.html'
229 };
230 })
231
232 .directive('uiFilterGroupVis', function() {
233 return {
234 restrict: 'E',
235 scope: {
236 property: '='
237 },
238 replace: true,
239 templateUrl: '/directives/ui-filter-group-vis.html'
240 };
241 })
242
243 .directive('uiFilterButtonVis', function() {
244 return {
245 restrict: 'E',
246 scope: {
247 property: '=',
248 classesOn: '=',
249 classesOff: '='
250 },
251 replace: true,
252 templateUrl: '/directives/ui-filter-button-vis.html',
253 link: function ($scope, element, attrs) {
254 $scope.toggle = function() {
255 $scope.property = !$scope.property;
256 }
257 }
258 };
259 })
260
261 /* Pagination */
262
263 .directive('uiPagination', function() {
264 return {
265 restrict: 'E',
266 replace: true,
267 bindToController: {
268 pagination: '=',
269 suffix: '=?'
270 },
271 controller: function($scope) {
272 var vm = this;
273
274 $scope.$watch('pagination.pagination', function(pagination) {
275 if(!pagination) return;
276 vm.computePages(pagination);
277 })
278
279 vm.computePages = function(pagination) {
280 vm.pages = [];
281 var len = 11;
282 var page = pagination.page;
283 var start = page - Math.floor(len / 2);
284 var end = page + Math.floor(len / 2);
285
286 if(start < 1) {
287 end = Math.min(pagination.max, end + Math.abs(start) + 1)
288 start = 1
289 } else if(end > pagination.max) {
290 start = Math.max(1, start - Math.abs(end - pagination.max))
291 end = pagination.max;
292 }
293
294 for(var i = start; i <= end; i++) {
295 vm.pages.push(i);
296 }
297 }
298
299 vm.changePage = function(page, limit) {
300 if(page <= 0 || page > vm.pagination.max) return;
301 var suffix = vm.suffix ? vm.suffix : '';
302 $scope.$emit('change-page' + suffix, page, limit);
303 }
304 },
305 controllerAs: 'pagination',
306 templateUrl: 'directives/ui/pagination.html'
307 };
308 })
309 })();