Merge: doc: fixed some typos and other misc. corrections
[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 /* Search */
22
23 .config(function($stateProvider, $locationProvider) {
24 $stateProvider
25 .state('search', {
26 url: '/search?q&p&n',
27 controller: 'SearchCtrl',
28 controllerAs: 'vm',
29 templateUrl: 'views/search.html',
30 resolve: {
31 entities: function(Model, $q, $stateParams, $state) {
32 var d = $q.defer();
33 var query = $stateParams.q;
34 var page = $stateParams.p ? $stateParams.p : 1;
35 var limit = $stateParams.n ? $stateParams.n : 10;
36 Model.search(query, page, limit, d.resolve,
37 function() {
38 $state.go('404', null, { location: false })
39 });
40 return d.promise;
41 }
42 }
43 })
44 })
45
46 .controller('SearchCtrl', function($scope, $state, $stateParams, entities) {
47 var vm = this;
48 vm.entities = entities;
49 vm.query = $stateParams.q;
50
51 $scope.$on('change-page', function(e, page, limit) {
52 $state.go('search', {q: vm.query, p: page, l: limit});
53 })
54 })
55
56 .directive('uiSearchField', function($document) {
57 return {
58 restrict: 'E',
59 replace: true,
60 controller: function($scope, $state, $stateParams, $location, Model) {
61 var vm = this;
62 vm.search = function() {
63 if(!vm.query) {
64 vm.reset();
65 return;
66 }
67 Model.search(vm.query, 1, 8,
68 function(data) {
69 vm.reset();
70 vm.results = data;
71 }, function(err) {
72 vm.reset();
73 vm.error = err;
74 });
75 }
76
77 vm.reset = function() {
78 vm.activeItem = -1;
79 vm.results = {
80 results: []
81 };
82 }
83
84 vm.update = function(e) {
85 if(e.keyCode == 38) {
86 vm.selectUp();
87 } else if(e.keyCode == 40) {
88 vm.selectDown();
89 } else if(e.keyCode == 27) {
90 vm.selectEscape();
91 } else if(e.keyCode == 13) {
92 vm.selectEnter();
93 }
94 }
95
96 vm.selectUp = function() {
97 if(vm.activeItem >= 0) {
98 vm.activeItem -= 1;
99 }
100 }
101
102 vm.selectDown = function() {
103 if(vm.activeItem < vm.results.results.length) {
104 vm.activeItem += 1;
105 }
106 }
107
108 vm.selectEnter = function(e) {
109 if(vm.activeItem >= 0 && vm.activeItem < vm.results.results.length) {
110 $location.url(vm.results.results[vm.activeItem].web_url);
111 } else {
112 $state.go('search', {q: vm.query, p: 1});
113 }
114 vm.reset();
115 }
116
117 vm.selectEscape = function() {
118 vm.reset();
119 }
120
121 vm.setActive = function(index) {
122 vm.activeItem = index;
123 }
124
125 vm.reset();
126
127 $scope.$watch(function() {
128 return $stateParams.q;
129 }, function(q) {
130 if(q) vm.query = q;
131 });
132 },
133 controllerAs: 'vm',
134 templateUrl: 'directives/ui/search-field.html',
135 link: function ($scope, element, attrs, ctrl) {
136 $document.bind('click', function (event) {
137 var isChild = $(element).has(event.target).length > 0;
138 var isSelf = element[0] == event.target;
139 var isInside = isChild || isSelf;
140 if (!isInside) {
141 ctrl.reset();
142 $scope.$apply();
143 }
144 });
145 }
146 };
147 })
148
149 /* Filters */
150
151 .directive('uiFilters', function() {
152 return {
153 restrict: 'E',
154 scope: {
155 property: '=',
156 classesOn: '=',
157 classesOff: '='
158 },
159 replace: true,
160 templateUrl: '/directives/ui-filter-button-vis.html',
161 link: function ($scope, element, attrs) {
162 $scope.toggle = function() {
163 $scope.property = !$scope.property;
164 }
165 }
166 };
167 })
168
169 .filter('visibility', function() {
170 return function(input, visibilityFilter) {
171 var res = [];
172 input.forEach(function(entry) {
173 if(visibilityFilter.public == false && entry.visibility == "public") {
174 return;
175 }
176 if(visibilityFilter.protected == false && entry.visibility == "protected") {
177 return;
178 }
179 if(visibilityFilter.private == false && entry.visibility == "private") {
180 return;
181 }
182 res.push(entry);
183 });
184 return res;
185 };
186 })
187
188 .directive('uiSummary', function($rootScope, $location, $anchorScroll) {
189 return {
190 restrict: 'E',
191 scope: {
192 target: '@'
193 },
194 replace: true,
195 templateUrl: '/directives/ui-summary.html',
196 link: function ($scope, element, attrs) {
197 $scope.goTo = function(entity) {
198 $location.hash(entity.id);
199 }
200
201 $scope.textToId = function(text) {
202 return text.replace(/ /g, '-').replace(/[^A-Za-z_-]/g, '');
203 }
204
205 $rootScope.reloadSummary = function() {
206 var h = angular.element(document.querySelectorAll(
207 $scope.target + ' h1, ' +
208 $scope.target + ' h2, ' +
209 $scope.target + ' h3, ' +
210 $scope.target + ' h4, ' +
211 $scope.target + ' h5, ' +
212 $scope.target + ' h6 '));
213
214 $scope.headings = [];
215 angular.forEach(h, function(heading) {
216 var head = angular.element(heading);
217 if(!head.is(':visible')) { return ; }
218 var text = head.text().trim();
219 var id = $scope.textToId(text);
220 if(!head.attr('id')) {
221 head.attr('id', id);
222 } else {
223 id = head.attr('id');
224 }
225 $scope.headings.push({
226 id: id,
227 text: text,
228 level: parseInt(head[0].nodeName[1])
229 });
230 });
231 $anchorScroll();
232 }
233
234 $scope.$watch('target', function() {
235 setTimeout(function() {
236 $rootScope.reloadSummary();
237 }, 100);
238 });
239 }
240 };
241 })
242
243 .directive('uiFilterForm', function() {
244 return {
245 restrict: 'E',
246 scope: {
247 searchFilter: '=',
248 visibilityFilter: '='
249 },
250 replace: true,
251 templateUrl: '/directives/ui-filter-form.html'
252 };
253 })
254
255 .directive('uiFilterField', function() {
256 return {
257 restrict: 'E',
258 scope: {
259 property: '='
260 },
261 replace: true,
262 templateUrl: '/directives/ui-filter-field.html'
263 };
264 })
265
266 .directive('uiFilterGroupVis', function() {
267 return {
268 restrict: 'E',
269 scope: {
270 property: '='
271 },
272 replace: true,
273 templateUrl: '/directives/ui-filter-group-vis.html'
274 };
275 })
276
277 .directive('uiFilterButtonVis', function() {
278 return {
279 restrict: 'E',
280 scope: {
281 property: '=',
282 classesOn: '=',
283 classesOff: '='
284 },
285 replace: true,
286 templateUrl: '/directives/ui-filter-button-vis.html',
287 link: function ($scope, element, attrs) {
288 $scope.toggle = function() {
289 $scope.property = !$scope.property;
290 }
291 }
292 };
293 })
294
295 /* Pagination */
296
297 .directive('uiPagination', function() {
298 return {
299 restrict: 'E',
300 replace: true,
301 scope: {},
302 bindToController: {
303 pagination: '=',
304 suffix: '=?'
305 },
306 controller: function($scope) {
307 var vm = this;
308
309 $scope.$watch('vm.pagination', function(pagination) {
310 if(!pagination) return;
311 vm.computePages(pagination);
312 })
313
314 vm.computePages = function(pagination) {
315 vm.pages = [];
316 var len = 11;
317 var page = pagination.page;
318 var start = page - Math.floor(len / 2);
319 var end = page + Math.floor(len / 2);
320
321 if(start < 1) {
322 end = Math.min(pagination.max, end + Math.abs(start) + 1)
323 start = 1
324 } else if(end > pagination.max) {
325 start = Math.max(1, start - Math.abs(end - pagination.max))
326 end = pagination.max;
327 }
328
329 for(var i = start; i <= end; i++) {
330 vm.pages.push(i);
331 }
332 }
333
334 vm.changePage = function(page, limit) {
335 if(page <= 0 || page > vm.pagination.max) return;
336 var suffix = vm.suffix ? vm.suffix : '';
337 $scope.$emit('change-page' + suffix, page, limit);
338 }
339 },
340 controllerAs: 'vm',
341 templateUrl: 'directives/ui/pagination.html'
342 };
343 })
344 })();