-if (angular.module("avRegistration", [ "ui.bootstrap", "ui.utils", "ui.router" ]),
-angular.module("avRegistration").config(function() {}), angular.module("avRegistration").factory("Authmethod", [ "$http", "$cookies", "ConfigService", "$interval", "$location", function($http, $cookies, ConfigService, $interval, $location) {
- var backendUrl = ConfigService.authAPI, authId = ConfigService.freeAuthId, authmethod = {
- captcha_code: null,
- captcha_image_url: "",
- captcha_status: "",
- admin: !1,
- getAuthevent: function() {
- var adminId = ConfigService.freeAuthId + "", electionsMatch = $location.path(), authevent = "", adminMatch = electionsMatch.match(/^\/admin\//), boothMatch = electionsMatch.match(/^\/booth\/([0-9]+)\//), electionsMatch = electionsMatch.match(/^\/(elections|election)\/([0-9]+)\//);
- return _.isArray(adminMatch) ? authevent = adminId : _.isArray(boothMatch) && 2 === boothMatch.length ? authevent = boothMatch[1] : _.isArray(electionsMatch) && 3 === electionsMatch.length && (authevent = electionsMatch[2]),
- authevent;
- },
- isAdmin: function() {
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+angular.module('avRegistration', ['ui.bootstrap','ui.utils','ui.router']);
+
+angular.module('avRegistration').config(function() {
+ /* Add New States Above */
+});
+/**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+angular.module('avRegistration')
+
+ .factory('Authmethod', function($http, $cookies, ConfigService, $interval, $location) {
+ var backendUrl = ConfigService.authAPI;
+ var authId = ConfigService.freeAuthId;
+ var authmethod = {};
+ authmethod.captcha_code = null;
+ authmethod.captcha_image_url = "";
+ authmethod.captcha_status = "";
+ authmethod.admin = false;
+
+ authmethod.getAuthevent = function() {
+ var adminId = ConfigService.freeAuthId + '';
+ var href = $location.path();
+ var authevent = '';
+
+ var adminMatch = href.match(/^\/admin\//);
+ var boothMatch = href.match(/^\/booth\/([0-9]+)\//);
+ var electionsMatch = href.match(/^\/(elections|election)\/([0-9]+)\//);
+
+ if (_.isArray(adminMatch)) {
+ authevent = adminId;
+ } else if(_.isArray(boothMatch) && 2 === boothMatch.length) {
+ authevent = boothMatch[1];
+ } else if(_.isArray(electionsMatch) && 3 === electionsMatch.length) {
+ authevent = electionsMatch[2];
+ }
+ return authevent;
+ };
+
+ authmethod.isAdmin = function() {
return authmethod.isLoggedIn() && authmethod.admin;
- },
- isLoggedIn: function() {
+ };
+
+ authmethod.isLoggedIn = function() {
var auth = $http.defaults.headers.common.Authorization;
- return auth && 0 < auth.length;
- },
- signup: function(data, eid) {
- eid = eid || authId;
- return $http.post(backendUrl + "auth-event/" + eid + "/register/", data);
- },
- getUserInfoExtra: function() {
- if (authmethod.isLoggedIn()) return $http.get(backendUrl + "user/extra/", {});
- var data = {
- then: function(onSuccess, onError) {
- return setTimeout(function() {
- onError({
- data: {
- message: "not-logged-in"
- }
- });
- }, 0), data;
+ return auth && auth.length > 0;
+ };
+
+ authmethod.signup = function(data, authevent) {
+ var eid = authevent || authId;
+ return $http.post(backendUrl + 'auth-event/'+eid+'/register/', data);
+ };
+
+ authmethod.getUserInfoExtra = function() {
+ if (!authmethod.isLoggedIn()) {
+ var data = {
+ then: function (onSuccess, onError) {
+ setTimeout(function() {
+ onError({data: {message:"not-logged-in"}});
+ }, 0);
+ return data;
}
- };
- return data;
- },
- getActivity: function(url, page, size, filterOptions, filterStr, receiver_id) {
- var params = {}, url = backendUrl + "auth-event/" + url + "/activity/";
- return "max" === size ? params.size = 500 : angular.isNumber(size) && 0 < size && size < 500 ? params.size = parseInt(size) : params.size = 10,
- angular.isNumber(page) ? params.page = parseInt(page) : params.page = 1, angular.isNumber(receiver_id) && (params.receiver_id = receiver_id),
- _.extend(params, filterOptions), filterStr && 0 < filterStr.length && (params.filter = filterStr),
- $http.get(url, {
- params: params
- });
- },
- getBallotBoxes: function(url, page, size, filterOptions, filterStr) {
- var params = {}, url = backendUrl + "auth-event/" + url + "/ballot-box/";
- return "max" === size ? params.size = 500 : angular.isNumber(size) && 0 < size && size < 500 ? params.size = parseInt(size) : params.size = 10,
- angular.isNumber(page) ? params.page = parseInt(page) : params.page = 1, _.extend(params, filterOptions),
- filterStr && 0 < filterStr.length && (params.filter = filterStr), $http.get(url, {
- params: params
- });
- },
- createBallotBox: function(url, params) {
- params = {
- name: params
- }, url = backendUrl + "auth-event/" + url + "/ballot-box/";
+ };
+ return data;
+ }
+ return $http.get(backendUrl + 'user/extra/', {});
+ };
+
+ /**
+ * @returns an activity page
+ */
+ authmethod.getActivity = function(eid, page, size, filterOptions, filterStr, receiver_id)
+ {
+ var params = {};
+ var url = backendUrl + 'auth-event/' + eid + '/activity/';
+
+ // 1. initialize GET params
+
+ if (size === 'max') {
+ params.size = 500;
+ } else if (angular.isNumber(size) && size > 0 && size < 500) {
+ params.size = parseInt(size);
+ } else {
+ params.size = 10;
+ }
+
+ if (!angular.isNumber(page)) {
+ params.page = 1;
+ } else {
+ params.page = parseInt(page);
+ }
+
+
+ if (angular.isNumber(receiver_id)) {
+ params.receiver_id = receiver_id;
+ }
+
+ _.extend(params, filterOptions);
+ if (filterStr && filterStr.length > 0) {
+ params.filter = filterStr;
+ }
+
+ // 2. generate request
+ return $http.get(url, {params: params});
+ };
+
+ /**
+ * @returns a page of ballot boxes
+ */
+ authmethod.getBallotBoxes = function(eid, page, size, filterOptions, filterStr)
+ {
+ var params = {};
+ var url = backendUrl + 'auth-event/' + eid + '/ballot-box/';
+
+ // 1. initialize GET params
+
+ if (size === 'max') {
+ params.size = 500;
+ } else if (angular.isNumber(size) && size > 0 && size < 500) {
+ params.size = parseInt(size);
+ } else {
+ params.size = 10;
+ }
+
+ if (!angular.isNumber(page)) {
+ params.page = 1;
+ } else {
+ params.page = parseInt(page);
+ }
+
+ _.extend(params, filterOptions);
+ if (filterStr && filterStr.length > 0) {
+ params.filter = filterStr;
+ }
+
+ // 2. generate request
+ return $http.get(url, {params: params});
+ };
+
+ /**
+ * @returns the http request
+ */
+ authmethod.createBallotBox = function(eid, name)
+ {
+ var params = {name: name};
+ var url = backendUrl + 'auth-event/' + eid + '/ballot-box/';
+
return $http.post(url, params);
- },
- postTallySheet: function(eid, url, data) {
- url = backendUrl + "auth-event/" + eid + "/ballot-box/" + url + "/tally-sheet/";
+ };
+
+ /**
+ * @returns the http request
+ */
+ authmethod.obtainVoterAuthCode = function (electionId, username)
+ {
+ var params = {username: username};
+ var url = backendUrl + 'auth-event/' + electionId + '/generate-auth-code/';
+
+ return $http.post(url, params);
+ };
+
+ /**
+ * @returns the http request
+ */
+ authmethod.postTallySheet = function(eid, ballot_box_id, data)
+ {
+ var url = backendUrl + 'auth-event/' + eid + '/ballot-box/' + ballot_box_id + '/tally-sheet/';
+
return $http.post(url, data);
- },
- voteStats: function(url) {
- url = backendUrl + "auth-event/" + url + "/vote-stats/";
+ };
+
+ /**
+ * @returns the http request
+ */
+ authmethod.voteStats = function(eid)
+ {
+ var url = backendUrl + 'auth-event/' + eid + '/vote-stats/';
+
return $http.get(url);
- },
- getTallySheet: function(eid, ballot_box_id, tally_sheet_id) {
- var url = null, url = tally_sheet_id ? backendUrl + "auth-event/" + eid + "/ballot-box/" + ballot_box_id + "/tally-sheet/" + tally_sheet_id + "/" : backendUrl + "auth-event/" + eid + "/ballot-box/" + ballot_box_id + "/tally-sheet/";
+ };
+
+
+ /**
+ * @returns the http request
+ */
+ authmethod.getTallySheet = function(eid, ballot_box_id, tally_sheet_id)
+ {
+ var url = null;
+ if (!tally_sheet_id) {
+ url = backendUrl + 'auth-event/' + eid + '/ballot-box/' + ballot_box_id + '/tally-sheet/';
+ } else {
+ url = backendUrl + 'auth-event/' + eid + '/ballot-box/' + ballot_box_id + '/tally-sheet/' + tally_sheet_id + '/';
+ }
+
return $http.get(url);
- },
- deleteTallySheet: function(eid, ballot_box_id, url) {
- url = backendUrl + "auth-event/" + eid + "/ballot-box/" + ballot_box_id + "/tally-sheet/" + url + "/";
+ };
+
+ /**
+ * @returns the http request
+ */
+ authmethod.deleteTallySheet = function(eid, ballot_box_id, tally_sheet_id)
+ {
+ var url = backendUrl + 'auth-event/' + eid + '/ballot-box/' + ballot_box_id + '/tally-sheet/' + tally_sheet_id + "/";
+
return $http.delete(url, {});
- },
- deleteBallotBox: function(eid, url) {
- url = backendUrl + "auth-event/" + eid + "/ballot-box/" + url + "/delete/";
+ };
+
+ /**
+ * @returns the http request
+ */
+ authmethod.deleteBallotBox = function(eid, ballot_box_id)
+ {
+ var url = backendUrl + 'auth-event/' + eid + '/ballot-box/' + ballot_box_id + "/delete/";
+
return $http.delete(url, {});
- },
- updateUserExtra: function(extra) {
- if (authmethod.isLoggedIn()) return $http.post(backendUrl + "user/extra/", extra);
- var data = {
- then: function(onSuccess, onError) {
- return setTimeout(function() {
- onError({
- data: {
- message: "not-logged-in"
- }
- });
- }, 0), data;
+ };
+
+ authmethod.updateUserExtra = function (extra) {
+ if (!authmethod.isLoggedIn()) {
+ var data = {
+ then: function (onSuccess, onError) {
+ setTimeout(function() {
+ onError({data: {message:"not-logged-in"}});
+ }, 0);
+ return data;
}
- };
- return data;
- },
- getUserInfo: function(userid) {
- if (authmethod.isLoggedIn()) return void 0 === userid ? $http.get(backendUrl + "user/", {}) : $http.get(backendUrl + "user/%d" % userid, {});
- var data = {
- then: function(onSuccess, onError) {
- return setTimeout(function() {
- onError({
- data: {
- message: "not-logged-in"
- }
- });
- }, 0), data;
+ };
+ return data;
+ }
+ return $http.post(backendUrl + 'user/extra/', extra);
+ };
+
+ authmethod.getUserInfo = function(userid) {
+ if (!authmethod.isLoggedIn()) {
+ var data = {
+ then: function (onSuccess, onError) {
+ setTimeout(function() {
+ onError({data: {message:"not-logged-in"}});
+ }, 0);
+ return data;
}
- };
- return data;
- },
- ping: function() {
- if (authmethod.isLoggedIn()) return $http.get(backendUrl + "auth-event/" + authId + "/ping/");
- var data = {
- then: function(onSuccess, onError) {
- return setTimeout(function() {
- onError({
- data: {
- message: "not-logged-in"
- }
- });
- }, 0), data;
+ };
+ return data;
+ }
+ if (typeof userid === 'undefined') {
+ return $http.get(backendUrl + 'user/', {});
+ } else {
+ return $http.get(backendUrl + 'user/%d' % userid, {});
+ }
+ };
+
+ authmethod.ping = function() {
+ if (!authmethod.isLoggedIn()) {
+ var data = {
+ then: function (onSuccess, onError) {
+ setTimeout(function() {
+ onError({data: {message:"not-logged-in"}});
+ }, 0);
+ return data;
}
- };
- return data;
- },
- getImage: function(ev, uid) {
- return $http.get(backendUrl + "auth-event/" + ev + "/census/img/" + uid + "/");
- },
- login: function(data, eid) {
- eid = eid || authId;
- return delete data.authevent, $http.post(backendUrl + "auth-event/" + eid + "/authenticate/", data);
- },
- censusQuery: function(data, eid) {
- eid = eid || authId;
- return delete data.authevent, $http.post(backendUrl + "auth-event/" + eid + "/census/public-query/", data);
- },
- resendAuthCode: function(data, eid) {
- return $http.post(backendUrl + "auth-event/" + eid + "/resend_auth_code/", data);
- },
- editChildrenParent: function(data, eid) {
- return $http.post(backendUrl + "auth-event/" + eid + "/edit-children-parent/", data);
- },
- getPerm: function(perm, object_type, data) {
- data = {
+ };
+ return data;
+ }
+ return $http.get(backendUrl + 'auth-event/'+authId+'/ping/');
+ };
+
+ authmethod.getImage = function(ev, uid) {
+ return $http.get(backendUrl + 'auth-event/'+ev+'/census/img/'+uid+'/');
+ };
+
+ authmethod.login = function(data, authevent) {
+ var eid = authevent || authId;
+ delete data['authevent'];
+ return $http.post(backendUrl + 'auth-event/'+eid+'/authenticate/', data);
+ };
+
+ authmethod.censusQuery = function(data, authevent) {
+ var eid = authevent || authId;
+ delete data['authevent'];
+ return $http.post(backendUrl + 'auth-event/'+eid+'/census/public-query/', data);
+ };
+
+ authmethod.resendAuthCode = function(data, eid) {
+ return $http.post(backendUrl + 'auth-event/'+eid+'/resend_auth_code/', data);
+ };
+
+ authmethod.editChildrenParent = function(data, eid) {
+ return $http.post(backendUrl + 'auth-event/'+eid+'/edit-children-parent/', data);
+ };
+
+ authmethod.getPerm = function(perm, object_type, object_id) {
+ var data = {
permission: perm,
object_type: object_type,
- object_id: null === data ? data : data + ""
+ object_id: (object_id === null) ? object_id : object_id + "" // to convert to string
};
- return $http.post(backendUrl + "get-perms/", data);
- },
- viewEvent: function(id) {
- return $http.get(backendUrl + "auth-event/" + id + "/");
- },
- viewEvents: function() {
- return $http.get(backendUrl + "auth-event/");
- },
- createEvent: function(data) {
- return $http.post(backendUrl + "auth-event/", data);
- },
- editEvent: function(id, data) {
- return $http.post(backendUrl + "auth-event/" + id + "/", data);
- },
- addCensus: function(id, d, validation) {
- d = {
- "field-validation": validation = !angular.isDefined(validation) ? "enabled" : validation,
- census: d
+ return $http.post(backendUrl + 'get-perms/', data);
+ };
+
+ authmethod.viewEvent = function(id) {
+ return $http.get(backendUrl + 'auth-event/' + id + '/');
+ };
+
+ authmethod.viewEvents = function() {
+ return $http.get(backendUrl + 'auth-event/');
+ };
+
+ authmethod.createEvent = function(data) {
+ return $http.post(backendUrl + 'auth-event/', data);
+ };
+
+ authmethod.editEvent = function(id, data) {
+ return $http.post(backendUrl + 'auth-event/' + id +'/', data);
+ };
+
+ authmethod.addCensus = function(id, data, validation) {
+ if (!angular.isDefined(validation)) {
+ validation = "enabled";
+ }
+ var d = {
+ "field-validation": validation,
+ "census": data
};
- return $http.post(backendUrl + "auth-event/" + id + "/census/", d);
- },
- getCensus: function(id, params) {
- return angular.isObject(params) ? $http.get(backendUrl + "auth-event/" + id + "/census/", {
- params: params
- }) : $http.get(backendUrl + "auth-event/" + id + "/census/");
- },
- getRegisterFields: function(viewEventData) {
- for (var fields = (fields = _.filter(angular.copy(viewEventData.extra_fields), function(item) {
- return !0 !== item.required_when_registered;
- })) || [], i = 0; i < fields.length; i++) if ("captcha" === fields[i].type) {
- var captcha = fields.splice(i, 1);
- fields.push(captcha[0]);
- break;
+ return $http.post(backendUrl + 'auth-event/' + id + '/census/', d);
+ };
+
+ authmethod.getCensus = function(id, params) {
+ if (!angular.isObject(params)) {
+ return $http.get(backendUrl + 'auth-event/' + id + '/census/');
+ }
+
+ return $http.get(
+ backendUrl + 'auth-event/' + id + '/census/',
+ {params:params});
+ };
+
+ authmethod.getRegisterFields = function (viewEventData) {
+ var fields = _.filter(
+ angular.copy(viewEventData.extra_fields),
+ function (item) {
+ if (true === item.required_when_registered) {
+ return false;
+ }
+ return true;
+ });
+
+ if (!fields) { fields = []; }
+
+ // put captcha the last
+ for (var i = 0; i < fields.length; i++) {
+ if (fields[i]['type'] === "captcha") {
+ var captcha = fields.splice(i, 1);
+ fields.push(captcha[0]);
+ break;
}
+ }
+ return fields;
+ };
+
+ authmethod.getCensusQueryFields = function (viewEventData)
+ {
+ var fields = angular.copy(viewEventData.extra_fields);
+
+ fields = _.filter(
+ fields,
+ function (field) {
+ return field.required_on_authentication;
+ }
+ );
+
return fields;
- },
- getCensusQueryFields: function(fields) {
- fields = angular.copy(fields.extra_fields);
- return fields = _.filter(fields, function(field) {
- return field.required_on_authentication;
- });
- },
- getLoginFields: function(viewEventData) {
- var fields = authmethod.getRegisterFields(viewEventData);
- _.contains([ "sms", "email" ], viewEventData.auth_method) ? fields.push({
- name: "code",
- type: "code",
- required: !0,
- required_on_authentication: !0
- }) : _.contains([ "sms-otp", "email-otp" ], viewEventData.auth_method) && fields.push({
- name: "code",
- type: "code",
- required: !0,
- steps: [ 1 ],
- required_on_authentication: !0
- }), fields = _.filter(fields, function(field) {
- return field.required_on_authentication;
- });
- for (var i = 0; i < fields.length; i++) if ("captcha" === fields[i].type) {
- var captcha = fields.splice(i, 1);
- fields.push(captcha[0]);
- break;
+ };
+
+ authmethod.getLoginWithCode = function (_viewEventData) {
+ return [
+ {
+ "name": "__username",
+ "type": "text",
+ "required": true,
+ "min": 3,
+ "max": 200,
+ "required_on_authentication": true
+ },
+ {
+ "name": "code",
+ "type": "code",
+ "required": true,
+ "required_on_authentication": true
+ }
+ ];
+ };
+
+ authmethod.getLoginFields = function (viewEventData) {
+ var fields = authmethod.getRegisterFields(
+ viewEventData
+ );
+ if (_.contains(["sms", "email"], viewEventData.auth_method))
+ {
+ fields.push({
+ "name": "code",
+ "type": "code",
+ "required": true,
+ "required_on_authentication": true
+ });
+ } else if (_.contains(["sms-otp", "email-otp"], viewEventData.auth_method))
+ {
+ fields.push({
+ "name": "code",
+ "type": "code",
+ "required": true,
+ "steps": [1],
+ "required_on_authentication": true
+ });
+ }
+
+ fields = _.filter(fields, function (field) {return field.required_on_authentication;});
+
+ // put captha the last
+ for (var i=0; i
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+angular.module('avRegistration')
+ .controller(
+ 'LoginController',
+ function(
+ $scope,
+ $stateParams
+ ) {
+ $scope.event_id = $stateParams.id;
+ $scope.code = $stateParams.code;
+ $scope.email = $stateParams.email;
+ $scope.username = $stateParams.username;
+ $scope.isOpenId = $stateParams.isOpenId;
+ $scope.withCode = $stateParams.withCode;
+ }
+ );
+
+/**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+angular.module('avRegistration')
+ .directive(
+ 'avLogin',
+ function(
+ Authmethod,
+ StateDataService,
+ $state,
+ $location,
+ $cookies,
+ $i18next,
+ $window,
+ $timeout,
+ ConfigService,
+ Patterns)
+ {
+ // we use it as something similar to a controller here
+ function link(scope, element, attrs)
+ {
+ scope.isCensusQuery = attrs.isCensusQuery;
+ scope.withCode = attrs.withCode;
+ scope.username = attrs.username;
+ scope.error = null;
+
+ // by default
+ scope.hide_default_login_lookup_field = false;
+ var adminId = ConfigService.freeAuthId + '';
+ var autheventid = scope.eventId = attrs.eventId;
+ scope.orgName = ConfigService.organization.orgName;
+ scope.openIDConnectProviders = ConfigService.openIDConnectProviders;
+
+ // redirect from admin login to admin elections if login is not needed
+ var autheventCookie = $cookies.get('authevent_' + adminId);
+ var authCookie = $cookies.get('auth_authevent_' + adminId);
+ if (!!autheventCookie && autheventCookie === adminId &&
+ autheventid === adminId && !!authCookie)
+ {
+ $window.location.href = '/admin/elections';
}
- };
- return authmethod;
-} ]), angular.module("avRegistration").controller("LoginController", [ "$scope", "$stateParams", "$filter", "$i18next", "$cookies", "$window", "ConfigService", "Authmethod", function($scope, $stateParams, $filter, $i18next, $cookies, $window, ConfigService, Authmethod) {
- $scope.event_id = $stateParams.id, $scope.code = $stateParams.code, $scope.email = $stateParams.email,
- $scope.isOpenId = $stateParams.isOpenId;
-} ]), angular.module("avRegistration").directive("avLogin", [ "Authmethod", "StateDataService", "$state", "$location", "$cookies", "$i18next", "$window", "$timeout", "ConfigService", "Patterns", function(Authmethod, StateDataService, $state, $location, $cookies, $i18next, $window, $timeout, ConfigService, Patterns) {
- return {
- restrict: "AE",
- scope: !0,
- link: function(scope, element, attrs) {
- scope.isCensusQuery = attrs.isCensusQuery, scope.error = null, scope.hide_default_login_lookup_field = !1;
- var adminId = ConfigService.freeAuthId + "", autheventid = scope.eventId = attrs.eventId;
- scope.orgName = ConfigService.organization.orgName, scope.openIDConnectProviders = ConfigService.openIDConnectProviders;
- var autheventCookie = $cookies.get("authevent_" + adminId), authCookie = $cookies.get("auth_authevent_" + adminId);
- function randomStr() {
- var random = sjcl.random.randomWords(64, 0);
- return sjcl.codec.hex.fromBits(random);
+ scope.sendingData = false;
+
+ scope.currentFormStep = 0;
+
+ scope.stateData = StateDataService.getData();
+
+ scope.signupLink = ConfigService.signupLink;
+
+ scope.allowUserResend = false;
+ scope.censusQuery = "not-sent";
+
+ scope.code = null;
+ if (attrs.code && attrs.code.length > 0) {
+ scope.code = attrs.code;
+ }
+ scope.email = null;
+ if (attrs.email && attrs.email.length > 0) {
+ scope.email = attrs.email;
+ }
+
+ scope.isAdmin = false;
+ if (autheventid === adminId) {
+ scope.isAdmin = true;
+ }
+
+ function isValidTel(inputName) {
+ if (!document.getElementById(inputName)) {
+ return false;
+ }
+ var telInput = angular.element(document.getElementById(inputName));
+ return telInput.intlTelInput("isValidNumber");
+ }
+
+ function isValidEmail(email) {
+ var pattern = Patterns.get('email');
+ return null !== email.match(pattern);
+ }
+
+ /**
+ * Send auth codes now to the voter
+ */
+ scope.resendAuthCode = function(field) {
+ // if invalid method or already sending data, do not proceed
+ if (
+ scope.sendingData ||
+ !_.contains(["email", "email-otp", "sms", "sms-otp"], scope.method)
+ ) {
+ return;
+ }
+
+ // if telIndex or emailIndex not set when needed, do not proceed
+ if (
+ (
+ _.contains(["sms", "sms-otp"], scope.method) &&
+ scope.telIndex === -1 &&
+ !scope.hide_default_login_lookup_field
+ ) || (
+ _.contains(["email", "email-otp"], scope.method) &&
+ scope.emailIndex === -1 &&
+ !scope.hide_default_login_lookup_field
+ )
+ ) {
+ return;
+ }
+
+ // obtain the data to be sent to the authapi to request
+ // new auth codes by filtering and validating login fields
+ // with steps == undefined or included in step 0
+ var stop = false;
+ var data = _.object(
+ _.filter(
+ scope.login_fields,
+ function (element, index) {
+ element.index = index;
+ return (
+ element.steps === undefined ||
+ element.steps.indexOf(0) !== -1
+ );
+ }
+ ).map(
+ function (element) {
+ if (
+ (
+ _.contains(["sms", "sms-otp"], scope.method) &&
+ element.index === scope.telIndex &&
+ !isValidTel("input" + scope.telIndex)
+ ) || (
+ _.contains(["email", "email-otp"], scope.method) &&
+ element.index === scope.emailIndex &&
+ !isValidEmail(element.value)
+ )
+ ) {
+ stop = true;
+ }
+ return [element.name, element.value];
+ }
+ )
+ );
+
+ // if any issue found, do not proceed
+ if (stop) {
+ return;
+ }
+
+
+ // reset code field, as we are going to send a new one
+ if (!!field) {
+ field.value = "";
+ }
+
+ scope.sendingData = true;
+ Authmethod.resendAuthCode(data, autheventid)
+ .then(
+ function(response) {
+ // disabling login that are from previous step
+ _.each(
+ scope.login_fields,
+ function (element) {
+ if (
+ element.steps === undefined ||
+ element.steps.indexOf(0) !== -1
+ ) {
+ element.disabled = true;
+ }
+ }
+ );
+ scope.currentFormStep = 1;
+ scope.error = null;
+ $timeout(scope.sendingDataTimeout, 3000);
+ },
+ function onError(response) {
+ $timeout(scope.sendingDataTimeout, 3000);
+ scope.error = $i18next('avRegistration.errorSendingAuthCode');
+ }
+ );
+ };
+
+ scope.sendingDataTimeout = function () {
+ scope.sendingData = false;
+ };
+
+ scope.checkCensus = function(valid) {
+ if (!valid) {
+ return;
+ }
+
+ if (scope.sendingData) {
+ return;
+ }
+ scope.censusQuery = "querying";
+
+ var data = {
+ 'captcha_code': Authmethod.captcha_code,
+ };
+ _.each(scope.login_fields, function (field) {
+ data[field.name] = field.value;
+ });
+
+ scope.sendingData = true;
+ Authmethod.censusQuery(data, autheventid)
+ .then(
+ function onSuccess(response) {
+ scope.sendingData = false;
+ scope.censusQueryData = response.data;
+ scope.censusQuery = "success";
+ },
+ function onError(response) {
+ scope.sendingData = false;
+ scope.censusQuery = "fail";
+ }
+ );
+ };
+
+ scope.loginUser = function(valid) {
+ if (!valid) {
+ return;
+ }
+ if (scope.sendingData) {
+ return;
+ }
+
+ // loginUser
+ if (_.contains(['sms-otp', 'email-otp'], scope.method) && scope.currentFormStep === 0) {
+ scope.resendAuthCode();
+ return;
+ }
+ var data = {
+ 'captcha_code': Authmethod.captcha_code,
+ };
+ _.each(scope.login_fields, function (field) {
+ if (field.name === 'email') {
+ scope.email = field.value;
+ } else if ('code' === field.name) {
+ field.value = field.value.trim().replace(/ |\n|\t|-|_/g,'').toUpperCase();
}
- autheventCookie && autheventCookie === adminId && autheventid === adminId && authCookie && ($window.location.href = "/admin/elections"),
- scope.sendingData = !1, scope.currentFormStep = 0, scope.stateData = StateDataService.getData(),
- scope.signupLink = ConfigService.signupLink, scope.allowUserResend = !1, scope.censusQuery = "not-sent",
- scope.code = null, attrs.code && 0 < attrs.code.length && (scope.code = attrs.code),
- scope.email = null, attrs.email && 0 < attrs.email.length && (scope.email = attrs.email),
- scope.isAdmin = !1, autheventid === adminId && (scope.isAdmin = !0), scope.resendAuthCode = function(field) {
- var stop, data;
- !scope.sendingData && _.contains([ "email", "email-otp", "sms", "sms-otp" ], scope.method) && (_.contains([ "sms", "sms-otp" ], scope.method) && -1 === scope.telIndex && !scope.hide_default_login_lookup_field || _.contains([ "email", "email-otp" ], scope.method) && -1 === scope.emailIndex && !scope.hide_default_login_lookup_field || (stop = !1,
- data = _.object(_.filter(scope.login_fields, function(element, index) {
- return element.index = index, void 0 === element.steps || -1 !== element.steps.indexOf(0);
- }).map(function(element) {
- var email, pattern;
- return (_.contains([ "sms", "sms-otp" ], scope.method) && element.index === scope.telIndex && (pattern = "input" + scope.telIndex,
- !document.getElementById(pattern) || !angular.element(document.getElementById(pattern)).intlTelInput("isValidNumber")) || _.contains([ "email", "email-otp" ], scope.method) && element.index === scope.emailIndex && (email = element.value,
- pattern = Patterns.get("email"), null === email.match(pattern))) && (stop = !0),
- [ element.name, element.value ];
- })), stop || (field && (field.value = ""), scope.sendingData = !0, Authmethod.resendAuthCode(data, autheventid).then(function(response) {
- _.each(scope.login_fields, function(element) {
- void 0 !== element.steps && -1 === element.steps.indexOf(0) || (element.disabled = !0);
- }), scope.currentFormStep = 1, scope.error = null, $timeout(scope.sendingDataTimeout, 3e3);
- }, function(response) {
- $timeout(scope.sendingDataTimeout, 3e3), scope.error = $i18next("avRegistration.errorSendingAuthCode");
- }))));
- }, scope.sendingDataTimeout = function() {
- scope.sendingData = !1;
- }, scope.checkCensus = function(valid) {
- var data;
- valid && (scope.sendingData || (scope.censusQuery = "querying", data = {
- captcha_code: Authmethod.captcha_code
- }, _.each(scope.login_fields, function(field) {
- data[field.name] = field.value;
- }), scope.sendingData = !0, Authmethod.censusQuery(data, autheventid).then(function(response) {
- scope.sendingData = !1, scope.censusQueryData = response.data, scope.censusQuery = "success";
- }, function(response) {
- scope.sendingData = !1, scope.censusQuery = "fail";
- })));
- }, scope.loginUser = function(valid) {
- var data;
- valid && (scope.sendingData || (_.contains([ "sms-otp", "email-otp" ], scope.method) && 0 === scope.currentFormStep ? scope.resendAuthCode() : (data = {
- captcha_code: Authmethod.captcha_code
- }, _.each(scope.login_fields, function(field) {
- "email" === field.name ? scope.email = field.value : "code" === field.name && (field.value = field.value.trim().replace(/ |\n|\t|-|_/g, "").toUpperCase()),
- data[field.name] = field.value;
- }), "smart-link" === scope.method && (data["auth-token"] = $location.search()["auth-token"]),
- scope.sendingData = !0, scope.error = null, Authmethod.login(data, autheventid).then(function(tokens) {
- var postfix, options;
- "ok" === tokens.data.status ? (postfix = "_authevent_" + autheventid, options = {},
- ConfigService.cookies && ConfigService.cookies.expires && (options.expires = new Date(),
- options.expires.setMinutes(options.expires.getMinutes() + ConfigService.cookies.expires)),
- $cookies.put("authevent_" + autheventid, autheventid, options), $cookies.put("userid" + postfix, tokens.data.username, options),
- $cookies.put("user" + postfix, scope.email || tokens.data.username || tokens.data.email, options),
- $cookies.put("auth" + postfix, tokens.data["auth-token"], options), $cookies.put("isAdmin" + postfix, scope.isAdmin, options),
- Authmethod.setAuth($cookies.get("auth" + postfix), scope.isAdmin, autheventid),
- scope.isAdmin ? Authmethod.getUserInfo().then(function(response) {
- $cookies.put("user" + postfix, response.data.email || scope.email || response.data.username, options),
- $window.location.href = "/admin/elections";
- }, function(response) {
- $window.location.href = "/admin/elections";
- }) : angular.isDefined(tokens.data["redirect-to-url"]) ? $window.location.href = tokens.data["redirect-to-url"] : angular.isDefined(tokens.data["vote-permission-token"]) ? ($window.sessionStorage.setItem("vote_permission_tokens", JSON.stringify([ {
+ data[field.name] = field.value;
+ });
+
+ // Get the smart link authentication token and set it in the data if
+ // this is an auth event with smart-link auth method
+ if (scope.method === 'smart-link')
+ {
+ data['auth-token'] = $location.search()['auth-token'];
+ }
+
+ scope.sendingData = true;
+ scope.error = null;
+ Authmethod
+ .login(data, autheventid)
+ .then(
+ function onSuccess(response) {
+ if (response.data.status === "ok") {
+ var postfix = "_authevent_" + autheventid;
+ var options = {};
+ if (ConfigService.cookies && ConfigService.cookies.expires) {
+ options.expires = new Date();
+ options.expires.setMinutes(options.expires.getMinutes() + ConfigService.cookies.expires);
+ }
+ $cookies.put("authevent_" + autheventid, autheventid, options);
+ $cookies.put("userid" + postfix, response.data.username, options);
+ $cookies.put("user" + postfix, scope.email || response.data.username || response.data.email, options);
+ $cookies.put("auth" + postfix, response.data['auth-token'], options);
+ $cookies.put("isAdmin" + postfix, scope.isAdmin, options);
+ Authmethod.setAuth($cookies.get("auth" + postfix), scope.isAdmin, autheventid);
+ if (scope.isAdmin)
+ {
+ Authmethod.getUserInfo()
+ .then(
+ function onSuccess(response) {
+ $cookies.put("user" + postfix, response.data.email || scope.email || response.data.username, options);
+ $window.location.href = '/admin/elections';
+ },
+ function onError(response) {
+ $window.location.href = '/admin/elections';
+ }
+ );
+ }
+ else if (angular.isDefined(response.data['redirect-to-url']))
+ {
+ $window.location.href = response.data['redirect-to-url'];
+ }
+ // if it's an election with no children elections
+ else if (angular.isDefined(response.data['vote-permission-token']))
+ {
+ $window.sessionStorage.setItem(
+ "vote_permission_tokens",
+ JSON.stringify([{
electionId: autheventid,
- token: tokens.data["vote-permission-token"]
- } ])), $window.location.href = "/booth/" + autheventid + "/vote") : angular.isDefined(tokens.data["vote-children-info"]) ? (tokens = _.chain(tokens.data["vote-children-info"]).filter(function(child) {
- return (0 === child["num-successful-logins-allowed"] || child["num-successful-logins"] < child["num-successful-logins-allowed"]) && !!child["vote-permission-token"];
- }).map(function(child, index) {
+ token: response.data['vote-permission-token']
+ }])
+ );
+ $window.location.href = '/booth/' + autheventid + '/vote';
+ }
+ // if it's an election with children elections then show access to them
+ else if (angular.isDefined(response.data['vote-children-info']))
+ {
+ // assumes the authapi response has the same children
+ var tokens = _
+ .chain(response.data['vote-children-info'])
+ .filter(function (child) {
+ return (
+ child['num-successful-logins-allowed'] === 0 ||
+ child['num-successful-logins'] < child['num-successful-logins-allowed']
+ ) && !!child['vote-permission-token'];
+ })
+ .map(function (child, index) {
return {
- electionId: child["auth-event-id"],
- token: child["vote-permission-token"],
- skipped: !1,
- voted: !1,
- isFirst: 0 === index
+ electionId: child['auth-event-id'],
+ token: child['vote-permission-token'],
+ skipped: false,
+ voted: false,
+ isFirst: index === 0
};
- }).value(), $window.sessionStorage.setItem("vote_permission_tokens", JSON.stringify(tokens)),
- 0 < tokens.length ? $window.location.href = "/booth/" + tokens[0].electionId + "/vote" : scope.error = $i18next("avRegistration.invalidCredentials", {
- support: ConfigService.contact.email
- })) : scope.error = $i18next("avRegistration.invalidCredentials", {
- support: ConfigService.contact.email
- })) : (scope.sendingData = !1, scope.status = "Not found", scope.error = $i18next("avRegistration.invalidCredentials", {
- support: ConfigService.contact.email
- }));
- }, function(response) {
- scope.sendingData = !1, scope.status = "Registration error: " + response.data.message,
- scope.error = $i18next("avRegistration.invalidCredentials", {
- support: ConfigService.contact.email
- });
- }))));
- }, scope.apply = function(authevent) {
- var electionsMatch, adminMatch;
- scope.method = authevent.auth_method, scope.name = authevent.name, scope.registrationAllowed = "open" === authevent.census && (autheventid !== adminId || ConfigService.allowAdminRegistration),
- scope.isCensusQuery ? scope.login_fields = Authmethod.getCensusQueryFields(authevent) : scope.login_fields = Authmethod.getLoginFields(authevent),
- scope.hide_default_login_lookup_field = authevent.hide_default_login_lookup_field,
- scope.telIndex = -1, scope.emailIndex = -1, scope.telField = null, scope.allowUserResend = (fields = !1,
- electionsMatch = $location.path(), adminMatch = electionsMatch.match(/^\/admin\//),
- electionsMatch = electionsMatch.match(/^\/(elections|election)\/([0-9]+)\//), _.isArray(adminMatch) ? fields = !0 : _.isArray(electionsMatch) && 3 === electionsMatch.length && (fields = _.isObject(authevent.auth_method_config) && _.isObject(authevent.auth_method_config.config) && !0 === authevent.auth_method_config.config.allow_user_resend),
- fields);
- var fields = _.map(scope.login_fields, function(el, index) {
- return scope.stateData[el.name] ? (el.value = scope.stateData[el.name], el.disabled = !0) : (el.value = null,
- el.disabled = !1), "email" === el.type ? (null !== scope.email && (el.value = scope.email,
- el.disabled = !0, "email-otp" === scope.method && (scope.currentFormStep = 1)),
- scope.emailIndex = index) : "code" === el.type && null !== scope.code ? (el.value = scope.code.trim().replace(/ |\n|\t|-|_/g, "").toUpperCase(),
- el.disabled = !0) : "tlf" === el.type && "sms" === scope.method ? (null !== scope.email && -1 === scope.email.indexOf("@") && (el.value = scope.email,
- el.disabled = !0), scope.telIndex = index + 1, scope.telField = el) : "tlf" === el.type && "sms-otp" === scope.method && (null !== scope.email && -1 === scope.email.indexOf("@") && (el.value = scope.email,
- el.disabled = !0, scope.currentFormStep = 1), scope.telIndex = index + 1, scope.telField = el),
- el;
- });
- _.filter(fields, function(el) {
- return null !== el.value;
- }).length === scope.login_fields.length && "openid-connect" !== scope.method && scope.loginUser(!0);
- }, scope.view = function(id) {
- Authmethod.viewEvent(id).then(function(response) {
- "ok" === response.data.status ? scope.apply(response.data.events) : (scope.status = "Not found",
- document.querySelector(".input-error").style.display = "block");
- }, function(response) {
- scope.status = "Scan error: " + response.data.message, document.querySelector(".input-error").style.display = "block";
- });
- }, scope.view(autheventid), scope.goSignup = function() {
- $state.go("registration.register", {
- id: autheventid
- });
- }, scope.forgotPassword = function() {
- console.log("forgotPassword");
- }, scope.openidConnectAuth = function(provider) {
- var randomState = randomStr(), authURI = randomStr();
- $cookies["openid-connect-csrf"] = angular.toJson({
- randomState: randomState,
- randomNonce: authURI,
- created: Date.now(),
- eventId: scope.eventId,
- providerId: provider.id
- }), provider ? (authURI = provider.authorization_endpoint + "?response_type=id_token&client_id=" + encodeURIComponent(provider.client_id) + "&scope=" + encodeURIComponent("openid") + "&redirect_uri=" + encodeURIComponent($window.location.origin + "/election/login-openid-connect-redirect") + "&state=" + randomState + "&nonce=" + authURI,
- $window.location.href = authURI) : scope.error = $i18next("avRegistration.openidError");
- };
- },
- templateUrl: "avRegistration/login-directive/login-directive.html"
- };
-} ]), angular.module("avRegistration").directive("avOpenidConnect", [ "$cookies", "$window", "$location", "ConfigService", "Authmethod", function($cookies, $window, $location, ConfigService, Authmethod) {
+ })
+ .value();
+ $window.sessionStorage.setItem(
+ "vote_permission_tokens",
+ JSON.stringify(tokens)
+ );
+
+ if (tokens.length > 0) {
+ $window.location.href = '/booth/' + tokens[0].electionId + '/vote';
+ } else {
+ scope.error = $i18next(
+ 'avRegistration.invalidCredentials',
+ {support: ConfigService.contact.email}
+ );
+ }
+ } else {
+ scope.error = $i18next(
+ 'avRegistration.invalidCredentials',
+ {support: ConfigService.contact.email}
+ );
+ }
+ } else {
+ scope.sendingData = false;
+ scope.status = 'Not found';
+ scope.error = $i18next(
+ 'avRegistration.invalidCredentials',
+ {support: ConfigService.contact.email}
+ );
+ }
+ },
+ function onError(response) {
+ scope.sendingData = false;
+ scope.status = 'Registration error: ' + response.data.message;
+ scope.error = $i18next(
+ 'avRegistration.invalidCredentials',
+ {support: ConfigService.contact.email}
+ );
+ }
+ );
+ };
+
+ scope.apply = function(authevent) {
+ scope.method = authevent['auth_method'];
+ scope.name = authevent['name'];
+ scope.registrationAllowed = (
+ (authevent['census'] === 'open') &&
+ (autheventid !== adminId || ConfigService.allowAdminRegistration)
+ );
+ if (!scope.isCensusQuery && !scope.withCode) {
+ scope.login_fields = Authmethod.getLoginFields(authevent);
+ } else if (scope.withCode) {
+ scope.login_fields = Authmethod.getLoginWithCode(authevent);
+ } else { // scope.isCensusQuery is true
+ scope.login_fields = Authmethod.getCensusQueryFields(authevent);
+ }
+ scope.hide_default_login_lookup_field = authevent.hide_default_login_lookup_field;
+ scope.telIndex = -1;
+ scope.emailIndex = -1;
+ scope.telField = null;
+ scope.allowUserResend = (function () {
+ if (scope.withCode) {
+ return false;
+ }
+ var ret = false;
+ var href = $location.path();
+ var adminMatch = href.match(/^\/admin\//);
+ var electionsMatch = href.match(/^\/(elections|election)\/([0-9]+)\//);
+
+ if (_.isArray(adminMatch)) {
+ ret = true;
+ } else if (_.isArray(electionsMatch) && 3 === electionsMatch.length) {
+ ret = (_.isObject(authevent.auth_method_config) &&
+ _.isObject(authevent.auth_method_config.config) &&
+ true === authevent.auth_method_config.config.allow_user_resend);
+ }
+ return ret;
+ })();
+
+ var fields = _.map(
+ scope.login_fields,
+ function (el, index) {
+ if (!!scope.stateData[el.name]) {
+ el.value = scope.stateData[el.name];
+ el.disabled = true;
+ } else {
+ el.value = null;
+ el.disabled = false;
+ }
+ if (el.type === "email") {
+ if (scope.email !== null) {
+ el.value = scope.email;
+ el.disabled = true;
+ if (scope.method === "email-otp") {
+ scope.currentFormStep = 1;
+ }
+ }
+ scope.emailIndex = index;
+ } else if (el.type === "code" && scope.code !== null) {
+ el.value = scope.code.trim().replace(/ |\n|\t|-|_/g,'').toUpperCase();
+ el.disabled = true;
+ } else if (el.type === "tlf" && scope.method === "sms") {
+ if (scope.email !== null && scope.email.indexOf('@') === -1) {
+ el.value = scope.email;
+ el.disabled = true;
+ }
+ scope.telIndex = index+1;
+ scope.telField = el;
+ } else if (el.type === "tlf" && scope.method === "sms-otp") {
+ if (scope.email !== null && scope.email.indexOf('@') === -1) {
+ el.value = scope.email;
+ el.disabled = true;
+ scope.currentFormStep = 1;
+ }
+ scope.telIndex = index+1;
+ scope.telField = el;
+ } else if (el.name === '__username' && scope.withCode) {
+ el.value = scope.username;
+ el.disabled = true;
+ }
+ return el;
+ });
+ var filled_fields = _.filter(fields,
+ function (el) { return el.value !== null; });
+
+ // if not all the fields all filled at this point, then we stop here
+ if (filled_fields.length !== scope.login_fields.length) {
+ return;
+ }
+
+ // if all fields all filled in and it's not OpenID Connect do
+ // auto-login
+ if (scope.method !== 'openid-connect')
+ {
+ scope.loginUser(true);
+ }
+
+ };
+
+ scope.view = function(id) {
+ Authmethod.viewEvent(id)
+ .then(
+ function onSuccess(response) {
+ if (response.data.status === "ok") {
+ scope.apply(response.data.events);
+ } else {
+ scope.status = 'Not found';
+ document.querySelector(".input-error").style.display = "block";
+ }
+ },
+ function onError(response) {
+ scope.status = 'Scan error: ' + response.data.message;
+ document.querySelector(".input-error").style.display = "block";
+ }
+ );
+ };
+ scope.view(autheventid);
+
+ scope.goSignup = function() {
+ $state.go('registration.register', {id: autheventid});
+ };
+
+ scope.forgotPassword = function() {
+ console.log('forgotPassword');
+ };
+
+ // generate a cryptogrpahically secure random string
+ function randomStr()
+ {
+ /* jshint ignore:start */
+ var random = sjcl.random.randomWords(/* bitlength */ 2048 / 32, 0);
+ return sjcl.codec.hex.fromBits(random);
+ /* jshint ignore:end */
+ }
+
+ // OpenIDConnect sets a cookie that is used to create a CSRF token
+ // similar to what is mentioned here:
+ // https://developers.google.com/identity/protocols/OpenIDConnect#createxsrftoken
+ scope.openidConnectAuth = function(provider)
+ {
+ var randomState = randomStr();
+ var randomNonce = randomStr();
+ $cookies['openid-connect-csrf'] = angular.toJson({
+ randomState: randomState,
+ randomNonce: randomNonce,
+ created: Date.now(),
+ eventId: scope.eventId,
+ providerId: provider.id
+ });
+
+ // find provider
+ if (!provider)
+ {
+ scope.error = $i18next('avRegistration.openidError');
+ return;
+ }
+
+ // Craft the OpenID Connect auth URI
+ var authURI = (provider.authorization_endpoint +
+ "?response_type=id_token" +
+ "&client_id=" + encodeURIComponent(provider.client_id) +
+ "&scope=" + encodeURIComponent("openid") +
+ "&redirect_uri=" + encodeURIComponent(
+ $window.location.origin +
+ "/election/login-openid-connect-redirect"
+ ) +
+ "&state=" + randomState +
+ "&nonce=" + randomNonce
+ );
+
+ // Redirect to the Auth URI
+ $window.location.href = authURI;
+ };
+ }
return {
- restrict: "AE",
- scope: !0,
- link: function(scope, element, attrs) {
- var maxOAuthLoginTimeout = 3e5;
- function simpleRedirectToLogin() {
- scope.csrf ? $window.location.href = "/election/" + scope.csrf.eventId + "/public/login" : $window.location.href = "/";
+ restrict: 'AE',
+ scope: true,
+ link: link,
+ templateUrl: 'avRegistration/login-directive/login-directive.html'
+ };
+ });
+
+/**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+angular.module('avRegistration')
+ .directive(
+ 'avOpenidConnect',
+ function(
+ $cookies,
+ $window,
+ $location,
+ ConfigService,
+ Authmethod
+ ) {
+ // we use it as something similar to a controller here
+ function link(scope, element, attrs)
+ {
+ // Maximum Oauth Login Timeout is 5 minutes
+ var maxOAuthLoginTimeout = 1000 * 60 * 5;
+
+ scope.csrf = null;
+
+ // simply redirect to login
+ function simpleRedirectToLogin()
+ {
+ if (scope.csrf)
+ {
+ $window.location.href = "/election/" + scope.csrf.eventId + "/public/login";
+ } else {
+ $window.location.href = "/";
+ }
+ }
+
+ // Returns the logout url if any from the appropiate openidprovider
+ // TODO: logout asumes that you are using the first provider, so it
+ // basically supports only one provider
+ function getLogoutUri()
+ {
+ if (ConfigService.openIDConnectProviders.length === 0 || !ConfigService.openIDConnectProviders[0].logout_uri)
+ {
+ return false;
+ }
+
+ var eventId = null;
+ if (scope.csrf)
+ {
+ eventId = scope.csrf.eventId;
+ }
+
+ var uri = ConfigService.openIDConnectProviders[0].logout_uri;
+ uri = uri.replace("__EVENT_ID__", "" + eventId);
+
+ var postfix = "_authevent_" + eventId;
+ if (!!$cookies.get("id_token_" + postfix))
+ {
+ uri = uri.replace("__ID_TOKEN__", $cookies.get("id_token_" + postfix));
+
+ // if __ID_TOKEN__ is there but we cannot replace it, we need to
+ // directly redirect to the login, otherwise the URI might show an
+ // error 500
+ } else if (uri.indexOf("__ID_TOKEN__") > -1)
+ {
+ uri = "/election/" + eventId + "/public/login";
+ }
+
+ return uri;
+ }
+
+ scope.redirectingToUri = false;
+
+ // Redirects to the login page of the respective event_id if any
+ function redirectToLogin()
+ {
+ if (scope.redirectingToUri)
+ {
+ return;
+ }
+
+ scope.redirectingToUri = true;
+
+ var eventId = null;
+ if (scope.csrf)
+ {
+ eventId = scope.csrf.eventId;
+ } else {
+ $window.location.href = "/";
+ return;
+ }
+
+ Authmethod.viewEvent(eventId)
+ .then(
+ function onSuccess(response)
+ {
+ if (response.data.status !== "ok" || !response.data.events || response.data.events.auth_method !== 'openid-connect' || !getLogoutUri())
+ {
+ simpleRedirectToLogin();
+ return;
+ }
+
+ var postfix = "_authevent_" + eventId;
+ var uri = getLogoutUri();
+ $cookies.remove("id_token_" + postfix);
+ $window.location.href = uri;
+ },
+ function onError(response)
+ {
+ simpleRedirectToLogin();
+ }
+ );
+ }
+
+ // Get the decoded value of a uri parameter from any uri. The uri does not
+ // need to have any domain, it can start with the character "?"
+ function getURIParameter(paramName, uri)
+ {
+ var paramName2 = paramName.replace(/[\[\]]/g, '\\$&');
+ var rx = new RegExp('[?&]' + paramName2 + '(=([^]*)|&|#|$)');
+ var params = rx.exec(uri);
+
+ if (!params)
+ {
+ return null;
+ }
+
+ if (!params[2])
+ {
+ return '';
}
- function getLogoutUri() {
- if (0 === ConfigService.openIDConnectProviders.length || !ConfigService.openIDConnectProviders[0].logout_uri) return !1;
- var eventId = null;
- scope.csrf && (eventId = scope.csrf.eventId);
- var uri = (uri = ConfigService.openIDConnectProviders[0].logout_uri).replace("__EVENT_ID__", "" + eventId), postfix = "_authevent_" + eventId;
- return $cookies.get("id_token_" + postfix) ? uri = uri.replace("__ID_TOKEN__", $cookies.get("id_token_" + postfix)) : -1 < uri.indexOf("__ID_TOKEN__") && (uri = "/election/" + eventId + "/public/login"),
- uri;
+ return decodeURIComponent(params[2].replace(/\+/g, ' '));
+ }
+
+ // validates the CSRF token
+ function validateCsrfToken()
+ {
+ if (!$cookies.get('openid-connect-csrf'))
+ {
+ redirectToLogin();
+ return null;
}
- function redirectToLogin() {
- var eventId;
- scope.redirectingToUri || (scope.redirectingToUri = !0, eventId = null, scope.csrf ? (eventId = scope.csrf.eventId,
- Authmethod.viewEvent(eventId).then(function(uri) {
- var postfix;
- "ok" === uri.data.status && uri.data.events && "openid-connect" === uri.data.events.auth_method && getLogoutUri() ? (postfix = "_authevent_" + eventId,
- uri = getLogoutUri(), $cookies.remove("id_token_" + postfix), $window.location.href = uri) : simpleRedirectToLogin();
- }, function(response) {
- simpleRedirectToLogin();
- })) : $window.location.href = "/");
+
+ // validate csrf token format and data
+ var csrf = scope.csrf = angular.fromJson($cookies.get('openid-connect-csrf'));
+ var uri = "?" + $window.location.hash.substr(1);
+
+ // NOTE: if you need to debug this callback, obtain the callback
+ // URL, get the callback received in the server (to obtain the
+ // nonce) that was received by the client and change the data here
+ // accordingly and set here the debug break point, then execute
+ // a line like the following in the comment.
+ //
+ // The only data that needs to be changed is the randomNonnce and
+ // the eventId.
+ //
+ // csrf = scope.csrf = {
+ // randomNonce: 'something',
+ // randomState: getURIParameter("state", uri),
+ // created: Date.now(),
+ // eventId: 11111
+ // };
+
+ $cookies.remove('openid-connect-csrf');
+ var isCsrfValid = (!!csrf &&
+ angular.isObject(csrf) &&
+ angular.isString(csrf.randomState) &&
+ angular.isString(csrf.randomNonce) &&
+ angular.isNumber(csrf.created) &&
+ getURIParameter("state", uri) === csrf.randomState &&
+ csrf.created - Date.now() < maxOAuthLoginTimeout
+ );
+
+ if (!isCsrfValid)
+ {
+ redirectToLogin();
+ return null;
}
- function getURIParameter(paramName2, params) {
- paramName2 = paramName2.replace(/[\[\]]/g, "\\$&"), params = new RegExp("[?&]" + paramName2 + "(=([^]*)|&|#|$)").exec(params);
- return params ? params[2] ? decodeURIComponent(params[2].replace(/\+/g, " ")) : "" : null;
+ return true;
+ }
+
+ // Process an OpenId Connect callback coming from the provider, try to
+ // validate the callback data and get the authentication token from our
+ // server and redirect to vote
+ function processOpenIdAuthCallback()
+ {
+ // validate csrf token from uri and from state in the hash
+ validateCsrfToken();
+
+ var uri = "?" + $window.location.hash.substr(1);
+
+ var data = {
+ id_token: getURIParameter("id_token", uri),
+ provider: scope.csrf.providerId,
+ nonce: scope.csrf.randomNonce
+ };
+
+ var options = {};
+ if (ConfigService.cookies && ConfigService.cookies.expires) {
+ options.expires = new Date();
+ options.expires.setMinutes(options.expires.getMinutes() + ConfigService.cookies.expires);
}
- scope.csrf = null, scope.redirectingToUri = !1, function() {
- !function() {
- if ($cookies.get("openid-connect-csrf")) {
- var csrf = scope.csrf = angular.fromJson($cookies.get("openid-connect-csrf")), uri = "?" + $window.location.hash.substr(1);
- if ($cookies.remove("openid-connect-csrf"), !!csrf && angular.isObject(csrf) && angular.isString(csrf.randomState) && angular.isString(csrf.randomNonce) && angular.isNumber(csrf.created) && getURIParameter("state", uri) === csrf.randomState && csrf.created - Date.now() < maxOAuthLoginTimeout) return;
+
+ var postfix = "_authevent_" + scope.csrf.eventId;
+ $cookies.put("id_token_" + postfix, data.id_token, options);
+
+ // Send the authentication request to our server
+ Authmethod.login(data, scope.csrf.eventId)
+ .then(
+ function onSuccess(response)
+ {
+ if (response.data.status === "ok")
+ {
+ scope.khmac = response.data.khmac;
+ var postfix = "_authevent_" + scope.csrf.eventId;
+ $cookies.put("authevent_" + scope.csrf.eventId, scope.csrf.eventId, options);
+ $cookies.put("userid" + postfix, response.data.username, options);
+ $cookies.put("user" + postfix, response.data.username, options);
+ $cookies.put("auth" + postfix, response.data['auth-token'], options);
+ $cookies.put("isAdmin" + postfix, false, options);
+ Authmethod.setAuth($cookies.get("auth" + postfix), scope.isAdmin, scope.csrf.eventId);
+
+ if (angular.isDefined(response.data['redirect-to-url']))
+ {
+ $window.location.href = response.data['redirect-to-url'];
+ }
+ else
+ {
+ // redirecting to vote link
+ Authmethod.getPerm("vote", "AuthEvent", scope.csrf.eventId)
+ .then(function onSuccess(response2)
+ {
+ var khmac = response2.data['permission-token'];
+ var path = khmac.split(";")[1];
+ var hash = path.split("/")[0];
+ var msg = path.split("/")[1];
+ $window.location.href = '/booth/' + scope.csrf.eventId + '/vote/' + hash + '/' + msg;
+ });
+ }
+ } else
+ {
+ // TODO: show error
redirectToLogin();
- } else redirectToLogin();
- }();
- var data = {
- id_token: getURIParameter("id_token", "?" + $window.location.hash.substr(1)),
- provider: scope.csrf.providerId,
- nonce: scope.csrf.randomNonce
- }, options = {};
- ConfigService.cookies && ConfigService.cookies.expires && (options.expires = new Date(),
- options.expires.setMinutes(options.expires.getMinutes() + ConfigService.cookies.expires));
- var postfix = "_authevent_" + scope.csrf.eventId;
- $cookies.put("id_token_" + postfix, data.id_token, options), Authmethod.login(data, scope.csrf.eventId).then(function(response) {
- var postfix;
- "ok" === response.data.status ? (scope.khmac = response.data.khmac, postfix = "_authevent_" + scope.csrf.eventId,
- $cookies.put("authevent_" + scope.csrf.eventId, scope.csrf.eventId, options), $cookies.put("userid" + postfix, response.data.username, options),
- $cookies.put("user" + postfix, response.data.username, options), $cookies.put("auth" + postfix, response.data["auth-token"], options),
- $cookies.put("isAdmin" + postfix, !1, options), Authmethod.setAuth($cookies.get("auth" + postfix), scope.isAdmin, scope.csrf.eventId),
- angular.isDefined(response.data["redirect-to-url"]) ? $window.location.href = response.data["redirect-to-url"] : Authmethod.getPerm("vote", "AuthEvent", scope.csrf.eventId).then(function(hash) {
- var msg = hash.data["permission-token"].split(";")[1], hash = msg.split("/")[0], msg = msg.split("/")[1];
- $window.location.href = "/booth/" + scope.csrf.eventId + "/vote/" + hash + "/" + msg;
- })) : redirectToLogin();
- }, function(response) {
+ return;
+ }
+ },
+ function onError(response)
+ {
+ // TODO: show error
redirectToLogin();
- });
- }();
- },
- templateUrl: "avRegistration/openid-connect-directive/openid-connect-directive.html"
- };
-} ]), angular.module("avRegistration").controller("LogoutController", [ "$scope", "$stateParams", "$filter", "ConfigService", "$i18next", "$state", "$cookies", "Authmethod", function($scope, $stateParams, $filter, ConfigService, $i18next, $state, $cookies, postfix) {
- ConfigService.freeAuthId;
- var authevent = postfix.getAuthevent(), postfix = "_authevent_" + authevent;
- $cookies.put("user" + postfix, ""), $cookies.put("auth" + postfix, ""), $cookies.put("authevent_" + authevent, ""),
- $cookies.put("userid" + postfix, ""), $cookies.put("isAdmin" + postfix, !1), authevent !== ConfigService.freeAuthId + "" && authevent ? $state.go("registration.login", {
- id: $cookies.get("authevent_" + authevent)
- }) : $state.go("admin.login");
-} ]), angular.module("avRegistration").controller("RegisterController", [ "$scope", "$stateParams", "$filter", "ConfigService", "$i18next", function($scope, $stateParams, $filter, ConfigService, $i18next) {
- $scope.event_id = $stateParams.id, $scope.email = $stateParams.email;
-} ]), angular.module("avRegistration").directive("avRegister", [ "Authmethod", "StateDataService", "$parse", "$state", "ConfigService", "$cookies", "$i18next", "$sce", function(Authmethod, StateDataService, $parse, $state, ConfigService, $cookies, $i18next, $sce) {
- return {
- restrict: "AE",
- scope: !0,
- link: function(scope, element, attrs) {
- var autheventid = attrs.eventId;
- scope.dnieurl = ConfigService.dnieUrl + autheventid + "/", scope.register = {},
- scope.sendingData = !1, scope.admin = !1, scope.error = null, scope.email = null,
- attrs.email && 0 < attrs.email.length && (scope.email = attrs.email), "admin" in attrs && (scope.admin = !0),
- scope.getLoginDetails = function(eventId) {
- return scope.admin ? {
- path: "admin.login_email",
- data: {
- email: scope.email
+ return;
+ }
+ );
+ }
+
+ processOpenIdAuthCallback();
+ }
+ return {
+ restrict: 'AE',
+ scope: true,
+ link: link,
+ templateUrl: 'avRegistration/openid-connect-directive/openid-connect-directive.html'
+ };
+ });
+
+/**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+angular.module('avRegistration').controller('LogoutController',
+ function($scope, $stateParams, $filter, ConfigService, $i18next, $state, $cookies, Authmethod) {
+ var adminId = ConfigService.freeAuthId;
+ var authevent = Authmethod.getAuthevent();
+ var postfix = "_authevent_" + authevent;
+ $cookies.put("user" + postfix, '');
+ $cookies.put("auth" + postfix, '');
+ $cookies.put("authevent_" + authevent, '');
+ $cookies.put("userid" + postfix, '');
+ $cookies.put("isAdmin" + postfix, false);
+ if (authevent === ConfigService.freeAuthId + '' || !authevent) {
+ $state.go("admin.login");
+ } else {
+ $state.go("registration.login", {id: $cookies.get("authevent_" + authevent)});
+ }
+ }
+);
+
+/**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+angular.module('avRegistration').controller('RegisterController',
+ function($scope, $stateParams, $filter, ConfigService, $i18next) {
+ $scope.event_id = $stateParams.id;
+ $scope.email = $stateParams.email;
+ }
+);
+
+/**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+angular.module('avRegistration')
+ .directive('avRegister', function(Authmethod, StateDataService, $parse, $state, ConfigService, $cookies, $i18next, $sce) {
+ // we use it as something similar to a controller here
+ function link(scope, element, attrs) {
+ var autheventid = attrs.eventId;
+ scope.dnieurl = ConfigService.dnieUrl + autheventid + '/';
+ scope.register = {};
+ scope.sendingData = false;
+ scope.admin = false;
+ scope.error = null;
+
+ scope.email = null;
+ if (attrs.email && attrs.email.length > 0) {
+ scope.email = attrs.email;
+ }
+
+ if ("admin" in attrs) {
+ scope.admin = true;
+ }
+
+ scope.getLoginDetails = function (eventId) {
+ if (!scope.admin) {
+ return {
+ path: 'election.public.show.login_email',
+ data: {id: eventId, email: scope.email}
+ };
+ } else {
+ return {path: 'admin.login_email', data:{email: scope.email}};
+ }
+ };
+
+ scope.signUp = function(valid) {
+ if (!valid) {
+ return;
+ }
+ scope.sendingData = true;
+ scope.error = null;
+ var data = {
+ 'captcha_code': Authmethod.captcha_code,
+ };
+ _.each(scope.register_fields, function (field) {
+ data[field.name] = field.value;
+ if (field.name === 'email' && _.contains(['email', 'email-otp'], scope.method))
+ {
+ scope.email = field.value;
+ }
+ else if (field.name === 'tlf' &&
+ _.contains(['sms', 'sms-otp'], scope.method))
+ {
+ scope.email = field.value;
+ }
+ });
+ var details;
+ Authmethod.signup(data, autheventid)
+ .then(
+ function onSuccess(response) {
+ details = scope.getLoginDetails(autheventid);
+ if (response.data.status === "ok") {
+ scope.user = response.data.user;
+ StateDataService.go(details.path, details.data, data);
+ scope.error = response.data.msg || $sce.trustAsHtml($i18next('avRegistration.invalidRegisterData', {
+ url: $state.href(details.path, details.data)
+ }));
+ } else {
+ scope.sendingData = false;
+ scope.status = 'Not found';
+ scope.error = response.data.msg || $sce.trustAsHtml($i18next('avRegistration.invalidRegisterData', {
+ url: $state.href(details.path, details.data)
+ }));
}
- } : {
- path: "election.public.show.login_email",
- data: {
- id: eventId,
- email: scope.email
+ },
+ function onError(response) {
+ details = scope.getLoginDetails(autheventid);
+ scope.sendingData = false;
+ scope.status = 'Registration error: ' + response.data.message;
+
+ if (!!response.data.error_codename && response.data.error_codename === 'invalid-dni') {
+ scope.error = $sce.trustAsHtml($i18next('avRegistration.invalidRegisterDNI'));
+ } else {
+ scope.error = response.data.msg || $sce.trustAsHtml($i18next('avRegistration.invalidRegisterData', {
+ url: $state.href(details.path, details.data)
+ }));
+ if (response.data.msg === 'Invalid captcha') {
+ Authmethod.newCaptcha();
+ }
}
- };
- }, scope.signUp = function(valid) {
- var data, details;
- valid && (scope.sendingData = !0, scope.error = null, data = {
- captcha_code: Authmethod.captcha_code
- }, _.each(scope.register_fields, function(field) {
- data[field.name] = field.value, ("email" === field.name && _.contains([ "email", "email-otp" ], scope.method) || "tlf" === field.name && _.contains([ "sms", "sms-otp" ], scope.method)) && (scope.email = field.value);
- }), Authmethod.signup(data, autheventid).then(function(response) {
- details = scope.getLoginDetails(autheventid), "ok" === response.data.status ? (scope.user = response.data.user,
- StateDataService.go(details.path, details.data, data)) : (scope.sendingData = !1,
- scope.status = "Not found"), scope.error = response.data.msg || $sce.trustAsHtml($i18next("avRegistration.invalidRegisterData", {
- url: $state.href(details.path, details.data)
- }));
- }, function(response) {
- details = scope.getLoginDetails(autheventid), scope.sendingData = !1, scope.status = "Registration error: " + response.data.message,
- response.data.error_codename && "invalid-dni" === response.data.error_codename ? scope.error = $sce.trustAsHtml($i18next("avRegistration.invalidRegisterDNI")) : (scope.error = response.data.msg || $sce.trustAsHtml($i18next("avRegistration.invalidRegisterData", {
- url: $state.href(details.path, details.data)
- })), "Invalid captcha" === response.data.msg && Authmethod.newCaptcha());
- }));
- }, scope.goLogin = function(event) {
- console.log("goLogin"), event && (event.preventDefault(), event.stopPropagation()),
- scope.authevent && (scope.authevent.id === ConfigService.freeAuthId ? $state.go("admin.login") : $state.go("election.public.show.login", {
- id: scope.authevent.id
- }));
- }, scope.apply = function(authevent) {
- scope.method = authevent.auth_method, scope.name = authevent.name, "open" === (scope.authevent = authevent).census && "openid-connect" !== scope.method || (authevent.id === ConfigService.freeAuthId ? $state.go("admin.login") : $state.go("election.public.show.login", {
- id: authevent.id
- })), scope.register_fields = Authmethod.getRegisterFields(authevent);
- _.map(scope.register_fields, function(el) {
- return el.value = null, el.disabled = !1, "email" === el.type && null !== scope.email && (el.value = scope.email,
- el.disabled = !0), el;
- });
- }, scope.view = function(id) {
- Authmethod.viewEvent(id).then(function(response) {
- "ok" === response.data.status ? scope.apply(response.data.events) : (scope.status = "Not found",
- document.querySelector(".input-error").style.display = "block");
- }, function(response) {
- scope.status = "Scan error: " + response.data.message, document.querySelector(".input-error").style.display = "block";
- });
- }, scope.view(autheventid);
- },
- templateUrl: "avRegistration/register-directive/register-directive.html"
+ }
+ );
+ };
+
+ scope.goLogin = function(event) {
+ console.log("goLogin");
+ if (event) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+
+ if (!scope.authevent) {
+ return;
+ }
+
+ if (scope.authevent['id'] === ConfigService.freeAuthId) {
+ $state.go("admin.login");
+ } else {
+ $state.go("election.public.show.login", {id: scope.authevent['id']});
+ }
+ };
+
+ scope.apply = function(authevent) {
+ scope.method = authevent['auth_method'];
+ scope.name = authevent['name'];
+ scope.authevent = authevent;
+
+ // if registration is closed, redirect to login
+ if (authevent['census'] !== 'open' || scope.method === 'openid-connect') {
+ if (authevent['id'] === ConfigService.freeAuthId) {
+ $state.go("admin.login");
+ } else {
+ $state.go("election.public.show.login", {id: authevent['id']});
+ }
+ }
+ scope.register_fields = Authmethod.getRegisterFields(authevent);
+ var fields = _.map(
+ scope.register_fields,
+ function (el) {
+ el.value = null;
+ el.disabled = false;
+ if (el.type === "email" && scope.email !== null) {
+ el.value = scope.email;
+ el.disabled = true;
+ }
+ return el;
+ });
+ };
+
+ scope.view = function(id) {
+ Authmethod.viewEvent(id)
+ .then(
+ function onSuccess(response) {
+ if (response.data.status === "ok") {
+ scope.apply(response.data.events);
+ } else {
+ scope.status = 'Not found';
+ document.querySelector(".input-error").style.display = "block";
+ }
+ },
+ function onError(response) {
+ scope.status = 'Scan error: ' + response.data.message;
+ document.querySelector(".input-error").style.display = "block";
+ }
+ );
+ };
+
+ scope.view(autheventid);
+ }
+
+ return {
+ restrict: 'AE',
+ scope: true,
+ link: link,
+ templateUrl: 'avRegistration/register-directive/register-directive.html'
+ };
+ });
+
+/**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+angular.module('avRegistration')
+ .factory('Patterns', function() {
+ var patterns = {};
+ patterns.get = function(name) {
+ if (name === 'dni') {
+ return /^\d{7,8}[a-zA-Z]{1}$/i;
+ } else if (name === 'mail' || name === 'email') {
+ return /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
+ } else {
+ return /.*/;
+ }
+ };
+ return patterns;
+ });
+
+/**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+/**
+ * Shows a field
+ */
+angular.module('avRegistration')
+ .directive('avrField', function($state) {
+ function link(scope, element, attrs) {
+ console.log("type = " + scope.field.type);
+ scope.index = attrs.index;
+ }
+
+ return {
+ restrict: 'AE',
+ scope: true,
+ link: link,
+ templateUrl: 'avRegistration/field-directive/field-directive.html'
+ };
+ });
+
+/**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+angular.module('avRegistration')
+ .directive('avrEmailField', function($state, Patterns) {
+ function link(scope, element, attrs) {
+ scope.emailRe = Patterns.get('email');
+ }
+ return {
+ restrict: 'AE',
+ link: link,
+ scope: true,
+ templateUrl: 'avRegistration/fields/email-field-directive/email-field-directive.html'
+ };
+ });
+
+/**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+angular.module('avRegistration')
+ .directive('avrDateField', function($state, Patterns) {
+ function link(scope, element, attrs) {
+ scope.years = [];
+ scope.months = [];
+ scope.field = scope.$parent.field;
+ scope.date = null;
+
+ function initializeValue() {
+ var dateValue = null;
+ if (
+ scope.field.value === null || scope.field.value.length === 0
+ ) {
+ dateValue = new Date();
+ } else {
+ var data = scope.field.value.split('-');
+ dateValue = new Date(data[0], parseInt(data[1]) - 1, data[2]);
+ }
+ scope.date = {
+ year: dateValue.getFullYear(),
+ month: dateValue.getMonth() + 1,
+ day: dateValue.getDate()
+ };
+ }
+ initializeValue();
+
+ scope.getYears = function () {
+ var initY = (new Date()).getFullYear();
+ var i = 0;
+ var years = [];
+
+ for (i=initY; i>=initY-130; i--) {
+ years.push(i);
+ }
+ return years;
+ };
+
+ scope.getMonths = function () {
+ var i = 0;
+ var months = [];
+
+ for (i=1; i<=12; i++) {
+ months.push(i);
+ }
+ return months;
+ };
+
+ scope.getDays = function() {
+ var days = [];
+ var i = 0;
+ var ndays = (new Date(scope.date.year, scope.date.month, 0)).getDate();
+ for (i=1; i<=ndays; i++) {
+ days.push(i);
+ }
+ return days;
+ };
+
+ function numberPadStart(num, size) {
+ var str = "000000000" + num;
+ return str.substr(str.length - size);
+ }
+
+ scope.onChange = function() {
+ var monthStr = numberPadStart(scope.date.month, 2);
+ var dayStr = numberPadStart(scope.date.day, 2);
+ scope.field.value = scope.date.year + "-" + monthStr + "-" + dayStr;
+ };
+
+ // initial value update
+ scope.onChange();
+ }
+ return {
+ restrict: 'AE',
+ link: link,
+ scope: {
+ label: '=',
+ },
+ templateUrl: 'avRegistration/fields/date-field-directive/date-field-directive.html'
+ };
+ });
+
+/**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+angular.module('avRegistration')
+ .directive('avrPasswordField', function($state) {
+ return {
+ restrict: 'AE',
+ scope: true,
+ templateUrl: 'avRegistration/fields/password-field-directive/password-field-directive.html'
+ };
+});
+angular.module('avRegistration')
+ .directive('avrTextField', function($state) {
+ function link(scope, element, attrs) {
+ if (angular.isUndefined(scope.field.regex)) {
+ scope.re = new RegExp("");
+ } else {
+ scope.re = new RegExp(scope.field.regex);
+ }
+ }
+ return {
+ restrict: 'AE',
+ link: link,
+ scope: true,
+ templateUrl: 'avRegistration/fields/text-field-directive/text-field-directive.html'
};
-} ]), angular.module("avRegistration").factory("Patterns", function() {
- var patterns = {
- get: function(name) {
- return "dni" === name ? /^\d{7,8}[a-zA-Z]{1}$/i : "mail" === name || "email" === name ? /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ : /.*/;
+ });
+
+/**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+angular.module('avRegistration')
+ .directive('avrDniField', function($state) {
+ function link(scope, element, attrs) {
+ var dni_re = /^([0-9]{1,8}[A-Z]|[LMXYZ][0-9]{1,7}[A-Z])$/;
+
+ /**
+ * Normalizes dnis, using uppercase, removing characters not allowed and
+ * left-side zeros
+ */
+ function normalize_dni(dni) {
+ if (!dni) {
+ return "";
+ }
+
+ var allowed_chars = "QWERTYUIOPASDFGHJKLZXCVBNM1234567890";
+ var dni2 = dni.toUpperCase();
+ var dni3 = "";
+ for (var i = 0; i < dni2.lenth; i++) {
+ var char = dni2[i];
+ if (allowed_chars.indexOf(char) >= 0) {
+ dni3 += char;
+ }
+ }
+ var numbers = "1234567890";
+ var last_char = "";
+ var dni4 = "";
+ for (var j = 0; j < dni3.lenth; j++) {
+ var char2 = dni3[j];
+ if ((last_char==="" || '1234567890'.indexOf(last_char) === -1) && char2 === '0') {
+ }
+ dni4 += char2;
+ last_char = char2;
+ }
+ return dni4;
+ }
+
+ // returns true if regex matches or if there's no regex
+ scope.validateDni = function(dni) {
+ var norm_dni = normalize_dni(dni);
+
+ if (!norm_dni.match(dni_re)) {
+ return true;
}
- };
- return patterns;
-}), angular.module("avRegistration").directive("avrField", [ "$state", function($state) {
+
+ var prefix = norm_dni.charAt(0);
+ var index = "LMXYZ".indexOf(prefix);
+ var niePrefix = 0;
+ if (index > -1) {
+ niePrefix = index;
+ norm_dni = norm_dni.substr(1);
+ if (prefix === 'Y') {
+ norm_dni = "1" + norm_dni;
+ } else if (prefix === 'Z') {
+ norm_dni = "2" + norm_dni;
+ }
+ }
+ var dni_letters = "TRWAGMYFPDXBNJZSQVHLCKE";
+ var letter = dni_letters.charAt( parseInt( norm_dni, 10 ) % 23 );
+ return letter === norm_dni.charAt(norm_dni.length - 1);
+ };
+ }
return {
- restrict: "AE",
- scope: !0,
- link: function(scope, element, attrs) {
- console.log("type = " + scope.field.type), scope.index = attrs.index;
- },
- templateUrl: "avRegistration/field-directive/field-directive.html"
+ restrict: 'AE',
+ link: link,
+ scope: true,
+ templateUrl: 'avRegistration/fields/dni-field-directive/dni-field-directive.html'
};
-} ]), angular.module("avRegistration").directive("avrEmailField", [ "$state", "Patterns", function($state, Patterns) {
+ });
+
+/**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+angular.module('avRegistration')
+ .directive('avrCodeField', function($state, Plugins) {
+ function link(scope, element, attrs) {
+ scope.codePattern = /[abcdefghjklmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789-]{8,9}/;
+ var rand_code = '' + _.random(1e12);
+ scope.code_id = 'input' + scope.index + rand_code;
+
+ scope.showResendAuthCode = function ()
+ {
+ var data = {showUserSendAuthCode: true};
+ Plugins.hook('hide-user-send-auth-code', data);
+ return data.showUserSendAuthCode;
+ };
+
+ // TODO: validate email for email-otp. For now, we just allow the resend
+ // button for that use-case
+ if (_.contains(['sms', 'sms-otp'], scope.method)) {
+ var telInput =
+ angular.element(document.getElementById('input' + scope.telIndex));
+ scope.isValidTel = telInput.intlTelInput("isValidNumber");
+ scope.$watch('telField.value',
+ function (newValue, oldValue) {
+ scope.isValidTel = telInput.intlTelInput("isValidNumber");
+ },
+ true);
+ }
+ }
return {
- restrict: "AE",
- link: function(scope, element, attrs) {
- scope.emailRe = Patterns.get("email");
- },
- scope: !0,
- templateUrl: "avRegistration/fields/email-field-directive/email-field-directive.html"
+ restrict: 'AE',
+ scope: true,
+ link: link,
+ templateUrl: 'avRegistration/fields/code-field-directive/code-field-directive.html'
};
-} ]), angular.module("avRegistration").directive("avrDateField", [ "$state", "Patterns", function($state, Patterns) {
- return {
- restrict: "AE",
- link: function(scope, element, attrs) {
- var data, dateValue;
- function numberPadStart(str, size) {
- str = "000000000" + str;
- return str.substr(str.length - size);
+ });
+
+/**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+angular.module('avRegistration')
+ .directive('avrTelField', function($state, $timeout) {
+ function link(scope, element, attrs) {
+
+ scope.tlfPattern = /^[+]?\d{9,14}$/;
+ scope.isValidNumber = true;
+
+ // lookup ip data and send callbacks when it is available
+
+ var ipData = null;
+ var ipCallbacks = [];
+ $.get('https://ipinfo.io', function() {}, "jsonp")
+ .always(function(resp) {
+ ipData = resp;
+ for (var i = 0; i < ipCallbacks.length; i++) {
+ ipCallbacks[i]();
+ }
+ });
+
+ $timeout(function() {
+ /* configure registration telephone phone number */
+ var telInput = angular.element(document.getElementById("input" + scope.index));
+ // initialise plugin
+ telInput.intlTelInput({
+ utilsScript: "election/utils.js",
+ separateDialCode: true,
+ initialCountry: "auto",
+ preferredCountries: ["es", "gb", "us"],
+ autoPlaceholder: "aggressive",
+ placeholderNumberType: "MOBILE",
+ geoIpLookup: function(callback) {
+ var applyCountry = function()
+ {
+ var countryCode = (ipData && ipData.country) ? ipData.country : "es";
+ callback(countryCode);
+ };
+ if (ipData) {
+ applyCountry();
+ } else {
+ ipCallbacks.push(applyCountry);
+ }
}
- scope.years = [], scope.months = [], scope.field = scope.$parent.field, scope.date = null,
- dateValue = (dateValue = null) === scope.field.value || 0 === scope.field.value.length ? new Date() : (data = scope.field.value.split("-"),
- new Date(data[0], parseInt(data[1]) - 1, data[2])), scope.date = {
- year: dateValue.getFullYear(),
- month: dateValue.getMonth() + 1,
- day: dateValue.getDate()
- }, scope.getYears = function() {
- for (var initY = new Date().getFullYear(), i = 0, years = [], i = initY; initY - 130 <= i; i--) years.push(i);
- return years;
- }, scope.getMonths = function() {
- for (var i = 0, months = [], i = 1; i <= 12; i++) months.push(i);
- return months;
- }, scope.getDays = function() {
- for (var days = [], i = 0, ndays = new Date(scope.date.year, scope.date.month, 0).getDate(), i = 1; i <= ndays; i++) days.push(i);
- return days;
- }, scope.onChange = function() {
- var monthStr = numberPadStart(scope.date.month, 2), dayStr = numberPadStart(scope.date.day, 2);
- scope.field.value = scope.date.year + "-" + monthStr + "-" + dayStr;
- }, scope.onChange();
- },
- scope: {
- label: "="
- },
- templateUrl: "avRegistration/fields/date-field-directive/date-field-directive.html"
- };
-} ]), angular.module("avRegistration").directive("avrPasswordField", [ "$state", function($state) {
- return {
- restrict: "AE",
- scope: !0,
- templateUrl: "avRegistration/fields/password-field-directive/password-field-directive.html"
- };
-} ]), angular.module("avRegistration").directive("avrTextField", [ "$state", function($state) {
+ });
+ if (_.isString(scope.field.value) && 0 < scope.field.value.length) {
+ telInput.intlTelInput("setNumber", scope.field.value);
+ }
+
+ var validateTel = function()
+ {
+ scope.$evalAsync(function() {
+ var intlNumber = telInput.intlTelInput("getNumber");
+ if (intlNumber) {
+ scope.field.value = intlNumber;
+ }
+ var isValid = telInput.intlTelInput("isValidNumber");
+ if (!isValid && $("#input"+ scope.index).val().replace("[ \t\n]", "").length > 0)
+ {
+ telInput.toggleClass("error", true);
+ scope.isValidNumber = false;
+ } else
+ {
+ telInput.toggleClass("error", false);
+ scope.isValidNumber = true;
+ }
+ });
+ };
+ // on keyup / change flag: reset
+ telInput.on("keyup change", validateTel);
+ });
+ }
return {
- restrict: "AE",
- link: function(scope, element, attrs) {
- angular.isUndefined(scope.field.regex) ? scope.re = new RegExp("") : scope.re = new RegExp(scope.field.regex);
- },
- scope: !0,
- templateUrl: "avRegistration/fields/text-field-directive/text-field-directive.html"
+ restrict: 'AE',
+ scope: true,
+ link: link,
+ templateUrl: 'avRegistration/fields/tel-field-directive/tel-field-directive.html'
};
-} ]), angular.module("avRegistration").directive("avrDniField", [ "$state", function($state) {
+ });
+
+/**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+angular.module('avRegistration')
+ .directive('avrBoolField', function($state) {
return {
- restrict: "AE",
- link: function(scope, element, attrs) {
- var dni_re = /^([0-9]{1,8}[A-Z]|[LMXYZ][0-9]{1,7}[A-Z])$/;
- scope.validateDni = function(index) {
- var norm_dni = function(dni) {
- if (!dni) return "";
- for (var dni2 = dni.toUpperCase(), dni3 = "", i = 0; i < dni2.lenth; i++) {
- var char = dni2[i];
- 0 <= "QWERTYUIOPASDFGHJKLZXCVBNM1234567890".indexOf(char) && (dni3 += char);
- }
- for (var last_char = "", dni4 = "", j = 0; j < dni3.lenth; j++) {
- var char2 = dni3[j];
- "" === last_char || "1234567890".indexOf(last_char), dni4 += char2, last_char = char2;
- }
- return dni4;
- }(index);
- if (!norm_dni.match(dni_re)) return !0;
- var prefix = norm_dni.charAt(0), index = "LMXYZ".indexOf(prefix);
- -1 < index && (norm_dni = norm_dni.substr(1), "Y" === prefix ? norm_dni = "1" + norm_dni : "Z" === prefix && (norm_dni = "2" + norm_dni));
- return "TRWAGMYFPDXBNJZSQVHLCKE".charAt(parseInt(norm_dni, 10) % 23) === norm_dni.charAt(norm_dni.length - 1);
- };
- },
- scope: !0,
- templateUrl: "avRegistration/fields/dni-field-directive/dni-field-directive.html"
+ restrict: 'AE',
+ scope: true,
+ templateUrl: 'avRegistration/fields/bool-field-directive/bool-field-directive.html'
};
-} ]), angular.module("avRegistration").directive("avrCodeField", [ "$state", "Plugins", function($state, Plugins) {
+ });
+
+/**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+angular.module('avRegistration')
+ .directive('avrIntField', function($state) {
+ function link(scope, element, attrs) {
+ if (angular.isUndefined(scope.field.regex)) {
+ scope.re = new RegExp("");
+ } else {
+ scope.re = new RegExp(scope.field.regex);
+ }
+ }
return {
- restrict: "AE",
- scope: !0,
- link: function(scope, element, attrs) {
- scope.codePattern = /[abcdefghjklmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789-]{8,9}/;
- var telInput, rand_code = "" + _.random(1e12);
- scope.code_id = "input" + scope.index + rand_code, scope.showResendAuthCode = function() {
- var data = {
- showUserSendAuthCode: !0
- };
- return Plugins.hook("hide-user-send-auth-code", data), data.showUserSendAuthCode;
- }, _.contains([ "sms", "sms-otp" ], scope.method) && (telInput = angular.element(document.getElementById("input" + scope.telIndex)),
- scope.isValidTel = telInput.intlTelInput("isValidNumber"), scope.$watch("telField.value", function(newValue, oldValue) {
- scope.isValidTel = telInput.intlTelInput("isValidNumber");
- }, !0));
- },
- templateUrl: "avRegistration/fields/code-field-directive/code-field-directive.html"
+ restrict: 'AE',
+ link: link,
+ scope: true,
+ templateUrl: 'avRegistration/fields/int-field-directive/int-field-directive.html'
};
-} ]), angular.module("avRegistration").directive("avrTelField", [ "$state", "$timeout", function($state, $timeout) {
+ });
+
+/**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+angular.module('avRegistration')
+ .directive('avrCaptchaField', ['Authmethod', '$state', '$interval', function(Authmethod, $state, $interval) {
+ function link(scope, element, attrs) {
+ var timeoutId = null;
+
+ scope.authMethod = Authmethod;
+ Authmethod.newCaptcha("");
+ }
+
return {
- restrict: "AE",
- scope: !0,
- link: function(scope, element, attrs) {
- scope.tlfPattern = /^[+]?\d{9,14}$/, scope.isValidNumber = !0;
- var ipData = null, ipCallbacks = [];
- $.get("https://ipinfo.io", function() {}, "jsonp").always(function(resp) {
- ipData = resp;
- for (var i = 0; i < ipCallbacks.length; i++) ipCallbacks[i]();
- }), $timeout(function() {
- var telInput = angular.element(document.getElementById("input" + scope.index));
- telInput.intlTelInput({
- utilsScript: "election/utils.js",
- separateDialCode: !0,
- initialCountry: "auto",
- preferredCountries: [ "es", "gb", "us" ],
- autoPlaceholder: "aggressive",
- placeholderNumberType: "MOBILE",
- geoIpLookup: function(callback) {
- function applyCountry() {
- var countryCode = ipData && ipData.country ? ipData.country : "es";
- callback(countryCode);
- }
- ipData ? applyCountry() : ipCallbacks.push(applyCountry);
- }
- }), _.isString(scope.field.value) && 0 < scope.field.value.length && telInput.intlTelInput("setNumber", scope.field.value);
- telInput.on("keyup change", function() {
- scope.$evalAsync(function() {
- var intlNumber = telInput.intlTelInput("getNumber");
- intlNumber && (scope.field.value = intlNumber), !telInput.intlTelInput("isValidNumber") && 0 < $("#input" + scope.index).val().replace("[ \t\n]", "").length ? (telInput.toggleClass("error", !0),
- scope.isValidNumber = !1) : (telInput.toggleClass("error", !1), scope.isValidNumber = !0);
- });
- });
- });
- },
- templateUrl: "avRegistration/fields/tel-field-directive/tel-field-directive.html"
+ restrict: 'AE',
+ scope: true,
+ link: link,
+ templateUrl: 'avRegistration/fields/captcha-field-directive/captcha-field-directive.html'
};
-} ]), angular.module("avRegistration").directive("avrBoolField", [ "$state", function($state) {
+ }]);
+
+/**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+angular.module('avRegistration')
+ .directive('avrTextareaField', function($state) {
return {
- restrict: "AE",
- scope: !0,
- templateUrl: "avRegistration/fields/bool-field-directive/bool-field-directive.html"
+ restrict: 'AE',
+ scope: true,
+ templateUrl: 'avRegistration/fields/textarea-field-directive/textarea-field-directive.html'
};
-} ]), angular.module("avRegistration").directive("avrIntField", [ "$state", function($state) {
+ });
+
+/**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+angular.module('avRegistration')
+ .directive('avrImageField', function($state, $timeout) {
+ function link(scope, element, attrs) {
+ function readImage(input) {
+ if ( input.files && input.files[0] ) {
+ var FR = new FileReader();
+ FR.onload = function(e) {
+ scope.field.value = e.target.result;
+ };
+ FR.readAsDataURL( input.files[0] );
+ }
+ }
+
+ $timeout(function() {
+ $("#image-field").change(function() { readImage( this ); });
+ }, 0);
+ }
+
return {
- restrict: "AE",
- link: function(scope, element, attrs) {
- angular.isUndefined(scope.field.regex) ? scope.re = new RegExp("") : scope.re = new RegExp(scope.field.regex);
- },
- scope: !0,
- templateUrl: "avRegistration/fields/int-field-directive/int-field-directive.html"
+ restrict: 'AE',
+ link: link,
+ scope: true,
+ templateUrl: 'avRegistration/fields/image-field-directive/image-field-directive.html'
};
-} ]), angular.module("avRegistration").directive("avrCaptchaField", [ "Authmethod", "$state", "$interval", function(Authmethod, $state, $interval) {
- return {
- restrict: "AE",
- scope: !0,
- link: function(scope, element, attrs) {
- (scope.authMethod = Authmethod).newCaptcha("");
+ });
+
+/**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+/**
+ * @description Service that manages the Plugins extension points.
+ *
+ * These are the hooks called by agora-gui-admin:
+ *
+ * - Name: election-modified
+ *
+ * Description: called by @a ElectionsApi.setCurrent service before the new
+ * election is set.
+ *
+ * Input data: {
+ * // old election object (before setCurrent was called)
+ * "old": Election,
+ *
+ * // old new election object that is going to be set
+ * "el": Election
+ * }
+ *
+ * - Name: send-auth-codes-steps
+ *
+ * Description: called by @a SendMsg.calculateSteps service before calculating
+ * the number of steps of the send authentication codes dialog. It's a good
+ * way of modifying @a SendMsg.steps.extra.
+ *
+ * Input data: {
+ * // current election object
+ * "el": Election,
+ *
+ * // ids of the electorate to which the authentication message is going
+ * // to be set. Might be null if it's all the electorate.
+ * "user_ids": List[Integer]
+ * }
+ *
+ * - Name: send-auth-codes-confirm-extra
+ *
+ * Description: called by @a SendMsg.confirmAuthCodesModal service before
+ * showing the @a SendAuthCodesModalConfirm window when sending authentication
+ * codes to the electorate. This hook allows to set some html to be shown in
+ * the modal window. Note that the html will not be trusted unless you
+ * explicitly make it trusted with @a $sce.
+ *
+ * Input data: {
+ * // modifiable list of html strings to shown in the modal confirm window.
+ * // starts empty, but other hook handlers might modify it. It's used as
+ * // the hook's output.
+ * "html": []
+ * }
+ *
+ * - Name: send-auth-codes-confirm-close
+ *
+ * Description: Called by @a .confirmAuthCodesModal service after
+ * closing the @a SendAuthCodesModalConfirm window to process the result of
+ * the modal (this result is the input of the hook) and decide what to do.
+ *
+ * Input data: string
+ *
+ * - Name: send-auth-codes-pre
+ *
+ * Description: Called by @a SendMsg.sendAuthCodes before sending auth codes.
+ * Used to decide whether or not to send them - if any hook handler returns
+ * a value interpretable as false, won't send it.
+ *
+ * Input data: {
+ * // current election object
+ * "el": Election,
+ *
+ * // ids of the electorate to which the authentication message is going
+ * // to be set. Might be null if it's all the electorate.
+ * "user_ids": List[Integer]
+ * }
+ *
+ * - Name: send-auth-codes-success
+ *
+ * Description: Called by @a SendMsg.sendAuthCodes after sending auth codes
+ * when the sending was successful.
+ *
+ * Input data: {
+ * // current election object
+ * "el": Election,
+ *
+ * // ids of the electorate to which the authentication message is going
+ * // to be set. Might be null if it's all the electorate.
+ * "ids": List[Integer]
+ *
+ * // response object from jquery
+ * "response": ResponseObject
+ * }
+ *
+ * - Name: send-auth-codes-error
+ *
+ * Description: Called by @a SendMsg.sendAuthCodes after sending auth codes
+ * when the sending had an error.
+ *
+ * Input data: {
+ * // current election object
+ * "el": Election,
+ *
+ * // ids of the electorate to which the authentication message is going
+ * // to be set. Might be null if it's all the electorate.
+ * "ids": List[Integer]
+ *
+ * // response object from jquery
+ * "response": ResponseObject
+ *
+ * - Name: add-to-census-pre
+ *
+ * Description: Called by @a avAdminElcensus.censusCall just before adding
+ * some electors to the election. A hook handler can cancel the add to census
+ * action return a value interpretable as false.
+ *
+ * // List of electors that are about to be added
+ * Input data: List[NewElectorMetadata]
+ *
+ * - Name: add-to-census-success
+ *
+ * Description: Called by @a avAdminElcensus.censusCall after adding
+ * some electors to the election when the call to the API was successful.
+ * Allows the hook handler process the api result.
+ *
+ * Input data: {
+ * // List of electors that are about to be added
+ * "data": List[NewElectorMetadata],
+ *
+ * // response object from jquery
+ * "response": ResponseObject
+ * }
+ *
+ * - Name: add-to-census-error
+ *
+ * Description: Called by @a avAdminElcensus.censusCall after adding
+ * some electors to the election when the call to the api produced an error.
+ * Allows the hook handler process the api result.
+ *
+ * Input data: {
+ * // List of electors that are about to be added
+ * "data": List[NewElectorMetadata],
+ *
+ * // response object from jquery
+ * "response": ResponseObject
+ * }
+ */
+angular.module('avRegistration')
+ .factory('Plugins', function() {
+ var plugins = {};
+ // TODO: What are plugins used for exactly? Please explain
+ plugins.plugins = {list: []};
+
+ // Signal storage
+ plugins.signals = $.Callbacks("unique");
+
+ /**
+ * List of hooks handlers.
+ *
+ * A hook is a point of extension. Each time @a Plugins.hook()
+ * is called, all the hooks are called with the arguments given and in
+ * list order, so that they can process the hook.
+ *
+ * To insert/delete/list hook handlers, access directly to
+ * @a Plugins.hooks.
+ *
+ * Each hook handler is a function that receives two arguments:
+ * - hookname
+ * - data
+ *
+ * A hook handler should return a value interpretable as a false
+ * expression if it wants no other hook to process the call, or
+ * anything else otherwise.
+ *
+ * Example hook handler:
+ *
+ *
+ * var fooHookHandler = function(hookname, data) {
+ * if (hookname === "foo") {
+ * processFoo(data);
+ * return false;
+ * }
+ *
+ * return true;
+ * };
+ *
+ * // add the handler
+ * Plugins.hooks.push(fooHookHandler);
+ *
+ */
+ plugins.hooks = [];
+
+ /*
+ * Adds a plugin.
+ *
+ * plugin format:
+ * {
+ * name: 'test',
+ * directive: 'test', (optional, only if this link has a directive)
+ * head: true | false,
+ * link: ui-sref link,
+ * menu: html() | {icon: icon, text: text}
+ * }
+ */
+ plugins.add = function(plugin) {
+ plugins.plugins.list.push(plugin);
+ };
+
+ /*
+ * Clears the plugins list.
+ */
+ plugins.clear = function() {
+ plugins.plugins.list = [];
+ };
+
+ /**
+ * Remove a plugin from the list.
+ */
+ plugins.remove = function(plugin) {
+ // Implemented by creating a new list without the plugin of that
+ // name
+ var pluginList = plugins.plugins.list;
+ plugins.plugins.list = [];
+ pluginList.forEach(function(pluginFromList) {
+ if (plugin.name !== pluginFromList.name) {
+ plugins.plugins.list.push(pluginFromList);
+ }
+ });
+ };
+
+ /**
+ * Emits a signal by name.
+ *
+ * @data can be any object or even null.
+ */
+ plugins.emit = function(signalName, data) {
+ plugins.signals.fire(signalName, data);
+ };
+
+ /**
+ * Calls to a hook by name.
+ *
+ * Each function stored as a hook is called with the provided
+ * @a hookname and @a data in the hook insertion order. When a hook
+ * returns a value interpretable as false, no more hooks are called.
+ *
+ * @a data can be any object or even null.
+ * @a hookname should be a string.
+ *
+ * @returns false if any of the hooks returns false, or true otherwise.
+ */
+ plugins.hook = function(hookname, data) {
+ for (var i=0; i
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+/*
+ * The avUi module contains a series of user interface directives and utilities.
+ */
+
+angular.module('avUi', []);
+
+/**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+jQuery.fn.flash = function(duration) {
+ var selector = this;
+
+ if (!angular.isNumber(duration)) {
+ duration = 300;
+ }
+
+ if (selector.attr("is-flashing") === "true") {
+ return;
+ }
+
+ selector.attr("is-flashing", "true");
+
+ selector
+ .addClass("flashing")
+ .delay(duration)
+ .queue(function() {
+ selector.removeClass("flashing").addClass("flashing-out").dequeue();
+ })
+ .delay(duration)
+ .queue(function() {
+ selector.removeClass("flashing flashing-out").dequeue();
+ selector.attr("is-flashing", "false");
+ });
+};
+/**
+ * This file is part of agora-gui-admin.
+ * Copyright (C) 2020 Agora Voting SL
+
+ * agora-gui-admin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-admin is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-admin. If not, see .
+**/
+
+angular.module('avUi')
+ .directive(
+ 'avChildrenElections',
+ function(ConfigService)
+ {
+ // we use it as something similar to a controller here
+ function link(scope, element, attrs)
+ {
+ scope.electionsById = {};
+ scope.selectedElectionId = scope.parentElectionId;
+
+ // process each election
+ _.each(
+ scope.childrenElectionInfo.presentation.categories,
+ function (category)
+ {
+ _.each(
+ category.events,
+ function (election)
+ {
+ if (
+ scope.mode === 'checkbox' ||
+ scope.mode === 'toggle-and-callback'
+ )
+ {
+ election.data = election.data || false;
+ election.disabled = election.disabled || false;
+ }
+ }
+ );
+ }
+ );
+
+ // add a processElection function
+ scope.click = function (election)
+ {
+ console.log("click to election.event_id = " + election.event_id);
+ if (scope.mode === 'checkbox')
+ {
+ election.data = !election.data;
+ }
+ else if (scope.mode === 'toggle-and-callback')
+ {
+ scope.selectedElectionId = election.event_id;
+ scope.callback({electionId: election.event_id});
+ }
+ };
+ }
+
+ return {
+ restrict: 'AE',
+ scope: {
+ mode: '@',
+ callback: '&?',
+ parentElectionId: '@?',
+ childrenElectionInfo: '='
},
- scope: !0,
- templateUrl: "avRegistration/fields/image-field-directive/image-field-directive.html"
- };
-} ]), angular.module("avRegistration").factory("Plugins", function() {
- var plugins = {
- plugins: {
- list: []
+ link: link,
+ templateUrl: 'avUi/children-elections-directive/children-elections-directive.html'
+ };
+ }
+ );
+
+/**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+/*
+ * Simple error directive.
+ */
+angular.module('avUi')
+ .directive('avSimpleError', function($resource, $window) {
+ function link(scope, element, attrs) {
+ // moves the title on top of the busy indicator
+ scope.updateTitle = function() {
+ var title = element.find(".av-simple-error-title");
+
+ // set margin-top
+ var marginTop = - title.height() - 45;
+ var marginLeft = - title.width()/2;
+ title.attr("style", "margin-top: " + marginTop + "px; margin-left: " + marginLeft + "px");
+ };
+
+ scope.$watch(attrs.title,
+ function() {
+ scope.updateTitle();
}
- };
- return plugins.signals = $.Callbacks("unique"), plugins.hooks = [], plugins.add = function(plugin) {
- plugins.plugins.list.push(plugin);
- }, plugins.clear = function() {
- plugins.plugins.list = [];
- }, plugins.remove = function(plugin) {
- var pluginList = plugins.plugins.list;
- plugins.plugins.list = [], pluginList.forEach(function(pluginFromList) {
- plugin.name !== pluginFromList.name && plugins.plugins.list.push(pluginFromList);
- });
- }, plugins.emit = function(signalName, data) {
- plugins.signals.fire(signalName, data);
- }, plugins.hook = function(hookname, data) {
- for (var i = 0; i < plugins.hooks.length; i++) if (!(0, plugins.hooks[i])(hookname, data)) return !1;
- return !0;
- }, plugins;
-}), angular.module("avRegistration").directive("avPluginHtml", [ "$compile", "$sce", "$parse", function($compile, $sce, $parse) {
- return function(scope, element, attrs) {
- var parsedHtml = $parse(attrs.ngBindHtml);
- scope.$watch(function() {
- return (parsedHtml(scope) || "").toString();
- }, function() {
- $compile(element, null, -9999)(scope);
- });
- };
-} ]), angular.module("avUi", []), jQuery.fn.flash = function(duration) {
- var selector = this;
- angular.isNumber(duration) || (duration = 300), "true" !== selector.attr("is-flashing") && (selector.attr("is-flashing", "true"),
- selector.addClass("flashing").delay(duration).queue(function() {
- selector.removeClass("flashing").addClass("flashing-out").dequeue();
- }).delay(duration).queue(function() {
- selector.removeClass("flashing flashing-out").dequeue(), selector.attr("is-flashing", "false");
- }));
-}, angular.module("avUi").directive("avChildrenElections", [ "ConfigService", function(ConfigService) {
+ );
+ }
return {
- restrict: "AE",
- scope: {
- mode: "@",
- callback: "&?",
- parentElectionId: "@?",
- childrenElectionInfo: "="
- },
- link: function(scope, element, attrs) {
- scope.electionsById = {}, scope.selectedElectionId = scope.parentElectionId, _.each(scope.childrenElectionInfo.presentation.categories, function(category) {
- _.each(category.events, function(election) {
- "checkbox" !== scope.mode && "toggle-and-callback" !== scope.mode || (election.data = election.data || !1,
- election.disabled = election.disabled || !1);
- });
- }), scope.click = function(election) {
- console.log("click to election.event_id = " + election.event_id), "checkbox" === scope.mode ? election.data = !election.data : "toggle-and-callback" === scope.mode && (scope.selectedElectionId = election.event_id,
- scope.callback({
- electionId: election.event_id
- }));
- };
- },
- templateUrl: "avUi/children-elections-directive/children-elections-directive.html"
+ restrict: 'AE',
+ scope: {},
+ link: link,
+ transclude: true,
+ templateUrl: 'avUi/simple-error-directive/simple-error-directive.html'
};
-} ]), angular.module("avUi").directive("avSimpleError", [ "$resource", "$window", function($resource, $window) {
+ });
+
+/**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+/*
+ * Simple change lang directive, that can be used in the navbar as a list
+ * element:
+ *
+ */
+angular.module('avUi')
+ .directive('avChangeLang', function($i18next, ipCookie, angularLoad, amMoment, ConfigService) {
+ function link(scope, element, attrs) {
+ scope.deflang = window.i18n.lng();
+ angular.element('#ng-app').attr('lang', scope.deflang);
+ scope.langs = $i18next.options.lngWhitelist;
+
+ // Changes i18n to a specific language, setting also a cookie for
+ // remembering it, and updating all the translations instantly.
+ //
+ // Triggered when the user clicks and selects a language.
+ scope.changeLang = function(lang) {
+ $i18next.options.lng = lang;
+ console.log("setting cookie");
+ var cookieConf = {
+ expires: 360,
+ path: "/"
+ };
+ ipCookie(
+ "lang",
+ lang,
+ _.extend(cookieConf, ConfigService.i18nextCookieOptions));
+ scope.deflang = lang;
+ angular.element('#ng-app').attr('lang', scope.deflang);
+
+ // async load moment i18n
+ angularLoad
+ .loadScript(ConfigService.base + '/locales/moment/' + lang + '.js')
+ .then(function () {
+ amMoment.changeLocale(lang);
+ });
+ };
+ }
+
return {
- restrict: "AE",
- scope: {},
- link: function(scope, element, attrs) {
- scope.updateTitle = function() {
- var title = element.find(".av-simple-error-title"), marginTop = -title.height() - 45, marginLeft = -title.width() / 2;
- title.attr("style", "margin-top: " + marginTop + "px; margin-left: " + marginLeft + "px");
- }, scope.$watch(attrs.title, function() {
- scope.updateTitle();
- });
- },
- transclude: !0,
- templateUrl: "avUi/simple-error-directive/simple-error-directive.html"
+ restrict: 'AE',
+ scope: {},
+ link: link,
+ templateUrl: 'avUi/change-lang-directive/change-lang-directive.html'
};
-} ]), angular.module("avUi").directive("avChangeLang", [ "$i18next", "ipCookie", "angularLoad", "amMoment", "ConfigService", function($i18next, ipCookie, angularLoad, amMoment, ConfigService) {
- return {
- restrict: "AE",
- scope: {},
- link: function(scope, element, attrs) {
- scope.deflang = window.i18n.lng(), angular.element("#ng-app").attr("lang", scope.deflang),
- scope.langs = $i18next.options.lngWhitelist, scope.changeLang = function(lang) {
- $i18next.options.lng = lang, console.log("setting cookie");
- ipCookie("lang", lang, _.extend({
- expires: 360,
- path: "/"
- }, ConfigService.i18nextCookieOptions)), scope.deflang = lang, angular.element("#ng-app").attr("lang", scope.deflang),
- angularLoad.loadScript(ConfigService.base + "/locales/moment/" + lang + ".js").then(function() {
- amMoment.changeLocale(lang);
- });
- };
- },
- templateUrl: "avUi/change-lang-directive/change-lang-directive.html"
+ });
+
+/**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+/*
+ * directive used to position an element always at the bottom, so that it's
+ * always shown completely. There are two scenarios:
+ * a) if the page has no scroll, we assume the element is shown, and do nothing
+ * b) if the page has scroll, the bottom of the page is not completely (or at
+ * all) being shown, so we set the affixed element the class affix-bottom and
+ * make space for it giving some bottom margin in its parent element.
+ *
+ * As an optional trigger to the settings of the affix-bottom, you can also set
+ * the data-force-affix-width attribute in the affixed element to a number of
+ * pixels. If this attribute is set and the window width is less than this,
+ * automatically the element will be affixed.
+ */
+angular.module('avUi')
+ .directive('avAffixBottom', function($window, $timeout, $parse) {
+ var affixBottomClass = "affix-bottom";
+ var checkPosition = function(scope, instance, el, options) {
+
+ var affix = false;
+ var elHeight = $(el).actual('height');
+
+ if (($("body").height() + elHeight > window.innerHeight) ||
+ (instance.forceAffixWidth && window.innerWidth < instance.forceAffixWidth)) {
+ affix = affixBottomClass;
+ }
+
+ if (instance.affixed === affix) {
+ return;
+ }
+
+ instance.affix = affix;
+ instance.setIsAffix(scope, affix);
+ el.removeClass("hidden");
+
+ if (!affix) {
+ el.removeClass(affixBottomClass);
+ $(el).parent().css("margin-bottom", instance.defaultBottomMargin);
+ } else {
+ el.addClass(affixBottomClass);
+
+ // add bottom-margin automatically
+ $(el).parent().css("margin-bottom", elHeight + "px");
+ }
+
};
-} ]), angular.module("avUi").directive("avAffixBottom", [ "$window", "$timeout", "$parse", function($window, $timeout, $parse) {
+
return {
- restrict: "EAC",
- link: function(scope, iElement, iAttrs) {
- var timeout, instance = {
- affix: !1,
- getIsAffix: null,
- setIsAffix: angular.noop,
- defaultBottomMargin: iElement.css("margin-bottom"),
- forceAffixWidth: parseInt(iAttrs.forceAffixWidth, 10)
- };
- function callCheckPos() {
- timeout = $timeout(function() {
- $timeout.cancel(timeout), function(scope, instance, el) {
- var affix = !1, elHeight = $(el).actual("height");
- ($("body").height() + elHeight > window.innerHeight || instance.forceAffixWidth && window.innerWidth < instance.forceAffixWidth) && (affix = "affix-bottom"),
- instance.affixed !== affix && (instance.affix = affix, instance.setIsAffix(scope, affix),
- el.removeClass("hidden"), affix ? (el.addClass("affix-bottom"), $(el).parent().css("margin-bottom", elHeight + "px")) : (el.removeClass("affix-bottom"),
- $(el).parent().css("margin-bottom", instance.defaultBottomMargin)));
- }(scope, instance, iElement);
- }, 300);
- }
- 0 < iAttrs.avAffixBottom.length && (instance.getIsAffix = $parse(iAttrs.avAffixBottom),
- instance.setIsAffix = instance.getIsAffix.assign), callCheckPos(), angular.element($window).on("resize", callCheckPos),
- angular.element(document.body).on("resize", callCheckPos), console.log("iElement NOT resize, height = " + iElement.height()),
- angular.element(iElement).on("resize", callCheckPos);
+ restrict: 'EAC',
+ link: function(scope, iElement, iAttrs) {
+ // instance saves state between calls to checkPosition
+ var instance = {
+ affix: false,
+ getIsAffix: null,
+ setIsAffix: angular.noop,
+ defaultBottomMargin: iElement.css("margin-bottom"),
+ forceAffixWidth: parseInt(iAttrs.forceAffixWidth, 10)
+ };
+
+
+ if (iAttrs.avAffixBottom.length > 0) {
+ instance.getIsAffix = $parse(iAttrs.avAffixBottom);
+ instance.setIsAffix = instance.getIsAffix.assign;
+ }
+
+ // timeout is used with callCheckPos so that we do not create too many
+ // calls to checkPosition, at most one per 300ms
+ var timeout;
+
+ function callCheckPos() {
+ timeout = $timeout(function() {
+ $timeout.cancel(timeout);
+ checkPosition(scope, instance, iElement, iAttrs);
+ }, 300);
}
+ callCheckPos();
+
+ // watch for window resizes and element resizes too
+ angular.element($window).on('resize', callCheckPos);
+ angular.element(document.body).on('resize', callCheckPos);
+ console.log("iElement NOT resize, height = " + iElement.height());
+ angular.element(iElement).on('resize', callCheckPos);
+ }
};
-} ]), angular.module("avUi").directive("avAutoHeight", [ "$window", "$timeout", function($window, $timeout) {
+
+ });
+
+/**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+/**
+ * Usage:
+ *
+ *
+ *
I need some space, this is a sibling
+ *
+ * I stretch to the available height,
+ * calculated from the height available from .parent and my siblings.
+ *
+ *
+ */
+angular.module('avUi')
+ .directive('avAutoHeight', function($window, $timeout) {
return {
- link: function(scope, element, attrs) {
- var promise = null, sibling = function() {
- return element.closest(attrs.parentSelector).find(attrs.siblingSelector);
- }, recalculate = function() {
- promise && $timeout.cancel(promise), promise = $timeout(function() {
- var height, additionalHeight = 0;
- attrs.additionalHeight && (additionalHeight = parseInt(attrs.additionalHeight, 10)),
- height = sibling().height(), element.css("max-height", height + additionalHeight + "px");
- }, 300);
- };
- scope.$watch(function() {
- return sibling().height();
- }, function(newValue, oldValue) {
- recalculate();
- }), recalculate();
+ link: function(scope, element, attrs) {
+ var sibling, recalculate, promise = null;
+
+ sibling = function() {
+ return element.closest(attrs.parentSelector).find(attrs.siblingSelector);
+ };
+
+ recalculate = function () {
+ if (promise) {
+ $timeout.cancel(promise);
+ }
+ promise = $timeout(function() {
+ var additionalHeight = 0, height;
+ if (!!attrs.additionalHeight) {
+ additionalHeight = parseInt(attrs.additionalHeight, 10);
+ }
+ height = sibling().height();
+ element.css('max-height', (height + additionalHeight) + "px");
+ }, 300);
+ };
+
+ scope.$watch(
+ function () {
+ return sibling().height();
+ },
+ function (newValue, oldValue) {
+ recalculate();
+ });
+
+ recalculate();
+ }
+ };
+ }
+);
+/**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+angular.module('avUi')
+ .directive('avAffixTopOffset', function($window, $timeout, $parse) {
+ var affixClass = "affix-top";
+ var checkPosition = function(scope, instance, el, options) {
+
+ var affix = false;
+ var offset = el.offset();
+
+ if (instance.affix && $window.pageYOffset + 20 >= instance.scrollAffix) {
+ return;
+ } else if (offset.top - $window.pageYOffset < instance.avAffixTopOffset) {
+ affix = true;
+ }
+
+ if (instance.affix === affix) {
+ return;
+ }
+
+ instance.affix = affix;
+ instance.scrollAffix = $window.pageYOffset;
+ if (!affix) {
+ el.removeClass(affixClass);
+ el.attr("style", "");
+
+ if (options.affixPlaceholder !== undefined) {
+ $(options.affixPlaceholder).removeClass("affixed");
+ }
+ } else {
+ el.addClass(affixClass);
+ el.data("page-offset", $window.pageYOffset);
+ el.css("position", "fixed");
+ el.css("float", "none");
+ el.css("top", Math.floor(instance.avAffixTopOffset) + "px");
+ el.css("left", Math.floor(instance.baseOffset.left) + "px");
+ el.css("width", Math.floor(instance.baseWidth) + "px");
+ el.css( "z-index", "10");
+
+ if (options.affixPlaceholder !== undefined) {
+ $(options.affixPlaceholder).addClass("affixed");
}
+ }
+
};
-} ]), angular.module("avUi").directive("avAffixTopOffset", [ "$window", "$timeout", "$parse", function($window, $timeout, $parse) {
+
return {
- restrict: "EAC",
- link: function(scope, iElement, iAttrs) {
- var instance = {
- affix: !1,
- scrollAffix: null,
- baseOffset: iElement.offset(),
- baseWidth: iElement.width(),
- avAffixTopOffset: parseInt(iAttrs.avAffixTopOffset, 10)
- };
- function callCheckPos() {
- !function(instance, el, options) {
- var affix = !1, offset = el.offset();
- instance.affix && $window.pageYOffset + 20 >= instance.scrollAffix || (offset.top - $window.pageYOffset < instance.avAffixTopOffset && (affix = !0),
- instance.affix !== affix && (instance.affix = affix, instance.scrollAffix = $window.pageYOffset,
- affix ? (el.addClass("affix-top"), el.data("page-offset", $window.pageYOffset),
- el.css("position", "fixed"), el.css("float", "none"), el.css("top", Math.floor(instance.avAffixTopOffset) + "px"),
- el.css("left", Math.floor(instance.baseOffset.left) + "px"), el.css("width", Math.floor(instance.baseWidth) + "px"),
- el.css("z-index", "10"), void 0 !== options.affixPlaceholder && $(options.affixPlaceholder).addClass("affixed")) : (el.removeClass("affix-top"),
- el.attr("style", ""), void 0 !== options.affixPlaceholder && $(options.affixPlaceholder).removeClass("affixed"))));
- }(instance, iElement, iAttrs);
- }
- callCheckPos(), angular.element($window).on("scroll", callCheckPos), angular.element($window).on("resize", function() {
- iElement.removeClass("affix-top"), iElement.attr("style", ""), instance.affix = !1,
- instance.scrollAffix = null, $timeout(function() {
- instance.baseOffset = iElement.offset(), instance.baseWidth = iElement.width(),
- callCheckPos();
- }, 300);
- });
+ restrict: 'EAC',
+ link: function(scope, iElement, iAttrs) {
+ // instance saves state between calls to checkPosition
+ var instance = {
+ affix: false,
+ scrollAffix: null,
+ baseOffset: iElement.offset(),
+ baseWidth: iElement.width(),
+ avAffixTopOffset: parseInt(iAttrs.avAffixTopOffset, 10)
+ };
+
+
+ function callCheckPos() {
+ checkPosition(scope, instance, iElement, iAttrs);
}
+ callCheckPos();
+
+ // when window resizes, the baseoffset etc needs to be reset
+ function resize() {
+ iElement.removeClass(affixClass);
+ iElement.attr("style", "");
+ instance.affix = false;
+ instance.scrollAffix = null;
+ $timeout(function () {
+ instance.baseOffset = iElement.offset();
+ instance.baseWidth = iElement.width();
+ callCheckPos();
+ }, 300);
+ }
+
+ // watch for window scrolling
+ angular.element($window).on('scroll', callCheckPos);
+ angular.element($window).on('resize', resize);
+ }
};
-} ]), angular.module("avUi").directive("avAffixTop", [ "$window", "$timeout", function($window, $timeout) {
- function updateMargin(el, options) {
- var height = parseInt(options.minHeight), height = Math.max($(el).height(), angular.isNumber(height) && !isNaN(height) ? height : 0);
- $(options.avAffixTop).css("padding-top", height + "px");
- }
+
+ });
+
+/**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+/*
+ * directive used to position an element always at the top. It just sets its
+ * specified element with a margin-top to make space for the affixed element.
+ * This is done dynamically, so that each time the affixed element's height
+ * changes, the top-margin of the specified is recalculated and set.
+ */
+angular.module('avUi')
+ .directive('avAffixTop', function($window, $timeout) {
+
+ // add margin-top automatically
+ var updateMargin = function(el, options) {
+ var minHeight = parseInt(options.minHeight);
+ var height = Math.max(
+ $(el).height(),
+ (angular.isNumber(minHeight) && !isNaN(minHeight) ? minHeight : 0) );
+ $(options.avAffixTop).css("padding-top", height + "px");
+ };
+
return {
- restrict: "EAC",
- link: function(scope, iElement, iAttrs) {
- var timeout;
- function updateMarginTimeout() {
- timeout = $timeout(function() {
- $timeout.cancel(timeout), updateMargin(iElement, iAttrs);
- }, 300);
- }
- updateMargin(iElement, iAttrs), void 0 === iAttrs.minHeight && (iAttrs.minHeight = "20"),
- updateMarginTimeout(), angular.element(iElement).bind("resize", updateMarginTimeout),
- angular.element($window).bind("resize", updateMarginTimeout), $(iAttrs.avAffixTop).change(updateMarginTimeout);
+ restrict: 'EAC',
+ link: function(scope, iElement, iAttrs) {
+ updateMargin(iElement, iAttrs);
+
+ if (iAttrs.minHeight === undefined) {
+ iAttrs.minHeight = "20";
+ }
+
+ // timeout is used with callCheckPos so that we do not create too many
+ // calls to checkPosition, at most one per 300ms
+ var timeout;
+
+ function updateMarginTimeout() {
+ timeout = $timeout(function() {
+ $timeout.cancel(timeout);
+ updateMargin(iElement, iAttrs);
+ }, 300);
}
+ updateMarginTimeout();
+
+ // watch for window resizes and element resizes too
+ angular.element(iElement).bind('resize', updateMarginTimeout);
+ angular.element($window).bind('resize', updateMarginTimeout);
+ $(iAttrs.avAffixTop).change(updateMarginTimeout);
+ }
};
-} ]), angular.module("avUi").directive("avCollapsing", [ "$window", "$timeout", function($window, $timeout) {
- function select(instance, el, val) {
- val = instance.parentSelector ? el.closest(instance.parentSelector).find(val) : angular.element(val);
- return val;
+
+ });
+
+/**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+/*
+ * avCollapsing limits the default maximum height of an element by making it
+ * collapsable if it exceeds the max-height of the selector.
+ * - if the element's height doesn't exceed its maximum height, the
+ * data-toggle-selector element will be set to hidden
+ * - if the element's height exceeds its maximum height, the
+ * data-toggle-selector element will be removed the class "hidden".
+ * - if the data-toggle-selector element it contains is clicked, they will be
+ * added the class ".in".
+ * - if the element's height exceeds its max height and the toggle is not
+ * ".in", then it adds the ".collapsed" class to the element, and makes sure
+ * the data-toggle-selector element is not hidden.
+ * - it will watch the element and window resizes to see if the conditions
+ * change.
+ * - both maxHeightSelector and data-toggle-selector will be found using the
+ * parent selector as a base if the attribute "parent-selector" is set.
+ * Otherwise, it will directly a global angular.element() to find them.
+ */
+angular.module('avUi')
+ .directive('avCollapsing', function($window, $timeout) {
+
+ function select(instance, el, selector) {
+ var val;
+ if (!!instance.parentSelector) {
+ val = el.closest(instance.parentSelector).find(selector);
+ } else {
+ val = angular.element(selector);
+ }
+ return val;
}
+
function collapseEl(instance, el) {
- return instance.collapseSelector ? select(instance, el, instance.collapseSelector) : angular.element(el);
+ var val = null;
+ if (!!instance.collapseSelector) {
+ val = select(instance, el, instance.collapseSelector);
+ } else {
+ val = angular.element(el);
+ }
+ return val;
}
- return {
- restrict: "EAC",
- link: function(scope, iElement, iAttrs) {
- var timeout, instance = {
- isCollapsed: !1,
- maxHeightSelector: iAttrs.avCollapsing,
- toggleSelector: iAttrs.toggleSelector,
- parentSelector: iAttrs.parentSelector,
- collapseSelector: iAttrs.collapseSelector
- };
- function callCheck() {
- timeout = $timeout(function() {
- $timeout.cancel(timeout), function(instance, el) {
- var maxHeight = select(instance, el, instance.maxHeightSelector).css("max-height"), height = angular.element(el)[0].scrollHeight, paddingTop = angular.element(el).css("padding-top");
- -1 !== maxHeight.indexOf("px") ? (paddingTop = paddingTop && -1 !== paddingTop.indexOf("px") ? parseInt(paddingTop.replace("px", "")) : 0,
- (maxHeight = parseInt(maxHeight.replace("px", ""))) < height - paddingTop ? instance.isCollapsed || (instance.isCollapsed = !0,
- collapseEl(instance, el).addClass("collapsed"), select(instance, el, instance.toggleSelector).removeClass("hidden in")) : instance.isCollapsed && (instance.isCollapsed = !1,
- collapseEl(instance, el).removeClass("collapsed"), select(instance, el, instance.toggleSelector).addClass("hidden"))) : console.log("invalid non-pixels max-height for " + instance.maxHeightSelector);
- }(instance, iElement);
- }, 500);
- }
- callCheck(), angular.element($window).bind("resize", callCheck), angular.element(iElement).bind("resize", callCheck),
- angular.element(instance.toggleSelector).bind("click", function() {
- !function(instance, el) {
- instance.isCollapsed ? (collapseEl(instance, el).removeClass("collapsed"), select(instance, el, instance.toggleSelector).addClass("in")) : (collapseEl(instance, el).addClass("collapsed"),
- select(instance, el, instance.toggleSelector).removeClass("in")), instance.isCollapsed = !instance.isCollapsed;
- }(instance, iElement);
- });
+
+ var checkCollapse = function(instance, el, options) {
+ var maxHeight = select(instance, el, instance.maxHeightSelector).css("max-height");
+ var height = angular.element(el)[0].scrollHeight;
+
+ // we want to remove padding-top in the calculation
+ var paddingTop = angular.element(el).css('padding-top');
+
+ if (maxHeight.indexOf("px") === -1) {
+ console.log("invalid non-pixels max-height for " + instance.maxHeightSelector);
+ return;
+ }
+
+ if (!paddingTop || paddingTop.indexOf("px") === -1) {
+ paddingTop = 0;
+ } else {
+ paddingTop = parseInt(paddingTop.replace("px", ""));
+ }
+
+ maxHeight = parseInt(maxHeight.replace("px", ""));
+
+ // make sure it's collapsed if it should
+ if (height - paddingTop > maxHeight) {
+ // already collapsed
+ if (instance.isCollapsed) {
+ return;
+ }
+ instance.isCollapsed = true;
+ collapseEl(instance, el).addClass("collapsed");
+ select(instance, el, instance.toggleSelector).removeClass("hidden in");
+
+ // removed collapsed and hide toggle otherwise
+ } else {
+ // already not collapsed
+ if (!instance.isCollapsed) {
+ return;
}
+ instance.isCollapsed = false;
+ collapseEl(instance, el).removeClass("collapsed");
+ select(instance, el, instance.toggleSelector).addClass("hidden");
+ }
+ };
+
+ var toggleCollapse = function(instance, el, options) {
+ // if it's collapsed, uncollapse
+ if (instance.isCollapsed) {
+ collapseEl(instance, el).removeClass("collapsed");
+ select(instance, el, instance.toggleSelector).addClass("in");
+
+ // collapse otherwise
+ } else {
+ collapseEl(instance, el).addClass("collapsed");
+ select(instance, el, instance.toggleSelector).removeClass("in");
+ }
+
+
+ instance.isCollapsed = !instance.isCollapsed;
};
-} ]), angular.module("avUi").directive("avRecompile", [ "$compile", "$parse", function($compile, $parse) {
- "use strict";
+
return {
- scope: !0,
- compile: function(el) {
- var template = function(el) {
- return angular.element("").append(el.clone()).html();
- }(el);
- return function(scope, $el, attrs) {
- var stopWatching = scope.$parent.$watch(attrs.avRecompile, function(_new, _old) {
- var newEl = attrs.hasOwnProperty("useBoolean");
- (!newEl || _new && "false" !== _new) && (newEl || _new && _new !== _old) && (newEl && $parse(attrs.kcdRecompile).assign(scope.$parent, !1),
- newEl = $compile(template)(scope.$parent), $el.replaceWith(newEl), stopWatching(),
- scope.$destroy());
- });
- };
+ restrict: 'EAC',
+ link: function(scope, iElement, iAttrs) {
+ var instance = {
+ isCollapsed: false,
+ maxHeightSelector: iAttrs.avCollapsing,
+ toggleSelector: iAttrs.toggleSelector,
+ parentSelector: iAttrs.parentSelector,
+ collapseSelector: iAttrs.collapseSelector
+ };
+
+ // timeout is used with callCheck so that we do not create too many
+ // calls to checkPosition, at most one per 100ms
+ var timeout;
+
+ function callCheck() {
+ timeout = $timeout(function() {
+ $timeout.cancel(timeout);
+ checkCollapse(instance, iElement, iAttrs);
+ }, 500);
+ }
+ callCheck();
+
+
+ function launchToggle() {
+ toggleCollapse(instance, iElement, iAttrs);
}
+
+ // watch for window resizes and element resizes too
+ angular.element($window).bind('resize', callCheck);
+ angular.element(iElement).bind('resize', callCheck);
+
+ // watch toggle's clicking
+ angular.element(instance.toggleSelector).bind('click', launchToggle);
+ }
};
-} ]), angular.module("avUi").directive("avDebounce", [ "$timeout", function($timeout) {
+
+ });
+
+/*
+The MIT License (MIT)
+Copyright (c) 2014 Kent C. Dodds
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+Source: https://github.com/kentcdodds/kcd-angular
+Copyright (c) 2014 Kent C. Dodds kent+github@doddsfamily.us
+ */
+
+angular.module('avUi').directive('avRecompile', function($compile, $parse) {
+ 'use strict';
+ function getElementAsHtml(el) {
+ return angular.element('').append(el.clone()).html();
+ }
+
+ return {
+ scope: true, // required to be able to clear watchers safely
+ compile: function(el) {
+ var template = getElementAsHtml(el);
+ return function link(scope, $el, attrs) {
+ var stopWatching = scope.$parent.$watch(attrs.avRecompile, function(_new, _old) {
+ var useBoolean = attrs.hasOwnProperty('useBoolean');
+ if ((useBoolean && (!_new || _new === 'false')) || (!useBoolean && (!_new || _new === _old))) {
+ return;
+ }
+ // reset kcdRecompile to false if we're using a boolean
+ if (useBoolean) {
+ $parse(attrs.kcdRecompile).assign(scope.$parent, false);
+ }
+
+ // recompile
+ var newEl = $compile(template)(scope.$parent);
+ $el.replaceWith(newEl);
+
+ // Destroy old scope, reassign new scope.
+ stopWatching();
+ scope.$destroy();
+ });
+ };
+ }
+ };
+});
+
+/**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+// source: https://gist.github.com/tommaitland/7579618#file-ng-debounce-js
+angular.module('avUi')
+ .directive('avDebounce', function($timeout) {
return {
- restrict: "A",
- require: "ngModel",
- priority: 99,
- link: function(scope, elm, attr, ngModelCtrl) {
- var debounce;
- "radio" !== attr.type && "checkbox" !== attr.type && (elm.unbind("input"), elm.bind("input", function() {
- $timeout.cancel(debounce), debounce = $timeout(function() {
- scope.$apply(function() {
- ngModelCtrl.$setViewValue(elm.val());
- });
- }, attr.avDebounce || 500);
- }), elm.bind("blur", function() {
- scope.$apply(function() {
- ngModelCtrl.$setViewValue(elm.val());
- });
- }));
+ restrict: 'A',
+ require: 'ngModel',
+ priority: 99,
+ link: function(scope, elm, attr, ngModelCtrl) {
+ if (attr.type === 'radio' || attr.type === 'checkbox') {
+ return;
}
+ elm.unbind('input');
+ var debounce;
+
+ elm.bind('input', function() {
+ $timeout.cancel(debounce);
+ debounce = $timeout( function() {
+ scope.$apply(function() {
+ ngModelCtrl.$setViewValue(elm.val());
+ });
+ }, attr.avDebounce || 500);
+ });
+
+ elm.bind('blur', function() {
+ scope.$apply(function() {
+ ngModelCtrl.$setViewValue(elm.val());
+ });
+ });
+ }
};
-} ]), angular.module("avUi").service("InsideIframeService", function() {
+});
+/**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+angular.module('avUi')
+ .service('InsideIframeService', function() {
return function() {
- try {
- return window.self !== window.top;
- } catch (e) {
- return !0;
- }
+ try {
+ return window.self !== window.top;
+ } catch (e) {
+ return true;
+ }
};
-}), angular.module("avUi").directive("avLoadCss", function() {
- return {
- restrict: "AE",
+ });
+
+/**
+ * This file is part of agora-gui-admin.
+ * Copyright (C) 2015-2021 Agora Voting SL
+
+ * agora-gui-admin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-admin is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-admin. If not, see .
+**/
+
+angular
+ .module('avUi')
+ .directive(
+ 'avLoadCss',
+ function()
+ {
+ function link(scope, element, _attrs)
+ {
+ function updateCss(newValue, oldValue)
+ {
+ if (newValue && typeof newValue === 'string' && newValue !== oldValue)
+ {
+ element.text(newValue);
+ }
+ }
+ updateCss(scope.css);
+ scope.$watch("css", updateCss);
+ }
+
+ return {
+ restrict: 'AE',
scope: {
- css: "="
+ css: '='
},
- link: function(scope, element, _attrs) {
- function updateCss(newValue, oldValue) {
- newValue && "string" == typeof newValue && newValue !== oldValue && element.text(newValue);
- }
- updateCss(scope.css), scope.$watch("css", updateCss);
- }
- };
-}), angular.module("avUi").service("PercentVotesService", function() {
- return function(total_votes, question, over, format) {
- function print(num) {
- return "str" === format ? num.toFixed(2) + "%" : num;
+ link: link
+ };
+ });
+
+/**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+/*
+ * Returns the percentage of votes received by an answer. The base number
+ * of the percentage that is used depends on the
+ * "answer_total_votes_percentage" option in the question.
+ */
+angular.module('avUi')
+ .service('PercentVotesService', function() {
+ return function (total_votes, question, over, format) {
+ if (format === undefined) {
+ format = "str";
+ }
+
+ function print(num) {
+ if (format === "str") {
+ return num.toFixed(2) + "%";
+ } else {
+ return num;
}
- if (void 0 === format && (format = "str"), 0 === total_votes) return print(0);
- var base = question.totals.valid_votes + question.totals.null_votes + question.totals.blank_votes;
- return "over-valid-votes" === (over = null == over ? question.answer_total_votes_percentage : over) || "over-total-valid-votes" === over ? base = question.totals.valid_votes : "over-total-valid-points" === over && void 0 !== question.totals.valid_points && (base = question.totals.valid_points),
- print(100 * total_votes / base);
+ }
+
+ // special case
+ if (total_votes === 0) {
+ return print(0.00);
+ }
+
+ var base = question.totals.valid_votes + question.totals.null_votes + question.totals.blank_votes;
+ if (over === undefined || over === null) {
+ over = question.answer_total_votes_percentage;
+ }
+ if ("over-valid-votes" === over || "over-total-valid-votes" === over) {
+ base = question.totals.valid_votes;
+ }
+ else if ("over-total-valid-points" === over &&
+ undefined !== question.totals.valid_points) {
+ base = question.totals.valid_points;
+ }
+
+ return print(100*total_votes / base);
};
-}), angular.module("avUi").service("CheckerService", function() {
+ });
+
+/**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+/*
+ * Checks input data with a list of checks.
+ *
+ * Example:
+ *
+ var checks = [
+ {
+ check: "array-group",
+ prefix: "question-",
+ checks: [
+ {check: "is-array", key: "questions"},
+ {check: "array-length", key: "questions", min: 1, max: 40},
+ {
+ check: "array-key-group-chain",
+ key: "questions",
+ prefix: "question-",
+ checks: [
+ {check: "is-int", key: "min"},
+ {check: "is-int", key: "max"},
+ {check: "is-int", key: "num_winners"},
+ {check: "is-array", key: "answers"},
+ {check: "array-length", key: "answers", min: 1, max: 10000},
+ {check: "int-size", key: "min", min: 0, max: "$value.max"},
+ {
+ check: "int-size",
+ key: "max",
+ min: "$value.min",
+ max: "$value.answers.length"
+ },
+ {
+ check: "int-size",
+ key: "num_winners",
+ max: "$value.answers.length"
+ }
+ ]
+ }
+ ]
+ }
+ ];
+
+ scope.errors = [];
+ CheckerService({
+ checks: checks,
+ data: scope.elections,
+ onError: function (errorKey, errorData) {
+ scope.errors.push({
+ data: errorData,
+ key: errorKey
+ });
+ }
+ });
+ */
+angular.module('avUi')
+ .service('CheckerService', function() {
function checker(d) {
- function evalValue(code, $value) {
- return angular.isString(code) ? eval(code) : code;
+
+ /*
+ * Used to eval the expressions given by the programmer in the checker
+ * script
+ */
+ function evalValue(code, $value) {
+ if (angular.isString(code)) {
+ /* jshint ignore:start */
+ return eval(code);
+ /* jshint ignore:end */
+ } else {
+ return code;
}
- function sumStrs(str1, str2) {
- var ret = "";
- return angular.isString(str1) && (ret = str1), angular.isString(str2) && (ret += str2),
- ret;
+ }
+
+ function sumStrs(str1, str2) {
+ var ret = "";
+ if (angular.isString(str1)) {
+ ret = str1;
}
- function error(errorKey, errorData, postfix) {
- angular.extend(errorData, d.errorData), d.onError(_.reduce([ d.prefix, errorKey, postfix ], sumStrs, ""), errorData);
+ if (angular.isString(str2)) {
+ ret += str2;
}
- angular.isUndefined(d.errorData) && (d.errorData = {});
- var ret = _.every(d.checks, function(item) {
- var errorData, min, max, itemMin, itemMax, data, extra, prefix, pass = !0, dataToCheck = angular.isDefined(item.key) ? d.data[item.key] : d.data;
- return "is-int" === item.check ? (pass = angular.isNumber(dataToCheck, item.postfix)) || error(item.check, {
- key: item.key
- }, item.postfix) : "is-array" === item.check ? (pass = angular.isArray(dataToCheck, item.postfix)) || error(item.check, {
- key: item.key
- }, item.postfix) : "lambda" === item.check ? item.validator(dataToCheck) || (errorData = {
- key: item.key
- }, angular.isUndefined(item.appendOnErrorLambda) || (errorData = item.appendOnErrorLambda(dataToCheck)),
- _.isObject(item.append) && _.isString(item.append.key) && !_.isUndefined(item.append.value) && (errorData[item.append.key] = evalValue(item.append.value, item)),
- error(item.check, errorData, item.postfix)) : "is-string-if-defined" === item.check ? (pass = angular.isUndefined(dataToCheck) || angular.isString(dataToCheck, item.postfix)) || error(item.check, {
- key: item.key
- }, item.postfix) : "array-length-if-defined" === item.check ? angular.isDefined(dataToCheck) && (itemMin = evalValue(item.min, d.data),
- itemMax = evalValue(item.max, d.data), (angular.isArray(dataToCheck) || angular.isString(dataToCheck)) && (min = angular.isUndefined(item.min) || dataToCheck.length >= itemMin,
- max = angular.isUndefined(item.max) || dataToCheck.length <= itemMax, pass = min && max,
- min || error("array-length-min", {
- key: item.key,
- min: itemMin,
- num: dataToCheck.length
- }, item.postfix), max || error("array-length-max", {
- key: item.key,
- max: itemMax,
- num: dataToCheck.length
- }, item.postfix))) : "is-string" === item.check ? (pass = angular.isString(dataToCheck, item.postfix)) || error(item.check, {
- key: item.key
- }, item.postfix) : "array-length" === item.check ? (itemMin = evalValue(item.min, d.data),
- itemMax = evalValue(item.max, d.data), (angular.isArray(dataToCheck) || angular.isString(dataToCheck)) && (min = angular.isUndefined(item.min) || dataToCheck.length >= itemMin,
- max = angular.isUndefined(item.max) || dataToCheck.length <= itemMax, pass = min && max,
- min || error("array-length-min", {
- key: item.key,
- min: itemMin,
- num: dataToCheck.length
- }, item.postfix), max || error("array-length-max", {
- key: item.key,
- max: itemMax,
- num: dataToCheck.length
- }, item.postfix))) : "int-size" === item.check ? (itemMin = evalValue(item.min, d.data),
- itemMax = evalValue(item.max, d.data), min = angular.isUndefined(item.min) || itemMin <= dataToCheck,
- max = angular.isUndefined(item.max) || dataToCheck <= itemMax, pass = min && max,
- min || error("int-size-min", {
- key: item.key,
- min: itemMin,
- value: dataToCheck
- }, item.postfix), max || error("int-size-max", {
- key: item.key,
- max: itemMax,
- value: dataToCheck
- }, item.postfix)) : "group-chain" === item.check ? pass = _.all(_.map(item.checks, function(check) {
+ return ret;
+ }
+
+ function error(errorKey, errorData, postfix) {
+ angular.extend(errorData, d.errorData);
+ d.onError(
+ _.reduce([d.prefix, errorKey, postfix], sumStrs, ""),
+ errorData
+ );
+ }
+
+ if (angular.isUndefined(d.errorData)) {
+ d.errorData = {};
+ }
+
+ var ret = _.every(d.checks, function (item) {
+ var pass = true;
+ var itemMin;
+ var itemMax;
+ var max;
+ var min;
+ var dataToCheck = angular.isDefined(item.key) ? d.data[item.key] : d.data;
+ if (item.check === "is-int") {
+ pass = angular.isNumber(dataToCheck, item.postfix);
+ if (!pass) {
+ error(item.check, {key: item.key}, item.postfix);
+ }
+
+ } else if (item.check === "is-array") {
+ pass = angular.isArray(dataToCheck, item.postfix);
+ if (!pass) {
+ error(item.check, {key: item.key}, item.postfix);
+ }
+ } else if (item.check === "lambda") {
+ if (!item.validator(dataToCheck)) {
+ var errorData = {key: item.key};
+ if (!angular.isUndefined(item.appendOnErrorLambda)) {
+ errorData = item.appendOnErrorLambda(dataToCheck);
+ }
+ if (_.isObject(item.append) &&
+ _.isString(item.append.key) &&
+ !_.isUndefined(item.append.value)) {
+ errorData[item.append.key] = evalValue(item.append.value, item);
+ }
+ error(item.check, errorData, item.postfix);
+ }
+
+ } else if (item.check === "is-string-if-defined") {
+ pass = angular.isUndefined(dataToCheck) ||
+ angular.isString(dataToCheck, item.postfix);
+ if (!pass) {
+ error(item.check, {key: item.key}, item.postfix);
+ }
+
+ } else if (item.check === "array-length-if-defined") {
+ if (angular.isDefined(dataToCheck)) {
+ itemMin = evalValue(item.min, d.data);
+ itemMax = evalValue(item.max, d.data);
+
+ if (angular.isArray(dataToCheck) || angular.isString(dataToCheck))
+ {
+ min = angular.isUndefined(item.min) || dataToCheck.length >= itemMin;
+ max = angular.isUndefined(item.max) || dataToCheck.length <= itemMax;
+ pass = min && max;
+ if (!min) {
+ error(
+ "array-length-min",
+ {key: item.key, min: itemMin, num: dataToCheck.length},
+ item.postfix);
+ }
+ if (!max) {
+ var itemErrorData0 = {key: item.key, max: itemMax, num: dataToCheck.length};
+ error(
+ "array-length-max",
+ itemErrorData0,
+ item.postfix);
+ }
+ }
+ }
+ } else if (item.check === "is-string") {
+ pass = angular.isString(dataToCheck, item.postfix);
+ if (!pass) {
+ error(item.check, {key: item.key}, item.postfix);
+ }
+
+ } else if (item.check === "array-length") {
+ itemMin = evalValue(item.min, d.data);
+ itemMax = evalValue(item.max, d.data);
+
+ if (angular.isArray(dataToCheck) || angular.isString(dataToCheck))
+ {
+ min = angular.isUndefined(item.min) || dataToCheck.length >= itemMin;
+ max = angular.isUndefined(item.max) || dataToCheck.length <= itemMax;
+ pass = min && max;
+ if (!min) {
+ error(
+ "array-length-min",
+ {key: item.key, min: itemMin, num: dataToCheck.length},
+ item.postfix);
+ }
+ if (!max) {
+ var itemErrorData = {key: item.key, max: itemMax, num: dataToCheck.length};
+ error(
+ "array-length-max",
+ itemErrorData,
+ item.postfix);
+ }
+ }
+
+ } else if (item.check === "int-size") {
+ itemMin = evalValue(item.min, d.data);
+ itemMax = evalValue(item.max, d.data);
+ min = angular.isUndefined(item.min) || dataToCheck >= itemMin;
+ max = angular.isUndefined(item.max) || dataToCheck <= itemMax;
+ pass = min && max;
+ if (!min) {
+ error(
+ "int-size-min",
+ {key: item.key, min: itemMin, value: dataToCheck},
+ item.postfix);
+ }
+ if (!max) {
+ error(
+ "int-size-max",
+ {key: item.key, max: itemMax, value: dataToCheck},
+ item.postfix);
+ }
+ } else if (item.check === "group-chain") {
+ pass = _.all(
+ _.map(
+ item.checks,
+ function(check) {
return checker({
- data: d.data,
- errorData: d.errorData,
- onError: d.onError,
- checks: [ check ],
- prefix: sumStrs(d.prefix, item.prefix)
+ data: d.data,
+ errorData: d.errorData,
+ onError: d.onError,
+ checks: [check],
+ prefix: sumStrs(d.prefix, item.prefix)
});
- })) : "array-key-group-chain" === item.check ? pass = _.every(dataToCheck, function(data, index) {
- var extra = {}, prefix = "";
- return angular.isString(d.prefix) && (prefix = d.prefix), angular.isString(item.prefix) && (prefix += item.prefix),
- extra.prefix = prefix, extra[item.append.key] = evalValue(item.append.value, data),
- checker({
- data: data,
- errorData: angular.extend({}, d.errorData, extra),
- onError: d.onError,
- checks: item.checks,
- prefix: sumStrs(d.prefix, item.prefix)
- });
- }) : "array-group-chain" === item.check ? pass = _.every(d.data, function(data, index) {
- var extra = {};
- return extra[item.append.key] = evalValue(item.append.value, data), checker({
- data: data,
- errorData: angular.extend({}, d.errorData, extra),
- onError: d.onError,
- checks: item.checks,
- prefix: sumStrs(d.prefix, item.prefix)
- });
- }) : "array-group" === item.check ? pass = _.contains(_.map(d.data, function(data, index) {
+ })
+ );
+ } else if (item.check === "array-key-group-chain") {
+ pass = _.every(
+ dataToCheck,
+ function (data, index) {
+ var extra = {};
+ var prefix = "";
+ if (angular.isString(d.prefix)) {
+ prefix = d.prefix;
+ }
+ if (angular.isString(item.prefix)) {
+ prefix += item.prefix;
+ }
+ extra.prefix = prefix;
+ extra[item.append.key] = evalValue(item.append.value, data);
+ return checker({
+ data: data,
+ errorData: angular.extend({}, d.errorData, extra),
+ onError: d.onError,
+ checks: item.checks,
+ prefix: sumStrs(d.prefix, item.prefix),
+ });
+ });
+ } else if (item.check === "array-group-chain") {
+ pass = _.every(d.data, function (data, index) {
+ var extra = {};
+ extra[item.append.key] = evalValue(item.append.value, data);
+ return checker({
+ data: data,
+ errorData: angular.extend({}, d.errorData, extra),
+ onError: d.onError,
+ checks: item.checks,
+ prefix: sumStrs(d.prefix, item.prefix),
+ });
+ });
+ } else if (item.check === "array-group") {
+ pass = _.contains(
+ _.map(
+ d.data,
+ function (data, index) {
var extra = {};
- return extra[item.append.key] = evalValue(item.append.value, data), checker({
- data: data,
- errorData: angular.extend({}, d.errorData, extra),
- onError: d.onError,
- checks: item.checks,
- prefix: sumStrs(d.prefix, item.prefix)
+ extra[item.append.key] = evalValue(item.append.value, data);
+ return checker({
+ data: data,
+ errorData: angular.extend({}, d.errorData, extra),
+ onError: d.onError,
+ checks: item.checks,
+ prefix: sumStrs(d.prefix, item.prefix),
});
- }), !0) : "object-key-chain" === item.check && (pass = _.isString(item.key) && _.isObject(dataToCheck)) && (data = dataToCheck,
- (extra = {})[item.append.key] = evalValue(item.append.value, data), prefix = "",
- angular.isString(d.prefix) && (prefix += d.prefix), angular.isString(item.prefix) && (prefix += item.prefix),
- pass = _.every(item.checks, function(check, index) {
+ }),
+ true);
+ } else if (item.check === "object-key-chain") {
+ pass = _.isString(item.key) && _.isObject(dataToCheck);
+ if (!!pass) {
+ var data = dataToCheck;
+ var extra = {};
+ extra[item.append.key] = evalValue(item.append.value, data);
+ var prefix = "";
+ if (angular.isString(d.prefix)) {
+ prefix += d.prefix;
+ }
+ if (angular.isString(item.prefix)) {
+ prefix += item.prefix;
+ }
+ pass = _.every(
+ item.checks,
+ function (check, index) {
return checker({
- data: data,
- errorData: angular.extend({}, d.errorData, extra),
- onError: d.onError,
- checks: [ check ],
- prefix: prefix
+ data: data,
+ errorData: angular.extend({}, d.errorData, extra),
+ onError: d.onError,
+ checks: [check],
+ prefix: prefix,
});
- })), !(!pass && "chain" === d.data.groupType);
- });
- return ret;
+ });
+ }
+ }
+ if (!pass && d.data.groupType === 'chain') {
+ return false;
+ }
+ return true;
+ });
+
+ return ret;
}
return checker;
-}), angular.module("avUi").service("AddDotsToIntService", function() {
- return function(number, fixedDigits) {
- var number_str = ((number = angular.isNumber(fixedDigits) && 0 <= fixedDigits ? number.toFixed(parseInt(fixedDigits)) : number) + "").replace(".", ","), ret = "", commaPos = number_str.length;
- -1 !== number_str.indexOf(",") && (commaPos = number_str.indexOf(","));
- for (var i = 0; i < commaPos; i++) {
- var reverse = commaPos - i;
- reverse % 3 == 0 && 0 < reverse && 0 < i && (ret += "."), ret += number_str[i];
+ });
+
+/**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+/*
+ * Given a number, adds dots every three digits.
+ *
+ * Example:
+ *
+ * AddDotsToIntService(1234567) --> "1.234.567"
+ * AddDotsToIntService(1111.234567) --> "1.111,234567"
+ */
+angular.module('avUi')
+ .service('AddDotsToIntService', function() {
+ return function (number, fixedDigits) {
+ if (angular.isNumber(fixedDigits) && fixedDigits >= 0) {
+ number = number.toFixed(parseInt(fixedDigits));
+ }
+ var number_str = (number + "").replace(".", ",");
+ var ret = "";
+ var commaPos = number_str.length;
+ if (number_str.indexOf(",") !== -1) {
+ commaPos = number_str.indexOf(",");
+ }
+ for (var i = 0; i < commaPos; i++) {
+ var reverse = commaPos - i;
+ if ((reverse % 3 === 0) && reverse > 0 && i > 0) {
+ ret = ret + ".";
}
- return ret + number_str.substr(commaPos, number_str.length);
+ ret = ret + number_str[i];
+ }
+ return ret + number_str.substr(commaPos, number_str.length);
};
-}), angular.module("avUi").service("EndsWithService", function() {
- return function(originString, searchString) {
- if (!angular.isString(originString) || !angular.isString(searchString)) return !1;
+ });
+
+/**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+angular.module('avUi')
+ .service('EndsWithService', function() {
+ return function (originString, searchString) {
+ if (!angular.isString(originString) || !angular.isString(searchString)) {
+ return false;
+ }
var lastIndex = originString.indexOf(searchString);
- return -1 !== lastIndex && lastIndex === originString.length - searchString.length;
- };
-}), angular.module("avUi").service("StateDataService", [ "$state", function($state) {
+ return lastIndex !== -1 && lastIndex === originString.length - searchString.length;
+ };
+ });
+/**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+/*
+ * Save data between states.
+ *
+ * Example:
+ *
+ * StateDataService.go('election.public.show.login', {id: autheventid}, {something: "foo"})
+ * StateDataService.getData() --> {something: "foo"}
+ */
+angular.module('avUi')
+ .service('StateDataService', function($state) {
var data = {};
return {
- go: function(path, stateData, newData) {
- data = angular.copy(newData), $state.go(path, stateData);
- },
- getData: function() {
- return data;
- }
+ go: function (path, stateData, newData) {
+ data = angular.copy(newData);
+ $state.go(path, stateData);
+ },
+ getData: function () {
+ return data;
+ }
};
-} ]), angular.module("avUi").directive("avScrollToBottom", [ "$timeout", function($timeout) {
+ });
+
+/**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+/**
+ * Always scrolls to bottom the div to which the directive is attached when
+ * the observed property is modified.
+ *
+ * Example:
+ *
+ *
+ */
+angular.module('avUi')
+ .directive('avScrollToBottom', function($timeout) {
return {
- restrict: "A",
- link: function(scope, element, attrs) {
- scope.$watch(function() {
- return element.children().length;
- }, function() {
- element.animate({
- scrollTop: element.prop("scrollHeight")
- }, 300);
- });
- }
+ restrict: 'A',
+ link: function postLink(scope, element, attrs) {
+ scope.$watch(
+ function () {
+ return element.children().length;
+ },
+ function () {
+ element.animate({ scrollTop: element.prop('scrollHeight') }, 300);
+ }
+ );
+ }
};
-} ]), angular.module("avUi").filter("addTargetBlank", function() {
- return function(tree) {
- tree = angular.element("
").append(tree).html();
+});
+/**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+/**
+ * Adds target blank to links.
+ *
+ * Usage example:
+ *
+ *
+ */
+angular.module('avUi')
+ .filter('addTargetBlank', function(){
+ return function(x) {
+ //defensively wrap in a div to avoid 'invalid html' exception, then add
+ //the target _blank to links
+ var tree = angular.element('
'+x+'
');
+ tree.find('a').attr('target', '_blank');
+
+ //trick to have a string representation
+ return angular.element('
').append(tree).html();
};
-}), angular.module("avUi").filter("htmlToText", function() {
+ });
+/**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+angular.module('avUi')
+ .filter('htmlToText', function() {
return function(text) {
- return angular.element("
" + text + "
").text();
+ return angular.element('
'+text+'
').text();
};
-}), angular.module("avUi").config([ "$provide", function($provide) {
- $provide.decorator("ngModelDirective", [ "$delegate", function($delegate) {
- var ngModel = $delegate[0], controller = ngModel.controller;
- return ngModel.controller = [ "$scope", "$element", "$attrs", "$injector", function(scope, element, attrs, $injector) {
- var $interpolate = $injector.get("$interpolate");
- attrs.$set("name", $interpolate(attrs.name || "")(scope)), $injector.invoke(controller, Object.setPrototypeOf(this, controller.prototype), {
- $scope: scope,
- $element: element,
- $attrs: attrs
- });
- } ], $delegate;
- } ]), $provide.decorator("formDirective", [ "$delegate", function($delegate) {
- var form = $delegate[0], controller = form.controller;
- return form.controller = [ "$scope", "$element", "$attrs", "$injector", function(scope, element, attrs, $injector) {
- var $interpolate = $injector.get("$interpolate");
- attrs.$set("name", $interpolate(attrs.name || attrs.ngForm || "")(scope)), $injector.invoke(controller, Object.setPrototypeOf(this, controller.prototype), {
- $scope: scope,
- $element: element,
- $attrs: attrs
- });
- } ], $delegate;
- } ]);
-} ]), angular.module("avUi").controller("DocumentationUiController", [ "$state", "$stateParams", "$http", "$scope", "$sce", "$i18next", "ConfigService", "InsideIframeService", "Authmethod", function($state, $stateParams, $http, $scope, $sce, $i18next, ConfigService, InsideIframeService, Authmethod) {
- $scope.inside_iframe = InsideIframeService(), $scope.documentation = ConfigService.documentation,
- $scope.documentation.security_contact = ConfigService.legal.security_contact, $scope.documentation_html_include = $sce.trustAsHtml(ConfigService.documentation_html_include),
- $scope.auths_url = "/election/" + $stateParams.id + "/public/authorities", $scope.election_id = $stateParams.id + "",
- Authmethod.viewEvent($stateParams.id).then(function(response) {
- "ok" === response.data.status && ($scope.authEvent = response.data.events);
+ });
+/**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+// see https://github.com/angular/angular.js/issues/1404
+angular.module('avUi')
+ .config(function($provide) {
+ $provide.decorator('ngModelDirective', function($delegate) {
+ var ngModel = $delegate[0], controller = ngModel.controller;
+ ngModel.controller = ['$scope', '$element', '$attrs', '$injector', function(scope, element, attrs, $injector) {
+ var $interpolate = $injector.get('$interpolate');
+ attrs.$set('name', $interpolate(attrs.name || '')(scope));
+ $injector.invoke(controller, Object.setPrototypeOf(this, controller.prototype), {
+ '$scope': scope,
+ '$element': element,
+ '$attrs': attrs
+ });
+ }];
+ return $delegate;
+ });
+ $provide.decorator('formDirective', function($delegate) {
+ var form = $delegate[0], controller = form.controller;
+ form.controller = ['$scope', '$element', '$attrs', '$injector', function(scope, element, attrs, $injector) {
+ var $interpolate = $injector.get('$interpolate');
+ attrs.$set('name', $interpolate(attrs.name || attrs.ngForm || '')(scope));
+ $injector.invoke(controller, Object.setPrototypeOf(this, controller.prototype), {
+ '$scope': scope,
+ '$element': element,
+ '$attrs': attrs
+ });
+ }];
+ return $delegate;
});
-} ]), angular.module("avUi").directive("documentationDirective", function() {
+ });
+
+ /**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-elections is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-elections is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-elections. If not, see .
+**/
+
+/*
+ * Shows the public view of an election. Controls mainly the changing inner states
+ * loading config, showing results, showing error if needed.
+ */
+angular.module('avUi').controller('DocumentationUiController',
+ function($state, $stateParams, $http, $scope, $sce, $i18next, ConfigService, InsideIframeService, Authmethod) {
+ $scope.inside_iframe = InsideIframeService();
+ $scope.documentation = ConfigService.documentation;
+ $scope.documentation.security_contact = ConfigService.legal.security_contact;
+ $scope.documentation_html_include = $sce.trustAsHtml(ConfigService.documentation_html_include);
+ $scope.auths_url = '/election/' + $stateParams.id + '/public/authorities';
+ $scope.election_id = $stateParams.id + '';
+
+ Authmethod.viewEvent($stateParams.id)
+ .then(function(response) {
+ if (response.data.status === "ok") {
+ $scope.authEvent = response.data.events;
+ }
+ });
+ }
+);
+
+angular.module('avUi')
+ .directive('documentationDirective', function() {
return {
- restrict: "AE",
- scope: {
- extra: "="
- },
- templateUrl: "avUi/documentation-directive/documentation-directive.html",
- controller: "DocumentationUiController"
+ restrict: 'AE',
+ scope: {
+ extra: '='
+ },
+ templateUrl: 'avUi/documentation-directive/documentation-directive.html',
+ controller: 'DocumentationUiController'
};
-}), angular.module("avUi").directive("avFoot", [ "ConfigService", function(ConfigService) {
+ });
+
+/**
+ * This file is part of agora-gui-admin.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-admin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-admin is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-admin. If not, see .
+**/
+
+angular.module('avUi')
+ .directive('avFoot', function(ConfigService) {
+ // we use it as something similar to a controller here
+ function link(scope, element, attrs) {
+ scope.contact = ConfigService.contact;
+ scope.social = ConfigService.social;
+ scope.technology = ConfigService.technology;
+ scope.legal = ConfigService.legal;
+ scope.organization = ConfigService.organization;
+ }
+
return {
- restrict: "AE",
- scope: {},
- link: function(scope, element, attrs) {
- scope.contact = ConfigService.contact, scope.social = ConfigService.social, scope.technology = ConfigService.technology,
- scope.legal = ConfigService.legal, scope.organization = ConfigService.organization;
- },
- templateUrl: "avUi/foot-directive/foot-directive.html"
+ restrict: 'AE',
+ scope: {
+ },
+ link: link,
+ templateUrl: 'avUi/foot-directive/foot-directive.html'
};
-} ]), angular.module("agora-gui-common", [ "ui.bootstrap", "ui.utils", "ui.router", "ngAnimate", "ngResource", "ngCookies", "ipCookie", "ngSanitize", "infinite-scroll", "angularMoment", "avConfig", "jm.i18next", "avRegistration", "avUi", "avTest", "angularFileUpload", "dndLists", "angularLoad", "ng-autofocus" ]),
-angular.module("jm.i18next").config([ "$i18nextProvider", "ConfigServiceProvider", function($i18nextProvider, ConfigServiceProvider) {
- $("#no-js").hide(), $i18nextProvider.options = _.extend({
- useCookie: !0,
- useLocalStorage: !1,
- fallbackLng: "en",
- cookieName: "lang",
- detectLngQS: "lang",
- lngWhitelist: [ "en", "es", "gl", "ca" ],
- resGetPath: "/locales/__lng__.json",
- defaultLoadingValue: ""
- }, ConfigServiceProvider.i18nextInitOptions);
-} ]), angular.module("agora-gui-common").run([ "$http", "$rootScope", function($http, $rootScope) {
- $rootScope.safeApply = function(fn) {
- var phase = $rootScope.$$phase;
- "$apply" === phase || "$digest" === phase ? fn && "function" == typeof fn && fn() : this.$apply(fn);
- }, $rootScope.$on("$stateChangeStart", function(event, toState, toParams, fromState, fromParams) {
- console.log("change start from " + fromState.name + " to " + toState.name), $("#angular-preloading").show();
- }), $rootScope.$on("$stateChangeSuccess", function(event, toState, toParams, fromState, fromParams) {
- console.log("change success"), $("#angular-preloading").hide();
+ });
+
+/**
+ * This file is part of agora-gui-common.
+ * Copyright (C) 2015-2016 Agora Voting SL
+
+ * agora-gui-common is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License.
+
+ * agora-gui-common is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with agora-gui-common. If not, see .
+**/
+
+angular.module(
+ 'agora-gui-common',
+ ['ui.bootstrap',
+ 'ui.utils',
+ 'ui.router',
+ 'ngAnimate',
+ 'ngResource',
+ 'ngCookies',
+ 'ipCookie',
+ 'ngSanitize',
+ 'infinite-scroll',
+ 'angularMoment',
+ 'avConfig',
+ 'jm.i18next',
+ 'avRegistration',
+ 'avUi',
+ 'avTest',
+ 'angularFileUpload',
+ 'dndLists',
+ 'angularLoad',
+ 'ng-autofocus'
+]);
+
+angular.module('jm.i18next').config(function ($i18nextProvider, ConfigServiceProvider) {
+ // note that we do not send the language: by default, it will try the language
+ // supported by the web browser
+ $("#no-js").hide();
+
+ $i18nextProvider.options = _.extend(
+ {
+ useCookie: true,
+ useLocalStorage: false,
+ fallbackLng: 'en',
+ cookieName: 'lang',
+ detectLngQS: 'lang',
+ lngWhitelist: ['en', 'es', 'gl', 'ca'],
+ resGetPath: '/locales/__lng__.json',
+ defaultLoadingValue: '' // ng-i18next option, *NOT* directly supported by i18next
+ },
+ ConfigServiceProvider.i18nextInitOptions);
+});
+
+angular.module('agora-gui-common').run(function($http, $rootScope) {
+
+ $rootScope.safeApply = function(fn) {
+ var phase = $rootScope.$$phase;
+ if (phase === '$apply' || phase === '$digest') {
+ if (fn && (typeof(fn) === 'function')) {
+ fn();
+ }
+ } else {
+ this.$apply(fn);
+ }
+ };
+
+ $rootScope.$on('$stateChangeStart',
+ function(event, toState, toParams, fromState, fromParams) {
+ console.log("change start from " + fromState.name + " to " + toState.name);
+ $("#angular-preloading").show();
+ });
+ $rootScope.$on('$stateChangeSuccess',
+ function(event, toState, toParams, fromState, fromParams) {
+ console.log("change success");
+ $("#angular-preloading").hide();
+ });
+});
+
+/*
+This directive will trigger a click if the user presses space or enter
+ */
+angular.module('agora-gui-common').directive('ngSpaceClick', function ($timeout) {
+ return function (scope, element, attrs) {
+ element.bind("keydown", function (event) {
+ switch (event.which) {
+ case 13: // ENTER
+ case 32: { // SPACE
+ $timeout(function() {event.currentTarget.click();},0);
+ event.stopPropagation();
+ }
+ }
});
-} ]), angular.module("agora-gui-common").directive("ngSpaceClick", [ "$timeout", function($timeout) {
- return function(scope, element, attrs) {
- element.bind("keydown", function(event) {
- switch (event.which) {
- case 13:
- case 32:
- $timeout(function() {
- event.currentTarget.click();
- }, 0), event.stopPropagation();
+ };
+});
+
+/*
+This directive allows us to pass a function in on an enter key to do what we want.
+ */
+angular.module('agora-gui-common').directive('ngEnter', function () {
+ return function (scope, element, attrs) {
+ element.bind("keydown keypress", function (event) {
+ if(event.which === 13) {
+ scope.$apply(function (){
+ scope.$eval(attrs.ngEnter);
+ });
+
+ event.preventDefault();
}
});
};
-} ]), angular.module("agora-gui-common").directive("ngEnter", function() {
- return function(scope, element, attrs) {
- element.bind("keydown keypress", function(event) {
- 13 === event.which && (scope.$apply(function() {
- scope.$eval(attrs.ngEnter);
- }), event.preventDefault());
- });
- };
-}), angular.module("agora-gui-common").filter("truncate", function() {
- return function(text, length, end) {
- return isNaN(length) && (length = 10), void 0 === end && (end = "..."), text.length <= length || text.length - end.length <= length ? text : String(text).substring(0, length - end.length) + end;
- };
-}), avConfigData.browserUpdate) try {
- document.addEventListener("DOMContentLoaded", $buo_f, !1);
-} catch (e) {
+});
+
+/**
+ * Truncate Filter
+ * @Param text
+ * @Param length, default is 10
+ * @Param end, default is "..."
+ * @return string
+ */
+angular.module('agora-gui-common').filter('truncate', function () {
+ return function (text, length, end) {
+ if (isNaN(length)) {
+ length = 10;
+ }
+
+ if (end === undefined) {
+ end = "...";
+ }
+
+ if (text.length <= length || text.length - end.length <= length) {
+ return text;
+ }
+ else {
+ return String(text).substring(0, length-end.length) + end;
+ }
+
+ };
+ });
+
+/*globals avConfigData:false, $buo:false */
+/**
+ * Check browser version with browser-update.org
+ */
+function $buo_f() {
+ $buo(avConfigData.browserUpdate);
+}
+
+if (avConfigData.browserUpdate) {
+ try {
+ document.addEventListener("DOMContentLoaded", $buo_f, false);
+ } catch (e) {
window.attachEvent("onload", $buo_f);
+ }
}
-angular.module("avTest", []), angular.module("avTest").controller("UnitTestE2EController", [ "$scope", "$location", "ConfigService", function($scope, $location, ConfigService) {
- ConfigService.debug && ($scope.html = $location.search().html, console.log($location.search()));
-} ]), angular.module("agora-gui-common").run([ "$templateCache", function($templateCache) {
- "use strict";
- $templateCache.put("avRegistration/error.html", '